mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Compare commits
258 Commits
cenum_bran
...
typecaptur
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88096e0556 | ||
|
|
1634217fc4 | ||
|
|
bc9b0900a5 | ||
|
|
f43a7540c5 | ||
|
|
410a25f334 | ||
|
|
35c04cdc36 | ||
|
|
3e641ab82b | ||
|
|
7972397c65 | ||
|
|
9bf933ae31 | ||
|
|
a69ee59b82 | ||
|
|
961aa0ef61 | ||
|
|
48318c3ad4 | ||
|
|
a004cd3d03 | ||
|
|
768ce6092d | ||
|
|
e4e499edd2 | ||
|
|
f36e9fea48 | ||
|
|
d5eec296a0 | ||
|
|
e2e2ca1d7f | ||
|
|
6ab7198f2f | ||
|
|
2e1f7c95ce | ||
|
|
a2ef63f5b6 | ||
|
|
b7c9a4e2e9 | ||
|
|
28ffb864a3 | ||
|
|
18b4ce4e7d | ||
|
|
551ce34b9b | ||
|
|
de09a19a48 | ||
|
|
ba55946c9a | ||
|
|
33ab18033a | ||
|
|
96127d4ff3 | ||
|
|
43163fe2a0 | ||
|
|
a52b30c951 | ||
|
|
7d6a864d56 | ||
|
|
eeab73df4e | ||
|
|
db45abdfc7 | ||
|
|
cb32441533 | ||
|
|
643aa47e99 | ||
|
|
5e1bf75621 | ||
|
|
7c8e3dd4fd | ||
|
|
ad02fad167 | ||
|
|
261184b5c1 | ||
|
|
d07da2804e | ||
|
|
702b63ddb7 | ||
|
|
e35dbd29fb | ||
|
|
b52ab886d2 | ||
|
|
4b95d6be4c | ||
|
|
34b0b6f8f9 | ||
|
|
4fea202e6d | ||
|
|
8cfdb76869 | ||
|
|
858f8d2405 | ||
|
|
67aa18c1aa | ||
|
|
31b15c775e | ||
|
|
bf7e7e2397 | ||
|
|
eb8fb8871f | ||
|
|
85dc9c45ab | ||
|
|
076ef187cb | ||
|
|
c8d39251a9 | ||
|
|
f5e6b697b8 | ||
|
|
e8e88c1920 | ||
|
|
82a58e1c66 | ||
|
|
8e8d0436ad | ||
|
|
db99de9717 | ||
|
|
fd9fbe26a1 | ||
|
|
582453cb45 | ||
|
|
b73a44ec7d | ||
|
|
be98a01ed8 | ||
|
|
625a6d987d | ||
|
|
2597f6217e | ||
|
|
0470f3be8e | ||
|
|
1d25197bfd | ||
|
|
aae873c044 | ||
|
|
6471728ee5 | ||
|
|
29bae1fbd6 | ||
|
|
9c770f360e | ||
|
|
3b6d68ef21 | ||
|
|
24c03f9800 | ||
|
|
ed61b51489 | ||
|
|
0205ee8688 | ||
|
|
abd3585c44 | ||
|
|
aa910a1c44 | ||
|
|
00b88a8027 | ||
|
|
229fdd6193 | ||
|
|
5292e08cd6 | ||
|
|
90990ed2f3 | ||
|
|
c99284103d | ||
|
|
b463358add | ||
|
|
0e10b71cbf | ||
|
|
cb2d8133e0 | ||
|
|
f2d27229d2 | ||
|
|
604661b12c | ||
|
|
440df8415e | ||
|
|
c31c423386 | ||
|
|
8358af2240 | ||
|
|
4625b457fb | ||
|
|
151a28a92a | ||
|
|
9fe6c77d28 | ||
|
|
1c4f7a4b61 | ||
|
|
f23bbb342c | ||
|
|
91b866c967 | ||
|
|
483fe62750 | ||
|
|
2a47cc2ca9 | ||
|
|
e707190539 | ||
|
|
cb62554a26 | ||
|
|
9c58db99af | ||
|
|
90c339ebdb | ||
|
|
e3a8a3ec02 | ||
|
|
bdbe81fedd | ||
|
|
f0142e3b1a | ||
|
|
2c6ff00261 | ||
|
|
61a21203f4 | ||
|
|
d7affc5028 | ||
|
|
eb9549a818 | ||
|
|
6d9906db0a | ||
|
|
334ee975b9 | ||
|
|
d600d0898c | ||
|
|
44f4efa5aa | ||
|
|
8151305701 | ||
|
|
3ac9bfc387 | ||
|
|
51b9cb85bc | ||
|
|
d805ff9782 | ||
|
|
fa9ba3f607 | ||
|
|
9d2be2851b | ||
|
|
2639338426 | ||
|
|
d8daa4ac83 | ||
|
|
6641155892 | ||
|
|
86034353ec | ||
|
|
944ef0fc8d | ||
|
|
194b7c4772 | ||
|
|
6963e143a1 | ||
|
|
4977bd1d78 | ||
|
|
a21e641748 | ||
|
|
208b0f6d0e | ||
|
|
0bc546595d | ||
|
|
a673b4ad66 | ||
|
|
9a68a5c063 | ||
|
|
e790df539d | ||
|
|
fbeb779335 | ||
|
|
625bfa5713 | ||
|
|
943a294900 | ||
|
|
3400dd5e42 | ||
|
|
e8b3c44de3 | ||
|
|
9575698fa4 | ||
|
|
5f0a7dd63e | ||
|
|
38bc11b7b8 | ||
|
|
04528aee1f | ||
|
|
5c82747970 | ||
|
|
b45c337515 | ||
|
|
9dc6b0e660 | ||
|
|
428165590e | ||
|
|
53051e04a3 | ||
|
|
869bcf8b2b | ||
|
|
382a65abcd | ||
|
|
908d705669 | ||
|
|
d422fb699f | ||
|
|
506e63284b | ||
|
|
ed92476916 | ||
|
|
1218afd51f | ||
|
|
b88722b4a6 | ||
|
|
694d297eb8 | ||
|
|
2053f2767b | ||
|
|
448176b0b7 | ||
|
|
b1a22b5002 | ||
|
|
e9aee55714 | ||
|
|
2acf3c57c7 | ||
|
|
f2babb6063 | ||
|
|
b8d07474fe | ||
|
|
cf913b41c6 | ||
|
|
adb3df05c6 | ||
|
|
34bded30eb | ||
|
|
774a375ce7 | ||
|
|
ee35001732 | ||
|
|
da4105ffb1 | ||
|
|
5c5692ae98 | ||
|
|
379d16abe7 | ||
|
|
078ce38c57 | ||
|
|
f99b903d78 | ||
|
|
3650b81970 | ||
|
|
bb6fcdfa6f | ||
|
|
8df112e157 | ||
|
|
af91f35017 | ||
|
|
3cce90bba1 | ||
|
|
1a351bdb6d | ||
|
|
efaac43248 | ||
|
|
f082cac762 | ||
|
|
2bd289ebd6 | ||
|
|
aba9baf207 | ||
|
|
e755c36ea2 | ||
|
|
6c7dc2a28e | ||
|
|
cdd530d807 | ||
|
|
02c0db7b8b | ||
|
|
8a62c12089 | ||
|
|
988549599d | ||
|
|
70159c00cc | ||
|
|
2dfbdea889 | ||
|
|
299d1f530f | ||
|
|
bf0ff8abbc | ||
|
|
123b1c8f44 | ||
|
|
a314e05826 | ||
|
|
83fd24faa2 | ||
|
|
1d4ad5f1d5 | ||
|
|
26d5cc694a | ||
|
|
a2122e0153 | ||
|
|
10fc94aaa7 | ||
|
|
0835bada39 | ||
|
|
277af1a2b6 | ||
|
|
5b835bec3e | ||
|
|
dc23cef59a | ||
|
|
098079d317 | ||
|
|
1ab57ecf20 | ||
|
|
808ab56545 | ||
|
|
19acdc7a19 | ||
|
|
e15fdc709f | ||
|
|
457244e3de | ||
|
|
d5559ecafd | ||
|
|
802fbfcf1e | ||
|
|
a20e74c401 | ||
|
|
7cdb1ce9eb | ||
|
|
0d170a70b6 | ||
|
|
b19cd0b87d | ||
|
|
50efc95c83 | ||
|
|
fa50268b4e | ||
|
|
ae1d51d089 | ||
|
|
1b8355ff07 | ||
|
|
f32afb70b8 | ||
|
|
60d96ca7b7 | ||
|
|
014f734260 | ||
|
|
de4963ef95 | ||
|
|
e7d3e60ebd | ||
|
|
a46f73ad24 | ||
|
|
759bc1d909 | ||
|
|
c79c9dac8d | ||
|
|
635d4babc4 | ||
|
|
9b3b4ae8be | ||
|
|
b3e7f074e9 | ||
|
|
ee1ed73fc5 | ||
|
|
10e11fb742 | ||
|
|
8b47317ec7 | ||
|
|
04626b72cd | ||
|
|
2151cd0929 | ||
|
|
93ded9c1e0 | ||
|
|
20964b43ce | ||
|
|
af192354fd | ||
|
|
ad48637cbb | ||
|
|
89507bd335 | ||
|
|
21533ffee4 | ||
|
|
4502a9286c | ||
|
|
ba11511c69 | ||
|
|
965ef19a5b | ||
|
|
8f86b331c1 | ||
|
|
59a1590955 | ||
|
|
fad87b294b | ||
|
|
13bb2b6690 | ||
|
|
4a803ed0cf | ||
|
|
9e80f1b26c | ||
|
|
9299c78747 | ||
|
|
e1a125e326 | ||
|
|
a13eb99962 | ||
|
|
d46733e11a | ||
|
|
ce569462f6 |
136
.github/workflows/main.yml
vendored
136
.github/workflows/main.yml
vendored
@@ -10,6 +10,7 @@ env:
|
||||
LLVM_RELEASE_VERSION_WINDOWS: 18
|
||||
LLVM_RELEASE_VERSION_MAC: 17
|
||||
LLVM_RELEASE_VERSION_LINUX: 17
|
||||
LLVM_RELEASE_VERSION_OPENBSD: 19
|
||||
LLVM_RELEASE_VERSION_UBUNTU22: 17
|
||||
LLVM_DEV_VERSION: 21
|
||||
jobs:
|
||||
@@ -52,9 +53,9 @@ jobs:
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
cd resources/testproject
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe -vvv --emit-llvm run hello_world_win32 --trust=full
|
||||
dir build\llvm\windows-x64
|
||||
dir out\llvm\windows-x64
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe clean
|
||||
dir build\llvm\windows-x64
|
||||
dir out\llvm\windows-x64
|
||||
|
||||
|
||||
- name: Build testproject lib
|
||||
@@ -90,7 +91,7 @@ jobs:
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-test unit -O1
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-test unit -O1 -D SLOW_TESTS
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
@@ -273,6 +274,7 @@ jobs:
|
||||
-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_LLVM_VERSION=${{matrix.llvm_version}}
|
||||
cmake --build build
|
||||
- name: CMake18
|
||||
@@ -287,6 +289,7 @@ jobs:
|
||||
-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_LLVM_VERSION=${{matrix.llvm_version}}.1
|
||||
cmake --build build
|
||||
|
||||
@@ -345,7 +348,7 @@ jobs:
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
../build/c3c compile-test unit
|
||||
../build/c3c compile-test unit -D SLOW_TESTS
|
||||
|
||||
- name: Build testproject
|
||||
run: |
|
||||
@@ -439,6 +442,7 @@ jobs:
|
||||
-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_LLVM_VERSION=${{matrix.llvm_version}}
|
||||
cmake --build build
|
||||
- name: CMake
|
||||
@@ -453,6 +457,7 @@ jobs:
|
||||
-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_LLVM_VERSION=${{matrix.llvm_version}}.1
|
||||
cmake --build build
|
||||
- name: Compile and run some examples
|
||||
@@ -489,7 +494,7 @@ jobs:
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
../build/c3c compile-test unit --sanitize=address
|
||||
../build/c3c compile-test unit --sanitize=address -D SLOW_TESTS
|
||||
|
||||
- name: Build testproject
|
||||
run: |
|
||||
@@ -583,7 +588,7 @@ jobs:
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
../build/c3c compile-test unit
|
||||
../build/c3c compile-test unit -D SLOW_TESTS
|
||||
|
||||
- name: Build testproject
|
||||
run: |
|
||||
@@ -665,7 +670,7 @@ jobs:
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
../build/c3c compile-test unit -O1
|
||||
../build/c3c compile-test unit -O1 -D SLOW_TESTS
|
||||
|
||||
- name: Test WASM
|
||||
run: |
|
||||
@@ -747,6 +752,119 @@ jobs:
|
||||
nix build -L ".#c3c-checks"
|
||||
fi
|
||||
|
||||
build-openbsd:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
# Don't abort runners if a single one fails
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Release, Debug]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: OpenBSD VM
|
||||
uses: vmactions/openbsd-vm@main
|
||||
with:
|
||||
prepare: |
|
||||
pkg_add cmake llvm-19.1.7p3 ninja
|
||||
|
||||
run: |
|
||||
echo "CMake"
|
||||
cmake -B build \
|
||||
-G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
|
||||
-DLLVM_ENABLE_LIBXML2=OFF \
|
||||
-DC3_LLVM_VERSION=${LLVM_RELEASE_VERSION_OPENBSD}
|
||||
cmake --build build
|
||||
echo "Compile and run some examples"
|
||||
cd resources
|
||||
../build/c3c compile examples/base64.c3
|
||||
../build/c3c compile examples/binarydigits.c3
|
||||
../build/c3c compile examples/brainfk.c3
|
||||
../build/c3c compile examples/factorial_macro.c3
|
||||
../build/c3c compile examples/fasta.c3
|
||||
../build/c3c compile examples/gameoflife.c3
|
||||
../build/c3c compile examples/hash.c3
|
||||
../build/c3c compile-only examples/levenshtein.c3
|
||||
../build/c3c compile examples/load_world.c3
|
||||
../build/c3c compile-only examples/map.c3
|
||||
../build/c3c compile examples/mandelbrot.c3
|
||||
../build/c3c compile examples/plus_minus.c3
|
||||
../build/c3c compile examples/nbodies.c3
|
||||
../build/c3c compile examples/spectralnorm.c3
|
||||
../build/c3c compile examples/swap.c3
|
||||
../build/c3c compile examples/contextfree/boolerr.c3
|
||||
../build/c3c compile examples/contextfree/dynscope.c3
|
||||
../build/c3c compile examples/contextfree/guess_number.c3
|
||||
../build/c3c compile examples/contextfree/multi.c3
|
||||
../build/c3c compile examples/contextfree/cleanup.c3
|
||||
../build/c3c compile-run examples/hello_world_many.c3
|
||||
../build/c3c compile-run examples/time.c3
|
||||
../build/c3c compile-run examples/fannkuch-redux.c3
|
||||
../build/c3c compile-run examples/contextfree/boolerr.c3
|
||||
../build/c3c compile-run examples/load_world.c3
|
||||
../build/c3c compile-run examples/process.c3
|
||||
../build/c3c compile-run examples/ls.c3
|
||||
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
|
||||
cd ..
|
||||
echo "Compile and run dynlib-test"
|
||||
cd resources/examples/dynlib-test
|
||||
../../../build/c3c -vv dynamic-lib add.c3
|
||||
mv add.so libadd.so
|
||||
cc test.c -L. -ladd -Wl,-rpath=.
|
||||
./a.out
|
||||
../../../build/c3c compile-run test.c3 -L . -l add -z -Wl,-rpath=.
|
||||
cd ../../../
|
||||
echo "Compile and run staticlib-test"
|
||||
cd resources/examples/staticlib-test
|
||||
../../../build/c3c -vv static-lib add.c3
|
||||
mv add.a libadd.a
|
||||
cc test.c -L. -ladd
|
||||
./a.out
|
||||
../../../build/c3c compile-run test.c3 -L . -l add
|
||||
cd ../../../
|
||||
echo "Compile run unit tests"
|
||||
cd test
|
||||
../build/c3c --max-mem 128 compile-test unit -D SLOW_TESTS
|
||||
cd ..
|
||||
echo "Build testproject"
|
||||
cd resources/testproject
|
||||
../../build/c3c run -vvv --trust=full
|
||||
cd ../../
|
||||
echo "Test WASM"
|
||||
cd resources/testfragments
|
||||
../../build/c3c compile --target wasm32 -g0 --no-entry -Os wasm4.c3
|
||||
cd ../../
|
||||
echo "Build testproject direct linker"
|
||||
cd resources/testproject
|
||||
../../build/c3c run -vvv --linker=builtin --trust=full
|
||||
cd ../../
|
||||
echo "Init a library & a project"
|
||||
./build/c3c init-lib mylib
|
||||
ls mylib.c3l
|
||||
./build/c3c init myproject
|
||||
ls myproject
|
||||
echo "run compiler tests"
|
||||
cd test
|
||||
../build/c3c --max-mem 128 compile-run -O1 src/test_suite_runner.c3 -- ../build/c3c test_suite/
|
||||
cd ..
|
||||
|
||||
- name: bundle_output
|
||||
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-openbsd-${{matrix.build_type}}.tar.gz c3
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: c3-openbsd-${{matrix.build_type}}
|
||||
path: c3-openbsd-${{matrix.build_type}}.tar.gz
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [build-msvc, build-linux, build-mac, build-linux-ubuntu22]
|
||||
@@ -769,6 +887,8 @@ jobs:
|
||||
- run: zip -r c3-windows-debug.zip c3-windows-Debug
|
||||
- run: mv c3-linux-Release/c3-linux-Release.tar.gz c3-linux-Release/c3-linux.tar.gz
|
||||
- run: mv c3-linux-Debug/c3-linux-Debug.tar.gz c3-linux-Debug/c3-linux-debug.tar.gz
|
||||
- run: mv c3-openbsd-Release/c3-openbsd-Release.tar.gz c3-openbsd-Release/c3-openbsd.tar.gz
|
||||
- run: mv c3-openbsd-Debug/c3-openbsd-Debug.tar.gz c3-openbsd-Debug/c3-openbsd-debug.tar.gz
|
||||
- run: mv c3-ubuntu-22-Release/c3-ubuntu-22-Release.tar.gz c3-ubuntu-22-Release/c3-ubuntu-22.tar.gz
|
||||
- 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
|
||||
@@ -790,6 +910,8 @@ jobs:
|
||||
c3-windows-debug.zip
|
||||
c3-linux-Release/c3-linux.tar.gz
|
||||
c3-linux-Debug/c3-linux-debug.tar.gz
|
||||
c3-openbsd-Release/c3-openbsd.tar.gz
|
||||
c3-openbsd-Debug/c3-openbsd-debug.tar.gz
|
||||
c3-ubuntu-22-Release/c3-ubuntu-22.tar.gz
|
||||
c3-ubuntu-22-Debug/c3-ubuntu-22-debug.tar.gz
|
||||
c3-macos-Release/c3-macos.zip
|
||||
|
||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -1,5 +1,7 @@
|
||||
# Prerequisites
|
||||
*.d
|
||||
testrun
|
||||
benchmarkrun
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
@@ -7,6 +9,8 @@
|
||||
*.obj
|
||||
*.elf
|
||||
*.ll
|
||||
*.wasm
|
||||
*.s
|
||||
|
||||
# Linker output
|
||||
*.ilk
|
||||
@@ -76,8 +80,10 @@ TAGS
|
||||
/.cache/
|
||||
/compile_commands.json
|
||||
|
||||
# 'nix build' resulting symlink
|
||||
# Nix
|
||||
result
|
||||
/.envrc
|
||||
/.direnv/
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
@@ -85,4 +91,4 @@ result
|
||||
# tests
|
||||
/test/tmp/*
|
||||
/test/testrun
|
||||
/test/test_suite_runner
|
||||
/test/test_suite_runner
|
||||
|
||||
258
CMakeLists.txt
258
CMakeLists.txt
@@ -1,5 +1,13 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
set(C3_LLVM_MIN_VERSION 17)
|
||||
set(C3_LLVM_MAX_VERSION 21)
|
||||
set(C3_LLVM_DEFAULT_VERSION 19)
|
||||
|
||||
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR)
|
||||
message(FATAL_ERROR "In-tree build detected, please build in a separate directory")
|
||||
endif()
|
||||
|
||||
# Grab the version
|
||||
file(READ "src/version.h" ver)
|
||||
if (NOT ${ver} MATCHES "COMPILER_VERSION \"([0-9]+.[0-9]+.[0-9]+)\"")
|
||||
@@ -7,8 +15,20 @@ if (NOT ${ver} MATCHES "COMPILER_VERSION \"([0-9]+.[0-9]+.[0-9]+)\"")
|
||||
endif()
|
||||
|
||||
# Set the project and version
|
||||
project(c3c VERSION ${CMAKE_MATCH_1})
|
||||
message("C3C version: ${CMAKE_PROJECT_VERSION}")
|
||||
project(c3c VERSION ${CMAKE_MATCH_1} LANGUAGES C CXX)
|
||||
message("Configuring C3C ${CMAKE_PROJECT_VERSION} for ${CMAKE_SYSTEM_NAME}")
|
||||
|
||||
# Helper functions
|
||||
function(c3_print_variables)
|
||||
set(msg "")
|
||||
foreach(var ${ARGN})
|
||||
if(msg)
|
||||
string(APPEND msg " ; ")
|
||||
endif()
|
||||
string(APPEND msg "${c3_print_prefix}${var}=\"${${var}}\"")
|
||||
endforeach()
|
||||
message(STATUS "${msg}")
|
||||
endfunction()
|
||||
|
||||
# Avoid warning for FetchContent
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
|
||||
@@ -16,7 +36,7 @@ if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED CMAKE_INSTALL_LIBDIR)
|
||||
if (MSVC)
|
||||
if (WIN32)
|
||||
set(CMAKE_INSTALL_LIBDIR "c:\\c3c\\lib")
|
||||
set(CMAKE_INSTALL_BINDIR "c:\\c3c")
|
||||
else ()
|
||||
@@ -36,36 +56,41 @@ set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
# Use /MT or /MTd
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
|
||||
if(MSVC)
|
||||
message(STATUS "MSVC version ${MSVC_VERSION}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2 /EHsc /utf-8")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /O2 /EHsc /utf-8")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi /EHa /utf-8")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Od /Zi /EHa /utf-8")
|
||||
add_compile_options(/utf-8)
|
||||
else()
|
||||
if (true)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O0 -fno-exceptions")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -gdwarf-3 -O0 -fno-exceptions")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3 -fno-exceptions")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3 -fno-exceptions")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -gdwarf-3 -O3 -fsanitize=undefined,address -fno-exceptions")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -gdwarf-3 -O1 -fsanitize=undefined,address -fno-exceptions")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3 -fsanitize=undefined,address -fno-exceptions")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3 -O1 -fsanitize=undefined,address -fno-exceptions")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined,address -fno-exceptions")
|
||||
endif()
|
||||
add_compile_options(-gdwarf-3 -fno-exceptions)
|
||||
|
||||
# add_compile_options(-fsanitize=address,undefined)
|
||||
# add_link_options(-fsanitize=address,undefined)
|
||||
endif()
|
||||
|
||||
option(C3_LINK_DYNAMIC "link dynamically with LLVM/LLD libs")
|
||||
# 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_LLVM_VERSION "auto" CACHE STRING "Use LLVM version [default: auto]")
|
||||
option(C3_USE_MIMALLOC "Use built-in mimalloc" OFF)
|
||||
option(C3_USE_TB "Use TB" OFF)
|
||||
set(C3_MIMALLOC_TAG "v1.7.3" CACHE STRING "Used version of mimalloc")
|
||||
option(C3_WITH_LLVM "Build with LLVM" ON)
|
||||
option(C3_LLD_DIR "Use custom LLD directory" "")
|
||||
option(LLVM_CRT_LIBRARY_DIR "Use custom llvm's compiler-rt directory" "")
|
||||
set(C3_OPTIONS
|
||||
C3_LINK_DYNAMIC
|
||||
C3_WITH_LLVM
|
||||
C3_LLVM_VERSION
|
||||
C3_USE_MIMALLOC
|
||||
C3_MIMALLOC_TAG
|
||||
C3_USE_TB
|
||||
C3_LLD_DIR
|
||||
C3_ENABLE_CLANGD_LSP
|
||||
LLVM_CRT_LIBRARY_DIR
|
||||
)
|
||||
|
||||
set(C3_USE_MIMALLOC OFF)
|
||||
if(C3_USE_MIMALLOC)
|
||||
@@ -83,13 +108,6 @@ endif()
|
||||
if (NOT WIN32)
|
||||
find_package(CURL)
|
||||
endif()
|
||||
if(C3_WITH_LLVM)
|
||||
if (NOT C3_LLVM_VERSION STREQUAL "auto")
|
||||
if (${C3_LLVM_VERSION} VERSION_LESS 17 OR ${C3_LLVM_VERSION} VERSION_GREATER 21)
|
||||
message(FATAL_ERROR "LLVM ${C3_LLVM_VERSION} is not supported!")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(Git QUIET)
|
||||
if(C3_USE_TB AND GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
@@ -107,7 +125,6 @@ if(C3_USE_TB AND GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
endif()
|
||||
|
||||
# Clangd LSP support
|
||||
option(C3_ENABLE_CLANGD_LSP "Enable/Disable output of compile commands during generation." OFF)
|
||||
if(C3_ENABLE_CLANGD_LSP)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
execute_process(
|
||||
@@ -120,7 +137,7 @@ endif(C3_ENABLE_CLANGD_LSP)
|
||||
if(C3_WITH_LLVM)
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||
if (C3_LLVM_VERSION STREQUAL "auto")
|
||||
set(C3_LLVM_VERSION "19")
|
||||
set(C3_LLVM_VERSION ${C3_LLVM_DEFAULT_VERSION})
|
||||
endif()
|
||||
FetchContent_Declare(
|
||||
LLVM_Windows
|
||||
@@ -139,6 +156,7 @@ if(C3_WITH_LLVM)
|
||||
FetchContent_MakeAvailable(LLVM_Windows)
|
||||
set(llvm_dir ${llvm_windows_SOURCE_DIR})
|
||||
endif()
|
||||
message("Loaded Windows LLVM libraries into ${llvm_dir}")
|
||||
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_dir} ${CMAKE_SYSTEM_PREFIX_PATH})
|
||||
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
@@ -149,7 +167,7 @@ if(C3_WITH_LLVM)
|
||||
#
|
||||
# Because of CMAKE_FIND_PACKAGE_SORT_ORDER CMAKE_FIND_PACKAGE_SORT_DIRECTION,
|
||||
# the newest version will always be found first.
|
||||
message(STATUS "CMAKE_PREFIX_PATH: ${CMAKE_PREFIX_PATH}")
|
||||
c3_print_variables(CMAKE_PREFIX_PATH)
|
||||
if (DEFINED LLVM_DIR)
|
||||
message(STATUS "Looking for LLVM CMake files in user-specified directory ${LLVM_DIR}")
|
||||
else()
|
||||
@@ -176,12 +194,15 @@ if(C3_WITH_LLVM)
|
||||
list(APPEND LLVM_LIBRARY_DIRS /usr/lib)
|
||||
endif()
|
||||
|
||||
list(REMOVE_DUPLICATES LLVM_LIBRARY_DIRS)
|
||||
|
||||
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
|
||||
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
|
||||
message(STATUS "Libraries located in: ${LLVM_LIBRARY_DIRS}")
|
||||
message(STATUS "LLVM libraries located in: ${LLVM_LIBRARY_DIRS}")
|
||||
|
||||
if (NOT LLVM_PACKAGE_VERSION VERSION_GREATER_EQUAL 15.0)
|
||||
message(FATAL_ERROR "LLVM version 15.0 or later is required.")
|
||||
if (${LLVM_PACKAGE_VERSION} VERSION_LESS C3_LLVM_MIN_VERSION OR
|
||||
${LLVM_PACKAGE_VERSION} VERSION_GREATER C3_LLVM_MAX_VERSION)
|
||||
message(FATAL_ERROR "LLVM ${LLVM_PACKAGE_VERSION} is not supported! LLVM version between ${C3_LLVM_MIN_VERSION} and ${C3_LLVM_MAX_VERSION} is required.")
|
||||
endif()
|
||||
|
||||
if(LLVM_ENABLE_RTTI)
|
||||
@@ -231,44 +252,43 @@ if(C3_WITH_LLVM)
|
||||
llvm_map_components_to_libnames(llvm_libs ${LLVM_LINK_COMPONENTS})
|
||||
|
||||
if(NOT ${C3_LLD_DIR} EQUAL "" AND EXISTS ${C3_LLD_DIR})
|
||||
message("C3_LLD_DIR: " ${C3_LLD_DIR})
|
||||
set(LLVM_LIBRARY_DIRS
|
||||
"${LLVM_LIBRARY_DIRS}"
|
||||
"${C3_LLD_DIR}"
|
||||
)
|
||||
list(APPEND LLVM_LIBRARY_DIRS ${C3_LLD_DIR})
|
||||
list(REMOVE_DUPLICATES LLVM_LIBRARY_DIRS)
|
||||
endif()
|
||||
|
||||
message(STATUS "Looking for static lld libraries in ${LLVM_LIBRARY_DIRS}")
|
||||
|
||||
# These don't seem to be reliable on windows.
|
||||
message(STATUS "using find_library")
|
||||
find_library(LLD_COFF NAMES liblldCOFF.dylib lldCOFF.lib lldCOFF.a liblldCOFF.dll.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_COMMON NAMES liblldCommon.dylib lldCommon.lib lldCommon.a liblldCommon.dll.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_ELF NAMES liblldELF.dylib lldELF.lib lldELF.a liblldELF.dll.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_MACHO NAMES liblldMachO.dylib lldMachO.lib lldMachO.a liblldMachO.dll.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_MINGW NAMES liblldMinGW.dylib lldMinGW.lib lldMinGW.a liblldMinGW.dll.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_WASM NAMES liblldWasm.dylib lldWasm.lib lldWasm.a liblldWasm.dll.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_COFF NAMES liblldCOFF.dylib lldCOFF.lib lldCOFF.a liblldCOFF.dll.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_COMMON NAMES liblldCommon.dylib lldCommon.lib lldCommon.a liblldCommon.dll.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_ELF NAMES liblldELF.dylib lldELF.lib lldELF.a liblldELF.dll.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
|
||||
find_library(LLD_MACHO NAMES liblldMachO.dylib lldMachO.lib lldMachO.a liblldMachO.dll.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
else()
|
||||
set(LLD_MACHO "")
|
||||
endif()
|
||||
find_library(LLD_MINGW NAMES liblldMinGW.dylib lldMinGW.lib lldMinGW.a liblldMinGW.dll.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_WASM NAMES liblldWasm.dylib lldWasm.lib lldWasm.a liblldWasm.dll.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
else()
|
||||
find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
message(STATUS "Looking for shared lld libraries in ${LLVM_LIBRARY_DIRS}")
|
||||
|
||||
find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
set(llvm_libs ${LLVM})
|
||||
|
||||
# These don't seem to be reliable on windows.
|
||||
message(STATUS "using find_library")
|
||||
find_library(LLD_COFF NAMES liblldCOFF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_COMMON NAMES liblldCommon.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_ELF NAMES liblldELF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_MACHO NAMES liblldMachO.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_MINGW NAMES liblldMinGW.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_WASM NAMES liblldWasm.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_COFF NAMES liblldCOFF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_COMMON NAMES liblldCommon.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_ELF NAMES liblldELF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
|
||||
find_library(LLD_MACHO NAMES liblldMachO.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
else()
|
||||
set(LLD_MACHO "")
|
||||
endif()
|
||||
find_library(LLD_MINGW NAMES liblldMinGW.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_WASM NAMES liblldWasm.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT(${CMAKE_BINARY_DIR} EQUAL ${CMAKE_SOURCE_DIR}))
|
||||
file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/lib)
|
||||
file(COPY ${CMAKE_SOURCE_DIR}/lib DESTINATION ${CMAKE_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
if(C3_WITH_LLVM)
|
||||
find_library(LLD_LOONG NAMES libLLVMLoongArchCodeGen.lib libLLVMLoongArchAsmParser.lib libLLVMLoongArchCodeGen.a libLLVMLoongArchAsmParser.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
# find_library(LLD_LOONG NAMES libLLVMLoongArchCodeGen.lib libLLVMLoongArchAsmParser.lib libLLVMLoongArchCodeGen.a libLLVMLoongArchAsmParser.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
set(lld_libs
|
||||
${LLD_COFF}
|
||||
${LLD_WASM}
|
||||
@@ -293,8 +313,8 @@ if(C3_WITH_LLVM)
|
||||
)
|
||||
endif()
|
||||
|
||||
message(STATUS "linking to llvm libs ${lld_libs}")
|
||||
message(STATUS "Found lld libs ${lld_libs}")
|
||||
message(STATUS "Linking to llvm libs ${llvm_libs}")
|
||||
message(STATUS "Linking to lld libs ${lld_libs}")
|
||||
endif()
|
||||
|
||||
add_library(miniz STATIC dependencies/miniz/miniz.c)
|
||||
@@ -370,6 +390,7 @@ add_executable(c3c
|
||||
src/utils/unzipper.c
|
||||
src/compiler/c_codegen.c
|
||||
src/compiler/decltable.c
|
||||
src/compiler/methodtable.c
|
||||
src/compiler/mac_support.c
|
||||
src/compiler/windows_support.c
|
||||
src/compiler/codegen_asm.c
|
||||
@@ -412,6 +433,11 @@ if(C3_WITH_LLVM)
|
||||
|
||||
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=1)
|
||||
add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp)
|
||||
if (MSVC)
|
||||
target_compile_options(c3c PRIVATE
|
||||
"$<$<CONFIG:Debug>:/EHa>"
|
||||
"$<$<CONFIG:Release>:/EHsc>")
|
||||
endif()
|
||||
else()
|
||||
target_sources(c3c PRIVATE src/utils/hostinfo.c)
|
||||
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=0)
|
||||
@@ -495,7 +521,7 @@ endif ()
|
||||
|
||||
if (CURL_FOUND)
|
||||
target_link_libraries(c3c ${CURL_LIBRARIES})
|
||||
target_include_directories(c3c PRIVATE ${CURL_INCLUDES})
|
||||
target_include_directories(c3c PRIVATE ${CURL_INCLUDE_DIRS})
|
||||
target_compile_definitions(c3c PUBLIC CURL_FOUND=1)
|
||||
else()
|
||||
target_compile_definitions(c3c PUBLIC CURL_FOUND=0)
|
||||
@@ -503,34 +529,27 @@ endif()
|
||||
|
||||
|
||||
if(MSVC)
|
||||
message("Adding MSVC options")
|
||||
target_compile_options(c3c PRIVATE /wd4068 /wd4090 /WX /Wv:18)
|
||||
target_compile_options(c3c PRIVATE
|
||||
/wd4068
|
||||
/wd4090
|
||||
/WX
|
||||
/Wv:18
|
||||
)
|
||||
|
||||
if(C3_WITH_LLVM)
|
||||
target_compile_options(c3c_wrappers PUBLIC /wd4624 /wd4267 /wd4244 /WX /Wv:18)
|
||||
target_compile_options(c3c_wrappers PUBLIC
|
||||
/wd4624
|
||||
/wd4267
|
||||
/wd4244
|
||||
/WX
|
||||
/Wv:18
|
||||
)
|
||||
if(NOT LLVM_ENABLE_RTTI)
|
||||
target_compile_options(c3c_wrappers PUBLIC /GR-)
|
||||
endif()
|
||||
target_link_options(c3c_wrappers PUBLIC /ignore:4099)
|
||||
endif()
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
target_compile_options(c3c PUBLIC /MTd)
|
||||
if (C3_WITH_LLVM)
|
||||
target_compile_options(c3c_wrappers PUBLIC /MTd)
|
||||
endif()
|
||||
target_compile_options(miniz PUBLIC /MTd)
|
||||
if (C3_USE_TB)
|
||||
target_compile_options(tilde-backend PUBLIC /MTd)
|
||||
endif()
|
||||
else()
|
||||
target_compile_options(c3c PUBLIC /MT)
|
||||
if (C3_WITH_LLVM)
|
||||
target_compile_options(c3c_wrappers PUBLIC /MT)
|
||||
endif()
|
||||
target_compile_options(miniz PUBLIC /MT)
|
||||
if (C3_USE_TB)
|
||||
target_compile_options(tilde-backend PUBLIC /MT)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(C3_WITH_LLVM)
|
||||
set(clang_lib_dir ${llvm_dir}/lib/clang/${C3_LLVM_VERSION}/lib/windows)
|
||||
set(sanitizer_runtime_libraries
|
||||
@@ -540,13 +559,20 @@ if(MSVC)
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "using gcc/clang warning switches")
|
||||
target_link_options(c3c PRIVATE -pthread)
|
||||
if (C3_WITH_LLVM AND NOT LLVM_ENABLE_RTTI)
|
||||
target_compile_options(c3c_wrappers PRIVATE -fno-rtti)
|
||||
endif()
|
||||
target_compile_options(c3c PRIVATE -pthread -Wall -Werror -Wno-unknown-pragmas -Wno-unused-result
|
||||
-Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
|
||||
target_compile_options(c3c PRIVATE
|
||||
-pthread
|
||||
-Wall
|
||||
-Werror
|
||||
-Wno-unknown-pragmas
|
||||
-Wno-unused-result
|
||||
-Wno-unused-function
|
||||
-Wno-unused-variable
|
||||
-Wno-unused-parameter
|
||||
)
|
||||
target_link_options(c3c PRIVATE -pthread)
|
||||
endif()
|
||||
|
||||
install(TARGETS c3c DESTINATION bin)
|
||||
@@ -557,6 +583,12 @@ if (NOT WIN32)
|
||||
install(FILES c3c.1 DESTINATION "share/man/man1")
|
||||
endif()
|
||||
|
||||
# Copy stdlib
|
||||
if (NOT ${CMAKE_BINARY_DIR} EQUAL ${CMAKE_SOURCE_DIR})
|
||||
file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/lib)
|
||||
file(COPY ${CMAKE_SOURCE_DIR}/lib DESTINATION ${CMAKE_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
if (C3_WITH_LLVM AND DEFINED sanitizer_runtime_libraries)
|
||||
add_custom_command(TARGET c3c POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E rm -rf -- $<TARGET_FILE_DIR:c3c>/c3c_rt
|
||||
@@ -576,3 +608,35 @@ if (C3_WITH_LLVM AND DEFINED sanitizer_runtime_libraries)
|
||||
endif()
|
||||
|
||||
feature_summary(WHAT ALL)
|
||||
|
||||
message(STATUS "Building ${CMAKE_PROJECT_NAME} with the following configuration:")
|
||||
|
||||
set(c3_print_prefix " ")
|
||||
|
||||
foreach(option IN LISTS C3_OPTIONS)
|
||||
if (DEFINED ${option})
|
||||
c3_print_variables(${option})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
foreach(flag_var
|
||||
CMAKE_BUILD_TYPE
|
||||
CMAKE_C_COMPILER
|
||||
CMAKE_CXX_COMPILER
|
||||
CMAKE_LINKER
|
||||
CMAKE_OBJCOPY
|
||||
CMAKE_STRIP
|
||||
CMAKE_DLLTOOL)
|
||||
c3_print_variables(${flag_var})
|
||||
endforeach()
|
||||
|
||||
message(STATUS "Build flags:")
|
||||
foreach(flag_var
|
||||
CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
|
||||
CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO
|
||||
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
|
||||
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
|
||||
c3_print_variables(${flag_var})
|
||||
endforeach()
|
||||
|
||||
message(STATUS "Output to: \"${CMAKE_BINARY_DIR}\"")
|
||||
|
||||
57
CMakePresets.json
Normal file
57
CMakePresets.json
Normal file
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"version": 3,
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "windows-base",
|
||||
"hidden": true,
|
||||
"architecture": {
|
||||
"value": "x64"
|
||||
},
|
||||
"toolset": {
|
||||
"value": "host=x64"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows-vs-2022-release",
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"displayName": "Windows x64 Visual Studio 17 2022",
|
||||
"inherits": "windows-base",
|
||||
"binaryDir": "build",
|
||||
"cacheVariables": {
|
||||
"CMAKE_CONFIGURATION_TYPES": "Release;RelWithDebInfo",
|
||||
"CMAKE_BUILD_TYPE": "Release"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows-vs-2022-debug",
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"displayName": "Windows x64 Visual Studio 17 2022 (Debug)",
|
||||
"inherits": "windows-base",
|
||||
"binaryDir": "build-debug",
|
||||
"cacheVariables": {
|
||||
"CMAKE_CONFIGURATION_TYPES": "Debug",
|
||||
"CMAKE_BUILD_TYPE": "Debug"
|
||||
}
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "windows-vs-2022-debug",
|
||||
"displayName": "Debug",
|
||||
"configurePreset": "windows-vs-2022-debug",
|
||||
"configuration": "Debug"
|
||||
},
|
||||
{
|
||||
"name": "windows-vs-2022-release",
|
||||
"displayName": "Release",
|
||||
"configurePreset": "windows-vs-2022-release",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "windows-vs-2022-release-with-debug-info",
|
||||
"displayName": "RelWithDebInfo",
|
||||
"configurePreset": "windows-vs-2022-release",
|
||||
"configuration": "RelWithDebInfo"
|
||||
}
|
||||
]
|
||||
}
|
||||
179
LICENSE
179
LICENSE
@@ -1,165 +1,20 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
Copyright (c) 2022-2025 Christoffer Lernö and contributors
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
165
LICENSE_SRC
Normal file
165
LICENSE_SRC
Normal file
@@ -0,0 +1,165 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
@@ -1,20 +0,0 @@
|
||||
Copyright (c) 2022 Christoffer Lernö and contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
147
README.md
147
README.md
@@ -12,6 +12,7 @@ Precompiled binaries for the following operating systems are available:
|
||||
- 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).
|
||||
|
||||
The manual for C3 can be found at [www.c3-lang.org](http://www.c3-lang.org).
|
||||
|
||||
@@ -141,7 +142,7 @@ fn void main()
|
||||
|
||||
### Current status
|
||||
|
||||
The current stable version of the compiler is **version 0.7.2**.
|
||||
The current stable version of the compiler is **version 0.7.4**.
|
||||
|
||||
The upcoming 0.7.x releases will focus on expanding the standard library,
|
||||
fixing bugs and improving compile time analysis.
|
||||
@@ -176,12 +177,14 @@ The compiler is currently verified to compile on Linux, Windows and MacOS.
|
||||
| NetBSD x86 | Untested | Untested | No | Yes | Untested | Yes* |
|
||||
| NetBSD x64 | Untested | Untested | No | Yes | Untested | Yes* |
|
||||
| OpenBSD x86 | Untested | Untested | No | Yes | Untested | Yes* |
|
||||
| OpenBSD x64 | Untested | Untested | No | Yes | Untested | Yes* |
|
||||
| OpenBSD x64 | Yes* | Yes | Yes* | Yes | Untested | Yes* |
|
||||
| MCU x86 | No | Untested | No | No | No | Yes* |
|
||||
| Wasm32 | No | Yes | No | No | No | No |
|
||||
| Wasm64 | No | Untested | No | No | No | No |
|
||||
|
||||
*\* Inline asm is still a work in progress*
|
||||
*\* 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*
|
||||
|
||||
More platforms will be supported in the future.
|
||||
|
||||
@@ -227,6 +230,14 @@ This installs the latest prerelease build, as opposed to the latest released ver
|
||||
|
||||
(*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))
|
||||
2. Unpack executable and standard lib.
|
||||
3. Run `./c3c`.
|
||||
|
||||
(*Note that this is specifically for OpenBSD 7.7, running it on any other version is prone to ABI breaks)
|
||||
|
||||
#### Installing on Arch Linux
|
||||
Arch includes c3c in the official 'extra' repo. It can be easily installed the usual way:
|
||||
|
||||
@@ -253,6 +264,38 @@ cd c3c-git
|
||||
makepkg -si
|
||||
```
|
||||
|
||||
#### Installing via Nix
|
||||
|
||||
You can access `c3c` via [flake.nix](./flake.nix), which will contain the latest commit of the compiler. To add `c3c` to your `flake.nix`, do the following:
|
||||
```nix
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs?ref=nixpkgs-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
c3c.url = "github:c3lang/c3c";
|
||||
# Those are desired if you don't want to copy extra nixpkgs
|
||||
c3c.inputs = {
|
||||
nixpkgs.follows = "nixpkgs";
|
||||
flake-utils.follows = "flake-utils";
|
||||
};
|
||||
};
|
||||
|
||||
outputs = { self, ... } @ inputs: inputs.flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = import inputs.nixpkgs { inherit system; };
|
||||
c3c = inputs.c3c.packages.${system}.c3c;
|
||||
in
|
||||
{
|
||||
devShells.default = pkgs.mkShell {
|
||||
buildInputs = [
|
||||
pkgs.c3c
|
||||
];
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### 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.
|
||||
@@ -265,14 +308,15 @@ See the `build-with-docker.sh` script for more information on other configurable
|
||||
|
||||
#### Installing on OS X using Homebrew
|
||||
|
||||
2. Install CMake: `brew install cmake`
|
||||
3. Install LLVM 17+: `brew install llvm`
|
||||
4. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
|
||||
5. Enter the C3C directory `cd c3c`.
|
||||
6. Create a build directory `mkdir build`
|
||||
7. Change directory to the build directory `cd build`
|
||||
8. Set up CMake build for debug: `cmake ..`
|
||||
9. Build: `cmake --build .`
|
||||
1. Install [Homebrew](https://brew.sh/)
|
||||
2. Install LLVM 17+: `brew install llvm`
|
||||
3. Install lld: `brew install lld`
|
||||
4. Install CMake: `brew install cmake`
|
||||
5. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
|
||||
6. Enter the C3C directory `cd c3c`.
|
||||
7. Set up CMake build for debug: `cmake -B build -S .`
|
||||
8. Build: `cmake --build build`
|
||||
9. Change directory to the build directory `cd build`
|
||||
|
||||
#### Installing on Windows using Scoop
|
||||
|
||||
@@ -313,28 +357,35 @@ called `hello_world` or `hello_world.exe`depending on platform.
|
||||
1. Make sure you have Visual Studio 17 2022 installed or alternatively install the "Buildtools for Visual Studio" (https://aka.ms/vs/17/release/vs_BuildTools.exe) and then select "Desktop development with C++"
|
||||
2. Install CMake
|
||||
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
|
||||
4. Enter the C3C directory `cd c3c`.
|
||||
5. Set up the CMake build `cmake -B build -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=Release`
|
||||
6. Build: `cmake --build build --config Release`
|
||||
7. You should now have the c3c.exe
|
||||
4. Enter the C3C directory: `cd c3c`.
|
||||
5. Set up the CMake build: `cmake --preset windows-vs-2022-release`
|
||||
6. Build: `cmake --build --preset windows-vs-2022-release`
|
||||
|
||||
You should now have a `c3c` executable.
|
||||
You should now have a `c3c` executable in `build\Release`.
|
||||
|
||||
You can try it out by running some sample code: `c3c.exe compile ../resources/examples/hash.c3`
|
||||
You can try it out by running some sample code: `c3c.exe compile ../../resources/examples/hash.c3`
|
||||
|
||||
Building `c3c` using Visual Studio Code is also supported when using the `CMake Tools` extension. Simply select the `Windows x64 Visual Studio 17 2022` configure preset and build.
|
||||
|
||||
*Note that if you run into linking issues when building, make sure that you are using the latest version of VS17.*
|
||||
|
||||
#### Compiling on Windows (Debug)
|
||||
|
||||
Debug build requires a different set of LLVM libraries to be loaded for which a separate CMake configuration is used to avoid conflicts.
|
||||
1. Configure: `cmake --preset windows-vs-2022-debug`
|
||||
2. Build: `cmake --build --preset windows-vs-2022-debug`
|
||||
|
||||
You should now have a `c3c` executable in `build-debug\Debug`.
|
||||
|
||||
#### Compiling on Ubuntu 24.04 LTS
|
||||
|
||||
1. Make sure you have a C compiler that handles C11 and a C++ compiler, such as GCC or Clang. Git also needs to be installed.
|
||||
2. Install LLVM 18 `sudo apt-get install cmake git clang zlib1g zlib1g-dev libllvm18 llvm llvm-dev llvm-runtime liblld-dev liblld-18 libpolly-18-dev`
|
||||
2. Install LLVM 18 `sudo apt-get install cmake git clang zlib1g zlib1g-dev libllvm18 llvm llvm-dev llvm-runtime liblld-dev liblld-18 libpolly-18-dev`. If you're using Ubuntu 25.04, also install `libpolly-20-dev`.
|
||||
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
|
||||
4. Enter the C3C directory `cd c3c`.
|
||||
5. Create a build directory `mkdir build`
|
||||
6. Change directory to the build directory `cd build`
|
||||
7. Set up CMake build: `cmake ..`
|
||||
8. Build: `cmake --build .`
|
||||
5. Set up CMake build: `cmake -B build -S .`
|
||||
6. Build: `cmake --build build`
|
||||
7. Change directory to the build directory `cd build`
|
||||
|
||||
You should now have a `c3c` executable.
|
||||
|
||||
@@ -347,13 +398,12 @@ You can try it out by running some sample code: `./c3c compile ../resources/exam
|
||||
2. Clone the C3C repository: `git clone https://github.com/c3lang/c3c.git`
|
||||
- If you only need the latest commit, you may want to make a shallow clone instead: `git clone https://github.com/c3lang/c3c.git --depth=1`
|
||||
3. Enter the directory: `cd c3c`
|
||||
4. Create a build directory: `mkdir build`
|
||||
5. Enter the build directory: `cd build`
|
||||
6. Create the CMake build cache: `cmake ..`
|
||||
7. Build: `cmake --build .`
|
||||
4. Create the CMake build cache: `cmake -B build -S .`
|
||||
5. Build: `cmake --build build`
|
||||
6. Enter the build directory: `cd build`
|
||||
|
||||
Your c3c executable should have compiled properly. You may want to test it: `./c3c compile ../resources/examples/hash.c3`
|
||||
For a sytem-wide installation, run the following as root: `cmake --install .`
|
||||
For a system-wide installation, run the following as root: `cmake --install .`
|
||||
|
||||
|
||||
#### Compiling on Fedora
|
||||
@@ -363,15 +413,15 @@ For a sytem-wide installation, run the following as root: `cmake --install .`
|
||||
3. Clone the C3C repository: `git clone https://github.com/c3lang/c3c.git`
|
||||
- If you only need the latest commit, you may want to make a shallow clone: `git clone https://github.com/c3lang/c3c.git --depth=1`
|
||||
4. Enter the C3C directory: `cd c3c`
|
||||
5. Create a build directory and navigate into it: `mkdir build && cd build`
|
||||
6. Create the CMake build cache. The Fedora repositories provide `.so` libraries for lld, so you need to set the C3_LINK_DYNAMIC flag: `cmake .. -DC3_LINK_DYNAMIC=1`
|
||||
7. Build the project: `cmake --build .`
|
||||
5. Create the CMake build cache. The Fedora repositories provide `.so` libraries for lld, so you need to set the C3_LINK_DYNAMIC flag: `cmake -B build -S . -DC3_LINK_DYNAMIC=1`
|
||||
6. Build the project: `cmake --build build`
|
||||
7. Enter the build directory: `cd build`
|
||||
|
||||
The c3c binary should be created in the build directory. You can try it out by running some sample code: `./c3c compile ../resources/examples/hash.c3`
|
||||
|
||||
#### Compiling on Arch Linux
|
||||
|
||||
1. Install required project dependencies: `sudo pacman -S curl lld llvm-libs clang cmake git libedit llvm`
|
||||
1. Install required project dependencies: `sudo pacman -S curl lld llvm-libs clang cmake git libedit llvm libxml2`
|
||||
2. Clone the C3C repository: `git clone https://github.com/c3lang/c3c.git`
|
||||
- If you only need the latest commit, you may want to make a shallow clone: `git clone https://github.com/c3lang/c3c.git --depth=1`
|
||||
3. Enter the C3C directory: `cd c3c`
|
||||
@@ -381,23 +431,30 @@ cmake -B build \
|
||||
-D C3_LINK_DYNAMIC=ON \
|
||||
-D CMAKE_BUILD_TYPE=Release
|
||||
```
|
||||
5. Build the project: `make -C build`.
|
||||
5. Build the project: `cmake --build build`.
|
||||
|
||||
After compilation, the `c3c` binary will be located in the `build` directory. You can test it by compiling an example: `./build/c3c compile resources/examples/ls.c3`.
|
||||
|
||||
6. To install the compiler globally: `sudo cmake --install build`
|
||||
|
||||
#### Compiling on NixOS
|
||||
|
||||
1. Enter nix shell, by typing `nix develop` in root directory
|
||||
2. Configure cmake via `cmake . -Bbuild $=C3_CMAKE_FLAGS`. Note: passing `C3_CMAKE_FLAGS` is needed in due to generate `compile_commands.json` and find missing libs.
|
||||
4. Build it `cmake --build build`
|
||||
5. Test it out: `./build/c3c -V`
|
||||
6. If you use `clangd` lsp server for your editor, it is recommended to make a symbolic link to `compile_command.json` in the root: `ln -s ./build/compile_commands.json compile_commands.json`
|
||||
|
||||
#### Compiling on other Linux / Unix variants
|
||||
|
||||
1. Install CMake.
|
||||
2. Install or compile LLVM and LLD *libraries* (version 17+ or higher)
|
||||
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
|
||||
4. Enter the C3C directory `cd c3c`.
|
||||
5. Create a build directory `mkdir build`
|
||||
6. Change directory to the build directory `cd build`
|
||||
7. Set up CMake build for debug: `cmake ..`. At this point you may need to manually
|
||||
provide the link path to the LLVM CMake directories, e.g. `cmake -DLLVM_DIR=/usr/local/opt/llvm/lib/cmake/llvm/ ..`
|
||||
8. Build: `cmake --build .`
|
||||
5. Set up CMake build for debug: `cmake -B build -S .`. At this point you may need to manually
|
||||
provide the link path to the LLVM CMake directories, e.g. `cmake -B build -S . -DLLVM_DIR=/usr/local/opt/llvm/lib/cmake/llvm/`
|
||||
6. Build: `cmake --build build`
|
||||
7. Change directory to the build directory `cd build`
|
||||
|
||||
*A note on compiling for Linux/Unix/MacOS: to be able to fetch vendor libraries
|
||||
libcurl is needed. The CMake script should detect it if it is available. Note that
|
||||
@@ -405,8 +462,13 @@ this functionality is non-essential and it is perfectly fine to user the compile
|
||||
|
||||
#### Licensing
|
||||
|
||||
The C3 compiler is licensed under LGPL 3.0, the standard library itself is
|
||||
MIT licensed.
|
||||
Unless specified otherwise, the code in this repository is MIT licensed.
|
||||
The exception is the compiler source code (the source code under `src`),
|
||||
which is licensed under LGPL 3.0.
|
||||
|
||||
This means you are free to use all parts of standard library,
|
||||
tests, benchmarks, grammar, examples and so on under the MIT license, including
|
||||
using those libraries and tests if your build your own C3 compiler.
|
||||
|
||||
#### Editor plugins
|
||||
|
||||
@@ -425,7 +487,12 @@ Editor plugins can be found at https://github.com/c3lang/editor-plugins.
|
||||
|
||||
A huge **THANK YOU** goes out to all contributors and sponsors.
|
||||
|
||||
A special thank you to sponsors [Caleb-o](https://github.com/Caleb-o) and [devdad](https://github.com/devdad) for going the extra mile.
|
||||
A special thank you to sponsors [Zack Puhl](https://github.com/NotsoanoNimus) and [konimarti](https://github.com/konimarti) for going the extra mile.
|
||||
|
||||
And honorable mention goes to past sponsors:
|
||||
[Ygor Pontelo](https://github.com/ygorpontelo), [Simone Raimondi](https://github.com/SRaimondi),
|
||||
[Jan Válek](https://github.com/jan-valek), [Pierre Curto](https://github.com/pierrec),
|
||||
[Caleb-o](https://github.com/Caleb-o) and [devdad](https://github.com/devdad)
|
||||
|
||||
## Star History
|
||||
|
||||
|
||||
218
benchmarks/stdlib/collections/hashmap.c3
Normal file
218
benchmarks/stdlib/collections/hashmap.c3
Normal file
@@ -0,0 +1,218 @@
|
||||
// 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.
|
||||
//
|
||||
// Some benchmark test ideas are sourced from this article on C++ hashmap benchmarking:
|
||||
// https://martin.ankerl.com/2022/08/27/hashmap-bench-01/
|
||||
//
|
||||
module hashmap_benchmarks;
|
||||
|
||||
import std::collections::map;
|
||||
import std::math::random;
|
||||
|
||||
|
||||
const DEFAULT_ITERATIONS = 16384;
|
||||
|
||||
Lcg64Random rand;
|
||||
|
||||
HashMap { int, int } modifying_numbers_random;
|
||||
|
||||
fn void bench_setup() @init
|
||||
{
|
||||
set_benchmark_warmup_iterations(3);
|
||||
set_benchmark_max_iterations(DEFAULT_ITERATIONS);
|
||||
|
||||
// TODO: Cannot take the address of a @benchmark function. If we could, we could pass &insert_erase as a fn ptr and use the $qnameof CT eval internally.
|
||||
set_benchmark_func_iterations($qnameof(insert_erase), 32);
|
||||
set_benchmark_func_iterations($qnameof(random_access), 1024);
|
||||
|
||||
random::seed(&rand, 0x4528_21e6_38d0_1377);
|
||||
|
||||
for (usz i = 0; i < 1_000; ++i) modifying_numbers_random.set(rand.next_int(), rand.next_int());
|
||||
}
|
||||
|
||||
|
||||
// ==============================================================================================
|
||||
module hashmap_benchmarks @benchmark;
|
||||
|
||||
import std::collections::map;
|
||||
|
||||
import std::math::random;
|
||||
import std::encoding::base64;
|
||||
|
||||
|
||||
fn void generic_hash_speeds()
|
||||
{
|
||||
(char){}.hash();
|
||||
(char[<100>]){}.hash();
|
||||
(char[100]){}.hash();
|
||||
(ichar){}.hash();
|
||||
(ichar[<100>]){}.hash();
|
||||
(ichar[100]){}.hash();
|
||||
(short){}.hash();
|
||||
(short[<100>]){}.hash();
|
||||
(short[100]){}.hash();
|
||||
(ushort){}.hash();
|
||||
(ushort[<100>]){}.hash();
|
||||
(ushort[100]){}.hash();
|
||||
(int){}.hash();
|
||||
(int[<100>]){}.hash();
|
||||
(int[100]){}.hash();
|
||||
(uint){}.hash();
|
||||
(uint[<100>]){}.hash();
|
||||
(uint[100]){}.hash();
|
||||
(long){}.hash();
|
||||
(long[<20>]){}.hash();
|
||||
(long[100]){}.hash();
|
||||
(ulong){}.hash();
|
||||
(ulong[<20>]){}.hash();
|
||||
(ulong[100]){}.hash();
|
||||
(int128){}.hash();
|
||||
(int128[<20>]){}.hash();
|
||||
(int128[100]){}.hash();
|
||||
(uint128){}.hash();
|
||||
(uint128[<20>]){}.hash();
|
||||
(uint128[100]){}.hash();
|
||||
(bool){}.hash();
|
||||
(bool[<100>]){}.hash();
|
||||
(bool[100]){}.hash();
|
||||
String x = "abc";
|
||||
char[] y = "abc";
|
||||
assert(x.hash() == y.hash());
|
||||
String z1 = "This is a much longer string than the above value because longer values lead to longer hashing times.";
|
||||
char[] z2 = "This is a much longer string than the above value because longer values lead to longer hashing times.";
|
||||
assert(z1.hash() == z2.hash());
|
||||
assert(int.typeid.hash());
|
||||
}
|
||||
|
||||
|
||||
fn void hash_speeds_of_many_random_values() => @pool()
|
||||
{
|
||||
var $arrsz = 10_000;
|
||||
uint fake_checksum;
|
||||
|
||||
char[] chars = allocator::new_array(tmem, char, $arrsz)[:$arrsz];
|
||||
foreach (&v : chars) *v = (char)random::next(&rand, uint.max);
|
||||
|
||||
ushort[] shorts = allocator::new_array(tmem, ushort, $arrsz)[:$arrsz];
|
||||
foreach (&v : shorts) *v = (ushort)random::next(&rand, uint.max);
|
||||
|
||||
uint[] ints = allocator::new_array(tmem, uint, $arrsz)[:$arrsz];
|
||||
foreach (&v : ints) *v = random::next(&rand, uint.max);
|
||||
|
||||
ulong[] longs = allocator::new_array(tmem, ulong, $arrsz)[:$arrsz];
|
||||
foreach (&v : longs) *v = (ulong)random::next(&rand, uint.max);
|
||||
|
||||
uint128[] vwideints = allocator::new_array(tmem, uint128, $arrsz)[:$arrsz];
|
||||
foreach (&v : vwideints) *v = (uint128)random::next(&rand, uint.max);
|
||||
|
||||
char[48][] zstrs = allocator::new_array(tmem, char[48], $arrsz)[:$arrsz];
|
||||
String[$arrsz] strs;
|
||||
foreach (x, &v : zstrs)
|
||||
{
|
||||
foreach (&c : (*v)[:random::next(&rand, 48)]) *c = (char)random::next(&rand, char.max);
|
||||
strs[x] = ((ZString)&v[0]).str_view();
|
||||
}
|
||||
|
||||
runtime::@start_benchmark();
|
||||
foreach (v : chars) fake_checksum += v.hash();
|
||||
foreach (v : shorts) fake_checksum += v.hash();
|
||||
foreach (v : ints) fake_checksum += v.hash();
|
||||
foreach (v : longs) fake_checksum += v.hash();
|
||||
foreach (v : vwideints) fake_checksum += v.hash();
|
||||
foreach (v : strs) fake_checksum += v.hash();
|
||||
runtime::@end_benchmark();
|
||||
}
|
||||
|
||||
|
||||
fn void modifying_numbers_init_from_map() => @pool()
|
||||
{
|
||||
HashMap { int, int } v;
|
||||
v.tinit_from_map(&modifying_numbers_random);
|
||||
v.free();
|
||||
}
|
||||
|
||||
|
||||
fn void insert_erase() => @pool()
|
||||
{
|
||||
uint iters = 1_000_000;
|
||||
HashMap { int, int } v;
|
||||
v.tinit();
|
||||
|
||||
runtime::@start_benchmark();
|
||||
for (int i = 0; i < iters; ++i) v[i] = i;
|
||||
for (int i = 0; i < iters; ++i) v.remove(i);
|
||||
|
||||
runtime::@end_benchmark();
|
||||
|
||||
v.free();
|
||||
}
|
||||
|
||||
|
||||
fn void random_access() => @pool()
|
||||
{
|
||||
HashMap { int, int } v;
|
||||
v.tinit();
|
||||
|
||||
uint bound = 10_000;
|
||||
usz pseudo_checksum = 0;
|
||||
|
||||
for (uint i = 0; i < bound; ++i) v[i] = i;
|
||||
|
||||
runtime::@start_benchmark();
|
||||
for (uint i = 0; i < 1_000_000; ++i) pseudo_checksum += (v[i.hash() % bound] ?? 0);
|
||||
runtime::@end_benchmark();
|
||||
|
||||
v.free();
|
||||
}
|
||||
|
||||
|
||||
fn void random_access_erase() => @pool()
|
||||
{
|
||||
HashMap { int, int } v;
|
||||
v.tinit();
|
||||
|
||||
uint bound = 10_000;
|
||||
|
||||
for (uint i = 0; i < bound; ++i) v[i] = i;
|
||||
|
||||
runtime::@start_benchmark();
|
||||
for (uint i = 0; i < bound; ++i)
|
||||
{
|
||||
v[i.hash() % bound] = i; // supplant an entry
|
||||
|
||||
v.remove(random::next(&rand, bound)); // remove a random entry
|
||||
}
|
||||
runtime::@end_benchmark();
|
||||
|
||||
v.free();
|
||||
}
|
||||
|
||||
|
||||
fn void random_access_string_keys() => @pool()
|
||||
{
|
||||
HashMap { String, ulong } v;
|
||||
v.tinit();
|
||||
|
||||
usz pseudo_checksum = 0;
|
||||
String[5_000] saved;
|
||||
|
||||
for (usz i = 0; i < saved.len; ++i)
|
||||
{
|
||||
ulong hash = i.hash();
|
||||
String b64key = base64::tencode(@as_char_view(hash));
|
||||
|
||||
v[b64key] = hash;
|
||||
|
||||
if (i < saved.len) saved[i] = b64key;
|
||||
}
|
||||
|
||||
runtime::@start_benchmark();
|
||||
for (usz i = 0; i < saved.len; ++i)
|
||||
{
|
||||
pseudo_checksum += v[ saved[random::next(&rand, saved.len)] ]!! % 512;
|
||||
}
|
||||
runtime::@end_benchmark();
|
||||
|
||||
v.free();
|
||||
}
|
||||
46
benchmarks/stdlib/core/string_trim.c3
Normal file
46
benchmarks/stdlib/core/string_trim.c3
Normal file
@@ -0,0 +1,46 @@
|
||||
module string_trim_wars;
|
||||
|
||||
const String WHITESPACE_TARGET = " \n\t\r\f\va \tbcde\v\f\r\t\n ";
|
||||
const String WHITESPACE_NUMERIC_TARGET = " 25290 0969 99a \tbcde12332 34 43 0000";
|
||||
|
||||
fn void initialize_bench() @init
|
||||
{
|
||||
set_benchmark_warmup_iterations(64);
|
||||
set_benchmark_max_iterations(1 << 24);
|
||||
}
|
||||
|
||||
macro void trim_bench($trim_str, String $target = WHITESPACE_TARGET) => @pool()
|
||||
{
|
||||
String s1;
|
||||
String s2 = $target.tcopy();
|
||||
|
||||
runtime::@start_benchmark();
|
||||
|
||||
$switch:
|
||||
$case @typeis($trim_str, String):
|
||||
s1 = s2.trim($trim_str);
|
||||
$case @typeis($trim_str, AsciiCharset):
|
||||
s1 = s2.trim_charset($trim_str);
|
||||
$default: $error "Unable to determine the right String `trim` operation to use.";
|
||||
$endswitch
|
||||
|
||||
@volatile_load(s1);
|
||||
|
||||
runtime::@end_benchmark();
|
||||
}
|
||||
|
||||
|
||||
module string_trim_wars @benchmark;
|
||||
|
||||
fn void trim_control() => trim_bench(" "); // only spaces
|
||||
|
||||
fn void trim_whitespace_default() => trim_bench("\t\n\r "); // default set
|
||||
fn void trim_whitespace_default_ordered() => trim_bench(" \n\t\r"); // default \w set, but ordered by expected freq
|
||||
|
||||
fn void trim_whitespace_bad() => trim_bench("\f\v\n\t\r "); // bad-perf ordering, all \w
|
||||
|
||||
fn void trim_whitespace_ordered_extended() => trim_bench(" \n\t\r\f\v"); // proposed ordering, all \w
|
||||
fn void trim_charset_whitespace() => trim_bench(ascii::WHITESPACE_SET); // use charset, all \w
|
||||
|
||||
fn void trim_many() => trim_bench(" \n\t\r\f\v0123456789", WHITESPACE_NUMERIC_TARGET); // ordered, all \w + num
|
||||
fn void trim_charset_many() => trim_bench(ascii::WHITESPACE_SET | ascii::NUMBER_SET, WHITESPACE_NUMERIC_TARGET); // set, all \w + num
|
||||
94
benchmarks/stdlib/hash/non_crypto_shootout.c3
Normal file
94
benchmarks/stdlib/hash/non_crypto_shootout.c3
Normal file
@@ -0,0 +1,94 @@
|
||||
// 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.
|
||||
module non_crypto_benchmarks;
|
||||
|
||||
|
||||
const usz COMMON_ITERATIONS = 1 << 18;
|
||||
|
||||
const char[] COMMON_1 = { 0xA5 };
|
||||
const char[] COMMON_4 = { 0xA5, 0xA5, 0xA5, 0xA5, };
|
||||
const char[] COMMON_8 = { [0..7] = 0xA5 };
|
||||
const char[] COMMON_16 = { [0..15] = 0xA5 };
|
||||
const char[] COMMON_32 = { [0..31] = 0xA5 };
|
||||
const char[] COMMON_64 = { [0..63] = 0xA5 };
|
||||
const char[] COMMON_128 = { [0..127] = 0xA5 };
|
||||
const char[] COMMON_1024 = { [0..1023] = 0xA5 };
|
||||
|
||||
|
||||
fn void initialize_bench() @init
|
||||
{
|
||||
set_benchmark_warmup_iterations(3);
|
||||
set_benchmark_max_iterations(COMMON_ITERATIONS + 3);
|
||||
}
|
||||
|
||||
|
||||
// =======================================================================================
|
||||
module non_crypto_benchmarks @benchmark;
|
||||
|
||||
import std::hash;
|
||||
|
||||
|
||||
fn void fnv64a_1() => fnv64a::hash(COMMON_1);
|
||||
fn void fnv32a_1() => fnv32a::hash(COMMON_1);
|
||||
fn void wyhash2_1() => wyhash2::hash(COMMON_1);
|
||||
fn void metro64_1() => metro64::hash(COMMON_1);
|
||||
fn void metro128_1() => metro128::hash(COMMON_1);
|
||||
fn void a5hash_1() => a5hash::hash(COMMON_1);
|
||||
fn void komi_1() => komi::hash(COMMON_1);
|
||||
|
||||
fn void fnv64a_4() => fnv64a::hash(COMMON_4);
|
||||
fn void fnv32a_4() => fnv32a::hash(COMMON_4);
|
||||
fn void wyhash2_4() => wyhash2::hash(COMMON_4);
|
||||
fn void metro64_4() => metro64::hash(COMMON_4);
|
||||
fn void metro128_4() => metro128::hash(COMMON_4);
|
||||
fn void a5hash_4() => a5hash::hash(COMMON_4);
|
||||
fn void komi_4() => komi::hash(COMMON_4);
|
||||
|
||||
fn void fnv64a_8() => fnv64a::hash(COMMON_8);
|
||||
fn void fnv32a_8() => fnv32a::hash(COMMON_8);
|
||||
fn void wyhash2_8() => wyhash2::hash(COMMON_8);
|
||||
fn void metro64_8() => metro64::hash(COMMON_8);
|
||||
fn void metro128_8() => metro128::hash(COMMON_8);
|
||||
fn void a5hash_8() => a5hash::hash(COMMON_8);
|
||||
fn void komi_8() => komi::hash(COMMON_8);
|
||||
|
||||
fn void fnv64a_16() => fnv64a::hash(COMMON_16);
|
||||
fn void fnv32a_16() => fnv32a::hash(COMMON_16);
|
||||
fn void wyhash2_16() => wyhash2::hash(COMMON_16);
|
||||
fn void metro64_16() => metro64::hash(COMMON_16);
|
||||
fn void metro128_16() => metro128::hash(COMMON_16);
|
||||
fn void a5hash_16() => a5hash::hash(COMMON_16);
|
||||
fn void komi_16() => komi::hash(COMMON_16);
|
||||
|
||||
fn void fnv64a_32() => fnv64a::hash(COMMON_32);
|
||||
fn void fnv32a_32() => fnv32a::hash(COMMON_32);
|
||||
// NOTE: wyhash2 cannot be used on inputs > 16 bytes.
|
||||
fn void metro64_32() => metro64::hash(COMMON_32);
|
||||
fn void metro128_32() => metro128::hash(COMMON_32);
|
||||
fn void a5hash_32() => a5hash::hash(COMMON_32);
|
||||
fn void komi_32() => komi::hash(COMMON_32);
|
||||
|
||||
fn void fnv64a_64() => fnv64a::hash(COMMON_64);
|
||||
fn void fnv32a_64() => fnv32a::hash(COMMON_64);
|
||||
// NOTE: wyhash2 cannot be used on inputs > 16 bytes.
|
||||
fn void metro64_64() => metro64::hash(COMMON_64);
|
||||
fn void metro128_64() => metro128::hash(COMMON_64);
|
||||
fn void a5hash_64() => a5hash::hash(COMMON_64);
|
||||
fn void komi_64() => komi::hash(COMMON_64);
|
||||
|
||||
fn void fnv64a_128() => fnv64a::hash(COMMON_128);
|
||||
fn void fnv32a_128() => fnv32a::hash(COMMON_128);
|
||||
// NOTE: wyhash2 cannot be used on inputs > 16 bytes.
|
||||
fn void metro64_128() => metro64::hash(COMMON_128);
|
||||
fn void metro128_128() => metro128::hash(COMMON_128);
|
||||
fn void a5hash_128() => a5hash::hash(COMMON_128);
|
||||
fn void komi_128() => komi::hash(COMMON_128);
|
||||
|
||||
fn void fnv64a_1024() => fnv64a::hash(COMMON_1024);
|
||||
fn void fnv32a_1024() => fnv32a::hash(COMMON_1024);
|
||||
// NOTE: wyhash2 cannot be used on inputs > 16 bytes.
|
||||
fn void metro64_1024() => metro64::hash(COMMON_1024);
|
||||
fn void metro128_1024() => metro128::hash(COMMON_1024);
|
||||
fn void a5hash_1024() => a5hash::hash(COMMON_1024);
|
||||
fn void komi_1024() => komi::hash(COMMON_1024);
|
||||
18
flake.nix
18
flake.nix
@@ -6,29 +6,27 @@
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = { self, ... } @ inputs: inputs.flake-utils.lib.eachDefaultSystem
|
||||
outputs = { self, ... }@inputs: inputs.flake-utils.lib.eachDefaultSystem
|
||||
(system:
|
||||
let pkgs = import inputs.nixpkgs { inherit system; };
|
||||
call = set: pkgs.callPackage ./nix/default.nix (
|
||||
set // {
|
||||
rev = self.rev or "unknown";
|
||||
}
|
||||
);
|
||||
c3cBuild = set: pkgs.callPackage ./nix/default.nix (set // {
|
||||
rev = self.rev or "unknown";
|
||||
});
|
||||
in {
|
||||
packages = {
|
||||
default = self.packages.${system}.c3c;
|
||||
|
||||
c3c = call {};
|
||||
c3c = c3cBuild {};
|
||||
|
||||
c3c-checks = pkgs.callPackage ./nix/default.nix {
|
||||
c3c-checks = c3cBuild {
|
||||
checks = true;
|
||||
};
|
||||
|
||||
c3c-debug = pkgs.callPackage ./nix/default.nix {
|
||||
c3c-debug = c3cBuild {
|
||||
debug = true;
|
||||
};
|
||||
|
||||
c3c-debug-checks = pkgs.callPackage ./nix/default.nix {
|
||||
c3c-debug-checks = c3cBuild {
|
||||
debug = true;
|
||||
checks = true;
|
||||
};
|
||||
|
||||
@@ -91,7 +91,7 @@ macro Type Atomic.or(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT
|
||||
return @atomic_exec(atomic::fetch_or, data, value, ordering);
|
||||
}
|
||||
|
||||
fn Type Atomic.xor(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
|
||||
macro Type Atomic.xor(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_xor, data, value, ordering);
|
||||
@@ -171,6 +171,7 @@ macro bool is_native_atomic_type($Type)
|
||||
$case SIGNED_INT:
|
||||
$case UNSIGNED_INT:
|
||||
$case POINTER:
|
||||
$case FUNC:
|
||||
$case FLOAT:
|
||||
$case BOOL:
|
||||
return true;
|
||||
@@ -193,7 +194,7 @@ macro bool is_native_atomic_type($Type)
|
||||
@require $defined(*ptr) : "Expected a pointer"
|
||||
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
|
||||
@require $defined(*ptr + y) : "+ must be defined between the values."
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
|
||||
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_add(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
|
||||
{
|
||||
@@ -213,7 +214,7 @@ macro fetch_add(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
|
||||
@require $defined(*ptr) : "Expected a pointer"
|
||||
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
|
||||
@require $defined(*ptr - y) : "- must be defined between the values."
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
|
||||
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_sub(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
|
||||
{
|
||||
@@ -232,13 +233,13 @@ macro fetch_sub(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
|
||||
@require $defined(*ptr) : "Expected a pointer"
|
||||
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
|
||||
@require $defined(*ptr * y) : "* must be defined between the values."
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
|
||||
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
var $load_ordering = $ordering;
|
||||
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
|
||||
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
|
||||
$load_ordering = SEQ_CONSISTENT;
|
||||
$endif
|
||||
|
||||
var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr));
|
||||
@@ -272,13 +273,13 @@ macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
@require $defined(*ptr) : "Expected a pointer"
|
||||
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
|
||||
@require $defined(*ptr * y) : "/ must be defined between the values."
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
|
||||
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
var $load_ordering = $ordering;
|
||||
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
|
||||
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
|
||||
$load_ordering = SEQ_CONSISTENT;
|
||||
$endif
|
||||
|
||||
var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr));
|
||||
@@ -313,7 +314,7 @@ macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
@require $defined(*ptr) : "Expected a pointer"
|
||||
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
|
||||
@require $defined(*ptr | y) : "| must be defined between the values."
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
|
||||
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_or(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
|
||||
{
|
||||
@@ -330,7 +331,7 @@ macro fetch_or(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile
|
||||
@require $defined(*ptr) : "Expected a pointer"
|
||||
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
|
||||
@require $defined(*ptr ^ y) : "^ must be defined between the values."
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
|
||||
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_xor(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
|
||||
{
|
||||
@@ -347,7 +348,7 @@ macro fetch_xor(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
|
||||
@require $defined(*ptr) : "Expected a pointer"
|
||||
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
|
||||
@require $defined(*ptr ^ y) : "& must be defined between the values."
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
|
||||
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_and(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
|
||||
{
|
||||
@@ -364,13 +365,13 @@ macro fetch_and(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
|
||||
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
|
||||
@require types::is_int($typeof(*ptr)) : "Only integer pointers may be used."
|
||||
@require types::is_int($typeof(y)) : "The value for shift right must be an integer"
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
|
||||
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_shift_right(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
var $load_ordering = $ordering;
|
||||
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
|
||||
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
|
||||
$load_ordering = SEQ_CONSISTENT;
|
||||
$endif
|
||||
|
||||
var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr));
|
||||
@@ -406,13 +407,13 @@ macro fetch_shift_right(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
|
||||
@require types::is_int($typeof(*ptr)) : "Only integer pointers may be used."
|
||||
@require types::is_int($typeof(y)) : "The value for shift left must be an integer"
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
|
||||
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_shift_left(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
var $load_ordering = $ordering;
|
||||
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
|
||||
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
|
||||
$load_ordering = SEQ_CONSISTENT;
|
||||
$endif
|
||||
|
||||
var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr));
|
||||
@@ -447,7 +448,7 @@ macro fetch_shift_left(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
|
||||
@require types::flat_kind($typeof(*ptr)) == BOOL : "Only bool pointers may be used."
|
||||
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
|
||||
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
|
||||
*>
|
||||
macro flag_set(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
@@ -455,7 +456,7 @@ macro flag_set(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
$typeof(*ptr) new_value = true;
|
||||
var $load_ordering = $ordering;
|
||||
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
|
||||
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
|
||||
$load_ordering = SEQ_CONSISTENT;
|
||||
$endif
|
||||
do
|
||||
{
|
||||
@@ -474,7 +475,7 @@ macro flag_set(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
@require $defined(*ptr) : "Expected a pointer"
|
||||
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
|
||||
@require types::flat_kind($typeof(*ptr)) == BOOL : "Only bool pointers may be used."
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
|
||||
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
|
||||
*>
|
||||
macro flag_clear(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
@@ -482,7 +483,7 @@ macro flag_clear(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
$typeof(*ptr) new_value = false;
|
||||
var $load_ordering = $ordering;
|
||||
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
|
||||
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
|
||||
$load_ordering = SEQ_CONSISTENT;
|
||||
$endif
|
||||
do
|
||||
{
|
||||
@@ -502,7 +503,7 @@ macro flag_clear(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
@require $defined(*ptr) : "Expected a pointer"
|
||||
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
|
||||
@require $defined(*ptr > y) : "Only values that are comparable with > may be used"
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
|
||||
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_max(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
|
||||
{
|
||||
@@ -521,7 +522,7 @@ macro fetch_max(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
|
||||
@require $defined(*ptr) : "Expected a pointer"
|
||||
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
|
||||
@require $defined(*ptr > y) : "Only values that are comparable with > may be used"
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
|
||||
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_min(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
|
||||
{
|
||||
|
||||
@@ -136,6 +136,7 @@ fn void BitSet.unset(&self, usz i)
|
||||
@param i : "The index of the bit"
|
||||
|
||||
@require i < SIZE : "Index was out of range"
|
||||
@pure
|
||||
*>
|
||||
fn bool BitSet.get(&self, usz i) @operator([]) @inline
|
||||
{
|
||||
@@ -144,6 +145,11 @@ fn bool BitSet.get(&self, usz i) @operator([]) @inline
|
||||
return self.data[q] & (1 << r) != 0;
|
||||
}
|
||||
|
||||
<*
|
||||
Return the number of bits.
|
||||
|
||||
@pure
|
||||
*>
|
||||
fn usz BitSet.len(&self) @operator(len) @inline
|
||||
{
|
||||
return SZ * BITS;
|
||||
|
||||
@@ -121,7 +121,24 @@ fn usz ElasticArray.add_all_to_limit(&self, ElasticArray* other_list)
|
||||
|
||||
@param [in] array
|
||||
*>
|
||||
fn usz ElasticArray.add_array_to_limit(&self, Type[] array)
|
||||
fn usz ElasticArray.add_array_to_limit(&self, Type[] array) @deprecated("Use push_all_to_limit")
|
||||
{
|
||||
if (!array.len) return 0;
|
||||
foreach (i, &value : array)
|
||||
{
|
||||
if (self.size == MAX_SIZE) return array.len - i;
|
||||
self.entries[self.size++] = *value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
<*
|
||||
Add as many values from this array as possible, returning the
|
||||
number of elements that didn't fit.
|
||||
|
||||
@param [in] array
|
||||
*>
|
||||
fn usz ElasticArray.push_all_to_limit(&self, Type[] array)
|
||||
{
|
||||
if (!array.len) return 0;
|
||||
foreach (i, &value : array)
|
||||
@@ -139,7 +156,7 @@ fn usz ElasticArray.add_array_to_limit(&self, Type[] array)
|
||||
@require array.len + self.size <= MAX_SIZE : `Size would exceed max.`
|
||||
@ensure self.size >= array.len
|
||||
*>
|
||||
fn void ElasticArray.add_array(&self, Type[] array)
|
||||
fn void ElasticArray.add_array(&self, Type[] array) @deprecated("Use push_all")
|
||||
{
|
||||
if (!array.len) return;
|
||||
foreach (&value : array)
|
||||
@@ -148,6 +165,21 @@ fn void ElasticArray.add_array(&self, Type[] array)
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Add the values of an array to this list.
|
||||
|
||||
@param [in] array
|
||||
@require array.len + self.size <= MAX_SIZE : `Size would exceed max.`
|
||||
@ensure self.size >= array.len
|
||||
*>
|
||||
fn void ElasticArray.push_all(&self, Type[] array)
|
||||
{
|
||||
if (!array.len) return;
|
||||
foreach (&value : array)
|
||||
{
|
||||
self.entries[self.size++] = *value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
|
||||
@@ -90,7 +90,7 @@ macro HashMap* HashMap.init_with_key_values(&self, Allocator allocator, ..., uin
|
||||
*>
|
||||
macro HashMap* HashMap.tinit_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
return self.tinit_with_key_values(tmem, capacity, load_factor);
|
||||
return self.init_with_key_values(tmem, $vasplat, capacity: capacity, load_factor: load_factor);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -182,6 +182,24 @@ fn Value*? HashMap.get_ref(&map, Key key)
|
||||
return NOT_FOUND?;
|
||||
}
|
||||
|
||||
fn Value* HashMap.get_or_create_ref(&map, Key key) @operator(&[])
|
||||
{
|
||||
uint hash = rehash(key.hash());
|
||||
if (map.count)
|
||||
{
|
||||
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key)) return &e.value;
|
||||
}
|
||||
}
|
||||
map.set(key, {});
|
||||
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key)) return &e.value;
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
|
||||
fn Entry*? HashMap.get_entry(&map, Key key)
|
||||
{
|
||||
if (!map.count) return NOT_FOUND?;
|
||||
@@ -194,8 +212,9 @@ fn Entry*? HashMap.get_entry(&map, Key key)
|
||||
}
|
||||
|
||||
<*
|
||||
Get the value or update and
|
||||
@require $assignable(#expr, Value)
|
||||
Get the value or set it to the value
|
||||
|
||||
@require $defined(Value val = #expr)
|
||||
*>
|
||||
macro Value HashMap.@get_or_set(&map, Key key, Value #expr)
|
||||
{
|
||||
@@ -229,15 +248,15 @@ fn bool HashMap.has_key(&map, Key key)
|
||||
fn bool HashMap.set(&map, Key key, Value value) @operator([]=)
|
||||
{
|
||||
// If the map isn't initialized, use the defaults to initialize it.
|
||||
switch (map.allocator.ptr)
|
||||
{
|
||||
case &dummy:
|
||||
map.init(mem);
|
||||
case null:
|
||||
map.tinit();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (map.allocator.ptr)
|
||||
{
|
||||
case &dummy:
|
||||
map.init(mem);
|
||||
case null:
|
||||
map.tinit();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
uint hash = rehash(key.hash());
|
||||
uint index = index_for(hash, map.table.len);
|
||||
for (Entry *e = map.table[index]; e != null; e = e.next)
|
||||
@@ -332,10 +351,7 @@ macro HashMap.@each_entry(map; @body(entry))
|
||||
}
|
||||
}
|
||||
|
||||
fn Value[] HashMap.tvalues(&map)
|
||||
{
|
||||
return map.values(tmem) @inline;
|
||||
}
|
||||
fn Value[] HashMap.tvalues(&self) => self.values(tmem) @inline;
|
||||
|
||||
fn Value[] HashMap.values(&self, Allocator allocator)
|
||||
{
|
||||
@@ -421,7 +437,7 @@ fn usz? HashMap.to_format(&self, Formatter* f) @dynamic
|
||||
{
|
||||
if (len > 2) len += f.print(", ")!;
|
||||
len += f.printf("%s: %s", entry.key, entry.value)!;
|
||||
};
|
||||
};
|
||||
return len + f.print(" }");
|
||||
}
|
||||
|
||||
|
||||
652
lib/std/collections/hashset.c3
Normal file
652
lib/std/collections/hashset.c3
Normal file
@@ -0,0 +1,652 @@
|
||||
<*
|
||||
@require $defined((Value){}.hash()) : `No .hash function found on the value`
|
||||
*>
|
||||
module std::collections::set {Value};
|
||||
import std::math;
|
||||
import std::io @norecurse;
|
||||
|
||||
const uint DEFAULT_INITIAL_CAPACITY = 16;
|
||||
const uint MAXIMUM_CAPACITY = 1u << 31;
|
||||
const float DEFAULT_LOAD_FACTOR = 0.75;
|
||||
|
||||
const Allocator SET_HEAP_ALLOCATOR = (Allocator)&dummy;
|
||||
|
||||
<* Copy the ONHEAP allocator to initialize to a set that is heap allocated *>
|
||||
const HashSet ONHEAP = { .allocator = SET_HEAP_ALLOCATOR };
|
||||
|
||||
struct Entry
|
||||
{
|
||||
uint hash;
|
||||
Value value;
|
||||
Entry* next;
|
||||
}
|
||||
|
||||
struct HashSet (Printable)
|
||||
{
|
||||
Entry*[] table;
|
||||
Allocator allocator;
|
||||
usz count; // Number of elements
|
||||
usz threshold; // Resize limit
|
||||
float load_factor;
|
||||
}
|
||||
|
||||
fn int HashSet.len(&self) @operator(len) => (int) self.count;
|
||||
|
||||
<*
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@require capacity > 0 : "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 : "The load factor must be higher than 0"
|
||||
@require !self.is_initialized() : "Set was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn HashSet* HashSet.init(&self, Allocator allocator, usz capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
capacity = math::next_power_of_2(capacity);
|
||||
self.allocator = allocator;
|
||||
self.threshold = (usz) (capacity * load_factor);
|
||||
self.load_factor = load_factor;
|
||||
self.table = allocator::new_array(allocator, Entry*, capacity);
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@require capacity > 0 : "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 : "The load factor must be higher than 0"
|
||||
@require !self.is_initialized() : "Set was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn HashSet* HashSet.tinit(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
return self.init(tmem, capacity, load_factor) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@require capacity > 0 : "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 : "The load factor must be higher than 0"
|
||||
@require !self.is_initialized() : "Set was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
|
||||
*>
|
||||
macro HashSet* HashSet.init_with_values(&self, Allocator allocator, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
self.init(allocator, capacity, load_factor);
|
||||
$for var $i = 0; $i < $vacount; $i++:
|
||||
self.add($vaarg[$i]);
|
||||
$endfor
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@require capacity > 0 : "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 : "The load factor must be higher than 0"
|
||||
@require !self.is_initialized() : "Set was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
|
||||
*>
|
||||
macro HashSet* HashSet.tinit_with_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
return self.init_with_values(tmem, $vasplat, capacity: capacity, load_factor: load_factor);
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] values : "The values for the HashSet"
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@require capacity > 0 : "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 : "The load factor must be higher than 0"
|
||||
@require !self.is_initialized() : "Set was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn HashSet* HashSet.init_from_values(&self, Allocator allocator, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
self.init(allocator, capacity, load_factor);
|
||||
foreach (v : values) self.add(v);
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] values : "The values for the HashSet entries"
|
||||
@require capacity > 0 : "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 : "The load factor must be higher than 0"
|
||||
@require !self.is_initialized() : "Set was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn HashSet* HashSet.tinit_from_values(&self, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
return self.init_from_values(tmem, values, capacity, load_factor);
|
||||
}
|
||||
|
||||
<*
|
||||
Has this hash set been initialized yet?
|
||||
|
||||
@param [&in] set : "The hash set we are testing"
|
||||
@return "Returns true if it has been initialized, false otherwise"
|
||||
*>
|
||||
fn bool HashSet.is_initialized(&set)
|
||||
{
|
||||
return set.allocator && set.allocator.ptr != &dummy;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@param [&in] other_set : "The set to copy from."
|
||||
@require !self.is_initialized() : "Set was already initialized"
|
||||
*>
|
||||
fn HashSet* HashSet.init_from_set(&self, Allocator allocator, HashSet* other_set)
|
||||
{
|
||||
self.init(allocator, other_set.table.len, other_set.load_factor);
|
||||
self.put_all_for_create(other_set);
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] other_set : "The set to copy from."
|
||||
@require !set.is_initialized() : "Set was already initialized"
|
||||
*>
|
||||
fn HashSet* HashSet.tinit_from_set(&set, HashSet* other_set)
|
||||
{
|
||||
return set.init_from_set(tmem, other_set) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
Check if the set is empty
|
||||
|
||||
@return "true if it is empty"
|
||||
@pure
|
||||
*>
|
||||
fn bool HashSet.is_empty(&set) @inline
|
||||
{
|
||||
return !set.count;
|
||||
}
|
||||
|
||||
<*
|
||||
Add all elements in the slice to the set.
|
||||
|
||||
@param [in] list
|
||||
@return "The number of new elements added"
|
||||
@ensure total <= list.len
|
||||
*>
|
||||
fn usz HashSet.add_all(&set, Value[] list)
|
||||
{
|
||||
usz total;
|
||||
foreach (v : list)
|
||||
{
|
||||
if (set.add(v)) total++;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] other
|
||||
@return "The number of new elements added"
|
||||
@ensure return <= other.count
|
||||
*>
|
||||
fn usz HashSet.add_all_from(&set, HashSet* other)
|
||||
{
|
||||
usz total;
|
||||
other.@each(;Value value)
|
||||
{
|
||||
if (set.add(value)) total++;
|
||||
};
|
||||
return total;
|
||||
}
|
||||
|
||||
<*
|
||||
@param value : "The value to add"
|
||||
@return "true if the value didn't exist in the set"
|
||||
*>
|
||||
fn bool HashSet.add(&set, Value value)
|
||||
{
|
||||
// If the set isn't initialized, use the defaults to initialize it.
|
||||
switch (set.allocator.ptr)
|
||||
{
|
||||
case &dummy:
|
||||
set.init(mem);
|
||||
case null:
|
||||
set.tinit();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
uint hash = rehash(value.hash());
|
||||
uint index = index_for(hash, set.table.len);
|
||||
for (Entry *e = set.table[index]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(value, e.value)) return false;
|
||||
}
|
||||
set.add_entry(hash, value, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
<*
|
||||
Iterate over all the values in the set
|
||||
*>
|
||||
macro HashSet.@each(set; @body(value))
|
||||
{
|
||||
if (!set.count) return;
|
||||
foreach (Entry* entry : set.table)
|
||||
{
|
||||
while (entry)
|
||||
{
|
||||
@body(entry.value);
|
||||
entry = entry.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Check if the set contains the given value.
|
||||
|
||||
@param value : "The value to check"
|
||||
@return "true if it exists in the set"
|
||||
*>
|
||||
fn bool HashSet.contains(&set, Value value)
|
||||
{
|
||||
if (!set.count) return false;
|
||||
uint hash = rehash(value.hash());
|
||||
for (Entry *e = set.table[index_for(hash, set.table.len)]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(value, e.value)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
<*
|
||||
Remove a single value from the set.
|
||||
|
||||
@param value : "The value to remove"
|
||||
@return? NOT_FOUND : "If the entry is not found"
|
||||
*>
|
||||
fn void? HashSet.remove(&set, Value value) @maydiscard
|
||||
{
|
||||
if (!set.remove_entry_for_value(value)) return NOT_FOUND?;
|
||||
}
|
||||
|
||||
fn usz HashSet.remove_all(&set, Value[] values)
|
||||
{
|
||||
usz total;
|
||||
foreach (v : values)
|
||||
{
|
||||
if (set.remove_entry_for_value(v)) total++;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] other : "Other set"
|
||||
*>
|
||||
fn usz HashSet.remove_all_from(&set, HashSet* other)
|
||||
{
|
||||
usz total;
|
||||
other.@each(;Value val)
|
||||
{
|
||||
if (set.remove_entry_for_value(val)) total++;
|
||||
};
|
||||
return total;
|
||||
}
|
||||
|
||||
<*
|
||||
Free all memory allocated by the hash set.
|
||||
*>
|
||||
fn void HashSet.free(&set)
|
||||
{
|
||||
if (!set.is_initialized()) return;
|
||||
set.clear();
|
||||
set.free_internal(set.table.ptr);
|
||||
*set = {};
|
||||
}
|
||||
|
||||
<*
|
||||
Clear all elements from the set while keeping the underlying storage
|
||||
|
||||
@ensure set.count == 0
|
||||
*>
|
||||
fn void HashSet.clear(&set)
|
||||
{
|
||||
if (!set.count) return;
|
||||
|
||||
foreach (Entry** &entry_ref : set.table)
|
||||
{
|
||||
Entry* entry = *entry_ref;
|
||||
if (!entry) continue;
|
||||
|
||||
Entry *next = entry.next;
|
||||
while (next)
|
||||
{
|
||||
Entry *to_delete = next;
|
||||
next = next.next;
|
||||
set.free_entry(to_delete);
|
||||
}
|
||||
|
||||
set.free_entry(entry);
|
||||
*entry_ref = null;
|
||||
}
|
||||
set.count = 0;
|
||||
}
|
||||
|
||||
fn void HashSet.reserve(&set, usz capacity)
|
||||
{
|
||||
if (capacity > set.threshold)
|
||||
{
|
||||
set.resize(math::next_power_of_2(capacity));
|
||||
}
|
||||
}
|
||||
|
||||
fn Value[] HashSet.tvalues(&self) => self.values(tmem) @inline;
|
||||
|
||||
fn Value[] HashSet.values(&self, Allocator allocator)
|
||||
{
|
||||
if (!self.count) return {};
|
||||
Value[] list = allocator::alloc_array(allocator, Value, self.count);
|
||||
usz index = 0;
|
||||
foreach (Entry* entry : self.table)
|
||||
{
|
||||
while (entry)
|
||||
{
|
||||
list[index++] = entry.value;
|
||||
entry = entry.next;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
// --- Set Operations ---
|
||||
|
||||
<*
|
||||
Returns the union of two sets (A | B)
|
||||
|
||||
@param [&in] other : "The other set to union with"
|
||||
@param [&inout] allocator : "Allocator for the new set"
|
||||
@return "A new set containing the union of both sets"
|
||||
*>
|
||||
fn HashSet HashSet.set_union(&self, Allocator allocator, HashSet* other)
|
||||
{
|
||||
usz new_capacity = math::next_power_of_2(self.count + other.count);
|
||||
HashSet result;
|
||||
result.init(allocator, new_capacity, self.load_factor);
|
||||
result.add_all_from(self);
|
||||
result.add_all_from(other);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn HashSet HashSet.tset_union(&self, HashSet* other) => self.set_union(tmem, other);
|
||||
|
||||
<*
|
||||
Returns the intersection of the two sets (A & B)
|
||||
|
||||
@param [&in] other : "The other set to intersect with"
|
||||
@param [&inout] allocator : "Allocator for the new set"
|
||||
@return "A new set containing the intersection of both sets"
|
||||
*>
|
||||
fn HashSet HashSet.intersection(&self, Allocator allocator, HashSet* other)
|
||||
{
|
||||
HashSet result;
|
||||
result.init(allocator, math::min(self.table.len, other.table.len), self.load_factor);
|
||||
|
||||
// Iterate through the smaller set for efficiency
|
||||
HashSet* smaller = self.count <= other.count ? self : other;
|
||||
HashSet* larger = self.count > other.count ? self : other;
|
||||
|
||||
smaller.@each(;Value value)
|
||||
{
|
||||
if (larger.contains(value)) result.add(value);
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn HashSet HashSet.tintersection(&self, HashSet* other) => self.intersection(tmem, other);
|
||||
|
||||
<*
|
||||
Return this set - other, so (A & ~B)
|
||||
|
||||
@param [&in] other : "The other set to compare with"
|
||||
@param [&inout] allocator : "Allocator for the new set"
|
||||
@return "A new set containing elements in this set but not in the other"
|
||||
*>
|
||||
fn HashSet HashSet.difference(&self, Allocator allocator, HashSet* other)
|
||||
{
|
||||
HashSet result;
|
||||
result.init(allocator, self.table.len, self.load_factor);
|
||||
self.@each(;Value value)
|
||||
{
|
||||
if (!other.contains(value))
|
||||
{
|
||||
result.add(value);
|
||||
}
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
fn HashSet HashSet.tdifference(&self, HashSet* other) => self.difference(tmem, other) @inline;
|
||||
|
||||
<*
|
||||
Return (A ^ B)
|
||||
|
||||
@param [&in] other : "The other set to compare with"
|
||||
@param [&inout] allocator : "Allocator for the new set"
|
||||
@return "A new set containing elements in this set or the other, but not both"
|
||||
*>
|
||||
fn HashSet HashSet.symmetric_difference(&self, Allocator allocator, HashSet* other)
|
||||
{
|
||||
HashSet result;
|
||||
result.init(allocator, self.table.len, self.load_factor);
|
||||
result.add_all_from(self);
|
||||
other.@each(;Value value)
|
||||
{
|
||||
if (!result.add(value))
|
||||
{
|
||||
result.remove(value);
|
||||
}
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
fn HashSet HashSet.tsymmetric_difference(&self, HashSet* other) => self.symmetric_difference(tmem, other) @inline;
|
||||
|
||||
<*
|
||||
Check if this hash set is a subset of another set.
|
||||
|
||||
@param [&in] other : "The other set to check against"
|
||||
@return "True if all elements of this set are in the other set"
|
||||
*>
|
||||
fn bool HashSet.is_subset(&self, HashSet* other)
|
||||
{
|
||||
if (self.count == 0) return true;
|
||||
if (self.count > other.count) return false;
|
||||
|
||||
self.@each(;Value value)
|
||||
{
|
||||
if (!other.contains(value)) return false;
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// --- private methods
|
||||
|
||||
fn void HashSet.add_entry(&set, uint hash, Value value, uint bucket_index) @private
|
||||
{
|
||||
Entry* entry = allocator::new(set.allocator, Entry, { .hash = hash, .value = value, .next = set.table[bucket_index] });
|
||||
set.table[bucket_index] = entry;
|
||||
if (set.count++ >= set.threshold)
|
||||
{
|
||||
set.resize(set.table.len * 2);
|
||||
}
|
||||
}
|
||||
|
||||
fn void HashSet.resize(&self, usz new_capacity) @private
|
||||
{
|
||||
Entry*[] old_table = self.table;
|
||||
usz old_capacity = old_table.len;
|
||||
if (old_capacity == MAXIMUM_CAPACITY)
|
||||
{
|
||||
self.threshold = uint.max;
|
||||
return;
|
||||
}
|
||||
Entry*[] new_table = allocator::new_array(self.allocator, Entry*, new_capacity);
|
||||
self.transfer(new_table);
|
||||
self.table = new_table;
|
||||
self.free_internal(old_table.ptr);
|
||||
self.threshold = (uint)(new_capacity * self.load_factor);
|
||||
}
|
||||
|
||||
fn usz? HashSet.to_format(&self, Formatter* f) @dynamic
|
||||
{
|
||||
usz len;
|
||||
len += f.print("{ ")!;
|
||||
self.@each(; Value value)
|
||||
{
|
||||
if (len > 2) len += f.print(", ")!;
|
||||
len += f.printf("%s", value)!;
|
||||
};
|
||||
return len + f.print(" }");
|
||||
}
|
||||
|
||||
fn void HashSet.transfer(&self, Entry*[] new_table) @private
|
||||
{
|
||||
Entry*[] src = self.table;
|
||||
uint new_capacity = new_table.len;
|
||||
foreach (uint j, Entry *e : src)
|
||||
{
|
||||
if (!e) continue;
|
||||
do
|
||||
{
|
||||
Entry* next = e.next;
|
||||
uint i = index_for(e.hash, new_capacity);
|
||||
e.next = new_table[i];
|
||||
new_table[i] = e;
|
||||
e = next;
|
||||
}
|
||||
while (e);
|
||||
}
|
||||
}
|
||||
|
||||
fn void HashSet.put_all_for_create(&set, HashSet* other_set) @private
|
||||
{
|
||||
if (!other_set.count) return;
|
||||
foreach (Entry *e : other_set.table)
|
||||
{
|
||||
while (e)
|
||||
{
|
||||
set.put_for_create(e.value);
|
||||
e = e.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn void HashSet.put_for_create(&set, Value value) @private
|
||||
{
|
||||
uint hash = rehash(value.hash());
|
||||
uint i = index_for(hash, set.table.len);
|
||||
for (Entry *e = set.table[i]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(value, e.value))
|
||||
{
|
||||
// Value already exists, no need to do anything
|
||||
return;
|
||||
}
|
||||
}
|
||||
set.create_entry(hash, value, i);
|
||||
}
|
||||
|
||||
fn void HashSet.free_internal(&self, void* ptr) @inline @private
|
||||
{
|
||||
allocator::free(self.allocator, ptr);
|
||||
}
|
||||
|
||||
fn void HashSet.create_entry(&set, uint hash, Value value, int bucket_index) @private
|
||||
{
|
||||
Entry* entry = allocator::new(set.allocator, Entry, {
|
||||
.hash = hash,
|
||||
.value = value,
|
||||
.next = set.table[bucket_index]
|
||||
});
|
||||
set.table[bucket_index] = entry;
|
||||
set.count++;
|
||||
}
|
||||
|
||||
<*
|
||||
Removes the entry for the specified value if present
|
||||
@return "true if found and removed, false otherwise"
|
||||
*>
|
||||
fn bool HashSet.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);
|
||||
Entry* prev = set.table[i];
|
||||
Entry* e = prev;
|
||||
while (e)
|
||||
{
|
||||
Entry *next = e.next;
|
||||
if (e.hash == hash && equals(value, e.value))
|
||||
{
|
||||
set.count--;
|
||||
if (prev == e)
|
||||
{
|
||||
set.table[i] = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev.next = next;
|
||||
}
|
||||
set.free_entry(e);
|
||||
return true;
|
||||
}
|
||||
prev = e;
|
||||
e = next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void HashSet.free_entry(&set, Entry *entry) @private
|
||||
{
|
||||
allocator::free(set.allocator, entry);
|
||||
}
|
||||
|
||||
struct HashSetIterator
|
||||
{
|
||||
HashSet* set;
|
||||
usz bucket_index;
|
||||
Entry* current;
|
||||
}
|
||||
|
||||
fn HashSetIterator HashSet.iter(&set) => { .set = set, .bucket_index = 0, .current = null };
|
||||
|
||||
fn Value? HashSetIterator.next(&self)
|
||||
{
|
||||
if (self.current)
|
||||
{
|
||||
Value value = self.current.value;
|
||||
self.current = self.current.next;
|
||||
return value;
|
||||
}
|
||||
|
||||
while (self.bucket_index < self.set.table.len)
|
||||
{
|
||||
self.current = self.set.table[self.bucket_index++];
|
||||
if (self.current)
|
||||
{
|
||||
Value value = self.current.value;
|
||||
self.current = self.current.next;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return NOT_FOUND?;
|
||||
}
|
||||
|
||||
fn usz HashSetIterator.len(&self) @operator(len)
|
||||
{
|
||||
return self.set.count;
|
||||
}
|
||||
|
||||
<* @pure *>
|
||||
fn uint rehash(uint hash) @inline @private
|
||||
{
|
||||
hash ^= (hash >> 20) ^ (hash >> 12);
|
||||
return hash ^ ((hash >> 7) ^ (hash >> 4));
|
||||
}
|
||||
|
||||
macro uint index_for(uint hash, uint capacity) @private => hash & (capacity - 1);
|
||||
|
||||
int dummy @local;
|
||||
327
lib/std/collections/linked_blockingqueue.c3
Normal file
327
lib/std/collections/linked_blockingqueue.c3
Normal file
@@ -0,0 +1,327 @@
|
||||
module std::collections::blockingqueue { Value };
|
||||
import std::thread, std::time;
|
||||
|
||||
|
||||
const INITIAL_CAPACITY = 16;
|
||||
|
||||
struct QueueEntry
|
||||
{
|
||||
Value value;
|
||||
QueueEntry* next; // Next in queue order
|
||||
QueueEntry* prev; // Previous in queue order
|
||||
}
|
||||
|
||||
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)
|
||||
Mutex lock;
|
||||
ConditionVariable not_empty;
|
||||
ConditionVariable not_full;
|
||||
Allocator allocator;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@param capacity : "Maximum capacity (0 for unbounded)"
|
||||
@require !self.is_initialized() : "Queue was already initialized"
|
||||
*>
|
||||
fn LinkedBlockingQueue* LinkedBlockingQueue.init(&self, Allocator allocator, usz capacity = 0)
|
||||
{
|
||||
self.allocator = allocator;
|
||||
self.capacity = capacity;
|
||||
self.count = 0;
|
||||
self.head = null;
|
||||
self.tail = null;
|
||||
|
||||
self.lock.init()!!;
|
||||
self.not_empty.init()!!;
|
||||
self.not_full.init()!!;
|
||||
return self;
|
||||
}
|
||||
|
||||
fn LinkedBlockingQueue* LinkedBlockingQueue.tinit(&self, usz capacity = 0)
|
||||
{
|
||||
return self.init(tmem, capacity) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.is_initialized() : "Queue must be initialized"
|
||||
*>
|
||||
fn void LinkedBlockingQueue.free(&self)
|
||||
{
|
||||
self.lock.@in_lock()
|
||||
{
|
||||
// Free all remaining entries
|
||||
QueueEntry* entry = self.head;
|
||||
while (entry != null)
|
||||
{
|
||||
QueueEntry* next = entry.next;
|
||||
allocator::free(self.allocator, entry);
|
||||
entry = next;
|
||||
}
|
||||
};
|
||||
|
||||
(void)self.lock.destroy();
|
||||
(void)self.not_empty.destroy();
|
||||
(void)self.not_full.destroy();
|
||||
}
|
||||
|
||||
fn void LinkedBlockingQueue.link_entry(&self, QueueEntry* entry) @private
|
||||
{
|
||||
entry.next = null;
|
||||
entry.prev = self.tail;
|
||||
|
||||
if (self.tail == null)
|
||||
{
|
||||
// First element in queue
|
||||
self.head = entry;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Append to tail
|
||||
self.tail.next = entry;
|
||||
}
|
||||
self.tail = entry;
|
||||
self.count++;
|
||||
}
|
||||
|
||||
|
||||
fn QueueEntry* LinkedBlockingQueue.unlink_head(&self) @private
|
||||
{
|
||||
if (self.head == null) return null;
|
||||
|
||||
QueueEntry* entry = self.head;
|
||||
self.head = entry.next;
|
||||
|
||||
if (self.head != null)
|
||||
{
|
||||
self.head.prev = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Queue is now empty
|
||||
self.tail = null;
|
||||
}
|
||||
|
||||
self.count--;
|
||||
return entry;
|
||||
}
|
||||
|
||||
<*
|
||||
@param value : "Value to add to the queue"
|
||||
@require self.is_initialized() : "Queue must be initialized"
|
||||
*>
|
||||
fn void LinkedBlockingQueue.push(&self, Value value)
|
||||
{
|
||||
self.lock.@in_lock()
|
||||
{
|
||||
while (self.capacity > 0 && self.count >= self.capacity)
|
||||
{
|
||||
self.not_full.wait(&self.lock)!!;
|
||||
}
|
||||
|
||||
QueueEntry* entry = allocator::new(self.allocator, QueueEntry, {
|
||||
.value = value,
|
||||
.next = null,
|
||||
.prev = null
|
||||
});
|
||||
self.link_entry(entry);
|
||||
|
||||
// Signal that queue is no longer empty
|
||||
self.not_empty.signal()!!;
|
||||
};
|
||||
}
|
||||
|
||||
<*
|
||||
Get a value from the queue, blocking if there is no element in the queue.
|
||||
|
||||
@require self.is_initialized() : "Queue must be initialized"
|
||||
@return "The removed value"
|
||||
*>
|
||||
fn Value LinkedBlockingQueue.poll(&self)
|
||||
{
|
||||
self.lock.@in_lock()
|
||||
{
|
||||
while (self.count == 0)
|
||||
{
|
||||
self.not_empty.wait(&self.lock)!!;
|
||||
}
|
||||
|
||||
QueueEntry* entry = self.unlink_head();
|
||||
Value value = entry.value;
|
||||
allocator::free(self.allocator, entry);
|
||||
if (self.capacity > 0)
|
||||
{
|
||||
self.not_full.signal()!!;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
}
|
||||
|
||||
<*
|
||||
Pop an element from the queue, fail is it is empty.
|
||||
|
||||
@require self.is_initialized() : "Queue must be initialized"
|
||||
@return "The removed value"
|
||||
@return? NO_MORE_ELEMENT : "If the queue is empty"
|
||||
*>
|
||||
fn Value? LinkedBlockingQueue.pop(&self)
|
||||
{
|
||||
self.lock.@in_lock()
|
||||
{
|
||||
if (self.count == 0) return NO_MORE_ELEMENT?;
|
||||
|
||||
QueueEntry* entry = self.unlink_head();
|
||||
Value value = entry.value;
|
||||
allocator::free(self.allocator, entry);
|
||||
|
||||
if (self.capacity > 0)
|
||||
{
|
||||
self.not_full.signal()!!;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
}
|
||||
|
||||
<*
|
||||
Poll with a timeout.
|
||||
|
||||
@param timeout : "Timeout in microseconds"
|
||||
@require self.is_initialized() : "Queue must be initialized"
|
||||
@return "The removed value or null if timeout occurred"
|
||||
@return? NO_MORE_ELEMENT : "If we reached the timeout"
|
||||
*>
|
||||
fn Value? LinkedBlockingQueue.poll_timeout(&self, Duration timeout)
|
||||
{
|
||||
self.lock.@in_lock()
|
||||
{
|
||||
// Use while loop to handle spurious wakeups
|
||||
if (!self.count)
|
||||
{
|
||||
Time start = time::now();
|
||||
Time end = start + timeout;
|
||||
while (!self.count)
|
||||
{
|
||||
if (end <= time::now()) break;
|
||||
if (catch self.not_empty.wait_until(&self.lock, end)) break;
|
||||
}
|
||||
if (!self.count) return NO_MORE_ELEMENT?;
|
||||
}
|
||||
|
||||
QueueEntry* entry = self.unlink_head();
|
||||
Value value = entry.value;
|
||||
allocator::free(self.allocator, entry);
|
||||
|
||||
// Must signal not_full after removing an item
|
||||
if (self.capacity > 0)
|
||||
{
|
||||
self.not_full.signal()!!;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
@require self.is_initialized() : "Queue must be initialized"
|
||||
@return "Current size of the queue"
|
||||
*>
|
||||
fn usz LinkedBlockingQueue.size(&self)
|
||||
{
|
||||
self.lock.@in_lock()
|
||||
{
|
||||
return self.count;
|
||||
};
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.is_initialized() : "Queue must be initialized"
|
||||
@return "True if queue is empty"
|
||||
*>
|
||||
fn bool LinkedBlockingQueue.is_empty(&self)
|
||||
{
|
||||
self.lock.@in_lock()
|
||||
{
|
||||
return self.count == 0;
|
||||
};
|
||||
}
|
||||
|
||||
<*
|
||||
Try to push, return CAPACITY_EXCEEDED if the queue is full.
|
||||
|
||||
@param value : "Value to add to the queue"
|
||||
@require self.is_initialized() : "Queue must be initialized"
|
||||
@return? CAPACITY_EXCEEDED : "If the queue is full"
|
||||
*>
|
||||
fn void? LinkedBlockingQueue.try_push(&self, Value value)
|
||||
{
|
||||
self.lock.@in_lock()
|
||||
{
|
||||
if (self.capacity > 0 && self.count >= self.capacity) return CAPACITY_EXCEEDED?;
|
||||
|
||||
QueueEntry* entry = allocator::new(self.allocator, QueueEntry, {
|
||||
.value = value,
|
||||
.next = null,
|
||||
.prev = null
|
||||
});
|
||||
self.link_entry(entry);
|
||||
self.not_empty.signal()!!;
|
||||
};
|
||||
}
|
||||
|
||||
<*
|
||||
Try to push, return CAPACITY_EXCEEDED if the queue is still full after timeout is reached.
|
||||
|
||||
@param value : "Value to add to the queue"
|
||||
@param timeout : "Timeout in microseconds"
|
||||
@require self.is_initialized() : "Queue must be initialized"
|
||||
@return? CAPACITY_EXCEEDED : "If the queue is full"
|
||||
*>
|
||||
fn void? LinkedBlockingQueue.push_timeout(&self, Value value, Duration timeout)
|
||||
{
|
||||
self.lock.@in_lock()
|
||||
{
|
||||
if (self.capacity > 0 && self.count >= self.capacity)
|
||||
{
|
||||
Time start = time::now();
|
||||
Time end = start + timeout;
|
||||
while (self.capacity > 0 && self.count >= self.capacity)
|
||||
{
|
||||
if (end <= time::now()) break;
|
||||
if (catch self.not_empty.wait_until(&self.lock, end)) break;
|
||||
}
|
||||
if (self.capacity > 0 && self.count >= self.capacity) return CAPACITY_EXCEEDED?;
|
||||
}
|
||||
|
||||
QueueEntry* entry = allocator::new(self.allocator, QueueEntry, {
|
||||
.value = value,
|
||||
.next = null,
|
||||
.prev = null
|
||||
});
|
||||
self.link_entry(entry);
|
||||
self.not_empty.signal()!!;
|
||||
};
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.is_initialized() : "Queue must be initialized"
|
||||
@return "The head value or NO_MORE_ELEMENT? if queue is empty"
|
||||
*>
|
||||
fn Value? LinkedBlockingQueue.peek(&self)
|
||||
{
|
||||
self.lock.@in_lock()
|
||||
{
|
||||
return (self.head != null) ? self.head.value : NO_MORE_ELEMENT?;
|
||||
};
|
||||
}
|
||||
|
||||
<*
|
||||
@return "True if queue is initialized"
|
||||
*>
|
||||
fn bool LinkedBlockingQueue.is_initialized(&self)
|
||||
{
|
||||
return self.allocator && self.lock.initialized;
|
||||
}
|
||||
646
lib/std/collections/linked_hashmap.c3
Normal file
646
lib/std/collections/linked_hashmap.c3
Normal file
@@ -0,0 +1,646 @@
|
||||
// Copyright (c) 2023 Christoffer Lerno. 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.
|
||||
<*
|
||||
@require $defined((Key){}.hash()) : `No .hash function found on the key`
|
||||
*>
|
||||
module std::collections::map{Key, Value};
|
||||
import std::math;
|
||||
import std::io @norecurse;
|
||||
|
||||
const LinkedHashMap LINKEDONHEAP = { .allocator = MAP_HEAP_ALLOCATOR };
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
struct LinkedHashMap (Printable)
|
||||
{
|
||||
LinkedEntry*[] table;
|
||||
Allocator allocator;
|
||||
usz count;
|
||||
usz threshold;
|
||||
float load_factor;
|
||||
LinkedEntry* head; // First inserted LinkedEntry
|
||||
LinkedEntry* tail; // Last inserted LinkedEntry
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@require capacity > 0 : "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 : "The load factor must be higher than 0"
|
||||
@require !self.is_initialized() : "Map was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn LinkedHashMap* LinkedHashMap.init(&self, Allocator allocator, usz capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
capacity = math::next_power_of_2(capacity);
|
||||
self.allocator = allocator;
|
||||
self.load_factor = load_factor;
|
||||
self.threshold = (usz)(capacity * load_factor);
|
||||
self.table = allocator::new_array(allocator, LinkedEntry*, capacity);
|
||||
self.head = null;
|
||||
self.tail = null;
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@require capacity > 0 : "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 : "The load factor must be higher than 0"
|
||||
@require !self.is_initialized() : "Map was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn LinkedHashMap* LinkedHashMap.tinit(&self, usz capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
return self.init(tmem, capacity, load_factor) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@require $vacount % 2 == 0 : "There must be an even number of arguments provided for keys and values"
|
||||
@require capacity > 0 : "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 : "The load factor must be higher than 0"
|
||||
@require !self.is_initialized() : "Map was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
|
||||
*>
|
||||
macro LinkedHashMap* LinkedHashMap.init_with_key_values(&self, Allocator allocator, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
self.init(allocator, capacity, load_factor);
|
||||
$for var $i = 0; $i < $vacount; $i += 2:
|
||||
self.set($vaarg[$i], $vaarg[$i + 1]);
|
||||
$endfor
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@require $vacount % 2 == 0 : "There must be an even number of arguments provided for keys and values"
|
||||
@require capacity > 0 : "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 : "The load factor must be higher than 0"
|
||||
@require !self.is_initialized() : "Map was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
|
||||
*>
|
||||
macro LinkedHashMap* LinkedHashMap.tinit_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
return self.init_with_key_values(tmem, $vasplat, capacity: capacity, load_factor: load_factor);
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] keys : "The keys for the LinkedHashMap entries"
|
||||
@param [in] values : "The values for the LinkedHashMap entries"
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@require keys.len == values.len : "Both keys and values arrays must be the same length"
|
||||
@require capacity > 0 : "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 : "The load factor must be higher than 0"
|
||||
@require !self.is_initialized() : "Map was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn LinkedHashMap* LinkedHashMap.init_from_keys_and_values(&self, Allocator allocator, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
assert(keys.len == values.len);
|
||||
self.init(allocator, capacity, load_factor);
|
||||
for (usz i = 0; i < keys.len; i++)
|
||||
{
|
||||
self.set(keys[i], values[i]);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] keys : "The keys for the LinkedHashMap entries"
|
||||
@param [in] values : "The values for the LinkedHashMap entries"
|
||||
@require keys.len == values.len : "Both keys and values arrays must be the same length"
|
||||
@require capacity > 0 : "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 : "The load factor must be higher than 0"
|
||||
@require !self.is_initialized() : "Map was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn LinkedHashMap* LinkedHashMap.tinit_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
return self.init_from_keys_and_values(tmem, keys, values, capacity, load_factor);
|
||||
}
|
||||
|
||||
<*
|
||||
Has this hash map been initialized yet?
|
||||
|
||||
@param [&in] map : "The hash map we are testing"
|
||||
@return "Returns true if it has been initialized, false otherwise"
|
||||
*>
|
||||
fn bool LinkedHashMap.is_initialized(&map)
|
||||
{
|
||||
return map.allocator && map.allocator.ptr != &dummy;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@param [&in] other_map : "The map to copy from."
|
||||
@require !self.is_initialized() : "Map was already initialized"
|
||||
*>
|
||||
fn LinkedHashMap* LinkedHashMap.init_from_map(&self, Allocator allocator, LinkedHashMap* other_map)
|
||||
{
|
||||
self.init(allocator, other_map.table.len, other_map.load_factor);
|
||||
self.put_all_for_create(other_map);
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] other_map : "The map to copy from."
|
||||
@require !map.is_initialized() : "Map was already initialized"
|
||||
*>
|
||||
fn LinkedHashMap* LinkedHashMap.tinit_from_map(&map, LinkedHashMap* other_map)
|
||||
{
|
||||
return map.init_from_map(tmem, other_map) @inline;
|
||||
}
|
||||
|
||||
fn bool LinkedHashMap.is_empty(&map) @inline
|
||||
{
|
||||
return !map.count;
|
||||
}
|
||||
|
||||
fn usz LinkedHashMap.len(&map) @inline => map.count;
|
||||
|
||||
fn Value*? LinkedHashMap.get_ref(&map, Key key)
|
||||
{
|
||||
if (!map.count) return NOT_FOUND?;
|
||||
uint hash = rehash(key.hash());
|
||||
for (LinkedEntry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key)) return &e.value;
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
}
|
||||
|
||||
fn LinkedEntry*? LinkedHashMap.get_entry(&map, Key key)
|
||||
{
|
||||
if (!map.count) return NOT_FOUND?;
|
||||
uint hash = rehash(key.hash());
|
||||
for (LinkedEntry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key)) return e;
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
}
|
||||
|
||||
<*
|
||||
Get the value or set it to the value
|
||||
|
||||
@require $defined(Value val = #expr)
|
||||
*>
|
||||
macro Value LinkedHashMap.@get_or_set(&map, Key key, Value #expr)
|
||||
{
|
||||
if (!map.count)
|
||||
{
|
||||
Value val = #expr;
|
||||
map.set(key, val);
|
||||
return val;
|
||||
}
|
||||
uint hash = rehash(key.hash());
|
||||
uint index = index_for(hash, map.table.len);
|
||||
for (LinkedEntry *e = map.table[index]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key)) return e.value;
|
||||
}
|
||||
Value val = #expr;
|
||||
map.add_entry(hash, key, val, index);
|
||||
return val;
|
||||
}
|
||||
|
||||
fn Value? LinkedHashMap.get(&map, Key key) @operator([]) => *map.get_ref(key) @inline;
|
||||
|
||||
fn bool LinkedHashMap.has_key(&map, Key key) => @ok(map.get_ref(key));
|
||||
|
||||
fn bool LinkedHashMap.set(&map, Key key, Value value) @operator([]=)
|
||||
{
|
||||
// If the map isn't initialized, use the defaults to initialize it.
|
||||
switch (map.allocator.ptr)
|
||||
{
|
||||
case &dummy:
|
||||
map.init(mem);
|
||||
case null:
|
||||
map.tinit();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
uint hash = rehash(key.hash());
|
||||
uint index = index_for(hash, map.table.len);
|
||||
for (LinkedEntry *e = map.table[index]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key))
|
||||
{
|
||||
e.value = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
map.add_entry(hash, key, value, index);
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void? LinkedHashMap.remove(&map, Key key) @maydiscard
|
||||
{
|
||||
if (!map.remove_entry_for_key(key)) return NOT_FOUND?;
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.clear(&map)
|
||||
{
|
||||
if (!map.count) return;
|
||||
|
||||
LinkedEntry* entry = map.head;
|
||||
while (entry)
|
||||
{
|
||||
LinkedEntry* next = entry.after;
|
||||
map.free_entry(entry);
|
||||
entry = next;
|
||||
}
|
||||
|
||||
foreach (LinkedEntry** &bucket : map.table)
|
||||
{
|
||||
*bucket = null;
|
||||
}
|
||||
|
||||
map.count = 0;
|
||||
map.head = null;
|
||||
map.tail = null;
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.free(&map)
|
||||
{
|
||||
if (!map.is_initialized()) return;
|
||||
map.clear();
|
||||
map.free_internal(map.table.ptr);
|
||||
map.table = {};
|
||||
}
|
||||
|
||||
fn Key[] LinkedHashMap.tkeys(&self)
|
||||
{
|
||||
return self.keys(tmem) @inline;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$if COPY_KEYS:
|
||||
list[index++] = entry.key.copy(allocator);
|
||||
$else
|
||||
list[index++] = entry.key;
|
||||
$endif
|
||||
entry = entry.after;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
macro LinkedHashMap.@each(map; @body(key, value))
|
||||
{
|
||||
map.@each_entry(; LinkedEntry* entry)
|
||||
{
|
||||
@body(entry.key, entry.value);
|
||||
};
|
||||
}
|
||||
|
||||
macro LinkedHashMap.@each_entry(map; @body(entry))
|
||||
{
|
||||
LinkedEntry* entry = map.head;
|
||||
while (entry)
|
||||
{
|
||||
@body(entry);
|
||||
entry = entry.after;
|
||||
}
|
||||
}
|
||||
|
||||
fn Value[] LinkedHashMap.tvalues(&map) => map.values(tmem) @inline;
|
||||
|
||||
fn Value[] LinkedHashMap.values(&self, Allocator allocator)
|
||||
{
|
||||
if (!self.count) return {};
|
||||
Value[] list = allocator::alloc_array(allocator, Value, self.count);
|
||||
usz index = 0;
|
||||
LinkedEntry* entry = self.head;
|
||||
while (entry)
|
||||
{
|
||||
list[index++] = entry.value;
|
||||
entry = entry.after;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
fn bool LinkedHashMap.has_value(&map, Value v) @if(VALUE_IS_EQUATABLE)
|
||||
{
|
||||
if (!map.count) return false;
|
||||
|
||||
LinkedEntry* entry = map.head;
|
||||
while (entry)
|
||||
{
|
||||
if (equals(v, entry.value)) return true;
|
||||
entry = entry.after;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn LinkedHashMapIterator LinkedHashMap.iter(&self) => { .map = self, .current = self.head, .started = false };
|
||||
|
||||
fn LinkedHashMapValueIterator LinkedHashMap.value_iter(&self) => { .map = self, .current = self.head, .started = false };
|
||||
|
||||
fn LinkedHashMapKeyIterator LinkedHashMap.key_iter(&self) => { .map = self, .current = self.head, .started = false };
|
||||
|
||||
fn bool LinkedHashMapIterator.next(&self)
|
||||
{
|
||||
if (!self.started)
|
||||
{
|
||||
self.current = self.map.head;
|
||||
self.started = true;
|
||||
}
|
||||
else if (self.current)
|
||||
{
|
||||
self.current = self.current.after;
|
||||
}
|
||||
return self.current != null;
|
||||
}
|
||||
|
||||
fn LinkedEntry*? LinkedHashMapIterator.get(&self)
|
||||
{
|
||||
return self.current ? self.current : NOT_FOUND?;
|
||||
}
|
||||
|
||||
fn Value*? LinkedHashMapValueIterator.get(&self)
|
||||
{
|
||||
return self.current ? &self.current.value : NOT_FOUND?;
|
||||
}
|
||||
|
||||
fn Key*? LinkedHashMapKeyIterator.get(&self)
|
||||
{
|
||||
return self.current ? &self.current.key : NOT_FOUND?;
|
||||
}
|
||||
|
||||
fn bool LinkedHashMapIterator.has_next(&self)
|
||||
{
|
||||
if (!self.started) return self.map.head != null;
|
||||
return self.current && self.current.after != null;
|
||||
}
|
||||
|
||||
// --- private methods
|
||||
|
||||
fn void LinkedHashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
|
||||
{
|
||||
$if COPY_KEYS:
|
||||
key = key.copy(map.allocator);
|
||||
$endif
|
||||
|
||||
LinkedEntry* entry = allocator::new(map.allocator, LinkedEntry, {
|
||||
.hash = hash,
|
||||
.key = key,
|
||||
.value = value,
|
||||
.next = map.table[bucket_index],
|
||||
.before = map.tail,
|
||||
.after = null
|
||||
});
|
||||
|
||||
// Update bucket chain
|
||||
map.table[bucket_index] = entry;
|
||||
|
||||
// Update linked list
|
||||
if (map.tail)
|
||||
{
|
||||
map.tail.after = entry;
|
||||
entry.before = map.tail;
|
||||
}
|
||||
else
|
||||
{
|
||||
map.head = entry;
|
||||
}
|
||||
map.tail = entry;
|
||||
|
||||
if (map.count++ >= map.threshold)
|
||||
{
|
||||
map.resize(map.table.len * 2);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if ((e.hash & old_capacity) == 0)
|
||||
{
|
||||
if (!lo_tail)
|
||||
{
|
||||
lo_head = e;
|
||||
}
|
||||
else
|
||||
{
|
||||
lo_tail.next = e;
|
||||
}
|
||||
lo_tail = e;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!hi_tail)
|
||||
{
|
||||
hi_head = e;
|
||||
}
|
||||
else
|
||||
{
|
||||
hi_tail.next = e;
|
||||
}
|
||||
hi_tail = e;
|
||||
}
|
||||
e.next = null;
|
||||
e = next;
|
||||
}
|
||||
while (e);
|
||||
|
||||
if (lo_tail)
|
||||
{
|
||||
lo_tail.next = null;
|
||||
new_table[i] = lo_head;
|
||||
}
|
||||
if (hi_tail)
|
||||
{
|
||||
hi_tail.next = null;
|
||||
new_table[i + old_capacity] = hi_head;
|
||||
}
|
||||
}
|
||||
|
||||
map.free_internal(old_table.ptr);
|
||||
}
|
||||
|
||||
fn usz? LinkedHashMap.to_format(&self, Formatter* f) @dynamic
|
||||
{
|
||||
usz len;
|
||||
len += f.print("{ ")!;
|
||||
self.@each_entry(; LinkedEntry* entry)
|
||||
{
|
||||
if (len > 2) len += f.print(", ")!;
|
||||
len += f.printf("%s: %s", entry.key, entry.value)!;
|
||||
};
|
||||
return len + f.print(" }");
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.transfer(&map, LinkedEntry*[] new_table) @private
|
||||
{
|
||||
LinkedEntry*[] src = map.table;
|
||||
uint new_capacity = new_table.len;
|
||||
foreach (uint j, LinkedEntry *e : src)
|
||||
{
|
||||
if (!e) continue;
|
||||
do
|
||||
{
|
||||
LinkedEntry* next = e.next;
|
||||
uint i = index_for(e.hash, new_capacity);
|
||||
e.next = new_table[i];
|
||||
new_table[i] = e;
|
||||
e = next;
|
||||
}
|
||||
while (e);
|
||||
}
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.put_all_for_create(&map, LinkedHashMap* other_map) @private
|
||||
{
|
||||
if (!other_map.count) return;
|
||||
other_map.@each(; Key key, Value value) {
|
||||
map.set(key, value);
|
||||
};
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.put_for_create(&map, Key key, Value value) @private
|
||||
{
|
||||
uint hash = rehash(key.hash());
|
||||
uint i = index_for(hash, map.table.len);
|
||||
for (LinkedEntry *e = map.table[i]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key))
|
||||
{
|
||||
e.value = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
map.create_entry(hash, key, value, i);
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.free_internal(&map, void* ptr) @inline @private
|
||||
{
|
||||
allocator::free(map.allocator, ptr);
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
if (prev)
|
||||
{
|
||||
prev.next = e.next;
|
||||
}
|
||||
else
|
||||
{
|
||||
map.table[i] = e.next;
|
||||
}
|
||||
|
||||
if (e.before)
|
||||
{
|
||||
e.before.after = e.after;
|
||||
}
|
||||
else
|
||||
{
|
||||
map.head = e.after;
|
||||
}
|
||||
|
||||
if (e.after)
|
||||
{
|
||||
e.after.before = e.before;
|
||||
}
|
||||
else
|
||||
{
|
||||
map.tail = e.before;
|
||||
}
|
||||
|
||||
map.count--;
|
||||
map.free_entry(e);
|
||||
return true;
|
||||
}
|
||||
prev = e;
|
||||
e = e.next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private
|
||||
{
|
||||
LinkedEntry *e = map.table[bucket_index];
|
||||
$if COPY_KEYS:
|
||||
key = key.copy(map.allocator);
|
||||
$endif
|
||||
LinkedEntry* entry = allocator::new(map.allocator, LinkedEntry, { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] });
|
||||
map.table[bucket_index] = entry;
|
||||
map.count++;
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.free_entry(&self, LinkedEntry *entry) @local
|
||||
{
|
||||
$if COPY_KEYS:
|
||||
allocator::free(self.allocator, entry.key);
|
||||
$endif
|
||||
self.free_internal(entry);
|
||||
}
|
||||
|
||||
|
||||
struct LinkedHashMapIterator
|
||||
{
|
||||
LinkedHashMap* map;
|
||||
LinkedEntry* current;
|
||||
bool started;
|
||||
}
|
||||
|
||||
typedef LinkedHashMapValueIterator = inline LinkedHashMapIterator;
|
||||
typedef LinkedHashMapKeyIterator = inline LinkedHashMapIterator;
|
||||
|
||||
fn usz LinkedHashMapValueIterator.len(self) @operator(len) => self.map.count;
|
||||
fn usz LinkedHashMapKeyIterator.len(self) @operator(len) => self.map.count;
|
||||
fn usz LinkedHashMapIterator.len(self) @operator(len) => self.map.count;
|
||||
|
||||
int dummy @local;
|
||||
723
lib/std/collections/linked_hashset.c3
Normal file
723
lib/std/collections/linked_hashset.c3
Normal file
@@ -0,0 +1,723 @@
|
||||
<*
|
||||
@require $defined((Value){}.hash()) : `No .hash function found on the value`
|
||||
*>
|
||||
module std::collections::set {Value};
|
||||
import std::math;
|
||||
import std::io @norecurse;
|
||||
|
||||
const LinkedHashSet LINKEDONHEAP = { .allocator = SET_HEAP_ALLOCATOR };
|
||||
|
||||
struct LinkedEntry
|
||||
{
|
||||
uint hash;
|
||||
Value value;
|
||||
LinkedEntry* next; // For bucket chain
|
||||
LinkedEntry* before; // Previous in insertion order
|
||||
LinkedEntry* after; // Next in insertion order
|
||||
}
|
||||
|
||||
struct LinkedHashSet (Printable)
|
||||
{
|
||||
LinkedEntry*[] table;
|
||||
Allocator allocator;
|
||||
usz count; // Number of elements
|
||||
usz threshold; // Resize limit
|
||||
float load_factor;
|
||||
LinkedEntry* head; // First inserted LinkedEntry
|
||||
LinkedEntry* tail; // Last inserted LinkedEntry
|
||||
}
|
||||
|
||||
fn int LinkedHashSet.len(&self) @operator(len) => (int) self.count;
|
||||
|
||||
<*
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@require capacity > 0 : "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 : "The load factor must be higher than 0"
|
||||
@require !self.is_initialized() : "Set was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn LinkedHashSet* LinkedHashSet.init(&self, Allocator allocator, usz capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
capacity = math::next_power_of_2(capacity);
|
||||
self.allocator = allocator;
|
||||
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;
|
||||
}
|
||||
|
||||
<*
|
||||
@require capacity > 0 : "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 : "The load factor must be higher than 0"
|
||||
@require !self.is_initialized() : "Set was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn LinkedHashSet* LinkedHashSet.tinit(&self, usz capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
return self.init(tmem, capacity, load_factor) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@require capacity > 0 : "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 : "The load factor must be higher than 0"
|
||||
@require !self.is_initialized() : "Set was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
|
||||
*>
|
||||
macro LinkedHashSet* LinkedHashSet.init_with_values(&self, Allocator allocator, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
self.init(allocator, capacity, load_factor);
|
||||
$for var $i = 0; $i < $vacount; $i++:
|
||||
self.add($vaarg[$i]);
|
||||
$endfor
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@require capacity > 0 : "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 : "The load factor must be higher than 0"
|
||||
@require !self.is_initialized() : "Set was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
|
||||
*>
|
||||
macro LinkedHashSet* LinkedHashSet.tinit_with_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
return self.init_with_values(tmem, $vasplat, capacity: capacity, load_factor: load_factor);
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] values : "The values for the LinkedHashSet"
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@require capacity > 0 : "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 : "The load factor must be higher than 0"
|
||||
@require !self.is_initialized() : "Set was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn LinkedHashSet* LinkedHashSet.init_from_values(&self, Allocator allocator, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
self.init(allocator, capacity, load_factor);
|
||||
foreach (v : values) self.add(v);
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] values : "The values for the LinkedHashSet entries"
|
||||
@require capacity > 0 : "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 : "The load factor must be higher than 0"
|
||||
@require !self.is_initialized() : "Set was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn LinkedHashSet* LinkedHashSet.tinit_from_values(&self, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
return self.init_from_values(tmem, values, capacity, load_factor);
|
||||
}
|
||||
|
||||
<*
|
||||
Has this linked hash set been initialized yet?
|
||||
|
||||
@param [&in] set : "The linked hash set we are testing"
|
||||
@return "Returns true if it has been initialized, false otherwise"
|
||||
*>
|
||||
fn bool LinkedHashSet.is_initialized(&set)
|
||||
{
|
||||
return set.allocator && set.allocator.ptr != &dummy;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@param [&in] other_set : "The set to copy from."
|
||||
@require !self.is_initialized() : "Set was already initialized"
|
||||
*>
|
||||
fn LinkedHashSet* LinkedHashSet.init_from_set(&self, Allocator allocator, LinkedHashSet* other_set)
|
||||
{
|
||||
self.init(allocator, other_set.table.len, other_set.load_factor);
|
||||
LinkedEntry* entry = other_set.head;
|
||||
while (entry) // Save insertion order
|
||||
{
|
||||
self.put_for_create(entry.value);
|
||||
entry = entry.after;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] other_set : "The set to copy from."
|
||||
@require !set.is_initialized() : "Set was already initialized"
|
||||
*>
|
||||
fn LinkedHashSet* LinkedHashSet.tinit_from_set(&set, LinkedHashSet* other_set)
|
||||
{
|
||||
return set.init_from_set(tmem, other_set) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
Check if the set is empty
|
||||
|
||||
@return "true if it is empty"
|
||||
@pure
|
||||
*>
|
||||
fn bool LinkedHashSet.is_empty(&set) @inline
|
||||
{
|
||||
return !set.count;
|
||||
}
|
||||
|
||||
<*
|
||||
Add all elements in the slice to the set.
|
||||
|
||||
@param [in] list
|
||||
@return "The number of new elements added"
|
||||
@ensure total <= list.len
|
||||
*>
|
||||
fn usz LinkedHashSet.add_all(&set, Value[] list)
|
||||
{
|
||||
usz total;
|
||||
foreach (v : list)
|
||||
{
|
||||
if (set.add(v)) total++;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] other
|
||||
@return "The number of new elements added"
|
||||
@ensure return <= other.count
|
||||
*>
|
||||
fn usz LinkedHashSet.add_all_from(&set, LinkedHashSet* other)
|
||||
{
|
||||
usz total;
|
||||
other.@each(;Value value)
|
||||
{
|
||||
if (set.add(value)) total++;
|
||||
};
|
||||
return total;
|
||||
}
|
||||
|
||||
<*
|
||||
@param value : "The value to add"
|
||||
@return "true if the value didn't exist in the set"
|
||||
*>
|
||||
fn bool LinkedHashSet.add(&set, Value value)
|
||||
{
|
||||
// If the set isn't initialized, use the defaults to initialize it.
|
||||
switch (set.allocator.ptr)
|
||||
{
|
||||
case &dummy:
|
||||
set.init(mem);
|
||||
case null:
|
||||
set.tinit();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
uint hash = rehash(value.hash());
|
||||
uint index = index_for(hash, set.table.len);
|
||||
for (LinkedEntry *e = set.table[index]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(value, e.value)) return false;
|
||||
}
|
||||
set.add_entry(hash, value, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
<*
|
||||
Iterate over all the values in the set
|
||||
*>
|
||||
macro LinkedHashSet.@each(set; @body(value))
|
||||
{
|
||||
if (!set.count) return;
|
||||
LinkedEntry* entry = set.head;
|
||||
while (entry)
|
||||
{
|
||||
@body(entry.value);
|
||||
entry = entry.after;
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Check if the set contains the given value.
|
||||
|
||||
@param value : "The value to check"
|
||||
@return "true if it exists in the set"
|
||||
*>
|
||||
fn bool LinkedHashSet.contains(&set, Value value)
|
||||
{
|
||||
if (!set.count) return false;
|
||||
uint hash = rehash(value.hash());
|
||||
for (LinkedEntry *e = set.table[index_for(hash, set.table.len)]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(value, e.value)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
<*
|
||||
Remove a single value from the set.
|
||||
|
||||
@param value : "The value to remove"
|
||||
@return? NOT_FOUND : "If the entry is not found"
|
||||
*>
|
||||
fn void? LinkedHashSet.remove(&set, Value value) @maydiscard
|
||||
{
|
||||
if (!set.remove_entry_for_value(value)) return NOT_FOUND?;
|
||||
}
|
||||
|
||||
fn usz LinkedHashSet.remove_all(&set, Value[] values)
|
||||
{
|
||||
usz total;
|
||||
foreach (v : values)
|
||||
{
|
||||
if (set.remove_entry_for_value(v)) total++;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] other : "Other set"
|
||||
*>
|
||||
fn usz LinkedHashSet.remove_all_from(&set, LinkedHashSet* other)
|
||||
{
|
||||
usz total;
|
||||
other.@each(;Value val)
|
||||
{
|
||||
if (set.remove_entry_for_value(val)) total++;
|
||||
};
|
||||
return total;
|
||||
}
|
||||
|
||||
<*
|
||||
Free all memory allocated by the hash set.
|
||||
*>
|
||||
fn void LinkedHashSet.free(&set)
|
||||
{
|
||||
if (!set.is_initialized()) return;
|
||||
set.clear();
|
||||
set.free_internal(set.table.ptr);
|
||||
set.table = {};
|
||||
}
|
||||
|
||||
<*
|
||||
Clear all elements from the set while keeping the underlying storage
|
||||
|
||||
@ensure set.count == 0
|
||||
*>
|
||||
fn void LinkedHashSet.clear(&set)
|
||||
{
|
||||
if (!set.count) return;
|
||||
|
||||
LinkedEntry* entry = set.head;
|
||||
while (entry)
|
||||
{
|
||||
LinkedEntry* next = entry.after;
|
||||
set.free_entry(entry);
|
||||
entry = next;
|
||||
}
|
||||
|
||||
foreach (LinkedEntry** &bucket : set.table)
|
||||
{
|
||||
*bucket = null;
|
||||
}
|
||||
|
||||
set.count = 0;
|
||||
set.head = null;
|
||||
set.tail = null;
|
||||
}
|
||||
|
||||
fn void LinkedHashSet.reserve(&set, usz capacity)
|
||||
{
|
||||
if (capacity > set.threshold)
|
||||
{
|
||||
set.resize(math::next_power_of_2(capacity));
|
||||
}
|
||||
}
|
||||
|
||||
// --- Set Operations ---
|
||||
|
||||
<*
|
||||
Returns the union of two sets (A | B)
|
||||
|
||||
@param [&in] other : "The other set to union with"
|
||||
@param [&inout] allocator : "Allocator for the new set"
|
||||
@return "A new set containing the union of both sets"
|
||||
*>
|
||||
fn LinkedHashSet LinkedHashSet.set_union(&self, Allocator allocator, LinkedHashSet* other)
|
||||
{
|
||||
usz new_capacity = math::next_power_of_2(self.count + other.count);
|
||||
LinkedHashSet result;
|
||||
result.init(allocator, new_capacity, self.load_factor);
|
||||
result.add_all_from(self);
|
||||
result.add_all_from(other);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn LinkedHashSet LinkedHashSet.tset_union(&self, LinkedHashSet* other) => self.set_union(tmem, other) @inline;
|
||||
|
||||
<*
|
||||
Returns the intersection of the two sets (A & B)
|
||||
|
||||
@param [&in] other : "The other set to intersect with"
|
||||
@param [&inout] allocator : "Allocator for the new set"
|
||||
@return "A new set containing the intersection of both sets"
|
||||
*>
|
||||
fn LinkedHashSet LinkedHashSet.intersection(&self, Allocator allocator, LinkedHashSet* other)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (larger.contains(value)) result.add(value);
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn LinkedHashSet LinkedHashSet.tintersection(&self, LinkedHashSet* other) => self.intersection(tmem, other) @inline;
|
||||
|
||||
<*
|
||||
Return this set - other, so (A & ~B)
|
||||
|
||||
@param [&in] other : "The other set to compare with"
|
||||
@param [&inout] allocator : "Allocator for the new set"
|
||||
@return "A new set containing elements in this set but not in the other"
|
||||
*>
|
||||
fn LinkedHashSet LinkedHashSet.difference(&self, Allocator allocator, LinkedHashSet* other)
|
||||
{
|
||||
LinkedHashSet result;
|
||||
result.init(allocator, self.table.len, self.load_factor);
|
||||
self.@each(;Value value)
|
||||
{
|
||||
if (!other.contains(value))
|
||||
{
|
||||
result.add(value);
|
||||
}
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
fn LinkedHashSet LinkedHashSet.tdifference(&self, LinkedHashSet* other) => self.difference(tmem, other) @inline;
|
||||
|
||||
<*
|
||||
Return (A ^ B)
|
||||
|
||||
@param [&in] other : "The other set to compare with"
|
||||
@param [&inout] allocator : "Allocator for the new set"
|
||||
@return "A new set containing elements in this set or the other, but not both"
|
||||
*>
|
||||
fn LinkedHashSet LinkedHashSet.symmetric_difference(&self, Allocator allocator, LinkedHashSet* other)
|
||||
{
|
||||
LinkedHashSet result;
|
||||
result.init(allocator, self.table.len, self.load_factor);
|
||||
result.add_all_from(self);
|
||||
other.@each(;Value value)
|
||||
{
|
||||
if (!result.add(value))
|
||||
{
|
||||
result.remove(value);
|
||||
}
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
fn LinkedHashSet LinkedHashSet.tsymmetric_difference(&self, LinkedHashSet* other) => self.symmetric_difference(tmem, other) @inline;
|
||||
|
||||
<*
|
||||
Check if this hash set is a subset of another set.
|
||||
|
||||
@param [&in] other : "The other set to check against"
|
||||
@return "True if all elements of this set are in the other set"
|
||||
*>
|
||||
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;
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
// --- private methods
|
||||
|
||||
fn void LinkedHashSet.add_entry(&set, uint hash, Value value, uint bucket_index) @private
|
||||
{
|
||||
LinkedEntry* entry = allocator::new(set.allocator, LinkedEntry, {
|
||||
.hash = hash,
|
||||
.value = value,
|
||||
.next = set.table[bucket_index],
|
||||
.before = set.tail,
|
||||
.after = null
|
||||
});
|
||||
|
||||
// Update bucket chain
|
||||
set.table[bucket_index] = entry;
|
||||
|
||||
// Update linked list
|
||||
if (set.tail)
|
||||
{
|
||||
set.tail.after = entry;
|
||||
entry.before = set.tail;
|
||||
}
|
||||
else
|
||||
{
|
||||
set.head = entry;
|
||||
}
|
||||
set.tail = entry;
|
||||
|
||||
if (set.count++ >= set.threshold)
|
||||
{
|
||||
set.resize(set.table.len * 2);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if ((e.hash & old_capacity) == 0)
|
||||
{
|
||||
if (!lo_tail)
|
||||
{
|
||||
lo_head = e;
|
||||
}
|
||||
else
|
||||
{
|
||||
lo_tail.next = e;
|
||||
}
|
||||
lo_tail = e;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!hi_tail)
|
||||
{
|
||||
hi_head = e;
|
||||
}
|
||||
else
|
||||
{
|
||||
hi_tail.next = e;
|
||||
}
|
||||
hi_tail = e;
|
||||
}
|
||||
e.next = null;
|
||||
e = next;
|
||||
}
|
||||
while (e);
|
||||
|
||||
if (lo_tail)
|
||||
{
|
||||
lo_tail.next = null;
|
||||
new_table[i] = lo_head;
|
||||
}
|
||||
if (hi_tail)
|
||||
{
|
||||
hi_tail.next = null;
|
||||
new_table[i + old_capacity] = hi_head;
|
||||
}
|
||||
}
|
||||
|
||||
set.free_internal(old_table.ptr);
|
||||
}
|
||||
|
||||
fn usz? LinkedHashSet.to_format(&self, Formatter* f) @dynamic
|
||||
{
|
||||
usz len;
|
||||
len += f.print("{ ")!;
|
||||
self.@each(; Value value)
|
||||
{
|
||||
if (len > 2) len += f.print(", ")!;
|
||||
len += f.printf("%s", value)!;
|
||||
};
|
||||
return len + f.print(" }");
|
||||
}
|
||||
|
||||
fn void LinkedHashSet.transfer(&set, LinkedEntry*[] new_table) @private
|
||||
{
|
||||
LinkedEntry*[] src = set.table;
|
||||
uint new_capacity = new_table.len;
|
||||
foreach (uint j, LinkedEntry *e : src)
|
||||
{
|
||||
if (!e) continue;
|
||||
do
|
||||
{
|
||||
LinkedEntry* next = e.next;
|
||||
uint i = index_for(e.hash, new_capacity);
|
||||
e.next = new_table[i];
|
||||
new_table[i] = e;
|
||||
e = next;
|
||||
}
|
||||
while (e);
|
||||
}
|
||||
}
|
||||
|
||||
fn void LinkedHashSet.put_for_create(&set, Value value) @private
|
||||
{
|
||||
uint hash = rehash(value.hash());
|
||||
uint i = index_for(hash, set.table.len);
|
||||
for (LinkedEntry *e = set.table[i]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(value, e.value))
|
||||
{
|
||||
// Value already exists, no need to do anything
|
||||
return;
|
||||
}
|
||||
}
|
||||
set.create_entry(hash, value, i);
|
||||
}
|
||||
|
||||
fn void LinkedHashSet.free_internal(&set, void* ptr) @inline @private
|
||||
{
|
||||
allocator::free(set.allocator, ptr);
|
||||
}
|
||||
|
||||
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,
|
||||
.next = set.table[bucket_index],
|
||||
.before = set.tail,
|
||||
.after = null
|
||||
});
|
||||
|
||||
set.table[bucket_index] = entry;
|
||||
|
||||
// Update linked list
|
||||
if (set.tail)
|
||||
{
|
||||
set.tail.after = entry;
|
||||
entry.before = set.tail;
|
||||
}
|
||||
else
|
||||
{
|
||||
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))
|
||||
{
|
||||
if (prev)
|
||||
{
|
||||
prev.next = e.next;
|
||||
}
|
||||
else
|
||||
{
|
||||
set.table[i] = e.next;
|
||||
}
|
||||
|
||||
if (e.before)
|
||||
{
|
||||
e.before.after = e.after;
|
||||
}
|
||||
else
|
||||
{
|
||||
set.head = e.after;
|
||||
}
|
||||
|
||||
if (e.after)
|
||||
{
|
||||
e.after.before = e.before;
|
||||
}
|
||||
else
|
||||
{
|
||||
set.tail = e.before;
|
||||
}
|
||||
|
||||
set.count--;
|
||||
set.free_entry(e);
|
||||
return true;
|
||||
}
|
||||
prev = e;
|
||||
e = e.next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void LinkedHashSet.free_entry(&set, LinkedEntry *entry) @private
|
||||
{
|
||||
allocator::free(set.allocator, entry);
|
||||
}
|
||||
|
||||
struct LinkedHashSetIterator
|
||||
{
|
||||
LinkedHashSet* set;
|
||||
LinkedEntry* current;
|
||||
bool started;
|
||||
}
|
||||
|
||||
fn LinkedHashSetIterator LinkedHashSet.iter(&set) => { .set = set, .current = set.head, .started = false };
|
||||
|
||||
fn bool LinkedHashSetIterator.next(&self)
|
||||
{
|
||||
if (!self.started)
|
||||
{
|
||||
self.current = self.set.head;
|
||||
self.started = true;
|
||||
}
|
||||
else if (self.current)
|
||||
{
|
||||
self.current = self.current.after;
|
||||
}
|
||||
return self.current != null;
|
||||
}
|
||||
|
||||
fn Value*? LinkedHashSetIterator.get(&self)
|
||||
{
|
||||
return self.current ? &self.current.value : NOT_FOUND?;
|
||||
}
|
||||
|
||||
fn bool LinkedHashSetIterator.has_next(&self)
|
||||
{
|
||||
if (!self.started) return self.set.head != null;
|
||||
return self.current && self.current.after != null;
|
||||
}
|
||||
|
||||
fn usz LinkedHashSetIterator.len(&self) @operator(len)
|
||||
{
|
||||
return self.set.count;
|
||||
}
|
||||
|
||||
int dummy @local;
|
||||
@@ -57,7 +57,7 @@ fn List* List.tinit(&self, usz initial_capacity = 16)
|
||||
fn List* List.init_with_array(&self, Allocator allocator, Type[] values)
|
||||
{
|
||||
self.init(allocator, values.len) @inline;
|
||||
self.add_array(values) @inline;
|
||||
self.push_all(values) @inline;
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ fn List* List.init_with_array(&self, Allocator allocator, Type[] values)
|
||||
fn List* List.tinit_with_array(&self, Type[] values)
|
||||
{
|
||||
self.tinit(values.len) @inline;
|
||||
self.add_array(values) @inline;
|
||||
self.push_all(values) @inline;
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -137,9 +137,10 @@ fn Type? List.pop_first(&self)
|
||||
*>
|
||||
fn void List.remove_at(&self, usz index)
|
||||
{
|
||||
self.set_size(self.size - 1);
|
||||
if (!self.size || index == self.size) return;
|
||||
self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
|
||||
usz new_size = self.size - 1;
|
||||
defer self.set_size(new_size);
|
||||
if (!new_size || index == new_size) return;
|
||||
self.entries[index .. new_size - 1] = self.entries[index + 1 .. new_size];
|
||||
}
|
||||
|
||||
fn void List.add_all(&self, List* other_list)
|
||||
@@ -198,7 +199,21 @@ fn Type[] List.array_view(&self)
|
||||
@param [in] array
|
||||
@ensure self.size >= array.len
|
||||
*>
|
||||
fn void List.add_array(&self, Type[] array)
|
||||
fn void List.add_array(&self, Type[] array) @deprecated("Use push_all")
|
||||
{
|
||||
if (!array.len) return;
|
||||
self.reserve(array.len);
|
||||
usz index = self.set_size(self.size + array.len);
|
||||
self.entries[index : array.len] = array[..];
|
||||
}
|
||||
|
||||
<*
|
||||
Add the values of an array to this list.
|
||||
|
||||
@param [in] array
|
||||
@ensure self.size >= array.len
|
||||
*>
|
||||
fn void List.push_all(&self, Type[] array)
|
||||
{
|
||||
if (!array.len) return;
|
||||
self.reserve(array.len);
|
||||
|
||||
@@ -178,11 +178,8 @@ fn void Object.init_array_if_needed(&self) @private
|
||||
fn void Object.set_object(&self, String key, Object* new_object) @private
|
||||
{
|
||||
self.init_map_if_needed();
|
||||
ObjectInternalMapEntry*? entry = self.map.get_entry(key);
|
||||
defer
|
||||
{
|
||||
(void)entry.value.free();
|
||||
}
|
||||
Object*? val = self.map.get_entry(key).value;
|
||||
defer (void)val.free();
|
||||
self.map.set(key, new_object);
|
||||
}
|
||||
|
||||
@@ -206,7 +203,7 @@ macro Object* Object.object_from_value(&self, value) @private
|
||||
return value;
|
||||
$case $Type.typeid == void*.typeid:
|
||||
return &NULL_OBJECT;
|
||||
$case $assignable(value, String):
|
||||
$case @assignable_to(value, String):
|
||||
return new_string(value, self.allocator);
|
||||
$default:
|
||||
$error "Unsupported object type.";
|
||||
|
||||
@@ -1,16 +1,75 @@
|
||||
module std::collections::tuple{Type1, Type2};
|
||||
module std::collections::pair{Type1, Type2};
|
||||
import std::io;
|
||||
|
||||
struct Tuple
|
||||
struct Pair (Printable)
|
||||
{
|
||||
Type1 first;
|
||||
Type2 second;
|
||||
}
|
||||
|
||||
module std::collections::triple{Type1, Type2, Type3};
|
||||
fn usz? Pair.to_format(&self, Formatter* f) @dynamic
|
||||
{
|
||||
return f.printf("{ %s, %s }", self.first, self.second);
|
||||
}
|
||||
|
||||
struct Triple
|
||||
<*
|
||||
@param [&out] a
|
||||
@param [&out] b
|
||||
@require $defined(*a = self.first) : "You cannot assign the first value to a"
|
||||
@require $defined(*b = self.second) : "You cannot assign the second value to b"
|
||||
*>
|
||||
macro void Pair.unpack(&self, a, b)
|
||||
{
|
||||
*a = self.first;
|
||||
*b = self.second;
|
||||
}
|
||||
|
||||
fn bool Pair.equal(self, Pair other) @operator(==) @if (types::has_equals(Type1) &&& types::has_equals(Type2))
|
||||
{
|
||||
return self.first == other.first && self.second == other.second;
|
||||
}
|
||||
|
||||
|
||||
|
||||
module std::collections::triple{Type1, Type2, Type3};
|
||||
import std::io;
|
||||
|
||||
struct Triple (Printable)
|
||||
{
|
||||
Type1 first;
|
||||
Type2 second;
|
||||
Type3 third;
|
||||
}
|
||||
}
|
||||
|
||||
fn usz? Triple.to_format(&self, Formatter* f) @dynamic
|
||||
{
|
||||
return f.printf("{ %s, %s, %s }", self.first, self.second, self.third);
|
||||
}
|
||||
<*
|
||||
@param [&out] a
|
||||
@param [&out] b
|
||||
@param [&out] c
|
||||
@require $defined(*a = self.first) : "You cannot assign the first value to a"
|
||||
@require $defined(*b = self.second) : "You cannot assign the second value to b"
|
||||
@require $defined(*c = self.third) : "You cannot assign the second value to c"
|
||||
*>
|
||||
macro void Triple.unpack(&self, a, b, c)
|
||||
{
|
||||
*a = self.first;
|
||||
*b = self.second;
|
||||
*c = self.third;
|
||||
}
|
||||
|
||||
fn bool Triple.equal(self, Triple other) @operator(==) @if (types::has_equals(Type1) &&& types::has_equals(Type2) &&& types::has_equals(Type3))
|
||||
{
|
||||
return self.first == other.first && self.second == other.second && self.third == other.third;
|
||||
}
|
||||
|
||||
|
||||
module std::collections::tuple{Type1, Type2};
|
||||
|
||||
struct Tuple @deprecated("Use 'Pair' instead")
|
||||
{
|
||||
Type1 first;
|
||||
Type2 second;
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ fn void*? ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignmen
|
||||
}
|
||||
// Otherwise just allocate new memory.
|
||||
void* mem = self.acquire(size, NO_ZERO, alignment)!;
|
||||
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
mem::copy(mem, old_pointer, math::min(size, old_size), mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return mem;
|
||||
}
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ fn void*? BackedArenaAllocator.resize(&self, void* pointer, usz size, usz alignm
|
||||
}
|
||||
|
||||
AllocChunk* data = self.acquire(size, NO_ZERO, alignment)!;
|
||||
mem::copy(data, pointer, chunk.size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
mem::copy(data, pointer, math::min(size, chunk.size), mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ fn void*? DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz a
|
||||
return old_pointer;
|
||||
}
|
||||
void* new_mem = self.acquire(size, NO_ZERO, alignment)!;
|
||||
mem::copy(new_mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
mem::copy(new_mem, old_pointer, math::min(old_size, size), mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return new_mem;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,12 +8,9 @@ import libc;
|
||||
<*
|
||||
The LibcAllocator is a wrapper around malloc to conform to the Allocator interface.
|
||||
*>
|
||||
typedef LibcAllocator (Allocator, Printable) = uptr;
|
||||
typedef LibcAllocator (Allocator) = uptr;
|
||||
const LibcAllocator LIBC_ALLOCATOR = {};
|
||||
|
||||
fn String LibcAllocator.to_string(&self, Allocator allocator) @dynamic => "Libc allocator".copy(allocator);
|
||||
fn usz? LibcAllocator.to_format(&self, Formatter *format) @dynamic => format.print("Libc allocator");
|
||||
|
||||
module std::core::mem::allocator @if(env::POSIX);
|
||||
import std::os;
|
||||
import libc;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module std::core::mem::allocator;
|
||||
|
||||
import std::math;
|
||||
<*
|
||||
The OnStackAllocator is similar to the ArenaAllocator: it allocates from a chunk of memory
|
||||
given to it.
|
||||
@@ -124,7 +124,7 @@ fn void*? OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignm
|
||||
OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof;
|
||||
usz old_size = header.size;
|
||||
void* mem = self.acquire(size, NO_ZERO, alignment)!;
|
||||
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
mem::copy(mem, old_pointer, math::min(old_size, size), mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return mem;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
module std::core::mem::allocator;
|
||||
module std::core::mem::allocator @if(!(env::POSIX || env::WIN32) || !$feature(VMEM_TEMP));
|
||||
import std::io, std::math;
|
||||
import std::core::sanitizer::asan;
|
||||
|
||||
// This implements the temp allocator.
|
||||
// The temp allocator is a specialized allocator only intended for use where
|
||||
@@ -327,3 +326,81 @@ fn void*? TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz al
|
||||
return &page.data[0];
|
||||
}
|
||||
|
||||
module std::core::mem::allocator @if((env::POSIX || env::WIN32) && $feature(VMEM_TEMP));
|
||||
import std::math;
|
||||
|
||||
tlocal VmemOptions temp_allocator_default_options = {
|
||||
.shrink_on_reset = env::MEMORY_ENV != NORMAL,
|
||||
.protect_unused_pages = env::COMPILER_OPT_LEVEL <= O1 || env::COMPILER_SAFE_MODE,
|
||||
.scratch_released_data = env::COMPILER_SAFE_MODE
|
||||
};
|
||||
|
||||
|
||||
fn TempAllocator*? new_temp_allocator(Allocator allocator, usz size, usz reserve = temp_allocator_reserve_size, usz min_size = temp_allocator_min_size, usz realloc_size = temp_allocator_realloc_size)
|
||||
{
|
||||
Vmem mem;
|
||||
TempAllocator* t = allocator::new(allocator, TempAllocator);
|
||||
defer catch allocator::free(allocator, t);
|
||||
t.vmem.init(preferred_size: isz.sizeof > 4 ? 4 * mem::GB : 512 * mem::MB,
|
||||
reserve_page_size: isz.sizeof > 4 ? 256 * mem::KB : 0,
|
||||
options: temp_allocator_default_options)!;
|
||||
t.allocator = allocator;
|
||||
return t;
|
||||
}
|
||||
|
||||
struct TempAllocator (Allocator)
|
||||
{
|
||||
Vmem vmem;
|
||||
TempAllocator* derived;
|
||||
Allocator allocator;
|
||||
}
|
||||
|
||||
<*
|
||||
@require size > 0
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
|
||||
*>
|
||||
fn void*? TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
|
||||
{
|
||||
return self.vmem.acquire(size, init_type, alignment) @inline;
|
||||
}
|
||||
|
||||
fn TempAllocator*? TempAllocator.derive_allocator(&self, usz reserve = 0)
|
||||
{
|
||||
if (self.derived) return self.derived;
|
||||
return self.derived = new_temp_allocator(self.allocator, 0)!;
|
||||
}
|
||||
|
||||
<*
|
||||
Reset the entire temp allocator, destroying all children
|
||||
*>
|
||||
fn void TempAllocator.reset(&self)
|
||||
{
|
||||
TempAllocator* child = self.derived;
|
||||
if (!child) return;
|
||||
child.reset();
|
||||
child.vmem.reset(0);
|
||||
}
|
||||
fn void TempAllocator.free(&self)
|
||||
{
|
||||
self.destroy();
|
||||
}
|
||||
|
||||
fn void TempAllocator.destroy(&self) @local
|
||||
{
|
||||
TempAllocator* child = self.derived;
|
||||
if (!child) return;
|
||||
child.destroy();
|
||||
self.vmem.free() @inline;
|
||||
allocator::free(self.allocator, self) @inline;
|
||||
}
|
||||
|
||||
fn void*? TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @dynamic
|
||||
{
|
||||
return self.vmem.resize(pointer, size, alignment) @inline;
|
||||
}
|
||||
|
||||
fn void TempAllocator.release(&self, void* old_pointer, bool b) @dynamic
|
||||
{
|
||||
self.vmem.release(old_pointer, b) @inline;
|
||||
}
|
||||
252
lib/std/core/allocators/vmem.c3
Normal file
252
lib/std/core/allocators/vmem.c3
Normal file
@@ -0,0 +1,252 @@
|
||||
module std::core::mem::allocator @if(env::POSIX || env::WIN32);
|
||||
import std::math, std::os::posix, libc, std::bits;
|
||||
import std::core::mem;
|
||||
import std::core::env;
|
||||
|
||||
|
||||
|
||||
// Virtual Memory allocator
|
||||
|
||||
faultdef VMEM_RESERVE_FAILED;
|
||||
|
||||
struct Vmem (Allocator)
|
||||
{
|
||||
VirtualMemory memory;
|
||||
usz allocated;
|
||||
usz pagesize;
|
||||
usz page_pot;
|
||||
usz last_page;
|
||||
usz high_water;
|
||||
VmemOptions options;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
<*
|
||||
Implements the Allocator interface method.
|
||||
|
||||
@require !reserve_page_size || math::is_power_of_2(reserve_page_size)
|
||||
@require reserve_page_size <= preferred_size : "The min reserve_page_size size must be less or equal to the preferred size"
|
||||
@require preferred_size >= 1 * mem::KB : "The preferred size must exceed 1 KB"
|
||||
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY, VMEM_RESERVE_FAILED
|
||||
*>
|
||||
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?;
|
||||
while (preferred_size >= min_size)
|
||||
{
|
||||
memory = vm::virtual_alloc(preferred_size, PROTECTED);
|
||||
// It worked?
|
||||
if (try memory) break;
|
||||
switch (@catch(memory))
|
||||
{
|
||||
case mem::OUT_OF_MEMORY:
|
||||
case vm::RANGE_OVERFLOW:
|
||||
// Try a smaller size.
|
||||
preferred_size /= 2;
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (catch memory) return VMEM_RESERVE_FAILED?;
|
||||
if (page_size > preferred_size) page_size = preferred_size;
|
||||
$if env::ADDRESS_SANITIZER:
|
||||
asan::poison_memory_region(memory.ptr, memory.size);
|
||||
$endif
|
||||
*self = { .memory = memory,
|
||||
.high_water = 0,
|
||||
.pagesize = page_size,
|
||||
.page_pot = page_size.ctz(),
|
||||
.options = options,
|
||||
};
|
||||
}
|
||||
|
||||
<*
|
||||
Implements the Allocator interface method.
|
||||
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
|
||||
@require size > 0
|
||||
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
|
||||
*>
|
||||
fn void*? Vmem.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
|
||||
{
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
usz total_len = self.memory.size;
|
||||
if (size > total_len) return mem::INVALID_ALLOC_SIZE?;
|
||||
void* start_mem = self.memory.ptr;
|
||||
void* unaligned_pointer_to_offset = start_mem + self.allocated + VmemHeader.sizeof;
|
||||
void* mem = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
|
||||
usz after = (usz)(mem - start_mem) + size;
|
||||
if (after > total_len) return mem::OUT_OF_MEMORY?;
|
||||
if (init_type == ZERO && self.high_water <= self.allocated)
|
||||
{
|
||||
init_type = NO_ZERO;
|
||||
}
|
||||
protect(self, after)!;
|
||||
VmemHeader* header = mem - VmemHeader.sizeof;
|
||||
header.size = size;
|
||||
if (init_type == ZERO) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return mem;
|
||||
}
|
||||
|
||||
fn bool Vmem.owns_pointer(&self, void* ptr) @inline
|
||||
{
|
||||
return (uptr)ptr >= (uptr)self.memory.ptr && (uptr)ptr < (uptr)self.memory.ptr + self.memory.size;
|
||||
}
|
||||
<*
|
||||
Implements the Allocator interface method.
|
||||
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
|
||||
@require old_pointer != null
|
||||
@require size > 0
|
||||
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
|
||||
*>
|
||||
fn void*? Vmem.resize(&self, void *old_pointer, usz size, usz alignment) @dynamic
|
||||
{
|
||||
if (size > self.memory.size) return mem::INVALID_ALLOC_SIZE?;
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
assert(self.owns_pointer(old_pointer), "Pointer originates from a different allocator: %p, not in %p - %p", old_pointer, self.memory.ptr, self.memory.ptr + self.allocated);
|
||||
VmemHeader* header = old_pointer - VmemHeader.sizeof;
|
||||
usz old_size = header.size;
|
||||
if (old_size == size) return old_pointer;
|
||||
// Do last allocation and alignment match?
|
||||
if (self.memory.ptr + self.allocated == old_pointer + old_size && mem::ptr_is_aligned(old_pointer, alignment))
|
||||
{
|
||||
if (old_size > size)
|
||||
{
|
||||
unprotect(self, self.allocated + size - old_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
usz allocated = self.allocated + size - old_size;
|
||||
if (allocated > self.memory.size) return mem::OUT_OF_MEMORY?;
|
||||
protect(self, allocated)!;
|
||||
}
|
||||
header.size = size;
|
||||
return old_pointer;
|
||||
}
|
||||
if (old_size > size)
|
||||
{
|
||||
$if env::ADDRESS_SANITIZER:
|
||||
asan::poison_memory_region(old_pointer + size, old_size - size);
|
||||
$endif
|
||||
header.size = size;
|
||||
return old_pointer;
|
||||
}
|
||||
// Otherwise just allocate new memory.
|
||||
void* mem = self.acquire(size, NO_ZERO, alignment)!;
|
||||
assert(size > old_size);
|
||||
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return mem;
|
||||
}
|
||||
|
||||
<*
|
||||
Implements the Allocator interface method.
|
||||
|
||||
@require ptr != null
|
||||
*>
|
||||
fn void Vmem.release(&self, void* ptr, bool) @dynamic
|
||||
{
|
||||
assert(self.owns_pointer(ptr), "Pointer originates from a different allocator %p.", ptr);
|
||||
VmemHeader* header = ptr - VmemHeader.sizeof;
|
||||
// Reclaim memory if it's the last element.
|
||||
if (ptr + header.size == self.memory.ptr + self.allocated)
|
||||
{
|
||||
unprotect(self, self.allocated - header.size - VmemHeader.sizeof);
|
||||
}
|
||||
}
|
||||
|
||||
fn usz Vmem.mark(&self)
|
||||
{
|
||||
return self.allocated;
|
||||
}
|
||||
|
||||
<*
|
||||
@require mark <= self.allocated : "Invalid mark"
|
||||
*>
|
||||
fn void Vmem.reset(&self, usz mark)
|
||||
{
|
||||
if (mark == self.allocated) return;
|
||||
unprotect(self, mark);
|
||||
}
|
||||
|
||||
fn void Vmem.free(&self)
|
||||
{
|
||||
if (!self.memory.ptr) return;
|
||||
$switch:
|
||||
$case env::ADDRESS_SANITIZER:
|
||||
asan::poison_memory_region(self.memory.ptr, self.memory.size);
|
||||
$case env::COMPILER_SAFE_MODE:
|
||||
((char*)self.memory.ptr)[0:self.allocated] = 0xAA;
|
||||
$endswitch
|
||||
(void)self.memory.destroy();
|
||||
*self = {};
|
||||
}
|
||||
|
||||
// Internal data
|
||||
|
||||
struct VmemHeader @local
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
macro void? protect(Vmem* mem, usz after) @local
|
||||
{
|
||||
usz shift = mem.page_pot;
|
||||
usz page_after = (after + mem.pagesize - 1) >> shift;
|
||||
usz last_page = mem.last_page;
|
||||
bool over_high_water = mem.high_water < after;
|
||||
if (page_after > last_page)
|
||||
{
|
||||
usz page_start = last_page << shift;
|
||||
usz page_len = (page_after - last_page) << shift;
|
||||
mem.memory.commit(page_start, page_len)!;
|
||||
if (mem.options.protect_unused_pages || over_high_water)
|
||||
{
|
||||
mem.memory.protect(page_start, page_len, READWRITE)!;
|
||||
}
|
||||
mem.last_page = page_after;
|
||||
}
|
||||
$if env::ADDRESS_SANITIZER:
|
||||
asan::unpoison_memory_region(mem.memory.ptr + mem.allocated, after - mem.allocated);
|
||||
$endif
|
||||
mem.allocated = after;
|
||||
if (over_high_water) mem.high_water = after;
|
||||
}
|
||||
|
||||
macro void unprotect(Vmem* mem, usz after) @local
|
||||
{
|
||||
usz shift = mem.page_pot;
|
||||
usz last_page = mem.last_page;
|
||||
usz page_after = mem.last_page = (after + mem.pagesize - 1) >> shift;
|
||||
$if env::ADDRESS_SANITIZER:
|
||||
asan::poison_memory_region(mem.memory.ptr + after, mem.allocated - after);
|
||||
$else
|
||||
if (mem.options.scratch_released_data)
|
||||
{
|
||||
mem::set(mem.memory.ptr + after, 0xAA, mem.allocated - after);
|
||||
}
|
||||
$endif
|
||||
if ((mem.options.shrink_on_reset || mem.options.protect_unused_pages) && page_after < last_page)
|
||||
{
|
||||
usz start = page_after << shift;
|
||||
usz len = (last_page - page_after) << shift;
|
||||
if (mem.options.shrink_on_reset) (void)mem.memory.decommit(start, len, false);
|
||||
if (mem.options.protect_unused_pages) (void)mem.memory.protect(start, len, PROTECTED);
|
||||
}
|
||||
mem.allocated = after;
|
||||
}
|
||||
133
lib/std/core/ansi.c3
Normal file
133
lib/std/core/ansi.c3
Normal file
@@ -0,0 +1,133 @@
|
||||
module std::core::string::ansi;
|
||||
|
||||
enum Ansi : const inline String
|
||||
{
|
||||
RESET = "\e[0m",
|
||||
BOLD = "\e[1m",
|
||||
DIM = "\e[2m",
|
||||
ITALIC = "\e[3m",
|
||||
UNDERLINE = "\e[4m",
|
||||
BLINK = "\e[5m",
|
||||
BLINK_FAST = "\e[6m",
|
||||
INVERT = "\e[7m",
|
||||
HIDDEN = "\e[8m",
|
||||
STRIKETHROUGH = "\e[9m",
|
||||
DOUBLE_UNDER = "\e[21m",
|
||||
NO_DIM = "\e[22m",
|
||||
NO_ITALIC = "\e[23m",
|
||||
NO_UNDERLINE = "\e[24m",
|
||||
NO_BLINK = "\e[25m",
|
||||
NO_INVERT = "\e[27m",
|
||||
NO_HIDDEN = "\e[28m",
|
||||
NO_STRIKETHROUGH = "\e[29m",
|
||||
BLACK = "\e[30m",
|
||||
RED = "\e[31m",
|
||||
GREEN = "\e[32m",
|
||||
YELLOW = "\e[33m",
|
||||
BLUE = "\e[34m",
|
||||
MAGENTA = "\e[35m",
|
||||
CYAN = "\e[36m",
|
||||
WHITE = "\e[37m",
|
||||
DEFAULT = "\e[39m",
|
||||
BRIGHT_BLACK = "\e[90m",
|
||||
BRIGHT_RED = "\e[91m",
|
||||
BRIGHT_GREEN = "\e[92m",
|
||||
BRIGHT_YELLOW = "\e[93m",
|
||||
BRIGHT_BLUE = "\e[94m",
|
||||
BRIGHT_MAGENTA = "\e[95m",
|
||||
BRIGHT_CYAN = "\e[96m",
|
||||
BRIGHT_WHITE = "\e[97m",
|
||||
BG_BLACK = "\e[40m",
|
||||
BG_RED = "\e[41m",
|
||||
BG_GREEN = "\e[42m",
|
||||
BG_YELLOW = "\e[43m",
|
||||
BG_BLUE = "\e[44m",
|
||||
BG_MAGENTA = "\e[45m",
|
||||
BG_CYAN = "\e[46m",
|
||||
BG_WHITE = "\e[47m",
|
||||
BG_DEFAULT = "\e[49m",
|
||||
BG_BRIGHT_BLACK = "\e[100m",
|
||||
BG_BRIGHT_RED = "\e[101m",
|
||||
BG_BRIGHT_GREEN = "\e[102m",
|
||||
BG_BRIGHT_YELLOW = "\e[103m",
|
||||
BG_BRIGHT_BLUE = "\e[104m",
|
||||
BG_BRIGHT_MAGENTA = "\e[105m",
|
||||
BG_BRIGHT_CYAN = "\e[106m",
|
||||
BG_BRIGHT_WHITE = "\e[107m",
|
||||
}
|
||||
|
||||
<*
|
||||
8-bit color code
|
||||
|
||||
@return `the formatting char for the given background color`
|
||||
*>
|
||||
macro String color_8bit(char $index, bool $bg = false) @const
|
||||
{
|
||||
int $mode = $bg ? 4 : 3;
|
||||
return @sprintf("\e[%s8;5;%sm", $mode, $index);
|
||||
}
|
||||
|
||||
<*
|
||||
24-bit color code
|
||||
|
||||
@return `the string for the given foreground color`
|
||||
*>
|
||||
macro String color_rgb(char $r, char $g, char $b, bool $bg = false) @const
|
||||
{
|
||||
int $mode = $bg ? 4 : 3;
|
||||
return @sprintf("\e[%s8;2;%s;%s;%sm", $mode, $r, $g, $b);
|
||||
}
|
||||
|
||||
<*
|
||||
24-bit color code rgb
|
||||
|
||||
@require $rgb <= 0xFF_FF_FF : `Expected a 24 bit RGB value`
|
||||
@return `the string char for the given foreground color`
|
||||
*>
|
||||
macro String color(uint $rgb, bool $bg = false) @const
|
||||
{
|
||||
int $mode = $bg ? 4 : 3;
|
||||
return @sprintf("\e[%s8;2;%s;%s;%sm", $mode, $rgb >> 16, ($rgb & 0xFF00) >> 8, $rgb & 0xFF);
|
||||
}
|
||||
|
||||
<*
|
||||
24-bit color code rgb
|
||||
|
||||
@require rgb <= 0xFF_FF_FF : `Expected a 24 bit RGB value`
|
||||
@return `the string char for the given foreground color`
|
||||
*>
|
||||
fn String make_color(Allocator mem, uint rgb, bool bg = false)
|
||||
{
|
||||
return make_color_rgb(mem, (char)(rgb >> 16), (char)((rgb & 0xFF00) >> 8), (char)rgb, bg);
|
||||
}
|
||||
|
||||
<*
|
||||
24-bit color code rgb
|
||||
|
||||
@require rgb <= 0xFF_FF_FF : `Expected a 24 bit RGB value`
|
||||
@return `the string char for the given foreground color`
|
||||
*>
|
||||
fn String make_tcolor(uint rgb, bool bg = false)
|
||||
{
|
||||
return make_color_rgb(tmem, (char)(rgb >> 16), (char)((rgb & 0xFF00) >> 8), (char)rgb, bg);
|
||||
}
|
||||
|
||||
<*
|
||||
24-bit color code rgb
|
||||
|
||||
@return `the string char for the given foreground color`
|
||||
*>
|
||||
fn String make_color_rgb(Allocator mem, char r, char g, char b, bool bg = false)
|
||||
{
|
||||
return string::format(mem, "\e[%s8;2;%s;%s;%sm", bg ? 4 : 3, r, g, b);
|
||||
}
|
||||
|
||||
<*
|
||||
24-bit color code rgb
|
||||
|
||||
@return `the string char for the given foreground color`
|
||||
*>
|
||||
fn String make_tcolor_rgb(char r, char g, char b, bool bg = false)
|
||||
{
|
||||
return string::format(tmem, "\e[%s8;2;%s;%s;%sm", bg ? 4 : 3, r, g, b);
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
module std::core::array;
|
||||
import std::core::array::slice;
|
||||
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 @typekind(array) == SLICE || @typekind(array) == ARRAY
|
||||
@require @typeis(array[0], $typeof(element)) : "array and element must have the same type"
|
||||
@require $Arr.kindof == SLICE || $Arr.kindof == ARRAY
|
||||
@require $Arr.inner == $Type : "array and element must have the same type"
|
||||
*>
|
||||
macro bool contains(array, element)
|
||||
macro bool contains($Arr array, $Type element)
|
||||
{
|
||||
foreach (&item : array)
|
||||
{
|
||||
@@ -18,11 +18,14 @@ macro bool contains(array, element)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Return the first index of element found in the array, searching from the start.
|
||||
|
||||
@param [in] array
|
||||
@param [in] element
|
||||
@require @typekind(array) == SLICE || @typekind(array) == ARRAY
|
||||
@require @typematch(array[0], element) : "array and element must have the same type"
|
||||
@return "the first index of the element"
|
||||
@return? NOT_FOUND
|
||||
*>
|
||||
@@ -35,6 +38,7 @@ macro index_of(array, element)
|
||||
return NOT_FOUND?;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Slice a 2d array and create a Slice2d from it.
|
||||
|
||||
@@ -74,6 +78,7 @@ macro rindex_of(array, element)
|
||||
return NOT_FOUND?;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Concatenate two arrays or slices, returning a slice containing the concatenation of them.
|
||||
|
||||
@@ -82,7 +87,7 @@ macro rindex_of(array, element)
|
||||
@param [&inout] allocator : "The allocator to use, default is the heap allocator"
|
||||
@require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY
|
||||
@require @typekind(arr2) == SLICE || @typekind(arr2) == ARRAY
|
||||
@require @typeis(arr1[0], $typeof(arr2[0])) : "Arrays must have the same type"
|
||||
@require @typematch(arr1[0], arr2[0]) : "Arrays must have the same type"
|
||||
@ensure result.len == arr1.len + arr2.len
|
||||
*>
|
||||
macro concat(Allocator allocator, arr1, arr2) @nodiscard
|
||||
@@ -99,6 +104,7 @@ macro concat(Allocator allocator, arr1, arr2) @nodiscard
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
<*
|
||||
Concatenate two arrays or slices, returning a slice containing the concatenation of them,
|
||||
allocated using the temp allocator.
|
||||
@@ -107,7 +113,450 @@ macro concat(Allocator allocator, arr1, arr2) @nodiscard
|
||||
@param [in] arr2
|
||||
@require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY
|
||||
@require @typekind(arr2) == SLICE || @typekind(arr2) == ARRAY
|
||||
@require @typeis(arr1[0], $typeof(arr2[0])) : "Arrays must have the same type"
|
||||
@require @typematch(arr1[0], arr2[0]) : "Arrays must have the same type"
|
||||
@ensure return.len == arr1.len + arr2.len
|
||||
*>
|
||||
macro tconcat(arr1, arr2) @nodiscard => concat(tmem, arr1, arr2);
|
||||
macro tconcat(arr1, arr2) @nodiscard => concat(tmem, arr1, arr2);
|
||||
|
||||
|
||||
<*
|
||||
Apply a reduction/folding operation to an iterable type. This walks along the input array
|
||||
and applies an `#operation` to each value, returning it to the `identity` (or "accumulator")
|
||||
base value.
|
||||
|
||||
For example:
|
||||
```c3
|
||||
int[] my_slice = { 1, 8, 12 };
|
||||
int folded = array::@reduce(my_slice, 2, fn (i, e) => i * e);
|
||||
assert(folded == (2 * 1 * 8 * 12));
|
||||
```
|
||||
|
||||
Notice how the given `identity` value started the multiplication chain at 2. When enumerating
|
||||
`my_slice`, each element is accumulated onto the `identity` value with each sequential iteration.
|
||||
```
|
||||
i = 2; // identity value
|
||||
i *= 1; // my_slice[0]
|
||||
i *= 8; // my_slice[1]
|
||||
i *= 12; // my_slice[2]
|
||||
```
|
||||
|
||||
@param [in] array
|
||||
@param identity
|
||||
@param #operation : "The reduction/folding labmda 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"
|
||||
*>
|
||||
macro @reduce(array, identity, #operation)
|
||||
{
|
||||
$typefrom(@reduce_fn(array, identity)) $func = #operation;
|
||||
foreach (index, element : array) identity = $func(identity, element, index);
|
||||
return identity;
|
||||
}
|
||||
|
||||
<*
|
||||
Apply a summation operator (+) to an identity value across a span of array elements
|
||||
and return the final accumulated result.
|
||||
|
||||
@pure
|
||||
|
||||
@param [in] array
|
||||
@param identity_value : "The base accumulator value to use for the sum"
|
||||
|
||||
@require @is_valid_list(array) : "Expected a valid list"
|
||||
@require $defined(array[0] + array[0]) : "Array element type must implement the '+' operator"
|
||||
@require $defined($typeof(array[0]) t = identity_value) : "The identity type must be assignable to the array element type"
|
||||
*>
|
||||
macro @sum(array, identity_value = 0)
|
||||
{
|
||||
return @reduce(array, ($typeof(array[0]))identity_value, fn (acc, e, u) => acc + e);
|
||||
}
|
||||
|
||||
<*
|
||||
Apply a product operator (*) to an identity value across a span of array elements
|
||||
and return the final accumulated result.
|
||||
|
||||
@pure
|
||||
|
||||
@param [in] array
|
||||
@param identity_value : "The base accumulator value to use for the product"
|
||||
|
||||
@require @is_valid_list(array) : "Expected a valid list"
|
||||
@require $defined(array[0] * array[0]) : "Array element type must implement the '*' operator"
|
||||
@require $defined($typeof(array[0]) t = identity_value) : "The identity type must be assignable to the array element type"
|
||||
*>
|
||||
macro @product(array, identity_value = 1)
|
||||
{
|
||||
return @reduce(array, ($typeof(array[0]))identity_value, fn (acc, e, u) => acc * e);
|
||||
}
|
||||
|
||||
<*
|
||||
Applies a given predicate function to each element of an array and returns a new
|
||||
array of `usz` values, each element representing an index within the original array
|
||||
where the predicate returned `true`.
|
||||
|
||||
The `.len` value of the returned array can also be used to quickly identify how many
|
||||
input array elements matched the predicate.
|
||||
|
||||
For example:
|
||||
```c3
|
||||
int[] arr = { 0, 20, 4, 30 };
|
||||
int[] matched_indices = array::@indices_of(mem, arr, fn (u, a) => a > 10);
|
||||
```
|
||||
|
||||
The `matched_indices` variable should contain a dynamically-allocated array of `[1, 3]`,
|
||||
and thus its count indicates that 2 of the 4 elements matched the predicate condition.
|
||||
|
||||
@param [&inout] allocator
|
||||
@param [in] array
|
||||
@param #predicate
|
||||
|
||||
@require @is_valid_list(array) : "Expected a valid list"
|
||||
@require $defined($typefrom(@predicate_fn(array)) p = #predicate)
|
||||
*>
|
||||
macro usz[] @indices_of(Allocator allocator, array, #predicate)
|
||||
{
|
||||
usz[] results = allocator::new_array(allocator, usz, find_len(array));
|
||||
usz matches;
|
||||
|
||||
$typefrom(@predicate_fn(array)) $predicate = #predicate;
|
||||
foreach (index, element : array)
|
||||
{
|
||||
if ($predicate(element, index)) results[matches++] = index;
|
||||
}
|
||||
|
||||
return results[:matches];
|
||||
}
|
||||
|
||||
<*
|
||||
Array `@indices_of` using the temp allocator.
|
||||
|
||||
@param [in] array
|
||||
@param #predicate
|
||||
|
||||
@require @is_valid_list(array) : "Expected a valid list"
|
||||
@require $defined($typefrom(@predicate_fn(array)) p = #predicate)
|
||||
*>
|
||||
macro usz[] @tindices_of(array, #predicate)
|
||||
{
|
||||
return @indices_of(tmem, array, #predicate);
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Applies a predicate function to each element of an input array and returns a new array
|
||||
containing shallow copies of _only_ the elements for which the predicate function returned
|
||||
a `true` value.
|
||||
|
||||
For example:
|
||||
```c3
|
||||
int[] my_arr = { 1, 2, 4, 10, 11, 45 };
|
||||
int[] evens = array::@filter(mem, my_arr, fn (e, u) => !(e % 2));
|
||||
assert(evens == (int[]){2, 4, 10 });
|
||||
```
|
||||
|
||||
@param [&inout] allocator
|
||||
@param [in] array
|
||||
@param #predicate
|
||||
|
||||
@require @is_valid_list(array) : "Expected a valid list"
|
||||
@require $defined($typefrom(@predicate_fn(array)) p = #predicate)
|
||||
*>
|
||||
macro @filter(Allocator allocator, array, #predicate) @nodiscard
|
||||
{
|
||||
var $InnerType = $typeof(array[0]);
|
||||
|
||||
usz[] matched_indices = @indices_of(allocator, array, #predicate);
|
||||
defer allocator::free(allocator, matched_indices.ptr); // can free this upon leaving this call
|
||||
|
||||
if (!matched_indices.len) return ($InnerType[]){};
|
||||
|
||||
$InnerType[] result = allocator::new_array(allocator, $InnerType, matched_indices.len);
|
||||
|
||||
foreach (i, index : matched_indices) result[i] = array[index];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
<*
|
||||
Array `@filter` using the temp allocator.
|
||||
|
||||
@param [in] array
|
||||
@param #predicate
|
||||
|
||||
@require @is_valid_list(array) : "Expected a valid list"
|
||||
@require $defined($typefrom(@predicate_fn(array)) p = #predicate)
|
||||
*>
|
||||
macro @tfilter(array, #predicate) @nodiscard
|
||||
{
|
||||
return @filter(tmem, array, #predicate);
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Returns `true` if _any_ element of the input array returns `true` when
|
||||
the `#predicate` function is applied.
|
||||
|
||||
@param [in] array
|
||||
@param #predicate
|
||||
|
||||
@require @is_valid_list(array) : "Expected a valid list"
|
||||
@require $defined($typefrom(@predicate_fn(array)) p = #predicate)
|
||||
*>
|
||||
macro bool @any(array, #predicate)
|
||||
{
|
||||
$typefrom(@predicate_fn(array)) $predicate = #predicate;
|
||||
foreach (index, element : array) if ($predicate(element, index)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Returns `true` if _all_ elements of the input array return `true` when
|
||||
the `#predicate` function is applied.
|
||||
|
||||
@param [in] array
|
||||
@param #predicate
|
||||
|
||||
@require @is_valid_list(array) : "Expected a valid list"
|
||||
@require $defined($typefrom(@predicate_fn(array)) p = #predicate)
|
||||
*>
|
||||
macro bool @all(array, #predicate)
|
||||
{
|
||||
$typefrom(@predicate_fn(array)) $predicate = #predicate;
|
||||
foreach (index, element : array) if (!$predicate(element, index)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Zip together two separate arrays/slices into a single array of Pairs or return values. Values will
|
||||
be collected up to the length of the shorter array if `fill_with` is left undefined; otherwise, they
|
||||
will be collected up to the length of the LONGER array, with missing values in the shorter array being
|
||||
assigned to the value of `fill_with`. Return array elements do not have to be of the same type.
|
||||
|
||||
For example:
|
||||
```c3
|
||||
uint[] chosen_session_ids = server::get_random_sessions(instance)[:128];
|
||||
String[200] refreshed_session_keys = prng::new_keys_batch();
|
||||
|
||||
Pair { uint, String }[] sessions_meta = array::zip(mem, chosen_session_ids, refreshed_session_keys);
|
||||
// The resulting Pair{}[] slice is then length of the shortest of the two arrays, so 128.
|
||||
|
||||
foreach (i, &sess : sessions:meta) {
|
||||
// distribute new session keys to associated instance IDs
|
||||
}
|
||||
```
|
||||
|
||||
Or:
|
||||
```c3
|
||||
String[] client_names = server::online_usernames(instance);
|
||||
uint128[] session_ids = server::user_keys();
|
||||
|
||||
// in this example, we 'know' ahead of time that 'session_ids' can only ever be SHORTER
|
||||
// than 'client_names', but never longer, because it's possible new users have logged
|
||||
// in without getting whatever this 'session ID' is delegated to them.
|
||||
Pair { String, uint128 }[] zipped = array::tzip(client_names, session_ids, fill_with: uint128.max);
|
||||
|
||||
server::refresh_session_keys_by_pair(zipped)!;
|
||||
```
|
||||
|
||||
### When an `operation` is supplied...
|
||||
Apply an operation to each element of two slices or arrays and return the results of
|
||||
each operation into a newly allocated array.
|
||||
|
||||
This essentially combines Iterable1 with Iterable2 using the `operation` functor.
|
||||
|
||||
See the functional `zipWith` construct, which has a more appropriate name than, e.g., `map`;
|
||||
a la: https://hackage.haskell.org/package/base-4.21.0.0/docs/Prelude.html#v:zipWith
|
||||
|
||||
Similar to "normal" `zip`, this macro pads the shorter input array with a given `fill_with`, or
|
||||
an empty value if one isn't supplied. This `fill_with` is supplied to the `operation` functor
|
||||
_BEFORE_ calculating its result while zipping.
|
||||
|
||||
For example: a functor of `fn char (char a, char b) => a + b` with a `fill_with` of 7,
|
||||
where the `left` array is the shorter iterable, will put 7 into that lambda in each place
|
||||
where `left` is being filled in during the zip operation.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use; default is the heap allocator."
|
||||
@param [in] left : "The left-side array. These items will be placed as the First in each Pair"
|
||||
@param [in] right : "The right-side array. These items will be placed as the Second in each Pair"
|
||||
@param #operation : "The function to apply. Must have a signature of `$typeof(a) (a, b)`, where the type of 'a' and 'b' is the element type of left/right respectively."
|
||||
@param fill_with : "The value used to fill or pad the shorter iterable to the length of the longer one while zipping."
|
||||
|
||||
@require @is_valid_list(left) &&& @is_valid_list(right) : "Left and right sides must be integer indexable"
|
||||
@require @is_valid_operation(#operation, left, right) : "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 = EMPTY_MACRO_SLOT, fill_with = EMPTY_MACRO_SLOT) @nodiscard
|
||||
{
|
||||
var $LeftType = $typeof(left[0]);
|
||||
var $RightType = $typeof(right[0]);
|
||||
|
||||
var $Type = Pair { $LeftType, $RightType };
|
||||
bool $is_op = @is_valid_macro_slot(#operation);
|
||||
$if $is_op:
|
||||
$Type = $typeof(#operation).returns;
|
||||
$endif
|
||||
|
||||
usz left_len = find_len(left);
|
||||
usz right_len = find_len(right);
|
||||
|
||||
$LeftType left_fill;
|
||||
$RightType right_fill;
|
||||
usz result_len = min(left_len, right_len);
|
||||
bool $has_fill = @is_valid_macro_slot(fill_with);
|
||||
$if $has_fill:
|
||||
switch
|
||||
{
|
||||
case left_len > right_len:
|
||||
$if !$defined(($RightType)fill_with):
|
||||
unreachable();
|
||||
$else
|
||||
right_fill = ($RightType)fill_with;
|
||||
result_len = left_len;
|
||||
$endif
|
||||
case left_len < right_len:
|
||||
$if !$defined(($LeftType)fill_with):
|
||||
unreachable();
|
||||
$else
|
||||
left_fill = ($LeftType)fill_with;
|
||||
result_len = right_len;
|
||||
$endif
|
||||
}
|
||||
$endif
|
||||
|
||||
if (result_len == 0) return ($Type[]){};
|
||||
|
||||
$Type[] result = allocator::alloc_array(allocator, $Type, result_len);
|
||||
|
||||
foreach (idx, &item : result)
|
||||
{
|
||||
$if $is_op:
|
||||
var $LambdaType = $typeof(fn $Type ($LeftType a, $RightType b) => ($Type){});
|
||||
$LambdaType $operation = ($LambdaType)#operation;
|
||||
$LeftType lval = idx >= left_len ? left_fill : left[idx];
|
||||
$RightType rval = idx >= right_len ? right_fill : right[idx];
|
||||
*item = $operation(lval, rval);
|
||||
$else
|
||||
*item = {
|
||||
idx >= left_len ? left_fill : left[idx],
|
||||
idx >= right_len ? right_fill : right[idx]
|
||||
};
|
||||
$endif
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
<*
|
||||
Array 'zip' using the temp allocator.
|
||||
|
||||
@param [in] left : "The left-side array. These items will be placed as the First in each Pair"
|
||||
@param [in] right : "The right-side array. These items will be placed as the Second in each Pair"
|
||||
@param #operation : "The function to apply. Must have a signature of `$typeof(a) (a, b)`, where the type of 'a' and 'b' is the element type of left/right respectively."
|
||||
@param fill_with : "The value used to fill or pad the shorter iterable to the length of the longer one while zipping."
|
||||
|
||||
@require @is_valid_list(left) &&& @is_valid_list(right) : "Left and right sides must be integer indexable"
|
||||
@require @is_valid_operation(#operation, left, right) : "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 = EMPTY_MACRO_SLOT, fill_with = EMPTY_MACRO_SLOT) @nodiscard
|
||||
{
|
||||
return @zip(tmem, left, right, #operation, fill_with);
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Apply an operation to each element of two slices or arrays and store the results of
|
||||
each operation into the 'left' value.
|
||||
|
||||
This is useful because no memory allocations are required in order to perform the operation.
|
||||
|
||||
A good example of using this might be using algorithmic transformations on data in-place:
|
||||
```
|
||||
char[] partial_cipher = get_next_plaintext_block();
|
||||
|
||||
array::@zip_into(
|
||||
partial_cipher[ENCRYPT_OFFSET:BASE_KEY.len],
|
||||
BASE_KEY,
|
||||
fn char (char a, char b) => a ^ (b * 5) % 37
|
||||
);
|
||||
```
|
||||
|
||||
This parameterizes the lambda function with left (`partial_cipher`) and right (`BASE_KEY`) slice
|
||||
elements and stores the end result in-place within the left slice. This is in contrast to a
|
||||
regular `zip_with` which will create a cloned final result and return it.
|
||||
|
||||
@param [inout] left : `Slice to store results of applied functor/operation.`
|
||||
@param [in] right : `Slice to apply in the functor/operation.`
|
||||
@param #operation : "The function to apply. Must have a signature of `$typeof(a) (a, b)`, where the type of 'a' and 'b' is the element type of left/right respectively."
|
||||
|
||||
@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 @assignable_to(#operation, $typefrom(@zip_into_fn(left, right))) : "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)
|
||||
{
|
||||
$typefrom(@zip_into_fn(left, right)) $operation = #operation;
|
||||
|
||||
foreach (i, &v : left) *v = $operation(left[i], right[i]);
|
||||
}
|
||||
|
||||
|
||||
// --- helper functions
|
||||
module std::core::array @private;
|
||||
|
||||
|
||||
macro typeid @predicate_fn(#array) @const
|
||||
{
|
||||
return $typeof(fn bool ($typeof(#array[0]) a, usz index = 0) => true).typeid;
|
||||
}
|
||||
|
||||
macro typeid @reduce_fn(#array, #identity) @const
|
||||
{
|
||||
return @typeid(fn $typeof(#identity) ($typeof(#identity) i, $typeof(#array[0]) a, usz index = 0) => i);
|
||||
}
|
||||
|
||||
macro typeid @zip_into_fn(#left, #right) @const
|
||||
{
|
||||
return @typeid(fn $typeof(#left[0]) ($typeof(#left[0]) l, $typeof(#right[0]) r) => l);
|
||||
}
|
||||
|
||||
macro bool @is_valid_operation(#operation, #left, #right) @const
|
||||
{
|
||||
$switch:
|
||||
$case @is_empty_macro_slot(#operation):
|
||||
return true;
|
||||
$case @typekind(#operation) != FUNC:
|
||||
return false;
|
||||
$default:
|
||||
return $defined(#operation(#left[0], #right[0]));
|
||||
$endswitch
|
||||
}
|
||||
|
||||
macro bool @is_valid_list(#expr) @const
|
||||
{
|
||||
return $defined(#expr[0]) &&& ($defined(#expr.len) ||| $defined(#expr.len()));
|
||||
}
|
||||
|
||||
macro bool @is_valid_fill(left, right, fill_with)
|
||||
{
|
||||
if (@is_empty_macro_slot(fill_with)) return true;
|
||||
usz left_len = @select($defined(left.len()), left.len(), left.len);
|
||||
usz right_len = @select($defined(right.len()), right.len(), right.len);
|
||||
if (left_len == right_len) return true;
|
||||
return left_len > right_len ? $defined(($typeof(right[0]))fill_with) : $defined(($typeof(left[0]))fill_with);
|
||||
}
|
||||
|
||||
macro usz find_len(list)
|
||||
{
|
||||
$if $defined(list.len()):
|
||||
return list.len();
|
||||
$else
|
||||
return list.len;
|
||||
$endif
|
||||
}
|
||||
|
||||
@@ -112,3 +112,27 @@ const char[256] HEX_VALUE = {
|
||||
const char[256] TO_UPPER @private = { ['a'..'z'] = 'a' - 'A' };
|
||||
const char[256] TO_LOWER @private = { ['A'..'Z'] = 'a' - 'A' };
|
||||
|
||||
typedef AsciiCharset = uint128;
|
||||
|
||||
macro AsciiCharset @create_set(String $string) @const
|
||||
{
|
||||
AsciiCharset $set;
|
||||
$foreach $c : $string:
|
||||
$set |= 1ULL << $c;
|
||||
$endforeach
|
||||
return $set;
|
||||
}
|
||||
|
||||
fn AsciiCharset create_set(String string)
|
||||
{
|
||||
AsciiCharset set;
|
||||
foreach (c : string) set |= (AsciiCharset)1ULL << c;
|
||||
return set;
|
||||
}
|
||||
|
||||
macro bool AsciiCharset.contains(set, char c) => !!(c < 128) & !!(set & (AsciiCharset)(1ULL << c));
|
||||
|
||||
const AsciiCharset WHITESPACE_SET = @create_set("\t\n\v\f\r ");
|
||||
const AsciiCharset NUMBER_SET = @create_set("0123456789");
|
||||
|
||||
|
||||
|
||||
@@ -91,10 +91,10 @@ bitstruct UInt128LE : uint128 @littleendian
|
||||
@require @is_array_or_slice_of_char(bytes) : "argument must be an array, a pointer to an array or a slice of char"
|
||||
@require is_bitorder($Type) : "type must be a bitorder integer"
|
||||
*>
|
||||
macro read(bytes, $Type)
|
||||
macro read($Val bytes, $Type)
|
||||
{
|
||||
char[] s;
|
||||
$switch @typekind(bytes):
|
||||
$switch $Val.kindof:
|
||||
$case POINTER:
|
||||
s = (*bytes)[:$Type.sizeof];
|
||||
$default:
|
||||
|
||||
@@ -18,10 +18,10 @@ import libc, std::hash, std::io, std::os::backtrace;
|
||||
macro foo(a, #b = EMPTY_MACRO_SLOT)
|
||||
{
|
||||
$if @is_valid_macro_slot(#b):
|
||||
return invoke_foo2(a, #b);
|
||||
$else
|
||||
return invoke_foo1(a);
|
||||
$endif
|
||||
return invoke_foo2(a, #b);
|
||||
$else
|
||||
return invoke_foo1(a);
|
||||
$endif
|
||||
}
|
||||
*>
|
||||
const EmptySlot EMPTY_MACRO_SLOT @builtin = null;
|
||||
@@ -39,20 +39,28 @@ macro @is_valid_macro_slot(#arg) @const @builtin => !@typeis(#arg, EmptySlot);
|
||||
macro @rnd() @const @builtin => $$rnd();
|
||||
|
||||
/*
|
||||
Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds.
|
||||
Use `NO_MORE_ELEMENT` when reading the end of an iterator, or accessing a result out of bounds.
|
||||
*/
|
||||
faultdef NO_MORE_ELEMENT @builtin;
|
||||
|
||||
/*
|
||||
Use `SearchResult` when trying to return a value from some collection but the element is missing.
|
||||
Use `NOT_FOUND` when trying to return a value from some collection but the element is missing.
|
||||
*/
|
||||
faultdef NOT_FOUND @builtin;
|
||||
|
||||
/*
|
||||
Use `CastResult` when an attempt at conversion fails.
|
||||
Use `TYPE_MISMATCH` when an attempt at conversion fails.
|
||||
*/
|
||||
faultdef TYPE_MISMATCH @builtin;
|
||||
|
||||
/*
|
||||
Use `CAPACITY_EXCEEDED` when trying to add to a bounded list or similar.
|
||||
*/
|
||||
faultdef CAPACITY_EXCEEDED @builtin;
|
||||
/*
|
||||
Use `NOT_IMPLEMENTED` when something is conditionally available.
|
||||
*/
|
||||
faultdef NOT_IMPLEMENTED @builtin;
|
||||
|
||||
alias VoidFn = fn void();
|
||||
|
||||
@@ -81,6 +89,10 @@ macro void @swap(#a, #b) @builtin
|
||||
#b = temp;
|
||||
}
|
||||
|
||||
macro usz bitsizeof($Type) @builtin @const => $Type.sizeof * 8u;
|
||||
|
||||
macro usz @bitsizeof(#expr) @builtin @const => $sizeof(#expr) * 8u;
|
||||
|
||||
<*
|
||||
Convert an `any` type to a type, returning an failure if there is a type mismatch.
|
||||
|
||||
@@ -96,58 +108,80 @@ macro anycast(any v, $Type) @builtin
|
||||
return ($Type*)v.ptr;
|
||||
}
|
||||
|
||||
fn bool print_backtrace(String message, int backtraces_to_ignore) @if(env::NATIVE_STACKTRACE) => @stack_mem(0x1100; Allocator smem)
|
||||
macro bool @assignable_to(#foo, $Type) @const @builtin => $defined(*&&($Type){} = #foo);
|
||||
|
||||
macro @addr(#val) @builtin
|
||||
{
|
||||
$if $defined(&#val):
|
||||
return &#val;
|
||||
$else
|
||||
return &&#val;
|
||||
$endif
|
||||
}
|
||||
|
||||
macro typeid @typeid(#value) @const @builtin
|
||||
{
|
||||
return $typeof(#value).typeid;
|
||||
}
|
||||
|
||||
macro TypeKind @typekind(#value) @const @builtin
|
||||
{
|
||||
return $typeof(#value).kindof;
|
||||
}
|
||||
|
||||
macro bool @typeis(#value, $Type) @const @builtin
|
||||
{
|
||||
return $typeof(#value).typeid == $Type.typeid;
|
||||
}
|
||||
|
||||
|
||||
fn bool print_backtrace(String message, int backtraces_to_ignore) @if (env::NATIVE_STACKTRACE) => @stack_mem(0x1100; Allocator smem)
|
||||
{
|
||||
Allocator t = allocator::current_temp;
|
||||
TempAllocator* new_t = allocator::new_temp_allocator(smem, 0x1000)!!;
|
||||
allocator::current_temp = new_t;
|
||||
defer
|
||||
{
|
||||
allocator::current_temp = t;
|
||||
new_t.free();
|
||||
}
|
||||
void*[256] buffer;
|
||||
void*[] backtraces = backtrace::capture_current(&buffer);
|
||||
backtraces_to_ignore++;
|
||||
BacktraceList? backtrace = backtrace::symbolize_backtrace(tmem, backtraces);
|
||||
if (catch backtrace) return false;
|
||||
if (backtrace.len() <= backtraces_to_ignore) return false;
|
||||
io::eprint("\nERROR: '");
|
||||
io::eprint(message);
|
||||
io::eprintn("'");
|
||||
foreach (i, &trace : backtrace)
|
||||
@stack_mem(2048; Allocator mem)
|
||||
{
|
||||
if (i < backtraces_to_ignore) continue;
|
||||
String inline_suffix = trace.is_inline ? " [inline]" : "";
|
||||
if (trace.is_unknown())
|
||||
BacktraceList? backtrace = backtrace::symbolize_backtrace(mem, backtraces);
|
||||
if (catch backtrace) return false;
|
||||
if (backtrace.len() <= backtraces_to_ignore) return false;
|
||||
io::eprint("\nERROR: '");
|
||||
io::eprint(message);
|
||||
io::eprintn("'");
|
||||
foreach (i, &trace : backtrace)
|
||||
{
|
||||
io::eprintfn(" in ???%s", inline_suffix);
|
||||
continue;
|
||||
if (i < backtraces_to_ignore) continue;
|
||||
String inline_suffix = trace.is_inline ? " [inline]" : "";
|
||||
if (trace.is_unknown())
|
||||
{
|
||||
io::eprintfn(" in ???%s", inline_suffix);
|
||||
continue;
|
||||
}
|
||||
if (trace.has_file())
|
||||
{
|
||||
io::eprintfn(" in %s (%s:%d) [%s]%s", trace.function, trace.file, trace.line, trace.object_file, inline_suffix);
|
||||
continue;
|
||||
}
|
||||
io::eprintfn(" in %s (source unavailable) [%s]%s", trace.function, trace.object_file, inline_suffix);
|
||||
}
|
||||
if (trace.has_file())
|
||||
{
|
||||
io::eprintfn(" in %s (%s:%d) [%s]%s", trace.function, trace.file, trace.line, trace.object_file, inline_suffix);
|
||||
continue;
|
||||
}
|
||||
io::eprintfn(" in %s (source unavailable) [%s]%s", trace.function, trace.object_file, inline_suffix);
|
||||
}
|
||||
};
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
fn void default_panic(String message, String file, String function, uint line) @if(env::NATIVE_STACKTRACE)
|
||||
{
|
||||
$if $defined(io::stderr):
|
||||
if (!print_backtrace(message, 2))
|
||||
{
|
||||
io::eprintfn("\nERROR: '%s', in %s (%s:%d)", message, function, file, line);
|
||||
}
|
||||
$if $defined(io::stderr) && env::PANIC_MSG:
|
||||
if (!print_backtrace(message, 2))
|
||||
{
|
||||
io::eprintfn("\nERROR: '%s', in %s (%s:%d)", message, function, file, line);
|
||||
}
|
||||
$endif
|
||||
$$trap();
|
||||
|
||||
}
|
||||
|
||||
macro void abort(String string = "Unrecoverable error reached", ...) @builtin @noreturn
|
||||
macro void abort(String string = "Unrecoverable error reached", ...) @format(0) @builtin @noreturn
|
||||
{
|
||||
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat);
|
||||
$$trap();
|
||||
@@ -155,20 +189,22 @@ macro void abort(String string = "Unrecoverable error reached", ...) @builtin @n
|
||||
|
||||
bool in_panic @local = false;
|
||||
|
||||
fn void default_panic(String message, String file, String function, uint line) @if(!env::NATIVE_STACKTRACE)
|
||||
fn void default_panic(String message, String file, String function, uint line) @if (!env::NATIVE_STACKTRACE)
|
||||
{
|
||||
if (in_panic)
|
||||
{
|
||||
io::eprintn("Panic inside of panic.");
|
||||
return;
|
||||
}
|
||||
in_panic = true;
|
||||
$if $defined(io::stderr):
|
||||
io::eprint("\nERROR: '");
|
||||
io::eprint(message);
|
||||
io::eprintfn("', in %s (%s:%d)", function, file, line);
|
||||
$if $defined(io::stderr) && env::PANIC_MSG:
|
||||
if (in_panic)
|
||||
{
|
||||
io::eprintn("Panic inside of panic.");
|
||||
return;
|
||||
}
|
||||
in_panic = true;
|
||||
$if $defined(io::stderr):
|
||||
io::eprint("\nERROR: '");
|
||||
io::eprint(message);
|
||||
io::eprintfn("', in %s (%s:%d)", function, file, line);
|
||||
$endif
|
||||
in_panic = false;
|
||||
$endif
|
||||
in_panic = false;
|
||||
$$trap();
|
||||
}
|
||||
|
||||
@@ -178,21 +214,23 @@ PanicFn panic = &default_panic;
|
||||
|
||||
fn void panicf(String fmt, String file, String function, uint line, args...)
|
||||
{
|
||||
if (in_panic)
|
||||
{
|
||||
io::eprint("Panic inside of panic: ");
|
||||
io::eprintn(fmt);
|
||||
return;
|
||||
}
|
||||
in_panic = true;
|
||||
@stack_mem(512; Allocator allocator)
|
||||
{
|
||||
DString s;
|
||||
s.init(allocator);
|
||||
s.appendf(fmt, ...args);
|
||||
in_panic = false;
|
||||
panic(s.str_view(), file, function, line);
|
||||
};
|
||||
$if $defined(io::stderr) && env::PANIC_MSG:
|
||||
if (in_panic)
|
||||
{
|
||||
io::eprint("Panic inside of panic: ");
|
||||
io::eprintn(fmt);
|
||||
return;
|
||||
}
|
||||
in_panic = true;
|
||||
@stack_mem(512; Allocator allocator)
|
||||
{
|
||||
DString s;
|
||||
s.init(allocator);
|
||||
s.appendf(fmt, ...args);
|
||||
in_panic = false;
|
||||
panic(s.str_view(), file, function, line);
|
||||
};
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -281,7 +319,7 @@ macro enum_by_name($Type, String enum_name) @builtin
|
||||
@param $Type : `The type of the enum`
|
||||
@require $Type.kindof == ENUM : `Only enums may be used`
|
||||
@require $defined($Type.#value) : `Expected '#value' to match an enum associated value`
|
||||
@require $assignable(value, $typeof(($Type){}.#value)) : `Expected the value to match the type of the associated value`
|
||||
@require @assignable_to(value, $typeof(($Type){}.#value)) : `Expected the value to match the type of the associated value`
|
||||
@ensure @typeis(return, $Type)
|
||||
@return? NOT_FOUND
|
||||
*>
|
||||
@@ -334,7 +372,7 @@ macro bool @unlikely(bool #value, $probability = 1.0) @builtin
|
||||
|
||||
<*
|
||||
@require values::@is_int(#value) || values::@is_bool(#value)
|
||||
@require $assignable(expected, $typeof(#value))
|
||||
@require @assignable_to(expected, $typeof(#value))
|
||||
@require $probability >= 0 && $probability <= 1.0
|
||||
*>
|
||||
macro @expect(#value, expected, $probability = 1.0) @builtin
|
||||
@@ -384,6 +422,34 @@ macro swizzle2(v, v2, ...) @builtin
|
||||
{
|
||||
return $$swizzle2(v, v2, $vasplat);
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Returns the count of leading zero bits from an integer at compile-time.
|
||||
|
||||
@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
|
||||
{
|
||||
$if $value == 0:
|
||||
return $sizeof($value) * 8; // it's all leading zeroes
|
||||
$endif
|
||||
|
||||
usz $n = 0;
|
||||
uint128 $x = (uint128)$value;
|
||||
|
||||
$if $x <= 0x0000_0000_0000_0000_FFFF_FFFF_FFFF_FFFF: $n += 64; $x <<= 64; $endif
|
||||
$if $x <= 0x0000_0000_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF: $n += 32; $x <<= 32; $endif
|
||||
$if $x <= 0x0000_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF: $n += 16; $x <<= 16; $endif
|
||||
$if $x <= 0x00FF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF: $n += 8; $x <<= 8; $endif
|
||||
$if $x <= 0x0FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF: $n += 4; $x <<= 4; $endif
|
||||
$if $x <= 0x3FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF: $n += 2; $x <<= 2; $endif
|
||||
$if $x <= 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF: $n += 1; $endif
|
||||
|
||||
return $n % ($sizeof($value) * 8); // mod by the bitsize of the input value to go back from uint128 -> it's-type
|
||||
}
|
||||
|
||||
<*
|
||||
Return the excuse in the Optional if it is Empty, otherwise
|
||||
return a null fault.
|
||||
@@ -408,6 +474,67 @@ macro bool @ok(#expr) @builtin
|
||||
return true;
|
||||
}
|
||||
|
||||
<*
|
||||
Check if an Optional expression evaluates to a fault. If so, return it;
|
||||
else, assign the result to an expression.
|
||||
|
||||
@require $defined(#v = #v) : "#v must be a variable"
|
||||
@require $defined(#expr!) : "Expected an optional expression"
|
||||
@require @assignable_to(#expr!!, $typeof(#v)) : `Type of #expr must be an optional of #v's type`
|
||||
*>
|
||||
macro void? @try(#v, #expr) @builtin @maydiscard
|
||||
{
|
||||
var res = #expr;
|
||||
if (catch err = res) return err?;
|
||||
#v = res;
|
||||
}
|
||||
|
||||
<*
|
||||
Check if an Optional expression evaluates to a fault. If so, return true if it is the
|
||||
expected fault, the optional if it is unexpected, or false if there was no fault and
|
||||
the assign happened.
|
||||
|
||||
This can be used in like this:
|
||||
|
||||
while (true)
|
||||
{
|
||||
char[] data;
|
||||
// Read until end of file
|
||||
if (@try_catch(data, load_line(), io::EOF)) break;
|
||||
.. use data ..
|
||||
}
|
||||
|
||||
In this example we read until we reach an EOF, which is expected. However, if we encounter some other
|
||||
fault, we rethrow is. Without this macro, the code is instead written like:
|
||||
|
||||
while (true)
|
||||
{
|
||||
char[]? data;
|
||||
data = load_line();
|
||||
if (catch err = data)
|
||||
{
|
||||
if (err = io::EOF) break;
|
||||
return err?
|
||||
}
|
||||
.. use data ..
|
||||
}
|
||||
|
||||
@require $defined(#v = #v) : "#v must be a variable"
|
||||
@require $defined(#expr!) : "Expected an optional expression"
|
||||
@require @assignable_to(#expr!!, $typeof(#v)) : `Type of #expr must be an optional of #v's type`
|
||||
@return "True if it was the expected fault, false if the variable was assigned, otherwise returns an optional."
|
||||
*>
|
||||
macro bool? @try_catch(#v, #expr, fault expected_fault) @builtin
|
||||
{
|
||||
var res = #expr;
|
||||
if (catch err = res)
|
||||
{
|
||||
return err == expected_fault ? true : err?;
|
||||
}
|
||||
#v = res;
|
||||
return false;
|
||||
}
|
||||
|
||||
<*
|
||||
@require $defined(&#value, (char*)&#value) : "This must be a value that can be viewed as a char array"
|
||||
*>
|
||||
@@ -477,8 +604,8 @@ macro uint ichar[<*>].hash(self) => hash_vec(self);
|
||||
macro uint bool[<*>].hash(self) => hash_vec(self);
|
||||
|
||||
macro uint typeid.hash(typeid t) => @generic_hash(((ulong)(uptr)t));
|
||||
macro uint String.hash(String c) => (uint)fnv32a::hash(c);
|
||||
macro uint char[].hash(char[] c) => (uint)fnv32a::hash(c);
|
||||
macro uint String.hash(String c) => (uint)a5hash::hash(c);
|
||||
macro uint char[].hash(char[] c) => (uint)a5hash::hash(c);
|
||||
macro uint void*.hash(void* ptr) => @generic_hash(((ulong)(uptr)ptr));
|
||||
|
||||
<*
|
||||
@@ -486,15 +613,27 @@ macro uint void*.hash(void* ptr) => @generic_hash(((ulong)(uptr)ptr));
|
||||
*>
|
||||
macro uint hash_array(array_ptr) @local
|
||||
{
|
||||
return (uint)fnv32a::hash(((char*)array_ptr)[:$sizeof(*array_ptr)]);
|
||||
var $len = $sizeof(*array_ptr);
|
||||
|
||||
$if $len > 16:
|
||||
return (uint)komi::hash(((char*)array_ptr)[:$len]);
|
||||
$else
|
||||
return (uint)wyhash2::hash(((char*)array_ptr)[:$len]);
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@require @typekind(vec) == VECTOR
|
||||
@require $Vec.typeof == VECTOR
|
||||
*>
|
||||
macro uint hash_vec(vec) @local
|
||||
macro uint hash_vec($Vec vec) @local
|
||||
{
|
||||
return (uint)fnv32a::hash(((char*)&&vec)[:$sizeof(vec.len * $typeof(vec).inner.sizeof)]);
|
||||
var $len = $sizeof(vec.len * $Vec.inner.sizeof);
|
||||
|
||||
$if $len > 16:
|
||||
return (uint)komi::hash(((char*)&&vec)[:$len]);
|
||||
$else
|
||||
return (uint)wyhash2::hash(((char*)&&vec)[:$len]);
|
||||
$endif
|
||||
}
|
||||
|
||||
const MAX_FRAMEADDRESS = 128;
|
||||
|
||||
@@ -126,3 +126,36 @@ macro max(x, ...) @builtin
|
||||
$endif
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
@require types::is_numerical($typeof($a))
|
||||
*>
|
||||
macro @max($a, ...) @builtin @const
|
||||
{
|
||||
$if $vacount == 1:
|
||||
return $a > $vaconst[0] ? $a : $vaconst[0];
|
||||
$else
|
||||
var $result = $a;
|
||||
$for var $x = 0; $x < $vacount; ++$x:
|
||||
$if $vaconst[$x] > $result: $result = $vaconst[$x]; $endif
|
||||
$endfor
|
||||
return $result;
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@require types::is_numerical($typeof($a))
|
||||
*>
|
||||
macro @min($a, ...) @builtin @const
|
||||
{
|
||||
$if $vacount == 1:
|
||||
return $a < $vaconst[0] ? $a : $vaconst[0];
|
||||
$else
|
||||
var $result = $a;
|
||||
$for var $x = 0; $x < $vacount; ++$x:
|
||||
$if $vaconst[$x] < $result: $result = $vaconst[$x]; $endif
|
||||
$endfor
|
||||
return $result;
|
||||
$endif
|
||||
}
|
||||
|
||||
|
||||
@@ -137,12 +137,13 @@ const bool BACKTRACE = $$BACKTRACE;
|
||||
const usz LLVM_VERSION = $$LLVM_VERSION;
|
||||
const bool BENCHMARKING = $$BENCHMARKING;
|
||||
const bool TESTING = $$TESTING;
|
||||
const bool PANIC_MSG = $$PANIC_MSG;
|
||||
const MemoryEnvironment MEMORY_ENV = MemoryEnvironment.from_ordinal($$MEMORY_ENVIRONMENT);
|
||||
const bool TRACK_MEMORY = DEBUG_SYMBOLS && (COMPILER_SAFE_MODE || TESTING);
|
||||
const bool X86_64 = ARCH_TYPE == X86_64;
|
||||
const bool X86 = ARCH_TYPE == X86;
|
||||
const bool AARCH64 = ARCH_TYPE == AARCH64;
|
||||
const bool NATIVE_STACKTRACE = LINUX || DARWIN || WIN32;
|
||||
const bool NATIVE_STACKTRACE = LINUX || DARWIN || OPENBSD || WIN32;
|
||||
const bool LINUX = LIBC && OS_TYPE == LINUX;
|
||||
const bool DARWIN = LIBC && os_is_darwin();
|
||||
const bool WIN32 = LIBC && OS_TYPE == WIN32;
|
||||
@@ -153,13 +154,18 @@ const bool NETBSD = LIBC && OS_TYPE == NETBSD;
|
||||
const bool BSD_FAMILY = env::FREEBSD || env::OPENBSD || env::NETBSD;
|
||||
const bool WASI = LIBC && OS_TYPE == WASI;
|
||||
const bool ANDROID = LIBC && OS_TYPE == ANDROID;
|
||||
const bool WASM_NOLIBC @builtin = !LIBC && ARCH_TYPE == ArchType.WASM32 || ARCH_TYPE == ArchType.WASM64;
|
||||
const bool WASM_NOLIBC @builtin @deprecated("Use 'FREESTANDING_WASM' instead") = !LIBC && ARCH_TYPE == ArchType.WASM32 || ARCH_TYPE == ArchType.WASM64;
|
||||
const bool FREESTANDING_PE32 = NO_LIBC && OS_TYPE == WIN32;
|
||||
const bool FREESTANDING_MACHO = NO_LIBC && OS_TYPE == MACOS;
|
||||
const bool FREESTANDING_ELF = NO_LIBC && !env::FREESTANDING_PE32 && !env::FREESTANDING_MACHO && !env::FREESTANDING_WASM;
|
||||
const bool FREESTANDING_WASM = NO_LIBC && (ARCH_TYPE == ArchType.WASM32 || ARCH_TYPE == ArchType.WASM64);
|
||||
const bool FREESTANDING = env::FREESTANDING_PE32 || env::FREESTANDING_MACHO || env::FREESTANDING_ELF || env::FREESTANDING_WASM;
|
||||
const bool ADDRESS_SANITIZER = $$ADDRESS_SANITIZER;
|
||||
const bool MEMORY_SANITIZER = $$MEMORY_SANITIZER;
|
||||
const bool THREAD_SANITIZER = $$THREAD_SANITIZER;
|
||||
const bool ANY_SANITIZER = ADDRESS_SANITIZER || MEMORY_SANITIZER || THREAD_SANITIZER;
|
||||
const int LANGUAGE_DEV_VERSION = $$LANGUAGE_DEV_VERSION;
|
||||
const bool HAS_NATIVE_ERRNO = env::LINUX || env::ANDROID || env::DARWIN || env::WIN32;
|
||||
const bool HAS_NATIVE_ERRNO = env::LINUX || env::ANDROID || env::OPENBSD || env::DARWIN || env::WIN32;
|
||||
|
||||
macro bool os_is_darwin() @const
|
||||
{
|
||||
@@ -198,6 +204,7 @@ macro bool os_is_posix() @const
|
||||
return false;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
const String[] AUTHORS = $$AUTHORS;
|
||||
const String[] AUTHOR_EMAILS = $$AUTHOR_EMAILS;
|
||||
const BUILTIN_EXPECT_IS_DISABLED = $feature(DISABLE_BUILTIN_EXPECT);
|
||||
const BUILTIN_PREFETCH_IS_DISABLED = $feature(DISABLE_BUILTIN_PREFETCH);
|
||||
|
||||
230
lib/std/core/logging.c3
Normal file
230
lib/std/core/logging.c3
Normal file
@@ -0,0 +1,230 @@
|
||||
module std::core::log;
|
||||
import std::io, std::thread, std::time, std::math::random;
|
||||
|
||||
const FULL_LOG = env::COMPILER_SAFE_MODE || $feature(FULL_LOG);
|
||||
|
||||
typedef LogCategory = inline char;
|
||||
typedef LogTag = char[12];
|
||||
|
||||
const LogCategory CATEGORY_APPLICATION = 0;
|
||||
const LogCategory CATEGORY_SYSTEM = 1;
|
||||
const LogCategory CATEGORY_KERNEL = 2;
|
||||
const LogCategory CATEGORY_AUDIO = 3;
|
||||
const LogCategory CATEGORY_VIDEO = 4;
|
||||
const LogCategory CATEGORY_RENDER = 5;
|
||||
const LogCategory CATEGORY_INPUT = 6;
|
||||
const LogCategory CATEGORY_NETWORK = 7;
|
||||
const LogCategory CATEGORY_SOCKET = 8;
|
||||
const LogCategory CATEGORY_SECURITY = 9;
|
||||
const LogCategory CATEGORY_TEST = 10;
|
||||
const LogCategory CATEGORY_ERROR = 11;
|
||||
const LogCategory CATEGORY_ASSERT = 12;
|
||||
const LogCategory CATEGORY_CRASH = 13;
|
||||
const LogCategory CATEGORY_STATS = 14;
|
||||
const LogCategory CATEGORY_CUSTOM_START = 100;
|
||||
|
||||
tlocal LogCategory default_category = CATEGORY_APPLICATION;
|
||||
tlocal LogTag current_tag;
|
||||
|
||||
|
||||
enum LogPriority : int
|
||||
{
|
||||
VERBOSE,
|
||||
DEBUG,
|
||||
INFO,
|
||||
WARN,
|
||||
ERROR,
|
||||
CRITICAL,
|
||||
}
|
||||
|
||||
interface Logger
|
||||
{
|
||||
fn void log(LogPriority priority, LogCategory category, LogTag tag, String file, String function, int line, String fmt, any[] args);
|
||||
}
|
||||
|
||||
macro void verbose(String fmt, ..., LogCategory category = default_category) => call_log(VERBOSE, category, fmt, $vasplat);
|
||||
macro void debug(String fmt, ..., LogCategory category = default_category) => call_log(DEBUG, category, fmt, $vasplat);
|
||||
macro void info(String fmt, ..., LogCategory category = default_category) => call_log(INFO, category, fmt, $vasplat);
|
||||
macro void warn(String fmt, ..., LogCategory category = default_category) => call_log(WARN, category, fmt, $vasplat);
|
||||
macro void error(String fmt, ..., LogCategory category = default_category) => call_log(ERROR, category, fmt, $vasplat);
|
||||
macro void critical(String fmt, ..., LogCategory category = default_category) => call_log(CRITICAL, category, fmt, $vasplat);
|
||||
|
||||
macro void @category_scope(LogCategory new_category; @body)
|
||||
{
|
||||
LogCategory old = default_category;
|
||||
default_category = new_category;
|
||||
defer default_category = old;
|
||||
@body();
|
||||
}
|
||||
|
||||
<*
|
||||
@require tag_prefix.len <= 3 : "The prefix may not exceed 3 bytes"
|
||||
*>
|
||||
macro void @tag_scope(String tag_prefix = ""; @body)
|
||||
{
|
||||
LogTag old = current_tag;
|
||||
push_tag(tag_prefix);
|
||||
defer current_tag = old;
|
||||
@body();
|
||||
}
|
||||
|
||||
<*
|
||||
@require tag_prefix.len <= 3 : "The prefix may not exceed 3 bytes"
|
||||
*>
|
||||
macro void push_tag(String tag_prefix = "")
|
||||
{
|
||||
current_tag = create_tag(tag_prefix);
|
||||
}
|
||||
|
||||
<*
|
||||
@require tag_prefix.len <= 3 : "The prefix may not exceed 3 bytes"
|
||||
*>
|
||||
fn LogTag create_tag(String tag_prefix)
|
||||
{
|
||||
LogTag tag @noinit;
|
||||
int start = 0;
|
||||
foreach (int i, c : tag_prefix)
|
||||
{
|
||||
if (c == 0) break;
|
||||
tag[start++] = c;
|
||||
}
|
||||
if (start > 0) tag[start++] = '_';
|
||||
for (int i = start; i < tag.len; i++)
|
||||
{
|
||||
tag[i] = (char)rand_in_range('a', 'z');
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
fn void set_priority_for_category(LogCategory category, LogPriority new_priority)
|
||||
{
|
||||
@atomic_store(config_priorities[category], new_priority, UNORDERED);
|
||||
}
|
||||
|
||||
fn LogPriority get_priority_for_category(LogCategory category)
|
||||
{
|
||||
return @atomic_load(config_priorities[category], UNORDERED);
|
||||
}
|
||||
|
||||
fn void set_priority_all(LogPriority new_priority)
|
||||
{
|
||||
for (int i = 0; i < config_priorities.len; i++)
|
||||
{
|
||||
@atomic_store(config_priorities[i], new_priority, UNORDERED);
|
||||
}
|
||||
}
|
||||
fn void set_logger(Logger logger)
|
||||
{
|
||||
init();
|
||||
if (!logger_mutex.is_initialized())
|
||||
{
|
||||
current_logger = logger;
|
||||
current_logfn = &logger.log;
|
||||
return;
|
||||
}
|
||||
logger_mutex.@in_lock()
|
||||
{
|
||||
current_logger = logger;
|
||||
current_logfn = &logger.log;
|
||||
};
|
||||
}
|
||||
|
||||
macro void init()
|
||||
{
|
||||
log_init.call(fn () => (void)logger_mutex.init());
|
||||
}
|
||||
|
||||
fn void call_log(LogPriority prio, LogCategory category, String fmt, args...)
|
||||
{
|
||||
LogPriority priority = mem::@atomic_load(config_priorities[category], UNORDERED);
|
||||
if (priority < prio) return;
|
||||
init();
|
||||
bool locked = logger_mutex.is_initialized() && @ok(logger_mutex.lock());
|
||||
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
|
||||
}
|
||||
|
||||
fn String? get_category_name(LogCategory category)
|
||||
{
|
||||
String val = category_names[category];
|
||||
return val ?: NOT_FOUND?;
|
||||
}
|
||||
|
||||
fn void set_category_name(LogCategory category, String name)
|
||||
{
|
||||
category_names[category] = name;
|
||||
}
|
||||
|
||||
struct NullLogger (Logger)
|
||||
{
|
||||
void* dummy;
|
||||
}
|
||||
|
||||
fn void NullLogger.log(&self, LogPriority priority, LogCategory category, LogTag tag, String file, String function, int line, String fmt, any[] args) @dynamic
|
||||
{}
|
||||
|
||||
struct MultiLogger (Logger)
|
||||
{
|
||||
Logger[] loggers;
|
||||
}
|
||||
|
||||
fn void MultiLogger.log(&self, LogPriority priority, LogCategory category, LogTag tag, String file, String function, int line, String fmt, any[] args) @dynamic
|
||||
{
|
||||
foreach (logger : self.loggers)
|
||||
{
|
||||
logger.log(priority, category, tag, file, function, line, fmt, args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module std::core::log @private;
|
||||
import std::io, std::thread, std::time;
|
||||
|
||||
struct StderrLogger (Logger) @if(env::LIBC)
|
||||
{
|
||||
void* dummy;
|
||||
}
|
||||
|
||||
fn void StderrLogger.log(&self, LogPriority priority, LogCategory category, LogTag tag, String file, String function, int line, String fmt, any[] args) @dynamic @if(env::LIBC)
|
||||
{
|
||||
@stack_mem(256 + 64; Allocator mem)
|
||||
{
|
||||
DString str;
|
||||
str.init(mem, 256);
|
||||
str.appendf(fmt, ...args);
|
||||
TzDateTime time = datetime::now().to_local();
|
||||
io::eprintfn("[%02d:%02d:%02d:%04d] [%s] %s", time.hour, time.min, time.sec, (time.usec / 1000), priority, str);
|
||||
};
|
||||
}
|
||||
|
||||
alias LogFn = fn void(void*, LogPriority priority, LogCategory category, LogTag tag, String file, String function, int line, String fmt, any[] args);
|
||||
LogFn current_logfn = @select(env::LIBC, (LogFn)&StderrLogger.log, (LogFn)&NullLogger.log);
|
||||
OnceFlag log_init;
|
||||
Mutex logger_mutex;
|
||||
Logger current_logger = @select(env::LIBC, &stderr_logger, &null_logger);
|
||||
StderrLogger stderr_logger @if (env::LIBC);
|
||||
NullLogger null_logger;
|
||||
LogPriority[256] config_priorities = { [0..255] = ERROR, [CATEGORY_APPLICATION] = INFO, [CATEGORY_TEST] = VERBOSE, [CATEGORY_ASSERT] = WARN};
|
||||
String[256] category_names = {
|
||||
[CATEGORY_APPLICATION] = "APP",
|
||||
[CATEGORY_SYSTEM] = "SYSTEM",
|
||||
[CATEGORY_KERNEL] = "KERNEL",
|
||||
[CATEGORY_AUDIO] = "AUDIO",
|
||||
[CATEGORY_VIDEO] = "VIDEO",
|
||||
[CATEGORY_RENDER] = "RENDER",
|
||||
[CATEGORY_INPUT] = "INPUT",
|
||||
[CATEGORY_NETWORK] = "NETWORD",
|
||||
[CATEGORY_SOCKET] = "SOCKET",
|
||||
[CATEGORY_SECURITY] = "SECURITY",
|
||||
[CATEGORY_TEST] = "TEST",
|
||||
[CATEGORY_ERROR] = "ERROR",
|
||||
[CATEGORY_ASSERT] = "ASSERT",
|
||||
[CATEGORY_CRASH] = "CRASH",
|
||||
[CATEGORY_STATS] = "STATS"
|
||||
};
|
||||
@@ -3,10 +3,15 @@
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::core::mem;
|
||||
import std::core::mem::allocator @public;
|
||||
import std::os::posix, std::os::win32;
|
||||
import std::math;
|
||||
|
||||
const MAX_MEMORY_ALIGNMENT = 0x1000_0000;
|
||||
const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2;
|
||||
const ulong KB = 1024;
|
||||
const ulong MB = KB * 1024;
|
||||
const ulong GB = MB * 1024;
|
||||
const ulong TB = GB * 1024;
|
||||
|
||||
faultdef OUT_OF_MEMORY, INVALID_ALLOC_SIZE;
|
||||
|
||||
@@ -15,13 +20,31 @@ macro bool @constant_is_power_of_2($x) @const @private
|
||||
return $x != 0 && ($x & ($x - 1)) == 0;
|
||||
}
|
||||
|
||||
fn usz os_pagesize()
|
||||
{
|
||||
$switch:
|
||||
$case env::POSIX:
|
||||
static usz pagesize;
|
||||
if (pagesize) return pagesize;
|
||||
return pagesize = posix::getpagesize();
|
||||
$case env::WIN32:
|
||||
static usz pagesize;
|
||||
if (pagesize) return pagesize;
|
||||
Win32_SYSTEM_INFO info;
|
||||
win32::getSystemInfo(&info);
|
||||
return pagesize = info.dwPageSize;
|
||||
$default:
|
||||
return 4096;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
<*
|
||||
Load a vector from memory according to a mask assuming default alignment.
|
||||
|
||||
@param ptr : "The pointer address to load from."
|
||||
@param mask : "The mask for the load"
|
||||
@param passthru : "The value to use for non masked values"
|
||||
@require $assignable(&&passthru, $typeof(ptr)) : "Pointer and passthru must match"
|
||||
@require $defined(*ptr = passthru) : "Pointer and passthru must match"
|
||||
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
|
||||
@require passthru.len == mask.len : "Mask and passthru must have the same length"
|
||||
|
||||
@@ -40,7 +63,7 @@ macro masked_load(ptr, bool[<*>] mask, passthru)
|
||||
@param passthru : "The value to use for non masked values"
|
||||
@param $alignment : "The alignment to assume for the pointer"
|
||||
|
||||
@require $assignable(&&passthru, $typeof(ptr)) : "Pointer and passthru must match"
|
||||
@require $defined(*ptr = passthru) : "Pointer and passthru must match"
|
||||
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
|
||||
@require passthru.len == mask.len : "Mask and passthru must have the same length"
|
||||
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||
@@ -61,7 +84,7 @@ macro @masked_load_aligned(ptr, bool[<*>] mask, passthru, usz $alignment)
|
||||
|
||||
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
|
||||
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
|
||||
@require $assignable(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match"
|
||||
@require $defined(*ptrvec[0] = passthru[0]) : "Pointer and passthru must match"
|
||||
@require passthru.len == mask.len : "Mask and passthru must have the same length"
|
||||
@require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
|
||||
|
||||
@@ -83,7 +106,7 @@ macro gather(ptrvec, bool[<*>] mask, passthru)
|
||||
|
||||
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
|
||||
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
|
||||
@require $assignable(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match"
|
||||
@require $defined(*ptrvec[0] = passthru[0]) : "Pointer and passthru must match"
|
||||
@require passthru.len == mask.len : "Mask and passthru must have the same length"
|
||||
@require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
|
||||
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||
@@ -103,7 +126,7 @@ macro @gather_aligned(ptrvec, bool[<*>] mask, passthru, usz $alignment)
|
||||
@param value : "The value to store masked"
|
||||
@param mask : "The mask for the store"
|
||||
|
||||
@require $assignable(&&value, $typeof(ptr)) : "Pointer and value must match"
|
||||
@require $defined(*ptr = value) : "Pointer and value must match"
|
||||
@require @typekind(value) == VECTOR : "Expected value to be a vector"
|
||||
@require value.len == mask.len : "Mask and value must have the same length"
|
||||
*>
|
||||
@@ -118,7 +141,7 @@ macro masked_store(ptr, value, bool[<*>] mask)
|
||||
@param mask : "The mask for the store"
|
||||
@param $alignment : "The alignment of the pointer"
|
||||
|
||||
@require $assignable(&&value, $typeof(ptr)) : "Pointer and value must match"
|
||||
@require $defined(*ptr = value) : "Pointer and value must match"
|
||||
@require @typekind(value) == VECTOR : "Expected value to be a vector"
|
||||
@require value.len == mask.len : "Mask and value must have the same length"
|
||||
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||
@@ -135,7 +158,7 @@ macro @masked_store_aligned(ptr, value, bool[<*>] mask, usz $alignment)
|
||||
@param mask : "The mask for the store"
|
||||
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
|
||||
@require @typekind(value) == VECTOR : "Expected value to be a vector"
|
||||
@require $assignable(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match"
|
||||
@require $defined(*ptrvec[0] = value[0]) : "Pointer and value must match"
|
||||
@require value.len == mask.len : "Mask and value must have the same length"
|
||||
@require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
|
||||
|
||||
@@ -153,7 +176,7 @@ macro scatter(ptrvec, value, bool[<*>] mask)
|
||||
|
||||
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
|
||||
@require @typekind(value) == VECTOR : "Expected value to be a vector"
|
||||
@require $assignable(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match"
|
||||
@require $defined(*ptrvec[0] = value[0]) : "Pointer and value must match"
|
||||
@require value.len == mask.len : "Mask and value must have the same length"
|
||||
@require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
|
||||
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||
@@ -282,7 +305,7 @@ macro compare_exchange_volatile(ptr, compare, value, AtomicOrdering $success = S
|
||||
*>
|
||||
fn usz aligned_offset(usz offset, usz alignment)
|
||||
{
|
||||
return alignment * ((offset + alignment - 1) / alignment);
|
||||
return (offset + alignment - 1) & ~(alignment - 1);
|
||||
}
|
||||
|
||||
macro void* aligned_pointer(void* ptr, usz alignment)
|
||||
@@ -298,6 +321,11 @@ fn bool ptr_is_aligned(void* ptr, usz alignment) @inline
|
||||
return (uptr)ptr & ((uptr)alignment - 1) == 0;
|
||||
}
|
||||
|
||||
fn bool ptr_is_page_aligned(void* ptr) @inline
|
||||
{
|
||||
return (uptr)ptr & ((uptr)os_pagesize() - 1) == 0;
|
||||
}
|
||||
|
||||
macro void zero_volatile(char[] data)
|
||||
{
|
||||
$$memset(data.ptr, (char)0, data.len, true, (usz)1);
|
||||
@@ -618,7 +646,7 @@ macro void @pool(usz reserve = 0; @body) @builtin
|
||||
@body();
|
||||
}
|
||||
|
||||
module std::core::mem @if(WASM_NOLIBC);
|
||||
module std::core::mem @if(env::FREESTANDING_WASM);
|
||||
import std::core::mem::allocator @public;
|
||||
SimpleHeapAllocator wasm_allocator @private;
|
||||
extern int __heap_base;
|
||||
@@ -656,6 +684,12 @@ macro @clone(value) @builtin @nodiscard
|
||||
return allocator::clone(mem, value);
|
||||
}
|
||||
|
||||
<*
|
||||
@param value : "The value to clone"
|
||||
@return "A pointer to the cloned value"
|
||||
*>
|
||||
macro @clone_slice(value) @builtin @nodiscard => allocator::clone_slice(mem, value);
|
||||
|
||||
<*
|
||||
@param value : "The value to clone"
|
||||
@return "A pointer to the cloned value, which must be released using free_aligned"
|
||||
@@ -678,6 +712,12 @@ macro @tclone(value) @builtin @nodiscard
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@param value : "The value to clone"
|
||||
@return "A pointer to the cloned value"
|
||||
*>
|
||||
macro @tclone_slice(value) @builtin @nodiscard => allocator::clone_slice(tmem, value);
|
||||
|
||||
fn void* malloc(usz size) @builtin @inline @nodiscard
|
||||
{
|
||||
return allocator::malloc(mem, size);
|
||||
@@ -700,7 +740,7 @@ fn void* tmalloc(usz size, usz alignment = 0) @builtin @inline @nodiscard
|
||||
|
||||
<*
|
||||
@require $vacount < 2 : "Too many arguments."
|
||||
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
@require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
|
||||
*>
|
||||
macro new($Type, ...) @nodiscard
|
||||
@@ -716,7 +756,7 @@ macro new($Type, ...) @nodiscard
|
||||
|
||||
<*
|
||||
@require $vacount < 2 : "Too many arguments."
|
||||
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
@require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
|
||||
*>
|
||||
macro new_with_padding($Type, usz padding, ...) @nodiscard
|
||||
@@ -734,7 +774,7 @@ macro new_with_padding($Type, usz padding, ...) @nodiscard
|
||||
Allocate using an aligned allocation. This is necessary for types with a default memory alignment
|
||||
exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
|
||||
@require $vacount < 2 : "Too many arguments."
|
||||
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
@require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
*>
|
||||
macro new_aligned($Type, ...) @nodiscard
|
||||
{
|
||||
@@ -774,7 +814,7 @@ macro alloc_aligned($Type) @nodiscard
|
||||
|
||||
<*
|
||||
@require $vacount < 2 : "Too many arguments."
|
||||
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
@require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
*>
|
||||
macro tnew($Type, ...) @nodiscard
|
||||
{
|
||||
@@ -789,7 +829,7 @@ macro tnew($Type, ...) @nodiscard
|
||||
|
||||
<*
|
||||
@require $vacount < 2 : "Too many arguments."
|
||||
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
@require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
*>
|
||||
macro temp_with_padding($Type, usz padding, ...) @nodiscard
|
||||
{
|
||||
@@ -903,6 +943,18 @@ fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMEN
|
||||
return tmem.resize(ptr, size, alignment)!!;
|
||||
}
|
||||
|
||||
<*
|
||||
Takes the address of a possibly unaligned variable or member,
|
||||
and offers safe access to that member, by constructing an UnalignedRef.
|
||||
|
||||
@require $defined(&#arg) : "It must be possible to take the address of the argument."
|
||||
@return "An 'UnalignedRef' with the proper type and alignment, with a pointer to argument"
|
||||
*>
|
||||
macro @unaligned_addr(#arg) @builtin
|
||||
{
|
||||
return (UnalignedRef{$typeof(#arg), $alignof(#arg)})&#arg;
|
||||
}
|
||||
|
||||
module std::core::mem @if(env::NO_LIBC);
|
||||
|
||||
fn CInt __memcmp(void* s1, void* s2, usz n) @weak @export("memcmp")
|
||||
@@ -940,3 +992,39 @@ fn void* __memcpy(void* dst, void* src, usz n) @weak @export("memcpy")
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
module std::core::mem::volatile { Type };
|
||||
|
||||
typedef Volatile @structlike = Type;
|
||||
|
||||
macro Type Volatile.get(&self)
|
||||
{
|
||||
return @volatile_load(*(Type*)self);
|
||||
}
|
||||
|
||||
macro Type Volatile.set(&self, Type val)
|
||||
{
|
||||
return @volatile_store(*(Type*)self, val);
|
||||
}
|
||||
|
||||
<*
|
||||
@require mem::@constant_is_power_of_2(ALIGNMENT) : "The alignment must be a power of 2"
|
||||
*>
|
||||
module std::core::mem::alignment { Type, ALIGNMENT };
|
||||
import std::core::mem @public;
|
||||
|
||||
<*
|
||||
An UnalignedRef offers correctly aligned access to addresses that may be unaligned or overaligned.
|
||||
*>
|
||||
typedef UnalignedRef = Type*;
|
||||
|
||||
macro Type UnalignedRef.get(self)
|
||||
{
|
||||
return @unaligned_load(*(Type*)self, ALIGNMENT);
|
||||
}
|
||||
|
||||
macro Type UnalignedRef.set(&self, Type val)
|
||||
{
|
||||
return @unaligned_store(*(Type*)self, val, ALIGNMENT);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module std::core::mem::allocator;
|
||||
import std::math;
|
||||
|
||||
// C3 has multiple different allocators available:
|
||||
// C3 has several different allocators available:
|
||||
//
|
||||
// Name Arena Uses buffer OOM Fallback? Mark? Reset?
|
||||
// ArenaAllocator Yes Yes No Yes Yes
|
||||
@@ -12,6 +12,7 @@ import std::math;
|
||||
// OnStackAllocator Yes Yes Yes No No *Note: Used by @stack_mem
|
||||
// TempAllocator Yes No Yes No* No* *Note: Mark/reset using @pool
|
||||
// TrackingAllocator No No N/A No No *Note: Wraps other heap allocator
|
||||
// Vmem Yes No No Yes Yes *Note: Can be set to huge sizes
|
||||
|
||||
const DEFAULT_SIZE_PREFIX = usz.sizeof;
|
||||
const DEFAULT_SIZE_PREFIX_ALIGNMENT = usz.alignof;
|
||||
@@ -64,7 +65,7 @@ alias MemoryAllocFn = fn char[]?(usz);
|
||||
|
||||
|
||||
|
||||
fn usz alignment_for_allocation(usz alignment) @inline @private
|
||||
fn usz alignment_for_allocation(usz alignment) @inline
|
||||
{
|
||||
return alignment < mem::DEFAULT_MEM_ALIGNMENT ? mem::DEFAULT_MEM_ALIGNMENT : alignment;
|
||||
}
|
||||
@@ -166,7 +167,7 @@ macro void free_aligned(Allocator allocator, void* ptr)
|
||||
<*
|
||||
@require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_aligned' instead"
|
||||
@require $vacount < 2 : "Too many arguments."
|
||||
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
@require $vacount == 0 ||| $defined($Type t = $vaexpr[0]) : "The second argument must be an initializer for the type"
|
||||
*>
|
||||
macro new(Allocator allocator, $Type, ...) @nodiscard
|
||||
{
|
||||
@@ -182,7 +183,7 @@ macro new(Allocator allocator, $Type, ...) @nodiscard
|
||||
<*
|
||||
@require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_aligned' instead"
|
||||
@require $vacount < 2 : "Too many arguments."
|
||||
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
@require $vacount == 0 ||| $defined($Type t = $vaexpr[0]) : "The second argument must be an initializer for the type"
|
||||
*>
|
||||
macro new_try(Allocator allocator, $Type, ...) @nodiscard
|
||||
{
|
||||
@@ -199,7 +200,7 @@ macro new_try(Allocator allocator, $Type, ...) @nodiscard
|
||||
Allocate using an aligned allocation. This is necessary for types with a default memory alignment
|
||||
exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
|
||||
@require $vacount < 2 : "Too many arguments."
|
||||
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
@require $vacount == 0 ||| $defined($Type t = $vaexpr[0]) : "The second argument must be an initializer for the type"
|
||||
*>
|
||||
macro new_aligned(Allocator allocator, $Type, ...) @nodiscard
|
||||
{
|
||||
@@ -316,6 +317,23 @@ macro clone(Allocator allocator, value) @nodiscard
|
||||
return new(allocator, $typeof(value), value);
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] allocator : "The allocator used to clone"
|
||||
@param slice : "The slice to clone"
|
||||
@return "A pointer to the cloned slice"
|
||||
|
||||
@require @typekind(slice) == SLICE || @typekind(slice) == ARRAY
|
||||
*>
|
||||
macro clone_slice(Allocator allocator, slice) @nodiscard
|
||||
{
|
||||
var $Type = $typeof(slice[0]);
|
||||
|
||||
$Type[] new_arr = new_array(allocator, $Type, slice.len);
|
||||
mem::copy(new_arr.ptr, &slice[0], slice.len * $Type.sizeof);
|
||||
|
||||
return new_arr;
|
||||
}
|
||||
|
||||
<*
|
||||
Clone overaligned values. Must be released using free_aligned.
|
||||
|
||||
@@ -463,7 +481,7 @@ macro usz temp_allocator_default_reserve_size() @local
|
||||
$endswitch
|
||||
}
|
||||
|
||||
macro Allocator heap() => thread_allocator;
|
||||
macro Allocator heap() @deprecated("Use 'mem' instead.") => thread_allocator;
|
||||
|
||||
<*
|
||||
@require !top_temp : "This should never be called when temp already exists"
|
||||
@@ -486,14 +504,14 @@ fn Allocator create_temp_allocator(Allocator allocator, usz size, usz reserve, u
|
||||
return current_temp = top_temp = allocator::new_temp_allocator(allocator, size, reserve, min_size, realloc_size)!!;
|
||||
}
|
||||
|
||||
macro Allocator temp()
|
||||
macro Allocator temp() @deprecated("Use 'tmem' instead")
|
||||
{
|
||||
return current_temp;
|
||||
}
|
||||
|
||||
alias tmem @builtin = current_temp;
|
||||
|
||||
fn void allow_implicit_temp_allocator_on_load_thread() @init(1) @local @if(env::LIBC || env::WASM_NOLIBC)
|
||||
fn void allow_implicit_temp_allocator_on_load_thread() @init(1) @local @if(env::LIBC || env::FREESTANDING_WASM)
|
||||
{
|
||||
auto_create_temp = true;
|
||||
}
|
||||
|
||||
234
lib/std/core/mem_mempool.c3
Normal file
234
lib/std/core/mem_mempool.c3
Normal file
@@ -0,0 +1,234 @@
|
||||
module std::core::mem::mempool;
|
||||
import std::core::mem, std::core::mem::allocator, std::math;
|
||||
|
||||
const INITIAL_CAPACITY = 0;
|
||||
|
||||
struct FixedBlockPoolNode
|
||||
{
|
||||
void* buffer;
|
||||
FixedBlockPoolNode *next;
|
||||
usz capacity;
|
||||
}
|
||||
|
||||
struct FixedBlockPoolEntry
|
||||
{
|
||||
void *previous;
|
||||
}
|
||||
|
||||
<*
|
||||
Fixed blocks pool pre-allocating blocks backed by an Allocator which are then reserved for the user,
|
||||
blocks deallocated by the user are later re-used by future blocks allocations
|
||||
|
||||
`grow_capacity` can be changed in order to affect how many blocks will be allocated by next pool allocation,
|
||||
it has to be greater than 0
|
||||
`allocated` number of allocated blocks
|
||||
`used` number of used blocks by the user
|
||||
*>
|
||||
struct FixedBlockPool
|
||||
{
|
||||
Allocator allocator;
|
||||
FixedBlockPoolNode head;
|
||||
FixedBlockPoolNode *tail;
|
||||
void *next_free;
|
||||
void *freelist;
|
||||
usz block_size;
|
||||
usz grow_capacity;
|
||||
usz allocated;
|
||||
usz page_size;
|
||||
usz alignment;
|
||||
usz used;
|
||||
bool initialized;
|
||||
}
|
||||
|
||||
<*
|
||||
Initialize an block pool
|
||||
|
||||
@param [in] allocator : "The allocator to use"
|
||||
@param block_size : "The block size to use"
|
||||
@param capacity : "The amount of blocks to be pre-allocated"
|
||||
@param alignment : "The alignment of the buffer"
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require !self.initialized : "The block pool must not be initialized"
|
||||
@require block_size > 0 : "Block size must be non zero"
|
||||
@require calculate_actual_capacity(capacity, block_size) * block_size >= block_size
|
||||
: "Total memory would overflow"
|
||||
*>
|
||||
macro FixedBlockPool* FixedBlockPool.init(&self, Allocator allocator, usz block_size, usz capacity = INITIAL_CAPACITY, usz alignment = 0)
|
||||
{
|
||||
self.allocator = allocator;
|
||||
self.tail = &self.head;
|
||||
self.head.next = null;
|
||||
self.block_size = math::max(block_size, FixedBlockPoolEntry.sizeof);
|
||||
capacity = calculate_actual_capacity(capacity, self.block_size);
|
||||
self.alignment = allocator::alignment_for_allocation(alignment);
|
||||
self.page_size = capacity * self.block_size;
|
||||
assert(self.page_size >= self.block_size, "Total memory would overflow %d %d", block_size, capacity);
|
||||
self.head.buffer = self.allocate_page();
|
||||
self.head.capacity = capacity;
|
||||
self.next_free = self.head.buffer;
|
||||
self.freelist = null;
|
||||
self.grow_capacity = capacity;
|
||||
self.initialized = true;
|
||||
self.allocated = capacity;
|
||||
self.used = 0;
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
Initialize an block pool
|
||||
|
||||
@param [in] allocator : "The allocator to use"
|
||||
@param $Type : "The type used for setting the block size"
|
||||
@param capacity : "The amount of blocks to be pre-allocated"
|
||||
@require !self.initialized : "The block pool must not be initialized"
|
||||
*>
|
||||
macro FixedBlockPool* FixedBlockPool.init_for_type(&self, Allocator allocator, $Type, usz capacity = INITIAL_CAPACITY)
|
||||
{
|
||||
return self.init(allocator, $Type.sizeof, capacity, $Type.alignof);
|
||||
}
|
||||
|
||||
<*
|
||||
Initialize an block pool using Temporary allocator
|
||||
|
||||
@param $Type : "The type used for setting the block size"
|
||||
@param capacity : "The amount of blocks to be pre-allocated"
|
||||
@require !self.initialized : "The block pool must not be initialized"
|
||||
*>
|
||||
macro FixedBlockPool* FixedBlockPool.tinit_for_type(&self, $Type, usz capacity = INITIAL_CAPACITY) => self.init_for_type(tmem, $Type, capacity);
|
||||
|
||||
<*
|
||||
Initialize an block pool using Temporary allocator
|
||||
|
||||
@param block_size : "The block size to use"
|
||||
@param capacity : "The amount of blocks to be pre-allocated"
|
||||
@require !self.initialized : "The block pool must not be initialized"
|
||||
*>
|
||||
macro FixedBlockPool* FixedBlockPool.tinit(&self, usz block_size, usz capacity = INITIAL_CAPACITY) => self.init(tmem, block_size, capacity);
|
||||
|
||||
<*
|
||||
Free up the entire block pool
|
||||
|
||||
@require self.initialized : "The block pool must be initialized"
|
||||
*>
|
||||
fn void FixedBlockPool.free(&self)
|
||||
{
|
||||
self.free_page(self.head.buffer);
|
||||
FixedBlockPoolNode* iter = self.head.next;
|
||||
|
||||
while (iter)
|
||||
{
|
||||
self.free_page(iter.buffer);
|
||||
FixedBlockPoolNode* current = iter;
|
||||
iter = iter.next;
|
||||
allocator::free(self.allocator, current);
|
||||
}
|
||||
self.initialized = false;
|
||||
self.allocated = 0;
|
||||
self.used = 0;
|
||||
}
|
||||
|
||||
<*
|
||||
Allocate an block on the block pool, re-uses previously deallocated blocks
|
||||
|
||||
@require self.initialized : "The block pool must be initialized"
|
||||
*>
|
||||
fn void* FixedBlockPool.alloc(&self)
|
||||
{
|
||||
defer self.used++;
|
||||
|
||||
if (self.freelist)
|
||||
{
|
||||
FixedBlockPoolEntry* entry = self.freelist;
|
||||
self.freelist = entry.previous;
|
||||
mem::clear(entry, self.block_size);
|
||||
return entry;
|
||||
}
|
||||
|
||||
void* end = self.tail.buffer + (self.tail.capacity * self.block_size);
|
||||
if (self.next_free >= end) self.new_node();
|
||||
void* ptr = self.next_free;
|
||||
self.next_free += self.block_size;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
<*
|
||||
Deallocate a block from the block pool
|
||||
|
||||
@require self.initialized : "The block pool must be initialized"
|
||||
@require self.check_ptr(ptr) : "The pointer should be part of the pool"
|
||||
*>
|
||||
fn void FixedBlockPool.dealloc(&self, void* ptr)
|
||||
{
|
||||
$if env::COMPILER_SAFE_MODE && !env::ADDRESS_SANITIZER:
|
||||
if (self.block_size > FixedBlockPoolEntry.sizeof)
|
||||
{
|
||||
mem::set(ptr + FixedBlockPoolEntry.sizeof, 0xAA, self.block_size);
|
||||
}
|
||||
$else
|
||||
// POINT FOR IMPROVEMENT, something like:
|
||||
// asan::poison_memory_region(&ptr, self.block_size);
|
||||
$endif
|
||||
|
||||
FixedBlockPoolEntry* entry = ptr;
|
||||
entry.previous = self.freelist;
|
||||
self.freelist = entry;
|
||||
self.used--;
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.initialized : "The block pool must be initialized"
|
||||
*>
|
||||
fn bool FixedBlockPool.check_ptr(&self, void *ptr) @local
|
||||
{
|
||||
FixedBlockPoolNode* iter = &self.head;
|
||||
|
||||
while (iter)
|
||||
{
|
||||
void* end = iter.buffer + (iter.capacity * self.block_size);
|
||||
if (ptr >= iter.buffer && ptr < end) return true;
|
||||
iter = iter.next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.grow_capacity > 0 : "How many blocks will it store"
|
||||
*>
|
||||
fn void FixedBlockPool.new_node(&self) @local
|
||||
{
|
||||
FixedBlockPoolNode* node = allocator::new(self.allocator, FixedBlockPoolNode);
|
||||
node.buffer = self.allocate_page();
|
||||
node.capacity = self.grow_capacity;
|
||||
self.tail.next = node;
|
||||
self.tail = node;
|
||||
self.next_free = node.buffer;
|
||||
self.allocated += node.capacity;
|
||||
}
|
||||
|
||||
macro void* FixedBlockPool.allocate_page(&self) @private
|
||||
{
|
||||
return self.alignment > mem::DEFAULT_MEM_ALIGNMENT
|
||||
? allocator::calloc_aligned(self.allocator, self.page_size, self.alignment)!!
|
||||
: allocator::calloc(self.allocator, self.page_size);
|
||||
}
|
||||
macro void FixedBlockPool.free_page(&self, void* page) @private
|
||||
{
|
||||
if (self.alignment > mem::DEFAULT_MEM_ALIGNMENT)
|
||||
{
|
||||
allocator::free_aligned(self.allocator, page);
|
||||
}
|
||||
else
|
||||
{
|
||||
allocator::free(self.allocator, page);
|
||||
}
|
||||
}
|
||||
|
||||
macro usz calculate_actual_capacity(usz capacity, usz block_size) @private
|
||||
{
|
||||
// Assume some overhead
|
||||
if (capacity) return capacity;
|
||||
capacity = (mem::os_pagesize() - 128) / block_size;
|
||||
return capacity ?: 1;
|
||||
}
|
||||
353
lib/std/core/os/mem_vm.c3
Normal file
353
lib/std/core/os/mem_vm.c3
Normal file
@@ -0,0 +1,353 @@
|
||||
<*
|
||||
The VM module holds code for working with virtual memory on supported platforms (currently Win32 and Posix)
|
||||
*>
|
||||
module std::core::mem::vm;
|
||||
import std::io, std::os::win32, std::os::posix, libc;
|
||||
|
||||
<*
|
||||
VirtualMemory is an abstraction for working with an allocated virtual memory area. It will invoke vm:: functions
|
||||
but will perform more checks and track its size (required to unmap the memory on Posix)
|
||||
*>
|
||||
struct VirtualMemory
|
||||
{
|
||||
void* ptr;
|
||||
usz size;
|
||||
VirtualMemoryAccess default_access;
|
||||
}
|
||||
|
||||
faultdef RANGE_OVERFLOW, UNKNOWN_ERROR, ACCESS_DENIED, UNMAPPED_ACCESS, UNALIGNED_ADDRESS, RELEASE_FAILED, UPDATE_FAILED, INVALID_ARGS;
|
||||
|
||||
enum VirtualMemoryAccess
|
||||
{
|
||||
PROTECTED,
|
||||
READ,
|
||||
WRITE,
|
||||
READWRITE,
|
||||
EXEC,
|
||||
EXECREAD,
|
||||
EXECWRITE,
|
||||
ANY
|
||||
}
|
||||
|
||||
fn usz aligned_alloc_size(usz size)
|
||||
{
|
||||
$if env::WIN32:
|
||||
return size > 0 ? mem::aligned_offset(size, win32::allocation_granularity()) : win32::allocation_granularity();
|
||||
$else
|
||||
return size > 0 ? mem::aligned_offset(size, mem::os_pagesize()) : mem::os_pagesize();
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
Allocate virtual memory, size is rounded up to platform granularity (Win32) / page size (Posix).
|
||||
|
||||
@param size : "The size of the memory to allocate, will be rounded up"
|
||||
@param access : "The initial access permissions."
|
||||
@return? mem::OUT_OF_MEMORY, RANGE_OVERFLOW, UNKNOWN_ERROR, ACCESS_DENIED, INVALID_ARGS
|
||||
@return "Pointer to the allocated memory, page aligned"
|
||||
*>
|
||||
fn void*? alloc(usz size, VirtualMemoryAccess access)
|
||||
{
|
||||
$switch:
|
||||
$case env::POSIX:
|
||||
void* ptr = posix::mmap(null, aligned_alloc_size(size), access.to_posix(), posix::MAP_PRIVATE | posix::MAP_ANONYMOUS, -1, 0);
|
||||
if (ptr != posix::MAP_FAILED) return ptr;
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::ENOMEM: return mem::OUT_OF_MEMORY?;
|
||||
case errno::EOVERFLOW: return RANGE_OVERFLOW?;
|
||||
case errno::EPERM: return ACCESS_DENIED?;
|
||||
case errno::EINVAL: return INVALID_ARGS?;
|
||||
default: return UNKNOWN_ERROR?;
|
||||
}
|
||||
$case env::WIN32:
|
||||
void* ptr = win32::virtualAlloc(null, aligned_alloc_size(size), MEM_RESERVE, access.to_win32());
|
||||
if (ptr) return ptr;
|
||||
switch (win32::getLastError())
|
||||
{
|
||||
case win32::ERROR_NOT_ENOUGH_MEMORY:
|
||||
case win32::ERROR_COMMITMENT_LIMIT: return mem::OUT_OF_MEMORY?;
|
||||
default: return UNKNOWN_ERROR?;
|
||||
}
|
||||
$default:
|
||||
unsupported("Virtual alloc only available on Win32 and Posix");
|
||||
$endswitch
|
||||
}
|
||||
|
||||
<*
|
||||
Release memory allocated with "alloc".
|
||||
|
||||
@param [&inout] ptr : "Pointer to page to release, should be allocated using vm::alloc"
|
||||
@param size : "The size of the allocated pointer"
|
||||
@require mem::ptr_is_page_aligned(ptr) : "The pointer should be page aligned"
|
||||
*>
|
||||
fn void? release(void* ptr, usz size)
|
||||
{
|
||||
$switch:
|
||||
$case env::POSIX:
|
||||
if (posix::munmap(ptr, aligned_alloc_size(size)))
|
||||
{
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EINVAL: return INVALID_ARGS?; // Not a valid mapping or size
|
||||
case errno::ENOMEM: return UNMAPPED_ACCESS?; // Address not mapped
|
||||
default: return RELEASE_FAILED?;
|
||||
}
|
||||
}
|
||||
$case env::WIN32:
|
||||
if (win32::virtualFree(ptr, 0, MEM_RELEASE)) return;
|
||||
switch (win32::getLastError())
|
||||
{
|
||||
case win32::ERROR_INVALID_ADDRESS: return INVALID_ARGS?;
|
||||
case win32::ERROR_NOT_ENOUGH_MEMORY: return mem::OUT_OF_MEMORY?;
|
||||
default: return RELEASE_FAILED?;
|
||||
}
|
||||
$default:
|
||||
unsupported("Virtual free only available on Win32 and Posix");
|
||||
$endswitch
|
||||
}
|
||||
|
||||
<*
|
||||
Change the access protection of a region in memory. The region must be page aligned.
|
||||
|
||||
@param [&inout] ptr : "Pointer to page to update, must be page aligned"
|
||||
@param len : "To what len to update, must be page aligned"
|
||||
@param access : "The new access"
|
||||
@require mem::ptr_is_page_aligned(ptr) : "The pointer should be page aligned"
|
||||
@require mem::ptr_is_page_aligned(ptr + len) : "The length must be page aligned"
|
||||
@return? ACCESS_DENIED, UNALIGNED_ADDRESS, RANGE_OVERFLOW, UPDATE_FAILED, UNMAPPED_ACCESS, INVALID_ARGS
|
||||
*>
|
||||
fn void? protect(void* ptr, usz len, VirtualMemoryAccess access)
|
||||
{
|
||||
$switch:
|
||||
$case env::POSIX:
|
||||
if (!posix::mprotect(ptr, len, access.to_posix())) return;
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EACCES: return ACCESS_DENIED?;
|
||||
case errno::EINVAL: return UNALIGNED_ADDRESS?;
|
||||
case errno::EOVERFLOW: return RANGE_OVERFLOW?;
|
||||
case errno::ENOMEM: return UNMAPPED_ACCESS?;
|
||||
default: return UPDATE_FAILED?;
|
||||
}
|
||||
$case env::WIN32:
|
||||
Win32_Protect old;
|
||||
if (win32::virtualProtect(ptr, len, access.to_win32(), &old)) return;
|
||||
switch (win32::getLastError())
|
||||
{
|
||||
case win32::ERROR_INVALID_ADDRESS: return UNALIGNED_ADDRESS?;
|
||||
case win32::ERROR_ACCESS_DENIED: return ACCESS_DENIED?;
|
||||
case win32::ERROR_INVALID_PARAMETER: return INVALID_ARGS?;
|
||||
default: return UPDATE_FAILED?;
|
||||
}
|
||||
$default:
|
||||
unsupported("'virtual_protect' is only available on Win32 and Posix.");
|
||||
$endswitch
|
||||
}
|
||||
|
||||
<*
|
||||
Makes a region of memory available that was previously retrieved using 'alloc'. This is necessary on Win32,
|
||||
but optional on Posix.
|
||||
|
||||
@param [&inout] ptr : "Pointer to page to update, must be page aligned"
|
||||
@param len : "To what len to commit, must be page aligned"
|
||||
@require mem::ptr_is_page_aligned(ptr) : "The pointer should be page aligned"
|
||||
@require mem::ptr_is_page_aligned(ptr + len) : "The length must be page aligned"
|
||||
@return? UNKNOWN_ERROR, mem::OUT_OF_MEMORY, ACCESS_DENIED, UNALIGNED_ADDRESS, RANGE_OVERFLOW, UPDATE_FAILED, UNMAPPED_ACCESS, INVALID_ARGS
|
||||
*>
|
||||
fn void? commit(void* ptr, usz len, VirtualMemoryAccess access = READWRITE)
|
||||
{
|
||||
$switch:
|
||||
$case env::POSIX:
|
||||
return protect(ptr, len, READWRITE) @inline;
|
||||
$case env::WIN32:
|
||||
void* result = win32::virtualAlloc(ptr, len, MEM_COMMIT, access.to_win32());
|
||||
if (result) return;
|
||||
switch (win32::getLastError())
|
||||
{
|
||||
case win32::ERROR_INVALID_ADDRESS: return UNALIGNED_ADDRESS?;
|
||||
case win32::ERROR_ACCESS_DENIED: return ACCESS_DENIED?;
|
||||
case win32::ERROR_COMMITMENT_LIMIT:
|
||||
case win32::ERROR_NOT_ENOUGH_MEMORY: return mem::OUT_OF_MEMORY?;
|
||||
case win32::ERROR_INVALID_PARAMETER: return INVALID_ARGS?;
|
||||
default: return UNKNOWN_ERROR?;
|
||||
}
|
||||
$default:
|
||||
unsupported("'virtual_commit' is only available on Win32 and Posix.");
|
||||
$endswitch
|
||||
}
|
||||
|
||||
<*
|
||||
Notifies that the memory in the region can be released back to the OS. On Win32 this decommits the region,
|
||||
whereas on Posix it tells the system that it may be reused using madvise. The "block" parameter is only
|
||||
respected on Posix, and protects the region from read/write/exec. On Win32 this always happens.
|
||||
|
||||
@param [&inout] ptr : "Pointer to page to update, must be page aligned"
|
||||
@param len : "To what len to commit, must be page aligned"
|
||||
@param block : "Set the released memory to protected"
|
||||
@require mem::ptr_is_page_aligned(ptr) : "The pointer should be page aligned"
|
||||
@require mem::ptr_is_page_aligned(ptr + len) : "The length must be page aligned"
|
||||
@return? ACCESS_DENIED, UNALIGNED_ADDRESS, RANGE_OVERFLOW, UPDATE_FAILED, UNMAPPED_ACCESS, INVALID_ARGS
|
||||
*>
|
||||
fn void? decommit(void* ptr, usz len, bool block = true)
|
||||
{
|
||||
$switch:
|
||||
$case env::POSIX:
|
||||
if (posix::madvise(ptr, len, posix::MADV_DONTNEED))
|
||||
{
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EINVAL: return UNALIGNED_ADDRESS?;
|
||||
case errno::ENOMEM: return UNMAPPED_ACCESS?;
|
||||
default: return UPDATE_FAILED?;
|
||||
}
|
||||
}
|
||||
if (block) (void)protect(ptr, len, PROTECTED) @inline;
|
||||
$case env::WIN32:
|
||||
if (!win32::virtualFree(ptr, len, MEM_DECOMMIT))
|
||||
{
|
||||
switch (win32::getLastError())
|
||||
{
|
||||
case win32::ERROR_INVALID_ADDRESS: return UNALIGNED_ADDRESS?;
|
||||
case win32::ERROR_INVALID_PARAMETER: return INVALID_ARGS?;
|
||||
case win32::ERROR_ACCESS_DENIED: return ACCESS_DENIED?;
|
||||
default: return UPDATE_FAILED?;
|
||||
}
|
||||
}
|
||||
$default:
|
||||
unsupported("'virtual_decommit' is only available on Win32 and Posix.");
|
||||
$endswitch
|
||||
}
|
||||
|
||||
<*
|
||||
Map a portion of an already-opened file into memory.
|
||||
|
||||
@param fd : "File descriptor"
|
||||
@param size : "Number of bytes to map, will be rounded up to page size"
|
||||
@param offset : "Byte offset in file, must be page size aligned"
|
||||
@param access : "The initial access permissions"
|
||||
@param shared : "if True then MAP_SHARED else MAP_PRIVATE"
|
||||
@return? mem::OUT_OF_MEMORY, RANGE_OVERFLOW, UNKNOWN_ERROR, ACCESS_DENIED, INVALID_ARGS, io::NO_PERMISSION, io::FILE_NOT_VALID, io::WOULD_BLOCK, io::FILE_NOT_FOUND
|
||||
@return "Pointer to the mapped region"
|
||||
*>
|
||||
fn void*? mmap_file(Fd fd, usz size, usz offset = 0, VirtualMemoryAccess access = READ, bool shared = false) @if (env::POSIX)
|
||||
{
|
||||
CInt flags = shared ? posix::MAP_SHARED : posix::MAP_PRIVATE;
|
||||
void* ptr = posix::mmap(null, aligned_alloc_size(size), access.to_posix(), flags, fd, offset);
|
||||
if (ptr != posix::MAP_FAILED) return ptr;
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::ENOMEM: return mem::OUT_OF_MEMORY?;
|
||||
case errno::EOVERFLOW: return RANGE_OVERFLOW?;
|
||||
case errno::EPERM: return ACCESS_DENIED?;
|
||||
case errno::EINVAL: return INVALID_ARGS?;
|
||||
case errno::EACCES: return io::NO_PERMISSION?;
|
||||
case errno::EBADF: return io::FILE_NOT_VALID?;
|
||||
case errno::EAGAIN: return io::WOULD_BLOCK?;
|
||||
case errno::ENXIO: return io::FILE_NOT_FOUND?;
|
||||
default: return UNKNOWN_ERROR?;
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Create a VirtualMemory using
|
||||
|
||||
@param size : "The size of the memory to allocate."
|
||||
@require size > 0 : "The size must be non-zero"
|
||||
@return? mem::OUT_OF_MEMORY, RANGE_OVERFLOW, UNKNOWN_ERROR, ACCESS_DENIED, INVALID_ARGS
|
||||
*>
|
||||
fn VirtualMemory? virtual_alloc(usz size, VirtualMemoryAccess access = PROTECTED)
|
||||
{
|
||||
size = aligned_alloc_size(size);
|
||||
void* ptr = alloc(size, access)!;
|
||||
return { ptr, size, access };
|
||||
}
|
||||
|
||||
<*
|
||||
Commits memory, using vm::commit
|
||||
|
||||
@param offset : "Starting from what offset to commit"
|
||||
@param len : "To what len to commit"
|
||||
@require mem::ptr_is_page_aligned(self.ptr + offset) : "The offset should be page aligned"
|
||||
@require mem::ptr_is_page_aligned(self.ptr + offset + len) : "The length must be page aligned"
|
||||
@require offset < self.size : "Offset out of range"
|
||||
@require offset + len <= self.size : "Length out of range"
|
||||
@return? UPDATE_FAILED, ACCESS_DENIED, UNALIGNED_ADDRESS, RANGE_OVERFLOW, UNKNOWN_ERROR
|
||||
*>
|
||||
macro void? VirtualMemory.commit(self, usz offset, usz len)
|
||||
{
|
||||
return commit(self.ptr + offset, len, self.default_access);
|
||||
}
|
||||
|
||||
<*
|
||||
Changes protection of a part of memory using vm::protect
|
||||
|
||||
@param offset : "Starting from what offset to update"
|
||||
@param len : "To what len to update"
|
||||
@require mem::ptr_is_page_aligned(self.ptr + offset) : "The offset should be page aligned"
|
||||
@require mem::ptr_is_page_aligned(self.ptr + offset + len) : "The length must be page aligned"
|
||||
@require offset < self.size : "Offset out of range"
|
||||
@require offset + len < self.size : "Length out of range"
|
||||
@return? UPDATE_FAILED, ACCESS_DENIED, UNALIGNED_ADDRESS, RANGE_OVERFLOW, UNKNOWN_ERROR
|
||||
*>
|
||||
macro void? VirtualMemory.protect(self, usz offset, usz len, VirtualMemoryAccess access)
|
||||
{
|
||||
return protect(self.ptr + offset, len, access);
|
||||
}
|
||||
<*
|
||||
Decommits a part of memory using vm::decommit
|
||||
|
||||
@param offset : "Starting from what offset to decommit"
|
||||
@param len : "To what len to decommit"
|
||||
@param block : "Should the memory be blocked from access after decommit"
|
||||
@require mem::ptr_is_page_aligned(self.ptr + offset) : "The offset should be page aligned"
|
||||
@require mem::ptr_is_page_aligned(self.ptr + offset + len) : "The length must be page aligned"
|
||||
@require offset < self.size : "Offset out of range"
|
||||
@require offset + len < self.size : "Length out of range"
|
||||
@return? UPDATE_FAILED
|
||||
*>
|
||||
fn void? VirtualMemory.decommit(self, usz offset, usz len, bool block = true)
|
||||
{
|
||||
return decommit(self.ptr + offset, len, block);
|
||||
}
|
||||
|
||||
<*
|
||||
Releases the memory region
|
||||
|
||||
@require self.ptr != null : "Virtual memory must be initialized to call destroy"
|
||||
*>
|
||||
fn void? VirtualMemory.destroy(&self)
|
||||
{
|
||||
return release(self.ptr, self.size);
|
||||
}
|
||||
|
||||
fn CInt VirtualMemoryAccess.to_posix(self) @if(env::POSIX) @private
|
||||
{
|
||||
switch (self)
|
||||
{
|
||||
case PROTECTED: return posix::PROT_NONE;
|
||||
case READ: return posix::PROT_READ;
|
||||
case WRITE: return posix::PROT_WRITE;
|
||||
case EXEC: return posix::PROT_EXEC;
|
||||
case READWRITE: return posix::PROT_READ | posix::PROT_WRITE;
|
||||
case EXECREAD: return posix::PROT_READ | posix::PROT_EXEC;
|
||||
case EXECWRITE: return posix::PROT_WRITE | posix::PROT_EXEC;
|
||||
case ANY: return posix::PROT_WRITE | posix::PROT_READ | posix::PROT_EXEC;
|
||||
}
|
||||
}
|
||||
|
||||
fn Win32_Protect VirtualMemoryAccess.to_win32(self) @if(env::WIN32) @private
|
||||
{
|
||||
switch (self)
|
||||
{
|
||||
case PROTECTED: return PAGE_NOACCESS;
|
||||
case READ: return PAGE_READONLY;
|
||||
case WRITE: return PAGE_READWRITE;
|
||||
case EXEC: return PAGE_EXECUTE;
|
||||
case READWRITE: return PAGE_READWRITE;
|
||||
case EXECWRITE: return PAGE_EXECUTE_READWRITE;
|
||||
case EXECREAD: return PAGE_EXECUTE_READ;
|
||||
case ANY: return PAGE_EXECUTE_READWRITE;
|
||||
}
|
||||
}
|
||||
|
||||
132
lib/std/core/refcount.c3
Normal file
132
lib/std/core/refcount.c3
Normal file
@@ -0,0 +1,132 @@
|
||||
<*
|
||||
Ref provides a general *external* ref counted wrapper for a pointer. For convenience, a ref count of 0
|
||||
means the reference is still valid.
|
||||
|
||||
When the rc drops to -1, it will first run the dealloc function on the underlying pointer (if it exists),
|
||||
then free the pointer and the atomic variable assuming that they are allocated using the Allocator in the Ref.
|
||||
|
||||
@require !$defined(Type.dealloc) ||| $defined(Type.dealloc(&&(Type){})) : "'dealloc' must only take a pointer to the underlying type"
|
||||
@require !$defined(Type.dealloc) ||| @typeis((Type){}.dealloc(), void) : "'dealloc' must return 'void'"
|
||||
*>
|
||||
module std::core::mem::ref { Type };
|
||||
import std::thread, std::atomic;
|
||||
|
||||
const OVERALIGNED @private = Type.alignof > mem::DEFAULT_MEM_ALIGNMENT;
|
||||
|
||||
alias DeallocFn = fn void(void*);
|
||||
|
||||
fn Ref wrap(Type* ptr, Allocator allocator = mem)
|
||||
{
|
||||
return { .refcount = allocator::new(allocator, Atomic{int}), .ptr = ptr, .allocator = allocator };
|
||||
}
|
||||
<*
|
||||
@require $vacount < 2 : "Too many arguments."
|
||||
@require $vacount == 0 ||| @assignable_to($vaexpr[0], Type) : "The first argument must be an initializer for the type"
|
||||
*>
|
||||
macro Ref new(..., Allocator allocator = mem)
|
||||
{
|
||||
|
||||
$switch:
|
||||
$case OVERALIGNED && !$vacount:
|
||||
Type* ptr = allocator::calloc_aligned(allocator, Type.sizeof, Type.alignof)!!;
|
||||
$case OVERALIGNED:
|
||||
Type* ptr = allocator::malloc_aligned(allocator, Type.sizeof, Type.alignof)!!;
|
||||
*ptr = $vaexpr[0];
|
||||
$case !$vacount:
|
||||
Type* ptr = allocator::calloc(allocator, Type.sizeof);
|
||||
$default:
|
||||
Type* ptr = allocator::malloc(allocator, Type.sizeof);
|
||||
*ptr = $vaexpr[0];
|
||||
$endswitch
|
||||
return { .refcount = allocator::new(allocator, Atomic{int}),
|
||||
.ptr = ptr,
|
||||
.allocator = allocator };
|
||||
}
|
||||
|
||||
struct Ref
|
||||
{
|
||||
Atomic{int}* refcount;
|
||||
Type* ptr;
|
||||
Allocator allocator;
|
||||
}
|
||||
|
||||
fn Ref* Ref.retain(&self)
|
||||
{
|
||||
assert(self.refcount != null, "Reference already released");
|
||||
assert(self.refcount.load(RELAXED) >= 0, "Retaining zombie");
|
||||
self.refcount.add(1, RELAXED);
|
||||
return self;
|
||||
}
|
||||
|
||||
fn void Ref.release(&self)
|
||||
{
|
||||
assert(self.refcount != null, "Reference already released");
|
||||
assert(self.refcount.load(RELAXED) >= 0, "Overrelease of refcount");
|
||||
if (self.refcount.sub(1, RELAXED) == 0)
|
||||
{
|
||||
thread::fence(ACQUIRE);
|
||||
$if $defined(Type.dealloc):
|
||||
self.ptr.dealloc();
|
||||
$endif
|
||||
$if OVERALIGNED:
|
||||
allocator::free_aligned(self.allocator, self.ptr);
|
||||
$else
|
||||
allocator::free(self.allocator, self.ptr);
|
||||
$endif
|
||||
allocator::free(self.allocator, self.refcount);
|
||||
*self = {};
|
||||
}
|
||||
}
|
||||
|
||||
module std::core::mem::rc;
|
||||
import std::thread, std::atomic;
|
||||
|
||||
<*
|
||||
A RefCounted struct should be an inline base of a struct.
|
||||
If a `dealloc` is defined, then it will be called rather than `free`
|
||||
|
||||
For convenience, a ref count of 0 is still valid, and the struct is
|
||||
only freed when when ref count drops to -1.
|
||||
|
||||
The macros rc::retain and rc::release must be used on the full pointer,
|
||||
not on the RefCounted substruct.
|
||||
|
||||
So `Foo* f = ...; RefCounted* rc = f; rc::release(rc);` will not do the right thing.
|
||||
*>
|
||||
struct RefCounted
|
||||
{
|
||||
Atomic{int} refcount;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @assignable_to(refcounted, RefCounted*) : "Expected a ref counted value"
|
||||
*>
|
||||
macro retain(refcounted)
|
||||
{
|
||||
if (refcounted)
|
||||
{
|
||||
assert(refcounted.refcount.load(RELAXED) >= 0, "Retaining zombie");
|
||||
refcounted.refcount.add(1, RELAXED);
|
||||
}
|
||||
return refcounted;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @assignable_to(refcounted, RefCounted*) : "Expected a ref counted value"
|
||||
@require !$defined(refcounted.dealloc()) ||| @typeis(refcounted.dealloc(), void)
|
||||
: "Expected refcounted type to have a valid dealloc"
|
||||
*>
|
||||
macro void release(refcounted)
|
||||
{
|
||||
if (!refcounted) return;
|
||||
assert(refcounted.refcount.load(RELAXED) >= 0, "Overrelease of refcount");
|
||||
if (refcounted.refcount.sub(1, RELAXED) == 0)
|
||||
{
|
||||
thread::fence(ACQUIRE);
|
||||
$if $defined(refcounted.dealloc):
|
||||
refcounted.dealloc();
|
||||
$else
|
||||
free(refcounted);
|
||||
$endif
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ macro @enum_lookup_new($Type, $name, value)
|
||||
}
|
||||
|
||||
|
||||
module std::core::runtime @if(WASM_NOLIBC);
|
||||
module std::core::runtime @if(env::FREESTANDING_WASM);
|
||||
|
||||
extern fn void __wasm_call_ctors();
|
||||
fn void wasm_initialize() @extern("_initialize") @wasm
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
module std::core::runtime;
|
||||
import libc, std::time, std::io, std::sort;
|
||||
import libc, std::time, std::io, std::sort, std::math, std::collections::map;
|
||||
|
||||
alias BenchmarkFn = fn void();
|
||||
alias BenchmarkFn = fn void ();
|
||||
|
||||
HashMap { String, uint } bench_fn_iters @local;
|
||||
|
||||
struct BenchmarkUnit
|
||||
{
|
||||
@@ -17,6 +19,7 @@ fn BenchmarkUnit[] benchmark_collection_create(Allocator allocator)
|
||||
foreach (i, benchmark : fns)
|
||||
{
|
||||
benchmarks[i] = { names[i], fns[i] };
|
||||
if (!bench_fn_iters.has_key(names[i])) bench_fn_iters[names[i]] = benchmark_max_iterations;
|
||||
}
|
||||
return benchmarks;
|
||||
}
|
||||
@@ -36,6 +39,42 @@ fn void set_benchmark_max_iterations(uint value) @builtin
|
||||
{
|
||||
assert(value > 0);
|
||||
benchmark_max_iterations = value;
|
||||
foreach (k : bench_fn_iters.key_iter()) bench_fn_iters[k] = value;
|
||||
}
|
||||
|
||||
fn void set_benchmark_func_iterations(String func, uint value) @builtin
|
||||
{
|
||||
assert(value > 0);
|
||||
bench_fn_iters[func] = value;
|
||||
}
|
||||
|
||||
|
||||
Clock benchmark_clock @local;
|
||||
NanoDuration benchmark_nano_seconds @local;
|
||||
long cycle_start @local;
|
||||
long cycle_stop @local;
|
||||
DString benchmark_log @local;
|
||||
bool benchmark_warming @local;
|
||||
uint this_iteration @local;
|
||||
|
||||
macro @start_benchmark()
|
||||
{
|
||||
benchmark_clock = std::time::clock::now();
|
||||
cycle_start = $$sysclock();
|
||||
}
|
||||
|
||||
macro @end_benchmark()
|
||||
{
|
||||
benchmark_nano_seconds = benchmark_clock.mark();
|
||||
cycle_stop = $$sysclock();
|
||||
}
|
||||
|
||||
macro @log_benchmark(msg, args...) => @pool()
|
||||
{
|
||||
if (benchmark_warming) return;
|
||||
|
||||
benchmark_log.appendf("%s [%d]: ", $$FUNC, this_iteration);
|
||||
benchmark_log.appendfn(msg, ...args);
|
||||
}
|
||||
|
||||
fn bool run_benchmarks(BenchmarkUnit[] benchmarks)
|
||||
@@ -58,36 +97,68 @@ fn bool run_benchmarks(BenchmarkUnit[] benchmarks)
|
||||
|
||||
name.clear();
|
||||
|
||||
long sys_clock_started;
|
||||
long sys_clock_finished;
|
||||
long sys_clocks;
|
||||
Clock clock;
|
||||
|
||||
foreach(unit : benchmarks)
|
||||
foreach (unit : benchmarks)
|
||||
{
|
||||
defer name.clear();
|
||||
name.appendf("Benchmarking %s ", unit.name);
|
||||
name.append_repeat('.', max_name - unit.name.len + 2);
|
||||
io::printf("%s ", name.str_view());
|
||||
|
||||
benchmark_warming = true;
|
||||
for (uint i = 0; i < benchmark_warmup_iterations; i++)
|
||||
{
|
||||
unit.func() @inline;
|
||||
}
|
||||
benchmark_warming = false;
|
||||
|
||||
clock = std::time::clock::now();
|
||||
sys_clock_started = $$sysclock();
|
||||
NanoDuration running_timer;
|
||||
long total_clocks;
|
||||
|
||||
for (uint i = 0; i < benchmark_max_iterations; i++)
|
||||
uint current_benchmark_iterations = bench_fn_iters[unit.name] ?? benchmark_max_iterations;
|
||||
char[] perc_str = { [0..19] = ' ', [20] = 0 };
|
||||
int perc = 0;
|
||||
uint print_step = current_benchmark_iterations / 100;
|
||||
|
||||
for (this_iteration = 0; this_iteration < current_benchmark_iterations; ++this_iteration, benchmark_nano_seconds = {})
|
||||
{
|
||||
if (0 == this_iteration % print_step) // only print right about when the % will update
|
||||
{
|
||||
perc_str[0..(uint)math::floor((this_iteration / (float)current_benchmark_iterations) * 20)] = '#';
|
||||
perc = (uint)math::ceil(100 * (this_iteration / (float)current_benchmark_iterations));
|
||||
|
||||
io::printf("\r%s [%s] %d / %d (%d%%)", name.str_view(), (ZString)perc_str, this_iteration, current_benchmark_iterations, perc);
|
||||
io::stdout().flush()!!;
|
||||
}
|
||||
|
||||
@start_benchmark(); // can be overridden by calls inside the unit's func
|
||||
|
||||
unit.func() @inline;
|
||||
|
||||
if (benchmark_nano_seconds == (NanoDuration){}) @end_benchmark(); // only mark when it wasn't already by the unit.func
|
||||
|
||||
total_clocks += cycle_stop - cycle_start;
|
||||
running_timer += benchmark_nano_seconds;
|
||||
}
|
||||
|
||||
sys_clock_finished = $$sysclock();
|
||||
NanoDuration nano_seconds = clock.mark();
|
||||
sys_clocks = sys_clock_finished - sys_clock_started;
|
||||
float clock_cycles = (float)total_clocks / current_benchmark_iterations;
|
||||
float measurement = (float)running_timer / current_benchmark_iterations;
|
||||
String[] units = { "nanoseconds", "microseconds", "milliseconds", "seconds" };
|
||||
|
||||
io::printfn("[COMPLETE] %.2f ns, %.2f CPU's clocks", (float)nano_seconds / benchmark_max_iterations, (float)sys_clocks / benchmark_max_iterations);
|
||||
float adjusted_measurement = measurement;
|
||||
while (adjusted_measurement > 1_000) adjusted_measurement /= 1_000;
|
||||
float adjusted_runtime_total = (float)running_timer;
|
||||
while (adjusted_runtime_total > 1_000) adjusted_runtime_total /= 1_000;
|
||||
|
||||
io::printf("\r%s ", name.str_view());
|
||||
io::printfn(
|
||||
"[COMPLETE] %.2f %s, %.2f CPU clocks, %d iterations (runtime %.2f %s)",
|
||||
adjusted_measurement,
|
||||
units[math::min(3, (int)math::floor(math::log(measurement, 1_000)))],
|
||||
clock_cycles,
|
||||
current_benchmark_iterations,
|
||||
adjusted_runtime_total,
|
||||
units[math::min(3, (int)math::floor(math::log((float)running_timer, 1_000)))],
|
||||
);
|
||||
}
|
||||
|
||||
io::printfn("\n%d benchmark%s run.\n", benchmarks.len, benchmarks.len > 1 ? "s" : "");
|
||||
@@ -96,5 +167,12 @@ fn bool run_benchmarks(BenchmarkUnit[] benchmarks)
|
||||
|
||||
fn bool default_benchmark_runner(String[] args) => @pool()
|
||||
{
|
||||
benchmark_log.init(mem);
|
||||
defer
|
||||
{
|
||||
if (benchmark_log.len()) io::printfn("\n---------- BENCHMARK LOG ----------\n%s\n", benchmark_log.str_view());
|
||||
benchmark_log.free();
|
||||
}
|
||||
|
||||
return run_benchmarks(benchmark_collection_create(tmem));
|
||||
}
|
||||
|
||||
@@ -140,7 +140,6 @@ fn void unmute_output(bool has_error) @local
|
||||
usz log_size = test_context.fake_stdout.seek(0, Seek.CURSOR)!!;
|
||||
if (has_error)
|
||||
{
|
||||
io::printf("\nTesting %s ", test_context.current_test_name);
|
||||
io::printn(test_context.has_ansi_codes ? "[\e[0;31mFAIL\e[0m]" : "[FAIL]");
|
||||
}
|
||||
|
||||
@@ -171,6 +170,11 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private
|
||||
usz max_name;
|
||||
bool sort_tests = true;
|
||||
bool check_leaks = true;
|
||||
if (!tests.len)
|
||||
{
|
||||
io::printn("There are no test units to run.");
|
||||
return true; // no tests == technically a pass
|
||||
}
|
||||
foreach (&unit : tests)
|
||||
{
|
||||
if (max_name < unit.name.len) max_name = unit.name.len;
|
||||
@@ -305,7 +309,7 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private
|
||||
}
|
||||
mem.free();
|
||||
}
|
||||
io::printfn("\n%d test%s run.\n", test_count-tests_skipped, test_count > 1 ? "s" : "");
|
||||
io::printfn("\n%d test%s run.\n", test_count-tests_skipped, test_count != 1 ? "s" : "");
|
||||
|
||||
int n_failed = test_count - tests_passed - tests_skipped;
|
||||
io::printf("Test Result: %s%s%s: ",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
module std::core::string;
|
||||
import std::io;
|
||||
import std::io, std::ascii;
|
||||
|
||||
|
||||
typedef String @if(!$defined(String)) = inline char[];
|
||||
<*
|
||||
@@ -107,6 +108,19 @@ fn String format(Allocator allocator, String fmt, args...) @format(1) => @pool()
|
||||
return str.copy_str(allocator);
|
||||
}
|
||||
|
||||
<*
|
||||
Return a new String created using the formatting function.
|
||||
|
||||
@param [inout] buffer : `The buffer to use`
|
||||
@param [in] fmt : `The formatting string`
|
||||
*>
|
||||
fn String bformat(char[] buffer, String fmt, args...) @format(1)
|
||||
{
|
||||
DString str = dstring::new_with_capacity(allocator::wrap(buffer), fmt.len + args.len * 8);
|
||||
str.appendf(fmt, ...args);
|
||||
return str.str_view();
|
||||
}
|
||||
|
||||
<*
|
||||
Return a temporary String created using the formatting function.
|
||||
|
||||
@@ -123,7 +137,7 @@ fn String tformat(String fmt, args...) @format(0)
|
||||
Check if a character is in a set.
|
||||
|
||||
@param c : `the character to check`
|
||||
@param [in] set : `The formatting string`
|
||||
@param [in] set : `String containing the characters`
|
||||
@pure
|
||||
@return `True if a character is in the set`
|
||||
*>
|
||||
@@ -172,7 +186,7 @@ fn String String.replace(self, Allocator allocator, String needle, String new_st
|
||||
@pool()
|
||||
{
|
||||
String[] split = self.tsplit(needle);
|
||||
return dstring::join(tmem, split, new_str).copy_str(mem);
|
||||
return dstring::join(tmem, split, new_str).copy_str(allocator);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -199,11 +213,28 @@ fn String String.treplace(self, String needle, String new_str)
|
||||
@pure
|
||||
@return `a substring of the string passed in`
|
||||
*>
|
||||
fn String String.trim(self, String to_trim = "\t\n\r ")
|
||||
fn String String.trim(self, String to_trim = " \n\t\r\f\v")
|
||||
{
|
||||
return self.trim_left(to_trim).trim_right(to_trim);
|
||||
}
|
||||
|
||||
<*
|
||||
Remove characters from the front and end of a string.
|
||||
|
||||
@param [in] self : `The string to trim`
|
||||
@param to_trim : `The set of characters to trim, defaults to whitespace`
|
||||
@pure
|
||||
@return `a substring of the string passed in`
|
||||
*>
|
||||
fn String String.trim_charset(self, AsciiCharset to_trim = ascii::WHITESPACE_SET)
|
||||
{
|
||||
usz start = 0;
|
||||
usz len = self.len;
|
||||
while (start < len && to_trim.contains(self[start])) start++;
|
||||
while (len > start && to_trim.contains(self[len - 1])) len--;
|
||||
return self[start..len - 1];
|
||||
}
|
||||
|
||||
<*
|
||||
Remove characters from the front of a string.
|
||||
|
||||
@@ -212,7 +243,7 @@ fn String String.trim(self, String to_trim = "\t\n\r ")
|
||||
@pure
|
||||
@return `a substring of the string passed in`
|
||||
*>
|
||||
fn String String.trim_left(self, String to_trim = "\t\n\r ")
|
||||
fn String String.trim_left(self, String to_trim = " \n\t\r\f\v")
|
||||
{
|
||||
usz start = 0;
|
||||
usz len = self.len;
|
||||
@@ -229,7 +260,7 @@ fn String String.trim_left(self, String to_trim = "\t\n\r ")
|
||||
@pure
|
||||
@return `a substring of the string passed in`
|
||||
*>
|
||||
fn String String.trim_right(self, String to_trim = "\t\n\r ")
|
||||
fn String String.trim_right(self, String to_trim = " \n\t\r\f\v")
|
||||
{
|
||||
usz len = self.len;
|
||||
while (len > 0 && char_in_set(self[len - 1], to_trim)) len--;
|
||||
@@ -412,6 +443,19 @@ fn bool String.contains(s, String substr)
|
||||
return @ok(s.index_of(substr));
|
||||
}
|
||||
|
||||
<*
|
||||
Check if a character is found in the string.
|
||||
|
||||
@param [in] s
|
||||
@param character : "The character to look for."
|
||||
@pure
|
||||
@return "true if the string contains the character, false otherwise"
|
||||
*>
|
||||
fn bool String.contains_char(s, char character)
|
||||
{
|
||||
return @ok(s.index_of_char(character));
|
||||
}
|
||||
|
||||
<*
|
||||
Check how many non-overlapping instances of a substring there is.
|
||||
|
||||
@@ -582,6 +626,7 @@ fn bool ZString.eq(self, ZString other) @operator(==)
|
||||
char* a = self;
|
||||
char* b = other;
|
||||
if (a == b) return true;
|
||||
if (!a || !b) return false;
|
||||
for (;; a++, b++)
|
||||
{
|
||||
char c = *a;
|
||||
@@ -608,12 +653,17 @@ fn usz ZString.char_len(str)
|
||||
|
||||
fn usz ZString.len(self)
|
||||
{
|
||||
usz len = 0;
|
||||
char* ptr = (char*)self;
|
||||
while (char c = ptr++[0]) len++;
|
||||
usz len;
|
||||
for (char* ptr = (char*)self; *ptr; ptr++) len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
fn usz WString.len(self)
|
||||
{
|
||||
usz len;
|
||||
for (Char16* ptr = (Char16*)self; *ptr; ptr++) len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
fn ZString String.zstr_copy(self, Allocator allocator)
|
||||
{
|
||||
@@ -756,6 +806,92 @@ fn String String.to_upper_copy(self, Allocator allocator)
|
||||
return copy;
|
||||
}
|
||||
|
||||
fn String String.capitalize_copy(self, Allocator allocator)
|
||||
{
|
||||
String s = self.copy(allocator);
|
||||
if (s.len > 0 && s[0].is_lower())
|
||||
{
|
||||
s[0] &= (char)~0x20;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
<*
|
||||
Convert a string from `snake_case` to PascalCase.
|
||||
|
||||
@param [in] self
|
||||
@return `"FooBar" from "foo_bar" the resulting pointer may safely be cast to ZString.`
|
||||
*>
|
||||
fn String String.snake_to_pascal_copy(self, Allocator allocator)
|
||||
{
|
||||
Splitter splitter = self.tokenize("_");
|
||||
char[] new_string = allocator::alloc_array(allocator, char, self.len + 1);
|
||||
usz index = 0;
|
||||
while (try s = splitter.next())
|
||||
{
|
||||
assert(s.len > 0);
|
||||
char c = s[0];
|
||||
if (c.is_lower()) c = c.to_upper();
|
||||
new_string[index++] = c;
|
||||
s = s[1..];
|
||||
new_string[index:s.len] = s[..];
|
||||
index += s.len;
|
||||
}
|
||||
new_string[index] = 0;
|
||||
return (String)new_string[:index];
|
||||
}
|
||||
|
||||
<*
|
||||
Movifies the current string from `snake_case` to PascalCase.
|
||||
|
||||
@param [inout] self
|
||||
*>
|
||||
fn void String.convert_snake_to_pascal(&self)
|
||||
{
|
||||
Splitter splitter = self.tokenize("_");
|
||||
String new_string = *self;
|
||||
usz index = 0;
|
||||
while (try s = splitter.next())
|
||||
{
|
||||
assert(s.len > 0);
|
||||
char c = s[0];
|
||||
if (c.is_lower()) c = c.to_upper();
|
||||
new_string[index++] = c;
|
||||
s = s[1..];
|
||||
new_string[index:s.len] = s[..];
|
||||
index += s.len;
|
||||
}
|
||||
*self = new_string[:index];
|
||||
}
|
||||
|
||||
<*
|
||||
Convert a string from `PascalCase` to `snake_case`.
|
||||
|
||||
@param [in] self
|
||||
@return `"foo_bar" from "FooBar" the resulting pointer may safely be cast to ZString.`
|
||||
*>
|
||||
fn String String.pascal_to_snake_copy(self, Allocator allocator) => @pool()
|
||||
{
|
||||
DString d;
|
||||
d.init(tmem, (usz)(self.len * 1.5));
|
||||
usz index = 0;
|
||||
foreach (i, c : self)
|
||||
{
|
||||
if (c.is_upper())
|
||||
{
|
||||
if (i > 0 && ((self[i - 1].is_lower() || self[i - 1].is_digit()) || (i < self.len - 1 && self[i + 1].is_lower())))
|
||||
{
|
||||
d.append_char('_');
|
||||
}
|
||||
d.append_char(c.to_lower());
|
||||
continue;
|
||||
}
|
||||
d.append_char(c);
|
||||
}
|
||||
return d.copy_str(allocator);
|
||||
}
|
||||
|
||||
|
||||
fn StringIterator String.iterator(self)
|
||||
{
|
||||
return { self, 0 };
|
||||
|
||||
233
lib/std/core/string_escape.c3
Normal file
233
lib/std/core/string_escape.c3
Normal file
@@ -0,0 +1,233 @@
|
||||
// Copyright (c) 2024 Christoffer Lerno. 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.
|
||||
|
||||
<*
|
||||
This module provides functionality for escaping and unescaping strings
|
||||
with standard C-style escape sequences, similar to what's used in JSON
|
||||
and other string literals.
|
||||
*>
|
||||
module std::core::string;
|
||||
import std::io;
|
||||
|
||||
faultdef INVALID_ESCAPE_SEQUENCE, UNTERMINATED_STRING, INVALID_HEX_ESCAPE, INVALID_UNICODE_ESCAPE;
|
||||
|
||||
<*
|
||||
Escape a string by adding quotes and converting special characters to escape sequences.
|
||||
|
||||
@param allocator : "The allocator to use for the result"
|
||||
@param s : "The string to escape"
|
||||
@param strip_quotes : "Do not include beginning and end quotes, defaults to false"
|
||||
@return "The escaped string with surrounding quotes, can safely be cast to ZString"
|
||||
*>
|
||||
fn String String.escape(String s, Allocator allocator, bool strip_quotes = true)
|
||||
{
|
||||
// Conservative allocation: most strings need minimal escaping
|
||||
usz initial_capacity = s.len + s.len / 5 + 2; // ~1.2x + quotes
|
||||
DString result = dstring::new_with_capacity(allocator, initial_capacity);
|
||||
|
||||
if (!strip_quotes) result.append_char('"');
|
||||
|
||||
foreach (char c : s)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '"': result.append(`\"`);
|
||||
case '\\': result.append(`\\`);
|
||||
case '\b': result.append(`\b`);
|
||||
case '\f': result.append(`\f`);
|
||||
case '\n': result.append(`\n`);
|
||||
case '\r': result.append(`\r`);
|
||||
case '\t': result.append(`\t`);
|
||||
case '\v': result.append(`\v`);
|
||||
case '\0': result.append(`\0`);
|
||||
default:
|
||||
if (c >= 32 && c <= 126)
|
||||
{
|
||||
// Printable ASCII
|
||||
result.append_char(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-printable, use hex escape
|
||||
result.appendf("\\x%02x", (uint)c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!strip_quotes) result.append_char('"');
|
||||
return result.copy_str(allocator);
|
||||
}
|
||||
|
||||
<*
|
||||
Escape a string using the temp allocator.
|
||||
|
||||
@param s : "The string to escape"
|
||||
@param strip_quotes : "Do not include beginning and end quotes, defaults to false"
|
||||
@return "The escaped string with surrounding quotes"
|
||||
*>
|
||||
fn String String.tescape(String s, bool strip_quotes = false) => s.escape(tmem, strip_quotes);
|
||||
|
||||
<*
|
||||
Calculate the length needed for an escaped string (including quotes).
|
||||
|
||||
@param s : "The string to check"
|
||||
@return "The length needed for the escaped version"
|
||||
*>
|
||||
fn usz escape_len(String s)
|
||||
{
|
||||
usz len = 2; // For quotes
|
||||
foreach (char c : s)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '"':
|
||||
case '\\':
|
||||
case '\b':
|
||||
case '\f':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '\v':
|
||||
case '\0':
|
||||
len += 2; // \X
|
||||
default:
|
||||
if (c >= 32 && c <= 126)
|
||||
{
|
||||
len += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
len += 4; // \xHH
|
||||
}
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
<*
|
||||
Unescape a quoted string by parsing escape sequences.
|
||||
|
||||
@param allocator : "The allocator to use for the result"
|
||||
@param s : "The quoted string to unescape"
|
||||
@param allow_unquoted : "Set to true to unescape strings not surrounded by quotes, defaults to false"
|
||||
@return "The unescaped string without quotes, safe to convert to ZString"
|
||||
@return? UNTERMINATED_STRING, INVALID_ESCAPE_SEQUENCE, INVALID_HEX_ESCAPE, INVALID_UNICODE_ESCAPE
|
||||
*>
|
||||
fn String? String.unescape(String s, Allocator allocator, bool allow_unquoted = false)
|
||||
{
|
||||
if (s.len >= 2 && s[0] == '"' && s[^1] == '"')
|
||||
{
|
||||
// Remove quotes.
|
||||
s = s[1:^2];
|
||||
}
|
||||
else if (!allow_unquoted) return UNTERMINATED_STRING?;
|
||||
|
||||
// Handle empty string case
|
||||
if (!s.len)
|
||||
{
|
||||
return "".copy(allocator);
|
||||
}
|
||||
|
||||
DString result = dstring::new_with_capacity(allocator, s.len);
|
||||
|
||||
usz len = s.len;
|
||||
for (usz i = 0; i < len; i++)
|
||||
{
|
||||
char c = s[i];
|
||||
if (c != '\\')
|
||||
{
|
||||
result.append_char(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle escape sequence
|
||||
if (i + 1 >= len) return INVALID_ESCAPE_SEQUENCE?;
|
||||
|
||||
char escape_char = s[++i];
|
||||
switch (escape_char)
|
||||
{
|
||||
case '"': result.append_char('"');
|
||||
case '\\': result.append_char('\\');
|
||||
case '/': result.append_char('/');
|
||||
case 'b': result.append_char('\b');
|
||||
case 'f': result.append_char('\f');
|
||||
case 'n': result.append_char('\n');
|
||||
case 'r': result.append_char('\r');
|
||||
case 't': result.append_char('\t');
|
||||
case 'v': result.append_char('\v');
|
||||
case '0': result.append_char('\0');
|
||||
case 'x':
|
||||
// Hex escape \xHH
|
||||
if (i + 2 >= len) return INVALID_HEX_ESCAPE?;
|
||||
char h1 = s[++i];
|
||||
char h2 = s[++i];
|
||||
if (!h1.is_xdigit() || !h2.is_xdigit()) return INVALID_HEX_ESCAPE?;
|
||||
uint val = h1 > '9' ? (h1 | 32) - 'a' + 10 : h1 - '0';
|
||||
val = val << 4;
|
||||
val += h2 > '9' ? (h2 | 32) - 'a' + 10 : h2 - '0';
|
||||
result.append_char((char)val);
|
||||
case 'u':
|
||||
// Unicode escape \uHHHH
|
||||
if (i + 4 >= len) return INVALID_UNICODE_ESCAPE?;
|
||||
uint val;
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
char hex_char = s[++i];
|
||||
if (!hex_char.is_xdigit()) return INVALID_UNICODE_ESCAPE?;
|
||||
val = val << 4 + (hex_char > '9' ? (hex_char | 32) - 'a' + 10 : hex_char - '0');
|
||||
}
|
||||
result.append_char32(val);
|
||||
case 'U':
|
||||
// Unicode escape \UHHHHHHHH
|
||||
if (i + 8 >= len) return INVALID_UNICODE_ESCAPE?;
|
||||
uint val;
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
char hex_char = s[++i];
|
||||
if (!hex_char.is_xdigit()) return INVALID_UNICODE_ESCAPE?;
|
||||
val = val << 4 + (hex_char > '9' ? (hex_char | 32) - 'a' + 10 : hex_char - '0');
|
||||
}
|
||||
result.append_char32(val);
|
||||
default:
|
||||
return INVALID_ESCAPE_SEQUENCE?;
|
||||
}
|
||||
}
|
||||
|
||||
return result.copy_str(allocator);
|
||||
}
|
||||
|
||||
<*
|
||||
Unescape a quoted string using the temp allocator.
|
||||
|
||||
@param s : "The quoted string to unescape"
|
||||
@param allow_unquoted : "Set to true to unescape strings not surrounded by quotes, defaults to false"
|
||||
@return "The unescaped string without quotes"
|
||||
@return? UNTERMINATED_STRING, INVALID_ESCAPE_SEQUENCE, INVALID_HEX_ESCAPE, INVALID_UNICODE_ESCAPE
|
||||
*>
|
||||
fn String? String.tunescape(String s, bool allow_unquoted = false) => s.unescape(tmem, allow_unquoted);
|
||||
|
||||
<*
|
||||
Check if a character needs to be escaped in a string literal.
|
||||
|
||||
@param c : "The character to check"
|
||||
@return "True if the character needs escaping"
|
||||
*>
|
||||
fn bool needs_escape(char c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '"':
|
||||
case '\\':
|
||||
case '\b':
|
||||
case '\f':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '\v':
|
||||
case '\0':
|
||||
return true;
|
||||
default:
|
||||
return c < 32 || c > 126;
|
||||
}
|
||||
}
|
||||
@@ -462,7 +462,7 @@ macro String.to_real(chars, $Type) @private
|
||||
$error "Unexpected type";
|
||||
$endswitch
|
||||
|
||||
while (chars.len && chars[0] == ' ') chars = chars[1..];
|
||||
chars = chars.trim();
|
||||
if (!chars.len) return MALFORMED_FLOAT?;
|
||||
|
||||
if (chars.len != 1)
|
||||
@@ -476,6 +476,9 @@ macro String.to_real(chars, $Type) @private
|
||||
chars = chars[1..];
|
||||
}
|
||||
}
|
||||
chars = chars.trim();
|
||||
if (!chars.len) return MALFORMED_FLOAT?;
|
||||
|
||||
if (chars == "infinity" || chars == "INFINITY") return sign * $Type.inf;
|
||||
if (chars == "NAN" || chars == "nan") return $Type.nan;
|
||||
|
||||
|
||||
@@ -339,6 +339,8 @@ macro bool is_same_vector_type($Type1, $Type2) @const
|
||||
$endif
|
||||
}
|
||||
|
||||
macro bool has_equals($Type) @const => $defined(($Type){} == ($Type){});
|
||||
|
||||
macro bool is_equatable_type($Type) @const
|
||||
{
|
||||
$if $defined($Type.less) || $defined($Type.compare_to) || $defined($Type.equals):
|
||||
|
||||
@@ -2,13 +2,11 @@ module std::core::values;
|
||||
import std::core::types;
|
||||
|
||||
|
||||
macro typeid @typeid(#value) @const @builtin => $typeof(#value).typeid;
|
||||
macro TypeKind @typekind(#value) @const @builtin => $typeof(#value).kindof;
|
||||
macro bool @typeis(#value, $Type) @const @builtin => $typeof(#value).typeid == $Type.typeid;
|
||||
macro bool @typematch(#value1, #value2) @builtin @const => $typeof(#value1) == $typeof(#value2);
|
||||
<*
|
||||
Return true if two values have the same type before any conversions.
|
||||
*>
|
||||
macro bool @is_same_type(#value1, #value2) @const => $typeof(#value1).typeid == $typeof(#value2).typeid;
|
||||
macro bool @is_same_type(#value1, #value2) @const @deprecated("Use @typematch") => $typeof(#value1).typeid == $typeof(#value2).typeid;
|
||||
macro bool @is_bool(#value) @const => types::is_bool($typeof(#value));
|
||||
macro bool @is_int(#value) @const => types::is_int($typeof(#value));
|
||||
macro bool @is_flat_intlike(#value) @const => types::is_flat_intlike($typeof(#value));
|
||||
@@ -18,8 +16,13 @@ macro bool @is_promotable_to_floatlike(#value) @const => types::is_promotable_to
|
||||
macro bool @is_promotable_to_float(#value) @const => types::is_promotable_to_float($typeof(#value));
|
||||
macro bool @is_vector(#value) @const => types::is_vector($typeof(#value));
|
||||
macro bool @is_same_vector_type(#value1, #value2) @const => types::is_same_vector_type($typeof(#value1), $typeof(#value2));
|
||||
macro bool @assign_to(#value1, #value2) @const => $assignable(#value1, $typeof(#value2));
|
||||
macro bool @assign_to(#value1, #value2) @const => @assignable_to(#value1, $typeof(#value2));
|
||||
macro bool @is_lvalue(#value) => $defined(#value = #value);
|
||||
macro bool @is_const(#foo) @const @builtin
|
||||
{
|
||||
var $v;
|
||||
return $defined($v = #foo);
|
||||
}
|
||||
|
||||
macro promote_int(x)
|
||||
{
|
||||
@@ -49,6 +52,7 @@ macro @select(bool $bool, #value_1, #value_2) @builtin
|
||||
return #value_2;
|
||||
$endif
|
||||
}
|
||||
|
||||
macro promote_int_same(x, y)
|
||||
{
|
||||
$if @is_int(x):
|
||||
|
||||
736
lib/std/crypto/ed25519.c3
Normal file
736
lib/std/crypto/ed25519.c3
Normal file
@@ -0,0 +1,736 @@
|
||||
/*
|
||||
Ed25519 Digital Signature Algorithm
|
||||
*/
|
||||
|
||||
module std::crypto::ed25519;
|
||||
import std::hash::sha512;
|
||||
|
||||
alias Ed25519PrivateKey = char[32];
|
||||
alias Ed25519PublicKey = char[Ed25519PrivateKey.len];
|
||||
alias Ed25519Signature = char[2 * Ed25519PublicKey.len];
|
||||
|
||||
<*
|
||||
Generate a public key from a private key.
|
||||
|
||||
@param [in] private_key : "32 bytes of cryptographically secure random data"
|
||||
@require private_key.len == Ed25519PrivateKey.len
|
||||
*>
|
||||
fn Ed25519PublicKey public_keygen(char[] private_key)
|
||||
{
|
||||
return pack(&&unproject(&&(BASE * expand_private_key(private_key)[:FBaseInt.len])));
|
||||
}
|
||||
|
||||
<*
|
||||
Sign a message.
|
||||
|
||||
@param [in] message
|
||||
@param [in] private_key
|
||||
@param [in] public_key
|
||||
@require private_key.len == Ed25519PrivateKey.len
|
||||
@require public_key.len == Ed25519PublicKey.len
|
||||
*>
|
||||
fn Ed25519Signature sign(char[] message, char[] private_key, char[] public_key)
|
||||
{
|
||||
Ed25519Signature r @noinit;
|
||||
|
||||
char[*] exp = expand_private_key(private_key);
|
||||
|
||||
Sha512 sha @noinit;
|
||||
sha.init();
|
||||
|
||||
sha.update(exp[FBaseInt.len..]);
|
||||
sha.update(message);
|
||||
|
||||
FBaseInt k = from_bytes(&&sha.final());
|
||||
|
||||
r[:F25519Int.len] = pack(&&unproject(&&(BASE * k[..])))[..];
|
||||
|
||||
sha.init();
|
||||
|
||||
sha.update(r[:F25519Int.len]);
|
||||
sha.update(public_key);
|
||||
sha.update(message);
|
||||
|
||||
FBaseInt z = from_bytes(&&sha.final());
|
||||
FBaseInt e = from_bytes(exp[:FBaseInt.len]);
|
||||
|
||||
r[F25519Int.len..] = (z * e + k)[..];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
<*
|
||||
Verify the signature of a message.
|
||||
|
||||
@param [in] message
|
||||
@param [in] signature
|
||||
@param [in] public_key
|
||||
@require signature.len == Ed25519Signature.len
|
||||
@require public_key.len == Ed25519PublicKey.len
|
||||
*>
|
||||
fn bool verify(char[] message, char[] signature, char[] public_key)
|
||||
{
|
||||
char ok = 1;
|
||||
|
||||
F25519Int lhs = pack(&&unproject(&&(BASE * signature[F25519Int.len..])));
|
||||
|
||||
Unpacking unp_p = unpack_on_curve((F25519Int*)public_key);
|
||||
Projection p = project(&unp_p.point);
|
||||
ok &= unp_p.on_curve;
|
||||
|
||||
Sha512 sha @noinit;
|
||||
sha.init();
|
||||
|
||||
sha.update(signature[:F25519Int.len]);
|
||||
sha.update(public_key);
|
||||
sha.update(message);
|
||||
|
||||
FBaseInt z = from_bytes(&&sha.final());
|
||||
|
||||
p = p * z[..];
|
||||
|
||||
Unpacking unp_q = unpack_on_curve((F25519Int*)signature[:F25519Int.len]);
|
||||
Projection q = project(&unp_q.point);
|
||||
ok &= unp_q.on_curve;
|
||||
|
||||
p = p + q;
|
||||
|
||||
F25519Int rhs = pack(&&unproject(&p));
|
||||
|
||||
return (bool)(ok & eq(&lhs, &rhs));
|
||||
}
|
||||
|
||||
// Base point for Ed25519. Generate a subgroup of order 2^252+0x14def9dea2f79cd65812631a5cf5d3ed
|
||||
const Projection BASE @private =
|
||||
{
|
||||
x"1ad5258f602d56c9 b2a7259560c72c69 5cdcd6fd31e2a4c0 fe536ecdd3366921",
|
||||
x"5866666666666666 6666666666666666 6666666666666666 6666666666666666",
|
||||
x"a3ddb7a5b38ade6d f5525177809ff020 7de3ab648e4eea66 65768bd70f5f8767",
|
||||
ONE
|
||||
};
|
||||
|
||||
<*
|
||||
Compute the pruned SHA-512 hash of a private key.
|
||||
|
||||
@param [in] private_key
|
||||
@require private_key.len == Ed25519PrivateKey.len
|
||||
*>
|
||||
fn char[sha512::HASH_SIZE] expand_private_key(char[] private_key) @local
|
||||
{
|
||||
char[*] r = sha512::hash(private_key);
|
||||
|
||||
r[0] &= 0b11111000;
|
||||
r[FBaseInt.len - 1] &= 0b01111111;
|
||||
r[FBaseInt.len - 1] |= 0b01000000;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Operations on the twisted Edwards curve -x^2+y^2=1-121665/121666*x^2*y^2 over the prime field F_(2^255-19) (edwards25519)
|
||||
The set of F_(2^255-19)-rational curve points is a group of order 2^3*(2^252+0x14def9dea2f79cd65812631a5cf5d3ed)
|
||||
*/
|
||||
|
||||
module std::crypto::ed25519 @private;
|
||||
|
||||
// Affine coordinates.
|
||||
struct Point
|
||||
{
|
||||
F25519Int x;
|
||||
F25519Int y;
|
||||
}
|
||||
|
||||
// Projective coordinates.
|
||||
struct Projection
|
||||
{
|
||||
F25519Int x;
|
||||
F25519Int y;
|
||||
F25519Int t;
|
||||
F25519Int z;
|
||||
}
|
||||
|
||||
// Neutral.
|
||||
const Projection NEUTRAL =
|
||||
{
|
||||
ZERO,
|
||||
ONE,
|
||||
ZERO,
|
||||
ONE
|
||||
};
|
||||
|
||||
<*
|
||||
Convert affine to projective coordinates.
|
||||
|
||||
@param [&in] p
|
||||
*>
|
||||
fn Projection project(Point* p) => { p.x, p.y, p.x * p.y, ONE };
|
||||
|
||||
<*
|
||||
Convert projective to affine coordinates.
|
||||
|
||||
@param [&in] p
|
||||
*>
|
||||
fn Point unproject(Projection* p)
|
||||
{
|
||||
Point r @noinit;
|
||||
|
||||
F25519Int inv = p.z.inv();
|
||||
r.x = p.x * inv;
|
||||
r.y = p.y * inv;
|
||||
|
||||
r.x.normalize();
|
||||
r.y.normalize();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
// d parameter for edwards25519 : -121665/121666
|
||||
const F25519Int D = x"a3785913ca4deb75 abd841414d0a7000 98e879777940c78c 73fe6f2bee6c0352";
|
||||
|
||||
// 2*d
|
||||
const F25519Int DD = x"59f1b226949bd6eb 56b183829a14e000 30d1f3eef2808e19 e7fcdf56dcd90624";
|
||||
|
||||
<*
|
||||
Compress a point.
|
||||
|
||||
@param [&in] p
|
||||
*>
|
||||
fn F25519Int pack(Point* p)
|
||||
{
|
||||
Point r = *p;
|
||||
|
||||
r.x.normalize();
|
||||
r.y.normalize();
|
||||
|
||||
r.y[^1] |= (r.x[0] & 1) << 7;
|
||||
|
||||
return r.y;
|
||||
}
|
||||
|
||||
struct Unpacking
|
||||
{
|
||||
Point point;
|
||||
char on_curve; // Non-zero if true.
|
||||
}
|
||||
|
||||
<*
|
||||
Uncompress a point. Check if it is on the curve.
|
||||
|
||||
@param [&in] encoding
|
||||
*>
|
||||
fn Unpacking unpack_on_curve(F25519Int* encoding)
|
||||
{
|
||||
Point p @noinit;
|
||||
|
||||
char parity = (*encoding)[^1] >> 7;
|
||||
|
||||
p.y = *encoding;
|
||||
p.y[^1] &= 0b01111111;
|
||||
|
||||
F25519Int y2 = p.y * p.y;
|
||||
F25519Int x2 = (D * y2 + ONE).inv() * (y2 - ONE);
|
||||
|
||||
F25519Int x = x2.sqrt();
|
||||
|
||||
p.x = f25519_select(&x, &&-x, (x[0] ^ parity) & 1);
|
||||
|
||||
F25519Int _x2 = p.x * p.x;
|
||||
|
||||
x2.normalize();
|
||||
_x2.normalize();
|
||||
|
||||
return {p, eq(&x2, &_x2)};
|
||||
}
|
||||
|
||||
macro Projection Projection.@add(&s, Projection #p) @operator(+) => s.add(@addr(#p));
|
||||
<*
|
||||
Addition.
|
||||
|
||||
@param [&in] s
|
||||
*>
|
||||
fn Projection Projection.add(&s, Projection* p) @operator(+)
|
||||
{
|
||||
Projection r @noinit;
|
||||
|
||||
F25519Int a = (s.y - s.x) * (p.y - p.x);
|
||||
F25519Int b = (s.y + s.x) * (p.y + p.x);
|
||||
F25519Int c = s.t * DD * p.t;
|
||||
F25519Int d = (s.z * p.z).mul_s(2);
|
||||
F25519Int e = b - a;
|
||||
F25519Int f = d - c;
|
||||
F25519Int g = d + c;
|
||||
F25519Int h = b + a;
|
||||
|
||||
r.x = e * f;
|
||||
r.y = g * h;
|
||||
r.t = e * h;
|
||||
r.z = f * g;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
<*
|
||||
Double a point.
|
||||
|
||||
@param [&in] s
|
||||
*>
|
||||
fn Projection Projection.twice(&s)
|
||||
{
|
||||
Projection r @noinit;
|
||||
|
||||
F25519Int a = s.x * s.x;
|
||||
F25519Int b = s.y * s.y;
|
||||
F25519Int c = (s.z * s.z).mul_s(2);
|
||||
F25519Int d = s.x + s.y;
|
||||
F25519Int e = d * d - a - b;
|
||||
F25519Int g = b - a;
|
||||
F25519Int f = g - c;
|
||||
F25519Int h = -b - a;
|
||||
|
||||
r.x = e * f;
|
||||
r.y = g * h;
|
||||
r.t = e * h;
|
||||
r.z = f * g;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
<*
|
||||
Variable base scalar multiplication.
|
||||
|
||||
@param [&in] s
|
||||
@param [in] n
|
||||
*>
|
||||
fn Projection Projection.mul(&s, char[] n) @operator(*)
|
||||
{
|
||||
Projection r = NEUTRAL;
|
||||
|
||||
for (isz i = n.len << 3 - 1; i >= 0; i--)
|
||||
{
|
||||
r = r.twice();
|
||||
|
||||
Projection t = r + s;
|
||||
|
||||
char bit = n[i >> 3] >> (i & 7) & 1;
|
||||
r.x = f25519_select(&r.x, &t.x, bit);
|
||||
r.y = f25519_select(&r.y, &t.y, bit);
|
||||
r.z = f25519_select(&r.z, &t.z, bit);
|
||||
r.t = f25519_select(&r.t, &t.t, bit);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Modular arithmetic over the prime field F_(2^255-19)
|
||||
*/
|
||||
|
||||
module std::crypto::ed25519 @private;
|
||||
|
||||
typedef F25519Int = inline char[32];
|
||||
|
||||
const F25519Int ZERO = {};
|
||||
const F25519Int ONE = {[0] = 1};
|
||||
|
||||
<*
|
||||
Reduce an element with carry to at most 2^255+18 (32 bytes)
|
||||
|
||||
@param [&inout] s
|
||||
*>
|
||||
fn void F25519Int.reduce_carry(&s, uint carry)
|
||||
{
|
||||
// Reduce using 2^255 = 19 mod p
|
||||
(*s)[^1] &= 0b01111111;
|
||||
|
||||
carry *= 19;
|
||||
|
||||
foreach (i, &v : s)
|
||||
{
|
||||
carry += *v;
|
||||
*v = (char)carry;
|
||||
carry >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Reduce an element to at most 2^255-19
|
||||
|
||||
@param [&inout] s
|
||||
*>
|
||||
fn void F25519Int.normalize(&s)
|
||||
{
|
||||
s.reduce_carry((*s)[^1] >> 7);
|
||||
|
||||
// Substract p
|
||||
F25519Int sub @noinit;
|
||||
ushort c = 19;
|
||||
foreach (i, v : (*s)[:^1])
|
||||
{
|
||||
c += v;
|
||||
sub[i] = (char)c;
|
||||
c >>= 8;
|
||||
}
|
||||
c += (*s)[^1] - 0b10000000;
|
||||
sub[^1] = (char)c;
|
||||
|
||||
*s = f25519_select(&sub, s, (char)(c >> 15));
|
||||
}
|
||||
|
||||
<*
|
||||
Constant-time equality comparison. Return is non-zero if true.
|
||||
|
||||
@param [&in] a
|
||||
@param [&in] b
|
||||
*>
|
||||
fn char eq(F25519Int* a, F25519Int* b)
|
||||
{
|
||||
char e;
|
||||
foreach (i, v : a) e |= v ^ (*b)[i];
|
||||
|
||||
e |= (e >> 4);
|
||||
e |= (e >> 2);
|
||||
e |= (e >> 1);
|
||||
|
||||
return e ^ 1;
|
||||
}
|
||||
|
||||
<*
|
||||
Constant-time conditonal 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"
|
||||
*>
|
||||
fn F25519Int f25519_select(F25519Int* zero, F25519Int* one, char condition)
|
||||
{
|
||||
F25519Int r @noinit;
|
||||
|
||||
foreach (i, z : zero) r[i] = z ^ (-condition & ((*one)[i] ^ z));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
macro F25519Int F25519Int.@add(&s, F25519Int #n) @operator(+) => s.add(@addr(#n));
|
||||
|
||||
<*
|
||||
Addition.
|
||||
|
||||
@param [&in] s
|
||||
@param [&in] n
|
||||
*>
|
||||
fn F25519Int F25519Int.add(&s, F25519Int* n) @operator(+)
|
||||
{
|
||||
F25519Int r @noinit;
|
||||
|
||||
ushort c;
|
||||
foreach (i, v : s)
|
||||
{
|
||||
c >>= 8;
|
||||
c += v + (*n)[i];
|
||||
r[i] = (char)c;
|
||||
}
|
||||
|
||||
r.reduce_carry(c >> 7);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
macro F25519Int F25519Int.@sub(&s, F25519Int #n) @operator(-) => s.sub(@addr(#n));
|
||||
|
||||
<*
|
||||
Substraction.
|
||||
|
||||
@param [&in] s
|
||||
@param [&in] n
|
||||
*>
|
||||
fn F25519Int F25519Int.sub(&s, F25519Int* n) @operator(-)
|
||||
{
|
||||
// Compute s+2*p-n instead of s-n to avoid underflow.
|
||||
F25519Int r @noinit;
|
||||
|
||||
uint c = (char)~(2 * 19 - 1);
|
||||
foreach (i, v : (*s)[:^1])
|
||||
{
|
||||
c += 0b11111111_00000000 + v - (*n)[i];
|
||||
r[i] = (char)c;
|
||||
c >>= 8;
|
||||
}
|
||||
c += (*s)[^1] - (*n)[^1];
|
||||
r[^1] = (char)c;
|
||||
|
||||
r.reduce_carry(c >> 7);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
<*
|
||||
Negation.
|
||||
|
||||
@param [&in] s
|
||||
*>
|
||||
fn F25519Int F25519Int.neg(&s) @operator(-)
|
||||
{
|
||||
// Compute 2*p-s instead of -s to avoid underflow.
|
||||
F25519Int r @noinit;
|
||||
|
||||
uint c = (char)~(2 * 19 - 1);
|
||||
foreach (i, v : (*s)[:^1])
|
||||
{
|
||||
c += 0b11111111_00000000 - v;
|
||||
r[i] = (char)c;
|
||||
c >>= 8;
|
||||
}
|
||||
c -= (*s)[^1];
|
||||
r[^1] = (char)c;
|
||||
|
||||
r.reduce_carry(c >> 7);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
macro F25519Int F25519Int.@mul(&s, F25519Int #n) @operator(*) => s.mul(@addr(#n));
|
||||
|
||||
<*
|
||||
Multiplication.
|
||||
|
||||
@param [&in] s
|
||||
@param [&in] n
|
||||
*>
|
||||
fn F25519Int F25519Int.mul(&s, F25519Int* n) @operator(*)
|
||||
{
|
||||
F25519Int r @noinit;
|
||||
|
||||
uint c;
|
||||
for (usz i = 0; i < F25519Int.len; i++)
|
||||
{
|
||||
c >>= 8;
|
||||
for (usz j; j <= i; j++) c += (*s)[j] * (*n)[i - j];
|
||||
// Reduce using 2^256 = 2*19 mod p
|
||||
for (usz j = i + 1; j < F25519Int.len; j++) c += (*s)[j] * (*n)[^j - i] * 2 * 19;
|
||||
r[i] = (char)c;
|
||||
}
|
||||
|
||||
r.reduce_carry(c >> 7);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
<*
|
||||
Multiplication by a small element.
|
||||
|
||||
@param [&in] s
|
||||
*>
|
||||
fn F25519Int F25519Int.mul_s(&s, uint n)
|
||||
{
|
||||
F25519Int r @noinit;
|
||||
|
||||
uint c;
|
||||
foreach (i, v : s)
|
||||
{
|
||||
c >>= 8;
|
||||
c += v * n;
|
||||
r[i] = (char)c;
|
||||
}
|
||||
|
||||
r.reduce_carry(c >> 7);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
<*
|
||||
Inverse an element.
|
||||
|
||||
@param [&in] s
|
||||
*>
|
||||
fn F25519Int F25519Int.inv(&s)
|
||||
{
|
||||
//Compute s^(p-2)
|
||||
F25519Int r = *s;
|
||||
|
||||
for (usz i; i < 255 - 1 - 5; i++) r = r * r * s;
|
||||
|
||||
r *= r;
|
||||
r = r * r * s;
|
||||
r *= r;
|
||||
r = r * r * s;
|
||||
r = r * r * s;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
<*
|
||||
Raise an element to the power of 2^252-3
|
||||
|
||||
@param [&in] s
|
||||
*>
|
||||
fn F25519Int F25519Int.pow_2523(&s) @local
|
||||
{
|
||||
F25519Int r = *s;
|
||||
|
||||
for (usz i; i < 252 - 1 - 2; i++) r = r * r * s;
|
||||
|
||||
r *= r;
|
||||
r = r * r * s;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
<*
|
||||
Compute the square root of an element.
|
||||
|
||||
@param [&in] s
|
||||
*>
|
||||
fn F25519Int F25519Int.sqrt(&s)
|
||||
{
|
||||
F25519Int twice = s.mul_s(2);
|
||||
F25519Int pow = twice.pow_2523();
|
||||
|
||||
return (twice * pow * pow - ONE) * s * pow;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Modular arithmetic over the prime field F_(2^252+0x14def9dea2f79cd65812631a5cf5d3ed)
|
||||
*/
|
||||
|
||||
module std::crypto::ed25519 @private;
|
||||
import std::math;
|
||||
|
||||
typedef FBaseInt = inline char[32];
|
||||
|
||||
// Order of the field : 2^252+0x14def9dea2f79cd65812631a5cf5d3ed
|
||||
const FBaseInt ORDER = x"edd3f55c1a631258 d69cf7a2def9de14 0000000000000000 0000000000000010";
|
||||
|
||||
<*
|
||||
Interpret bytes as a normalized element.
|
||||
|
||||
@param [in] bytes
|
||||
*>
|
||||
fn FBaseInt from_bytes(char[] bytes)
|
||||
{
|
||||
FBaseInt r;
|
||||
|
||||
usz bitc = min(252 - 1, bytes.len << 3);
|
||||
usz bytec = bitc >> 3;
|
||||
usz mod = bitc & 7;
|
||||
usz rem = bytes.len << 3 - bitc;
|
||||
|
||||
r[:bytec] = bytes[^bytec..];
|
||||
|
||||
if (mod)
|
||||
{
|
||||
r <<= mod;
|
||||
r[0] |= bytes[^bytec + 1] >> (8 - mod);
|
||||
}
|
||||
|
||||
for (isz i = rem - 1; i >= 0; i--)
|
||||
{
|
||||
r <<= 1;
|
||||
r[0] |= bytes[i >> 3] >> (i & 7) & 1;
|
||||
r = r.sub_l(&ORDER);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
<*
|
||||
Constant-time conditonal 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"
|
||||
*>
|
||||
fn FBaseInt fbase_select(FBaseInt* zero, FBaseInt* one, char condition)
|
||||
{
|
||||
FBaseInt r @noinit;
|
||||
|
||||
foreach (i, z : zero) r[i] = z ^ (-condition & ((*one)[i] ^ z));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
macro FBaseInt FBaseInt.@add(&s, FBaseInt #n) @operator(+) => s.add(@addr(#n));
|
||||
|
||||
<*
|
||||
Addition.
|
||||
|
||||
@param [&in] s
|
||||
@param [&in] n
|
||||
*>
|
||||
fn FBaseInt FBaseInt.add(&s, FBaseInt* n) @operator(+)
|
||||
{
|
||||
FBaseInt r @noinit;
|
||||
|
||||
ushort c;
|
||||
foreach (i, v : s)
|
||||
{
|
||||
c += v + (*n)[i];
|
||||
r[i] = (char)c;
|
||||
c >>= 8;
|
||||
}
|
||||
|
||||
return r.sub_l(&ORDER);
|
||||
}
|
||||
|
||||
<*
|
||||
Substraction if RHS is less than LHS else identity.
|
||||
|
||||
@param [&in] s
|
||||
@param [&in] n
|
||||
*>
|
||||
fn FBaseInt FBaseInt.sub_l(&s, FBaseInt* n)
|
||||
{
|
||||
FBaseInt sub @noinit;
|
||||
ushort c;
|
||||
foreach (i, v : s)
|
||||
{
|
||||
c = v - (*n)[i] - c;
|
||||
sub[i] = (char)c;
|
||||
c = (c >> 8) & 1;
|
||||
}
|
||||
|
||||
return fbase_select(&sub, s, (char)c);
|
||||
}
|
||||
|
||||
<*
|
||||
Left shift.
|
||||
|
||||
@param [&in] s
|
||||
*>
|
||||
fn FBaseInt FBaseInt.shl(&s, usz n) @operator(<<)
|
||||
{
|
||||
FBaseInt r @noinit;
|
||||
|
||||
ushort c;
|
||||
foreach (i, v : s)
|
||||
{
|
||||
c |= v << n;
|
||||
r[i] = (char)c;
|
||||
c >>= 8;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
macro FBaseInt FBaseInt.@mul(&s, FBaseInt #n) @operator(*) => s.mul(@addr(#n));
|
||||
<*
|
||||
Multiplication.
|
||||
|
||||
@param [&in] s
|
||||
@param [&in] n
|
||||
*>
|
||||
fn FBaseInt FBaseInt.mul(&s, FBaseInt* n) @operator(*)
|
||||
{
|
||||
FBaseInt r;
|
||||
|
||||
for (isz i = 252; i >= 0; i--)
|
||||
{
|
||||
r = (r << 1).sub_l(&ORDER);
|
||||
r = fbase_select(&r, &&(r + s), (*n)[i >> 3] >> (i & 7) & 1);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -5,7 +5,9 @@ module std::encoding::json;
|
||||
import std::io;
|
||||
import std::collections::object;
|
||||
|
||||
faultdef UNEXPECTED_CHARACTER, INVALID_ESCAPE_SEQUENCE, DUPLICATE_MEMBERS, INVALID_NUMBER;
|
||||
faultdef UNEXPECTED_CHARACTER, INVALID_ESCAPE_SEQUENCE, INVALID_NUMBER, MAX_DEPTH_REACHED;
|
||||
|
||||
int max_depth = 128;
|
||||
|
||||
fn Object*? parse_string(Allocator allocator, String s)
|
||||
{
|
||||
@@ -24,7 +26,7 @@ fn Object*? parse(Allocator allocator, InStream s)
|
||||
JsonContext context = { .last_string = dstring::new_with_capacity(smem, 64), .stream = s, .allocator = allocator };
|
||||
@pool()
|
||||
{
|
||||
return parse_any(&context);
|
||||
return parse_any(&context)!;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -62,11 +64,14 @@ struct JsonContext @local
|
||||
DString last_string;
|
||||
double last_number;
|
||||
char current;
|
||||
bitstruct : char {
|
||||
int depth;
|
||||
bitstruct : char
|
||||
{
|
||||
bool skip_comments;
|
||||
bool reached_end;
|
||||
bool pushed_back;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -105,10 +110,16 @@ fn JsonTokenType? lex_number(JsonContext *context, char c) @local
|
||||
t.append(c);
|
||||
c = read_next(context)!;
|
||||
}
|
||||
bool leading_zero = c == '0';
|
||||
while (c.is_digit())
|
||||
{
|
||||
t.append(c);
|
||||
c = read_next(context)!;
|
||||
if (leading_zero)
|
||||
{
|
||||
if (c.is_digit()) return INVALID_NUMBER?;
|
||||
leading_zero = false;
|
||||
}
|
||||
}
|
||||
if (c == '.')
|
||||
{
|
||||
@@ -148,6 +159,8 @@ fn Object*? parse_map(JsonContext* context) @local
|
||||
Object* map = object::new_obj(context.allocator);
|
||||
defer catch map.free();
|
||||
JsonTokenType token = advance(context)!;
|
||||
defer context.depth--;
|
||||
if (++context.depth >= max_depth) return json::MAX_DEPTH_REACHED?;
|
||||
|
||||
@stack_mem(256; Allocator mem)
|
||||
{
|
||||
@@ -156,7 +169,6 @@ fn Object*? parse_map(JsonContext* context) @local
|
||||
{
|
||||
if (token != JsonTokenType.STRING) return UNEXPECTED_CHARACTER?;
|
||||
DString string = context.last_string;
|
||||
if (map.has_key(string.str_view())) return DUPLICATE_MEMBERS?;
|
||||
// Copy the key to our temp holder, since our
|
||||
// last_string may be used in parse_any
|
||||
temp_key.clear();
|
||||
@@ -180,6 +192,8 @@ fn Object*? parse_array(JsonContext* context) @local
|
||||
{
|
||||
Object* list = object::new_obj(context.allocator);
|
||||
defer catch list.free();
|
||||
defer context.depth--;
|
||||
if (++context.depth >= max_depth) return json::MAX_DEPTH_REACHED?;
|
||||
JsonTokenType token = advance(context)!;
|
||||
while (token != JsonTokenType.RBRACKET)
|
||||
{
|
||||
@@ -248,7 +262,7 @@ fn JsonTokenType? advance(JsonContext* context) @local
|
||||
case '\v':
|
||||
continue;
|
||||
case '/':
|
||||
if (!context.skip_comments) break;
|
||||
if (!context.skip_comments) break WS;
|
||||
c = read_next(context)!;
|
||||
if (c != '*')
|
||||
{
|
||||
|
||||
96
lib/std/hash/a5hash.c3
Normal file
96
lib/std/hash/a5hash.c3
Normal file
@@ -0,0 +1,96 @@
|
||||
// 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.
|
||||
//
|
||||
// An implementation of Aleksey Vaneev's a5hash, version 5.16, in C3:
|
||||
// https://github.com/avaneev/komihash
|
||||
//
|
||||
// The license for komihash from the above repository at the time of writing is as follows:
|
||||
//
|
||||
// >> MIT License
|
||||
// >>
|
||||
// >> Copyright (c) 2025 Aleksey Vaneev
|
||||
// >>
|
||||
// >> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// >> of this software and associated documentation files (the "Software"), to deal
|
||||
// >> in the Software without restriction, including without limitation the rights
|
||||
// >> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// >> copies of the Software, and to permit persons to whom the Software is
|
||||
// >> furnished to do so, subject to the following conditions:
|
||||
// >>
|
||||
// >> The above copyright notice and this permission notice shall be included in all
|
||||
// >> copies or substantial portions of the Software.
|
||||
// >>
|
||||
// >> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// >> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// >> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// >> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// >> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// >> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// >> SOFTWARE.
|
||||
//
|
||||
//
|
||||
module std::hash::a5hash;
|
||||
|
||||
|
||||
macro @a5mul(#u, #v, #lo, #hi) @local
|
||||
{
|
||||
uint128 imd = (uint128)#u * (uint128)#v;
|
||||
#lo = (ulong)imd;
|
||||
#hi = (ulong)(imd >> 64);
|
||||
}
|
||||
|
||||
|
||||
fn ulong hash(char[] data, ulong seed = 0)
|
||||
{
|
||||
ulong seed1 = 0x243F_6A88_85A3_08D3 ^ data.len;
|
||||
ulong seed2 = 0x4528_21E6_38D0_1377 ^ data.len;
|
||||
ulong val10 = 0xAAAA_AAAA_AAAA_AAAA;
|
||||
ulong val01 = 0x5555_5555_5555_5555;
|
||||
ulong a, b;
|
||||
|
||||
@a5mul(seed2 ^ (seed & val10), seed1 ^ (seed & val01), seed1, seed2);
|
||||
|
||||
val10 ^= seed2;
|
||||
|
||||
if (@likely(data.len > 3))
|
||||
{
|
||||
if (data.len > 16)
|
||||
{
|
||||
val01 ^= seed1;
|
||||
|
||||
for (; data.len > 16; data = data[16..])
|
||||
{
|
||||
@a5mul(
|
||||
@unaligned_load(((ulong*)data.ptr)[0], 1) ^ seed1,
|
||||
@unaligned_load(((ulong*)data.ptr)[1], 1) ^ seed2,
|
||||
seed1, seed2
|
||||
);
|
||||
|
||||
seed1 += val01;
|
||||
seed2 += val10;
|
||||
}
|
||||
|
||||
a = @unaligned_load(*(ulong*)(data.ptr + (uptr)data.len - 16), 1);
|
||||
b = @unaligned_load(*(ulong*)(data.ptr + (uptr)data.len - 8), 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
a = ((ulong)@unaligned_load(*(uint*)&data[0], 1) << 32)
|
||||
| @unaligned_load(*(uint*)&data[^4], 1);
|
||||
|
||||
b = ((ulong)@unaligned_load(*(uint*)&data[(data.len >> 3) * 4], 1) << 32)
|
||||
| @unaligned_load(*(uint*)(data.ptr + data.len - 4 - (data.len >> 3) * 4), 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
a = data.len ? (data[0] | (data.len > 1 ? ((ulong)data[1] << 8) : 0) | (data.len > 2 ? ((ulong)data[2] << 16) : 0)) : 0;
|
||||
b = 0;
|
||||
}
|
||||
|
||||
@a5mul(a ^ seed1, b ^ seed2, seed1, seed2);
|
||||
@a5mul(val01 ^ seed1, seed2, a, b);
|
||||
|
||||
return a ^ b;
|
||||
}
|
||||
156
lib/std/hash/komi.c3
Normal file
156
lib/std/hash/komi.c3
Normal file
@@ -0,0 +1,156 @@
|
||||
// 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.
|
||||
//
|
||||
// An implementation of Aleksey Vaneev's komihash, version 5.27, in C3:
|
||||
// https://github.com/avaneev/komihash
|
||||
//
|
||||
// The license for komihash from the above repository at the time of writing is as follows:
|
||||
//
|
||||
// >> MIT License
|
||||
// >>
|
||||
// >> Copyright (c) 2021-2025 Aleksey Vaneev
|
||||
// >>
|
||||
// >> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// >> of this software and associated documentation files (the "Software"), to deal
|
||||
// >> in the Software without restriction, including without limitation the rights
|
||||
// >> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// >> copies of the Software, and to permit persons to whom the Software is
|
||||
// >> furnished to do so, subject to the following conditions:
|
||||
// >>
|
||||
// >> The above copyright notice and this permission notice shall be included in all
|
||||
// >> copies or substantial portions of the Software.
|
||||
// >>
|
||||
// >> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// >> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// >> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// >> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// >> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// >> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// >> SOFTWARE.
|
||||
//
|
||||
//
|
||||
module std::hash::komi;
|
||||
|
||||
|
||||
macro @komimul(#u, #v, #lo, #hi) @local
|
||||
{
|
||||
uint128 imd = (uint128)#u * (uint128)#v;
|
||||
#lo = (ulong)imd;
|
||||
#hi += (ulong)(imd >> 64);
|
||||
}
|
||||
|
||||
|
||||
fn ulong hash(char[] data, ulong seed = 0)
|
||||
{
|
||||
ulong seed1 = 0x243F_6A88_85A3_08D3 ^ (seed & 0x5555_5555_5555_5555);
|
||||
ulong seed5 = 0x4528_21E6_38D0_1377 ^ (seed & 0xAAAA_AAAA_AAAA_AAAA);
|
||||
ulong r1h, r2h;
|
||||
|
||||
// HASHROUND
|
||||
@komimul(seed1, seed5, seed1, seed5);
|
||||
seed1 ^= seed5;
|
||||
|
||||
if (@likely(data.len < 16))
|
||||
{
|
||||
r1h = seed1;
|
||||
r2h = seed5;
|
||||
|
||||
if (@likely(data.len >= 8))
|
||||
{
|
||||
r1h ^= @unaligned_load(*(ulong*)data.ptr, 1);
|
||||
|
||||
r2h ^= (data.len < 12)
|
||||
? ((data[data.len - 3] | ((ulong)data[data.len - 2] << 8) | ((ulong)data[data.len - 1] << 16) | ((ulong)1 << 24)) >> ((data.len * 8) ^ 88))
|
||||
: (((@unaligned_load(*(uint*)&data[^4], 1) | ((ulong)1 << 32)) >> (128 - data.len * 8)) << 32 | @unaligned_load(*(uint*)&data[8], 1));
|
||||
}
|
||||
else if (data.len != 0)
|
||||
{
|
||||
r1h ^= (data.len < 4)
|
||||
? (((ulong)1 << (data.len * 8)) ^ data[0] ^ (data.len > 1 ? (ulong)data[1] << 8 : 0) ^ (data.len > 2 ? (ulong)data[2] << 16 : 0))
|
||||
: (((@unaligned_load(*(uint*)&data[^4], 1) | ((ulong)1 << 32)) >> (64 - data.len * 8)) << 32 | @unaligned_load(*(uint*)&data[0], 1));
|
||||
}
|
||||
}
|
||||
else if (data.len < 32)
|
||||
{
|
||||
// HASH16
|
||||
@komimul(
|
||||
@unaligned_load(*(ulong*)&data[0], 1) ^ seed1,
|
||||
@unaligned_load(*(ulong*)&data[8], 1) ^ seed5,
|
||||
seed1, seed5
|
||||
);
|
||||
seed1 ^= seed5;
|
||||
|
||||
if (data.len < 24)
|
||||
{
|
||||
r1h = (((@unaligned_load(*(ulong*)&data[^8], 1) >> 8) | ((ulong)1 << 56)) >> (((int)(data.len * 8) ^ 184))) ^ seed1;
|
||||
r2h = seed5;
|
||||
}
|
||||
else
|
||||
{
|
||||
r1h = @unaligned_load(*(ulong*)&data[16], 1) ^ seed1;
|
||||
r2h = (((@unaligned_load(*(ulong*)&data[^8], 1) >> 8) | ((ulong)1 << 56)) >> (((int)(data.len * 8) ^ 248))) ^ seed5;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (data.len >= 64)
|
||||
{
|
||||
ulong[8] seeds = {
|
||||
seed1, 0x1319_8A2E_0370_7344 ^ seed1, 0xA409_3822_299F_31D0 ^ seed1, 0x082E_FA98_EC4E_6C89 ^ seed1,
|
||||
seed5, 0xBE54_66CF_34E9_0C6C ^ seed5, 0xC0AC_29B7_C97C_50DD ^ seed5, 0x3F84_D5B5_B547_0917 ^ seed5,
|
||||
};
|
||||
|
||||
// HASHLOOP64
|
||||
for (; data.len >= 64; data = data[64:^64])
|
||||
{
|
||||
$for var $x = 0; $x < 4; ++$x :
|
||||
@komimul(
|
||||
@unaligned_load(*(ulong*)&data[0 + ($x * 8)], 1) ^ seeds[$x],
|
||||
@unaligned_load(*(ulong*)&data[32 + ($x * 8)], 1) ^ seeds[4 + $x],
|
||||
seeds[$x], seeds[4 + $x]
|
||||
);
|
||||
$endfor
|
||||
|
||||
seeds[3] ^= seeds[6];
|
||||
seeds[0] ^= seeds[7];
|
||||
seeds[2] ^= seeds[5];
|
||||
seeds[1] ^= seeds[4];
|
||||
}
|
||||
|
||||
seed1 = seeds[0] ^ seeds[1] ^ seeds[2] ^ seeds[3];
|
||||
seed5 = seeds[4] ^ seeds[5] ^ seeds[6] ^ seeds[7];
|
||||
}
|
||||
|
||||
for (; data.len >= 16; data = data[16:^16])
|
||||
{
|
||||
@komimul(
|
||||
@unaligned_load(*(ulong*)&data[0], 1) ^ seed1,
|
||||
@unaligned_load(*(ulong*)&data[8], 1) ^ seed5,
|
||||
seed1, seed5
|
||||
);
|
||||
seed1 ^= seed5;
|
||||
}
|
||||
|
||||
if (data.len < 8)
|
||||
{
|
||||
// NOTE: This is translated from the original code. It grabs the last ulong off the buffer even though the
|
||||
// data slice is less than 8 bytes. This is possible because this branch only occurs in a loop where
|
||||
// the original data slice length is >= 32.
|
||||
r1h = (((@unaligned_load(*(ulong*)(data.ptr + data.len - 8), 1) >> 8) | ((ulong)1 << 56)) >> ((data.len * 8) ^ 0x38)) ^ seed1;
|
||||
r2h = seed5;
|
||||
}
|
||||
else
|
||||
{
|
||||
r1h = @unaligned_load(*(ulong*)data.ptr, 1) ^ seed1;
|
||||
r2h = (((@unaligned_load(*(ulong*)&data[^8], 1) >> 8) | ((ulong)1 << 56)) >> ((data.len * 8) ^ 0x78)) ^ seed5;
|
||||
}
|
||||
}
|
||||
|
||||
// HASHFIN
|
||||
@komimul(r1h, r2h, seed1, seed5);
|
||||
seed1 ^= seed5;
|
||||
@komimul(seed1, seed5, seed1, seed5);
|
||||
seed1 ^= seed5;
|
||||
return seed1;
|
||||
}
|
||||
149
lib/std/hash/metro128.c3
Normal file
149
lib/std/hash/metro128.c3
Normal file
@@ -0,0 +1,149 @@
|
||||
// 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.
|
||||
//
|
||||
// MetroHash64 and MetroHash128 are different enough to warrant their own
|
||||
// modules, and there would be no reason to create a generic module just
|
||||
// for the two. If you inspect the differences, the only shared portion
|
||||
// of the entire process is the `update` method.
|
||||
//
|
||||
module std::hash::metro128;
|
||||
|
||||
|
||||
const ulong[4] K @local = {
|
||||
0xc83a91e1,
|
||||
0x8648dbdb,
|
||||
0x7bdec03b,
|
||||
0x2f5870a5,
|
||||
};
|
||||
|
||||
|
||||
struct MetroHash128
|
||||
{
|
||||
union
|
||||
{
|
||||
ulong[4] state;
|
||||
uint128 result;
|
||||
}
|
||||
union
|
||||
{
|
||||
ulong[4] stomach_64;
|
||||
char[32] stomach;
|
||||
}
|
||||
ulong bytes;
|
||||
}
|
||||
|
||||
|
||||
fn uint128 hash(char[] data, ulong seed = 0)
|
||||
{
|
||||
MetroHash128 m;
|
||||
m.init(seed);
|
||||
m.update(data);
|
||||
return m.final();
|
||||
}
|
||||
|
||||
|
||||
fn void MetroHash128.init(&self, ulong seed = 0)
|
||||
{
|
||||
self.state = {
|
||||
(seed - K[0]) * K[3],
|
||||
(seed + K[1]) * K[2],
|
||||
(seed + K[0]) * K[2],
|
||||
(seed - K[1]) * K[3],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
fn void MetroHash128.update(&self, char[] data)
|
||||
{
|
||||
if (self.bytes % 32) // partial buffer
|
||||
{
|
||||
ulong to_fill = min(data.len, (32 - (self.bytes % 32)));
|
||||
|
||||
self.stomach[(self.bytes % 32):to_fill] = data[:to_fill];
|
||||
|
||||
data = data[to_fill..];
|
||||
self.bytes += to_fill;
|
||||
|
||||
if (self.bytes % 32) return; // still awaiting more input, or final
|
||||
|
||||
self.state[0] += self.stomach_64[0] * K[0]; self.state[0] = self.state[0].rotr(29) + self.state[2];
|
||||
self.state[1] += self.stomach_64[1] * K[1]; self.state[1] = self.state[1].rotr(29) + self.state[3];
|
||||
self.state[2] += self.stomach_64[2] * K[2]; self.state[2] = self.state[2].rotr(29) + self.state[0];
|
||||
self.state[3] += self.stomach_64[3] * K[3]; self.state[3] = self.state[3].rotr(29) + self.state[1];
|
||||
}
|
||||
|
||||
self.bytes += data.len;
|
||||
|
||||
for (; data.len >= 32; data = data[32:^32])
|
||||
{
|
||||
self.state[0] += @unaligned_load(((ulong*)data.ptr)[0], 1) * K[0]; self.state[0] = self.state[0].rotr(29) + self.state[2];
|
||||
self.state[1] += @unaligned_load(((ulong*)data.ptr)[1], 1) * K[1]; self.state[1] = self.state[1].rotr(29) + self.state[3];
|
||||
self.state[2] += @unaligned_load(((ulong*)data.ptr)[2], 1) * K[2]; self.state[2] = self.state[2].rotr(29) + self.state[0];
|
||||
self.state[3] += @unaligned_load(((ulong*)data.ptr)[3], 1) * K[3]; self.state[3] = self.state[3].rotr(29) + self.state[1];
|
||||
}
|
||||
|
||||
// Gobble up the leftover bytes. Nom nom.
|
||||
if (data.len > 0) self.stomach[:data.len] = data[..];
|
||||
}
|
||||
|
||||
|
||||
fn uint128 MetroHash128.final(&self)
|
||||
{
|
||||
if (self.bytes >= 32)
|
||||
{
|
||||
self.state[2] ^= (((self.state[0] + self.state[3]) * K[0]) + self.state[1]).rotr(21) * K[1];
|
||||
self.state[3] ^= (((self.state[1] + self.state[2]) * K[1]) + self.state[0]).rotr(21) * K[0];
|
||||
self.state[0] ^= (((self.state[0] + self.state[2]) * K[0]) + self.state[3]).rotr(21) * K[1];
|
||||
self.state[1] ^= (((self.state[1] + self.state[3]) * K[1]) + self.state[2]).rotr(21) * K[0];
|
||||
}
|
||||
|
||||
char[] final_data = self.stomach[:(self.bytes % 32)];
|
||||
|
||||
if (final_data.len >= 16)
|
||||
{
|
||||
self.state[0] += ((ulong*)final_data.ptr)[0] * K[2]; self.state[0] = self.state[0].rotr(33) * K[3];
|
||||
self.state[1] += ((ulong*)final_data.ptr)[1] * K[2]; self.state[1] = self.state[1].rotr(33) * K[3];
|
||||
self.state[0] ^= ((self.state[0] * K[2]) + self.state[1]).rotr(45) * K[1];
|
||||
self.state[1] ^= ((self.state[1] * K[3]) + self.state[0]).rotr(45) * K[0];
|
||||
|
||||
final_data = final_data[16:^16];
|
||||
}
|
||||
|
||||
if (final_data.len >= 8)
|
||||
{
|
||||
self.state[0] += @unaligned_load(((ulong*)final_data.ptr)[0], 1) * K[2]; self.state[0] = self.state[0].rotr(33) * K[3];
|
||||
self.state[0] ^= ((self.state[0] * K[2]) + self.state[1]).rotr(27) * K[1];
|
||||
|
||||
final_data = final_data[8:^8];
|
||||
}
|
||||
|
||||
if (final_data.len >= 4)
|
||||
{
|
||||
self.state[1] += @unaligned_load(((uint*)final_data.ptr)[0], 1) * K[2]; self.state[1] = self.state[1].rotr(33) * K[3];
|
||||
self.state[1] ^= ((self.state[1] * K[3]) + self.state[0]).rotr(46) * K[0];
|
||||
|
||||
final_data = final_data[4:^4];
|
||||
}
|
||||
|
||||
if (final_data.len >= 2)
|
||||
{
|
||||
self.state[0] += @unaligned_load(((ushort*)final_data.ptr)[0], 1) * K[2]; self.state[0] = self.state[0].rotr(33) * K[3];
|
||||
self.state[0] ^= ((self.state[0] * K[2]) + self.state[1]).rotr(22) * K[1];
|
||||
|
||||
final_data = final_data[2:^2];
|
||||
}
|
||||
|
||||
if (final_data.len >= 1)
|
||||
{
|
||||
self.state[1] += ((char*)final_data.ptr)[0] * K[2]; self.state[1] = self.state[1].rotr(33) * K[3];
|
||||
self.state[1] ^= ((self.state[1] * K[3]) + self.state[0]).rotr(58) * K[0];
|
||||
}
|
||||
|
||||
self.state[0] += ((self.state[0] * K[0]) + self.state[1]).rotr(13);
|
||||
self.state[1] += ((self.state[1] * K[1]) + self.state[0]).rotr(37);
|
||||
self.state[0] += ((self.state[0] * K[2]) + self.state[1]).rotr(13);
|
||||
self.state[1] += ((self.state[1] * K[3]) + self.state[0]).rotr(37);
|
||||
|
||||
return self.result;
|
||||
}
|
||||
152
lib/std/hash/metro64.c3
Normal file
152
lib/std/hash/metro64.c3
Normal file
@@ -0,0 +1,152 @@
|
||||
// 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.
|
||||
//
|
||||
// MetroHash64 and MetroHash128 are different enough to warrant their own
|
||||
// modules, and there would be no reason to create a generic module just
|
||||
// for the two. If you inspect the differences, the only shared portion
|
||||
// of the entire process is the `update` method.
|
||||
//
|
||||
module std::hash::metro64;
|
||||
|
||||
|
||||
const ulong[4] K @local = {
|
||||
0xd6d018f5,
|
||||
0xa2aa033b,
|
||||
0x62992fc1,
|
||||
0x30bc5b29,
|
||||
};
|
||||
|
||||
|
||||
struct MetroHash64
|
||||
{
|
||||
union
|
||||
{
|
||||
ulong[4] state;
|
||||
ulong result;
|
||||
}
|
||||
union
|
||||
{
|
||||
ulong[4] stomach_64;
|
||||
char[32] stomach;
|
||||
}
|
||||
ulong bytes;
|
||||
ulong vseed;
|
||||
}
|
||||
|
||||
|
||||
fn ulong hash(char[] data, ulong seed = 0)
|
||||
{
|
||||
MetroHash64 m;
|
||||
m.init(seed);
|
||||
m.update(data);
|
||||
return m.final();
|
||||
}
|
||||
|
||||
|
||||
fn void MetroHash64.init(&self, ulong seed = 0)
|
||||
{
|
||||
self.vseed = (seed + K[2]) * K[0];
|
||||
|
||||
self.state[0] = self.vseed;
|
||||
self.state[1] = self.vseed;
|
||||
self.state[2] = self.vseed;
|
||||
self.state[3] = self.vseed;
|
||||
}
|
||||
|
||||
|
||||
fn void MetroHash64.update(&self, char[] data)
|
||||
{
|
||||
if (self.bytes % 32) // partial buffer
|
||||
{
|
||||
ulong to_fill = min(data.len, (32 - (self.bytes % 32)));
|
||||
|
||||
self.stomach[(self.bytes % 32):to_fill] = data[:to_fill];
|
||||
|
||||
data = data[to_fill..];
|
||||
self.bytes += to_fill;
|
||||
|
||||
if (self.bytes % 32) return; // still awaiting more input, or final
|
||||
|
||||
self.state[0] += self.stomach_64[0] * K[0]; self.state[0] = self.state[0].rotr(29) + self.state[2];
|
||||
self.state[1] += self.stomach_64[1] * K[1]; self.state[1] = self.state[1].rotr(29) + self.state[3];
|
||||
self.state[2] += self.stomach_64[2] * K[2]; self.state[2] = self.state[2].rotr(29) + self.state[0];
|
||||
self.state[3] += self.stomach_64[3] * K[3]; self.state[3] = self.state[3].rotr(29) + self.state[1];
|
||||
}
|
||||
|
||||
self.bytes += data.len;
|
||||
|
||||
for (; data.len >= 32; data = data[32:^32])
|
||||
{
|
||||
self.state[0] += @unaligned_load(((ulong*)data.ptr)[0], 1) * K[0]; self.state[0] = self.state[0].rotr(29) + self.state[2];
|
||||
self.state[1] += @unaligned_load(((ulong*)data.ptr)[1], 1) * K[1]; self.state[1] = self.state[1].rotr(29) + self.state[3];
|
||||
self.state[2] += @unaligned_load(((ulong*)data.ptr)[2], 1) * K[2]; self.state[2] = self.state[2].rotr(29) + self.state[0];
|
||||
self.state[3] += @unaligned_load(((ulong*)data.ptr)[3], 1) * K[3]; self.state[3] = self.state[3].rotr(29) + self.state[1];
|
||||
}
|
||||
|
||||
// Gobble up the leftover bytes. Nom nom.
|
||||
if (data.len > 0) self.stomach[:data.len] = data[..];
|
||||
}
|
||||
|
||||
|
||||
fn ulong MetroHash64.final(&self)
|
||||
{
|
||||
if (self.bytes >= 32)
|
||||
{
|
||||
self.state[2] ^= (((self.state[0] + self.state[3]) * K[0]) + self.state[1]).rotr(37) * K[1];
|
||||
self.state[3] ^= (((self.state[1] + self.state[2]) * K[1]) + self.state[0]).rotr(37) * K[0];
|
||||
self.state[0] ^= (((self.state[0] + self.state[2]) * K[0]) + self.state[3]).rotr(37) * K[1];
|
||||
self.state[1] ^= (((self.state[1] + self.state[3]) * K[1]) + self.state[2]).rotr(37) * K[0];
|
||||
|
||||
self.state[0] = self.vseed + (self.state[0] ^ self.state[1]);
|
||||
}
|
||||
|
||||
char[] final_data = self.stomach[:(self.bytes % 32)];
|
||||
|
||||
if (final_data.len >= 16)
|
||||
{
|
||||
self.state[1] = self.state[0] + @unaligned_load(((ulong*)final_data.ptr)[0], 1) * K[2]; self.state[1] = self.state[1].rotr(29) * K[3];
|
||||
self.state[2] = self.state[0] + @unaligned_load(((ulong*)final_data.ptr)[1], 1) * K[2]; self.state[2] = self.state[2].rotr(29) * K[3];
|
||||
self.state[1] ^= (self.state[1] * K[0]).rotr(21) + self.state[2];
|
||||
self.state[2] ^= (self.state[2] * K[3]).rotr(21) + self.state[1];
|
||||
self.state[0] += self.state[2];
|
||||
|
||||
final_data = final_data[16:^16];
|
||||
}
|
||||
|
||||
if (final_data.len >= 8)
|
||||
{
|
||||
self.state[0] += @unaligned_load(((ulong*)final_data.ptr)[0], 1) * K[3];
|
||||
self.state[0] ^= self.state[0].rotr(55) * K[1];
|
||||
|
||||
final_data = final_data[8:^8];
|
||||
}
|
||||
|
||||
if (final_data.len >= 4)
|
||||
{
|
||||
self.state[0] += @unaligned_load(((uint*)final_data.ptr)[0], 1) * K[3];
|
||||
self.state[0] ^= self.state[0].rotr(26) * K[1];
|
||||
|
||||
final_data = final_data[4:^4];
|
||||
}
|
||||
|
||||
if (final_data.len >= 2)
|
||||
{
|
||||
self.state[0] += @unaligned_load(((ushort*)final_data.ptr)[0], 1) * K[3];
|
||||
self.state[0] ^= self.state[0].rotr(48) * K[1];
|
||||
|
||||
final_data = final_data[2:^2];
|
||||
}
|
||||
|
||||
if (final_data.len >= 1)
|
||||
{
|
||||
self.state[0] += ((char*)final_data.ptr)[0] * K[3];
|
||||
self.state[0] ^= self.state[0].rotr(37) * K[1];
|
||||
}
|
||||
|
||||
self.state[0] ^= self.state[0].rotr(28);
|
||||
self.state[0] *= K[0];
|
||||
self.state[0] ^= self.state[0].rotr(29);
|
||||
|
||||
return self.result;
|
||||
}
|
||||
164
lib/std/hash/siphash.c3
Normal file
164
lib/std/hash/siphash.c3
Normal file
@@ -0,0 +1,164 @@
|
||||
// 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.
|
||||
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.
|
||||
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.
|
||||
module std::hash::siphash24_128;
|
||||
import std::hash::siphash;
|
||||
alias SipHash24_128 = SipHash { uint128, 2, 4 };
|
||||
alias hash = siphash::hash { uint128, 2, 4 };
|
||||
|
||||
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."
|
||||
*>
|
||||
module std::hash::siphash { OutType, BLOCK_ROUNDS, FINALIZE_ROUNDS };
|
||||
|
||||
|
||||
struct SipHash
|
||||
{
|
||||
ulong[4] v;
|
||||
long m;
|
||||
int m_idx;
|
||||
usz len;
|
||||
}
|
||||
|
||||
fn OutType hash(char[] data, uint128 key)
|
||||
{
|
||||
SipHash s;
|
||||
s.init(key);
|
||||
s.update(data);
|
||||
return s.final();
|
||||
}
|
||||
|
||||
fn void SipHash.init(&self, uint128 key)
|
||||
{
|
||||
ulong[2] key_64 = bitcast(key, ulong[2]);
|
||||
|
||||
self.v = {
|
||||
0x736f_6d65_7073_6575 ^ key_64[0],
|
||||
0x646f_7261_6e64_6f6d ^ key_64[1],
|
||||
0x6c79_6765_6e65_7261 ^ key_64[0],
|
||||
0x7465_6462_7974_6573 ^ key_64[1],
|
||||
};
|
||||
|
||||
$if OutType.typeid == uint128.typeid :
|
||||
self.v[1] ^= 0xEE;
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] data : "Bytes to hash"
|
||||
*>
|
||||
fn void SipHash.update(&self, char[] data)
|
||||
{
|
||||
self.len += data.len;
|
||||
|
||||
foreach (byte : data)
|
||||
{
|
||||
self.m |= (long)byte << (self.m_idx++ << 3);
|
||||
|
||||
if (self.m_idx < 8) continue;
|
||||
|
||||
// This runs every time the m_idx is 8, then it resets to 0.
|
||||
self.v[3] ^= self.m;
|
||||
|
||||
$for var $i = 0; $i < BLOCK_ROUNDS; ++$i : // unrolled loop
|
||||
self.round();
|
||||
$endfor
|
||||
|
||||
self.v[0] ^= self.m;
|
||||
|
||||
self.m_idx = 0;
|
||||
self.m = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fn OutType SipHash.final(&self)
|
||||
{
|
||||
char[8] last = { [7] = (char)self.len };
|
||||
|
||||
self.update(last[(self.m_idx < 7 ? self.m_idx : 7)..]);
|
||||
|
||||
$if OutType.typeid == uint128.typeid :
|
||||
self.v[2] ^= 0xEE;
|
||||
$else
|
||||
self.v[2] ^= 0xFF;
|
||||
$endif
|
||||
|
||||
$for var $i = 0; $i < FINALIZE_ROUNDS; ++$i : // unrolled loop
|
||||
self.round();
|
||||
$endfor
|
||||
|
||||
$if OutType.typeid == ulong.typeid :
|
||||
return self.v[0] ^ self.v[1] ^ self.v[2] ^ self.v[3];
|
||||
$else
|
||||
ulong lo = self.v[0] ^ self.v[1] ^ self.v[2] ^ self.v[3];
|
||||
|
||||
self.v[1] ^= 0xDD;
|
||||
|
||||
$for var $i = 0; $i < FINALIZE_ROUNDS; ++$i : // unrolled loop
|
||||
self.round();
|
||||
$endfor
|
||||
|
||||
return lo | ((uint128)(self.v[0] ^ self.v[1] ^ self.v[2] ^ self.v[3]) << 64);
|
||||
$endif
|
||||
}
|
||||
|
||||
|
||||
fn void SipHash.round(&self) @local
|
||||
{
|
||||
self.v[0] += self.v[1];
|
||||
self.v[1] = self.v[1].rotl(13);
|
||||
self.v[1] ^= self.v[0];
|
||||
self.v[0] = self.v[0].rotl(32);
|
||||
self.v[2] += self.v[3];
|
||||
self.v[3] = self.v[3].rotl(16);
|
||||
self.v[3] ^= self.v[2];
|
||||
self.v[0] += self.v[3];
|
||||
self.v[3] = self.v[3].rotl(21);
|
||||
self.v[3] ^= self.v[0];
|
||||
self.v[2] += self.v[1];
|
||||
self.v[1] = self.v[1].rotl(17);
|
||||
self.v[1] ^= self.v[2];
|
||||
self.v[2] = self.v[2].rotl(32);
|
||||
}
|
||||
|
||||
|
||||
|
||||
185
lib/std/hash/whirlpool/whirlpool.c3
Normal file
185
lib/std/hash/whirlpool/whirlpool.c3
Normal file
@@ -0,0 +1,185 @@
|
||||
// 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.
|
||||
//
|
||||
// Dedicated from repo: https://github.com/NotsoanoNimus/whirlpool.c3l
|
||||
module std::hash::whirlpool;
|
||||
|
||||
import std::hash::hmac;
|
||||
|
||||
|
||||
const BLOCK_SIZE = 64;
|
||||
const HASH_SIZE = 64;
|
||||
const BLOCK_128 = 64 / int128.sizeof;
|
||||
|
||||
struct Whirlpool
|
||||
{
|
||||
ulong[8] hash;
|
||||
union
|
||||
{
|
||||
char[BLOCK_SIZE] block;
|
||||
int128[BLOCK_128] block_128;
|
||||
}
|
||||
|
||||
// Using these two integers with a funnel shift permits the original WHIRLPOOL 2^256 length limit.
|
||||
uint128 counter_high;
|
||||
uint128 counter_low;
|
||||
}
|
||||
|
||||
alias HmacWhirlpool = Hmac { Whirlpool, HASH_SIZE, BLOCK_SIZE };
|
||||
alias hmac = hmac::hash { Whirlpool, HASH_SIZE, BLOCK_SIZE };
|
||||
alias pbkdf2 = hmac::pbkdf2 { Whirlpool, HASH_SIZE, BLOCK_SIZE };
|
||||
|
||||
fn char[HASH_SIZE] hash(char[] data)
|
||||
{
|
||||
Whirlpool w;
|
||||
w.update(data);
|
||||
return w.final();
|
||||
}
|
||||
|
||||
// Added to satisfy HMAC
|
||||
macro void Whirlpool.init(&self) => *self = { };
|
||||
|
||||
<*
|
||||
@require data.len <= isz.max : "Update with smaller slices"
|
||||
*>
|
||||
fn void Whirlpool.update(&self, char[] data)
|
||||
{
|
||||
char remainder = (char)self.counter_low & 0x3F; // if not at an even BLOCK_SIZE, how many bytes are over it
|
||||
uint to_pad = BLOCK_SIZE - remainder; // how many bytes must be filled to get the BLOCK_SIZE
|
||||
|
||||
uint128 prev_ctr = self.counter_low;
|
||||
self.counter_low += data.len;
|
||||
|
||||
// Handle counter rollover by just adding 1 onto 'high'. The overflow amount in 'low' remains valid.
|
||||
// Since 'data' is limited to INT64_MAX, there are no edge cases where 'high' must be incremented by more than 1.
|
||||
// This is so much data (> 2^128 bytes) that this will likely NEVER happen in practice...
|
||||
if (@unlikely(self.counter_low < prev_ctr)) ++self.counter_high;
|
||||
|
||||
// Pad partial blocks of input data.
|
||||
if (remainder > 0)
|
||||
{
|
||||
usz span = to_pad < data.len ? to_pad : data.len;
|
||||
self.block[remainder:span] = data[:span];
|
||||
|
||||
// When there wasn't enough data ingested to fill a block, just return to allow more incoming data to fill in.
|
||||
// If the algorithm is finalized during a 'partial' block, it has its own padding to fill the remaining gap.
|
||||
if (data.len < to_pad) return;
|
||||
|
||||
self.process_block(&self.block);
|
||||
data = data[to_pad..];
|
||||
}
|
||||
|
||||
// Digest blocks wholesale.
|
||||
while (data.len >= BLOCK_SIZE)
|
||||
{
|
||||
self.process_block(data);
|
||||
|
||||
data = (data.len > BLOCK_SIZE) ? data[BLOCK_SIZE..] : {};
|
||||
}
|
||||
|
||||
// Any leftovers should be swept into the 'block' buffer in the context for the next update or for 'final'.
|
||||
if (data.len)
|
||||
{
|
||||
usz leftover_span = (BLOCK_SIZE < data.len ? BLOCK_SIZE : data.len);
|
||||
|
||||
self.block[:leftover_span] = data[:leftover_span];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn char[HASH_SIZE] Whirlpool.final(&self)
|
||||
{
|
||||
char remainder = (char)self.counter_low & 0x3F; // if not at an even BLOCK_SIZE, how many bytes are over it
|
||||
|
||||
// Terminating byte gets added to the block.
|
||||
self.block[remainder++] = 0x80;
|
||||
|
||||
// When fewer than 256 bits are available to store the counter into, pad the block with zeroes and process it.
|
||||
if (remainder > 32)
|
||||
{
|
||||
if (remainder < 64) self.block[remainder..63] = 0x00;
|
||||
|
||||
self.process_block(&self.block);
|
||||
remainder = 0;
|
||||
}
|
||||
|
||||
// Zero out the rest of the chunk (or a new chunk if the above 'if' was true), up to byte 32.
|
||||
if (remainder < 32) self.block[remainder..31] = 0x00;
|
||||
|
||||
// Insert the big-endian values of the counter as a suffix of the block.
|
||||
// This 'counter' value is actually the number of BITS processed, hence the << 3 (or x8).
|
||||
self.block_128[2] = $$bswap(self.counter_high << 3 | (self.counter_low >> 125 & 0x07));
|
||||
self.block_128[3] = $$bswap(self.counter_low << 3);
|
||||
|
||||
// Process the final block.
|
||||
self.process_block(&self.block);
|
||||
|
||||
// Each ulong in the resultant hash should be bit-swapped before the final return.
|
||||
char[HASH_SIZE] hash @align(ulong.alignof);
|
||||
ulong* hash_ref = (ulong*)&hash;
|
||||
|
||||
$for var $i = 0; $i < 8; ++$i :
|
||||
hash_ref[$i] = $$bswap(self.hash[$i]);
|
||||
$endfor
|
||||
|
||||
// All done!
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
macro ulong @w_op(#src, $shift) @private
|
||||
=> S_BOX[(0 * 256) + (int)(#src[($shift + 0) & 7] >> 56) ]
|
||||
^ S_BOX[(1 * 256) + (int)(#src[($shift + 7) & 7] >> 48) & 0xFF]
|
||||
^ S_BOX[(2 * 256) + (int)(#src[($shift + 6) & 7] >> 40) & 0xFF]
|
||||
^ S_BOX[(3 * 256) + (int)(#src[($shift + 5) & 7] >> 32) & 0xFF]
|
||||
^ S_BOX[(4 * 256) + (int)(#src[($shift + 4) & 7] >> 24) & 0xFF]
|
||||
^ S_BOX[(5 * 256) + (int)(#src[($shift + 3) & 7] >> 16) & 0xFF]
|
||||
^ S_BOX[(6 * 256) + (int)(#src[($shift + 2) & 7] >> 8) & 0xFF]
|
||||
^ S_BOX[(7 * 256) + (int)(#src[($shift + 1) & 7] >> 0) & 0xFF];
|
||||
|
||||
|
||||
const ulong[10] RC @private = {
|
||||
0x1823c6e887b8014f,
|
||||
0x36a6d2f5796f9152,
|
||||
0x60bc9b8ea30c7b35,
|
||||
0x1de0d7c22e4bfe57,
|
||||
0x157737e59ff04ada,
|
||||
0x58c9290ab1a06b85,
|
||||
0xbd5d10f4cb3e0567,
|
||||
0xe427418ba77d95d8,
|
||||
0xfbee7c66dd17479e,
|
||||
0xca2dbf07ad5a8333
|
||||
};
|
||||
const ROUNDS = 10;
|
||||
|
||||
fn void Whirlpool.process_block(&self, char* block) @local
|
||||
{
|
||||
ulong[2 * 8] k; // key
|
||||
ulong[2 * 8] state; // state
|
||||
|
||||
// NOTE: These loops are unrolled with C3's Chad-tier compile-time evaluation.
|
||||
$for var $round = 0; $round < 8; $round++:
|
||||
k[$round] = self.hash[$round];
|
||||
state[$round] = $$bswap(@unaligned_load(((ulong*)block)[$round], 1)) ^ self.hash[$round];
|
||||
self.hash[$round] = state[$round];
|
||||
$endfor
|
||||
|
||||
$for var $round = 0; $round < ROUNDS; ++$round :
|
||||
var $m = $round % 2;
|
||||
|
||||
k[(($m ^ 1) * 8) + 0] = @w_op((&k[$m * 8]), 0) ^ RC[$round];
|
||||
|
||||
$for var $i = 1; $i < 8; $i++ :
|
||||
k[(($m ^ 1) * 8) + $i] = @w_op((&k[$m * 8]), $i);
|
||||
$endfor
|
||||
|
||||
$for var $i = 0; $i < 8; $i++ :
|
||||
state[(($m ^ 1) * 8) + $i] = @w_op(&(state[$m * 8]), $i) ^ k[(($m ^ 1) * 8) + $i];
|
||||
$endfor
|
||||
$endfor
|
||||
|
||||
$for var $x = 0; $x < 8; $x++:
|
||||
self.hash[$x] ^= state[$x];
|
||||
$endfor
|
||||
}
|
||||
539
lib/std/hash/whirlpool/whirlpool_sbox.c3
Normal file
539
lib/std/hash/whirlpool/whirlpool_sbox.c3
Normal file
@@ -0,0 +1,539 @@
|
||||
// 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.
|
||||
//
|
||||
// Dedicated from repo: https://github.com/NotsoanoNimus/whirlpool.c3l
|
||||
module std::hash::whirlpool @private;
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// WHIRLPOOL s-box data.
|
||||
const ulong[8 * 256] S_BOX = {
|
||||
// C0
|
||||
0x18186018c07830d8, 0x23238c2305af4626, 0xc6c63fc67ef991b8, 0xe8e887e8136fcdfb,
|
||||
0x878726874ca113cb, 0xb8b8dab8a9626d11, 0x0101040108050209, 0x4f4f214f426e9e0d,
|
||||
0x3636d836adee6c9b, 0xa6a6a2a6590451ff, 0xd2d26fd2debdb90c, 0xf5f5f3f5fb06f70e,
|
||||
0x7979f979ef80f296, 0x6f6fa16f5fcede30, 0x91917e91fcef3f6d, 0x52525552aa07a4f8,
|
||||
0x60609d6027fdc047, 0xbcbccabc89766535, 0x9b9b569baccd2b37, 0x8e8e028e048c018a,
|
||||
0xa3a3b6a371155bd2, 0x0c0c300c603c186c, 0x7b7bf17bff8af684, 0x3535d435b5e16a80,
|
||||
0x1d1d741de8693af5, 0xe0e0a7e05347ddb3, 0xd7d77bd7f6acb321, 0xc2c22fc25eed999c,
|
||||
0x2e2eb82e6d965c43, 0x4b4b314b627a9629, 0xfefedffea321e15d, 0x575741578216aed5,
|
||||
0x15155415a8412abd, 0x7777c1779fb6eee8, 0x3737dc37a5eb6e92, 0xe5e5b3e57b56d79e,
|
||||
0x9f9f469f8cd92313, 0xf0f0e7f0d317fd23, 0x4a4a354a6a7f9420, 0xdada4fda9e95a944,
|
||||
0x58587d58fa25b0a2, 0xc9c903c906ca8fcf, 0x2929a429558d527c, 0x0a0a280a5022145a,
|
||||
0xb1b1feb1e14f7f50, 0xa0a0baa0691a5dc9, 0x6b6bb16b7fdad614, 0x85852e855cab17d9,
|
||||
0xbdbdcebd8173673c, 0x5d5d695dd234ba8f, 0x1010401080502090, 0xf4f4f7f4f303f507,
|
||||
0xcbcb0bcb16c08bdd, 0x3e3ef83eedc67cd3, 0x0505140528110a2d, 0x676781671fe6ce78,
|
||||
0xe4e4b7e47353d597, 0x27279c2725bb4e02, 0x4141194132588273, 0x8b8b168b2c9d0ba7,
|
||||
0xa7a7a6a7510153f6, 0x7d7de97dcf94fab2, 0x95956e95dcfb3749, 0xd8d847d88e9fad56,
|
||||
0xfbfbcbfb8b30eb70, 0xeeee9fee2371c1cd, 0x7c7ced7cc791f8bb, 0x6666856617e3cc71,
|
||||
0xdddd53dda68ea77b, 0x17175c17b84b2eaf, 0x4747014702468e45, 0x9e9e429e84dc211a,
|
||||
0xcaca0fca1ec589d4, 0x2d2db42d75995a58, 0xbfbfc6bf9179632e, 0x07071c07381b0e3f,
|
||||
0xadad8ead012347ac, 0x5a5a755aea2fb4b0, 0x838336836cb51bef, 0x3333cc3385ff66b6,
|
||||
0x636391633ff2c65c, 0x02020802100a0412, 0xaaaa92aa39384993, 0x7171d971afa8e2de,
|
||||
0xc8c807c80ecf8dc6, 0x19196419c87d32d1, 0x494939497270923b, 0xd9d943d9869aaf5f,
|
||||
0xf2f2eff2c31df931, 0xe3e3abe34b48dba8, 0x5b5b715be22ab6b9, 0x88881a8834920dbc,
|
||||
0x9a9a529aa4c8293e, 0x262698262dbe4c0b, 0x3232c8328dfa64bf, 0xb0b0fab0e94a7d59,
|
||||
0xe9e983e91b6acff2, 0x0f0f3c0f78331e77, 0xd5d573d5e6a6b733, 0x80803a8074ba1df4,
|
||||
0xbebec2be997c6127, 0xcdcd13cd26de87eb, 0x3434d034bde46889, 0x48483d487a759032,
|
||||
0xffffdbffab24e354, 0x7a7af57af78ff48d, 0x90907a90f4ea3d64, 0x5f5f615fc23ebe9d,
|
||||
0x202080201da0403d, 0x6868bd6867d5d00f, 0x1a1a681ad07234ca, 0xaeae82ae192c41b7,
|
||||
0xb4b4eab4c95e757d, 0x54544d549a19a8ce, 0x93937693ece53b7f, 0x222288220daa442f,
|
||||
0x64648d6407e9c863, 0xf1f1e3f1db12ff2a, 0x7373d173bfa2e6cc, 0x12124812905a2482,
|
||||
0x40401d403a5d807a, 0x0808200840281048, 0xc3c32bc356e89b95, 0xecec97ec337bc5df,
|
||||
0xdbdb4bdb9690ab4d, 0xa1a1bea1611f5fc0, 0x8d8d0e8d1c830791, 0x3d3df43df5c97ac8,
|
||||
0x97976697ccf1335b, 0x0000000000000000, 0xcfcf1bcf36d483f9, 0x2b2bac2b4587566e,
|
||||
0x7676c57697b3ece1, 0x8282328264b019e6, 0xd6d67fd6fea9b128, 0x1b1b6c1bd87736c3,
|
||||
0xb5b5eeb5c15b7774, 0xafaf86af112943be, 0x6a6ab56a77dfd41d, 0x50505d50ba0da0ea,
|
||||
0x45450945124c8a57, 0xf3f3ebf3cb18fb38, 0x3030c0309df060ad, 0xefef9bef2b74c3c4,
|
||||
0x3f3ffc3fe5c37eda, 0x55554955921caac7, 0xa2a2b2a2791059db, 0xeaea8fea0365c9e9,
|
||||
0x656589650fecca6a, 0xbabad2bab9686903, 0x2f2fbc2f65935e4a, 0xc0c027c04ee79d8e,
|
||||
0xdede5fdebe81a160, 0x1c1c701ce06c38fc, 0xfdfdd3fdbb2ee746, 0x4d4d294d52649a1f,
|
||||
0x92927292e4e03976, 0x7575c9758fbceafa, 0x06061806301e0c36, 0x8a8a128a249809ae,
|
||||
0xb2b2f2b2f940794b, 0xe6e6bfe66359d185, 0x0e0e380e70361c7e, 0x1f1f7c1ff8633ee7,
|
||||
0x6262956237f7c455, 0xd4d477d4eea3b53a, 0xa8a89aa829324d81, 0x96966296c4f43152,
|
||||
0xf9f9c3f99b3aef62, 0xc5c533c566f697a3, 0x2525942535b14a10, 0x59597959f220b2ab,
|
||||
0x84842a8454ae15d0, 0x7272d572b7a7e4c5, 0x3939e439d5dd72ec, 0x4c4c2d4c5a619816,
|
||||
0x5e5e655eca3bbc94, 0x7878fd78e785f09f, 0x3838e038ddd870e5, 0x8c8c0a8c14860598,
|
||||
0xd1d163d1c6b2bf17, 0xa5a5aea5410b57e4, 0xe2e2afe2434dd9a1, 0x616199612ff8c24e,
|
||||
0xb3b3f6b3f1457b42, 0x2121842115a54234, 0x9c9c4a9c94d62508, 0x1e1e781ef0663cee,
|
||||
0x4343114322528661, 0xc7c73bc776fc93b1, 0xfcfcd7fcb32be54f, 0x0404100420140824,
|
||||
0x51515951b208a2e3, 0x99995e99bcc72f25, 0x6d6da96d4fc4da22, 0x0d0d340d68391a65,
|
||||
0xfafacffa8335e979, 0xdfdf5bdfb684a369, 0x7e7ee57ed79bfca9, 0x242490243db44819,
|
||||
0x3b3bec3bc5d776fe, 0xabab96ab313d4b9a, 0xcece1fce3ed181f0, 0x1111441188552299,
|
||||
0x8f8f068f0c890383, 0x4e4e254e4a6b9c04, 0xb7b7e6b7d1517366, 0xebeb8beb0b60cbe0,
|
||||
0x3c3cf03cfdcc78c1, 0x81813e817cbf1ffd, 0x94946a94d4fe3540, 0xf7f7fbf7eb0cf31c,
|
||||
0xb9b9deb9a1676f18, 0x13134c13985f268b, 0x2c2cb02c7d9c5851, 0xd3d36bd3d6b8bb05,
|
||||
0xe7e7bbe76b5cd38c, 0x6e6ea56e57cbdc39, 0xc4c437c46ef395aa, 0x03030c03180f061b,
|
||||
0x565645568a13acdc, 0x44440d441a49885e, 0x7f7fe17fdf9efea0, 0xa9a99ea921374f88,
|
||||
0x2a2aa82a4d825467, 0xbbbbd6bbb16d6b0a, 0xc1c123c146e29f87, 0x53535153a202a6f1,
|
||||
0xdcdc57dcae8ba572, 0x0b0b2c0b58271653, 0x9d9d4e9d9cd32701, 0x6c6cad6c47c1d82b,
|
||||
0x3131c43195f562a4, 0x7474cd7487b9e8f3, 0xf6f6fff6e309f115, 0x464605460a438c4c,
|
||||
0xacac8aac092645a5, 0x89891e893c970fb5, 0x14145014a04428b4, 0xe1e1a3e15b42dfba,
|
||||
0x16165816b04e2ca6, 0x3a3ae83acdd274f7, 0x6969b9696fd0d206, 0x09092409482d1241,
|
||||
0x7070dd70a7ade0d7, 0xb6b6e2b6d954716f, 0xd0d067d0ceb7bd1e, 0xeded93ed3b7ec7d6,
|
||||
0xcccc17cc2edb85e2, 0x424215422a578468, 0x98985a98b4c22d2c, 0xa4a4aaa4490e55ed,
|
||||
0x2828a0285d885075, 0x5c5c6d5cda31b886, 0xf8f8c7f8933fed6b, 0x8686228644a411c2,
|
||||
|
||||
// C1
|
||||
0xd818186018c07830, 0x2623238c2305af46, 0xb8c6c63fc67ef991, 0xfbe8e887e8136fcd,
|
||||
0xcb878726874ca113, 0x11b8b8dab8a9626d, 0x0901010401080502, 0x0d4f4f214f426e9e,
|
||||
0x9b3636d836adee6c, 0xffa6a6a2a6590451, 0x0cd2d26fd2debdb9, 0x0ef5f5f3f5fb06f7,
|
||||
0x967979f979ef80f2, 0x306f6fa16f5fcede, 0x6d91917e91fcef3f, 0xf852525552aa07a4,
|
||||
0x4760609d6027fdc0, 0x35bcbccabc897665, 0x379b9b569baccd2b, 0x8a8e8e028e048c01,
|
||||
0xd2a3a3b6a371155b, 0x6c0c0c300c603c18, 0x847b7bf17bff8af6, 0x803535d435b5e16a,
|
||||
0xf51d1d741de8693a, 0xb3e0e0a7e05347dd, 0x21d7d77bd7f6acb3, 0x9cc2c22fc25eed99,
|
||||
0x432e2eb82e6d965c, 0x294b4b314b627a96, 0x5dfefedffea321e1, 0xd5575741578216ae,
|
||||
0xbd15155415a8412a, 0xe87777c1779fb6ee, 0x923737dc37a5eb6e, 0x9ee5e5b3e57b56d7,
|
||||
0x139f9f469f8cd923, 0x23f0f0e7f0d317fd, 0x204a4a354a6a7f94, 0x44dada4fda9e95a9,
|
||||
0xa258587d58fa25b0, 0xcfc9c903c906ca8f, 0x7c2929a429558d52, 0x5a0a0a280a502214,
|
||||
0x50b1b1feb1e14f7f, 0xc9a0a0baa0691a5d, 0x146b6bb16b7fdad6, 0xd985852e855cab17,
|
||||
0x3cbdbdcebd817367, 0x8f5d5d695dd234ba, 0x9010104010805020, 0x07f4f4f7f4f303f5,
|
||||
0xddcbcb0bcb16c08b, 0xd33e3ef83eedc67c, 0x2d0505140528110a, 0x78676781671fe6ce,
|
||||
0x97e4e4b7e47353d5, 0x0227279c2725bb4e, 0x7341411941325882, 0xa78b8b168b2c9d0b,
|
||||
0xf6a7a7a6a7510153, 0xb27d7de97dcf94fa, 0x4995956e95dcfb37, 0x56d8d847d88e9fad,
|
||||
0x70fbfbcbfb8b30eb, 0xcdeeee9fee2371c1, 0xbb7c7ced7cc791f8, 0x716666856617e3cc,
|
||||
0x7bdddd53dda68ea7, 0xaf17175c17b84b2e, 0x454747014702468e, 0x1a9e9e429e84dc21,
|
||||
0xd4caca0fca1ec589, 0x582d2db42d75995a, 0x2ebfbfc6bf917963, 0x3f07071c07381b0e,
|
||||
0xacadad8ead012347, 0xb05a5a755aea2fb4, 0xef838336836cb51b, 0xb63333cc3385ff66,
|
||||
0x5c636391633ff2c6, 0x1202020802100a04, 0x93aaaa92aa393849, 0xde7171d971afa8e2,
|
||||
0xc6c8c807c80ecf8d, 0xd119196419c87d32, 0x3b49493949727092, 0x5fd9d943d9869aaf,
|
||||
0x31f2f2eff2c31df9, 0xa8e3e3abe34b48db, 0xb95b5b715be22ab6, 0xbc88881a8834920d,
|
||||
0x3e9a9a529aa4c829, 0x0b262698262dbe4c, 0xbf3232c8328dfa64, 0x59b0b0fab0e94a7d,
|
||||
0xf2e9e983e91b6acf, 0x770f0f3c0f78331e, 0x33d5d573d5e6a6b7, 0xf480803a8074ba1d,
|
||||
0x27bebec2be997c61, 0xebcdcd13cd26de87, 0x893434d034bde468, 0x3248483d487a7590,
|
||||
0x54ffffdbffab24e3, 0x8d7a7af57af78ff4, 0x6490907a90f4ea3d, 0x9d5f5f615fc23ebe,
|
||||
0x3d202080201da040, 0x0f6868bd6867d5d0, 0xca1a1a681ad07234, 0xb7aeae82ae192c41,
|
||||
0x7db4b4eab4c95e75, 0xce54544d549a19a8, 0x7f93937693ece53b, 0x2f222288220daa44,
|
||||
0x6364648d6407e9c8, 0x2af1f1e3f1db12ff, 0xcc7373d173bfa2e6, 0x8212124812905a24,
|
||||
0x7a40401d403a5d80, 0x4808082008402810, 0x95c3c32bc356e89b, 0xdfecec97ec337bc5,
|
||||
0x4ddbdb4bdb9690ab, 0xc0a1a1bea1611f5f, 0x918d8d0e8d1c8307, 0xc83d3df43df5c97a,
|
||||
0x5b97976697ccf133, 0x0000000000000000, 0xf9cfcf1bcf36d483, 0x6e2b2bac2b458756,
|
||||
0xe17676c57697b3ec, 0xe68282328264b019, 0x28d6d67fd6fea9b1, 0xc31b1b6c1bd87736,
|
||||
0x74b5b5eeb5c15b77, 0xbeafaf86af112943, 0x1d6a6ab56a77dfd4, 0xea50505d50ba0da0,
|
||||
0x5745450945124c8a, 0x38f3f3ebf3cb18fb, 0xad3030c0309df060, 0xc4efef9bef2b74c3,
|
||||
0xda3f3ffc3fe5c37e, 0xc755554955921caa, 0xdba2a2b2a2791059, 0xe9eaea8fea0365c9,
|
||||
0x6a656589650fecca, 0x03babad2bab96869, 0x4a2f2fbc2f65935e, 0x8ec0c027c04ee79d,
|
||||
0x60dede5fdebe81a1, 0xfc1c1c701ce06c38, 0x46fdfdd3fdbb2ee7, 0x1f4d4d294d52649a,
|
||||
0x7692927292e4e039, 0xfa7575c9758fbcea, 0x3606061806301e0c, 0xae8a8a128a249809,
|
||||
0x4bb2b2f2b2f94079, 0x85e6e6bfe66359d1, 0x7e0e0e380e70361c, 0xe71f1f7c1ff8633e,
|
||||
0x556262956237f7c4, 0x3ad4d477d4eea3b5, 0x81a8a89aa829324d, 0x5296966296c4f431,
|
||||
0x62f9f9c3f99b3aef, 0xa3c5c533c566f697, 0x102525942535b14a, 0xab59597959f220b2,
|
||||
0xd084842a8454ae15, 0xc57272d572b7a7e4, 0xec3939e439d5dd72, 0x164c4c2d4c5a6198,
|
||||
0x945e5e655eca3bbc, 0x9f7878fd78e785f0, 0xe53838e038ddd870, 0x988c8c0a8c148605,
|
||||
0x17d1d163d1c6b2bf, 0xe4a5a5aea5410b57, 0xa1e2e2afe2434dd9, 0x4e616199612ff8c2,
|
||||
0x42b3b3f6b3f1457b, 0x342121842115a542, 0x089c9c4a9c94d625, 0xee1e1e781ef0663c,
|
||||
0x6143431143225286, 0xb1c7c73bc776fc93, 0x4ffcfcd7fcb32be5, 0x2404041004201408,
|
||||
0xe351515951b208a2, 0x2599995e99bcc72f, 0x226d6da96d4fc4da, 0x650d0d340d68391a,
|
||||
0x79fafacffa8335e9, 0x69dfdf5bdfb684a3, 0xa97e7ee57ed79bfc, 0x19242490243db448,
|
||||
0xfe3b3bec3bc5d776, 0x9aabab96ab313d4b, 0xf0cece1fce3ed181, 0x9911114411885522,
|
||||
0x838f8f068f0c8903, 0x044e4e254e4a6b9c, 0x66b7b7e6b7d15173, 0xe0ebeb8beb0b60cb,
|
||||
0xc13c3cf03cfdcc78, 0xfd81813e817cbf1f, 0x4094946a94d4fe35, 0x1cf7f7fbf7eb0cf3,
|
||||
0x18b9b9deb9a1676f, 0x8b13134c13985f26, 0x512c2cb02c7d9c58, 0x05d3d36bd3d6b8bb,
|
||||
0x8ce7e7bbe76b5cd3, 0x396e6ea56e57cbdc, 0xaac4c437c46ef395, 0x1b03030c03180f06,
|
||||
0xdc565645568a13ac, 0x5e44440d441a4988, 0xa07f7fe17fdf9efe, 0x88a9a99ea921374f,
|
||||
0x672a2aa82a4d8254, 0x0abbbbd6bbb16d6b, 0x87c1c123c146e29f, 0xf153535153a202a6,
|
||||
0x72dcdc57dcae8ba5, 0x530b0b2c0b582716, 0x019d9d4e9d9cd327, 0x2b6c6cad6c47c1d8,
|
||||
0xa43131c43195f562, 0xf37474cd7487b9e8, 0x15f6f6fff6e309f1, 0x4c464605460a438c,
|
||||
0xa5acac8aac092645, 0xb589891e893c970f, 0xb414145014a04428, 0xbae1e1a3e15b42df,
|
||||
0xa616165816b04e2c, 0xf73a3ae83acdd274, 0x066969b9696fd0d2, 0x4109092409482d12,
|
||||
0xd77070dd70a7ade0, 0x6fb6b6e2b6d95471, 0x1ed0d067d0ceb7bd, 0xd6eded93ed3b7ec7,
|
||||
0xe2cccc17cc2edb85, 0x68424215422a5784, 0x2c98985a98b4c22d, 0xeda4a4aaa4490e55,
|
||||
0x752828a0285d8850, 0x865c5c6d5cda31b8, 0x6bf8f8c7f8933fed, 0xc28686228644a411,
|
||||
|
||||
// C2
|
||||
0x30d818186018c078, 0x462623238c2305af, 0x91b8c6c63fc67ef9, 0xcdfbe8e887e8136f,
|
||||
0x13cb878726874ca1, 0x6d11b8b8dab8a962, 0x0209010104010805, 0x9e0d4f4f214f426e,
|
||||
0x6c9b3636d836adee, 0x51ffa6a6a2a65904, 0xb90cd2d26fd2debd, 0xf70ef5f5f3f5fb06,
|
||||
0xf2967979f979ef80, 0xde306f6fa16f5fce, 0x3f6d91917e91fcef, 0xa4f852525552aa07,
|
||||
0xc04760609d6027fd, 0x6535bcbccabc8976, 0x2b379b9b569baccd, 0x018a8e8e028e048c,
|
||||
0x5bd2a3a3b6a37115, 0x186c0c0c300c603c, 0xf6847b7bf17bff8a, 0x6a803535d435b5e1,
|
||||
0x3af51d1d741de869, 0xddb3e0e0a7e05347, 0xb321d7d77bd7f6ac, 0x999cc2c22fc25eed,
|
||||
0x5c432e2eb82e6d96, 0x96294b4b314b627a, 0xe15dfefedffea321, 0xaed5575741578216,
|
||||
0x2abd15155415a841, 0xeee87777c1779fb6, 0x6e923737dc37a5eb, 0xd79ee5e5b3e57b56,
|
||||
0x23139f9f469f8cd9, 0xfd23f0f0e7f0d317, 0x94204a4a354a6a7f, 0xa944dada4fda9e95,
|
||||
0xb0a258587d58fa25, 0x8fcfc9c903c906ca, 0x527c2929a429558d, 0x145a0a0a280a5022,
|
||||
0x7f50b1b1feb1e14f, 0x5dc9a0a0baa0691a, 0xd6146b6bb16b7fda, 0x17d985852e855cab,
|
||||
0x673cbdbdcebd8173, 0xba8f5d5d695dd234, 0x2090101040108050, 0xf507f4f4f7f4f303,
|
||||
0x8bddcbcb0bcb16c0, 0x7cd33e3ef83eedc6, 0x0a2d050514052811, 0xce78676781671fe6,
|
||||
0xd597e4e4b7e47353, 0x4e0227279c2725bb, 0x8273414119413258, 0x0ba78b8b168b2c9d,
|
||||
0x53f6a7a7a6a75101, 0xfab27d7de97dcf94, 0x374995956e95dcfb, 0xad56d8d847d88e9f,
|
||||
0xeb70fbfbcbfb8b30, 0xc1cdeeee9fee2371, 0xf8bb7c7ced7cc791, 0xcc716666856617e3,
|
||||
0xa77bdddd53dda68e, 0x2eaf17175c17b84b, 0x8e45474701470246, 0x211a9e9e429e84dc,
|
||||
0x89d4caca0fca1ec5, 0x5a582d2db42d7599, 0x632ebfbfc6bf9179, 0x0e3f07071c07381b,
|
||||
0x47acadad8ead0123, 0xb4b05a5a755aea2f, 0x1bef838336836cb5, 0x66b63333cc3385ff,
|
||||
0xc65c636391633ff2, 0x041202020802100a, 0x4993aaaa92aa3938, 0xe2de7171d971afa8,
|
||||
0x8dc6c8c807c80ecf, 0x32d119196419c87d, 0x923b494939497270, 0xaf5fd9d943d9869a,
|
||||
0xf931f2f2eff2c31d, 0xdba8e3e3abe34b48, 0xb6b95b5b715be22a, 0x0dbc88881a883492,
|
||||
0x293e9a9a529aa4c8, 0x4c0b262698262dbe, 0x64bf3232c8328dfa, 0x7d59b0b0fab0e94a,
|
||||
0xcff2e9e983e91b6a, 0x1e770f0f3c0f7833, 0xb733d5d573d5e6a6, 0x1df480803a8074ba,
|
||||
0x6127bebec2be997c, 0x87ebcdcd13cd26de, 0x68893434d034bde4, 0x903248483d487a75,
|
||||
0xe354ffffdbffab24, 0xf48d7a7af57af78f, 0x3d6490907a90f4ea, 0xbe9d5f5f615fc23e,
|
||||
0x403d202080201da0, 0xd00f6868bd6867d5, 0x34ca1a1a681ad072, 0x41b7aeae82ae192c,
|
||||
0x757db4b4eab4c95e, 0xa8ce54544d549a19, 0x3b7f93937693ece5, 0x442f222288220daa,
|
||||
0xc86364648d6407e9, 0xff2af1f1e3f1db12, 0xe6cc7373d173bfa2, 0x248212124812905a,
|
||||
0x807a40401d403a5d, 0x1048080820084028, 0x9b95c3c32bc356e8, 0xc5dfecec97ec337b,
|
||||
0xab4ddbdb4bdb9690, 0x5fc0a1a1bea1611f, 0x07918d8d0e8d1c83, 0x7ac83d3df43df5c9,
|
||||
0x335b97976697ccf1, 0x0000000000000000, 0x83f9cfcf1bcf36d4, 0x566e2b2bac2b4587,
|
||||
0xece17676c57697b3, 0x19e68282328264b0, 0xb128d6d67fd6fea9, 0x36c31b1b6c1bd877,
|
||||
0x7774b5b5eeb5c15b, 0x43beafaf86af1129, 0xd41d6a6ab56a77df, 0xa0ea50505d50ba0d,
|
||||
0x8a5745450945124c, 0xfb38f3f3ebf3cb18, 0x60ad3030c0309df0, 0xc3c4efef9bef2b74,
|
||||
0x7eda3f3ffc3fe5c3, 0xaac755554955921c, 0x59dba2a2b2a27910, 0xc9e9eaea8fea0365,
|
||||
0xca6a656589650fec, 0x6903babad2bab968, 0x5e4a2f2fbc2f6593, 0x9d8ec0c027c04ee7,
|
||||
0xa160dede5fdebe81, 0x38fc1c1c701ce06c, 0xe746fdfdd3fdbb2e, 0x9a1f4d4d294d5264,
|
||||
0x397692927292e4e0, 0xeafa7575c9758fbc, 0x0c3606061806301e, 0x09ae8a8a128a2498,
|
||||
0x794bb2b2f2b2f940, 0xd185e6e6bfe66359, 0x1c7e0e0e380e7036, 0x3ee71f1f7c1ff863,
|
||||
0xc4556262956237f7, 0xb53ad4d477d4eea3, 0x4d81a8a89aa82932, 0x315296966296c4f4,
|
||||
0xef62f9f9c3f99b3a, 0x97a3c5c533c566f6, 0x4a102525942535b1, 0xb2ab59597959f220,
|
||||
0x15d084842a8454ae, 0xe4c57272d572b7a7, 0x72ec3939e439d5dd, 0x98164c4c2d4c5a61,
|
||||
0xbc945e5e655eca3b, 0xf09f7878fd78e785, 0x70e53838e038ddd8, 0x05988c8c0a8c1486,
|
||||
0xbf17d1d163d1c6b2, 0x57e4a5a5aea5410b, 0xd9a1e2e2afe2434d, 0xc24e616199612ff8,
|
||||
0x7b42b3b3f6b3f145, 0x42342121842115a5, 0x25089c9c4a9c94d6, 0x3cee1e1e781ef066,
|
||||
0x8661434311432252, 0x93b1c7c73bc776fc, 0xe54ffcfcd7fcb32b, 0x0824040410042014,
|
||||
0xa2e351515951b208, 0x2f2599995e99bcc7, 0xda226d6da96d4fc4, 0x1a650d0d340d6839,
|
||||
0xe979fafacffa8335, 0xa369dfdf5bdfb684, 0xfca97e7ee57ed79b, 0x4819242490243db4,
|
||||
0x76fe3b3bec3bc5d7, 0x4b9aabab96ab313d, 0x81f0cece1fce3ed1, 0x2299111144118855,
|
||||
0x03838f8f068f0c89, 0x9c044e4e254e4a6b, 0x7366b7b7e6b7d151, 0xcbe0ebeb8beb0b60,
|
||||
0x78c13c3cf03cfdcc, 0x1ffd81813e817cbf, 0x354094946a94d4fe, 0xf31cf7f7fbf7eb0c,
|
||||
0x6f18b9b9deb9a167, 0x268b13134c13985f, 0x58512c2cb02c7d9c, 0xbb05d3d36bd3d6b8,
|
||||
0xd38ce7e7bbe76b5c, 0xdc396e6ea56e57cb, 0x95aac4c437c46ef3, 0x061b03030c03180f,
|
||||
0xacdc565645568a13, 0x885e44440d441a49, 0xfea07f7fe17fdf9e, 0x4f88a9a99ea92137,
|
||||
0x54672a2aa82a4d82, 0x6b0abbbbd6bbb16d, 0x9f87c1c123c146e2, 0xa6f153535153a202,
|
||||
0xa572dcdc57dcae8b, 0x16530b0b2c0b5827, 0x27019d9d4e9d9cd3, 0xd82b6c6cad6c47c1,
|
||||
0x62a43131c43195f5, 0xe8f37474cd7487b9, 0xf115f6f6fff6e309, 0x8c4c464605460a43,
|
||||
0x45a5acac8aac0926, 0x0fb589891e893c97, 0x28b414145014a044, 0xdfbae1e1a3e15b42,
|
||||
0x2ca616165816b04e, 0x74f73a3ae83acdd2, 0xd2066969b9696fd0, 0x124109092409482d,
|
||||
0xe0d77070dd70a7ad, 0x716fb6b6e2b6d954, 0xbd1ed0d067d0ceb7, 0xc7d6eded93ed3b7e,
|
||||
0x85e2cccc17cc2edb, 0x8468424215422a57, 0x2d2c98985a98b4c2, 0x55eda4a4aaa4490e,
|
||||
0x50752828a0285d88, 0xb8865c5c6d5cda31, 0xed6bf8f8c7f8933f, 0x11c28686228644a4,
|
||||
|
||||
// C3
|
||||
0x7830d818186018c0, 0xaf462623238c2305, 0xf991b8c6c63fc67e, 0x6fcdfbe8e887e813,
|
||||
0xa113cb878726874c, 0x626d11b8b8dab8a9, 0x0502090101040108, 0x6e9e0d4f4f214f42,
|
||||
0xee6c9b3636d836ad, 0x0451ffa6a6a2a659, 0xbdb90cd2d26fd2de, 0x06f70ef5f5f3f5fb,
|
||||
0x80f2967979f979ef, 0xcede306f6fa16f5f, 0xef3f6d91917e91fc, 0x07a4f852525552aa,
|
||||
0xfdc04760609d6027, 0x766535bcbccabc89, 0xcd2b379b9b569bac, 0x8c018a8e8e028e04,
|
||||
0x155bd2a3a3b6a371, 0x3c186c0c0c300c60, 0x8af6847b7bf17bff, 0xe16a803535d435b5,
|
||||
0x693af51d1d741de8, 0x47ddb3e0e0a7e053, 0xacb321d7d77bd7f6, 0xed999cc2c22fc25e,
|
||||
0x965c432e2eb82e6d, 0x7a96294b4b314b62, 0x21e15dfefedffea3, 0x16aed55757415782,
|
||||
0x412abd15155415a8, 0xb6eee87777c1779f, 0xeb6e923737dc37a5, 0x56d79ee5e5b3e57b,
|
||||
0xd923139f9f469f8c, 0x17fd23f0f0e7f0d3, 0x7f94204a4a354a6a, 0x95a944dada4fda9e,
|
||||
0x25b0a258587d58fa, 0xca8fcfc9c903c906, 0x8d527c2929a42955, 0x22145a0a0a280a50,
|
||||
0x4f7f50b1b1feb1e1, 0x1a5dc9a0a0baa069, 0xdad6146b6bb16b7f, 0xab17d985852e855c,
|
||||
0x73673cbdbdcebd81, 0x34ba8f5d5d695dd2, 0x5020901010401080, 0x03f507f4f4f7f4f3,
|
||||
0xc08bddcbcb0bcb16, 0xc67cd33e3ef83eed, 0x110a2d0505140528, 0xe6ce78676781671f,
|
||||
0x53d597e4e4b7e473, 0xbb4e0227279c2725, 0x5882734141194132, 0x9d0ba78b8b168b2c,
|
||||
0x0153f6a7a7a6a751, 0x94fab27d7de97dcf, 0xfb374995956e95dc, 0x9fad56d8d847d88e,
|
||||
0x30eb70fbfbcbfb8b, 0x71c1cdeeee9fee23, 0x91f8bb7c7ced7cc7, 0xe3cc716666856617,
|
||||
0x8ea77bdddd53dda6, 0x4b2eaf17175c17b8, 0x468e454747014702, 0xdc211a9e9e429e84,
|
||||
0xc589d4caca0fca1e, 0x995a582d2db42d75, 0x79632ebfbfc6bf91, 0x1b0e3f07071c0738,
|
||||
0x2347acadad8ead01, 0x2fb4b05a5a755aea, 0xb51bef838336836c, 0xff66b63333cc3385,
|
||||
0xf2c65c636391633f, 0x0a04120202080210, 0x384993aaaa92aa39, 0xa8e2de7171d971af,
|
||||
0xcf8dc6c8c807c80e, 0x7d32d119196419c8, 0x70923b4949394972, 0x9aaf5fd9d943d986,
|
||||
0x1df931f2f2eff2c3, 0x48dba8e3e3abe34b, 0x2ab6b95b5b715be2, 0x920dbc88881a8834,
|
||||
0xc8293e9a9a529aa4, 0xbe4c0b262698262d, 0xfa64bf3232c8328d, 0x4a7d59b0b0fab0e9,
|
||||
0x6acff2e9e983e91b, 0x331e770f0f3c0f78, 0xa6b733d5d573d5e6, 0xba1df480803a8074,
|
||||
0x7c6127bebec2be99, 0xde87ebcdcd13cd26, 0xe468893434d034bd, 0x75903248483d487a,
|
||||
0x24e354ffffdbffab, 0x8ff48d7a7af57af7, 0xea3d6490907a90f4, 0x3ebe9d5f5f615fc2,
|
||||
0xa0403d202080201d, 0xd5d00f6868bd6867, 0x7234ca1a1a681ad0, 0x2c41b7aeae82ae19,
|
||||
0x5e757db4b4eab4c9, 0x19a8ce54544d549a, 0xe53b7f93937693ec, 0xaa442f222288220d,
|
||||
0xe9c86364648d6407, 0x12ff2af1f1e3f1db, 0xa2e6cc7373d173bf, 0x5a24821212481290,
|
||||
0x5d807a40401d403a, 0x2810480808200840, 0xe89b95c3c32bc356, 0x7bc5dfecec97ec33,
|
||||
0x90ab4ddbdb4bdb96, 0x1f5fc0a1a1bea161, 0x8307918d8d0e8d1c, 0xc97ac83d3df43df5,
|
||||
0xf1335b97976697cc, 0x0000000000000000, 0xd483f9cfcf1bcf36, 0x87566e2b2bac2b45,
|
||||
0xb3ece17676c57697, 0xb019e68282328264, 0xa9b128d6d67fd6fe, 0x7736c31b1b6c1bd8,
|
||||
0x5b7774b5b5eeb5c1, 0x2943beafaf86af11, 0xdfd41d6a6ab56a77, 0x0da0ea50505d50ba,
|
||||
0x4c8a574545094512, 0x18fb38f3f3ebf3cb, 0xf060ad3030c0309d, 0x74c3c4efef9bef2b,
|
||||
0xc37eda3f3ffc3fe5, 0x1caac75555495592, 0x1059dba2a2b2a279, 0x65c9e9eaea8fea03,
|
||||
0xecca6a656589650f, 0x686903babad2bab9, 0x935e4a2f2fbc2f65, 0xe79d8ec0c027c04e,
|
||||
0x81a160dede5fdebe, 0x6c38fc1c1c701ce0, 0x2ee746fdfdd3fdbb, 0x649a1f4d4d294d52,
|
||||
0xe0397692927292e4, 0xbceafa7575c9758f, 0x1e0c360606180630, 0x9809ae8a8a128a24,
|
||||
0x40794bb2b2f2b2f9, 0x59d185e6e6bfe663, 0x361c7e0e0e380e70, 0x633ee71f1f7c1ff8,
|
||||
0xf7c4556262956237, 0xa3b53ad4d477d4ee, 0x324d81a8a89aa829, 0xf4315296966296c4,
|
||||
0x3aef62f9f9c3f99b, 0xf697a3c5c533c566, 0xb14a102525942535, 0x20b2ab59597959f2,
|
||||
0xae15d084842a8454, 0xa7e4c57272d572b7, 0xdd72ec3939e439d5, 0x6198164c4c2d4c5a,
|
||||
0x3bbc945e5e655eca, 0x85f09f7878fd78e7, 0xd870e53838e038dd, 0x8605988c8c0a8c14,
|
||||
0xb2bf17d1d163d1c6, 0x0b57e4a5a5aea541, 0x4dd9a1e2e2afe243, 0xf8c24e616199612f,
|
||||
0x457b42b3b3f6b3f1, 0xa542342121842115, 0xd625089c9c4a9c94, 0x663cee1e1e781ef0,
|
||||
0x5286614343114322, 0xfc93b1c7c73bc776, 0x2be54ffcfcd7fcb3, 0x1408240404100420,
|
||||
0x08a2e351515951b2, 0xc72f2599995e99bc, 0xc4da226d6da96d4f, 0x391a650d0d340d68,
|
||||
0x35e979fafacffa83, 0x84a369dfdf5bdfb6, 0x9bfca97e7ee57ed7, 0xb44819242490243d,
|
||||
0xd776fe3b3bec3bc5, 0x3d4b9aabab96ab31, 0xd181f0cece1fce3e, 0x5522991111441188,
|
||||
0x8903838f8f068f0c, 0x6b9c044e4e254e4a, 0x517366b7b7e6b7d1, 0x60cbe0ebeb8beb0b,
|
||||
0xcc78c13c3cf03cfd, 0xbf1ffd81813e817c, 0xfe354094946a94d4, 0x0cf31cf7f7fbf7eb,
|
||||
0x676f18b9b9deb9a1, 0x5f268b13134c1398, 0x9c58512c2cb02c7d, 0xb8bb05d3d36bd3d6,
|
||||
0x5cd38ce7e7bbe76b, 0xcbdc396e6ea56e57, 0xf395aac4c437c46e, 0x0f061b03030c0318,
|
||||
0x13acdc565645568a, 0x49885e44440d441a, 0x9efea07f7fe17fdf, 0x374f88a9a99ea921,
|
||||
0x8254672a2aa82a4d, 0x6d6b0abbbbd6bbb1, 0xe29f87c1c123c146, 0x02a6f153535153a2,
|
||||
0x8ba572dcdc57dcae, 0x2716530b0b2c0b58, 0xd327019d9d4e9d9c, 0xc1d82b6c6cad6c47,
|
||||
0xf562a43131c43195, 0xb9e8f37474cd7487, 0x09f115f6f6fff6e3, 0x438c4c464605460a,
|
||||
0x2645a5acac8aac09, 0x970fb589891e893c, 0x4428b414145014a0, 0x42dfbae1e1a3e15b,
|
||||
0x4e2ca616165816b0, 0xd274f73a3ae83acd, 0xd0d2066969b9696f, 0x2d12410909240948,
|
||||
0xade0d77070dd70a7, 0x54716fb6b6e2b6d9, 0xb7bd1ed0d067d0ce, 0x7ec7d6eded93ed3b,
|
||||
0xdb85e2cccc17cc2e, 0x578468424215422a, 0xc22d2c98985a98b4, 0x0e55eda4a4aaa449,
|
||||
0x8850752828a0285d, 0x31b8865c5c6d5cda, 0x3fed6bf8f8c7f893, 0xa411c28686228644,
|
||||
|
||||
// C4
|
||||
0xc07830d818186018, 0x05af462623238c23, 0x7ef991b8c6c63fc6, 0x136fcdfbe8e887e8,
|
||||
0x4ca113cb87872687, 0xa9626d11b8b8dab8, 0x0805020901010401, 0x426e9e0d4f4f214f,
|
||||
0xadee6c9b3636d836, 0x590451ffa6a6a2a6, 0xdebdb90cd2d26fd2, 0xfb06f70ef5f5f3f5,
|
||||
0xef80f2967979f979, 0x5fcede306f6fa16f, 0xfcef3f6d91917e91, 0xaa07a4f852525552,
|
||||
0x27fdc04760609d60, 0x89766535bcbccabc, 0xaccd2b379b9b569b, 0x048c018a8e8e028e,
|
||||
0x71155bd2a3a3b6a3, 0x603c186c0c0c300c, 0xff8af6847b7bf17b, 0xb5e16a803535d435,
|
||||
0xe8693af51d1d741d, 0x5347ddb3e0e0a7e0, 0xf6acb321d7d77bd7, 0x5eed999cc2c22fc2,
|
||||
0x6d965c432e2eb82e, 0x627a96294b4b314b, 0xa321e15dfefedffe, 0x8216aed557574157,
|
||||
0xa8412abd15155415, 0x9fb6eee87777c177, 0xa5eb6e923737dc37, 0x7b56d79ee5e5b3e5,
|
||||
0x8cd923139f9f469f, 0xd317fd23f0f0e7f0, 0x6a7f94204a4a354a, 0x9e95a944dada4fda,
|
||||
0xfa25b0a258587d58, 0x06ca8fcfc9c903c9, 0x558d527c2929a429, 0x5022145a0a0a280a,
|
||||
0xe14f7f50b1b1feb1, 0x691a5dc9a0a0baa0, 0x7fdad6146b6bb16b, 0x5cab17d985852e85,
|
||||
0x8173673cbdbdcebd, 0xd234ba8f5d5d695d, 0x8050209010104010, 0xf303f507f4f4f7f4,
|
||||
0x16c08bddcbcb0bcb, 0xedc67cd33e3ef83e, 0x28110a2d05051405, 0x1fe6ce7867678167,
|
||||
0x7353d597e4e4b7e4, 0x25bb4e0227279c27, 0x3258827341411941, 0x2c9d0ba78b8b168b,
|
||||
0x510153f6a7a7a6a7, 0xcf94fab27d7de97d, 0xdcfb374995956e95, 0x8e9fad56d8d847d8,
|
||||
0x8b30eb70fbfbcbfb, 0x2371c1cdeeee9fee, 0xc791f8bb7c7ced7c, 0x17e3cc7166668566,
|
||||
0xa68ea77bdddd53dd, 0xb84b2eaf17175c17, 0x02468e4547470147, 0x84dc211a9e9e429e,
|
||||
0x1ec589d4caca0fca, 0x75995a582d2db42d, 0x9179632ebfbfc6bf, 0x381b0e3f07071c07,
|
||||
0x012347acadad8ead, 0xea2fb4b05a5a755a, 0x6cb51bef83833683, 0x85ff66b63333cc33,
|
||||
0x3ff2c65c63639163, 0x100a041202020802, 0x39384993aaaa92aa, 0xafa8e2de7171d971,
|
||||
0x0ecf8dc6c8c807c8, 0xc87d32d119196419, 0x7270923b49493949, 0x869aaf5fd9d943d9,
|
||||
0xc31df931f2f2eff2, 0x4b48dba8e3e3abe3, 0xe22ab6b95b5b715b, 0x34920dbc88881a88,
|
||||
0xa4c8293e9a9a529a, 0x2dbe4c0b26269826, 0x8dfa64bf3232c832, 0xe94a7d59b0b0fab0,
|
||||
0x1b6acff2e9e983e9, 0x78331e770f0f3c0f, 0xe6a6b733d5d573d5, 0x74ba1df480803a80,
|
||||
0x997c6127bebec2be, 0x26de87ebcdcd13cd, 0xbde468893434d034, 0x7a75903248483d48,
|
||||
0xab24e354ffffdbff, 0xf78ff48d7a7af57a, 0xf4ea3d6490907a90, 0xc23ebe9d5f5f615f,
|
||||
0x1da0403d20208020, 0x67d5d00f6868bd68, 0xd07234ca1a1a681a, 0x192c41b7aeae82ae,
|
||||
0xc95e757db4b4eab4, 0x9a19a8ce54544d54, 0xece53b7f93937693, 0x0daa442f22228822,
|
||||
0x07e9c86364648d64, 0xdb12ff2af1f1e3f1, 0xbfa2e6cc7373d173, 0x905a248212124812,
|
||||
0x3a5d807a40401d40, 0x4028104808082008, 0x56e89b95c3c32bc3, 0x337bc5dfecec97ec,
|
||||
0x9690ab4ddbdb4bdb, 0x611f5fc0a1a1bea1, 0x1c8307918d8d0e8d, 0xf5c97ac83d3df43d,
|
||||
0xccf1335b97976697, 0x0000000000000000, 0x36d483f9cfcf1bcf, 0x4587566e2b2bac2b,
|
||||
0x97b3ece17676c576, 0x64b019e682823282, 0xfea9b128d6d67fd6, 0xd87736c31b1b6c1b,
|
||||
0xc15b7774b5b5eeb5, 0x112943beafaf86af, 0x77dfd41d6a6ab56a, 0xba0da0ea50505d50,
|
||||
0x124c8a5745450945, 0xcb18fb38f3f3ebf3, 0x9df060ad3030c030, 0x2b74c3c4efef9bef,
|
||||
0xe5c37eda3f3ffc3f, 0x921caac755554955, 0x791059dba2a2b2a2, 0x0365c9e9eaea8fea,
|
||||
0x0fecca6a65658965, 0xb9686903babad2ba, 0x65935e4a2f2fbc2f, 0x4ee79d8ec0c027c0,
|
||||
0xbe81a160dede5fde, 0xe06c38fc1c1c701c, 0xbb2ee746fdfdd3fd, 0x52649a1f4d4d294d,
|
||||
0xe4e0397692927292, 0x8fbceafa7575c975, 0x301e0c3606061806, 0x249809ae8a8a128a,
|
||||
0xf940794bb2b2f2b2, 0x6359d185e6e6bfe6, 0x70361c7e0e0e380e, 0xf8633ee71f1f7c1f,
|
||||
0x37f7c45562629562, 0xeea3b53ad4d477d4, 0x29324d81a8a89aa8, 0xc4f4315296966296,
|
||||
0x9b3aef62f9f9c3f9, 0x66f697a3c5c533c5, 0x35b14a1025259425, 0xf220b2ab59597959,
|
||||
0x54ae15d084842a84, 0xb7a7e4c57272d572, 0xd5dd72ec3939e439, 0x5a6198164c4c2d4c,
|
||||
0xca3bbc945e5e655e, 0xe785f09f7878fd78, 0xddd870e53838e038, 0x148605988c8c0a8c,
|
||||
0xc6b2bf17d1d163d1, 0x410b57e4a5a5aea5, 0x434dd9a1e2e2afe2, 0x2ff8c24e61619961,
|
||||
0xf1457b42b3b3f6b3, 0x15a5423421218421, 0x94d625089c9c4a9c, 0xf0663cee1e1e781e,
|
||||
0x2252866143431143, 0x76fc93b1c7c73bc7, 0xb32be54ffcfcd7fc, 0x2014082404041004,
|
||||
0xb208a2e351515951, 0xbcc72f2599995e99, 0x4fc4da226d6da96d, 0x68391a650d0d340d,
|
||||
0x8335e979fafacffa, 0xb684a369dfdf5bdf, 0xd79bfca97e7ee57e, 0x3db4481924249024,
|
||||
0xc5d776fe3b3bec3b, 0x313d4b9aabab96ab, 0x3ed181f0cece1fce, 0x8855229911114411,
|
||||
0x0c8903838f8f068f, 0x4a6b9c044e4e254e, 0xd1517366b7b7e6b7, 0x0b60cbe0ebeb8beb,
|
||||
0xfdcc78c13c3cf03c, 0x7cbf1ffd81813e81, 0xd4fe354094946a94, 0xeb0cf31cf7f7fbf7,
|
||||
0xa1676f18b9b9deb9, 0x985f268b13134c13, 0x7d9c58512c2cb02c, 0xd6b8bb05d3d36bd3,
|
||||
0x6b5cd38ce7e7bbe7, 0x57cbdc396e6ea56e, 0x6ef395aac4c437c4, 0x180f061b03030c03,
|
||||
0x8a13acdc56564556, 0x1a49885e44440d44, 0xdf9efea07f7fe17f, 0x21374f88a9a99ea9,
|
||||
0x4d8254672a2aa82a, 0xb16d6b0abbbbd6bb, 0x46e29f87c1c123c1, 0xa202a6f153535153,
|
||||
0xae8ba572dcdc57dc, 0x582716530b0b2c0b, 0x9cd327019d9d4e9d, 0x47c1d82b6c6cad6c,
|
||||
0x95f562a43131c431, 0x87b9e8f37474cd74, 0xe309f115f6f6fff6, 0x0a438c4c46460546,
|
||||
0x092645a5acac8aac, 0x3c970fb589891e89, 0xa04428b414145014, 0x5b42dfbae1e1a3e1,
|
||||
0xb04e2ca616165816, 0xcdd274f73a3ae83a, 0x6fd0d2066969b969, 0x482d124109092409,
|
||||
0xa7ade0d77070dd70, 0xd954716fb6b6e2b6, 0xceb7bd1ed0d067d0, 0x3b7ec7d6eded93ed,
|
||||
0x2edb85e2cccc17cc, 0x2a57846842421542, 0xb4c22d2c98985a98, 0x490e55eda4a4aaa4,
|
||||
0x5d8850752828a028, 0xda31b8865c5c6d5c, 0x933fed6bf8f8c7f8, 0x44a411c286862286,
|
||||
|
||||
// C5
|
||||
0x18c07830d8181860, 0x2305af462623238c, 0xc67ef991b8c6c63f, 0xe8136fcdfbe8e887,
|
||||
0x874ca113cb878726, 0xb8a9626d11b8b8da, 0x0108050209010104, 0x4f426e9e0d4f4f21,
|
||||
0x36adee6c9b3636d8, 0xa6590451ffa6a6a2, 0xd2debdb90cd2d26f, 0xf5fb06f70ef5f5f3,
|
||||
0x79ef80f2967979f9, 0x6f5fcede306f6fa1, 0x91fcef3f6d91917e, 0x52aa07a4f8525255,
|
||||
0x6027fdc04760609d, 0xbc89766535bcbcca, 0x9baccd2b379b9b56, 0x8e048c018a8e8e02,
|
||||
0xa371155bd2a3a3b6, 0x0c603c186c0c0c30, 0x7bff8af6847b7bf1, 0x35b5e16a803535d4,
|
||||
0x1de8693af51d1d74, 0xe05347ddb3e0e0a7, 0xd7f6acb321d7d77b, 0xc25eed999cc2c22f,
|
||||
0x2e6d965c432e2eb8, 0x4b627a96294b4b31, 0xfea321e15dfefedf, 0x578216aed5575741,
|
||||
0x15a8412abd151554, 0x779fb6eee87777c1, 0x37a5eb6e923737dc, 0xe57b56d79ee5e5b3,
|
||||
0x9f8cd923139f9f46, 0xf0d317fd23f0f0e7, 0x4a6a7f94204a4a35, 0xda9e95a944dada4f,
|
||||
0x58fa25b0a258587d, 0xc906ca8fcfc9c903, 0x29558d527c2929a4, 0x0a5022145a0a0a28,
|
||||
0xb1e14f7f50b1b1fe, 0xa0691a5dc9a0a0ba, 0x6b7fdad6146b6bb1, 0x855cab17d985852e,
|
||||
0xbd8173673cbdbdce, 0x5dd234ba8f5d5d69, 0x1080502090101040, 0xf4f303f507f4f4f7,
|
||||
0xcb16c08bddcbcb0b, 0x3eedc67cd33e3ef8, 0x0528110a2d050514, 0x671fe6ce78676781,
|
||||
0xe47353d597e4e4b7, 0x2725bb4e0227279c, 0x4132588273414119, 0x8b2c9d0ba78b8b16,
|
||||
0xa7510153f6a7a7a6, 0x7dcf94fab27d7de9, 0x95dcfb374995956e, 0xd88e9fad56d8d847,
|
||||
0xfb8b30eb70fbfbcb, 0xee2371c1cdeeee9f, 0x7cc791f8bb7c7ced, 0x6617e3cc71666685,
|
||||
0xdda68ea77bdddd53, 0x17b84b2eaf17175c, 0x4702468e45474701, 0x9e84dc211a9e9e42,
|
||||
0xca1ec589d4caca0f, 0x2d75995a582d2db4, 0xbf9179632ebfbfc6, 0x07381b0e3f07071c,
|
||||
0xad012347acadad8e, 0x5aea2fb4b05a5a75, 0x836cb51bef838336, 0x3385ff66b63333cc,
|
||||
0x633ff2c65c636391, 0x02100a0412020208, 0xaa39384993aaaa92, 0x71afa8e2de7171d9,
|
||||
0xc80ecf8dc6c8c807, 0x19c87d32d1191964, 0x497270923b494939, 0xd9869aaf5fd9d943,
|
||||
0xf2c31df931f2f2ef, 0xe34b48dba8e3e3ab, 0x5be22ab6b95b5b71, 0x8834920dbc88881a,
|
||||
0x9aa4c8293e9a9a52, 0x262dbe4c0b262698, 0x328dfa64bf3232c8, 0xb0e94a7d59b0b0fa,
|
||||
0xe91b6acff2e9e983, 0x0f78331e770f0f3c, 0xd5e6a6b733d5d573, 0x8074ba1df480803a,
|
||||
0xbe997c6127bebec2, 0xcd26de87ebcdcd13, 0x34bde468893434d0, 0x487a75903248483d,
|
||||
0xffab24e354ffffdb, 0x7af78ff48d7a7af5, 0x90f4ea3d6490907a, 0x5fc23ebe9d5f5f61,
|
||||
0x201da0403d202080, 0x6867d5d00f6868bd, 0x1ad07234ca1a1a68, 0xae192c41b7aeae82,
|
||||
0xb4c95e757db4b4ea, 0x549a19a8ce54544d, 0x93ece53b7f939376, 0x220daa442f222288,
|
||||
0x6407e9c86364648d, 0xf1db12ff2af1f1e3, 0x73bfa2e6cc7373d1, 0x12905a2482121248,
|
||||
0x403a5d807a40401d, 0x0840281048080820, 0xc356e89b95c3c32b, 0xec337bc5dfecec97,
|
||||
0xdb9690ab4ddbdb4b, 0xa1611f5fc0a1a1be, 0x8d1c8307918d8d0e, 0x3df5c97ac83d3df4,
|
||||
0x97ccf1335b979766, 0x0000000000000000, 0xcf36d483f9cfcf1b, 0x2b4587566e2b2bac,
|
||||
0x7697b3ece17676c5, 0x8264b019e6828232, 0xd6fea9b128d6d67f, 0x1bd87736c31b1b6c,
|
||||
0xb5c15b7774b5b5ee, 0xaf112943beafaf86, 0x6a77dfd41d6a6ab5, 0x50ba0da0ea50505d,
|
||||
0x45124c8a57454509, 0xf3cb18fb38f3f3eb, 0x309df060ad3030c0, 0xef2b74c3c4efef9b,
|
||||
0x3fe5c37eda3f3ffc, 0x55921caac7555549, 0xa2791059dba2a2b2, 0xea0365c9e9eaea8f,
|
||||
0x650fecca6a656589, 0xbab9686903babad2, 0x2f65935e4a2f2fbc, 0xc04ee79d8ec0c027,
|
||||
0xdebe81a160dede5f, 0x1ce06c38fc1c1c70, 0xfdbb2ee746fdfdd3, 0x4d52649a1f4d4d29,
|
||||
0x92e4e03976929272, 0x758fbceafa7575c9, 0x06301e0c36060618, 0x8a249809ae8a8a12,
|
||||
0xb2f940794bb2b2f2, 0xe66359d185e6e6bf, 0x0e70361c7e0e0e38, 0x1ff8633ee71f1f7c,
|
||||
0x6237f7c455626295, 0xd4eea3b53ad4d477, 0xa829324d81a8a89a, 0x96c4f43152969662,
|
||||
0xf99b3aef62f9f9c3, 0xc566f697a3c5c533, 0x2535b14a10252594, 0x59f220b2ab595979,
|
||||
0x8454ae15d084842a, 0x72b7a7e4c57272d5, 0x39d5dd72ec3939e4, 0x4c5a6198164c4c2d,
|
||||
0x5eca3bbc945e5e65, 0x78e785f09f7878fd, 0x38ddd870e53838e0, 0x8c148605988c8c0a,
|
||||
0xd1c6b2bf17d1d163, 0xa5410b57e4a5a5ae, 0xe2434dd9a1e2e2af, 0x612ff8c24e616199,
|
||||
0xb3f1457b42b3b3f6, 0x2115a54234212184, 0x9c94d625089c9c4a, 0x1ef0663cee1e1e78,
|
||||
0x4322528661434311, 0xc776fc93b1c7c73b, 0xfcb32be54ffcfcd7, 0x0420140824040410,
|
||||
0x51b208a2e3515159, 0x99bcc72f2599995e, 0x6d4fc4da226d6da9, 0x0d68391a650d0d34,
|
||||
0xfa8335e979fafacf, 0xdfb684a369dfdf5b, 0x7ed79bfca97e7ee5, 0x243db44819242490,
|
||||
0x3bc5d776fe3b3bec, 0xab313d4b9aabab96, 0xce3ed181f0cece1f, 0x1188552299111144,
|
||||
0x8f0c8903838f8f06, 0x4e4a6b9c044e4e25, 0xb7d1517366b7b7e6, 0xeb0b60cbe0ebeb8b,
|
||||
0x3cfdcc78c13c3cf0, 0x817cbf1ffd81813e, 0x94d4fe354094946a, 0xf7eb0cf31cf7f7fb,
|
||||
0xb9a1676f18b9b9de, 0x13985f268b13134c, 0x2c7d9c58512c2cb0, 0xd3d6b8bb05d3d36b,
|
||||
0xe76b5cd38ce7e7bb, 0x6e57cbdc396e6ea5, 0xc46ef395aac4c437, 0x03180f061b03030c,
|
||||
0x568a13acdc565645, 0x441a49885e44440d, 0x7fdf9efea07f7fe1, 0xa921374f88a9a99e,
|
||||
0x2a4d8254672a2aa8, 0xbbb16d6b0abbbbd6, 0xc146e29f87c1c123, 0x53a202a6f1535351,
|
||||
0xdcae8ba572dcdc57, 0x0b582716530b0b2c, 0x9d9cd327019d9d4e, 0x6c47c1d82b6c6cad,
|
||||
0x3195f562a43131c4, 0x7487b9e8f37474cd, 0xf6e309f115f6f6ff, 0x460a438c4c464605,
|
||||
0xac092645a5acac8a, 0x893c970fb589891e, 0x14a04428b4141450, 0xe15b42dfbae1e1a3,
|
||||
0x16b04e2ca6161658, 0x3acdd274f73a3ae8, 0x696fd0d2066969b9, 0x09482d1241090924,
|
||||
0x70a7ade0d77070dd, 0xb6d954716fb6b6e2, 0xd0ceb7bd1ed0d067, 0xed3b7ec7d6eded93,
|
||||
0xcc2edb85e2cccc17, 0x422a578468424215, 0x98b4c22d2c98985a, 0xa4490e55eda4a4aa,
|
||||
0x285d8850752828a0, 0x5cda31b8865c5c6d, 0xf8933fed6bf8f8c7, 0x8644a411c2868622,
|
||||
|
||||
// C6
|
||||
0x6018c07830d81818, 0x8c2305af46262323, 0x3fc67ef991b8c6c6, 0x87e8136fcdfbe8e8,
|
||||
0x26874ca113cb8787, 0xdab8a9626d11b8b8, 0x0401080502090101, 0x214f426e9e0d4f4f,
|
||||
0xd836adee6c9b3636, 0xa2a6590451ffa6a6, 0x6fd2debdb90cd2d2, 0xf3f5fb06f70ef5f5,
|
||||
0xf979ef80f2967979, 0xa16f5fcede306f6f, 0x7e91fcef3f6d9191, 0x5552aa07a4f85252,
|
||||
0x9d6027fdc0476060, 0xcabc89766535bcbc, 0x569baccd2b379b9b, 0x028e048c018a8e8e,
|
||||
0xb6a371155bd2a3a3, 0x300c603c186c0c0c, 0xf17bff8af6847b7b, 0xd435b5e16a803535,
|
||||
0x741de8693af51d1d, 0xa7e05347ddb3e0e0, 0x7bd7f6acb321d7d7, 0x2fc25eed999cc2c2,
|
||||
0xb82e6d965c432e2e, 0x314b627a96294b4b, 0xdffea321e15dfefe, 0x41578216aed55757,
|
||||
0x5415a8412abd1515, 0xc1779fb6eee87777, 0xdc37a5eb6e923737, 0xb3e57b56d79ee5e5,
|
||||
0x469f8cd923139f9f, 0xe7f0d317fd23f0f0, 0x354a6a7f94204a4a, 0x4fda9e95a944dada,
|
||||
0x7d58fa25b0a25858, 0x03c906ca8fcfc9c9, 0xa429558d527c2929, 0x280a5022145a0a0a,
|
||||
0xfeb1e14f7f50b1b1, 0xbaa0691a5dc9a0a0, 0xb16b7fdad6146b6b, 0x2e855cab17d98585,
|
||||
0xcebd8173673cbdbd, 0x695dd234ba8f5d5d, 0x4010805020901010, 0xf7f4f303f507f4f4,
|
||||
0x0bcb16c08bddcbcb, 0xf83eedc67cd33e3e, 0x140528110a2d0505, 0x81671fe6ce786767,
|
||||
0xb7e47353d597e4e4, 0x9c2725bb4e022727, 0x1941325882734141, 0x168b2c9d0ba78b8b,
|
||||
0xa6a7510153f6a7a7, 0xe97dcf94fab27d7d, 0x6e95dcfb37499595, 0x47d88e9fad56d8d8,
|
||||
0xcbfb8b30eb70fbfb, 0x9fee2371c1cdeeee, 0xed7cc791f8bb7c7c, 0x856617e3cc716666,
|
||||
0x53dda68ea77bdddd, 0x5c17b84b2eaf1717, 0x014702468e454747, 0x429e84dc211a9e9e,
|
||||
0x0fca1ec589d4caca, 0xb42d75995a582d2d, 0xc6bf9179632ebfbf, 0x1c07381b0e3f0707,
|
||||
0x8ead012347acadad, 0x755aea2fb4b05a5a, 0x36836cb51bef8383, 0xcc3385ff66b63333,
|
||||
0x91633ff2c65c6363, 0x0802100a04120202, 0x92aa39384993aaaa, 0xd971afa8e2de7171,
|
||||
0x07c80ecf8dc6c8c8, 0x6419c87d32d11919, 0x39497270923b4949, 0x43d9869aaf5fd9d9,
|
||||
0xeff2c31df931f2f2, 0xabe34b48dba8e3e3, 0x715be22ab6b95b5b, 0x1a8834920dbc8888,
|
||||
0x529aa4c8293e9a9a, 0x98262dbe4c0b2626, 0xc8328dfa64bf3232, 0xfab0e94a7d59b0b0,
|
||||
0x83e91b6acff2e9e9, 0x3c0f78331e770f0f, 0x73d5e6a6b733d5d5, 0x3a8074ba1df48080,
|
||||
0xc2be997c6127bebe, 0x13cd26de87ebcdcd, 0xd034bde468893434, 0x3d487a7590324848,
|
||||
0xdbffab24e354ffff, 0xf57af78ff48d7a7a, 0x7a90f4ea3d649090, 0x615fc23ebe9d5f5f,
|
||||
0x80201da0403d2020, 0xbd6867d5d00f6868, 0x681ad07234ca1a1a, 0x82ae192c41b7aeae,
|
||||
0xeab4c95e757db4b4, 0x4d549a19a8ce5454, 0x7693ece53b7f9393, 0x88220daa442f2222,
|
||||
0x8d6407e9c8636464, 0xe3f1db12ff2af1f1, 0xd173bfa2e6cc7373, 0x4812905a24821212,
|
||||
0x1d403a5d807a4040, 0x2008402810480808, 0x2bc356e89b95c3c3, 0x97ec337bc5dfecec,
|
||||
0x4bdb9690ab4ddbdb, 0xbea1611f5fc0a1a1, 0x0e8d1c8307918d8d, 0xf43df5c97ac83d3d,
|
||||
0x6697ccf1335b9797, 0x0000000000000000, 0x1bcf36d483f9cfcf, 0xac2b4587566e2b2b,
|
||||
0xc57697b3ece17676, 0x328264b019e68282, 0x7fd6fea9b128d6d6, 0x6c1bd87736c31b1b,
|
||||
0xeeb5c15b7774b5b5, 0x86af112943beafaf, 0xb56a77dfd41d6a6a, 0x5d50ba0da0ea5050,
|
||||
0x0945124c8a574545, 0xebf3cb18fb38f3f3, 0xc0309df060ad3030, 0x9bef2b74c3c4efef,
|
||||
0xfc3fe5c37eda3f3f, 0x4955921caac75555, 0xb2a2791059dba2a2, 0x8fea0365c9e9eaea,
|
||||
0x89650fecca6a6565, 0xd2bab9686903baba, 0xbc2f65935e4a2f2f, 0x27c04ee79d8ec0c0,
|
||||
0x5fdebe81a160dede, 0x701ce06c38fc1c1c, 0xd3fdbb2ee746fdfd, 0x294d52649a1f4d4d,
|
||||
0x7292e4e039769292, 0xc9758fbceafa7575, 0x1806301e0c360606, 0x128a249809ae8a8a,
|
||||
0xf2b2f940794bb2b2, 0xbfe66359d185e6e6, 0x380e70361c7e0e0e, 0x7c1ff8633ee71f1f,
|
||||
0x956237f7c4556262, 0x77d4eea3b53ad4d4, 0x9aa829324d81a8a8, 0x6296c4f431529696,
|
||||
0xc3f99b3aef62f9f9, 0x33c566f697a3c5c5, 0x942535b14a102525, 0x7959f220b2ab5959,
|
||||
0x2a8454ae15d08484, 0xd572b7a7e4c57272, 0xe439d5dd72ec3939, 0x2d4c5a6198164c4c,
|
||||
0x655eca3bbc945e5e, 0xfd78e785f09f7878, 0xe038ddd870e53838, 0x0a8c148605988c8c,
|
||||
0x63d1c6b2bf17d1d1, 0xaea5410b57e4a5a5, 0xafe2434dd9a1e2e2, 0x99612ff8c24e6161,
|
||||
0xf6b3f1457b42b3b3, 0x842115a542342121, 0x4a9c94d625089c9c, 0x781ef0663cee1e1e,
|
||||
0x1143225286614343, 0x3bc776fc93b1c7c7, 0xd7fcb32be54ffcfc, 0x1004201408240404,
|
||||
0x5951b208a2e35151, 0x5e99bcc72f259999, 0xa96d4fc4da226d6d, 0x340d68391a650d0d,
|
||||
0xcffa8335e979fafa, 0x5bdfb684a369dfdf, 0xe57ed79bfca97e7e, 0x90243db448192424,
|
||||
0xec3bc5d776fe3b3b, 0x96ab313d4b9aabab, 0x1fce3ed181f0cece, 0x4411885522991111,
|
||||
0x068f0c8903838f8f, 0x254e4a6b9c044e4e, 0xe6b7d1517366b7b7, 0x8beb0b60cbe0ebeb,
|
||||
0xf03cfdcc78c13c3c, 0x3e817cbf1ffd8181, 0x6a94d4fe35409494, 0xfbf7eb0cf31cf7f7,
|
||||
0xdeb9a1676f18b9b9, 0x4c13985f268b1313, 0xb02c7d9c58512c2c, 0x6bd3d6b8bb05d3d3,
|
||||
0xbbe76b5cd38ce7e7, 0xa56e57cbdc396e6e, 0x37c46ef395aac4c4, 0x0c03180f061b0303,
|
||||
0x45568a13acdc5656, 0x0d441a49885e4444, 0xe17fdf9efea07f7f, 0x9ea921374f88a9a9,
|
||||
0xa82a4d8254672a2a, 0xd6bbb16d6b0abbbb, 0x23c146e29f87c1c1, 0x5153a202a6f15353,
|
||||
0x57dcae8ba572dcdc, 0x2c0b582716530b0b, 0x4e9d9cd327019d9d, 0xad6c47c1d82b6c6c,
|
||||
0xc43195f562a43131, 0xcd7487b9e8f37474, 0xfff6e309f115f6f6, 0x05460a438c4c4646,
|
||||
0x8aac092645a5acac, 0x1e893c970fb58989, 0x5014a04428b41414, 0xa3e15b42dfbae1e1,
|
||||
0x5816b04e2ca61616, 0xe83acdd274f73a3a, 0xb9696fd0d2066969, 0x2409482d12410909,
|
||||
0xdd70a7ade0d77070, 0xe2b6d954716fb6b6, 0x67d0ceb7bd1ed0d0, 0x93ed3b7ec7d6eded,
|
||||
0x17cc2edb85e2cccc, 0x15422a5784684242, 0x5a98b4c22d2c9898, 0xaaa4490e55eda4a4,
|
||||
0xa0285d8850752828, 0x6d5cda31b8865c5c, 0xc7f8933fed6bf8f8, 0x228644a411c28686,
|
||||
|
||||
// C7
|
||||
0x186018c07830d818, 0x238c2305af462623, 0xc63fc67ef991b8c6, 0xe887e8136fcdfbe8,
|
||||
0x8726874ca113cb87, 0xb8dab8a9626d11b8, 0x0104010805020901, 0x4f214f426e9e0d4f,
|
||||
0x36d836adee6c9b36, 0xa6a2a6590451ffa6, 0xd26fd2debdb90cd2, 0xf5f3f5fb06f70ef5,
|
||||
0x79f979ef80f29679, 0x6fa16f5fcede306f, 0x917e91fcef3f6d91, 0x525552aa07a4f852,
|
||||
0x609d6027fdc04760, 0xbccabc89766535bc, 0x9b569baccd2b379b, 0x8e028e048c018a8e,
|
||||
0xa3b6a371155bd2a3, 0x0c300c603c186c0c, 0x7bf17bff8af6847b, 0x35d435b5e16a8035,
|
||||
0x1d741de8693af51d, 0xe0a7e05347ddb3e0, 0xd77bd7f6acb321d7, 0xc22fc25eed999cc2,
|
||||
0x2eb82e6d965c432e, 0x4b314b627a96294b, 0xfedffea321e15dfe, 0x5741578216aed557,
|
||||
0x155415a8412abd15, 0x77c1779fb6eee877, 0x37dc37a5eb6e9237, 0xe5b3e57b56d79ee5,
|
||||
0x9f469f8cd923139f, 0xf0e7f0d317fd23f0, 0x4a354a6a7f94204a, 0xda4fda9e95a944da,
|
||||
0x587d58fa25b0a258, 0xc903c906ca8fcfc9, 0x29a429558d527c29, 0x0a280a5022145a0a,
|
||||
0xb1feb1e14f7f50b1, 0xa0baa0691a5dc9a0, 0x6bb16b7fdad6146b, 0x852e855cab17d985,
|
||||
0xbdcebd8173673cbd, 0x5d695dd234ba8f5d, 0x1040108050209010, 0xf4f7f4f303f507f4,
|
||||
0xcb0bcb16c08bddcb, 0x3ef83eedc67cd33e, 0x05140528110a2d05, 0x6781671fe6ce7867,
|
||||
0xe4b7e47353d597e4, 0x279c2725bb4e0227, 0x4119413258827341, 0x8b168b2c9d0ba78b,
|
||||
0xa7a6a7510153f6a7, 0x7de97dcf94fab27d, 0x956e95dcfb374995, 0xd847d88e9fad56d8,
|
||||
0xfbcbfb8b30eb70fb, 0xee9fee2371c1cdee, 0x7ced7cc791f8bb7c, 0x66856617e3cc7166,
|
||||
0xdd53dda68ea77bdd, 0x175c17b84b2eaf17, 0x47014702468e4547, 0x9e429e84dc211a9e,
|
||||
0xca0fca1ec589d4ca, 0x2db42d75995a582d, 0xbfc6bf9179632ebf, 0x071c07381b0e3f07,
|
||||
0xad8ead012347acad, 0x5a755aea2fb4b05a, 0x8336836cb51bef83, 0x33cc3385ff66b633,
|
||||
0x6391633ff2c65c63, 0x020802100a041202, 0xaa92aa39384993aa, 0x71d971afa8e2de71,
|
||||
0xc807c80ecf8dc6c8, 0x196419c87d32d119, 0x4939497270923b49, 0xd943d9869aaf5fd9,
|
||||
0xf2eff2c31df931f2, 0xe3abe34b48dba8e3, 0x5b715be22ab6b95b, 0x881a8834920dbc88,
|
||||
0x9a529aa4c8293e9a, 0x2698262dbe4c0b26, 0x32c8328dfa64bf32, 0xb0fab0e94a7d59b0,
|
||||
0xe983e91b6acff2e9, 0x0f3c0f78331e770f, 0xd573d5e6a6b733d5, 0x803a8074ba1df480,
|
||||
0xbec2be997c6127be, 0xcd13cd26de87ebcd, 0x34d034bde4688934, 0x483d487a75903248,
|
||||
0xffdbffab24e354ff, 0x7af57af78ff48d7a, 0x907a90f4ea3d6490, 0x5f615fc23ebe9d5f,
|
||||
0x2080201da0403d20, 0x68bd6867d5d00f68, 0x1a681ad07234ca1a, 0xae82ae192c41b7ae,
|
||||
0xb4eab4c95e757db4, 0x544d549a19a8ce54, 0x937693ece53b7f93, 0x2288220daa442f22,
|
||||
0x648d6407e9c86364, 0xf1e3f1db12ff2af1, 0x73d173bfa2e6cc73, 0x124812905a248212,
|
||||
0x401d403a5d807a40, 0x0820084028104808, 0xc32bc356e89b95c3, 0xec97ec337bc5dfec,
|
||||
0xdb4bdb9690ab4ddb, 0xa1bea1611f5fc0a1, 0x8d0e8d1c8307918d, 0x3df43df5c97ac83d,
|
||||
0x976697ccf1335b97, 0x0000000000000000, 0xcf1bcf36d483f9cf, 0x2bac2b4587566e2b,
|
||||
0x76c57697b3ece176, 0x82328264b019e682, 0xd67fd6fea9b128d6, 0x1b6c1bd87736c31b,
|
||||
0xb5eeb5c15b7774b5, 0xaf86af112943beaf, 0x6ab56a77dfd41d6a, 0x505d50ba0da0ea50,
|
||||
0x450945124c8a5745, 0xf3ebf3cb18fb38f3, 0x30c0309df060ad30, 0xef9bef2b74c3c4ef,
|
||||
0x3ffc3fe5c37eda3f, 0x554955921caac755, 0xa2b2a2791059dba2, 0xea8fea0365c9e9ea,
|
||||
0x6589650fecca6a65, 0xbad2bab9686903ba, 0x2fbc2f65935e4a2f, 0xc027c04ee79d8ec0,
|
||||
0xde5fdebe81a160de, 0x1c701ce06c38fc1c, 0xfdd3fdbb2ee746fd, 0x4d294d52649a1f4d,
|
||||
0x927292e4e0397692, 0x75c9758fbceafa75, 0x061806301e0c3606, 0x8a128a249809ae8a,
|
||||
0xb2f2b2f940794bb2, 0xe6bfe66359d185e6, 0x0e380e70361c7e0e, 0x1f7c1ff8633ee71f,
|
||||
0x62956237f7c45562, 0xd477d4eea3b53ad4, 0xa89aa829324d81a8, 0x966296c4f4315296,
|
||||
0xf9c3f99b3aef62f9, 0xc533c566f697a3c5, 0x25942535b14a1025, 0x597959f220b2ab59,
|
||||
0x842a8454ae15d084, 0x72d572b7a7e4c572, 0x39e439d5dd72ec39, 0x4c2d4c5a6198164c,
|
||||
0x5e655eca3bbc945e, 0x78fd78e785f09f78, 0x38e038ddd870e538, 0x8c0a8c148605988c,
|
||||
0xd163d1c6b2bf17d1, 0xa5aea5410b57e4a5, 0xe2afe2434dd9a1e2, 0x6199612ff8c24e61,
|
||||
0xb3f6b3f1457b42b3, 0x21842115a5423421, 0x9c4a9c94d625089c, 0x1e781ef0663cee1e,
|
||||
0x4311432252866143, 0xc73bc776fc93b1c7, 0xfcd7fcb32be54ffc, 0x0410042014082404,
|
||||
0x515951b208a2e351, 0x995e99bcc72f2599, 0x6da96d4fc4da226d, 0x0d340d68391a650d,
|
||||
0xfacffa8335e979fa, 0xdf5bdfb684a369df, 0x7ee57ed79bfca97e, 0x2490243db4481924,
|
||||
0x3bec3bc5d776fe3b, 0xab96ab313d4b9aab, 0xce1fce3ed181f0ce, 0x1144118855229911,
|
||||
0x8f068f0c8903838f, 0x4e254e4a6b9c044e, 0xb7e6b7d1517366b7, 0xeb8beb0b60cbe0eb,
|
||||
0x3cf03cfdcc78c13c, 0x813e817cbf1ffd81, 0x946a94d4fe354094, 0xf7fbf7eb0cf31cf7,
|
||||
0xb9deb9a1676f18b9, 0x134c13985f268b13, 0x2cb02c7d9c58512c, 0xd36bd3d6b8bb05d3,
|
||||
0xe7bbe76b5cd38ce7, 0x6ea56e57cbdc396e, 0xc437c46ef395aac4, 0x030c03180f061b03,
|
||||
0x5645568a13acdc56, 0x440d441a49885e44, 0x7fe17fdf9efea07f, 0xa99ea921374f88a9,
|
||||
0x2aa82a4d8254672a, 0xbbd6bbb16d6b0abb, 0xc123c146e29f87c1, 0x535153a202a6f153,
|
||||
0xdc57dcae8ba572dc, 0x0b2c0b582716530b, 0x9d4e9d9cd327019d, 0x6cad6c47c1d82b6c,
|
||||
0x31c43195f562a431, 0x74cd7487b9e8f374, 0xf6fff6e309f115f6, 0x4605460a438c4c46,
|
||||
0xac8aac092645a5ac, 0x891e893c970fb589, 0x145014a04428b414, 0xe1a3e15b42dfbae1,
|
||||
0x165816b04e2ca616, 0x3ae83acdd274f73a, 0x69b9696fd0d20669, 0x092409482d124109,
|
||||
0x70dd70a7ade0d770, 0xb6e2b6d954716fb6, 0xd067d0ceb7bd1ed0, 0xed93ed3b7ec7d6ed,
|
||||
0xcc17cc2edb85e2cc, 0x4215422a57846842, 0x985a98b4c22d2c98, 0xa4aaa4490e55eda4,
|
||||
0x28a0285d88507528, 0x5c6d5cda31b8865c, 0xf8c7f8933fed6bf8, 0x86228644a411c286,
|
||||
};
|
||||
56
lib/std/hash/wyhash2.c3
Normal file
56
lib/std/hash/wyhash2.c3
Normal file
@@ -0,0 +1,56 @@
|
||||
// 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.
|
||||
//
|
||||
// An implementation of Wang Yi's wyhash(2) algorithm in C3:
|
||||
// https://github.com/wangyi-fudan/wyhash
|
||||
//
|
||||
module std::hash::wyhash2;
|
||||
|
||||
|
||||
fn ulong wyr3(char* in, usz len) @inline
|
||||
=> ((ulong)in[0] << 16) | ((ulong)in[len >> 1] << 8) | (ulong)in[len - 1];
|
||||
|
||||
|
||||
// See: https://docs.google.com/spreadsheets/d/1HmqDj-suH4wBFNg7etwE8WVBlfCufvD5-gAnIENs94k/edit?gid=1915335726#gid=1915335726
|
||||
// Credit to article:
|
||||
// https://medium.com/@tprodanov/benchmarking-non-cryptographic-hash-functions-in-rust-2e6091077d11
|
||||
//
|
||||
// wyhash2 has a >90% chance of collisions when its input data is above 16 bytes in length.
|
||||
// However, it is the fastest performing and most evenly randomized hash for very low-length inputs,
|
||||
// making it an ideal candidate for hashing primitive data types quickly and making things like hash
|
||||
// tables even faster. Therefore, a 16-byte input limit is imposed on all calls to the hash function.
|
||||
//
|
||||
<*
|
||||
@require input.len <= 16 : `wyhash2 is not useable for inputs over 16 bytes in length.`
|
||||
*>
|
||||
fn ulong hash(char[] input, ulong seed = 0)
|
||||
{
|
||||
seed ^= 0xa076_1d64_78bd_642f;
|
||||
|
||||
ulong a, b;
|
||||
|
||||
if (@likely(input.len <= 8)) // more likely to encounter 8-byte or lower type here
|
||||
{
|
||||
if (@likely(input.len >= 4))
|
||||
{
|
||||
a = (ulong)@unaligned_load(*(uint*)input.ptr, 1); // first 4 bytes widened to a u64
|
||||
b = (ulong)@unaligned_load(*(uint*)&input[^4], 1); // a walking 4-byte window based on input.len
|
||||
}
|
||||
else if (input.len > 0)
|
||||
{
|
||||
a = wyr3(input, input.len);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
a = @unaligned_load(*(ulong*)input.ptr, 1); // first 8 bytes
|
||||
b = @unaligned_load(*(ulong*)&input[^8], 1); // a walking 8-byte window based on input.len
|
||||
}
|
||||
|
||||
uint128 r = ((uint128)a ^ 0xe703_7ed1_a0b4_28db) * ((uint128)b ^ seed);
|
||||
ulong pre_res = (ulong)r ^ (ulong)(r >> 64);
|
||||
|
||||
r = ((uint128)0xe703_7ed1_a0b4_28db ^ input.len) * (uint128)pre_res;
|
||||
return (ulong)r ^ (ulong)(r >> 64);
|
||||
}
|
||||
92
lib/std/io/file_mmap.c3
Normal file
92
lib/std/io/file_mmap.c3
Normal file
@@ -0,0 +1,92 @@
|
||||
module std::io::file::mmap @if(env::LIBC &&& env::POSIX);
|
||||
import std::core::mem::vm, std::io::file;
|
||||
|
||||
struct FileMmap
|
||||
{
|
||||
File file;
|
||||
VirtualMemory vm;
|
||||
usz offset;
|
||||
usz len;
|
||||
}
|
||||
|
||||
<*
|
||||
Provides a slice of bytes to the expected mapped range discarding the extra bytes due to misaligment of offset and/or size.
|
||||
|
||||
@return "Slice of the mapped range where the first byte matches the file's byte at the offset specified to File::file_mmap()"
|
||||
*>
|
||||
fn char[] FileMmap.bytes(&self)
|
||||
{
|
||||
return self.vm.ptr[self.offset:self.len];
|
||||
}
|
||||
|
||||
<*
|
||||
Destroys the underlyng VirtualMemory object ie. calls munmap()"
|
||||
*>
|
||||
fn void? FileMmap.destroy(&self) @maydiscard
|
||||
{
|
||||
fault err1 = @catch(self.file.close());
|
||||
fault err2 = @catch(self.vm.destroy());
|
||||
if (err1) return err1?;
|
||||
if (err2) return err2?;
|
||||
}
|
||||
|
||||
module std::io::file @if(env::LIBC &&& env::POSIX);
|
||||
|
||||
<*
|
||||
Maps a region of an already-opened file into memory
|
||||
|
||||
@param file : "Already opened file created on the caller scope"
|
||||
@param offset : "Byte offset in file, will be rounded down to page size"
|
||||
@param len : "Size in bytes to map starting from offset, will be rounded up to page size"
|
||||
@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)
|
||||
{
|
||||
if (len == 0)
|
||||
{
|
||||
usz cur = file.seek(0, CURSOR)!;
|
||||
defer file.seek(cur, SET)!!;
|
||||
usz file_size = file.seek(0, END)!;
|
||||
len = file_size - offset;
|
||||
}
|
||||
|
||||
// get the page size
|
||||
usz page_size = vm::aligned_alloc_size(0);
|
||||
|
||||
// align the offset specified by the user (might be not aligned)
|
||||
usz page_offset = offset & (page_size - 1);
|
||||
usz map_offset = offset - page_offset;
|
||||
|
||||
// adjust map length (both the region start and the region end might be not aligned)
|
||||
usz map_len = len + page_offset; // when region start not aligned
|
||||
map_len = vm::aligned_alloc_size(map_len); // when region end not aligned
|
||||
|
||||
void* ptr = vm::mmap_file(file.fd(), map_len, map_offset, access, shared)!;
|
||||
|
||||
// FileMmap does not own the supplied file
|
||||
return {{}, {ptr, map_len, access}, page_offset, len};
|
||||
}
|
||||
|
||||
<*
|
||||
Maps a region of the given file into memory
|
||||
|
||||
@param filename : "File path"
|
||||
@param mode : "File opening mode"
|
||||
@param offset : "Byte offset in file, will be rounded down to page size"
|
||||
@param len : "Size in bytes to map starting from offset, will be rounded up to page size"
|
||||
@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)
|
||||
{
|
||||
File file = open(filename, mode)!;
|
||||
defer catch (void)file.close();
|
||||
FileMmap mm = mmap_file(file, offset, len, access, shared)!;
|
||||
|
||||
// FileMmap owns the file and it will close it on destroy()
|
||||
mm.file = file;
|
||||
|
||||
return mm;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,6 @@ struct Formatter
|
||||
PrintFlags flags;
|
||||
uint width;
|
||||
uint prec;
|
||||
usz idx;
|
||||
fault first_fault;
|
||||
}
|
||||
}
|
||||
@@ -147,10 +146,14 @@ fn usz? Formatter.out_str(&self, any arg) @private
|
||||
case VOID:
|
||||
return self.out_substr("void");
|
||||
case FAULT:
|
||||
return self.out_substr((*(fault*)arg.ptr).nameof);
|
||||
fault f = *(fault*)arg.ptr;
|
||||
return self.out_substr(f ? f.nameof : "(empty-fault)");
|
||||
case INTERFACE:
|
||||
any a = *(any*)arg;
|
||||
return a ? self.out_str(a) : self.out_substr("(empty-interface)");
|
||||
case ANY:
|
||||
return self.out_str(*(any*)arg);
|
||||
any a = *(any*)arg;
|
||||
return a ? self.out_str(a) : self.out_substr("(empty-any)");
|
||||
case OPTIONAL:
|
||||
unreachable();
|
||||
case SIGNED_INT:
|
||||
@@ -495,6 +498,11 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
|
||||
out = ((char*)current.ptr)[:current.type.sizeof];
|
||||
break;
|
||||
}
|
||||
if (current.type.kindof == POINTER)
|
||||
{
|
||||
out = ((*(char**)current.ptr))[:current.type.inner.sizeof];
|
||||
break;
|
||||
}
|
||||
total_len += self.out_substr("<INVALID>")!;
|
||||
continue;
|
||||
}
|
||||
@@ -566,5 +574,5 @@ fn usz? Formatter.print(&self, String str)
|
||||
self.out_fn = &out_null_fn;
|
||||
}
|
||||
foreach (c : str) self.out(c)!;
|
||||
return self.idx;
|
||||
return str.len;
|
||||
}
|
||||
|
||||
@@ -660,7 +660,6 @@ fn usz? Formatter.out_char(&self, any arg) @private
|
||||
fn usz? Formatter.out_reverse(&self, char[] buf) @private
|
||||
{
|
||||
usz n;
|
||||
usz buffer_start_idx = self.idx;
|
||||
usz len = buf.len;
|
||||
// pad spaces up to given width
|
||||
if (!self.flags.zeropad && !self.flags.left)
|
||||
|
||||
103
lib/std/io/io.c3
103
lib/std/io/io.c3
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2021-2022 Christoffer Lerno. All rights reserved.
|
||||
// Copyright (c) 2021-2025 Christoffer Lerno. 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.
|
||||
module std::io;
|
||||
@@ -16,6 +16,7 @@ faultdef
|
||||
BUSY,
|
||||
CANNOT_READ_DIR,
|
||||
DIR_NOT_EMPTY,
|
||||
PARENT_DIR_MISSING,
|
||||
EOF,
|
||||
FILE_CANNOT_DELETE,
|
||||
FILE_IS_DIR,
|
||||
@@ -49,41 +50,23 @@ faultdef
|
||||
"\r" will be filtered from the String.
|
||||
|
||||
@param stream : `The stream to read from.`
|
||||
@require @is_instream(stream) : `The stream must implement InStream.`
|
||||
@require @is_not_instream_if_ptr(stream) : "The value for 'stream' should have been passed as a pointer and not as a value, please add '&'."
|
||||
@require @is_instream(stream) : `Make sure that the stream is actually an InStream.`
|
||||
@param [inout] allocator : `the allocator to use.`
|
||||
@return `The string containing the data read.`
|
||||
*>
|
||||
macro String? readline(Allocator allocator, stream = io::stdin())
|
||||
{
|
||||
bool $is_stream = @typeis(stream, InStream);
|
||||
$if $is_stream:
|
||||
$typeof(&stream.read_byte) func = &stream.read_byte;
|
||||
char val = func((void*)stream)!;
|
||||
$else
|
||||
char val = stream.read_byte()!;
|
||||
$endif
|
||||
|
||||
if (val == '\n') return "";
|
||||
if (allocator == tmem)
|
||||
{
|
||||
DString str = dstring::temp_with_capacity(256);
|
||||
readline_to_stream(&str, stream)!;
|
||||
return str.str_view();
|
||||
}
|
||||
@pool()
|
||||
{
|
||||
DString str = dstring::temp_with_capacity(256);
|
||||
if (val != '\r') str.append(val);
|
||||
while (1)
|
||||
{
|
||||
$if $is_stream:
|
||||
char? c = func((void*)stream);
|
||||
$else
|
||||
char? c = stream.read_byte();
|
||||
$endif
|
||||
if (catch err = c)
|
||||
{
|
||||
if (err == io::EOF) break;
|
||||
return err?;
|
||||
}
|
||||
if (c == '\r') continue;
|
||||
if (c == '\n') break;
|
||||
str.append_char(c);
|
||||
}
|
||||
readline_to_stream(&str, stream)!;
|
||||
return str.copy_str(allocator);
|
||||
};
|
||||
}
|
||||
@@ -93,6 +76,7 @@ macro String? readline(Allocator allocator, stream = io::stdin())
|
||||
on the temporary allocator and does not need to be freed.
|
||||
|
||||
@param stream : `The stream to read from.`
|
||||
@require @is_not_instream_if_ptr(stream) : "The value for 'stream' should have been passed as a pointer and not as a value, please add '&'."
|
||||
@require @is_instream(stream) : `The stream must implement InStream.`
|
||||
@return `The temporary string containing the data read.`
|
||||
*>
|
||||
@@ -101,6 +85,65 @@ macro String? treadline(stream = io::stdin())
|
||||
return readline(tmem, stream) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
Reads a string, see `readline`, the data is passed to an outstream
|
||||
|
||||
@param out_stream : `The stream to write to`
|
||||
@param in_stream : `The stream to read from.`
|
||||
@require @is_not_instream_if_ptr(in_stream) : "The value for 'in_stream' should have been passed as a pointer and not as a value, please add '&'."
|
||||
@require @is_not_outstream_if_ptr(out_stream) : "The value for 'out_stream' should have been passed as a pointer and not as a value, please add '&'."
|
||||
@require @is_instream(in_stream) : `The in_stream must implement InStream.`
|
||||
@require @is_outstream(out_stream) : `The out_stream must implement OutStream.`
|
||||
@return `The number of bytes written`
|
||||
*>
|
||||
macro usz? readline_to_stream(out_stream, in_stream = io::stdin())
|
||||
{
|
||||
bool $is_stream = @typeis(in_stream, InStream);
|
||||
$if $is_stream:
|
||||
var func = &in_stream.read_byte;
|
||||
char val = func((void*)in_stream)!;
|
||||
$else
|
||||
char val = in_stream.read_byte()!;
|
||||
$endif
|
||||
bool $is_out_stream = @typeis(out_stream, OutStream);
|
||||
$if $is_out_stream:
|
||||
var out_func = &out_stream.write_byte;
|
||||
$endif
|
||||
|
||||
if (val == '\n') return 0;
|
||||
usz len;
|
||||
if (val != '\r')
|
||||
{
|
||||
$if $is_out_stream:
|
||||
out_func((void*)out_stream.ptr, val)!;
|
||||
$else
|
||||
out_stream.write_byte(val)!;
|
||||
$endif
|
||||
len++;
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
$if $is_stream:
|
||||
char? c = func((void*)in_stream);
|
||||
$else
|
||||
char? c = in_stream.read_byte();
|
||||
$endif
|
||||
if (catch err = c)
|
||||
{
|
||||
if (err == io::EOF) break;
|
||||
return err?;
|
||||
}
|
||||
if (c == '\r') continue;
|
||||
if (c == '\n') break;
|
||||
$if $is_out_stream:
|
||||
out_func((void*)out_stream.ptr, c)!;
|
||||
$else
|
||||
out_stream.write_byte(c)!;
|
||||
$endif
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
<*
|
||||
Print a value to a stream.
|
||||
|
||||
@@ -117,7 +160,7 @@ macro usz? fprint(out, x)
|
||||
$case ZString: return out.write(x.str_view());
|
||||
$case DString: return out.write(x.str_view());
|
||||
$default:
|
||||
$if $assignable(x, String):
|
||||
$if @assignable_to(x, String):
|
||||
return out.write((String)x);
|
||||
$else
|
||||
$if is_struct_with_default_print($Type):
|
||||
@@ -211,7 +254,7 @@ macro void eprint(x)
|
||||
|
||||
@param x : "The value to print"
|
||||
*>
|
||||
macro void eprintn(x)
|
||||
macro void eprintn(x = "")
|
||||
{
|
||||
(void)fprintn(io::stderr(), x);
|
||||
}
|
||||
|
||||
@@ -3,28 +3,28 @@ import std::io::path, libc, std::os;
|
||||
|
||||
macro void? native_chdir(Path path)
|
||||
{
|
||||
$switch:
|
||||
$case env::POSIX:
|
||||
if (posix::chdir(path.as_zstr()))
|
||||
{
|
||||
switch (libc::errno())
|
||||
@pool()
|
||||
{
|
||||
$switch:
|
||||
$case env::POSIX:
|
||||
if (posix::chdir(path.str_view().zstr_tcopy()))
|
||||
{
|
||||
case errno::EACCES: return io::NO_PERMISSION?;
|
||||
case errno::ENAMETOOLONG: return io::NAME_TOO_LONG?;
|
||||
case errno::ENOTDIR: return io::FILE_NOT_DIR?;
|
||||
case errno::ENOENT: return io::FILE_NOT_FOUND?;
|
||||
case errno::ELOOP: return io::SYMLINK_FAILED?;
|
||||
default: return io::GENERAL_ERROR?;
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EACCES: return io::NO_PERMISSION?;
|
||||
case errno::ENAMETOOLONG: return io::NAME_TOO_LONG?;
|
||||
case errno::ENOTDIR: return io::FILE_NOT_DIR?;
|
||||
case errno::ENOENT: return io::FILE_NOT_FOUND?;
|
||||
case errno::ELOOP: return io::SYMLINK_FAILED?;
|
||||
default: return io::GENERAL_ERROR?;
|
||||
}
|
||||
}
|
||||
}
|
||||
$case env::WIN32:
|
||||
@pool()
|
||||
{
|
||||
$case env::WIN32:
|
||||
// TODO improve with better error handling.
|
||||
if (win32::setCurrentDirectoryW(path.str_view().to_temp_utf16()!!)) return;
|
||||
};
|
||||
return io::GENERAL_ERROR?;
|
||||
$default:
|
||||
return io::UNSUPPORTED_OPERATION?;
|
||||
$endswitch
|
||||
return io::GENERAL_ERROR?;
|
||||
$default:
|
||||
return io::UNSUPPORTED_OPERATION?;
|
||||
$endswitch
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,7 +5,11 @@ fn PathList? native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Al
|
||||
{
|
||||
PathList list;
|
||||
list.init(allocator);
|
||||
DIRPtr directory = posix::opendir(dir.str_view() ? dir.as_zstr() : (ZString)".");
|
||||
DIRPtr directory @noinit;
|
||||
@pool()
|
||||
{
|
||||
directory = posix::opendir(dir.str_view() ? dir.str_view().zstr_tcopy() : (ZString)".");
|
||||
};
|
||||
defer if (directory) posix::closedir(directory);
|
||||
if (!directory) return (path::is_dir(dir) ? io::CANNOT_READ_DIR : io::FILE_NOT_DIR)?;
|
||||
Posix_dirent* entry;
|
||||
|
||||
@@ -7,9 +7,11 @@ import std::os::posix;
|
||||
|
||||
macro bool? native_mkdir(Path path, MkdirPermissions permissions)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
$switch:
|
||||
$case env::POSIX:
|
||||
if (!posix::mkdir(path.as_zstr(), permissions == NORMAL ? 0o777 : 0o700)) return true;
|
||||
if (!posix::mkdir(path.str_view().zstr_tcopy(), permissions == NORMAL ? 0o777 : 0o700)) return true;
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EACCES:
|
||||
@@ -23,11 +25,11 @@ macro bool? native_mkdir(Path path, MkdirPermissions permissions)
|
||||
case errno::EEXIST: return false;
|
||||
case errno::ELOOP: return io::SYMLINK_FAILED?;
|
||||
case errno::ENOTDIR: return io::FILE_NOT_FOUND?;
|
||||
default: return io::GENERAL_ERROR?;
|
||||
case errno::ENOENT: return io::PARENT_DIR_MISSING?;
|
||||
default:
|
||||
return io::GENERAL_ERROR?;
|
||||
}
|
||||
$case env::WIN32:
|
||||
@pool()
|
||||
{
|
||||
// TODO security attributes
|
||||
if (win32::createDirectoryW(path.str_view().to_temp_utf16()!!, null)) return true;
|
||||
switch (win32::getLastError())
|
||||
@@ -43,8 +45,8 @@ macro bool? native_mkdir(Path path, MkdirPermissions permissions)
|
||||
default:
|
||||
return io::GENERAL_ERROR?;
|
||||
}
|
||||
};
|
||||
$default:
|
||||
return io::UNSUPPORTED_OPERATION?;
|
||||
$endswitch
|
||||
};
|
||||
}
|
||||
@@ -6,9 +6,11 @@ import std::os::posix;
|
||||
|
||||
macro bool? native_rmdir(Path path)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
$switch:
|
||||
$case env::POSIX:
|
||||
if (!posix::rmdir(path.as_zstr())) return true;
|
||||
if (!posix::rmdir(path.str_view().zstr_tcopy())) return true;
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EBUSY: return io::BUSY?;
|
||||
@@ -24,25 +26,23 @@ macro bool? native_rmdir(Path path)
|
||||
default: return io::GENERAL_ERROR?;
|
||||
}
|
||||
$case env::WIN32:
|
||||
@pool()
|
||||
if (win32::removeDirectoryW(path.str_view().to_temp_utf16()!!)) return true;
|
||||
switch (win32::getLastError())
|
||||
{
|
||||
if (win32::removeDirectoryW(path.str_view().to_temp_utf16()!!)) return true;
|
||||
switch (win32::getLastError())
|
||||
{
|
||||
case win32::ERROR_ACCESS_DENIED:
|
||||
return io::NO_PERMISSION?;
|
||||
case win32::ERROR_CURRENT_DIRECTORY:
|
||||
return io::BUSY?;
|
||||
case win32::ERROR_DIR_NOT_EMPTY:
|
||||
return io::DIR_NOT_EMPTY?;
|
||||
case win32::ERROR_DIRECTORY:
|
||||
case win32::ERROR_PATH_NOT_FOUND:
|
||||
return false;
|
||||
default:
|
||||
return io::GENERAL_ERROR?;
|
||||
}
|
||||
};
|
||||
case win32::ERROR_ACCESS_DENIED:
|
||||
return io::NO_PERMISSION?;
|
||||
case win32::ERROR_CURRENT_DIRECTORY:
|
||||
return io::BUSY?;
|
||||
case win32::ERROR_DIR_NOT_EMPTY:
|
||||
return io::DIR_NOT_EMPTY?;
|
||||
case win32::ERROR_DIRECTORY:
|
||||
case win32::ERROR_PATH_NOT_FOUND:
|
||||
return false;
|
||||
default:
|
||||
return io::GENERAL_ERROR?;
|
||||
}
|
||||
$default:
|
||||
return io::UNSUPPORTED_OPERATION?;
|
||||
$endswitch
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,30 +6,33 @@ import std::io, std::os, libc;
|
||||
*>
|
||||
fn void? native_rmtree(Path dir)
|
||||
{
|
||||
DIRPtr directory = posix::opendir(dir.as_zstr());
|
||||
defer if (directory) posix::closedir(directory);
|
||||
if (!directory) return path::is_dir(dir) ? io::CANNOT_READ_DIR? : io::FILE_NOT_DIR?;
|
||||
Posix_dirent* entry;
|
||||
while ((entry = posix::readdir(directory)))
|
||||
@pool()
|
||||
{
|
||||
@pool()
|
||||
DIRPtr directory = posix::opendir(dir.str_view().zstr_tcopy());
|
||||
defer if (directory) posix::closedir(directory);
|
||||
if (!directory) return path::is_dir(dir) ? io::CANNOT_READ_DIR? : io::FILE_NOT_DIR?;
|
||||
Posix_dirent* entry;
|
||||
while ((entry = posix::readdir(directory)))
|
||||
{
|
||||
String name = ((ZString)&entry.name).str_view();
|
||||
if (!name || name == "." || name == "..") continue;
|
||||
Path new_path = dir.tappend(name)!;
|
||||
if (entry.d_type == posix::DT_DIR)
|
||||
@pool()
|
||||
{
|
||||
native_rmtree(new_path)!;
|
||||
continue;
|
||||
}
|
||||
if (libc::remove(new_path.as_zstr()))
|
||||
{
|
||||
// TODO improve
|
||||
return io::GENERAL_ERROR?;
|
||||
}
|
||||
};
|
||||
}
|
||||
os::native_rmdir(dir)!;
|
||||
String name = ((ZString)&entry.name).str_view();
|
||||
if (!name || name == "." || name == "..") continue;
|
||||
Path new_path = dir.tappend(name)!;
|
||||
if (entry.d_type == posix::DT_DIR)
|
||||
{
|
||||
native_rmtree(new_path)!;
|
||||
continue;
|
||||
}
|
||||
if (libc::remove(new_path.str_view().zstr_tcopy()))
|
||||
{
|
||||
// TODO improve
|
||||
return io::GENERAL_ERROR?;
|
||||
}
|
||||
};
|
||||
}
|
||||
os::native_rmdir(dir)!;
|
||||
};
|
||||
}
|
||||
|
||||
module std::io::os @if(env::WIN32);
|
||||
|
||||
@@ -93,7 +93,7 @@ enum MkdirPermissions
|
||||
@param recursive : `If directories in between should be created if they're missing, defaults to false`
|
||||
@param permissions : `The permissions to set on the directory`
|
||||
*>
|
||||
macro bool? mkdir(Path path, bool recursive = false, MkdirPermissions permissions = NORMAL)
|
||||
macro bool? mkdir(path, bool recursive = false, MkdirPermissions permissions = NORMAL)
|
||||
{
|
||||
$if @typeis(path, String):
|
||||
@pool() { return _mkdir(temp(path), recursive, permissions); };
|
||||
@@ -148,27 +148,20 @@ fn Path? new(Allocator allocator, String path, PathEnv path_env = DEFAULT_ENV)
|
||||
|
||||
@return? INVALID_PATH : `if the path was invalid`
|
||||
*>
|
||||
fn Path? temp(String path, PathEnv path_env = DEFAULT_ENV)
|
||||
{
|
||||
return new(tmem, path, path_env);
|
||||
}
|
||||
fn Path? temp(String path, PathEnv path_env = DEFAULT_ENV) => new(tmem, path, path_env);
|
||||
|
||||
fn Path? from_win32_wstring(Allocator allocator, WString path) => @pool()
|
||||
fn Path? from_wstring(Allocator allocator, WString path) => @pool()
|
||||
{
|
||||
return path::new(allocator, string::tfrom_wstring(path)!);
|
||||
}
|
||||
|
||||
fn Path? for_windows(Allocator allocator, String path)
|
||||
{
|
||||
return new(allocator, path, WIN32);
|
||||
}
|
||||
fn Path? from_win32_wstring(Allocator allocator, WString path) @deprecated("Use 'from_wstring' instead") => from_wstring(allocator, path);
|
||||
|
||||
fn Path? for_posix(Allocator allocator, String path)
|
||||
{
|
||||
return new(allocator, path, POSIX);
|
||||
}
|
||||
fn Path? for_windows(Allocator allocator, String path) => new(allocator, path, WIN32);
|
||||
|
||||
fn bool Path.equals(self, Path p2)
|
||||
fn Path? for_posix(Allocator allocator, String path) => new(allocator, path, POSIX);
|
||||
|
||||
fn bool Path.equals(self, Path p2) @operator(==)
|
||||
{
|
||||
return self.env == p2.env && self.path_string == p2.path_string;
|
||||
}
|
||||
@@ -340,15 +333,9 @@ fn String Path.volume_name(self)
|
||||
return self.path_string[:len];
|
||||
}
|
||||
|
||||
fn Path? String.to_path(self, Allocator allocator)
|
||||
{
|
||||
return new(allocator, self);
|
||||
}
|
||||
fn Path? String.to_path(self, Allocator allocator) => new(allocator, self);
|
||||
|
||||
fn Path? String.to_tpath(self)
|
||||
{
|
||||
return new(tmem, self);
|
||||
}
|
||||
fn Path? String.to_tpath(self) => new(tmem, self);
|
||||
|
||||
fn usz? volume_name_len(String path, PathEnv path_env) @local
|
||||
{
|
||||
@@ -527,7 +514,7 @@ fn String? normalize(String path_str, PathEnv path_env = DEFAULT_ENV)
|
||||
return path_str[:len];
|
||||
}
|
||||
|
||||
fn ZString Path.as_zstr(self) => (ZString)self.path_string.ptr;
|
||||
fn ZString Path.as_zstr(self) @deprecated => (ZString)self.path_string.ptr;
|
||||
|
||||
fn String Path.root_directory(self)
|
||||
{
|
||||
@@ -607,29 +594,17 @@ fn bool? traverse(Path path, TraverseCallback callback, any data)
|
||||
return false;
|
||||
}
|
||||
|
||||
fn String Path.str_view(self) @inline
|
||||
{
|
||||
return self.path_string;
|
||||
}
|
||||
fn String Path.str_view(self) @inline => self.path_string;
|
||||
|
||||
|
||||
fn bool Path.has_suffix(self, String str)
|
||||
{
|
||||
return self.str_view().ends_with(str);
|
||||
}
|
||||
fn bool Path.has_suffix(self, String str) => self.str_view().ends_with(str);
|
||||
|
||||
<*
|
||||
@require self.allocator != null : "This Path should never be freed"
|
||||
*>
|
||||
fn void Path.free(self)
|
||||
{
|
||||
allocator::free(self.allocator, self.path_string.ptr);
|
||||
}
|
||||
fn void Path.free(self) => allocator::free(self.allocator, self.path_string.ptr);
|
||||
|
||||
fn usz? Path.to_format(&self, Formatter* formatter) @dynamic
|
||||
{
|
||||
return formatter.print(self.str_view());
|
||||
}
|
||||
fn usz? Path.to_format(&self, Formatter* formatter) @dynamic => formatter.print(self.str_view());
|
||||
|
||||
|
||||
const bool[256] RESERVED_PATH_CHAR_POSIX = {
|
||||
@@ -649,10 +624,7 @@ const bool[256] RESERVED_PATH_CHAR_WIN32 = {
|
||||
['*'] = true,
|
||||
};
|
||||
|
||||
macro bool is_reserved_win32_path_char(char c)
|
||||
{
|
||||
return RESERVED_PATH_CHAR_WIN32[c];
|
||||
}
|
||||
macro bool is_reserved_win32_path_char(char c) => RESERVED_PATH_CHAR_WIN32[c];
|
||||
|
||||
macro bool is_reserved_path_char(char c, PathEnv path_env = DEFAULT_ENV)
|
||||
{
|
||||
|
||||
@@ -37,14 +37,24 @@ fn usz? available(InStream s)
|
||||
return 0;
|
||||
}
|
||||
|
||||
macro bool @is_instream(#expr)
|
||||
macro bool @is_instream(#expr) @const
|
||||
{
|
||||
return $assignable(#expr, InStream);
|
||||
return @assignable_to(#expr, InStream);
|
||||
}
|
||||
|
||||
macro bool @is_outstream(#expr)
|
||||
macro bool @is_not_instream_if_ptr(#expr) @const
|
||||
{
|
||||
return $assignable(#expr, OutStream);
|
||||
return !$defined(&#expr) ||| !@is_instream(&#expr);
|
||||
}
|
||||
|
||||
macro bool @is_outstream(#expr) @const
|
||||
{
|
||||
return @assignable_to(#expr, OutStream);
|
||||
}
|
||||
|
||||
macro bool @is_not_outstream_if_ptr(#expr) @const
|
||||
{
|
||||
return !$defined(&#expr) ||| !@is_outstream(&#expr);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -222,11 +232,10 @@ macro usz? read_varint(stream, x_ptr)
|
||||
}
|
||||
<*
|
||||
@require @is_outstream(stream)
|
||||
@require @typekind(x).is_int()
|
||||
@require $Type.kindof.is_int()
|
||||
*>
|
||||
macro usz? write_varint(stream, x)
|
||||
macro usz? write_varint(stream, $Type x)
|
||||
{
|
||||
var $Type = $typeof(x);
|
||||
const MAX = MAX_VARS[$Type.sizeof];
|
||||
char[MAX] buffer @noinit;
|
||||
usz i;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
|
||||
// Copyright (c) 2021-2025 Christoffer Lerno. 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.
|
||||
module libc;
|
||||
@@ -8,6 +8,9 @@ const int EXIT_FAILURE = 1;
|
||||
const int EXIT_SUCCESS = 0;
|
||||
const int RAND_MAX = 0x7fffffff;
|
||||
|
||||
alias WChar @if(env::WIN32) = Char16;
|
||||
alias WChar @if(!env::WIN32) = Char32;
|
||||
|
||||
struct DivResult
|
||||
{
|
||||
CInt quot;
|
||||
@@ -148,6 +151,7 @@ extern fn void qsort(void* base, usz items, usz size, CompareFunction compare);
|
||||
extern fn CInt raise(CInt signal);
|
||||
extern fn CInt rand();
|
||||
extern fn isz read(Fd fd, void* buf, usz nbyte) @if(!env::WIN32);
|
||||
extern fn isz readlink(ZString pathname, char* buf, int bufsize) @if(!env::WIN32);
|
||||
extern fn void* realloc(void* ptr, usz size);
|
||||
extern fn CInt remove(ZString filename);
|
||||
extern fn CInt rename(ZString old_name, ZString new_name);
|
||||
@@ -255,6 +259,8 @@ macro CFile stdout() { return (CFile*)(uptr)STDOUT_FD; }
|
||||
macro CFile stderr() { return (CFile*)(uptr)STDERR_FD; }
|
||||
|
||||
module libc @if(!env::LIBC);
|
||||
import std::core::mem;
|
||||
|
||||
|
||||
fn void longjmp(JmpBuf* buffer, CInt value) @weak @extern("longjmp") @nostrip
|
||||
{
|
||||
@@ -284,22 +290,9 @@ fn void* realloc(void* ptr, usz size) @weak @extern("realloc") @nostrip
|
||||
unreachable("realloc unavailable");
|
||||
}
|
||||
|
||||
fn void* memcpy(void* dest, void* src, usz n) @weak @extern("memcpy") @nostrip
|
||||
{
|
||||
for (usz i = 0; i < n; i++) ((char*)dest)[i] = ((char*)src)[i];
|
||||
return dest;
|
||||
}
|
||||
|
||||
fn void* memmove(void* dest, void* src, usz n) @weak @extern("memmove") @nostrip
|
||||
{
|
||||
return memcpy(dest, src, n) @inline;
|
||||
}
|
||||
|
||||
fn void* memset(void* dest, CInt value, usz n) @weak @extern("memset") @nostrip
|
||||
{
|
||||
for (usz i = 0; i < n; i++) ((char*)dest)[i] = (char)value;
|
||||
return dest;
|
||||
}
|
||||
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
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@ import std::time;
|
||||
<*
|
||||
Return a "timespec" from a duration.
|
||||
|
||||
@require self >= 0
|
||||
@require self >= time::NANO_DURATION_ZERO
|
||||
*>
|
||||
fn TimeSpec NanoDuration.to_timespec(self) @inline
|
||||
{
|
||||
@@ -16,7 +16,7 @@ fn TimeSpec NanoDuration.to_timespec(self) @inline
|
||||
<*
|
||||
Convert a duration to a timespec.
|
||||
|
||||
@require self >= 0
|
||||
@require self >= time::DURATION_ZERO
|
||||
*>
|
||||
fn TimeSpec Duration.to_timespec(self) @inline
|
||||
{
|
||||
@@ -24,3 +24,11 @@ fn TimeSpec Duration.to_timespec(self) @inline
|
||||
Time_t sec = (Time_t)(self / time::SEC);
|
||||
return { .s = sec, .ns = ns };
|
||||
}
|
||||
|
||||
<*
|
||||
Convert a timestamp to a timespec.
|
||||
*>
|
||||
fn TimeSpec Time.to_timespec(self) @inline
|
||||
{
|
||||
return ((Duration)self).to_timespec();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,11 @@ extern fn int* __errno() @if(env::ANDROID);
|
||||
macro int errno() @if(env::ANDROID) => *__errno();
|
||||
macro void errno_set(int err) @if(env::ANDROID) => *(__errno()) = err;
|
||||
|
||||
// OpenBSD
|
||||
extern fn int* __errno() @if(env::OPENBSD);
|
||||
macro int errno() @if(env::OPENBSD) => *__errno();
|
||||
macro void errno_set(int err) @if(env::OPENBSD) => *(__errno()) = err;
|
||||
|
||||
// Darwin
|
||||
extern fn int* __error() @if(env::DARWIN);
|
||||
macro int errno() @if(env::DARWIN) => *__error();
|
||||
|
||||
60
lib/std/libc/os/openbsd.c3
Normal file
60
lib/std/libc/os/openbsd.c3
Normal file
@@ -0,0 +1,60 @@
|
||||
module libc @if(env::OPENBSD);
|
||||
|
||||
// Checked for x86_64
|
||||
|
||||
alias Blksize_t = int;
|
||||
alias Nlink_t = $typefrom(env::X86_64 ? uint.typeid : CUInt.typeid);
|
||||
alias Dev_t = int;
|
||||
alias Ino_t = ulong;
|
||||
alias Mode_t = uint;
|
||||
alias Blkcnt_t = long;
|
||||
alias Fflags_t = uint;
|
||||
|
||||
struct Stat @if(env::X86_64)
|
||||
{
|
||||
Mode_t st_mode;
|
||||
Dev_t st_dev;
|
||||
Ino_t st_ino;
|
||||
Nlink_t st_nlink;
|
||||
Uid_t st_uid;
|
||||
Gid_t st_gid;
|
||||
Dev_t st_rdev;
|
||||
TimeSpec st_atime;
|
||||
TimeSpec st_mtime;
|
||||
TimeSpec st_ctime;
|
||||
Off_t st_size;
|
||||
Blkcnt_t st_blocks;
|
||||
Blksize_t st_blksize;
|
||||
Fflags_t st_flags;
|
||||
uint st_gen;
|
||||
ulong[10] st_spare;
|
||||
}
|
||||
|
||||
// TODO: Investigate if this needs to be fixed
|
||||
struct Stat @if(!env::X86_64)
|
||||
{
|
||||
Dev_t st_dev;
|
||||
Ino_t st_ino;
|
||||
Mode_t st_mode;
|
||||
Nlink_t st_nlink;
|
||||
Uid_t st_uid;
|
||||
Gid_t st_gid;
|
||||
Dev_t st_rdev;
|
||||
CInt __pad1;
|
||||
Off_t st_size;
|
||||
Blksize_t st_blksize;
|
||||
CInt __pad2;
|
||||
Blkcnt_t st_blocks;
|
||||
Time_t st_atime;
|
||||
long st_atime_nsec;
|
||||
Time_t st_mtime;
|
||||
long st_mtime_nsec;
|
||||
Time_t st_ctime;
|
||||
long st_ctime_nsec;
|
||||
CInt[2] __unused;
|
||||
}
|
||||
|
||||
extern fn CInt stat(ZString path, Stat* stat);
|
||||
|
||||
extern fn CInt sysctl(CInt *name, CUInt namelen, void *oldp, usz *oldlenp, void *newp, usz newlen);
|
||||
|
||||
@@ -64,116 +64,194 @@ extern fn CInt sigaction(CInt signum, Sigaction *action, Sigaction *oldaction);
|
||||
|
||||
module libc::termios @if(env::LIBC &&& env::POSIX);
|
||||
|
||||
typedef Cc = char;
|
||||
typedef Speed = CUInt;
|
||||
typedef Tcflags = CUInt;
|
||||
typedef Tcactions = CInt;
|
||||
bitstruct Tc_iflags : CUInt
|
||||
{
|
||||
bool ignbrk;
|
||||
bool brkint;
|
||||
bool ignpar;
|
||||
bool parmrk;
|
||||
bool inpck;
|
||||
bool istrip;
|
||||
bool inlcr;
|
||||
bool igncr;
|
||||
bool icrnl;
|
||||
bool iuclc;
|
||||
bool ixon;
|
||||
bool ixany;
|
||||
bool ixoff;
|
||||
bool imaxbel;
|
||||
bool iutf8;
|
||||
}
|
||||
|
||||
const Tcactions TCOOFF = 0;
|
||||
const Tcactions TCOON = 1;
|
||||
const Tcactions TCIOFF = 2;
|
||||
const Tcactions TCION = 3;
|
||||
const Tcactions TCIFLUSH = 0;
|
||||
const Tcactions TCOFLUSH = 1;
|
||||
const Tcactions TCIOFLUSH = 2;
|
||||
const Tcactions TCSANOW = 0;
|
||||
const Tcactions TCSADRAIN = 1;
|
||||
const Tcactions TCSAFLUSH = 2;
|
||||
const Speed B0 = 0000000;
|
||||
const Speed B50 = 0000001;
|
||||
const Speed B75 = 0000002;
|
||||
const Speed B110 = 0000003;
|
||||
const Speed B134 = 0000004;
|
||||
const Speed B150 = 0000005;
|
||||
const Speed B200 = 0000006;
|
||||
const Speed B300 = 0000007;
|
||||
const Speed B600 = 0000010;
|
||||
const Speed B1200 = 0000011;
|
||||
const Speed B1800 = 0000012;
|
||||
const Speed B2400 = 0000013;
|
||||
const Speed B4800 = 0000014;
|
||||
const Speed B9600 = 0000015;
|
||||
const Speed B19200 = 0000016;
|
||||
const Speed B38400 = 0000017;
|
||||
const Speed B57600 = 0010001;
|
||||
const Speed B115200 = 0010002;
|
||||
const Speed B230400 = 0010003;
|
||||
const Speed B460800 = 0010004;
|
||||
const Speed B500000 = 0010005;
|
||||
const Speed B576000 = 0010006;
|
||||
const Speed B921600 = 0010007;
|
||||
const Speed B1000000 = 0010010;
|
||||
const Speed B1152000 = 0010011;
|
||||
const Speed B1500000 = 0010012;
|
||||
const Speed B2000000 = 0010013;
|
||||
const Speed B2500000 = 0010014;
|
||||
const Speed B3000000 = 0010015;
|
||||
const Speed B3500000 = 0010016;
|
||||
const Speed B4000000 = 0010017;
|
||||
const Speed MAX_BAUD = B4000000;
|
||||
const Tcflags VINTR = 0;
|
||||
const Tcflags VQUIT = 1;
|
||||
const Tcflags VERASE = 2;
|
||||
const Tcflags VKILL = 3;
|
||||
const Tcflags VEOF = 4;
|
||||
const Tcflags VTIME = 5;
|
||||
const Tcflags VMIN = 6;
|
||||
const Tcflags VSWTC = 7;
|
||||
const Tcflags VSTART = 8;
|
||||
const Tcflags VSTOP = 9;
|
||||
const Tcflags VSUSP = 10;
|
||||
const Tcflags VEOL = 11;
|
||||
const Tcflags VREPRINT = 12;
|
||||
const Tcflags VDISCARD = 13;
|
||||
const Tcflags VWERASE = 14;
|
||||
const Tcflags VLNEXT = 15;
|
||||
const Tcflags VEOL2 = 16;
|
||||
const Tcflags ISIG = 0000001;
|
||||
const Tcflags ICANON = 0000002;
|
||||
const Tcflags ECHO = 0000010;
|
||||
const Tcflags ECHOE = 0000020;
|
||||
const Tcflags ECHOK = 0000040;
|
||||
const Tcflags ECHONL = 0000100;
|
||||
const Tcflags NOFLSH = 0000200;
|
||||
const Tcflags TOSTOP = 0000400;
|
||||
const Tcflags IEXTEN = 0100000;
|
||||
const Tcflags CSIZE = 0000060;
|
||||
const Tcflags CS5 = 0000000;
|
||||
const Tcflags CS6 = 0000020;
|
||||
const Tcflags CS7 = 0000040;
|
||||
const Tcflags CS8 = 0000060;
|
||||
const Tcflags CSTOPB = 0000100;
|
||||
const Tcflags CREAD = 0000200;
|
||||
const Tcflags PARENB = 0000400;
|
||||
const Tcflags PARODD = 0001000;
|
||||
const Tcflags HUPCL = 0002000;
|
||||
const Tcflags CLOCAL = 0004000;
|
||||
const Tcflags OPOST = 0000001;
|
||||
const Tcflags OLCUC = 0000002;
|
||||
const Tcflags ONLCR = 0000004;
|
||||
const Tcflags OCRNL = 0000010;
|
||||
const Tcflags ONOCR = 0000020;
|
||||
const Tcflags ONLRET = 0000040;
|
||||
const Tcflags OFILL = 0000100;
|
||||
const Tcflags OFDEL = 0000200;
|
||||
const Tcflags VTDLY = 0040000;
|
||||
const Tcflags VT0 = 0000000;
|
||||
const Tcflags VT1 = 0040000;
|
||||
const Tcflags IGNBRK = 0000001;
|
||||
const Tcflags BRKINT = 0000002;
|
||||
const Tcflags IGNPAR = 0000004;
|
||||
const Tcflags PARMRK = 0000010;
|
||||
const Tcflags INPCK = 0000020;
|
||||
const Tcflags ISTRIP = 0000040;
|
||||
const Tcflags INLCR = 0000100;
|
||||
const Tcflags IGNCR = 0000200;
|
||||
const Tcflags ICRNL = 0000400;
|
||||
const Tcflags IUCLC = 0001000;
|
||||
const Tcflags IXON = 0002000;
|
||||
const Tcflags IXANY = 0004000;
|
||||
const Tcflags IXOFF = 0010000;
|
||||
const Tcflags IMAXBEL = 0020000;
|
||||
const Tcflags IUTF8 = 0040000;
|
||||
bitstruct Tc_oflags : CUInt
|
||||
{
|
||||
bool opost : 0;
|
||||
bool olcuc : 1;
|
||||
bool onlcr : 2;
|
||||
bool ocrnl : 3;
|
||||
bool onocr : 4;
|
||||
bool onlret : 5;
|
||||
bool ofill : 6;
|
||||
bool ofdel : 7;
|
||||
T_nldly nldly : 8..8;
|
||||
T_crdly crdly : 9..10;
|
||||
T_tabdly tabdly : 11..12;
|
||||
T_bsdly bsdly : 13..13;
|
||||
T_vtdly vtdly : 14..14;
|
||||
T_ffdly ffdly : 15..15;
|
||||
}
|
||||
|
||||
bitstruct Tc_cflags : CUInt
|
||||
{
|
||||
T_csize csize : 4..5;
|
||||
bool cstopb : 6;
|
||||
bool cread : 7;
|
||||
bool parenb : 8;
|
||||
bool parodd : 9;
|
||||
bool hupcl : 10;
|
||||
bool clocal : 11;
|
||||
bool addrb : 29;
|
||||
}
|
||||
|
||||
bitstruct Tc_lflags : CUInt
|
||||
{
|
||||
bool isig : 0;
|
||||
bool icanon : 1;
|
||||
bool xcase : 2;
|
||||
bool echo : 3;
|
||||
bool echoe : 4;
|
||||
bool echok : 5;
|
||||
bool echonl : 6;
|
||||
bool noflsh : 7;
|
||||
bool tostop : 8;
|
||||
bool echoctl : 9;
|
||||
bool echoprt : 10;
|
||||
bool echoke : 11;
|
||||
bool flusho : 12;
|
||||
bool pendin : 14;
|
||||
bool iexten : 15;
|
||||
bool extproc : 16;
|
||||
}
|
||||
|
||||
enum T_nldly : const char
|
||||
{
|
||||
NL0 = 0b0,
|
||||
NL1 = 0b1,
|
||||
}
|
||||
|
||||
enum T_crdly : const char
|
||||
{
|
||||
CR0 = 0b00,
|
||||
CR1 = 0b01,
|
||||
CR2 = 0b10,
|
||||
CR3 = 0b11,
|
||||
}
|
||||
|
||||
enum T_tabdly : const char
|
||||
{
|
||||
TAB0 = 0b00,
|
||||
TAB1 = 0b01,
|
||||
TAB2 = 0b10,
|
||||
TAB3 = 0b11,
|
||||
XTABS = TAB3,
|
||||
}
|
||||
|
||||
enum T_bsdly : const char
|
||||
{
|
||||
BS0 = 0b0,
|
||||
BS1 = 0b1,
|
||||
}
|
||||
|
||||
enum T_ffdly : const char
|
||||
{
|
||||
FF0 = 0b0,
|
||||
FF1 = 0b1,
|
||||
}
|
||||
|
||||
enum T_vtdly : const char
|
||||
{
|
||||
VT0 = 0b0,
|
||||
VT1 = 0b1,
|
||||
}
|
||||
|
||||
enum T_csize : const char
|
||||
{
|
||||
CS5 = 0b00,
|
||||
CS6 = 0b01,
|
||||
CS7 = 0b10,
|
||||
CS8 = 0b11,
|
||||
}
|
||||
|
||||
enum Speed : const CUInt
|
||||
{
|
||||
B0 = 0o0000000,
|
||||
B50 = 0o0000001,
|
||||
B75 = 0o0000002,
|
||||
B110 = 0o0000003,
|
||||
B134 = 0o0000004,
|
||||
B150 = 0o0000005,
|
||||
B200 = 0o0000006,
|
||||
B300 = 0o0000007,
|
||||
B600 = 0o0000010,
|
||||
B1200 = 0o0000011,
|
||||
B1800 = 0o0000012,
|
||||
B2400 = 0o0000013,
|
||||
B4800 = 0o0000014,
|
||||
B9600 = 0o0000015,
|
||||
B19200 = 0o0000016,
|
||||
B38400 = 0o0000017,
|
||||
B57600 = 0o0010001,
|
||||
B115200 = 0o0010002,
|
||||
B230400 = 0o0010003,
|
||||
B460800 = 0o0010004,
|
||||
B500000 = 0o0010005,
|
||||
B576000 = 0o0010006,
|
||||
B921600 = 0o0010007,
|
||||
B1000000 = 0o0010010,
|
||||
B1152000 = 0o0010011,
|
||||
B1500000 = 0o0010012,
|
||||
B2000000 = 0o0010013,
|
||||
B2500000 = 0o0010014,
|
||||
B3000000 = 0o0010015,
|
||||
B3500000 = 0o0010016,
|
||||
B4000000 = 0o0010017,
|
||||
MAX_BAUD = B4000000,
|
||||
}
|
||||
|
||||
enum Cc : const char
|
||||
{
|
||||
VINTR = 0,
|
||||
VQUIT = 1,
|
||||
VERASE = 2,
|
||||
VKILL = 3,
|
||||
VEOF = 4,
|
||||
VTIME = 5,
|
||||
VMIN = 6,
|
||||
VSWTC = 7,
|
||||
VSTART = 8,
|
||||
VSTOP = 9,
|
||||
VSUSP = 10,
|
||||
VEOL = 11,
|
||||
VREPRINT = 12,
|
||||
VDISCARD = 13,
|
||||
VWERASE = 14,
|
||||
VLNEXT = 15,
|
||||
VEOL2 = 16,
|
||||
}
|
||||
|
||||
enum Tcactions : const CInt
|
||||
{
|
||||
TCOOFF = 0,
|
||||
TCOON = 1,
|
||||
TCIOFF = 2,
|
||||
TCION = 3,
|
||||
TCIFLUSH = 0,
|
||||
TCOFLUSH = 1,
|
||||
TCIOFLUSH = 2,
|
||||
TCSANOW = 0,
|
||||
TCSADRAIN = 1,
|
||||
TCSAFLUSH = 2,
|
||||
}
|
||||
|
||||
extern fn CInt tcgetattr(Fd fd, Termios* self);
|
||||
extern fn CInt tcsetattr(Fd fd, Tcactions optional_actions, Termios* self);
|
||||
@@ -189,13 +267,119 @@ extern fn CInt cfsetispeed(Termios* self, Speed speed);
|
||||
const CInt NCCS = 32;
|
||||
struct Termios
|
||||
{
|
||||
Tcflags c_iflag;
|
||||
Tcflags c_oflag;
|
||||
Tcflags c_cflag;
|
||||
Tcflags c_lflag;
|
||||
Tc_iflags c_iflag;
|
||||
Tc_oflags c_oflag;
|
||||
Tc_cflags c_cflag;
|
||||
Tc_lflags c_lflag;
|
||||
Cc c_line;
|
||||
Cc[NCCS] c_cc;
|
||||
Speed c_ispeed;
|
||||
Speed c_ospeed;
|
||||
}
|
||||
}
|
||||
|
||||
const Tcactions TCOOFF @deprecated = 0;
|
||||
const Tcactions TCOON @deprecated = 1;
|
||||
const Tcactions TCIOFF @deprecated = 2;
|
||||
const Tcactions TCION @deprecated = 3;
|
||||
const Tcactions TCIFLUSH @deprecated = 0;
|
||||
const Tcactions TCOFLUSH @deprecated = 1;
|
||||
const Tcactions TCIOFLUSH @deprecated = 2;
|
||||
const Tcactions TCSANOW @deprecated = 0;
|
||||
const Tcactions TCSADRAIN @deprecated = 1;
|
||||
const Tcactions TCSAFLUSH @deprecated = 2;
|
||||
const Speed B0 @deprecated = 0o0000000;
|
||||
const Speed B50 @deprecated = 0o0000001;
|
||||
const Speed B75 @deprecated = 0o0000002;
|
||||
const Speed B110 @deprecated = 0o0000003;
|
||||
const Speed B134 @deprecated = 0o0000004;
|
||||
const Speed B150 @deprecated = 0o0000005;
|
||||
const Speed B200 @deprecated = 0o0000006;
|
||||
const Speed B300 @deprecated = 0o0000007;
|
||||
const Speed B600 @deprecated = 0o0000010;
|
||||
const Speed B1200 @deprecated = 0o0000011;
|
||||
const Speed B1800 @deprecated = 0o0000012;
|
||||
const Speed B2400 @deprecated = 0o0000013;
|
||||
const Speed B4800 @deprecated = 0o0000014;
|
||||
const Speed B9600 @deprecated = 0o0000015;
|
||||
const Speed B19200 @deprecated = 0o0000016;
|
||||
const Speed B38400 @deprecated = 0o0000017;
|
||||
const Speed B57600 @deprecated = 0o0010001;
|
||||
const Speed B115200 @deprecated = 0o0010002;
|
||||
const Speed B230400 @deprecated = 0o0010003;
|
||||
const Speed B460800 @deprecated = 0o0010004;
|
||||
const Speed B500000 @deprecated = 0o0010005;
|
||||
const Speed B576000 @deprecated = 0o0010006;
|
||||
const Speed B921600 @deprecated = 0o0010007;
|
||||
const Speed B1000000 @deprecated = 0o0010010;
|
||||
const Speed B1152000 @deprecated = 0o0010011;
|
||||
const Speed B1500000 @deprecated = 0o0010012;
|
||||
const Speed B2000000 @deprecated = 0o0010013;
|
||||
const Speed B2500000 @deprecated = 0o0010014;
|
||||
const Speed B3000000 @deprecated = 0o0010015;
|
||||
const Speed B3500000 @deprecated = 0o0010016;
|
||||
const Speed B4000000 @deprecated = 0o0010017;
|
||||
const Speed MAX_BAUD @deprecated = B4000000;
|
||||
const Cc VINTR @deprecated = 0;
|
||||
const Cc VQUIT @deprecated = 1;
|
||||
const Cc VERASE @deprecated = 2;
|
||||
const Cc VKILL @deprecated = 3;
|
||||
const Cc VEOF @deprecated = 4;
|
||||
const Cc VTIME @deprecated = 5;
|
||||
const Cc VMIN @deprecated = 6;
|
||||
const Cc VSWTC @deprecated = 7;
|
||||
const Cc VSTART @deprecated = 8;
|
||||
const Cc VSTOP @deprecated = 9;
|
||||
const Cc VSUSP @deprecated = 10;
|
||||
const Cc VEOL @deprecated = 11;
|
||||
const Cc VREPRINT @deprecated = 12;
|
||||
const Cc VDISCARD @deprecated = 13;
|
||||
const Cc VWERASE @deprecated = 14;
|
||||
const Cc VLNEXT @deprecated = 15;
|
||||
const Cc VEOL2 @deprecated = 16;
|
||||
const Tc_lflags ISIG @deprecated = {.isig};
|
||||
const Tc_lflags ICANON @deprecated = {.icanon};
|
||||
const Tc_lflags ECHO @deprecated = {.echo};
|
||||
const Tc_lflags ECHOE @deprecated = {.echoe};
|
||||
const Tc_lflags ECHOK @deprecated = {.echok};
|
||||
const Tc_lflags ECHONL @deprecated = {.echonl};
|
||||
const Tc_lflags NOFLSH @deprecated = {.noflsh};
|
||||
const Tc_lflags TOSTOP @deprecated = {.tostop};
|
||||
const Tc_lflags IEXTEN @deprecated = {.iexten};
|
||||
const Tc_cflags CSIZE @deprecated = {.csize = CS8};
|
||||
const Tc_cflags CS5 @deprecated = {.csize = CS5};
|
||||
const Tc_cflags CS6 @deprecated = {.csize = CS6};
|
||||
const Tc_cflags CS7 @deprecated = {.csize = CS7};
|
||||
const Tc_cflags CS8 @deprecated = {.csize = CS8};
|
||||
const Tc_cflags CSTOPB @deprecated = {.cstopb};
|
||||
const Tc_cflags CREAD @deprecated = {.cread};
|
||||
const Tc_cflags PARENB @deprecated = {.parenb};
|
||||
const Tc_cflags PARODD @deprecated = {.parodd};
|
||||
const Tc_cflags HUPCL @deprecated = {.hupcl};
|
||||
const Tc_cflags CLOCAL @deprecated = {.clocal};
|
||||
const Tc_oflags OPOST @deprecated = {.opost};
|
||||
const Tc_oflags OLCUC @deprecated = {.olcuc};
|
||||
const Tc_oflags ONLCR @deprecated = {.onlcr};
|
||||
const Tc_oflags OCRNL @deprecated = {.ocrnl};
|
||||
const Tc_oflags ONOCR @deprecated = {.onocr};
|
||||
const Tc_oflags ONLRET @deprecated = {.onlret};
|
||||
const Tc_oflags OFILL @deprecated = {.ofill};
|
||||
const Tc_oflags OFDEL @deprecated = {.ofdel};
|
||||
const Tc_oflags VTDLY @deprecated = {.vtdly = VT1};
|
||||
const Tc_oflags VT0 @deprecated = {.vtdly = VT0};
|
||||
const Tc_oflags VT1 @deprecated = {.vtdly = VT1};
|
||||
const Tc_iflags IGNBRK @deprecated = {.ignbrk};
|
||||
const Tc_iflags BRKINT @deprecated = {.brkint};
|
||||
const Tc_iflags IGNPAR @deprecated = {.ignpar};
|
||||
const Tc_iflags PARMRK @deprecated = {.parmrk};
|
||||
const Tc_iflags INPCK @deprecated = {.inpck};
|
||||
const Tc_iflags ISTRIP @deprecated = {.istrip};
|
||||
const Tc_iflags INLCR @deprecated = {.inlcr};
|
||||
const Tc_iflags IGNCR @deprecated = {.igncr};
|
||||
const Tc_iflags ICRNL @deprecated = {.icrnl};
|
||||
const Tc_iflags IUCLC @deprecated = {.iuclc};
|
||||
const Tc_iflags IXON @deprecated = {.ixon};
|
||||
const Tc_iflags IXANY @deprecated = {.ixany};
|
||||
const Tc_iflags IXOFF @deprecated = {.ixoff};
|
||||
const Tc_iflags IMAXBEL @deprecated = {.imaxbel};
|
||||
const Tc_iflags IUTF8 @deprecated = {.iutf8};
|
||||
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
module libc::termios @if(env::LIBC &&& env::POSIX);
|
||||
|
||||
fn int sendBreak(Fd fd, int duration) => tcsendbreak(fd, duration);
|
||||
fn int send_break(Fd fd, int duration) => tcsendbreak(fd, duration);
|
||||
fn int drain(Fd fd) => tcdrain(fd);
|
||||
fn int flush(Fd fd, int queue_selector) => tcflush(fd, queue_selector);
|
||||
fn int flow(Fd fd, int action) => tcflow(fd, action);
|
||||
fn Speed Termios.getOSpeed(Termios* self) => cfgetospeed(self);
|
||||
fn Speed Termios.getISpeed(Termios* self) => cfgetispeed(self);
|
||||
fn int Termios.setOSpeed(Termios* self, Speed speed) => cfsetospeed(self, speed);
|
||||
fn int Termios.setISpeed(Termios* self, Speed speed) => cfsetispeed(self, speed);
|
||||
fn int Termios.getAttr(Termios* self, Fd fd) => tcgetattr(fd, self);
|
||||
fn int Termios.setAttr(Termios* self, Fd fd, Tcactions optional_actions) => tcsetattr(fd, optional_actions, self);
|
||||
fn Speed Termios.get_ospeed(&self) => cfgetospeed(self);
|
||||
fn Speed Termios.get_ispeed(&self) => cfgetispeed(self);
|
||||
fn int Termios.set_ospeed(&self, Speed speed) => cfsetospeed(self, speed);
|
||||
fn int Termios.set_ispeed(&self, Speed speed) => cfsetispeed(self, speed);
|
||||
fn int Termios.get_attr(&self, Fd fd) => tcgetattr(fd, self);
|
||||
fn int Termios.set_attr(&self, Fd fd, Tcactions optional_actions) => tcsetattr(fd, optional_actions, self);
|
||||
|
||||
fn int sendBreak(Fd fd, int duration) @deprecated => send_break(fd, duration);
|
||||
fn Speed Termios.getOSpeed(&self) @deprecated => self.get_ospeed();
|
||||
fn Speed Termios.getISpeed(&self) @deprecated => self.get_ispeed();
|
||||
fn int Termios.setOSpeed(&self, Speed speed) @deprecated => self.set_ospeed(speed);
|
||||
fn int Termios.setISpeed(&self, Speed speed) @deprecated => self.set_ispeed(speed);
|
||||
fn int Termios.getAttr(&self, Fd fd) @deprecated => self.get_attr(fd);
|
||||
fn int Termios.setAttr(&self, Fd fd, Tcactions optional_actions) @deprecated => self.set_attr(fd, optional_actions);
|
||||
|
||||
module libc::termios @if(!env::LIBC ||| !env::POSIX);
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ 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 = self }).div(c);
|
||||
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(/)
|
||||
{
|
||||
Real div = b.v.dot(b.v);
|
||||
|
||||
@@ -6,6 +6,8 @@ import std::math::complex;
|
||||
import std::math::matrix;
|
||||
import std::math::quaternion;
|
||||
|
||||
attrdef @MathLibc(name) = @extern(name), @link(env::POSIX, "m");
|
||||
|
||||
const E = 2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466;
|
||||
const LOG2E = 1.44269504088896340735992468100189214; // log2(e)
|
||||
const LOG10E = 0.434294481903251827651128918916605082; // log10(e)
|
||||
@@ -67,14 +69,14 @@ enum RoundingMode : int
|
||||
faultdef OVERFLOW, MATRIX_INVERSE_DOESNT_EXIST;
|
||||
|
||||
<*
|
||||
@require types::is_numerical($typeof(x)) : `The input must be a numerical value or numerical vector`
|
||||
@require types::is_numerical($Num) : `The input must be a numerical value or numerical vector`
|
||||
*>
|
||||
macro deg_to_rad(x) => x * PI / 180;
|
||||
macro deg_to_rad($Num x) => x * PI / 180;
|
||||
|
||||
<*
|
||||
@require types::is_numerical($typeof(x)) : `The input must be a numerical value or numerical vector`
|
||||
@require types::is_numerical($Num) : `The input must be a numerical value or numerical vector`
|
||||
*>
|
||||
macro abs(x) => $$abs(x);
|
||||
macro abs($Num x) => $$abs(x);
|
||||
|
||||
<*
|
||||
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
|
||||
@@ -101,13 +103,12 @@ macro is_approx_rel(x, y, eps)
|
||||
<*
|
||||
@require values::@is_int(x) : `The input must be an integer`
|
||||
*>
|
||||
macro sign(x)
|
||||
macro $Num sign($Num x)
|
||||
{
|
||||
var $Type = $typeof(x);
|
||||
$if $Type.kindof == UNSIGNED_INT:
|
||||
return ($Type)(x > 0);
|
||||
$if $Num.kindof == UNSIGNED_INT:
|
||||
return ($Num)(x > 0);
|
||||
$else
|
||||
return ($Type)(x > 0) - ($Type)(x < 0);
|
||||
return ($Num)(x > 0) - ($Num)(x < 0);
|
||||
$endif
|
||||
}
|
||||
|
||||
@@ -115,9 +116,9 @@ macro sign(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 atan2(x, y)
|
||||
macro atan2($Num1 x, $Num2 y)
|
||||
{
|
||||
$if @typeis(x, float) && @typeis(y, float):
|
||||
$if $Num1 == float || $Num2 == float:
|
||||
return _atan2f(x, y);
|
||||
$else
|
||||
return _atan2(x, y);
|
||||
@@ -127,15 +128,15 @@ macro atan2(x, y)
|
||||
<*
|
||||
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
|
||||
@require @typekind(sinp) == POINTER : "Expected sinp to be a pointer"
|
||||
@require values::@is_same_type(sinp, cosp) : "Expected sinp and cosp to have the same type"
|
||||
@require $assignable(x, $typeof(*sinp)) : "Expected x and sinp/cosp to have the same type"
|
||||
@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($Num x, $Num* sinp, $Num* cosp)
|
||||
{
|
||||
$if @typeis(sinp, float*.typeid):
|
||||
return _sincosf(x, sinp, cosp);
|
||||
$if $Num == float:
|
||||
_sincosf(x, sinp, cosp);
|
||||
$else
|
||||
return _sincos(x, sinp, cosp);
|
||||
_sincos(x, sinp, cosp);
|
||||
$endif
|
||||
}
|
||||
|
||||
@@ -145,9 +146,9 @@ macro sincos_ref(x, sinp, cosp)
|
||||
@param x : `the angle in radians`
|
||||
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
|
||||
*>
|
||||
macro sincos(x)
|
||||
macro sincos($Num x)
|
||||
{
|
||||
$if @typeis(x, float):
|
||||
$if $Num == float:
|
||||
float[<2>] v @noinit;
|
||||
_sincosf(x, &v[0], &v[1]);
|
||||
$else
|
||||
@@ -160,9 +161,9 @@ macro sincos(x)
|
||||
<*
|
||||
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
|
||||
*>
|
||||
macro atan(x)
|
||||
macro atan($Num x)
|
||||
{
|
||||
$if @typeis(x, float):
|
||||
$if $Num == float:
|
||||
return _atanf(x);
|
||||
$else
|
||||
return _atan(x);
|
||||
@@ -172,9 +173,9 @@ macro atan(x)
|
||||
<*
|
||||
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
|
||||
*>
|
||||
macro atanh(x)
|
||||
macro atanh($Num x)
|
||||
{
|
||||
$if @typeis(x, float):
|
||||
$if $Num == float:
|
||||
return _atanhf(x);
|
||||
$else
|
||||
return _atanh(x);
|
||||
@@ -184,9 +185,9 @@ macro atanh(x)
|
||||
<*
|
||||
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
|
||||
*>
|
||||
macro acos(x)
|
||||
macro acos($Num x)
|
||||
{
|
||||
$if @typeis(x, float):
|
||||
$if $Num == float:
|
||||
return _acosf(x);
|
||||
$else
|
||||
return _acos(x);
|
||||
@@ -196,9 +197,9 @@ macro acos(x)
|
||||
<*
|
||||
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
|
||||
*>
|
||||
macro acosh(x)
|
||||
macro acosh($Num x)
|
||||
{
|
||||
$if @typeis(x, float):
|
||||
$if $Num == float:
|
||||
return _acoshf(x);
|
||||
$else
|
||||
return _acosh(x);
|
||||
@@ -208,9 +209,9 @@ macro acosh(x)
|
||||
<*
|
||||
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
|
||||
*>
|
||||
macro asin(x)
|
||||
macro asin($Num x)
|
||||
{
|
||||
$if @typeis(x, float):
|
||||
$if $Num == float:
|
||||
return _asinf(x);
|
||||
$else
|
||||
return _asin(x);
|
||||
@@ -220,9 +221,9 @@ macro asin(x)
|
||||
<*
|
||||
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
|
||||
*>
|
||||
macro asinh(x)
|
||||
macro asinh($Num x)
|
||||
{
|
||||
$if @typeis(x, float):
|
||||
$if $Num == float:
|
||||
return _asinhf(x);
|
||||
$else
|
||||
return _asinh(x);
|
||||
@@ -342,6 +343,23 @@ macro log(x, base)
|
||||
*>
|
||||
macro log2(x) => $$log2(values::promote_int(x));
|
||||
|
||||
<*
|
||||
@require values::@is_int($x) : `The input value must be an integer.`
|
||||
@require $x >= 0 : `The input value must be a positive integer.`
|
||||
@return `A floored base-2 log of an input integer value.`
|
||||
*>
|
||||
macro @intlog2($x)
|
||||
{
|
||||
$if $x <= 1:
|
||||
return 0;
|
||||
$endif
|
||||
|
||||
$typeof($x) $z = 0;
|
||||
$for var $y = $x; $y > 0; $y >>= 1, ++$z: $endfor
|
||||
|
||||
return $z - 1;
|
||||
}
|
||||
|
||||
<*
|
||||
@require values::@is_promotable_to_floatlike(x) : `The input must be a number or a float vector`
|
||||
*>
|
||||
@@ -349,7 +367,7 @@ macro log10(x) => $$log10(values::promote_int(x));
|
||||
|
||||
<*
|
||||
@require types::is_numerical($typeof(x)) : `The input must be a floating point value or float vector`
|
||||
@require types::is_same($typeof(x), $typeof(y)) : `The input types must be equal`
|
||||
@require @typematch(x, y) : `The input types must be equal`
|
||||
*>
|
||||
macro max(x, y, ...)
|
||||
{
|
||||
@@ -394,7 +412,7 @@ macro nearbyint(x) => $$nearbyint(x);
|
||||
|
||||
<*
|
||||
@require values::@is_promotable_to_floatlike(x) : `The input must be a number or a float vector`
|
||||
@require $assignable(exp, $typeof(values::promote_int(x))) || values::@is_int(exp) : `The input must be an integer, castable to the type of x`
|
||||
@require @assignable_to(exp, $typeof(values::promote_int(x))) || values::@is_int(exp) : `The input must be an integer, castable to the type of x`
|
||||
*>
|
||||
macro pow(x, exp)
|
||||
{
|
||||
@@ -576,7 +594,7 @@ macro normalize(x) @private
|
||||
@param then_value : "The vector to get elements from where the mask is 'true'"
|
||||
@param else_value : "The vector to get elements from where the mask is 'false'"
|
||||
@require values::@is_vector(then_value) && values::@is_vector(else_value) : "'Then' and 'else' must be vectors."
|
||||
@require values::@is_same_type(then_value, else_value) : "'Then' and 'else' vectors must be of the same type."
|
||||
@require @typematch(then_value, else_value) : "'Then' and 'else' vectors must be of the same type."
|
||||
@require then_value.len == mask.len : "Mask and selected vectors must be of the same width."
|
||||
|
||||
@return "a vector of the same type as then/else"
|
||||
@@ -1012,33 +1030,33 @@ macro void float.set_word(float* f, uint u) => *f = bitcast(u, float);
|
||||
|
||||
macro double scalbn(double x, int n) => _scalbn(x, n);
|
||||
|
||||
extern fn double _atan(double x) @extern("atan");
|
||||
extern fn float _atanf(float x) @extern("atanf");
|
||||
extern fn double _atan2(double, double) @extern("atan2");
|
||||
extern fn float _atan2f(float, float) @extern("atan2f");
|
||||
extern fn double _atan(double x) @MathLibc("atan");
|
||||
extern fn float _atanf(float x) @MathLibc("atanf");
|
||||
extern fn double _atan2(double, double) @MathLibc("atan2");
|
||||
extern fn float _atan2f(float, float) @MathLibc("atan2f");
|
||||
|
||||
extern fn void _sincos(double, double*, double*) @extern("__sincos") @link("m") @if(env::DARWIN);
|
||||
extern fn void _sincosf(float, float*, float*) @extern("__sincosf") @link("m") @if(env::DARWIN);
|
||||
extern fn void _sincos(double, double*, double*) @MathLibc("__sincos") @if(env::DARWIN);
|
||||
extern fn void _sincosf(float, float*, float*) @MathLibc("__sincosf") @if(env::DARWIN);
|
||||
|
||||
extern fn void _sincos(double, double*, double*) @extern("sincos") @link("m") @if(!env::DARWIN && !env::WIN32);
|
||||
extern fn void _sincosf(float, float*, float*) @extern("sincosf") @link("m") @if(!env::DARWIN && !env::WIN32);
|
||||
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); }
|
||||
|
||||
extern fn double _tan(double x) @extern("tan");
|
||||
extern fn float _tanf(float x) @extern("tanf");
|
||||
extern fn double _scalbn(double x, int n) @extern("scalbn");
|
||||
extern fn double _acos(double x) @extern("acos");
|
||||
extern fn double _asin(double x) @extern("asin");
|
||||
extern fn double _acosh(double x) @extern("acosh");
|
||||
extern fn double _asinh(double x) @extern("asinh");
|
||||
extern fn double _atanh(double x) @extern("atanh");
|
||||
extern fn float _acosf(float x) @extern("acosf");
|
||||
extern fn float _asinf(float x) @extern("asinf");
|
||||
extern fn float _acoshf(float x) @extern("acoshf");
|
||||
extern fn float _asinhf(float x) @extern("asinhf");
|
||||
extern fn float _atanhf(float x) @extern("atanhf");
|
||||
extern fn double _tan(double x) @MathLibc("tan");
|
||||
extern fn float _tanf(float x) @MathLibc("tanf");
|
||||
extern fn double _scalbn(double x, int n) @MathLibc("scalbn");
|
||||
extern fn double _acos(double x) @MathLibc("acos");
|
||||
extern fn double _asin(double x) @MathLibc("asin");
|
||||
extern fn double _acosh(double x) @MathLibc("acosh");
|
||||
extern fn double _asinh(double x) @MathLibc("asinh");
|
||||
extern fn double _atanh(double x) @MathLibc("atanh");
|
||||
extern fn float _acosf(float x) @MathLibc("acosf");
|
||||
extern fn float _asinf(float x) @MathLibc("asinf");
|
||||
extern fn float _acoshf(float x) @MathLibc("acoshf");
|
||||
extern fn float _asinhf(float x) @MathLibc("asinhf");
|
||||
extern fn float _atanhf(float x) @MathLibc("atanhf");
|
||||
|
||||
|
||||
fn double _frexp(double x, int* e)
|
||||
@@ -1092,23 +1110,23 @@ fn float _frexpf(float x, int* e)
|
||||
}
|
||||
}
|
||||
|
||||
macro overflow_add_helper(x, y) @local
|
||||
macro $Num overflow_add_helper($Num x, $Num y) @local
|
||||
{
|
||||
$typeof(x) res @noinit;
|
||||
$Num res @noinit;
|
||||
if ($$overflow_add(x, y, &res)) return OVERFLOW?;
|
||||
return res;
|
||||
}
|
||||
|
||||
macro overflow_sub_helper(x, y) @local
|
||||
macro $Num overflow_sub_helper($Num x, $Num y) @local
|
||||
{
|
||||
$typeof(x) res @noinit;
|
||||
$Num res @noinit;
|
||||
if ($$overflow_sub(x, y, &res)) return OVERFLOW?;
|
||||
return res;
|
||||
}
|
||||
|
||||
macro overflow_mul_helper(x, y) @local
|
||||
macro $Num overflow_mul_helper($Num x, $Num y) @local
|
||||
{
|
||||
$typeof(x) res @noinit;
|
||||
$Num res @noinit;
|
||||
if ($$overflow_mul(x, y, &res)) return OVERFLOW?;
|
||||
return res;
|
||||
}
|
||||
@@ -1116,29 +1134,23 @@ macro overflow_mul_helper(x, y) @local
|
||||
<*
|
||||
@param [&out] out : "Where the result of the addition is stored"
|
||||
@return "Whether the addition resulted in an integer overflow"
|
||||
@require values::@is_same_type(a, b) : "a and b must be the same type"
|
||||
@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) &&& values::@is_same_type(*out, a) : "out must be a pointer of the same type as a and b"
|
||||
*>
|
||||
macro bool overflow_add(a, b, out) => $$overflow_add(a, b, out);
|
||||
*>
|
||||
macro bool overflow_add($Num a, $Num b, $Num* out) => $$overflow_add(a, b, out);
|
||||
|
||||
<*
|
||||
@param [&out] out : "Where the result of the subtraction is stored"
|
||||
@return "Whether the subtraction resulted in an integer overflow"
|
||||
@require values::@is_same_type(a, b) : "a and b must be the same type"
|
||||
@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) &&& values::@is_same_type(*out, a) : "out must be a pointer of the same type as a and b"
|
||||
*>
|
||||
macro bool overflow_sub(a, b, out) => $$overflow_sub(a, b, out);
|
||||
*>
|
||||
macro bool overflow_sub($Num a, $Num b, $Num* out) => $$overflow_sub(a, b, out);
|
||||
|
||||
<*
|
||||
@param [&out] out : "Where the result of the multiplication is stored"
|
||||
@return "Whether the multiplication resulted in an integer overflow"
|
||||
@require values::@is_same_type(a, b) : "a and b must be the same type"
|
||||
@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) &&& values::@is_same_type(*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 bool overflow_mul($Num a, $Num b, $Num* out) => $$overflow_mul(a, b, out);
|
||||
|
||||
<*
|
||||
@require types::is_vector($Type) || ($Type.kindof == ARRAY &&& types::is_numerical($typefrom($Type.inner)))
|
||||
@@ -1152,10 +1164,9 @@ macro iota($Type)
|
||||
return $val;
|
||||
}
|
||||
|
||||
macro mul_div_helper(val, mul, div) @private
|
||||
macro $Num mul_div_helper($Num val, $Num mul, $Num div) @private
|
||||
{
|
||||
var $Type = $typeof(val);
|
||||
return ($Type)(($Type)mul * (val / ($Type)div) + ($Type)mul * (val % ($Type)div) / ($Type)div);
|
||||
return mul * (val / div) + mul * (val % div) / div;
|
||||
}
|
||||
macro char char.muldiv(self, char mul, char div) => mul_div_helper(self, mul, div);
|
||||
macro ichar ichar.muldiv(self, ichar mul, ichar div) => mul_div_helper(self, mul, div);
|
||||
@@ -1175,49 +1186,49 @@ macro bool @is_same_vector_or_scalar(#vector_value, #vector_or_scalar) @private
|
||||
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
|
||||
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
|
||||
*>
|
||||
macro char[<*>] char[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
|
||||
macro char[<*>] char[<*>].muldiv(self, mul, div) => mul_div_helper(self, ($typeof(self))mul, ($typeof(self))div);
|
||||
|
||||
<*
|
||||
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
|
||||
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
|
||||
*>
|
||||
macro ichar[<*>] ichar[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
|
||||
macro ichar[<*>] ichar[<*>].muldiv(self, mul, div) => mul_div_helper(self, ($typeof(self))mul, ($typeof(self))div);
|
||||
|
||||
<*
|
||||
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
|
||||
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
|
||||
*>
|
||||
macro short[<*>] short[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
|
||||
macro short[<*>] short[<*>].muldiv(self, mul, div) => mul_div_helper(self, ($typeof(self))mul, ($typeof(self))div);
|
||||
|
||||
<*
|
||||
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
|
||||
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
|
||||
*>
|
||||
macro ushort[<*>] ushort[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
|
||||
macro ushort[<*>] ushort[<*>].muldiv(self, mul, div) => mul_div_helper(self, ($typeof(self))mul, ($typeof(self))div);
|
||||
|
||||
<*
|
||||
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
|
||||
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
|
||||
*>
|
||||
macro int[<*>] int[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
|
||||
macro int[<*>] int[<*>].muldiv(self, mul, div) => mul_div_helper(self, ($typeof(self))mul, ($typeof(self))div);
|
||||
|
||||
<*
|
||||
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
|
||||
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
|
||||
*>
|
||||
macro uint[<*>] uint[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
|
||||
macro uint[<*>] uint[<*>].muldiv(self, mul, div) => mul_div_helper(self, ($typeof(self))mul, ($typeof(self))div);
|
||||
|
||||
<*
|
||||
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
|
||||
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
|
||||
*>
|
||||
macro long[<*>] long[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
|
||||
macro long[<*>] long[<*>].muldiv(self, mul, div) => mul_div_helper(self, ($typeof(self))mul, ($typeof(self))div);
|
||||
|
||||
<*
|
||||
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
|
||||
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
|
||||
*>
|
||||
macro ulong[<*>] ulong[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
|
||||
macro ulong[<*>] ulong[<*>].muldiv(self, mul, div) => mul_div_helper(self, ($typeof(self))mul, ($typeof(self))div);
|
||||
|
||||
<*
|
||||
@require types::is_int($typeof(a)) : `The input must be an integer`
|
||||
|
||||
@@ -117,7 +117,7 @@ macro bool next_bool(random)
|
||||
*>
|
||||
macro float next_float(random)
|
||||
{
|
||||
uint val = random.next_int() & (1 << 24 - 1);
|
||||
uint val = random.next_int() & (1U << 24 - 1);
|
||||
return val * 0x1.0p-24f;
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ macro double next_double(random)
|
||||
}
|
||||
|
||||
// True if the value is a Random.
|
||||
macro bool is_random(random) => $assignable(random, Random);
|
||||
macro bool is_random(random) => @assignable_to(random, Random);
|
||||
|
||||
macro uint128 @long_to_int128(#function) => (uint128)#function << 64 + #function;
|
||||
macro ulong @int_to_long(#function) => (ulong)#function << 32 + #function;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module std::math::random;
|
||||
import std::hash::fnv32a, std::time;
|
||||
import std::hash::a5hash, std::time;
|
||||
|
||||
const ODD_PHI64 @local = 0x9e3779b97f4a7c15;
|
||||
const MUL_MCG64 @local = 0xf1357aea2e62a9c5;
|
||||
@@ -69,12 +69,11 @@ fn void seeder(char[] input, char[] out_buffer)
|
||||
|
||||
macro uint hash(value) @local
|
||||
{
|
||||
return fnv32a::hash(&&bitcast(value, char[$sizeof(value)]));
|
||||
return (uint)a5hash::hash(&&bitcast(value, char[$sizeof(value)]));
|
||||
}
|
||||
|
||||
fn char[8 * 4] entropy() @if(!env::WASM_NOLIBC)
|
||||
fn char[8 * 4] entropy() @if(!env::FREESTANDING_WASM)
|
||||
{
|
||||
|
||||
void* addr = malloc(1);
|
||||
free(addr);
|
||||
static uint random_int;
|
||||
@@ -92,7 +91,7 @@ fn char[8 * 4] entropy() @if(!env::WASM_NOLIBC)
|
||||
return bitcast(entropy_data, char[8 * 4]);
|
||||
}
|
||||
|
||||
fn char[8 * 4] entropy() @if(env::WASM_NOLIBC)
|
||||
fn char[8 * 4] entropy() @if(env::FREESTANDING_WASM)
|
||||
{
|
||||
static uint random_int;
|
||||
random_int += 0xedf19156;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module std::net::os;
|
||||
const bool SUPPORTS_INET = env::LIBC && (env::WIN32 || env::DARWIN || env::LINUX || env::ANDROID);
|
||||
const bool SUPPORTS_INET = env::LIBC && (env::WIN32 || env::DARWIN || env::LINUX || env::ANDROID || env::OPENBSD);
|
||||
|
||||
typedef AIFamily = CInt;
|
||||
typedef AIProtocol = CInt;
|
||||
@@ -23,7 +23,7 @@ struct AddrInfo
|
||||
ZString ai_canonname;
|
||||
SockAddrPtr ai_addr;
|
||||
}
|
||||
struct @if(env::LINUX)
|
||||
struct @if(env::LINUX || env::OPENBSD)
|
||||
{
|
||||
SockAddrPtr ai_addr;
|
||||
ZString ai_canonname;
|
||||
@@ -53,12 +53,41 @@ const AIFamily AF_APPLETALK = PLATFORM_AF_APPLETALK;
|
||||
|
||||
const O_NONBLOCK = PLATFORM_O_NONBLOCK;
|
||||
|
||||
extern fn CInt getaddrinfo(ZString nodename, ZString servname, AddrInfo* hints, AddrInfo** res) @if(SUPPORTS_INET);
|
||||
extern fn void freeaddrinfo(AddrInfo* res) @if(SUPPORTS_INET);
|
||||
extern fn CInt setsockopt(NativeSocket socket, CInt level, CInt optname, void* optval, Socklen_t optlen) @if(SUPPORTS_INET);
|
||||
extern fn CInt getsockopt(NativeSocket socket, CInt level, CInt optname, void* optval, Socklen_t optlen) @if(SUPPORTS_INET);
|
||||
|
||||
module std::net::os @if(!env::LIBC || !(env::WIN32 || env::DARWIN || env::LINUX || env::ANDROID));
|
||||
<*
|
||||
The getaddrinfo() function is used to get a list of IP addresses and port numbers for host hostname and service servname.
|
||||
|
||||
@param [in] nodename
|
||||
@param [in] servname
|
||||
@param [in] hints
|
||||
@param [out] res
|
||||
@require (void*)nodename || (void*)servname : "One the names must be non-null"
|
||||
*>
|
||||
extern fn CInt getaddrinfo(ZString nodename, ZString servname, AddrInfo* hints, AddrInfo** res) @if(SUPPORTS_INET);
|
||||
|
||||
<*
|
||||
freeaddrinfo() frees an AddrInfo created by getaddrinfo.
|
||||
|
||||
@param [&in] res
|
||||
*>
|
||||
extern fn void freeaddrinfo(AddrInfo* res) @if(SUPPORTS_INET);
|
||||
|
||||
<*
|
||||
Set options on a socket.
|
||||
|
||||
@param [out] optval
|
||||
*>
|
||||
extern fn CInt setsockopt(NativeSocket socket, CInt level, CInt optname, void* optval, Socklen_t optlen) @if(SUPPORTS_INET);
|
||||
|
||||
<*
|
||||
Get options on a socket
|
||||
|
||||
@param [in] optval
|
||||
@param [inout] optlen
|
||||
*>
|
||||
extern fn CInt getsockopt(NativeSocket socket, CInt level, CInt optname, void* optval, Socklen_t* optlen) @if(SUPPORTS_INET);
|
||||
|
||||
module std::net::os @if(!env::LIBC || !(env::WIN32 || env::DARWIN || env::LINUX || env::ANDROID || env::OPENBSD));
|
||||
|
||||
const AIFamily PLATFORM_AF_INET6 = 0;
|
||||
const AIFamily PLATFORM_AF_IPX = 0;
|
||||
|
||||
109
lib/std/net/os/openbsd.c3
Normal file
109
lib/std/net/os/openbsd.c3
Normal file
@@ -0,0 +1,109 @@
|
||||
module std::net::os @if(env::OPENBSD);
|
||||
import libc;
|
||||
|
||||
// for most of this, see OpenBSD /usr/include/sys/socket.h
|
||||
const AIFlags AI_EXT = 0x8;
|
||||
const AIFlags AI_NUMERICSERV = 0x10;
|
||||
const AIFlags AI_FQDN = 0x20;
|
||||
const AIFlags AI_ADDRCONFIG = 0x40;
|
||||
|
||||
const AIFamily PLATFORM_AF_LOCAL = AF_UNIX; // draft POSIX compatibility
|
||||
const AIFamily PLATFORM_AF_IMPLINK = 3; // arpanet imp addresses
|
||||
const AIFamily PLATFORM_AF_PUP = 4; // pup protocols: e.g. BSP
|
||||
const AIFamily PLATFORM_AF_CHAOS = 5; // mit CHAOS protocols
|
||||
const AIFamily PLATFORM_AF_NS = 6; // XEROX NS protocols
|
||||
const AIFamily PLATFORM_AF_ISO = 7; // ISO protocols
|
||||
const AIFamily PLATFORM_AF_OSI = PLATFORM_AF_ISO;
|
||||
const AIFamily PLATFORM_AF_ECMA = 8; // european computer manufacturers
|
||||
const AIFamily PLATFORM_AF_DATAKIT = 9; // datakit protocols
|
||||
const AIFamily PLATFORM_AF_CCITT = 10; // CCITT protocols, X.25 etc
|
||||
const AIFamily PLATFORM_AF_SNA = 11; // IBM SNA
|
||||
const AIFamily PLATFORM_AF_DECNET = 12; // DECnet
|
||||
const AIFamily PLATFORM_AF_DLI = 13; // DEC Direct data link interface
|
||||
const AIFamily PLATFORM_AF_LAT = 14; // LAT
|
||||
const AIFamily PLATFORM_AF_HYLINK = 15; // NSC Hyperchannel
|
||||
const AIFamily PLATFORM_AF_APPLETALK = 16; // Apple Talk
|
||||
const AIFamily PLATFORM_AF_ROUTE = 17; // Internal Routing Protocol
|
||||
const AIFamily PLATFORM_AF_LINK = 18; // Link layer interface
|
||||
const AIFamily PLATFORM_PSEUDO_AF_XTP = 19; // eXpress Transfer Protocol (no AF)
|
||||
const AIFamily PLATFORM_AF_COIP = 20; // connection-oriented IP, aka ST II
|
||||
const AIFamily PLATFORM_AF_CNT = 21; // Computer Network Technology
|
||||
const AIFamily PLATFORM_PSEUDO_AF_RTIP = 22; // Help Identify RTIP packets
|
||||
const AIFamily PLATFORM_AF_IPX = 23; // Novell Internet Protocol
|
||||
const AIFamily PLATFORM_AF_INET6 = 24; // IPv6
|
||||
const AIFamily PLATFORM_PSEUDO_AF_PIP = 25; // Help Identify PIP packets
|
||||
const AIFamily PLATFORM_AF_ISDN = 26; // Integrated Services Digital Network*/
|
||||
const AIFamily PLATFORM_AF_E164 = PLATFORM_AF_ISDN; // CCITT E.164 recommendation
|
||||
const AIFamily PLATFORM_AF_NATM = 27; // native ATM access
|
||||
const AIFamily PLATFORM_AF_ENCAP = 28;
|
||||
const AIFamily PLATFORM_AF_SIP = 29; // Simple Internet Protocol
|
||||
const AIFamily PLATFORM_AF_KEY = 30;
|
||||
const AIFamily PLATFORM_PSEUDO_AF_HDRCMPLT = 31; // Used by BPF to not rewrite headers in interface output routine
|
||||
const AIFamily PLATFORM_AF_BLUETOOTH = 32; // Bluetooth
|
||||
const AIFamily PLATFORM_AF_MPLS = 33; // MPLS
|
||||
const AIFamily PLATFORM_PSEUDO_AF_PFLOW = 34; // pflow
|
||||
const AIFamily PLATFORM_PSEUDO_AF_PIPEX = 35; // PIPEX
|
||||
const AIFamily PLATFORM_AF_FRAME = 36; // frame (Ethernet) sockets
|
||||
const AIFamily PLATFORM_AF_MAX = 37;
|
||||
|
||||
// see: https://github.com/openbsd/src/blob/1ca4dc792538d17b4c2bd71550f68070505fd7b9/sys/sys/socket.h#L157
|
||||
const int SOL_SOCKET = 0xFFFF;
|
||||
|
||||
const int SO_DEBUG = 0x0001; // turn on debugging info recording
|
||||
const int SO_ACCEPTCONN = 0x0002; // socket has had listen()
|
||||
const int SO_REUSEADDR = 0x0004; // allow local address reuse
|
||||
const int SO_KEEPALIVE = 0x0008; // keep connections alive
|
||||
const int SO_DONTROUTE = 0x0010; // just use interface addresses
|
||||
const int SO_BROADCAST = 0x0020; // permit sending of broadcast msgs
|
||||
const int SO_USELOOPBACK = 0x0040; // bypass hardware when possible
|
||||
const int SO_LINGER = 0x0080; // linger on close if data present
|
||||
const int SO_OOBINLINE = 0x0100; // leave received OOB data in line
|
||||
const int SO_REUSEPORT = 0x0200; // allow local address & port reuse
|
||||
const int SO_TIMESTAMP = 0x0800; // timestamp received dgram traffic
|
||||
const int SO_BINDANY = 0x1000; // allow bind to any address
|
||||
const int SO_ZEROIZE = 0x2000; // zero out all mbufs sent over socket
|
||||
|
||||
// additional
|
||||
const int SO_SNDBUF = 0x1001; // send buffer size
|
||||
const int SO_RCVBUF = 0x1002; // receive buffer size
|
||||
const int SO_SNDLOWAT = 0x1003; // send low-water mark
|
||||
const int SO_RCVLOWAT = 0x1004; // receive low-water mark
|
||||
const int SO_SNDTIMEO = 0x1005; // send timeout
|
||||
const int SO_RCVTIMEO = 0x1006; // receive timeout
|
||||
const int SO_ERROR = 0x1007; // get error status and clear
|
||||
const int SO_TYPE = 0x1008; // get socket type
|
||||
const int SO_NETPROC = 0x1020; // multiplex; network processing
|
||||
const int SO_RTABLE = 0x1021; // routing table to be used
|
||||
const int SO_PEERCRED = 0x1022; // get connect-time credentials
|
||||
const int SO_SPLICE = 0x1023; // splice data to other socket
|
||||
const int SO_DOMAIN = 0x1024; // get socket domain
|
||||
const int SO_PROTOCOL = 0x1025; // get socket protocol
|
||||
|
||||
// POLLIN through POLLNVAL are predefined by lib/std/net/os/posix.c3
|
||||
const CUShort POLLRDNORM = 0x0040;
|
||||
const CUShort POLLNORM = POLLRDNORM;
|
||||
const CUShort POLLWRNORM = POLLOUT;
|
||||
const CUShort POLLRDBAND = 0x0080;
|
||||
const CUShort POLLWRBAND = 0x0100;
|
||||
|
||||
const CInt MSG_OOB = 0x1; // process out-of-band data
|
||||
const CInt MSG_PEEK = 0x2; // peek at incoming message
|
||||
const CInt MSG_DONTROUTE = 0x4; // send without using routing tables
|
||||
const CInt MSG_EOR = 0x8; // data completes record
|
||||
const CInt MSG_TRUNC = 0x10; // data discarded before delivery
|
||||
const CInt MSG_CTRUNC = 0x20; // control data lost before delivery
|
||||
const CInt MSG_WAITALL = 0x40; // wait for full request or error
|
||||
const CInt MSG_DONTWAIT = 0x80; // this message should be nonblocking
|
||||
const CInt MSG_BCAST = 0x100; // this message rec'd as broadcast
|
||||
const CInt MSG_MCAST = 0x200; // this message rec'd as multicast
|
||||
const CInt MSG_NOSIGNAL = 0x400; // do not send SIGPIPE
|
||||
const CInt MSG_CMSG_CLOEXEC = 0x800; // set FD_CLOEXEC on received fds
|
||||
const CInt MSG_WAITFORONE = 0x1000; // nonblocking but wait for one msg
|
||||
|
||||
// socket creation options
|
||||
const SOCK_CLOEXEC = 0x8000; // set FD_CLOEXEC
|
||||
const SOCK_NONBLOCK = 0x4000; // set O_NONBLOCK
|
||||
const SOCK_NONBLOCK_INHERIT = 0x2000; // inherit O_NONBLOCK from listener
|
||||
const SOCK_DNS = 0x1000; // set SS_DNS
|
||||
|
||||
const PLATFORM_O_NONBLOCK = SOCK_NONBLOCK;
|
||||
@@ -23,33 +23,56 @@ macro void @loop_over_ai(AddrInfo* ai; @body(NativeSocket fd, AddrInfo* ai))
|
||||
}
|
||||
}
|
||||
|
||||
const Duration POLL_FOREVER = -1;
|
||||
const Duration POLL_FOREVER = (Duration)-1;
|
||||
|
||||
typedef PollSubscribes = ushort;
|
||||
typedef PollEvents = ushort;
|
||||
enum PollSubscribe : const ushort
|
||||
{
|
||||
ANY_READ = os::POLLIN,
|
||||
PRIO_READ = os::POLLPRI,
|
||||
OOB_READ = os::POLLRDBAND,
|
||||
READ = os::POLLRDNORM,
|
||||
ANY_WRITE = os::POLLOUT,
|
||||
OOB_WRITE = os::POLLWRBAND,
|
||||
WRITE = os::POLLWRNORM,
|
||||
}
|
||||
|
||||
const PollSubscribes SUBSCRIBE_ANY_READ = os::POLLIN;
|
||||
const PollSubscribes SUBSCRIBE_PRIO_READ = os::POLLPRI;
|
||||
const PollSubscribes SUBSCRIBE_OOB_READ = os::POLLRDBAND;
|
||||
const PollSubscribes SUBSCRIBE_READ = os::POLLRDNORM;
|
||||
const PollSubscribes SUBSCRIBE_ANY_WRITE = os::POLLOUT;
|
||||
const PollSubscribes SUBSCRIBE_OOB_WRITE = os::POLLWRBAND;
|
||||
const PollSubscribes SUBSCRIBE_WRITE = os::POLLWRNORM;
|
||||
const PollSubscribe SUBSCRIBE_ANY_READ = (PollSubscribe)os::POLLIN;
|
||||
const PollSubscribe SUBSCRIBE_PRIO_READ = (PollSubscribe)os::POLLPRI;
|
||||
const PollSubscribe SUBSCRIBE_OOB_READ = (PollSubscribe)os::POLLRDBAND;
|
||||
const PollSubscribe SUBSCRIBE_READ = (PollSubscribe)os::POLLRDNORM;
|
||||
const PollSubscribe SUBSCRIBE_ANY_WRITE = (PollSubscribe)os::POLLOUT;
|
||||
const PollSubscribe SUBSCRIBE_OOB_WRITE = (PollSubscribe)os::POLLWRBAND;
|
||||
const PollSubscribe SUBSCRIBE_WRITE = (PollSubscribe)os::POLLWRNORM;
|
||||
|
||||
const PollEvents POLL_EVENT_READ_PRIO = os::POLLPRI;
|
||||
const PollEvents POLL_EVENT_READ_OOB = os::POLLRDBAND;
|
||||
const PollEvents POLL_EVENT_READ = os::POLLRDNORM;
|
||||
const PollEvents POLL_EVENT_WRITE_OOB = os::POLLWRBAND;
|
||||
const PollEvents POLL_EVENT_WRITE = os::POLLWRNORM;
|
||||
const PollEvents POLL_EVENT_DISCONNECT = os::POLLHUP;
|
||||
const PollEvents POLL_EVENT_ERROR = os::POLLERR;
|
||||
const PollEvents POLL_EVENT_INVALID = os::POLLNVAL;
|
||||
enum PollEvent : const ushort
|
||||
{
|
||||
READ_PRIO = os::POLLPRI,
|
||||
READ_OOB = os::POLLRDBAND,
|
||||
READ = os::POLLRDNORM,
|
||||
WRITE_OOB = os::POLLWRBAND,
|
||||
WRITE = os::POLLWRNORM,
|
||||
DISCONNECT = os::POLLHUP,
|
||||
ERROR = os::POLLERR,
|
||||
INVALID = os::POLLNVAL,
|
||||
}
|
||||
|
||||
const PollEvent POLL_EVENT_READ_PRIO = (PollEvent)os::POLLPRI;
|
||||
const PollEvent POLL_EVENT_READ_OOB = (PollEvent)os::POLLRDBAND;
|
||||
const PollEvent POLL_EVENT_READ = (PollEvent)os::POLLRDNORM;
|
||||
const PollEvent POLL_EVENT_WRITE_OOB = (PollEvent)os::POLLWRBAND;
|
||||
const PollEvent POLL_EVENT_WRITE = (PollEvent)os::POLLWRNORM;
|
||||
const PollEvent POLL_EVENT_DISCONNECT = (PollEvent)os::POLLHUP;
|
||||
const PollEvent POLL_EVENT_ERROR = (PollEvent)os::POLLERR;
|
||||
const PollEvent POLL_EVENT_INVALID = (PollEvent)os::POLLNVAL;
|
||||
|
||||
alias PollSubscribes @deprecated("Use PollSubscribe") = PollSubscribe;
|
||||
alias PollEvents @deprecated("Use PollEvent") = PollEvent;
|
||||
|
||||
struct Poll
|
||||
{
|
||||
NativeSocket socket;
|
||||
PollSubscribes events;
|
||||
PollEvents revents;
|
||||
PollSubscribe events;
|
||||
PollEvent revents;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -116,7 +139,8 @@ fn void? Socket.set_option(&self, SocketOption option, bool value)
|
||||
fn bool? Socket.get_option(&self, SocketOption option)
|
||||
{
|
||||
CInt flag;
|
||||
int errcode = os::setsockopt(self.sock, os::SOL_SOCKET, option.value, &flag, CInt.sizeof);
|
||||
Socklen_t socklen = CInt.sizeof;
|
||||
int errcode = os::getsockopt(self.sock, os::SOL_SOCKET, option.value, &flag, &socklen);
|
||||
if (errcode != 0) return SOCKOPT_FAILED?;
|
||||
return (bool)flag;
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@ import std::time, libc;
|
||||
typedef TcpSocket = inline Socket;
|
||||
typedef TcpServerSocket = inline Socket;
|
||||
|
||||
fn TcpSocket? connect(String host, uint port, Duration timeout = 0, SocketOption... options, IpProtocol ip_protocol = UNSPECIFIED)
|
||||
fn TcpSocket? connect(String host, uint port, Duration timeout = time::DURATION_ZERO, SocketOption... options, IpProtocol ip_protocol = UNSPECIFIED)
|
||||
{
|
||||
AddrInfo* ai = net::addrinfo(host, port, ip_protocol.ai_family, os::SOCK_STREAM)!;
|
||||
defer os::freeaddrinfo(ai);
|
||||
if (timeout > 0)
|
||||
if (timeout > time::DURATION_ZERO)
|
||||
{
|
||||
return (TcpSocket)net::connect_with_timeout_from_addrinfo(ai, options, timeout)!;
|
||||
}
|
||||
|
||||
@@ -87,11 +87,12 @@ fn void*[] capture_current(void*[] buffer)
|
||||
|
||||
alias BacktraceList = List{Backtrace};
|
||||
|
||||
alias symbolize_backtrace @if(env::LINUX) = linux::symbolize_backtrace;
|
||||
alias symbolize_backtrace @if(env::WIN32) = win32::symbolize_backtrace;
|
||||
alias symbolize_backtrace @if(env::DARWIN) = darwin::symbolize_backtrace;
|
||||
alias symbolize_backtrace @if(env::LINUX) = linux::symbolize_backtrace;
|
||||
alias symbolize_backtrace @if(env::WIN32) = win32::symbolize_backtrace;
|
||||
alias symbolize_backtrace @if(env::DARWIN) = darwin::symbolize_backtrace;
|
||||
alias symbolize_backtrace @if(env::OPENBSD) = openbsd::symbolize_backtrace;
|
||||
|
||||
fn BacktraceList? symbolize_backtrace(Allocator allocator, void*[] backtrace) @if(!env::NATIVE_STACKTRACE)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ fn String? get_home_dir(Allocator allocator)
|
||||
|
||||
|
||||
<*
|
||||
Returns the current user's config directory.
|
||||
Returns the current user's config directory.
|
||||
*>
|
||||
fn Path? get_config_dir(Allocator allocator) => @pool()
|
||||
{
|
||||
@@ -87,13 +87,15 @@ fn Path? get_config_dir(Allocator allocator) => @pool()
|
||||
return path::new(allocator, tget_var("AppData"));
|
||||
$else
|
||||
$if env::DARWIN:
|
||||
String s = tget_var("HOME")!;
|
||||
String home_dir = tget_var("HOME")!;
|
||||
const DIR = "Library/Application Support";
|
||||
$else
|
||||
String s = tget_var("XDG_CONFIG_HOME") ?? tget_var("HOME")!;
|
||||
String? config_path = tget_var("XDG_CONFIG_HOME");
|
||||
if (try config_path && config_path.len > 0) return path::new(allocator, config_path);
|
||||
String home_dir = tget_var("HOME")!;
|
||||
const DIR = ".config";
|
||||
$endif
|
||||
return path::temp(s).append(allocator, DIR);
|
||||
return path::temp(home_dir).append(allocator, DIR);
|
||||
$endif
|
||||
}
|
||||
|
||||
@@ -115,10 +117,10 @@ fn bool clear_var(String name) => @pool()
|
||||
$endswitch
|
||||
}
|
||||
|
||||
fn String? executable_path(Allocator allocator)
|
||||
fn String? executable_path()
|
||||
{
|
||||
$if env::DARWIN:
|
||||
return darwin::executable_path(allocator);
|
||||
return darwin::executable_path();
|
||||
$else
|
||||
return NOT_FOUND?;
|
||||
$endif
|
||||
|
||||
139
lib/std/os/linux/epoll.c3
Normal file
139
lib/std/os/linux/epoll.c3
Normal file
@@ -0,0 +1,139 @@
|
||||
module std::os::linux @if(env::LINUX);
|
||||
import libc;
|
||||
|
||||
// https://github.com/bminor/glibc/blob/master/sysdeps/unix/sysv/linux/sys/epoll.h
|
||||
const uint EPOLLIN = EpollEvents.EPOLLIN;
|
||||
const uint EPOLLPRI = EpollEvents.EPOLLPRI;
|
||||
const uint EPOLLOUT = EpollEvents.EPOLLOUT;
|
||||
const uint EPOLLRDNORM = EpollEvents.EPOLLRDNORM;
|
||||
const uint EPOLLRDBAND = EpollEvents.EPOLLRDBAND;
|
||||
const uint EPOLLWRNORM = EpollEvents.EPOLLWRNORM;
|
||||
const uint EPOLLWRBAND = EpollEvents.EPOLLWRBAND;
|
||||
const uint EPOLLMSG = EpollEvents.EPOLLMSG;
|
||||
const uint EPOLLERR = EpollEvents.EPOLLERR;
|
||||
const uint EPOLLHUP = EpollEvents.EPOLLHUP;
|
||||
const uint EPOLLRDHUP = EpollEvents.EPOLLRDHUP;
|
||||
const uint EPOLLEXCLUSIVE = EpollEvents.EPOLLEXCLUSIVE;
|
||||
const uint EPOLLWAKEUP = EpollEvents.EPOLLWAKEUP;
|
||||
const uint EPOLLONESHOT = EpollEvents.EPOLLONESHOT;
|
||||
const uint EPOLLET = EpollEvents.EPOLLET;
|
||||
|
||||
enum EpollEvents: const inline uint
|
||||
{
|
||||
EPOLLIN = 0x001,
|
||||
EPOLLPRI = 0x002,
|
||||
EPOLLOUT = 0x004,
|
||||
EPOLLRDNORM = 0x040,
|
||||
EPOLLRDBAND = 0x080,
|
||||
EPOLLWRNORM = 0x100,
|
||||
EPOLLWRBAND = 0x200,
|
||||
EPOLLMSG = 0x400,
|
||||
EPOLLERR = 0x008,
|
||||
EPOLLHUP = 0x010,
|
||||
EPOLLRDHUP = 0x2000,
|
||||
EPOLLEXCLUSIVE = 1u << 28,
|
||||
EPOLLWAKEUP = 1u << 29,
|
||||
EPOLLONESHOT = 1u << 30,
|
||||
EPOLLET = 1u << 31
|
||||
}
|
||||
|
||||
/* Valid opcodes ( "op" parameter ) to issue to epoll_ctl(). */
|
||||
const uint EPOLL_CTL_ADD = 1; /* Add a file descriptor to the interface. */
|
||||
const uint EPOLL_CTL_DEL = 2; /* Remove a file descriptor from the interface. */
|
||||
const uint EPOLL_CTL_MOD = 3; /* Change file descriptor epoll_event structure. */
|
||||
|
||||
union EpollData
|
||||
{
|
||||
void* ptr;
|
||||
int fd;
|
||||
uint u32;
|
||||
ulong u64;
|
||||
}
|
||||
|
||||
struct EpollEvent @packed
|
||||
{
|
||||
uint events; /* Epoll events */
|
||||
EpollData data; /* User data variable */
|
||||
}
|
||||
|
||||
struct EpollParams
|
||||
{
|
||||
uint busy_poll_usecs;
|
||||
ushort busy_poll_budget;
|
||||
char prefer_busy_poll;
|
||||
|
||||
/* pad the struct to a multiple of 64bits */
|
||||
char __pad;
|
||||
}
|
||||
|
||||
// https://github.com/MatthiasWM/mosrun/blob/master/scratchpad.txt#L330-L348
|
||||
/*
|
||||
* Ioctl's have the command encoded in the lower word,
|
||||
* and the size of any in or out parameters in the upper
|
||||
* word. The high 2 bits of the upper word are used
|
||||
* to encode the in/out status of the parameter; for now
|
||||
* we restrict parameters to at most 256 bytes (disklabels are 216 bytes).
|
||||
*/
|
||||
const IOCPARM_MASK = 0xff; /* parameters must be < 256 bytes */
|
||||
const IOC_VOID = 0x20000000; /* no parameters */
|
||||
const IOC_OUT = 0x40000000; /* copy out parameters */
|
||||
const IOC_IN = 0x80000000; /* copy in parameters */
|
||||
const IOC_INOUT = (IOC_IN|IOC_OUT);
|
||||
|
||||
macro ulong @ioctl_IO ($x,$y) @const => (IOC_VOID | (($x)<<8)|y);
|
||||
macro ulong @ioctl_IOR ($x,$y,$Type) @const => (IOC_OUT |(($Type.sizeof&IOCPARM_MASK)<<16)|(($x)<<8)|$y);
|
||||
macro ulong @ioctl_IOW ($x,$y,$Type) @const => (IOC_IN |(($Type.sizeof&IOCPARM_MASK)<<16)|(($x)<<8)|$y);
|
||||
/* this should be _IORW, but stdio got there first */
|
||||
macro ulong @ioctl_IOWR ($x,$y,$Type) @const => (IOC_INOUT|(($Type.sizeof&IOCPARM_MASK)<<16)|(($x)<<8)|$y);
|
||||
macro ulong @ioctl_ION ($x,$y,$n) @const => (IOC_INOUT|((($n)&IOCPARM_MASK)<<16)|(($x)<<8)|y);
|
||||
|
||||
// https://github.com/bminor/glibc/blob/master/sysdeps/unix/sysv/linux/sys/epoll.h
|
||||
const EPOLL_IOC_TYPE = 0x8A;
|
||||
const EPIOCSPARAMS = @ioctl_IOW(EPOLL_IOC_TYPE, 0x01, EpollParams);
|
||||
const EPIOCGPARAMS = @ioctl_IOR(EPOLL_IOC_TYPE, 0x02, EpollParams);
|
||||
|
||||
<*
|
||||
* Creates an epoll instance. Returns an fd for the new instance.
|
||||
* The "size" parameter is a hint specifying the number of file
|
||||
* descriptors to be associated with the new instance.
|
||||
* The fd returned by epoll_create() should be closed with close().
|
||||
*>
|
||||
extern fn int epoll_create(int);
|
||||
|
||||
<*
|
||||
* Same as epoll_create but with an FLAGS parameter.
|
||||
* The unused SIZE parameter has been dropped.
|
||||
*>
|
||||
extern fn int epoll_create1(int);
|
||||
|
||||
<*
|
||||
* Manipulate an epoll instance "epfd". Returns 0 in case of success,
|
||||
* -1 in case of error ( the "errno" variable will contain the
|
||||
* specific error code ) The "op" parameter is one of the EPOLL_CTL_*
|
||||
* constants defined above. The "fd" parameter is the target of the
|
||||
* operation. The "event" parameter describes which events the caller
|
||||
* is interested in and any associated user data.
|
||||
*>
|
||||
extern fn int epoll_ctl (int, int, int, EpollEvent*);
|
||||
|
||||
<*
|
||||
* Wait for events on an epoll instance "epfd". Returns the number of
|
||||
* triggered events returned in "events" buffer. Or -1 in case of
|
||||
* error with the "errno" variable set to the specific error code. The
|
||||
* "events" parameter is a buffer that will contain triggered
|
||||
* events. The "maxevents" is the maximum number of events to be
|
||||
* returned ( usually size of "events" ). The "timeout" parameter
|
||||
* specifies the maximum wait time in milliseconds (-1 == infinite).
|
||||
*>
|
||||
extern fn int epoll_wait (int, EpollEvent*, int, int);
|
||||
|
||||
<*
|
||||
* Same as epoll_wait, but the thread's signal mask is temporarily
|
||||
* and atomically replaced with the one provided as parameter.
|
||||
*>
|
||||
extern fn int epoll_pwait (int, EpollEvent*, int, int, Sigset_t*);
|
||||
|
||||
<*
|
||||
* Same as epoll_pwait, but the timeout as a timespec.
|
||||
*>
|
||||
extern fn int epoll_pwait2 (int, EpollEvent*, int, TimeSpec*, Sigset_t*);
|
||||
@@ -1,5 +1,34 @@
|
||||
module std::os::linux @if(env::LINUX);
|
||||
import libc, std::os, std::io, std::collections::list;
|
||||
import libc, std::os, std::io, std::collections::list, std::net::os;
|
||||
|
||||
// https://man7.org/linux/man-pages/man3/inet_ntop.3.html
|
||||
extern fn char** inet_ntop(int, void*, char*, Socklen_t);
|
||||
|
||||
// https://linux.die.net/man/3/ntohs
|
||||
<*
|
||||
* The htonl() function converts the unsigned integer hostlong from host byte order to network byte order.
|
||||
*>
|
||||
extern fn uint htonl(uint hostlong);
|
||||
<*
|
||||
* The htons() function converts the unsigned short integer hostshort from host byte order to network byte order.
|
||||
*>
|
||||
extern fn ushort htons(ushort hostshort);
|
||||
<*
|
||||
* The ntohl() function converts the unsigned integer netlong from network byte order to host byte order.
|
||||
*>
|
||||
extern fn uint ntohl(uint netlong);
|
||||
<*
|
||||
* The ntohs() function converts the unsigned short integer netshort from network byte order to host byte order.
|
||||
*>
|
||||
extern fn ushort ntohs(ushort netshort);
|
||||
|
||||
// https://man7.org/linux/man-pages/man3/bzero.3.html
|
||||
<*
|
||||
* The bzero() function erases the data in the n bytes of the memory
|
||||
* starting at the location pointed to by s, by writing zeros (bytes
|
||||
* containing '\0') to that area.
|
||||
*>
|
||||
extern fn void bzero(char*, usz);
|
||||
|
||||
extern fn isz readlink(ZString path, char* buf, usz bufsize);
|
||||
|
||||
@@ -89,6 +118,35 @@ struct Linux_Dl_info
|
||||
void* dli_saddr; /* Address of nearest symbol */
|
||||
}
|
||||
|
||||
alias Dl_iterate_phdr_callback64 = fn CInt(Linux_dl_phdr_info_64*, usz, void*);
|
||||
alias Dl_iterate_phdr_callback32 = fn CInt(Linux_dl_phdr_info_32*, usz, void*);
|
||||
extern fn CInt dl_iterate_phdr64(Dl_iterate_phdr_callback64 callback, void* data);
|
||||
extern fn CInt dl_iterate_phdr32(Dl_iterate_phdr_callback32 callback, void* data);
|
||||
|
||||
struct Linux_dl_phdr_info_64
|
||||
{
|
||||
Elf64_Addr dlpi_addr;
|
||||
ZString dlpi_name;
|
||||
Elf64_Phdr* dlpi_phdr;
|
||||
Elf64_Half dlpi_phnum;
|
||||
ulong dlpi_adds;
|
||||
ulong dlpi_subs;
|
||||
usz dlpi_tsl_modid;
|
||||
void* dlpi_tls_data;
|
||||
}
|
||||
|
||||
struct Linux_dl_phdr_info_32
|
||||
{
|
||||
Elf32_Addr dlpi_addr;
|
||||
ZString dlpi_name;
|
||||
Elf32_Phdr* dlpi_phdr;
|
||||
Elf32_Half dlpi_phnum;
|
||||
ulong dlpi_adds;
|
||||
ulong dlpi_subs;
|
||||
usz dlpi_tsl_modid;
|
||||
void* dlpi_tls_data;
|
||||
}
|
||||
|
||||
fn ulong? elf_module_image_base(String path) @local
|
||||
{
|
||||
File file = file::open(path, "rb")!;
|
||||
@@ -130,70 +188,76 @@ fn ulong? elf_module_image_base(String path) @local
|
||||
|
||||
fn void? backtrace_add_from_exec(Allocator allocator, BacktraceList* list, void* addr) @local
|
||||
{
|
||||
char[] buf = mem::talloc_array(char, 1024);
|
||||
|
||||
String exec_path = process::execute_stdout_to_buffer(buf, {"realpath", "-e", string::tformat("/proc/%d/exe", posix::getpid())})!;
|
||||
char[1024] buf @noinit;
|
||||
String exec_path = process::execute_stdout_to_buffer(&buf, {"realpath", "-e", string::bformat(&&(char[64]){}, "/proc/%d/exe", posix::getpid())})!;
|
||||
String obj_name = exec_path.copy(allocator);
|
||||
String addr2line = process::execute_stdout_to_buffer(buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", exec_path, string::tformat("0x%x", addr)})!;
|
||||
String addr2line = process::execute_stdout_to_buffer(&buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", exec_path, string::bformat(&&(char[64]){}, "0x%x", addr)})!;
|
||||
return backtrace_add_addr2line(allocator, list, addr, addr2line, obj_name, "???");
|
||||
}
|
||||
|
||||
fn void? backtrace_add_from_dlinfo(Allocator allocator, BacktraceList* list, void* addr, Linux_Dl_info* info) @local
|
||||
{
|
||||
char[] buf = mem::talloc_array(char, 1024);
|
||||
char[1024] buf @noinit;
|
||||
|
||||
void* obj_addr = addr - (uptr)info.dli_fbase + (uptr)elf_module_image_base(info.dli_fname.str_view())!;
|
||||
ZString obj_path = info.dli_fname;
|
||||
String sname = info.dli_sname ? info.dli_sname.str_view() : "???";
|
||||
String addr2line = process::execute_stdout_to_buffer(buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", obj_path.str_view(), string::tformat("0x%x", obj_addr - 1)})!;
|
||||
String addr2line = process::execute_stdout_to_buffer(&buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", obj_path.str_view(), string::bformat(&&(char[64]){}, "0x%x", obj_addr - 1)})!;
|
||||
return backtrace_add_addr2line(allocator, list, addr, addr2line, info.dli_fname.str_view(), sname);
|
||||
}
|
||||
|
||||
fn Backtrace? backtrace_line_parse(Allocator allocator, String string, String obj_name, String func_name, bool is_inlined)
|
||||
{
|
||||
String[] parts = string.trim().tsplit(" at ");
|
||||
if (parts.len != 2) return NOT_FOUND?;
|
||||
|
||||
uint line = 0;
|
||||
String source = "";
|
||||
if (!parts[1].contains("?") && parts[1].contains(":"))
|
||||
@stack_mem(256; Allocator mem)
|
||||
{
|
||||
usz index = parts[1].rindex_of_char(':')!;
|
||||
source = parts[1][:index];
|
||||
line = parts[1][index + 1..].to_uint()!;
|
||||
}
|
||||
return {
|
||||
.function = parts[0].copy(allocator),
|
||||
.object_file = obj_name.copy(allocator),
|
||||
.file = source.copy(allocator),
|
||||
.line = line,
|
||||
.allocator = allocator,
|
||||
.is_inline = is_inlined
|
||||
String[] parts = string.trim().split(mem, " at ");
|
||||
if (parts.len != 2) return NOT_FOUND?;
|
||||
|
||||
uint line = 0;
|
||||
String source = "";
|
||||
if (!parts[1].contains("?") && parts[1].contains(":"))
|
||||
{
|
||||
usz index = parts[1].rindex_of_char(':')!;
|
||||
source = parts[1][:index];
|
||||
line = parts[1][index + 1..].to_uint()!;
|
||||
}
|
||||
return {
|
||||
.function = parts[0].copy(allocator),
|
||||
.object_file = obj_name.copy(allocator),
|
||||
.file = source.copy(allocator),
|
||||
.line = line,
|
||||
.allocator = allocator,
|
||||
.is_inline = is_inlined
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
fn void? backtrace_add_addr2line(Allocator allocator, BacktraceList* list, void* addr, String addr2line, String obj_name, String func_name) @local
|
||||
{
|
||||
String[] inline_parts = addr2line.tsplit("(inlined by)");
|
||||
usz last = inline_parts.len - 1;
|
||||
foreach (i, part : inline_parts)
|
||||
@stack_mem(1024; Allocator mem)
|
||||
{
|
||||
bool is_inline = i != last;
|
||||
Backtrace? trace = backtrace_line_parse(allocator, part, obj_name, func_name, is_inline);
|
||||
if (catch trace)
|
||||
String[] inline_parts = addr2line.split(mem, "(inlined by)");
|
||||
usz last = inline_parts.len - 1;
|
||||
foreach (i, part : inline_parts)
|
||||
{
|
||||
list.push({
|
||||
.function = func_name.copy(allocator),
|
||||
.object_file = obj_name.copy(allocator),
|
||||
.offset = (uptr)addr,
|
||||
.file = "".copy(allocator),
|
||||
.line = 0,
|
||||
.allocator = allocator,
|
||||
.is_inline = is_inline
|
||||
});
|
||||
continue;
|
||||
bool is_inline = i != last;
|
||||
Backtrace? trace = backtrace_line_parse(allocator, part, obj_name, func_name, is_inline);
|
||||
if (catch trace)
|
||||
{
|
||||
list.push({
|
||||
.function = func_name.copy(allocator),
|
||||
.object_file = obj_name.copy(allocator),
|
||||
.offset = (uptr)addr,
|
||||
.file = "".copy(allocator),
|
||||
.line = 0,
|
||||
.allocator = allocator,
|
||||
.is_inline = is_inline
|
||||
});
|
||||
continue;
|
||||
}
|
||||
list.push(trace);
|
||||
}
|
||||
list.push(trace);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn void? backtrace_add_element(Allocator allocator, BacktraceList *list, void* addr) @local
|
||||
@@ -204,15 +268,12 @@ fn void? backtrace_add_element(Allocator allocator, BacktraceList *list, void* a
|
||||
return;
|
||||
}
|
||||
|
||||
@pool()
|
||||
Linux_Dl_info info;
|
||||
if (dladdr(addr, &info) == 0)
|
||||
{
|
||||
Linux_Dl_info info;
|
||||
if (dladdr(addr, &info) == 0)
|
||||
{
|
||||
return backtrace_add_from_exec(allocator, list, addr);
|
||||
}
|
||||
return backtrace_add_from_dlinfo(allocator, list, addr, &info);
|
||||
};
|
||||
return backtrace_add_from_exec(allocator, list, addr);
|
||||
}
|
||||
return backtrace_add_from_dlinfo(allocator, list, addr, &info);
|
||||
}
|
||||
|
||||
fn BacktraceList? symbolize_backtrace(Allocator allocator, void*[] backtrace)
|
||||
@@ -227,12 +288,9 @@ fn BacktraceList? symbolize_backtrace(Allocator allocator, void*[] backtrace)
|
||||
}
|
||||
list.free();
|
||||
}
|
||||
@pool()
|
||||
foreach (addr : backtrace)
|
||||
{
|
||||
foreach (addr : backtrace)
|
||||
{
|
||||
backtrace_add_element(allocator, &list, addr)!;
|
||||
}
|
||||
};
|
||||
backtrace_add_element(allocator, &list, addr)!;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -79,19 +79,26 @@ alias Darwin_mach_timebase_info_data_t = Darwin_mach_timebase_info;
|
||||
extern fn void mach_timebase_info(Darwin_mach_timebase_info_data_t* timebase);
|
||||
extern fn ulong mach_absolute_time();
|
||||
|
||||
fn String? executable_path(Allocator allocator)
|
||||
fn String? executable_path()
|
||||
{
|
||||
char[4096] path;
|
||||
uint len = path.len;
|
||||
if (darwin_NSGetExecutablePath(&path, &len) < 0) return NOT_FOUND?;
|
||||
return ((ZString)&path).copy(allocator);
|
||||
static char[4096] path;
|
||||
static uint len = 0;
|
||||
if (!len)
|
||||
{
|
||||
char[4096] buf;
|
||||
uint temp_len = buf.len;
|
||||
if (darwin_NSGetExecutablePath(&buf, &temp_len) < 0) return NOT_FOUND?;
|
||||
path[:len] = buf[:len];
|
||||
len = (int)((ZString)&buf).len();
|
||||
}
|
||||
return (String)path[:len];
|
||||
}
|
||||
|
||||
fn uptr? load_address() @local
|
||||
{
|
||||
Darwin_segment_command_64* cmd = darwin::getsegbyname("__TEXT");
|
||||
if (!cmd) return backtrace::SEGMENT_NOT_FOUND?;
|
||||
String path = env::executable_path(tmem) ?? backtrace::EXECUTABLE_PATH_NOT_FOUND?!;
|
||||
String path = env::executable_path() ?? backtrace::EXECUTABLE_PATH_NOT_FOUND?!;
|
||||
uint dyld_count = darwin::_dyld_image_count();
|
||||
for (uint i = 0; i < dyld_count; i++)
|
||||
{
|
||||
@@ -103,23 +110,22 @@ fn uptr? load_address() @local
|
||||
return backtrace::IMAGE_NOT_FOUND?;
|
||||
}
|
||||
|
||||
|
||||
fn Backtrace? backtrace_load_element(Allocator allocator, String execpath, void* buffer, void* load_address) @local
|
||||
{
|
||||
@pool()
|
||||
if (buffer)
|
||||
{
|
||||
if (buffer)
|
||||
{
|
||||
char* buf = tmalloc(1024);
|
||||
String s = process::execute_stdout_to_buffer(buf[:1024],
|
||||
{ "atos", "-o", execpath, "-arch", env::AARCH64 ? "arm64" : "x86_64", "-l",
|
||||
string::tformat("%p", load_address),
|
||||
string::tformat("%p", buffer - 1),
|
||||
"-fullPath" })!;
|
||||
String[] parts = s.tsplit(" ");
|
||||
char[1024] buf;
|
||||
String s = process::execute_stdout_to_buffer(&buf,
|
||||
{ "atos", "-o", execpath, "-arch", env::AARCH64 ? "arm64" : "x86_64", "-l",
|
||||
string::bformat(&&(char[64]){}, "%p", load_address),
|
||||
string::bformat(&&(char[64]){}, "%p", buffer - 1),
|
||||
"-fullPath" })!;
|
||||
@stack_mem(512; Allocator mem)
|
||||
{
|
||||
String[] parts = s.split(mem, " ", 5);
|
||||
if (parts.len == 4)
|
||||
{
|
||||
String[] path_parts = parts[3].tsplit(":");
|
||||
String[] path_parts = parts[3].split(mem, ":");
|
||||
return {
|
||||
.offset = (uptr)buffer,
|
||||
.function = parts[0].copy(allocator),
|
||||
@@ -129,17 +135,17 @@ fn Backtrace? backtrace_load_element(Allocator allocator, String execpath, void*
|
||||
.allocator = allocator
|
||||
};
|
||||
}
|
||||
}
|
||||
Darwin_Dl_info info;
|
||||
if (!buffer || !darwin::dladdr(buffer, &info)) return backtrace::BACKTRACE_UNKNOWN;
|
||||
return {
|
||||
.offset = (uptr)buffer,
|
||||
.function = info.dli_sname ? info.dli_sname.copy(allocator) : "???".copy(allocator),
|
||||
.object_file = info.dli_fname.copy(allocator),
|
||||
.file = "".copy(allocator),
|
||||
.line = 0,
|
||||
.allocator = allocator
|
||||
};
|
||||
}
|
||||
Darwin_Dl_info info;
|
||||
if (!buffer || !darwin::dladdr(buffer, &info)) return backtrace::BACKTRACE_UNKNOWN;
|
||||
return {
|
||||
.offset = (uptr)buffer,
|
||||
.function = info.dli_sname ? info.dli_sname.copy(allocator) : "???".copy(allocator),
|
||||
.object_file = info.dli_fname.copy(allocator),
|
||||
.file = "".copy(allocator),
|
||||
.line = 0,
|
||||
.allocator = allocator
|
||||
};
|
||||
}
|
||||
|
||||
@@ -156,14 +162,11 @@ fn BacktraceList? symbolize_backtrace(Allocator allocator, void*[] backtrace)
|
||||
}
|
||||
list.free();
|
||||
}
|
||||
@pool()
|
||||
String execpath = executable_path()!;
|
||||
foreach (addr : backtrace)
|
||||
{
|
||||
String execpath = executable_path(tmem)!;
|
||||
foreach (addr : backtrace)
|
||||
{
|
||||
list.push(backtrace_load_element(allocator, execpath, addr, load_addr) ?? backtrace::BACKTRACE_UNKNOWN);
|
||||
}
|
||||
};
|
||||
list.push(backtrace_load_element(allocator, execpath, addr, load_addr) ?? backtrace::BACKTRACE_UNKNOWN);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +1,120 @@
|
||||
module std::os::openbsd @if(env::OPENBSD);
|
||||
import libc, std::os, std::collections::list;
|
||||
|
||||
|
||||
// Native `sysctl` identifiers from <sys/sysctl.h>
|
||||
const CTL_UNSPEC = 0; /* unused */
|
||||
const CTL_KERN = 1; /* "high kernel": proc, limits */
|
||||
const CTL_VM = 2; /* virtual memory */
|
||||
const CTL_FS = 3; /* file system, mount type is next */
|
||||
const CTL_NET = 4; /* network, see socket.h */
|
||||
const CTL_DEBUG = 5; /* debugging parameters */
|
||||
const CTL_HW = 6; /* generic cpu/io */
|
||||
const CTL_MACHDEP = 7; /* machine dependent */
|
||||
const CTL_GAP_UNUSED = 8; /* was CTL_USER: removed 2013-04 */
|
||||
const CTL_DDB = 9; /* DDB user interface, see db_var.h */
|
||||
const CTL_VFS = 10; /* VFS sysctl's */
|
||||
const CTL_MAXID = 11; /* number of valid top-level ids */
|
||||
|
||||
const HW_MACHINE = 1; /* string: machine class */
|
||||
const HW_MODEL = 2; /* string: specific machine model */
|
||||
const HW_NCPU = 3; /* int: number of configured cpus */
|
||||
const HW_BYTEORDER = 4; /* int: machine byte order */
|
||||
const HW_PHYSMEM = 5; /* int: total memory */
|
||||
const HW_USERMEM = 6; /* int: non-kernel memory */
|
||||
const HW_PAGESIZE = 7; /* int: software page size */
|
||||
const HW_DISKNAMES = 8; /* strings: disk drive names */
|
||||
const HW_DISKSTATS = 9; /* struct: diskstats[] */
|
||||
const HW_DISKCOUNT = 10; /* int: number of disks */
|
||||
const HW_SENSORS = 11; /* node: hardware monitors */
|
||||
const HW_CPUSPEED = 12; /* get CPU frequency */
|
||||
const HW_SETPERF = 13; /* set CPU performance % */
|
||||
const HW_VENDOR = 14; /* string: vendor name */
|
||||
const HW_PRODUCT = 15; /* string: product name */
|
||||
const HW_VERSION = 16; /* string: hardware version */
|
||||
const HW_SERIALNO = 17; /* string: hardware serial number */
|
||||
const HW_UUID = 18; /* string: universal unique id */
|
||||
const HW_PHYSMEM64 = 19; /* quad: total memory */
|
||||
const HW_USERMEM64 = 20; /* quad: non-kernel memory */
|
||||
const HW_NCPUFOUND = 21; /* int: number of cpus found */
|
||||
const HW_ALLOWPOWERDOWN = 22; /* allow power button shutdown */
|
||||
const HW_PERFPOLICY = 23; /* set performance policy */
|
||||
const HW_SMT = 24; /* int: enable SMT/HT/CMT */
|
||||
const HW_NCPUONLINE = 25; /* int: number of cpus being used */
|
||||
const HW_POWER = 26; /* int: machine has wall-power */
|
||||
const HW_BATTERY = 27; /* node: battery */
|
||||
const HW_UCOMNAMES = 28; /* strings: ucom names */
|
||||
const HW_MAXID = 29; /* number of valid hw ids */
|
||||
|
||||
|
||||
extern fn ZString* backtrace_symbols_fmt(void **addrlist, usz len, ZString fmt);
|
||||
|
||||
fn Backtrace? backtrace_line_parse(Allocator allocator, String obj, String addr2line) @local
|
||||
{
|
||||
@stack_mem(256; Allocator mem)
|
||||
{
|
||||
String[] parts = addr2line.trim().split(mem, " at ");
|
||||
if (parts.len != 2) return NOT_FOUND?;
|
||||
|
||||
uint line = 0;
|
||||
String source = "";
|
||||
if (!parts[1].contains("?") && parts[1].contains(":"))
|
||||
{
|
||||
usz index = parts[1].rindex_of_char(':')!;
|
||||
source = parts[1][:index];
|
||||
line = parts[1][index + 1..].to_uint()!;
|
||||
}
|
||||
return {
|
||||
.function = parts[0].copy(allocator),
|
||||
.object_file = obj.copy(allocator),
|
||||
.file = source.copy(allocator),
|
||||
.line = line,
|
||||
.allocator = allocator,
|
||||
.is_inline = false,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
fn void? backtrace_add_addr2line(Allocator allocator, BacktraceList* list, String obj, String addr2line) @local
|
||||
{
|
||||
list.push(backtrace_line_parse(allocator, obj, addr2line)!);
|
||||
}
|
||||
|
||||
fn void? backtrace_add_from_exec(Allocator allocator, BacktraceList* list, String fun, String obj) @local
|
||||
{
|
||||
char[1024] buf @noinit;
|
||||
String addr2line = process::execute_stdout_to_buffer(&buf, {"llvm-addr2line", "-fpe", obj, fun})!;
|
||||
return backtrace_add_addr2line(allocator, list, obj, addr2line);
|
||||
}
|
||||
|
||||
fn void? backtrace_add_element(Allocator allocator, BacktraceList *list, String fun, String obj) @local
|
||||
{
|
||||
return backtrace_add_from_exec(allocator, list, fun, obj);
|
||||
}
|
||||
|
||||
fn BacktraceList? symbolize_backtrace(Allocator allocator, void*[] backtrace)
|
||||
{
|
||||
BacktraceList list;
|
||||
list.init(allocator, backtrace.len);
|
||||
defer catch
|
||||
{
|
||||
foreach (trace : list)
|
||||
{
|
||||
trace.free();
|
||||
}
|
||||
list.free();
|
||||
}
|
||||
|
||||
ZString *strings = backtrace_symbols_fmt(backtrace.ptr, backtrace.len, "%n%D %f");
|
||||
|
||||
for (int i = 0; i < backtrace.len; i++) {
|
||||
String full = strings[i].str_view();
|
||||
Splitter iter = full.tokenize(" ");
|
||||
String fun = iter.next()!;
|
||||
String obj = iter.next()!;
|
||||
backtrace_add_element(allocator, &list, fun, obj)!;
|
||||
}
|
||||
|
||||
free(strings);
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
module std::os::posix @if(env::POSIX);
|
||||
|
||||
extern ZString* environ;
|
||||
|
||||
extern fn CLong sysconf(CInt name);
|
||||
|
||||
const CInt _SC_PAGESIZE @if(env::LINUX) = 30;
|
||||
const CInt _SC_PAGESIZE @if(env::DARWIN) = 29;
|
||||
|
||||
28
lib/std/os/posix/mman.c3
Normal file
28
lib/std/os/posix/mman.c3
Normal file
@@ -0,0 +1,28 @@
|
||||
module std::os::posix @if(env::POSIX);
|
||||
import libc;
|
||||
|
||||
|
||||
const PROT_NONE = 0x00; // no permissions
|
||||
const PROT_READ = 0x01; // pages can be read
|
||||
const PROT_WRITE = 0x02; // pages can be written
|
||||
const PROT_EXEC = 0x04; // pages can be executed
|
||||
|
||||
const MAP_SHARED = 0x0001; // share changes
|
||||
const MAP_PRIVATE = 0x0002; // changes are private
|
||||
|
||||
const MAP_FILE = 0x0000; // map from file (default)
|
||||
const MAP_ANONYMOUS = 0x1000; // allocated from memory, swap space
|
||||
|
||||
const void* MAP_FAILED = (void *)(uptr)-1; // mmap failed
|
||||
const MADV_NORMAL = 0; // no further special treatment
|
||||
const MADV_RANDOM = 1; // expect random page refs
|
||||
const MADV_SEQUENTIAL = 2; // expect sequential page refs
|
||||
const MADV_WILLNEED = 3; // will need these pages
|
||||
const MADV_DONTNEED = 4; // dont need these pages
|
||||
|
||||
extern fn void* mmap(void*, usz, CInt, CInt, CInt, Off_t);
|
||||
extern fn CInt munmap(void*, usz);
|
||||
extern fn CInt mprotect(void*, usz, CInt);
|
||||
extern fn int madvise(void*, usz, CInt);
|
||||
|
||||
extern fn CInt getpagesize();
|
||||
@@ -58,7 +58,9 @@ const CInt WUNTRACES = 2;
|
||||
JmpBuf backtrace_jmpbuf @local;
|
||||
alias BacktraceFn = fn CInt(void** buffer, CInt size);
|
||||
|
||||
fn CInt backtrace(void** buffer, CInt size)
|
||||
extern fn CInt backtrace(void** buffer, CInt size) @if(env::OPENBSD);
|
||||
|
||||
fn CInt backtrace(void** buffer, CInt size) @if(!env::OPENBSD)
|
||||
{
|
||||
if (size < 1) return 0;
|
||||
void* handle = libc::dlopen("libc.so.6", libc::RTLD_LAZY|libc::RTLD_NODELETE);
|
||||
|
||||
@@ -55,21 +55,18 @@ fn void? create_named_pipe_helper(void** rd, void **wr) @local @if(env::WIN32)
|
||||
|
||||
tlocal long index = 0;
|
||||
long unique = index++;
|
||||
@pool()
|
||||
{
|
||||
String s = string::tformat(`\\.\pipe\c3_subprocess.%08x.%08x.%d`, win32::getCurrentProcessId(), win32::getCurrentThreadId(), unique);
|
||||
Win32_LPCSTR str = (Win32_LPCSTR)s.ptr;
|
||||
*rd = win32::createNamedPipeA(
|
||||
str,
|
||||
win32::PIPE_ACCESS_INBOUND | win32::FILE_FLAG_OVERLAPPED,
|
||||
win32::PIPE_TYPE_BYTE | win32::PIPE_WAIT,
|
||||
1, 4096, 4096, 0, &sa_attr);
|
||||
if (win32::INVALID_HANDLE_VALUE == *rd) return FAILED_TO_CREATE_PIPE?;
|
||||
*wr = win32::createFileA(
|
||||
str, win32::GENERIC_WRITE, 0, &sa_attr,
|
||||
win32::OPEN_EXISTING, win32::FILE_ATTRIBUTE_NORMAL, null);
|
||||
if (win32::INVALID_HANDLE_VALUE == *wr) return FAILED_TO_CREATE_PIPE?;
|
||||
};
|
||||
String s = string::bformat(&&(char[128]){}, `\\.\pipe\c3_subprocess.%08x.%08x.%d`, win32::getCurrentProcessId(), win32::getCurrentThreadId(), unique);
|
||||
Win32_LPCSTR str = (Win32_LPCSTR)s.ptr;
|
||||
*rd = win32::createNamedPipeA(
|
||||
str,
|
||||
win32::PIPE_ACCESS_INBOUND | win32::FILE_FLAG_OVERLAPPED,
|
||||
win32::PIPE_TYPE_BYTE | win32::PIPE_WAIT,
|
||||
1, 4096, 4096, 0, &sa_attr);
|
||||
if (win32::INVALID_HANDLE_VALUE == *rd) return FAILED_TO_CREATE_PIPE?;
|
||||
*wr = win32::createFileA(
|
||||
str, win32::GENERIC_WRITE, 0, &sa_attr,
|
||||
win32::OPEN_EXISTING, win32::FILE_ATTRIBUTE_NORMAL, null);
|
||||
if (win32::INVALID_HANDLE_VALUE == *wr) return FAILED_TO_CREATE_PIPE?;
|
||||
}
|
||||
|
||||
fn WString convert_command_line_win32(String[] command_line) @inline @if(env::WIN32) @local
|
||||
@@ -143,12 +140,12 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
|
||||
if (!win32::setHandleInformation(wr, win32::HANDLE_FLAG_INHERIT, 0)) return FAILED_TO_CREATE_PIPE?;
|
||||
}
|
||||
|
||||
@pool()
|
||||
@stack_mem(2048; Allocator mem)
|
||||
{
|
||||
WString used_environment = null;
|
||||
if (!options.inherit_environment)
|
||||
{
|
||||
DString env = dstring::temp_with_capacity(64);
|
||||
DString env = dstring::new_with_capacity(mem, 64);
|
||||
if (!environment.len)
|
||||
{
|
||||
env.append("\0");
|
||||
@@ -159,7 +156,7 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
|
||||
env.append("\0");
|
||||
}
|
||||
env.append("\0");
|
||||
used_environment = env.str_view().to_temp_wstring()!;
|
||||
used_environment = env.str_view().to_wstring(mem)!;
|
||||
}
|
||||
|
||||
// Handle stdin pipe if not inheriting
|
||||
@@ -266,26 +263,26 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
|
||||
<*
|
||||
@require command_line.len > 0
|
||||
*>
|
||||
fn ZString* tcopy_command_line(String[] command_line) @local @inline @if(env::POSIX)
|
||||
fn ZString* copy_command_line(Allocator mem, String[] command_line) @local @inline @if(env::POSIX)
|
||||
{
|
||||
ZString* copy = mem::talloc_array(ZString, command_line.len + 1);
|
||||
ZString* copy = allocator::alloc_array(mem, ZString, command_line.len + 1);
|
||||
foreach (i, str : command_line)
|
||||
{
|
||||
copy[i] = str.zstr_tcopy();
|
||||
copy[i] = str.zstr_copy(mem);
|
||||
}
|
||||
copy[command_line.len] = null;
|
||||
return copy;
|
||||
}
|
||||
|
||||
const ZString[1] EMPTY_ENVIRONMENT @if(env::POSIX) = { null };
|
||||
fn ZString* tcopy_env(String[] environment) @local @inline @if(env::POSIX)
|
||||
fn ZString* copy_env(Allocator mem, String[] environment) @local @inline @if(env::POSIX)
|
||||
{
|
||||
if (!environment.len) return &EMPTY_ENVIRONMENT;
|
||||
ZString* copy = mem::talloc_array(ZString, environment.len + 1);
|
||||
ZString* copy = allocator::alloc_array(mem, ZString, environment.len + 1);
|
||||
copy[environment.len] = null;
|
||||
foreach (i, str : environment)
|
||||
{
|
||||
copy[i] = str.zstr_tcopy();
|
||||
copy[i] = str.zstr_copy(mem);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
@@ -295,6 +292,7 @@ fn String? execute_stdout_to_buffer(char[] buffer, String[] command_line, SubPro
|
||||
SubProcess process = process::create(command_line, options, environment)!;
|
||||
process.join()!;
|
||||
usz len = process.read_stdout(buffer.ptr, buffer.len)!;
|
||||
if (len == 0) return "";
|
||||
return (String)buffer[:len - 1];
|
||||
}
|
||||
|
||||
@@ -338,10 +336,10 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
|
||||
}
|
||||
|
||||
Pid_t child;
|
||||
@pool()
|
||||
@stack_mem(2048; Allocator mem)
|
||||
{
|
||||
ZString* command_line_copy = tcopy_command_line(command_line);
|
||||
ZString* used_environment = options.inherit_environment ? posix::environ : tcopy_env(environment);
|
||||
ZString* command_line_copy = copy_command_line(mem, command_line);
|
||||
ZString* used_environment = options.inherit_environment ? posix::environ : copy_env(mem, environment);
|
||||
if (options.search_user_path)
|
||||
{
|
||||
if (posix::spawnp(&child, command_line_copy[0], &actions, null, command_line_copy, used_environment)) return FAILED_TO_START_PROCESS?;
|
||||
@@ -532,4 +530,4 @@ fn bool? SubProcess.is_running(&self)
|
||||
self.join()!;
|
||||
return false;
|
||||
$endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,6 +221,8 @@ const Win32_DWORD ERROR_DEVICE_FEATURE_NOT_SUPPORTED = 0x13C;
|
||||
const Win32_DWORD ERROR_MR_MID_NOT_FOUND = 0x13D;
|
||||
const Win32_DWORD ERROR_SCOPE_NOT_FOUND = 0x13E;
|
||||
const Win32_DWORD ERROR_UNDEFINED_SCOPE = 0x13F;
|
||||
const Win32_DWORD ERROR_INVALID_ADDRESS = 0x1E7;
|
||||
const Win32_DWORD ERROR_IO_INCOMPLETE = 0x3E4;
|
||||
const Win32_DWORD ERROR_IO_PENDING = 0x3E5;
|
||||
const Win32_DWORD ERROR_TIMEOUT = 0x5B4;
|
||||
const Win32_DWORD ERROR_TIMEOUT = 0x5B4;
|
||||
const Win32_DWORD ERROR_COMMITMENT_LIMIT = 0x5AF;
|
||||
27
lib/std/os/win32/libloaderapi.c3
Normal file
27
lib/std/os/win32/libloaderapi.c3
Normal file
@@ -0,0 +1,27 @@
|
||||
module std::os::win32 @if(env::WIN32);
|
||||
|
||||
typedef Win32_DLL_DIRECTORY_COOKIE = void*;
|
||||
alias Win32_PDLL_DIRECTORY_COOKIE = Win32_DLL_DIRECTORY_COOKIE*;
|
||||
|
||||
extern fn Win32_HMODULE loadLibraryA(Win32_LPCSTR lpLibFileName) @extern("LoadLibraryA");
|
||||
extern fn Win32_HMODULE loadLibraryW(Win32_LPCWSTR lpLibFileName) @extern("LoadLibraryW");
|
||||
extern fn Win32_HMODULE loadLibraryExA(Win32_LPCSTR lpLibFileName, Win32_HANDLE hFile, Win32_DWORD dwFlags) @extern("LoadLibraryExA");
|
||||
extern fn Win32_HMODULE loadLibraryExW(Win32_LPCWSTR lpLibFileName, Win32_HANDLE hFile, Win32_DWORD dwFlags) @extern("LoadLibraryExW");
|
||||
|
||||
extern fn Win32_BOOL freeLibrary(Win32_HMODULE hLibModule) @extern("FreeLibrary");
|
||||
extern fn void freeLibraryAndExitThread(Win32_HMODULE hLibModule, Win32_DWORD dwExitCode) @extern("FreeLibraryAndExitThread");
|
||||
|
||||
extern fn Win32_DWORD getModuleFileNameA(Win32_HMODULE hModule, Win32_LPSTR lpFilename, Win32_DWORD nSize) @extern("GetModuleFileNameA");
|
||||
extern fn Win32_DWORD getModuleFileNameW(Win32_HMODULE hModule, Win32_LPWSTR lpFilename, Win32_DWORD nSize) @extern("GetModuleFileNameW");
|
||||
|
||||
extern fn Win32_HMODULE getModuleHandleA(Win32_LPCSTR lpModuleName) @extern("GetModuleHandleA");
|
||||
extern fn Win32_HMODULE getModuleHandleW(Win32_LPCWSTR lpModuleName) @extern("GetModuleHandleW");
|
||||
extern fn Win32_BOOL getModuleHandleExA(Win32_DWORD dwFlags, Win32_LPCSTR lpModuleName, Win32_HMODULE* phModule) @extern("GetModuleHandleExA");
|
||||
extern fn Win32_BOOL getModuleHandleExW(Win32_DWORD dwFlags, Win32_LPCWSTR lpModuleName, Win32_HMODULE* phModule) @extern("GetModuleHandleExW");
|
||||
|
||||
extern fn Win32_BOOL disableThreadLibraryCalls(Win32_HMODULE hLibModule) @extern("DisableThreadLibraryCalls");
|
||||
|
||||
extern fn Win32_FARPROC getProcAddress(Win32_HMODULE hModule, Win32_LPCSTR lpProcName) @extern("GetProcAddress");
|
||||
extern fn Win32_DLL_DIRECTORY_COOKIE addDllDirectory(Win32_PCWSTR newDirectory) @extern("AddDllDirectory");
|
||||
extern fn Win32_BOOL removeDllDirectory(Win32_DLL_DIRECTORY_COOKIE cookie) @extern("RemoveDllDirectory");
|
||||
extern fn Win32_BOOL setDefaultDllDirectories(Win32_DWORD directoryFlags) @extern("SetDefaultDllDirectories");
|
||||
52
lib/std/os/win32/memoryapi.c3
Normal file
52
lib/std/os/win32/memoryapi.c3
Normal file
@@ -0,0 +1,52 @@
|
||||
module std::os::win32 @if(env::WIN32);
|
||||
|
||||
enum Win32_AllocationType : const Win32_DWORD
|
||||
{
|
||||
MEM_COMMIT = 0x00001000,
|
||||
MEM_RESERVE = 0x00002000,
|
||||
MEM_RESET = 0x00080000,
|
||||
MEM_RESET_UNDO = 0x01000000,
|
||||
MEM_LARGE_PAGES = 0x20000000,
|
||||
MEM_PHYSICAL = 0x00400000,
|
||||
MEM_TOP_DOWN = 0x00100000,
|
||||
MEM_WRITE_WATCH = 0x00200000
|
||||
}
|
||||
|
||||
enum Win32_Protect : const Win32_DWORD
|
||||
{
|
||||
PAGE_EXECUTE = 0x10,
|
||||
PAGE_EXECUTE_READ = 0x20,
|
||||
PAGE_EXECUTE_READWRITE = 0x40,
|
||||
PAGE_EXECUTE_WRITECOPY = 0x80,
|
||||
PAGE_NOACCESS = 0x01,
|
||||
PAGE_READONLY = 0x02,
|
||||
PAGE_READWRITE = 0x04,
|
||||
PAGE_WRITECOPY = 0x08,
|
||||
PAGE_TARGETS_INVALID = 0x40000000,
|
||||
PAGE_TARGETS_NO_UPDATE = 0x40000000,
|
||||
PAGE_GUARD = 0x100,
|
||||
PAGE_NOCACHE = 0x200,
|
||||
PAGE_WRITECOMBINE = 0x400,
|
||||
}
|
||||
|
||||
enum Win32_FreeType : const Win32_DWORD
|
||||
{
|
||||
MEM_DECOMMIT = 0x00004000,
|
||||
MEM_RELEASE = 0x00008000,
|
||||
MEM_COALESCE_PLACEHOLDERS = 0x00000001,
|
||||
MEM_PRESERVE_PLACEHOLDER = 0x00000002,
|
||||
}
|
||||
extern fn Win32_LPVOID virtualAlloc(Win32_LPVOID lpAddres, Win32_SIZE_T dwSize, Win32_AllocationType flAllocationType, Win32_Protect flProtect) @extern("VirtualAlloc");
|
||||
extern fn Win32_PVOID virtualAlloc2(Win32_HANDLE process, Win32_PVOID baseAddress, Win32_SIZE_T size, Win32_AllocationType allocationType, Win32_ULONG pageProtection, Win32_MEM_EXTENDED_PARAMETER* extendedParameters, Win32_ULONG parameterCount) @extern("VirtualAlloc2");
|
||||
extern fn Win32_BOOL virtualFree(Win32_LPVOID lpAddress, Win32_SIZE_T dwSize, Win32_FreeType dwFreeType) @extern("VirtualFree");
|
||||
extern fn Win32_BOOL virtualProtect(Win32_LPVOID lpAddress, Win32_SIZE_T dwSize, Win32_Protect flNewProtect, Win32_Protect* lpflOldProtect) @extern("VirtualProtect");
|
||||
|
||||
fn usz allocation_granularity()
|
||||
{
|
||||
static usz granularity;
|
||||
if (granularity) return granularity;
|
||||
|
||||
Win32_SYSTEM_INFO info;
|
||||
win32::getSystemInfo(&info);
|
||||
return granularity = (usz)info.dwAllocationGranularity;
|
||||
}
|
||||
@@ -104,8 +104,6 @@ extern fn Win32_BOOL symGetLineFromInlineContext(Win32_HANDLE hProcess, Win32_DW
|
||||
extern fn Win32_ULONG rtlWalkFrameChain(Win32_PVOID*, Win32_ULONG, Win32_ULONG) @extern("RtlWalkFrameChain");
|
||||
extern fn Win32_BOOL symInitialize(Win32_HANDLE hProcess, Win32_PCSTR userSearchPath, Win32_BOOL fInvadeProcess) @extern("SymInitialize");
|
||||
extern fn Win32_BOOL symCleanup(Win32_HANDLE hProcess) @extern("SymCleanup");
|
||||
extern fn Win32_DWORD getModuleFileNameA(Win32_HMODULE hModule, Win32_LPSTR lpFilename, Win32_DWORD nSize) @extern("GetModuleFileNameA");
|
||||
extern fn Win32_DWORD getModuleFileNameExA(Win32_HANDLE hProcess, Win32_HMODULE hModule, Win32_LPSTR lpFilename, Win32_DWORD nSize) @extern("GetModuleFileNameExA");
|
||||
extern fn Win32_DWORD64 symLoadModuleEx(Win32_HANDLE hProcess, Win32_HANDLE hFile, Win32_PCSTR imageName, Win32_PCSTR moduleName, Win32_DWORD64 baseOfDll, Win32_DWORD dllSize, Win32_PMODLOAD_DATA data, Win32_DWORD flags) @extern("SymLoadModule");
|
||||
extern fn Win32_BOOL stackWalk64(Win32_DWORD machineType, Win32_HANDLE hProcess, Win32_HANDLE hThread, Win32_LPSTACKFRAME64 stackFrame, Win32_PVOID contextRecord, Win32_PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryRoutine, Win32_PFUNCTION_TABLE_ACCESS_ROUTINE64 functionTableAccessRoutine, Win32_PGET_MODULE_BASE_ROUTINE64 getModuleBaseRoutine, Win32_PTRANSLATE_ADDRESS_ROUTINE64 translateAddress) @extern("StackWalk64");
|
||||
extern fn void rtlCaptureContext(Win32_PCONTEXT contextRecord) @extern("RtlCaptureContext");
|
||||
@@ -120,8 +118,6 @@ extern fn Win32_BOOL symFromAddr(Win32_HANDLE hProcess, Win32_DWORD64 address, W
|
||||
extern fn Win32_BOOL symGetLineFromAddr64(Win32_HANDLE hProcess, Win32_DWORD64 dwAddr, Win32_PDWORD pdwDisplacement, Win32_PIMAGEHLP_LINE64 line) @extern("SymGetLineFromAddr64");
|
||||
extern fn Win32_WORD rtlCaptureStackBackTrace(Win32_DWORD framesToSkip, Win32_DWORD framesToCapture, Win32_PVOID *backTrace, Win32_PDWORD backTraceHash) @extern("RtlCaptureStackBackTrace");
|
||||
extern fn Win32_BOOL symGetModuleInfo64(Win32_HANDLE hProcess, Win32_DWORD64 qwAddr, Win32_PIMAGEHLP_MODULE64 moduleInfo) @extern("SymGetModuleInfo64");
|
||||
extern fn Win32_HANDLE getModuleHandleA(Win32_LPCSTR lpModuleName) @extern("GetModuleHandleA");
|
||||
extern fn Win32_HANDLE getModuleHandleW(Win32_LPCWSTR lpModuleName) @extern("GetModuleHandleW");
|
||||
|
||||
fn Win32_DWORD? load_modules()
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user