mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 20:11:17 +00:00
Compare commits
419 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d7697280c | ||
|
|
0a93581695 | ||
|
|
0509b40b21 | ||
|
|
6e11bdbd35 | ||
|
|
8a6e996442 | ||
|
|
be00fdb253 | ||
|
|
0dd1a93d0d | ||
|
|
7ca70b20be | ||
|
|
e0cfe56121 | ||
|
|
6ca77065d8 | ||
|
|
e96dce92cd | ||
|
|
cec9b21707 | ||
|
|
8c58b31bbd | ||
|
|
c785572467 | ||
|
|
a297470887 | ||
|
|
f0682422c0 | ||
|
|
1f856cacf5 | ||
|
|
c9ecb09cd7 | ||
|
|
4961d0433f | ||
|
|
ba48627ca0 | ||
|
|
45eb3acffe | ||
|
|
d3ad533dd6 | ||
|
|
9e54014848 | ||
|
|
f8e3ffd267 | ||
|
|
8b8a2beb0d | ||
|
|
79a4b6855b | ||
|
|
86680279fa | ||
|
|
b46d3947dd | ||
|
|
c4212c4649 | ||
|
|
63f619e5b6 | ||
|
|
ce06de4b18 | ||
|
|
e1d546225f | ||
|
|
c4f9efc8f5 | ||
|
|
69e30c19f8 | ||
|
|
940874e349 | ||
|
|
d3f2180330 | ||
|
|
68b5c1e1f1 | ||
|
|
c8e671d34b | ||
|
|
46c7e9aefa | ||
|
|
2126be2222 | ||
|
|
fa4fb44779 | ||
|
|
07e8779d4e | ||
|
|
77db50bce8 | ||
|
|
ea4c864d4b | ||
|
|
e6ec09f2c5 | ||
|
|
122179980c | ||
|
|
27e76fe59e | ||
|
|
d13f302ac8 | ||
|
|
3e1e3e3e29 | ||
|
|
0388910c17 | ||
|
|
4b984e12a5 | ||
|
|
4e717657bd | ||
|
|
78dcda0bb2 | ||
|
|
bc63c16c93 | ||
|
|
e3851f3723 | ||
|
|
3a502feb1d | ||
|
|
ef72e19bf0 | ||
|
|
8b794e8cea | ||
|
|
549e27a800 | ||
|
|
d05cc991f5 | ||
|
|
07be4b0e06 | ||
|
|
6fcda240b8 | ||
|
|
fff3cf33c7 | ||
|
|
a862437bac | ||
|
|
7a6df10b39 | ||
|
|
c54c400291 | ||
|
|
aaa5c0f743 | ||
|
|
9d2f4e72c2 | ||
|
|
70a849cbb5 | ||
|
|
ecb25a0010 | ||
|
|
300983f831 | ||
|
|
f2df4855ff | ||
|
|
50c590bb5f | ||
|
|
4a99ebef51 | ||
|
|
20d93ede0c | ||
|
|
f8b2f7f268 | ||
|
|
dc6d994480 | ||
|
|
2b3b7e32b8 | ||
|
|
03e2b30ede | ||
|
|
f3afec61bb | ||
|
|
bda33ca3f9 | ||
|
|
50c1aac9bb | ||
|
|
9092defd46 | ||
|
|
7dd9256e2d | ||
|
|
a056efce04 | ||
|
|
0bad8f92b0 | ||
|
|
b040736f7f | ||
|
|
778260213e | ||
|
|
50385be614 | ||
|
|
3c50376175 | ||
|
|
6848753a10 | ||
|
|
13771cc536 | ||
|
|
ac3b2f0fea | ||
|
|
4b61ac7dae | ||
|
|
70d0ad1fcc | ||
|
|
af2a0ffd3f | ||
|
|
02c3d5419b | ||
|
|
55fba09b3b | ||
|
|
d2a7dc4a9a | ||
|
|
d8ca0f69f6 | ||
|
|
7d0e143224 | ||
|
|
bd139f73ac | ||
|
|
f23dda8d50 | ||
|
|
a88364aaad | ||
|
|
9530fe8fcd | ||
|
|
26dc88e096 | ||
|
|
1f1c445a76 | ||
|
|
3e4f9e875f | ||
|
|
dab4844195 | ||
|
|
e40bab2d30 | ||
|
|
ca91ad4097 | ||
|
|
e2b11c17bc | ||
|
|
eda997545a | ||
|
|
92b3490210 | ||
|
|
ba545b44f0 | ||
|
|
4f130cfe56 | ||
|
|
145b76ec75 | ||
|
|
e30952b484 | ||
|
|
b145c073f0 | ||
|
|
948d56b321 | ||
|
|
69d0fa8c44 | ||
|
|
a845a932f5 | ||
|
|
3221180315 | ||
|
|
c326c525be | ||
|
|
16aadae9bd | ||
|
|
b7ffa3b17c | ||
|
|
772b20c26b | ||
|
|
c7eb0024c7 | ||
|
|
1a2dcd07ee | ||
|
|
ab32231cd1 | ||
|
|
a0192a0116 | ||
|
|
13e3ecbde2 | ||
|
|
fefe6d1342 | ||
|
|
bbef5656a5 | ||
|
|
ad3cd88350 | ||
|
|
5183370773 | ||
|
|
f74891d214 | ||
|
|
d2885faa79 | ||
|
|
c59d47f652 | ||
|
|
f863c4ae84 | ||
|
|
bb2a2526e4 | ||
|
|
f9b86226a8 | ||
|
|
a4f5c97150 | ||
|
|
5de03abe0d | ||
|
|
c3f5806aa3 | ||
|
|
5a36f0bc16 | ||
|
|
c5dbbf9ff7 | ||
|
|
304b604652 | ||
|
|
b787985bf7 | ||
|
|
d72ec09cee | ||
|
|
d4bd68c188 | ||
|
|
f51bfa5a44 | ||
|
|
3e4d1de70e | ||
|
|
721aaa28aa | ||
|
|
15503a9054 | ||
|
|
660654f9e0 | ||
|
|
2f7d18bfb8 | ||
|
|
29a6a0db32 | ||
|
|
7b2fe92241 | ||
|
|
70da1f748a | ||
|
|
3033295884 | ||
|
|
8c12f92aff | ||
|
|
76da7936e5 | ||
|
|
2a924ae3b0 | ||
|
|
5ba9acad5d | ||
|
|
4cb984e56d | ||
|
|
70606a2bbe | ||
|
|
259112e178 | ||
|
|
a2cde1e072 | ||
|
|
de04c52379 | ||
|
|
27970085e5 | ||
|
|
e0afc0f9ea | ||
|
|
0e44e63fa8 | ||
|
|
2623d7d525 | ||
|
|
4e78e32ced | ||
|
|
f65ca07b62 | ||
|
|
7a805340c5 | ||
|
|
a863d7fe9e | ||
|
|
f60bfa8442 | ||
|
|
50fdf9900d | ||
|
|
8785c2c46f | ||
|
|
c8fa7b0cb3 | ||
|
|
f2e69f8fdc | ||
|
|
8a9edc02b6 | ||
|
|
d173ba0377 | ||
|
|
fbb4ae056a | ||
|
|
ae10ae6847 | ||
|
|
64ab67bb58 | ||
|
|
dd650bc334 | ||
|
|
48923a2237 | ||
|
|
1f29110271 | ||
|
|
e133f4406a | ||
|
|
2fa258a066 | ||
|
|
87d29a62e5 | ||
|
|
190dc246b3 | ||
|
|
6ae84aac78 | ||
|
|
c8c58f946c | ||
|
|
0d1fb2843e | ||
|
|
d67fcb3956 | ||
|
|
f1325f6539 | ||
|
|
3a1bba19af | ||
|
|
0857363470 | ||
|
|
713199d7be | ||
|
|
b941f93416 | ||
|
|
c22b7d45c1 | ||
|
|
c78bb45f2f | ||
|
|
11f9365eb0 | ||
|
|
cdc1656f3a | ||
|
|
8fb3ec73ff | ||
|
|
214e806a33 | ||
|
|
8e0d6d11b9 | ||
|
|
9412b58d80 | ||
|
|
dad97fc2d9 | ||
|
|
ff33cc4dad | ||
|
|
51e0e5e66d | ||
|
|
5fa6ecf9ae | ||
|
|
34c7f4e6b7 | ||
|
|
737559d3f8 | ||
|
|
f801372074 | ||
|
|
ea2dce0ab4 | ||
|
|
314c6f94f0 | ||
|
|
8fd119e546 | ||
|
|
8612476103 | ||
|
|
35812bd7ba | ||
|
|
f1ef2e8138 | ||
|
|
c47cb512ab | ||
|
|
fe7d4230d8 | ||
|
|
b6e166f44d | ||
|
|
ab2d223e71 | ||
|
|
c6c7baa3b4 | ||
|
|
218f293cd4 | ||
|
|
4d641d193c | ||
|
|
67ff78f1ca | ||
|
|
4f0716ab13 | ||
|
|
07c59e6a6c | ||
|
|
86a674b87e | ||
|
|
9957ab259c | ||
|
|
4c3944f626 | ||
|
|
6f9b466d7c | ||
|
|
61badb6af7 | ||
|
|
e31e57c7e7 | ||
|
|
469188044d | ||
|
|
38063e5602 | ||
|
|
ba5e2b7fa6 | ||
|
|
eed806962d | ||
|
|
a1ce5e15ce | ||
|
|
f0735c945a | ||
|
|
d84e131b73 | ||
|
|
ad1511e69c | ||
|
|
db4dc114f2 | ||
|
|
a7f363ea43 | ||
|
|
0ccbba61ce | ||
|
|
d921a4e168 | ||
|
|
819a85ee06 | ||
|
|
a3d15fe16c | ||
|
|
56d25cdeeb | ||
|
|
d027a15b4a | ||
|
|
a16316d7b4 | ||
|
|
14d8e93004 | ||
|
|
37c62bf9b7 | ||
|
|
72839d7654 | ||
|
|
1994cba73e | ||
|
|
55cdcbb39b | ||
|
|
c7ce6230db | ||
|
|
7c45ae24ae | ||
|
|
99c350fc43 | ||
|
|
faf1c5cb64 | ||
|
|
ece6efc75e | ||
|
|
0a809ab5f0 | ||
|
|
78ff1a4af5 | ||
|
|
c0dcae4f1d | ||
|
|
5e32c8a828 | ||
|
|
4d15a2f45e | ||
|
|
a913f21c45 | ||
|
|
322c70433b | ||
|
|
ad9cfcdcc7 | ||
|
|
4232c9d2b0 | ||
|
|
9edd59d280 | ||
|
|
df74cbf06f | ||
|
|
5af224ab16 | ||
|
|
7b734df09e | ||
|
|
1340a47bc2 | ||
|
|
4f4476ba75 | ||
|
|
5c7a183f8a | ||
|
|
53bada2a1e | ||
|
|
43efb7df2f | ||
|
|
f5cea221a6 | ||
|
|
b7082f34a1 | ||
|
|
d20d957881 | ||
|
|
008274cda5 | ||
|
|
e07ab7547f | ||
|
|
cf10837eb8 | ||
|
|
291b26f230 | ||
|
|
08e8c9bf57 | ||
|
|
625152440c | ||
|
|
fbd51821d1 | ||
|
|
c3ebf51295 | ||
|
|
9a9ff7f32c | ||
|
|
7b73eec82b | ||
|
|
75ba4a1cdb | ||
|
|
e5ca9065bd | ||
|
|
1042d0825f | ||
|
|
17942925f5 | ||
|
|
7424317d03 | ||
|
|
dbf1d91961 | ||
|
|
eb1644b302 | ||
|
|
cde5bc3263 | ||
|
|
e995e289db | ||
|
|
5020caa9c3 | ||
|
|
f34eb7d9f3 | ||
|
|
bf74ef0e5e | ||
|
|
0ff52311c3 | ||
|
|
e453e6f9ca | ||
|
|
6078598aff | ||
|
|
9fdb3b3b4a | ||
|
|
1362aa655f | ||
|
|
9c22ab8925 | ||
|
|
ca2dbb2f4b | ||
|
|
16bbc5a026 | ||
|
|
627f10cd18 | ||
|
|
13509b9231 | ||
|
|
ca88afbf5b | ||
|
|
42c9c9894b | ||
|
|
b4de62cfc2 | ||
|
|
226fbc191b | ||
|
|
4839d8861d | ||
|
|
789b47d565 | ||
|
|
c13cdcdd36 | ||
|
|
4ae3d0150f | ||
|
|
7d153a162a | ||
|
|
7bc3e94ff3 | ||
|
|
9c1fb26660 | ||
|
|
3dd725a0f0 | ||
|
|
a8aad53038 | ||
|
|
68c60f58c0 | ||
|
|
c9c3f33acc | ||
|
|
5ffc5187eb | ||
|
|
5d31cdfa16 | ||
|
|
e8ff4af5b9 | ||
|
|
723e1dd9a6 | ||
|
|
369a4558a3 | ||
|
|
5e6a3d9d8e | ||
|
|
62dca4f1c5 | ||
|
|
061c02306f | ||
|
|
f006b05010 | ||
|
|
c5a727aa9b | ||
|
|
e67e9d3bbf | ||
|
|
ea86c9d37a | ||
|
|
d1a2e6e5bd | ||
|
|
c96985f1db | ||
|
|
b7010c83e0 | ||
|
|
20a3d19ac7 | ||
|
|
0ded93ab9b | ||
|
|
ec82ec0426 | ||
|
|
6281f8ff89 | ||
|
|
2c9d2d4fd7 | ||
|
|
8569239bc1 | ||
|
|
5463c398cb | ||
|
|
7381734913 | ||
|
|
462322026f | ||
|
|
b5e5c719ed | ||
|
|
a0f4976b07 | ||
|
|
44c2486a74 | ||
|
|
5fc6672784 | ||
|
|
bcb1edba90 | ||
|
|
8099e7a75d | ||
|
|
cc9a501351 | ||
|
|
b536a23124 | ||
|
|
6ca5bcc6b8 | ||
|
|
ac966f118a | ||
|
|
f13472a8c3 | ||
|
|
0e213ae777 | ||
|
|
a0c82a6a47 | ||
|
|
a087ba608b | ||
|
|
9112d63655 | ||
|
|
3f7f7a0aa7 | ||
|
|
8d03aafe72 | ||
|
|
b0c0fd7dc8 | ||
|
|
c273f26cb3 | ||
|
|
60101830cc | ||
|
|
a58d782704 | ||
|
|
9b94c1dda9 | ||
|
|
201a6b350e | ||
|
|
b2724caeda | ||
|
|
9d99d556a1 | ||
|
|
a1a6511e26 | ||
|
|
652456646f | ||
|
|
ca0dc49f64 | ||
|
|
ae1b39eb60 | ||
|
|
22f7faf60e | ||
|
|
f3bf9eb14d | ||
|
|
347a1a48d4 | ||
|
|
c9793457f3 | ||
|
|
50d31ba398 | ||
|
|
2788c4cc00 | ||
|
|
ba54232b8d | ||
|
|
489bb70901 | ||
|
|
dd06dfa5ba | ||
|
|
f39e339726 | ||
|
|
295b374b48 | ||
|
|
8ed390c394 | ||
|
|
f9e9cac6e8 | ||
|
|
f3304acc93 | ||
|
|
a233771433 | ||
|
|
ea9a871d90 | ||
|
|
84d010bb2f | ||
|
|
e0ba468b7e | ||
|
|
f88c0dd645 | ||
|
|
758918c077 | ||
|
|
7b516e6113 | ||
|
|
61a76bb834 | ||
|
|
e6b6edefaf | ||
|
|
a228eb020d | ||
|
|
c46933a81a | ||
|
|
746046c8c0 | ||
|
|
acab95792f | ||
|
|
b882265e52 | ||
|
|
547f2ef189 | ||
|
|
69004943a7 |
119
.github/workflows/main.yml
vendored
119
.github/workflows/main.yml
vendored
@@ -11,7 +11,7 @@ env:
|
||||
LLVM_RELEASE_VERSION_MAC: 17
|
||||
LLVM_RELEASE_VERSION_LINUX: 17
|
||||
LLVM_RELEASE_VERSION_UBUNTU20: 17
|
||||
LLVM_DEV_VERSION: 20
|
||||
LLVM_DEV_VERSION: 21
|
||||
jobs:
|
||||
|
||||
build-msvc:
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
cd resources/testproject
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe --debug-log --emit-llvm run hello_world_win32
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe -vvv --emit-llvm run hello_world_win32 --trust=full
|
||||
dir build\llvm_ir
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe clean
|
||||
dir build\llvm_ir
|
||||
@@ -61,13 +61,19 @@ jobs:
|
||||
run: |
|
||||
cd resources/testproject
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe --debug-log build hello_world_win32_lib
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe -vvv build hello_world_win32_lib --trust=full
|
||||
|
||||
- name: Compile and run dynlib-test
|
||||
run: |
|
||||
cd resources/examples/dynlib-test
|
||||
..\..\..\build\${{ matrix.build_type }}\c3c.exe dynamic-lib add.c3
|
||||
..\..\..\build\${{ matrix.build_type }}\c3c.exe compile-run test.c3 -l ./add.lib
|
||||
..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv dynamic-lib add.c3
|
||||
..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv compile-run test.c3 -l ./add.lib
|
||||
|
||||
- name: Compile and run staticlib-test
|
||||
run: |
|
||||
cd resources/examples/staticlib-test
|
||||
..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv static-lib add.c3
|
||||
..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv compile-run test.c3 -l ./add.lib
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
@@ -81,15 +87,16 @@ jobs:
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib5 --print-linking examples\raylib\raylib_snake.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib5 --print-linking examples\raylib\raylib_tetris.c3
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-test unit -O1
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
python3.exe src/tester.py ..\build\${{ matrix.build_type }}\c3c.exe test_suite/
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-test unit -O1
|
||||
|
||||
- name: Test python script
|
||||
run: |
|
||||
@@ -126,8 +133,8 @@ jobs:
|
||||
install: git binutils mingw-w64-x86_64-clang mingw-w64-x86_64-ninja mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-python
|
||||
- shell: msys2 {0}
|
||||
run: |
|
||||
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-llvm-18.1.8-1-any.pkg.tar.zst
|
||||
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-18.1.8-1-any.pkg.tar.zst
|
||||
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-llvm-19.1.7-1-any.pkg.tar.zst
|
||||
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-19.1.7-1-any.pkg.tar.zst
|
||||
- name: CMake
|
||||
run: |
|
||||
cmake -B build -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
@@ -147,7 +154,7 @@ jobs:
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log
|
||||
../../build/c3c run -vvv --trust=full
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
@@ -156,7 +163,7 @@ jobs:
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build hello_world_lib --cc cc --debug-log
|
||||
../../build/c3c build hello_world_lib --cc cc -vvv --trust=full
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
@@ -203,12 +210,12 @@ jobs:
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log
|
||||
../../build/c3c run -vvv --trust=full
|
||||
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build hello_world_lib --debug-log
|
||||
../../build/c3c build hello_world_lib -vvv --trust=full
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
@@ -216,13 +223,13 @@ jobs:
|
||||
python3 src/tester.py ../build/c3c.exe test_suite/
|
||||
|
||||
build-linux:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
# Don't abort runners if a single one fails
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Release, Debug]
|
||||
llvm_version: [17, 18, 19, 20]
|
||||
llvm_version: [17, 18, 19, 20, 21]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -320,12 +327,21 @@ jobs:
|
||||
- name: Compile and run dynlib-test
|
||||
run: |
|
||||
cd resources/examples/dynlib-test
|
||||
../../../build/c3c dynamic-lib add.c3
|
||||
../../../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=.
|
||||
|
||||
- name: Compile and run staticlib-test
|
||||
run: |
|
||||
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
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
@@ -334,7 +350,7 @@ jobs:
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log
|
||||
../../build/c3c run -vvv --trust=full
|
||||
|
||||
- name: Test WASM
|
||||
run: |
|
||||
@@ -353,7 +369,7 @@ jobs:
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log --linker=builtin
|
||||
../../build/c3c run -vvv --linker=builtin --trust=full
|
||||
|
||||
- name: Init a library & a project
|
||||
run: |
|
||||
@@ -390,7 +406,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Release, Debug]
|
||||
llvm_version: [17, 18, 19, 20]
|
||||
llvm_version: [17, 18, 19, 20, 21]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install common deps
|
||||
@@ -471,17 +487,17 @@ jobs:
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
../build/c3c compile-test unit
|
||||
../build/c3c compile-test unit --sanitize=address
|
||||
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log
|
||||
../../build/c3c run -vvv --trust=full
|
||||
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log --linker=builtin
|
||||
../../build/c3c run -vvv --linker=builtin --trust=full
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
@@ -505,13 +521,13 @@ jobs:
|
||||
path: c3-ubuntu-20-${{matrix.build_type}}.tar.gz
|
||||
|
||||
build-with-docker:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
ubuntu_version: [20.04, 22.04]
|
||||
build_type: [Release, Debug]
|
||||
llvm_version: [17, 18, 19, 20]
|
||||
llvm_version: [17, 18, 19]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -568,7 +584,7 @@ jobs:
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log
|
||||
../../build/c3c run -vvv --trust=full
|
||||
|
||||
- name: Test WASM
|
||||
run: |
|
||||
@@ -578,7 +594,7 @@ jobs:
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log --linker=builtin
|
||||
../../build/c3c run -vvv --linker=builtin --trust=full
|
||||
|
||||
- name: Init a library & a project
|
||||
run: |
|
||||
@@ -639,28 +655,33 @@ jobs:
|
||||
- name: Compile and run dynlib-test
|
||||
run: |
|
||||
cd resources/examples/dynlib-test
|
||||
../../../build/c3c dynamic-lib add.c3
|
||||
../../../build/c3c -vv dynamic-lib add.c3
|
||||
../../../build/c3c compile-run test.c3 -l ./add.dylib
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
../build/c3c compile-test unit
|
||||
../build/c3c compile-test unit -O1
|
||||
|
||||
- name: Test WASM
|
||||
run: |
|
||||
cd resources/testfragments
|
||||
../../build/c3c compile --target wasm32 -g0 --no-entry -Os wasm4.c3
|
||||
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log
|
||||
../../build/c3c run -vvv --trust=full
|
||||
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log --linker=builtin
|
||||
../../build/c3c run -vvv --linker=builtin --trust=full
|
||||
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build hello_world_lib --debug-log
|
||||
../../build/c3c build hello_world_lib -vvv --trust=full
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
@@ -683,9 +704,39 @@ jobs:
|
||||
name: c3-macos-${{matrix.build_type}}
|
||||
path: c3-macos-${{matrix.build_type}}.zip
|
||||
|
||||
build-nix:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
# Don't abort runners if a single one fails
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [ Release, Debug ]
|
||||
nixpkgs: [ Lock, Latest ]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
github_access_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Update flake (if necessary)
|
||||
run: |
|
||||
if [[ matrix.nixpkgs == "Latest" ]]; then
|
||||
nix flake update
|
||||
fi
|
||||
nix flake info
|
||||
|
||||
- name: Build and check
|
||||
run: |
|
||||
if [[ ${{ matrix.build_type }} = "Debug" ]]; then
|
||||
nix build -L ".#c3c-debug-checks"
|
||||
else
|
||||
nix build -L ".#c3c-checks"
|
||||
fi
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [build-msvc, build-linux, build-mac, build-linux-ubuntu20]
|
||||
if: github.ref == 'refs/heads/master'
|
||||
|
||||
|
||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -19,6 +19,7 @@
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.tlb
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
@@ -75,3 +76,12 @@ TAGS
|
||||
/.cache/
|
||||
/compile_commands.json
|
||||
|
||||
# 'nix build' resulting symlink
|
||||
result
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# tests
|
||||
/test/tmp/*
|
||||
/test/testrun
|
||||
|
||||
187
CMakeLists.txt
187
CMakeLists.txt
@@ -38,10 +38,10 @@ set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
if(MSVC)
|
||||
message(STATUS "MSVC version ${MSVC_VERSION}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2 /EHsc")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /O2 /EHsc")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi /EHa")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Od /Zi /EHa")
|
||||
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")
|
||||
else()
|
||||
if (true)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -fno-exceptions")
|
||||
@@ -65,6 +65,7 @@ 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_USE_MIMALLOC OFF)
|
||||
if(C3_USE_MIMALLOC)
|
||||
@@ -73,9 +74,9 @@ if(C3_USE_MIMALLOC)
|
||||
option(MI_PADDING OFF)
|
||||
option(MI_DEBUG_FULL OFF)
|
||||
FetchContent_Declare(
|
||||
mimalloc
|
||||
GIT_REPOSITORY https://github.com/microsoft/mimalloc.git
|
||||
GIT_TAG ${C3_MIMALLOC_TAG}
|
||||
mimalloc
|
||||
GIT_REPOSITORY https://github.com/microsoft/mimalloc.git
|
||||
GIT_TAG ${C3_MIMALLOC_TAG}
|
||||
)
|
||||
FetchContent_MakeAvailable(mimalloc)
|
||||
endif()
|
||||
@@ -84,7 +85,7 @@ if (NOT WIN32)
|
||||
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 20)
|
||||
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()
|
||||
@@ -108,9 +109,9 @@ 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)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink
|
||||
${CMAKE_BINARY_DIR}/compile_commands.json
|
||||
${CMAKE_SOURCE_DIR}/compile_commands.json
|
||||
)
|
||||
@@ -119,15 +120,15 @@ 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 "18")
|
||||
set(C3_LLVM_VERSION "19")
|
||||
endif()
|
||||
FetchContent_Declare(
|
||||
LLVM_Windows
|
||||
URL https://github.com/c3lang/win-llvm/releases/download/llvm_18_1_8_with_rt/llvm-18.1.8-windows-amd64-msvc17-libcmt.7z
|
||||
URL https://github.com/c3lang/win-llvm/releases/download/llvm_19_1_5/llvm-19.1.5-windows-amd64-msvc17-libcmt.7z
|
||||
)
|
||||
FetchContent_Declare(
|
||||
LLVM_Windows_debug
|
||||
URL https://github.com/c3lang/win-llvm/releases/download/llvm_18_1_8_with_rt/llvm-18.1.8-windows-amd64-msvc17-libcmt-dbg.7z
|
||||
URL https://github.com/c3lang/win-llvm/releases/download/llvm_19_1_5/llvm-19.1.5-windows-amd64-msvc17-libcmt-dbg.7z
|
||||
)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
message("Loading Windows LLVM debug libraries, this may take a while...")
|
||||
@@ -150,6 +151,13 @@ if(C3_WITH_LLVM)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (EXISTS /usr/lib)
|
||||
# Some systems (such as Alpine Linux) seem to put some of the relevant
|
||||
# LLVM files in /usr/lib, but this doesn't seem to be included in the
|
||||
# value of LLVM_LIBRARY_DIRS.
|
||||
list(APPEND LLVM_LIBRARY_DIRS /usr/lib)
|
||||
endif()
|
||||
|
||||
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
|
||||
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
|
||||
message(STATUS "Libraries located in: ${LLVM_LIBRARY_DIRS}")
|
||||
@@ -173,54 +181,54 @@ if(C3_WITH_LLVM)
|
||||
|
||||
if(NOT C3_LINK_DYNAMIC)
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
AllTargetsAsmParsers
|
||||
AllTargetsCodeGens
|
||||
AllTargetsDescs
|
||||
AllTargetsDisassemblers
|
||||
AllTargetsInfos
|
||||
Analysis
|
||||
AsmPrinter
|
||||
BitReader
|
||||
Core
|
||||
DebugInfoPDB
|
||||
InstCombine
|
||||
IrReader
|
||||
LibDriver
|
||||
Linker
|
||||
LTO
|
||||
MC
|
||||
MCDisassembler
|
||||
native
|
||||
nativecodegen
|
||||
Object
|
||||
Option
|
||||
ScalarOpts
|
||||
Support
|
||||
Target
|
||||
TransformUtils
|
||||
WindowsManifest
|
||||
WindowsDriver
|
||||
AllTargetsAsmParsers
|
||||
AllTargetsCodeGens
|
||||
AllTargetsDescs
|
||||
AllTargetsDisassemblers
|
||||
AllTargetsInfos
|
||||
Analysis
|
||||
AsmPrinter
|
||||
BitReader
|
||||
Core
|
||||
DebugInfoPDB
|
||||
InstCombine
|
||||
IrReader
|
||||
LibDriver
|
||||
Linker
|
||||
LTO
|
||||
MC
|
||||
MCDisassembler
|
||||
native
|
||||
nativecodegen
|
||||
Object
|
||||
Option
|
||||
ScalarOpts
|
||||
Support
|
||||
Target
|
||||
TransformUtils
|
||||
WindowsManifest
|
||||
WindowsDriver
|
||||
)
|
||||
|
||||
llvm_map_components_to_libnames(llvm_libs ${LLVM_LINK_COMPONENTS})
|
||||
|
||||
if(NOT ${C3_LLD_DIR} EQUAL "" AND EXISTS ${C3_LLD_DIR})
|
||||
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}"
|
||||
"${LLVM_LIBRARY_DIRS}"
|
||||
"${C3_LLD_DIR}"
|
||||
)
|
||||
list(REMOVE_DUPLICATES LLVM_LIBRARY_DIRS)
|
||||
endif()
|
||||
|
||||
# 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.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_COMMON NAMES liblldCommon.dylib lldCommon.lib lldCommon.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_ELF NAMES liblldELF.dylib lldELF.lib lldELF.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_MACHO NAMES liblldMachO.dylib lldMachO.lib lldMachO.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_MINGW NAMES liblldMinGW.dylib lldMinGW.lib lldMinGW.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_WASM NAMES liblldWasm.dylib lldWasm.lib lldWasm.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)
|
||||
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)
|
||||
else()
|
||||
find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
set(llvm_libs ${LLVM})
|
||||
@@ -244,27 +252,27 @@ 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)
|
||||
set(lld_libs
|
||||
${LLD_COFF}
|
||||
${LLD_COMMON}
|
||||
${LLD_WASM}
|
||||
${LLD_MINGW}
|
||||
${LLD_ELF}
|
||||
${LLD_MACHO}
|
||||
)
|
||||
${LLD_COFF}
|
||||
${LLD_WASM}
|
||||
${LLD_MINGW}
|
||||
${LLD_ELF}
|
||||
${LLD_MACHO}
|
||||
${LLD_COMMON}
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
set(lld_libs ${lld_libs} xar)
|
||||
find_file(RT_ASAN_DYNAMIC NAMES libclang_rt.asan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin")
|
||||
find_file(RT_TSAN_DYNAMIC NAMES libclang_rt.tsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin")
|
||||
find_file(RT_UBSAN_DYNAMIC NAMES libclang_rt.ubsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin")
|
||||
find_file(RT_LSAN_DYNAMIC NAMES libclang_rt.lsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin")
|
||||
find_file(RT_ASAN_DYNAMIC NAMES libclang_rt.asan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin" ${LLVM_CRT_LIBRARY_DIR})
|
||||
find_file(RT_TSAN_DYNAMIC NAMES libclang_rt.tsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin" ${LLVM_CRT_LIBRARY_DIR})
|
||||
find_file(RT_UBSAN_DYNAMIC NAMES libclang_rt.ubsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin" ${LLVM_CRT_LIBRARY_DIR})
|
||||
find_file(RT_LSAN_DYNAMIC NAMES libclang_rt.lsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin" ${LLVM_CRT_LIBRARY_DIR})
|
||||
set(sanitizer_runtime_libraries
|
||||
${RT_ASAN_DYNAMIC}
|
||||
${RT_TSAN_DYNAMIC}
|
||||
# Unused
|
||||
# ${RT_UBSAN_DYNAMIC}
|
||||
# ${RT_LSAN_DYNAMIC}
|
||||
)
|
||||
)
|
||||
endif()
|
||||
|
||||
message(STATUS "linking to llvm libs ${lld_libs}")
|
||||
@@ -342,11 +350,12 @@ add_executable(c3c
|
||||
src/utils/whereami.c
|
||||
src/utils/cpus.c
|
||||
src/utils/unzipper.c
|
||||
src/compiler/c_codegen.c
|
||||
src/compiler/decltable.c
|
||||
src/compiler/mac_support.c
|
||||
src/compiler/mac_support.c
|
||||
src/compiler/windows_support.c
|
||||
src/compiler/codegen_asm.c
|
||||
src/compiler/asm_target.c
|
||||
src/compiler/asm_target.c
|
||||
src/compiler/expr.c
|
||||
src/utils/time.c
|
||||
src/utils/http.c
|
||||
@@ -354,7 +363,7 @@ add_executable(c3c
|
||||
src/build/common_build.c
|
||||
src/compiler/sema_const.c
|
||||
${CMAKE_BINARY_DIR}/git_hash.h
|
||||
)
|
||||
)
|
||||
|
||||
if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
# We are inside of a git repository so rebuilding the hash every time something changes.
|
||||
@@ -370,18 +379,18 @@ else()
|
||||
endif()
|
||||
|
||||
if(C3_WITH_LLVM)
|
||||
target_sources(c3c PRIVATE
|
||||
src/compiler/llvm_codegen.c
|
||||
src/compiler/llvm_codegen_debug_info.c
|
||||
src/compiler/llvm_codegen_expr.c
|
||||
src/compiler/llvm_codegen_function.c
|
||||
src/compiler/llvm_codegen_instr.c
|
||||
src/compiler/llvm_codegen_module.c
|
||||
src/compiler/llvm_codegen_stmt.c
|
||||
src/compiler/llvm_codegen_type.c
|
||||
src/compiler/llvm_codegen_value.c
|
||||
src/compiler/llvm_codegen_storeload.c
|
||||
src/compiler/llvm_codegen_builtins.c)
|
||||
target_sources(c3c PRIVATE
|
||||
src/compiler/llvm_codegen.c
|
||||
src/compiler/llvm_codegen_debug_info.c
|
||||
src/compiler/llvm_codegen_expr.c
|
||||
src/compiler/llvm_codegen_function.c
|
||||
src/compiler/llvm_codegen_instr.c
|
||||
src/compiler/llvm_codegen_module.c
|
||||
src/compiler/llvm_codegen_stmt.c
|
||||
src/compiler/llvm_codegen_type.c
|
||||
src/compiler/llvm_codegen_value.c
|
||||
src/compiler/llvm_codegen_storeload.c
|
||||
src/compiler/llvm_codegen_builtins.c)
|
||||
|
||||
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=1)
|
||||
add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp)
|
||||
@@ -409,7 +418,7 @@ if (C3_USE_TB)
|
||||
tilde-backend/src/tb/x64/*.c
|
||||
tilde-backend/src/tb/wasm/*.c
|
||||
tilde-backend/src/tb/aarch64/*.c
|
||||
)
|
||||
)
|
||||
target_sources(c3c PRIVATE
|
||||
src/compiler/tilde_codegen.c
|
||||
src/compiler/tilde_codegen_instr.c
|
||||
@@ -447,7 +456,7 @@ if(C3_WITH_LLVM)
|
||||
|
||||
target_link_libraries(c3c_wrappers ${llvm_libs} ${lld_libs})
|
||||
|
||||
else()
|
||||
else()
|
||||
|
||||
target_link_libraries(c3c ${llvm_libs} miniz ${lld_libs})
|
||||
|
||||
@@ -507,10 +516,10 @@ if(MSVC)
|
||||
if(C3_WITH_LLVM)
|
||||
set(clang_lib_dir ${llvm_dir}/lib/clang/${C3_LLVM_VERSION}/lib/windows)
|
||||
set(sanitizer_runtime_libraries
|
||||
${clang_lib_dir}/clang_rt.asan-x86_64.lib
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.lib
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.dll
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib)
|
||||
${clang_lib_dir}/clang_rt.asan-x86_64.lib
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.lib
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.dll
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "using gcc/clang warning switches")
|
||||
@@ -532,17 +541,17 @@ 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
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory $<TARGET_FILE_DIR:c3c>/c3c_rt
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy ${sanitizer_runtime_libraries} $<TARGET_FILE_DIR:c3c>/c3c_rt
|
||||
VERBATIM
|
||||
COMMENT "Copying sanitizer runtime libraries to output directory")
|
||||
COMMAND "${CMAKE_COMMAND}" -E rm -rf -- $<TARGET_FILE_DIR:c3c>/c3c_rt
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory $<TARGET_FILE_DIR:c3c>/c3c_rt
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy ${sanitizer_runtime_libraries} $<TARGET_FILE_DIR:c3c>/c3c_rt
|
||||
VERBATIM
|
||||
COMMENT "Copying sanitizer runtime libraries to output directory")
|
||||
|
||||
if (APPLE)
|
||||
# Change LC_ID_DYLIB to be rpath-based instead of having an absolute path
|
||||
add_custom_command(TARGET c3c POST_BUILD
|
||||
COMMAND find $<TARGET_FILE_DIR:c3c>/c3c_rt -type f -name "*.dylib" -execdir ${LLVM_TOOLS_BINARY_DIR}/llvm-install-name-tool -id @rpath/{} {} $<SEMICOLON>
|
||||
VERBATIM)
|
||||
COMMAND find $<TARGET_FILE_DIR:c3c>/c3c_rt -type f -name "*.dylib" -execdir ${LLVM_TOOLS_BINARY_DIR}/llvm-install-name-tool -id @rpath/{} {} $<SEMICOLON>
|
||||
VERBATIM)
|
||||
endif()
|
||||
|
||||
install(DIRECTORY $<TARGET_FILE_DIR:c3c>/c3c_rt/ DESTINATION bin/c3c_rt)
|
||||
|
||||
28
README.md
28
README.md
@@ -55,7 +55,7 @@ fn void Stack.push(Stack* this, Type element)
|
||||
if (this.capacity == this.size)
|
||||
{
|
||||
this.capacity *= 2;
|
||||
if (this.capacity < 16) this.capacity = 16;
|
||||
if (this.capacity < 16) this.capacity = 16;
|
||||
this.elems = realloc(this.elems, Type.sizeof * this.capacity);
|
||||
}
|
||||
this.elems[this.size++] = element;
|
||||
@@ -138,7 +138,7 @@ fn void main()
|
||||
|
||||
### Current status
|
||||
|
||||
The current stable version of the compiler is **version 0.6.3**.
|
||||
The current stable version of the compiler is **version 0.6.6**.
|
||||
|
||||
The upcoming 0.6.x releases will focus on expanding the standard library.
|
||||
Follow the issues [here](https://github.com/c3lang/c3c/issues).
|
||||
@@ -275,6 +275,14 @@ A `c3c` executable will be found under `bin/`.
|
||||
8. Set up CMake build for debug: `cmake ..`
|
||||
9. Build: `cmake --build .`
|
||||
|
||||
#### Installing on Windows using Scoop
|
||||
|
||||
c3c is included in 'Main' bucket.
|
||||
|
||||
```sh
|
||||
scoop install c3
|
||||
```
|
||||
|
||||
#### Getting started with a "hello world"
|
||||
|
||||
Create a `main.c3` file with:
|
||||
@@ -303,7 +311,7 @@ called `hello_world` or `hello_world.exe`depending on platform.
|
||||
|
||||
#### Compiling on Windows
|
||||
|
||||
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++" (there is also `c3c/resources/install_win_reqs.bat` to automate this)
|
||||
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`.
|
||||
@@ -349,6 +357,20 @@ Your c3c executable should have compiled properly. You may want to test it: `./c
|
||||
For a sytem-wide installation, run the following as root: `cmake --install .`
|
||||
|
||||
|
||||
#### Compiling on Fedora
|
||||
|
||||
1. Install required project dependencies: `dnf install cmake clang git llvm llvm-devel lld lld-devel ncurses-devel`
|
||||
2. Optionally, install additional dependencies: `dnf install libcurl-devel zlib-devel libzstd-devel libxml2-devel libffi-devel`
|
||||
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 .`
|
||||
|
||||
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 other Linux / Unix variants
|
||||
|
||||
1. Install CMake.
|
||||
|
||||
69
benchmarks/stdlib/sort/quicksort.c3
Normal file
69
benchmarks/stdlib/sort/quicksort.c3
Normal file
@@ -0,0 +1,69 @@
|
||||
module sort_bench;
|
||||
|
||||
import std::sort;
|
||||
|
||||
fn void init() @init
|
||||
{
|
||||
set_benchmark_warmup_iterations(5);
|
||||
set_benchmark_max_iterations(10_000);
|
||||
}
|
||||
|
||||
fn void quicksort_bench() @benchmark
|
||||
{
|
||||
// test set: 500 numbers between 0 and 99;
|
||||
int[] data = {
|
||||
71, 28, 2, 13, 62, 10, 54, 78, 63, 86,
|
||||
33, 65, 89, 51, 58, 0, 51, 16, 87, 30,
|
||||
89, 14, 52, 41, 88, 25, 83, 91, 56, 86,
|
||||
14, 64, 76, 18, 39, 24, 79, 62, 34, 58,
|
||||
90, 24, 56, 73, 85, 82, 79, 63, 47, 69,
|
||||
78, 29, 49, 28, 43, 47, 56, 53, 79, 56,
|
||||
19, 63, 29, 52, 71, 93, 61, 46, 30, 11,
|
||||
21, 26, 37, 86, 93, 74, 62, 0, 41, 17,
|
||||
26, 27, 34, 11, 54, 69, 72, 44, 74, 3,
|
||||
61, 62, 80, 90, 3, 82, 16, 12, 28, 1,
|
||||
2, 49, 4, 44, 57, 86, 63, 74, 33, 41,
|
||||
76, 77, 56, 57, 56, 88, 74, 71, 6, 59,
|
||||
40, 42, 94, 55, 21, 17, 17, 63, 21, 83,
|
||||
73, 19, 39, 88, 93, 74, 21, 0, 63, 45,
|
||||
69, 66, 22, 68, 86, 86, 85, 67, 8, 50,
|
||||
23, 98, 64, 80, 64, 36, 40, 30, 73, 36,
|
||||
23, 14, 1, 77, 82, 8, 18, 73, 37, 86,
|
||||
29, 70, 27, 87, 64, 81, 13, 0, 4, 83,
|
||||
90, 17, 71, 66, 38, 39, 54, 22, 86, 18,
|
||||
84, 66, 77, 25, 64, 93, 80, 91, 2, 92,
|
||||
47, 32, 90, 16, 46, 29, 56, 87, 70, 73,
|
||||
89, 41, 5, 54, 93, 63, 16, 39, 71, 84,
|
||||
74, 91, 69, 59, 49, 87, 74, 37, 75, 83,
|
||||
77, 19, 51, 44, 79, 62, 94, 20, 24, 83,
|
||||
37, 70, 57, 32, 93, 8, 29, 11, 7, 92,
|
||||
8, 23, 20, 21, 7, 70, 28, 20, 96, 6,
|
||||
50, 58, 30, 61, 66, 42, 50, 54, 64, 7,
|
||||
10, 53, 63, 44, 16, 39, 83, 73, 3, 29,
|
||||
97, 32, 36, 68, 84, 64, 73, 5, 29, 13,
|
||||
48, 3, 84, 65, 75, 68, 66, 22, 39, 33,
|
||||
39, 24, 27, 85, 18, 34, 3, 63, 32, 9,
|
||||
29, 66, 24, 90, 75, 50, 11, 95, 47, 14,
|
||||
92, 1, 76, 45, 76, 41, 55, 54, 38, 67,
|
||||
43, 40, 5, 61, 97, 11, 61, 24, 92, 24,
|
||||
76, 53, 60, 34, 78, 80, 70, 75, 30, 90,
|
||||
65, 99, 80, 61, 94, 75, 63, 67, 10, 35,
|
||||
23, 42, 31, 48, 14, 68, 84, 14, 79, 1,
|
||||
25, 94, 23, 53, 49, 69, 44, 73, 63, 51,
|
||||
44, 96, 88, 51, 94, 24, 64, 72, 59, 81,
|
||||
73, 93, 14, 35, 9, 53, 25, 48, 50, 88,
|
||||
46, 97, 67, 40, 27, 17, 2, 42, 11, 82,
|
||||
0, 46, 44, 38, 31, 88, 63, 88, 10, 82,
|
||||
77, 61, 24, 39, 27, 33, 10, 91, 69, 22,
|
||||
42, 74, 71, 13, 32, 56, 12, 46, 81, 74,
|
||||
17, 26, 45, 50, 76, 84, 76, 36, 43, 65,
|
||||
81, 64, 0, 49, 70, 11, 76, 19, 60, 55,
|
||||
15, 98, 31, 91, 56, 8, 97, 9, 3, 94,
|
||||
3, 88, 7, 2, 3, 98, 10, 51, 21, 79,
|
||||
99, 3, 8, 76, 52, 13, 40, 90, 85, 15,
|
||||
70, 77, 43, 30, 4, 89, 18, 21, 59, 17,
|
||||
};
|
||||
sort::quicksort(data);
|
||||
}
|
||||
|
||||
|
||||
61
flake.lock
generated
Normal file
61
flake.lock
generated
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1738297584,
|
||||
"narHash": "sha256-AYvaFBzt8dU0fcSK2jKD0Vg23K2eIRxfsVXIPCW9a0E=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9189ac18287c599860e878e905da550aa6dec1cd",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
44
flake.nix
Normal file
44
flake.nix
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
description = "C3 compiler flake";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs?ref=nixpkgs-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
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";
|
||||
}
|
||||
);
|
||||
in {
|
||||
packages = {
|
||||
default = self.packages.${system}.c3c;
|
||||
|
||||
c3c = call {};
|
||||
|
||||
c3c-checks = pkgs.callPackage ./nix/default.nix {
|
||||
checks = true;
|
||||
};
|
||||
|
||||
c3c-debug = pkgs.callPackage ./nix/default.nix {
|
||||
debug = true;
|
||||
};
|
||||
|
||||
c3c-debug-checks = pkgs.callPackage ./nix/default.nix {
|
||||
debug = true;
|
||||
checks = true;
|
||||
};
|
||||
};
|
||||
|
||||
devShells = {
|
||||
default = pkgs.callPackage ./nix/shell.nix {
|
||||
c3c = self.packages.${system}.c3c-debug;
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -4,6 +4,7 @@ set(GIT_HASH "unknown")
|
||||
|
||||
if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/.git")
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
|
||||
OUTPUT_VARIABLE GIT_HASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
COMMAND_ERROR_IS_FATAL ANY)
|
||||
|
||||
140
lib/std/bits.c3
140
lib/std/bits.c3
@@ -10,85 +10,85 @@ macro reverse(i) => $$bitreverse(i);
|
||||
*>
|
||||
macro bswap(i) @builtin => $$bswap(i);
|
||||
|
||||
macro uint[<*>].popcount(self) => $$popcount(self);
|
||||
macro uint[<*>].ctz(self) => $$ctz(self);
|
||||
macro uint[<*>].clz(self) => $$clz(self);
|
||||
macro uint[<*>] uint[<*>].fshl(hi, uint[<*>] lo, uint[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro uint[<*>] uint[<*>].fshr(hi, uint[<*>] lo, uint[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro uint[<*>] uint[<*>].rotl(self, uint[<*>] shift) => $$fshl(self, self, shift);
|
||||
macro uint[<*>] uint[<*>].rotr(self, uint[<*>] shift) => $$fshr(self, self, shift);
|
||||
macro uint[<?>].popcount(self) => $$popcount(self);
|
||||
macro uint[<?>].ctz(self) => $$ctz(self);
|
||||
macro uint[<?>].clz(self) => $$clz(self);
|
||||
macro uint[<?>] uint[<?>].fshl(hi, uint[<?>] lo, uint[<?>] shift) => $$fshl(hi, lo, shift);
|
||||
macro uint[<?>] uint[<?>].fshr(hi, uint[<?>] lo, uint[<?>] shift) => $$fshr(hi, lo, shift);
|
||||
macro uint[<?>] uint[<?>].rotl(self, uint[<?>] shift) => $$fshl(self, self, shift);
|
||||
macro uint[<?>] uint[<?>].rotr(self, uint[<?>] shift) => $$fshr(self, self, shift);
|
||||
|
||||
macro int[<*>].popcount(self) => $$popcount(self);
|
||||
macro int[<*>].ctz(self) => $$ctz(self);
|
||||
macro int[<*>].clz(self) => $$clz(self);
|
||||
macro int[<*>] int[<*>].fshl(hi, int[<*>] lo, int[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro int[<*>] int[<*>].fshr(hi, int[<*>] lo, int[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro int[<*>] int[<*>].rotl(self, int[<*>] shift) => $$fshl(self, self, shift);
|
||||
macro int[<*>] int[<*>].rotr(self, int[<*>] shift) => $$fshr(self, self, shift);
|
||||
macro int[<?>].popcount(self) => $$popcount(self);
|
||||
macro int[<?>].ctz(self) => $$ctz(self);
|
||||
macro int[<?>].clz(self) => $$clz(self);
|
||||
macro int[<?>] int[<?>].fshl(hi, int[<?>] lo, int[<?>] shift) => $$fshl(hi, lo, shift);
|
||||
macro int[<?>] int[<?>].fshr(hi, int[<?>] lo, int[<?>] shift) => $$fshr(hi, lo, shift);
|
||||
macro int[<?>] int[<?>].rotl(self, int[<?>] shift) => $$fshl(self, self, shift);
|
||||
macro int[<?>] int[<?>].rotr(self, int[<?>] shift) => $$fshr(self, self, shift);
|
||||
|
||||
macro ushort[<*>].popcount(self) => $$popcount(self);
|
||||
macro ushort[<*>].ctz(self) => $$ctz(self);
|
||||
macro ushort[<*>].clz(self) => $$clz(self);
|
||||
macro ushort[<*>] ushort[<*>].fshl(hi, ushort[<*>] lo, ushort[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro ushort[<*>] ushort[<*>].fshr(hi, ushort[<*>] lo, ushort[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro ushort[<*>] ushort[<*>].rotl(self, ushort[<*>] shift) => $$fshl(self, self, shift);
|
||||
macro ushort[<*>] ushort[<*>].rotr(self, ushort[<*>] shift) => $$fshr(self, self, shift);
|
||||
macro ushort[<?>].popcount(self) => $$popcount(self);
|
||||
macro ushort[<?>].ctz(self) => $$ctz(self);
|
||||
macro ushort[<?>].clz(self) => $$clz(self);
|
||||
macro ushort[<?>] ushort[<?>].fshl(hi, ushort[<?>] lo, ushort[<?>] shift) => $$fshl(hi, lo, shift);
|
||||
macro ushort[<?>] ushort[<?>].fshr(hi, ushort[<?>] lo, ushort[<?>] shift) => $$fshr(hi, lo, shift);
|
||||
macro ushort[<?>] ushort[<?>].rotl(self, ushort[<?>] shift) => $$fshl(self, self, shift);
|
||||
macro ushort[<?>] ushort[<?>].rotr(self, ushort[<?>] shift) => $$fshr(self, self, shift);
|
||||
|
||||
macro short[<*>].popcount(self) => $$popcount(self);
|
||||
macro short[<*>].ctz(self) => $$ctz(self);
|
||||
macro short[<*>].clz(self) => $$clz(self);
|
||||
macro short[<*>] short[<*>].fshl(hi, short[<*>] lo, short[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro short[<*>] short[<*>].fshr(hi, short[<*>] lo, short[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro short[<*>] short[<*>].rotl(self, short[<*>] shift) => $$fshl(self, self, shift);
|
||||
macro short[<*>] short[<*>].rotr(self, short[<*>] shift) => $$fshr(self, self, shift);
|
||||
macro short[<?>].popcount(self) => $$popcount(self);
|
||||
macro short[<?>].ctz(self) => $$ctz(self);
|
||||
macro short[<?>].clz(self) => $$clz(self);
|
||||
macro short[<?>] short[<?>].fshl(hi, short[<?>] lo, short[<?>] shift) => $$fshl(hi, lo, shift);
|
||||
macro short[<?>] short[<?>].fshr(hi, short[<?>] lo, short[<?>] shift) => $$fshr(hi, lo, shift);
|
||||
macro short[<?>] short[<?>].rotl(self, short[<?>] shift) => $$fshl(self, self, shift);
|
||||
macro short[<?>] short[<?>].rotr(self, short[<?>] shift) => $$fshr(self, self, shift);
|
||||
|
||||
macro char[<*>].popcount(self) => $$popcount(self);
|
||||
macro char[<*>].ctz(self) => $$ctz(self);
|
||||
macro char[<*>].clz(self) => $$clz(self);
|
||||
macro char[<*>] char[<*>].fshl(hi, char[<*>] lo, char[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro char[<*>] char[<*>].fshr(hi, char[<*>] lo, char[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro char[<*>] char[<*>].rotl(self, char[<*>] shift) => $$fshl(self, self, shift);
|
||||
macro char[<*>] char[<*>].rotr(self, char[<*>] shift) => $$fshr(self, self, shift);
|
||||
macro char[<?>].popcount(self) => $$popcount(self);
|
||||
macro char[<?>].ctz(self) => $$ctz(self);
|
||||
macro char[<?>].clz(self) => $$clz(self);
|
||||
macro char[<?>] char[<?>].fshl(hi, char[<?>] lo, char[<?>] shift) => $$fshl(hi, lo, shift);
|
||||
macro char[<?>] char[<?>].fshr(hi, char[<?>] lo, char[<?>] shift) => $$fshr(hi, lo, shift);
|
||||
macro char[<?>] char[<?>].rotl(self, char[<?>] shift) => $$fshl(self, self, shift);
|
||||
macro char[<?>] char[<?>].rotr(self, char[<?>] shift) => $$fshr(self, self, shift);
|
||||
|
||||
macro ichar[<*>].popcount(self) => $$popcount(self);
|
||||
macro ichar[<*>].ctz(self) => $$ctz(self);
|
||||
macro ichar[<*>].clz(self) => $$clz(self);
|
||||
macro ichar[<*>] ichar[<*>].fshl(hi, ichar[<*>] lo, ichar[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro ichar[<*>] ichar[<*>].fshr(hi, ichar[<*>] lo, ichar[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro ichar[<*>] ichar[<*>].rotl(self, ichar[<*>] shift) => $$fshl(self, self, shift);
|
||||
macro ichar[<*>] ichar[<*>].rotr(self, ichar[<*>] shift) => $$fshr(self, self, shift);
|
||||
macro ichar[<?>].popcount(self) => $$popcount(self);
|
||||
macro ichar[<?>].ctz(self) => $$ctz(self);
|
||||
macro ichar[<?>].clz(self) => $$clz(self);
|
||||
macro ichar[<?>] ichar[<?>].fshl(hi, ichar[<?>] lo, ichar[<?>] shift) => $$fshl(hi, lo, shift);
|
||||
macro ichar[<?>] ichar[<?>].fshr(hi, ichar[<?>] lo, ichar[<?>] shift) => $$fshr(hi, lo, shift);
|
||||
macro ichar[<?>] ichar[<?>].rotl(self, ichar[<?>] shift) => $$fshl(self, self, shift);
|
||||
macro ichar[<?>] ichar[<?>].rotr(self, ichar[<?>] shift) => $$fshr(self, self, shift);
|
||||
|
||||
macro ulong[<*>].popcount(self) => $$popcount(self);
|
||||
macro ulong[<*>].ctz(self) => $$ctz(self);
|
||||
macro ulong[<*>].clz(self) => $$clz(self);
|
||||
macro ulong[<*>] ulong[<*>].fshl(hi, ulong[<*>] lo, ulong[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro ulong[<*>] ulong[<*>].fshr(hi, ulong[<*>] lo, ulong[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro ulong[<*>] ulong[<*>].rotl(self, ulong[<*>] shift) => $$fshl(self, self, shift);
|
||||
macro ulong[<*>] ulong[<*>].rotr(self, ulong[<*>] shift) => $$fshr(self, self, shift);
|
||||
macro ulong[<?>].popcount(self) => $$popcount(self);
|
||||
macro ulong[<?>].ctz(self) => $$ctz(self);
|
||||
macro ulong[<?>].clz(self) => $$clz(self);
|
||||
macro ulong[<?>] ulong[<?>].fshl(hi, ulong[<?>] lo, ulong[<?>] shift) => $$fshl(hi, lo, shift);
|
||||
macro ulong[<?>] ulong[<?>].fshr(hi, ulong[<?>] lo, ulong[<?>] shift) => $$fshr(hi, lo, shift);
|
||||
macro ulong[<?>] ulong[<?>].rotl(self, ulong[<?>] shift) => $$fshl(self, self, shift);
|
||||
macro ulong[<?>] ulong[<?>].rotr(self, ulong[<?>] shift) => $$fshr(self, self, shift);
|
||||
|
||||
macro long[<*>].popcount(self) => $$popcount(self);
|
||||
macro long[<*>].ctz(self) => $$ctz(self);
|
||||
macro long[<*>].clz(self) => $$clz(self);
|
||||
macro long[<*>] long[<*>].fshl(hi, long[<*>] lo, long[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro long[<*>] long[<*>].fshr(hi, long[<*>] lo, long[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro long[<*>] long[<*>].rotl(self, long[<*>] shift) => $$fshl(self, self, shift);
|
||||
macro long[<*>] long[<*>].rotr(self, long[<*>] shift) => $$fshr(self, self, shift);
|
||||
macro long[<?>].popcount(self) => $$popcount(self);
|
||||
macro long[<?>].ctz(self) => $$ctz(self);
|
||||
macro long[<?>].clz(self) => $$clz(self);
|
||||
macro long[<?>] long[<?>].fshl(hi, long[<?>] lo, long[<?>] shift) => $$fshl(hi, lo, shift);
|
||||
macro long[<?>] long[<?>].fshr(hi, long[<?>] lo, long[<?>] shift) => $$fshr(hi, lo, shift);
|
||||
macro long[<?>] long[<?>].rotl(self, long[<?>] shift) => $$fshl(self, self, shift);
|
||||
macro long[<?>] long[<?>].rotr(self, long[<?>] shift) => $$fshr(self, self, shift);
|
||||
|
||||
macro uint128[<*>].popcount(self) => $$popcount(self);
|
||||
macro uint128[<*>].ctz(self) => $$ctz(self);
|
||||
macro uint128[<*>].clz(self) => $$clz(self);
|
||||
macro uint128[<*>] uint128[<*>].fshl(hi, uint128[<*>] lo, uint128[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro uint128[<*>] uint128[<*>].fshr(hi, uint128[<*>] lo, uint128[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro uint128[<*>] uint128[<*>].rotl(self, uint128[<*>] shift) => $$fshl(self, self, shift);
|
||||
macro uint128[<*>] uint128[<*>].rotr(self, uint128[<*>] shift) => $$fshr(self, self, shift);
|
||||
macro uint128[<?>].popcount(self) => $$popcount(self);
|
||||
macro uint128[<?>].ctz(self) => $$ctz(self);
|
||||
macro uint128[<?>].clz(self) => $$clz(self);
|
||||
macro uint128[<?>] uint128[<?>].fshl(hi, uint128[<?>] lo, uint128[<?>] shift) => $$fshl(hi, lo, shift);
|
||||
macro uint128[<?>] uint128[<?>].fshr(hi, uint128[<?>] lo, uint128[<?>] shift) => $$fshr(hi, lo, shift);
|
||||
macro uint128[<?>] uint128[<?>].rotl(self, uint128[<?>] shift) => $$fshl(self, self, shift);
|
||||
macro uint128[<?>] uint128[<?>].rotr(self, uint128[<?>] shift) => $$fshr(self, self, shift);
|
||||
|
||||
macro int128[<*>].popcount(self) => $$popcount(self);
|
||||
macro int128[<*>].ctz(self) => $$ctz(self);
|
||||
macro int128[<*>].clz(self) => $$clz(self);
|
||||
macro int128[<*>] int128[<*>].fshl(hi, int128[<*>] lo, int128[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro int128[<*>] int128[<*>].fshr(hi, int128[<*>] lo, int128[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro int128[<*>] int128[<*>].rotl(self, int128[<*>] shift) => $$fshl(self, self, shift);
|
||||
macro int128[<*>] int128[<*>].rotr(self, int128[<*>] shift) => $$fshr(self, self, shift);
|
||||
macro int128[<?>].popcount(self) => $$popcount(self);
|
||||
macro int128[<?>].ctz(self) => $$ctz(self);
|
||||
macro int128[<?>].clz(self) => $$clz(self);
|
||||
macro int128[<?>] int128[<?>].fshl(hi, int128[<?>] lo, int128[<?>] shift) => $$fshl(hi, lo, shift);
|
||||
macro int128[<?>] int128[<?>].fshr(hi, int128[<?>] lo, int128[<?>] shift) => $$fshr(hi, lo, shift);
|
||||
macro int128[<?>] int128[<?>].rotl(self, int128[<?>] shift) => $$fshl(self, self, shift);
|
||||
macro int128[<?>] int128[<?>].rotr(self, int128[<?>] shift) => $$fshr(self, self, shift);
|
||||
|
||||
macro uint.popcount(self) => $$popcount(self);
|
||||
macro uint.ctz(self) => $$ctz(self);
|
||||
|
||||
@@ -117,15 +117,10 @@ fn void GrowableBitSet.set(&self, usz i)
|
||||
usz q = i / BITS;
|
||||
usz r = i % BITS;
|
||||
usz current_len = self.data.len();
|
||||
if (q >= current_len)
|
||||
while (q >= current_len)
|
||||
{
|
||||
usz n = q + 1;
|
||||
self.data.reserve(n);
|
||||
if (n - 1 >= current_len)
|
||||
{
|
||||
self.data.entries[current_len .. (n - 1)] = 0;
|
||||
}
|
||||
self.data.size = n;
|
||||
self.data.push(0);
|
||||
current_len++;
|
||||
}
|
||||
self.data.set(q, self.data[q] | (1 << r));
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ fn usz! EnumMap.to_format(&self, Formatter* formatter) @dynamic
|
||||
foreach (i, &value : self.values)
|
||||
{
|
||||
if (i != 0) formatter.print(", ")!;
|
||||
n += formatter.printf("%s: %s", (Enum)i, *value)!;
|
||||
n += formatter.printf("%s: %s", Enum.from_ordinal(i), *value)!;
|
||||
}
|
||||
n += formatter.print(" }")!;
|
||||
return n;
|
||||
|
||||
@@ -16,9 +16,9 @@ distinct EnumSet (Printable) = EnumSetType;
|
||||
fn void EnumSet.add(&self, Enum v)
|
||||
{
|
||||
$if IS_CHAR_ARRAY:
|
||||
(*self)[(usz)v / 8] |= (char)(1u << ((usz)v % 8));
|
||||
(*self)[(usz)v.ordinal / 8] |= (char)(1u << ((usz)v.ordinal % 8));
|
||||
$else
|
||||
*self = (EnumSet)((EnumSetType)*self | 1u << (EnumSetType)v);
|
||||
*self = (EnumSet)((EnumSetType)*self | 1u << (EnumSetType)v.ordinal);
|
||||
$endif
|
||||
}
|
||||
|
||||
@@ -35,11 +35,11 @@ fn bool EnumSet.remove(&self, Enum v)
|
||||
{
|
||||
$if IS_CHAR_ARRAY:
|
||||
if (!self.has(v) @inline) return false;
|
||||
(*self)[(usz)v / 8] &= (char)~(1u << ((usz)v % 8));
|
||||
(*self)[(usz)v.ordinal / 8] &= (char)~(1u << ((usz)v.ordinal % 8));
|
||||
return true;
|
||||
$else
|
||||
EnumSetType old = (EnumSetType)*self;
|
||||
EnumSetType new = old & ~(1u << (EnumSetType)v);
|
||||
EnumSetType new = old & ~(1u << (EnumSetType)v.ordinal);
|
||||
*self = (EnumSet)new;
|
||||
return old != new;
|
||||
$endif
|
||||
@@ -48,9 +48,9 @@ fn bool EnumSet.remove(&self, Enum v)
|
||||
fn bool EnumSet.has(&self, Enum v)
|
||||
{
|
||||
$if IS_CHAR_ARRAY:
|
||||
return (bool)(((*self)[(usz)v / 8] << ((usz)v % 8)) & 0x01);
|
||||
return (bool)(((*self)[(usz)v.ordinal / 8] << ((usz)v.ordinal % 8)) & 0x01);
|
||||
$else
|
||||
return ((EnumSetType)*self & (1u << (EnumSetType)v)) != 0;
|
||||
return ((EnumSetType)*self & (1u << (EnumSetType)v.ordinal)) != 0;
|
||||
$endif
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
*>
|
||||
module std::collections::map(<Key, Value>);
|
||||
import std::math;
|
||||
import std::io @norecurse;
|
||||
|
||||
struct HashMap
|
||||
struct HashMap (Printable)
|
||||
{
|
||||
Entry*[] table;
|
||||
Allocator allocator;
|
||||
@@ -400,6 +401,21 @@ fn bool HashMap.has_value(&map, Value v) @if(VALUE_IS_EQUATABLE)
|
||||
return false;
|
||||
}
|
||||
|
||||
fn HashMapIterator HashMap.iter(&self)
|
||||
{
|
||||
return { .map = self, .index = -1 };
|
||||
}
|
||||
|
||||
fn HashMapValueIterator HashMap.value_iter(&self)
|
||||
{
|
||||
return { .map = self, .index = -1 };
|
||||
}
|
||||
|
||||
fn HashMapKeyIterator HashMap.key_iter(&self)
|
||||
{
|
||||
return { .map = self, .index = -1 };
|
||||
}
|
||||
|
||||
// --- private methods
|
||||
|
||||
fn void HashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
|
||||
@@ -431,6 +447,18 @@ fn void HashMap.resize(&map, uint new_capacity) @private
|
||||
map.threshold = (uint)(new_capacity * map.load_factor);
|
||||
}
|
||||
|
||||
fn usz! HashMap.to_format(&self, Formatter* f) @dynamic
|
||||
{
|
||||
usz len;
|
||||
len += f.print("{ ")!;
|
||||
self.@each_entry(; Entry* entry)
|
||||
{
|
||||
if (len > 2) len += f.print(", ")!;
|
||||
len += f.printf("%s: %s", entry.key, entry.value)!;
|
||||
};
|
||||
return len + f.print(" }");
|
||||
}
|
||||
|
||||
fn void HashMap.transfer(&map, Entry*[] new_table) @private
|
||||
{
|
||||
Entry*[] src = map.table;
|
||||
@@ -455,8 +483,11 @@ fn void HashMap.put_all_for_create(&map, HashMap* other_map) @private
|
||||
if (!other_map.count) return;
|
||||
foreach (Entry *e : other_map.table)
|
||||
{
|
||||
if (!e) continue;
|
||||
map.put_for_create(e.key, e.value);
|
||||
while (e)
|
||||
{
|
||||
map.put_for_create(e.key, e.value);
|
||||
e = e.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,6 +513,7 @@ fn void HashMap.free_internal(&map, void* ptr) @inline @private
|
||||
|
||||
fn bool HashMap.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);
|
||||
Entry* prev = map.table[i];
|
||||
@@ -528,3 +560,54 @@ fn void HashMap.free_entry(&self, Entry *entry) @local
|
||||
self.free_internal(entry);
|
||||
}
|
||||
|
||||
|
||||
struct HashMapIterator
|
||||
{
|
||||
HashMap* map;
|
||||
int top_index;
|
||||
int index;
|
||||
Entry* current_entry;
|
||||
}
|
||||
|
||||
distinct HashMapValueIterator = HashMapIterator;
|
||||
distinct HashMapKeyIterator = HashMapIterator;
|
||||
|
||||
|
||||
<*
|
||||
@require idx < self.map.count
|
||||
*>
|
||||
fn Entry HashMapIterator.get(&self, usz idx) @operator([])
|
||||
{
|
||||
if (idx < self.index)
|
||||
{
|
||||
self.top_index = 0;
|
||||
self.current_entry = null;
|
||||
self.index = -1;
|
||||
}
|
||||
while (self.index != idx)
|
||||
{
|
||||
if (self.current_entry)
|
||||
{
|
||||
self.current_entry = self.current_entry.next;
|
||||
if (self.current_entry) self.index++;
|
||||
continue;
|
||||
}
|
||||
self.current_entry = self.map.table[self.top_index++];
|
||||
if (self.current_entry) self.index++;
|
||||
}
|
||||
return *self.current_entry;
|
||||
}
|
||||
|
||||
fn Value HashMapValueIterator.get(&self, usz idx) @operator([])
|
||||
{
|
||||
return ((HashMapIterator*)self).get(idx).value;
|
||||
}
|
||||
|
||||
fn Key HashMapKeyIterator.get(&self, usz idx) @operator([])
|
||||
{
|
||||
return ((HashMapIterator*)self).get(idx).key;
|
||||
}
|
||||
|
||||
fn usz HashMapValueIterator.len(self) @operator(len) => self.map.count;
|
||||
fn usz HashMapKeyIterator.len(self) @operator(len) => self.map.count;
|
||||
fn usz HashMapIterator.len(self) @operator(len) => self.map.count;
|
||||
|
||||
@@ -45,7 +45,7 @@ fn LinkedList* LinkedList.temp_init(&self)
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.allocator
|
||||
@require self.allocator != null
|
||||
*>
|
||||
macro void LinkedList.free_node(&self, Node* node) @private
|
||||
{
|
||||
@@ -200,7 +200,7 @@ fn void LinkedList.link_before(&self, Node *succ, Type value) @private
|
||||
}
|
||||
|
||||
<*
|
||||
@require self._first
|
||||
@require self._first != null
|
||||
*>
|
||||
fn void LinkedList.unlink_first(&self) @private
|
||||
{
|
||||
@@ -296,7 +296,7 @@ fn bool LinkedList.remove_last_match(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||
return false;
|
||||
}
|
||||
<*
|
||||
@require self._last
|
||||
@require self._last != null
|
||||
*>
|
||||
fn void LinkedList.unlink_last(&self) @inline @private
|
||||
{
|
||||
|
||||
@@ -19,6 +19,20 @@ struct List (Printable)
|
||||
Type *entries;
|
||||
}
|
||||
|
||||
<*
|
||||
@param initial_capacity "The initial capacity to reserve"
|
||||
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
||||
*>
|
||||
fn List* List.init(&self, usz initial_capacity = 16, Allocator allocator)
|
||||
{
|
||||
self.allocator = allocator;
|
||||
self.size = 0;
|
||||
self.capacity = 0;
|
||||
self.entries = null;
|
||||
self.reserve(initial_capacity);
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@param initial_capacity "The initial capacity to reserve"
|
||||
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
||||
@@ -40,7 +54,7 @@ fn List* List.new_init(&self, usz initial_capacity = 16, Allocator allocator = a
|
||||
*>
|
||||
fn List* List.temp_init(&self, usz initial_capacity = 16)
|
||||
{
|
||||
return self.new_init(initial_capacity, allocator::temp()) @inline;
|
||||
return self.init(initial_capacity, allocator::temp()) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -220,11 +234,11 @@ fn void List.push_front(&self, Type type) @inline
|
||||
fn void List.insert_at(&self, usz index, Type type)
|
||||
{
|
||||
self.reserve(1);
|
||||
for (usz i = self.size; i > index; i--)
|
||||
self.set_size(self.size + 1);
|
||||
for (isz i = self.size - 1; i > index; i--)
|
||||
{
|
||||
self.entries[i] = self.entries[i - 1];
|
||||
}
|
||||
self.set_size(self.size + 1);
|
||||
self.entries[index] = type;
|
||||
}
|
||||
|
||||
@@ -328,7 +342,8 @@ fn usz List.retain_if(&self, ElementPredicate selection)
|
||||
fn usz List.remove_using_test(&self, ElementTest filter, any context)
|
||||
{
|
||||
usz old_size = self.size;
|
||||
defer {
|
||||
defer
|
||||
{
|
||||
if (old_size != self.size) self._update_size_change(old_size, self.size);
|
||||
}
|
||||
return list_common::list_remove_using_test(self, filter, false, context);
|
||||
@@ -425,7 +440,7 @@ macro void List.pre_free(&self) @private
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.capacity
|
||||
@require self.capacity > 0
|
||||
*>
|
||||
macro void List.post_alloc(&self) @private
|
||||
{
|
||||
|
||||
@@ -131,8 +131,11 @@ fn Map new_from_map(Map other_map, Allocator allocator = null)
|
||||
if (!other_map_impl.count) return (Map)map;
|
||||
foreach (Entry *e : other_map_impl.table)
|
||||
{
|
||||
if (!e) continue;
|
||||
map._put_for_create(e.key, e.value);
|
||||
while (e)
|
||||
{
|
||||
map._put_for_create(e.key, e.value);
|
||||
e = e.next;
|
||||
}
|
||||
}
|
||||
return (Map)map;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,43 @@
|
||||
module std::collections::maybe(<Type>);
|
||||
import std::io;
|
||||
|
||||
struct Maybe
|
||||
struct Maybe (Printable)
|
||||
{
|
||||
Type value;
|
||||
bool has_value;
|
||||
}
|
||||
|
||||
fn usz! Maybe.to_format(&self, Formatter* f) @dynamic
|
||||
{
|
||||
if (self.has_value) return f.printf("[%s]", self.value);
|
||||
return f.printf("[EMPTY]");
|
||||
}
|
||||
|
||||
fn void Maybe.set(&self, Type val)
|
||||
{
|
||||
*self = { .value = val, .has_value = true };
|
||||
}
|
||||
|
||||
fn void Maybe.reset(&self)
|
||||
{
|
||||
*self = {};
|
||||
}
|
||||
|
||||
fn Maybe value(Type val)
|
||||
{
|
||||
return { .value = val, .has_value = true };
|
||||
}
|
||||
|
||||
fn Maybe Maybe.with_value(Type val) @operator(construct)
|
||||
{
|
||||
return { .value = val, .has_value = true };
|
||||
}
|
||||
|
||||
fn Maybe Maybe.empty() @operator(construct)
|
||||
{
|
||||
return { };
|
||||
}
|
||||
|
||||
const Maybe EMPTY = { };
|
||||
|
||||
macro Type! Maybe.get(self)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
<*
|
||||
@require values::@is_int(SIZE) &&& SIZE > 0 "The size must be positive integer"
|
||||
*>
|
||||
module std::collections::ringbuffer(<Type, SIZE>);
|
||||
|
||||
struct RingBuffer
|
||||
|
||||
@@ -71,23 +71,21 @@ import std::io;
|
||||
@param [in] input `The raw RGB or RGBA pixels to encode`
|
||||
@param [&in] desc `The descriptor of the image`
|
||||
*>
|
||||
fn usz! write(String filename, char[] input, QOIDesc* desc)
|
||||
fn usz! write(String filename, char[] input, QOIDesc* desc) => @pool()
|
||||
{
|
||||
@pool() {
|
||||
// encode data
|
||||
char[] output = encode(input, desc)!;
|
||||
// encode data
|
||||
char[] output = new_encode(input, desc, allocator: allocator::temp())!;
|
||||
|
||||
// open file
|
||||
File! f = file::open(filename, "wb");
|
||||
if (catch f) { return QOIError.FILE_OPEN_FAILED?; }
|
||||
// open file
|
||||
File! f = file::open(filename, "wb");
|
||||
if (catch f) return QOIError.FILE_OPEN_FAILED?;
|
||||
|
||||
// write data to file and close it
|
||||
usz! written = f.write(output);
|
||||
if (catch written) { return QOIError.FILE_WRITE_FAILED?; }
|
||||
if (catch f.close()) { return QOIError.FILE_WRITE_FAILED?; }
|
||||
// write data to file and close it
|
||||
usz! written = f.write(output);
|
||||
if (catch written) return QOIError.FILE_WRITE_FAILED?;
|
||||
if (catch f.close()) return QOIError.FILE_WRITE_FAILED?;
|
||||
|
||||
return written;
|
||||
};
|
||||
return written;
|
||||
}
|
||||
|
||||
|
||||
@@ -112,15 +110,17 @@ fn usz! write(String filename, char[] input, QOIDesc* desc)
|
||||
@param [&out] desc `The descriptor to fill with the image's info`
|
||||
@param channels `The channels to be used`
|
||||
*>
|
||||
fn char[]! read(String filename, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap())
|
||||
fn char[]! new_read(String filename, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap()) => @pool(allocator)
|
||||
{
|
||||
// read file
|
||||
char[]! data = file::load_new(filename);
|
||||
if (catch data) return QOIError.FILE_OPEN_FAILED?;
|
||||
defer mem::free(data);
|
||||
|
||||
char[] data = file::load_temp(filename) ?? QOIError.FILE_OPEN_FAILED?!;
|
||||
// pass data to decode function
|
||||
return decode(data, desc, channels, allocator);
|
||||
return new_decode(data, desc, channels, allocator);
|
||||
}
|
||||
|
||||
fn char[]! read(String filename, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap()) @deprecated("Use new_read")
|
||||
{
|
||||
return new_read(filename, desc, channels, allocator);
|
||||
}
|
||||
|
||||
|
||||
@@ -128,6 +128,11 @@ fn char[]! read(String filename, QOIDesc* desc, QOIChannels channels = AUTO, All
|
||||
module std::compression::qoi;
|
||||
import std::bits;
|
||||
|
||||
fn char[]! encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::heap()) @deprecated("use encode_new")
|
||||
{
|
||||
return new_encode(input, desc, allocator);
|
||||
}
|
||||
|
||||
<*
|
||||
Encode raw RGB or RGBA pixels into a QOI image in memory.
|
||||
|
||||
@@ -141,7 +146,7 @@ import std::bits;
|
||||
@param [in] input `The raw RGB or RGBA pixels to encode`
|
||||
@param [&in] desc `The descriptor of the image`
|
||||
*>
|
||||
fn char[]! encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::heap())
|
||||
fn char[]! new_encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::heap()) @nodiscard
|
||||
{
|
||||
// check info in desc
|
||||
if (desc.width == 0 || desc.height == 0) return QOIError.INVALID_PARAMETERS?;
|
||||
@@ -191,70 +196,77 @@ fn char[]! encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::
|
||||
if (desc.channels == RGBA) p.a = input[loc + 3];
|
||||
|
||||
// check if we can run the previous pixel
|
||||
if (prev == p) {
|
||||
if (prev == p)
|
||||
{
|
||||
run_length++;
|
||||
if (run_length == 62 || loc == loc_end) {
|
||||
*@extract(OpRun, output, &pos) = { OP_RUN, run_length - 1 };
|
||||
run_length = 0;
|
||||
}
|
||||
} else {
|
||||
// end last run if there was one
|
||||
if (run_length > 0) {
|
||||
if (run_length == 62 || loc == loc_end)
|
||||
{
|
||||
*@extract(OpRun, output, &pos) = { OP_RUN, run_length - 1 };
|
||||
run_length = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// end last run if there was one
|
||||
if (run_length > 0)
|
||||
{
|
||||
*@extract(OpRun, output, &pos) = { OP_RUN, run_length - 1 };
|
||||
run_length = 0;
|
||||
}
|
||||
|
||||
switch {
|
||||
// check if we can index the palette
|
||||
case (palette[p.hash()] == p):
|
||||
*@extract(OpIndex, output, &pos) = {
|
||||
OP_INDEX,
|
||||
p.hash()
|
||||
switch
|
||||
{
|
||||
// check if we can index the palette
|
||||
case (palette[p.hash()] == p):
|
||||
*@extract(OpIndex, output, &pos) = {
|
||||
OP_INDEX,
|
||||
p.hash()
|
||||
};
|
||||
|
||||
// check if we can use diff or luma
|
||||
case (prev != p && prev.a == p.a):
|
||||
// diff the pixels
|
||||
diff = p.rgb - prev.rgb;
|
||||
if (diff.r > -3 && diff.r < 2
|
||||
&& diff.g > -3 && diff.g < 2
|
||||
&& diff.b > -3 && diff.b < 2)
|
||||
{
|
||||
*@extract(OpDiff, output, &pos) = {
|
||||
OP_DIFF,
|
||||
(char)diff.r + 2,
|
||||
(char)diff.g + 2,
|
||||
(char)diff.b + 2
|
||||
};
|
||||
|
||||
// check if we can use diff or luma
|
||||
case (prev != p && prev.a == p.a):
|
||||
// diff the pixels
|
||||
diff = p.rgb - prev.rgb;
|
||||
if (
|
||||
diff.r > -3 && diff.r < 2 &&
|
||||
diff.g > -3 && diff.g < 2 &&
|
||||
diff.b > -3 && diff.b < 2
|
||||
) {
|
||||
*@extract(OpDiff, output, &pos) = {
|
||||
OP_DIFF,
|
||||
(char)diff.r + 2,
|
||||
(char)diff.g + 2,
|
||||
(char)diff.b + 2
|
||||
};
|
||||
palette[p.hash()] = p;
|
||||
} else {
|
||||
// check luma eligibility
|
||||
luma = { diff.r - diff.g, diff.g, diff.b - diff.g };
|
||||
if (
|
||||
luma.r >= -8 && luma.r <= 7 &&
|
||||
luma.g >= -32 && luma.g <= 31 &&
|
||||
luma.b >= -8 && luma.b <= 7
|
||||
) {
|
||||
*@extract(OpLuma, output, &pos) = {
|
||||
OP_LUMA,
|
||||
(char)luma.g + 32,
|
||||
(char)luma.r + 8,
|
||||
(char)luma.b + 8
|
||||
};
|
||||
palette[p.hash()] = p;
|
||||
} else { nextcase; }
|
||||
}
|
||||
|
||||
// worst case scenario: just encode the raw pixel
|
||||
default:
|
||||
if (prev.a != p.a) {
|
||||
*@extract(OpRGBA, output, &pos) = { OP_RGBA, p.r, p.g, p.b, p.a };
|
||||
} else {
|
||||
*@extract(OpRGB, output, &pos) = { OP_RGB, p.r, p.g, p.b };
|
||||
}
|
||||
palette[p.hash()] = p;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// check luma eligibility
|
||||
luma = { diff.r - diff.g, diff.g, diff.b - diff.g };
|
||||
if (luma.r >= -8 && luma.r <= 7
|
||||
&& luma.g >= -32 && luma.g <= 31
|
||||
&& luma.b >= -8 && luma.b <= 7)
|
||||
{
|
||||
*@extract(OpLuma, output, &pos) = {
|
||||
OP_LUMA,
|
||||
(char)luma.g + 32,
|
||||
(char)luma.r + 8,
|
||||
(char)luma.b + 8
|
||||
};
|
||||
palette[p.hash()] = p;
|
||||
break;
|
||||
}
|
||||
nextcase;
|
||||
|
||||
// worst case scenario: just encode the raw pixel
|
||||
default:
|
||||
if (prev.a != p.a)
|
||||
{
|
||||
*@extract(OpRGBA, output, &pos) = { OP_RGBA, p.r, p.g, p.b, p.a };
|
||||
}
|
||||
else
|
||||
{
|
||||
*@extract(OpRGB, output, &pos) = { OP_RGB, p.r, p.g, p.b };
|
||||
}
|
||||
palette[p.hash()] = p;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,6 +278,11 @@ fn char[]! encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::
|
||||
}
|
||||
|
||||
|
||||
fn char[]! decode(char[] data, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap())
|
||||
{
|
||||
return new_decode(data, desc, channels, allocator);
|
||||
}
|
||||
|
||||
<*
|
||||
Decode a QOI image from memory.
|
||||
|
||||
@@ -287,7 +304,7 @@ fn char[]! encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::
|
||||
@param [&out] desc `The descriptor to fill with the image's info`
|
||||
@param channels `The channels to be used`
|
||||
*>
|
||||
fn char[]! decode(char[] data, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap())
|
||||
fn char[]! new_decode(char[] data, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap()) @nodiscard
|
||||
{
|
||||
// check input data
|
||||
if (data.len < Header.sizeof + END_OF_STREAM.len) return QOIError.INVALID_DATA?;
|
||||
@@ -409,19 +426,21 @@ struct Header @packed
|
||||
char colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear
|
||||
}
|
||||
|
||||
const char[*] END_OF_STREAM = {0, 0, 0, 0, 0, 0, 0, 1};
|
||||
const char[?] END_OF_STREAM = {0, 0, 0, 0, 0, 0, 0, 1};
|
||||
|
||||
// inefficient, but it's only run once at a time
|
||||
macro @enumcast($Type, raw)
|
||||
{
|
||||
foreach (value : $Type.values) {
|
||||
foreach (value : $Type.values)
|
||||
{
|
||||
if (value.id == raw) return value;
|
||||
}
|
||||
return QOIError.INVALID_DATA?;
|
||||
}
|
||||
|
||||
distinct Pixel = inline char[<4>];
|
||||
macro char Pixel.hash(Pixel p) {
|
||||
macro char Pixel.hash(Pixel p)
|
||||
{
|
||||
return (p.r * 3 + p.g * 5 + p.b * 7 + p.a * 11) % 64;
|
||||
}
|
||||
|
||||
@@ -452,7 +471,7 @@ bitstruct OpDiff : char
|
||||
char diff_green : 2..3;
|
||||
char diff_blue : 0..1;
|
||||
}
|
||||
bitstruct OpLuma : ushort
|
||||
bitstruct OpLuma : ushort @align(1)
|
||||
{
|
||||
char tag : 6..7;
|
||||
char diff_green : 0..5;
|
||||
|
||||
@@ -28,7 +28,12 @@ fn void ArenaAllocator.clear(&self)
|
||||
struct ArenaAllocatorHeader @local
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
char[?] data;
|
||||
}
|
||||
|
||||
macro ArenaAllocator* wrap(char[] bytes)
|
||||
{
|
||||
return ArenaAllocator{}.init(bytes);
|
||||
}
|
||||
|
||||
<*
|
||||
|
||||
@@ -61,8 +61,8 @@ struct DynamicArenaChunk @local
|
||||
}
|
||||
|
||||
<*
|
||||
@require ptr
|
||||
@require self.page `tried to free pointer on invalid allocator`
|
||||
@require ptr != null
|
||||
@require self.page != null `tried to free pointer on invalid allocator`
|
||||
*>
|
||||
fn void DynamicArenaAllocator.release(&self, void* ptr, bool) @dynamic
|
||||
{
|
||||
@@ -77,7 +77,7 @@ fn void DynamicArenaAllocator.release(&self, void* ptr, bool) @dynamic
|
||||
<*
|
||||
@require size > 0 `Resize doesn't support zeroing`
|
||||
@require old_pointer != null `Resize doesn't handle null pointers`
|
||||
@require self.page `tried to realloc pointer on invalid allocator`
|
||||
@require self.page != null `tried to realloc pointer on invalid allocator`
|
||||
*>
|
||||
fn void*! DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ struct SimpleHeapAllocator (Allocator)
|
||||
}
|
||||
|
||||
<*
|
||||
@require allocator "An underlying memory provider must be given"
|
||||
@require allocator != null "An underlying memory provider must be given"
|
||||
@require !self.free_list "The allocator may not be already initialized"
|
||||
*>
|
||||
fn void SimpleHeapAllocator.init(&self, MemoryAllocFn allocator)
|
||||
@@ -98,7 +98,7 @@ fn void*! SimpleHeapAllocator._alloc(&self, usz bytes) @local
|
||||
return current + 1;
|
||||
case current.size > aligned_bytes:
|
||||
Header* unallocated = (Header*)((char*)current + aligned_bytes + Header.sizeof);
|
||||
unallocated.size = current.size - aligned_bytes;
|
||||
unallocated.size = current.size - aligned_bytes - Header.sizeof;
|
||||
unallocated.next = current.next;
|
||||
if (current == self.free_list)
|
||||
{
|
||||
|
||||
@@ -52,17 +52,17 @@ fn void OnStackAllocator.free(&self)
|
||||
struct OnStackAllocatorHeader
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
char[?] data;
|
||||
}
|
||||
|
||||
<*
|
||||
@require old_pointer
|
||||
@require old_pointer != null
|
||||
*>
|
||||
fn void OnStackAllocator.release(&self, void* old_pointer, bool aligned) @dynamic
|
||||
{
|
||||
if (allocation_in_stack_mem(self, old_pointer)) return;
|
||||
on_stack_allocator_remove_chunk(self, old_pointer);
|
||||
self.release(old_pointer, aligned);
|
||||
self.backing_allocator.release(old_pointer, aligned);
|
||||
}
|
||||
|
||||
fn bool allocation_in_stack_mem(OnStackAllocator* a, void* ptr) @local
|
||||
|
||||
@@ -4,7 +4,7 @@ import std::io, std::math;
|
||||
struct TempAllocatorChunk @local
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
char[?] data;
|
||||
}
|
||||
|
||||
struct TempAllocator (Allocator)
|
||||
@@ -13,7 +13,7 @@ struct TempAllocator (Allocator)
|
||||
TempAllocatorPage* last_page;
|
||||
usz used;
|
||||
usz capacity;
|
||||
char[*] data;
|
||||
char[?] data;
|
||||
}
|
||||
|
||||
const usz PAGE_IS_ALIGNED @private = (usz)isz.max + 1u;
|
||||
@@ -26,7 +26,7 @@ struct TempAllocatorPage
|
||||
usz mark;
|
||||
usz size;
|
||||
usz ident;
|
||||
char[*] data;
|
||||
char[?] data;
|
||||
}
|
||||
|
||||
macro usz TempAllocatorPage.pagesize(&self) => self.size & ~PAGE_IS_ALIGNED;
|
||||
@@ -80,7 +80,7 @@ fn void TempAllocator.reset(&self, usz mark) @dynamic
|
||||
usz cleaned = self.used - mark;
|
||||
if (cleaned > 0)
|
||||
{
|
||||
$if env::COMPILER_SAFE_MODE:
|
||||
$if env::COMPILER_SAFE_MODE && !env::ADDRESS_SANITIZER:
|
||||
self.data[mark : cleaned] = 0xAA;
|
||||
$endif
|
||||
asan::poison_memory_region(&self.data[mark], cleaned);
|
||||
@@ -179,8 +179,10 @@ fn void*! TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz al
|
||||
{
|
||||
mem = allocator::malloc_aligned(self.backing_allocator, total_alloc_size, alignment)!;
|
||||
}
|
||||
void* start = mem;
|
||||
mem += mem::aligned_offset(TempAllocatorPage.sizeof, alignment);
|
||||
page = (TempAllocatorPage*)mem - 1;
|
||||
page.start = mem;
|
||||
page.start = start;
|
||||
page.size = size | PAGE_IS_ALIGNED;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -49,13 +49,10 @@ fn void TrackingAllocator.free(&self)
|
||||
<*
|
||||
@return "the total allocated memory not yet freed."
|
||||
*>
|
||||
fn usz TrackingAllocator.allocated(&self)
|
||||
fn usz TrackingAllocator.allocated(&self) => @pool()
|
||||
{
|
||||
usz allocated = 0;
|
||||
@pool()
|
||||
{
|
||||
foreach (&allocation : self.map.value_tlist()) allocated += allocation.size;
|
||||
};
|
||||
foreach (&allocation : self.map.value_tlist()) allocated += allocation.size;
|
||||
return allocated;
|
||||
}
|
||||
|
||||
@@ -116,101 +113,103 @@ fn void TrackingAllocator.clear(&self)
|
||||
self.map.clear();
|
||||
}
|
||||
|
||||
fn bool TrackingAllocator.has_leaks(&self)
|
||||
{
|
||||
return self.map.len() > 0;
|
||||
}
|
||||
|
||||
fn void TrackingAllocator.print_report(&self) => self.fprint_report(io::stdout())!!;
|
||||
|
||||
fn void! TrackingAllocator.fprint_report(&self, OutStream out)
|
||||
{
|
||||
|
||||
fn void! TrackingAllocator.fprint_report(&self, OutStream out) => @pool()
|
||||
{
|
||||
usz total = 0;
|
||||
usz entries = 0;
|
||||
bool leaks = false;
|
||||
@pool()
|
||||
{
|
||||
Allocation[] allocs = self.map.value_tlist();
|
||||
if (allocs.len)
|
||||
{
|
||||
if (!allocs[0].backtrace[0])
|
||||
{
|
||||
io::fprintn(out, "======== Memory Report ========")!;
|
||||
io::fprintn(out, "Size in bytes Address")!;
|
||||
foreach (i, &allocation : allocs)
|
||||
{
|
||||
entries++;
|
||||
total += allocation.size;
|
||||
io::fprintfn(out, "%13s %p", allocation.size, allocation.ptr)!;
|
||||
}
|
||||
io::fprintn(out, "===============================")!;
|
||||
|
||||
}
|
||||
else
|
||||
Allocation[] allocs = self.map.value_tlist();
|
||||
if (allocs.len)
|
||||
{
|
||||
if (!allocs[0].backtrace[0])
|
||||
{
|
||||
io::fprintn(out, "======== Memory Report ========")!;
|
||||
io::fprintn(out, "Size in bytes Address")!;
|
||||
foreach (i, &allocation : allocs)
|
||||
{
|
||||
io::fprintn(out, "================================== Memory Report ==================================")!;
|
||||
io::fprintn(out, "Size in bytes Address Function ")!;
|
||||
foreach (i, &allocation : allocs)
|
||||
{
|
||||
entries++;
|
||||
total += allocation.size;
|
||||
BacktraceList backtraces = {};
|
||||
Backtrace trace = backtrace::BACKTRACE_UNKNOWN;
|
||||
if (allocation.backtrace[3])
|
||||
{
|
||||
trace = backtrace::symbolize_backtrace(allocation.backtrace[3:1], allocator::temp()).get(0) ?? backtrace::BACKTRACE_UNKNOWN;
|
||||
}
|
||||
if (trace.function.len) leaks = true;
|
||||
io::fprintfn(out, "%13s %p %s:%d", allocation.size,
|
||||
allocation.ptr, trace.function.len ? trace.function : "???",
|
||||
trace.line ? trace.line : 0)!;
|
||||
}
|
||||
io::fprintn(out, "===================================================================================")!;
|
||||
entries++;
|
||||
total += allocation.size;
|
||||
io::fprintfn(out, "%13s %p", allocation.size, allocation.ptr)!;
|
||||
}
|
||||
io::fprintn(out, "===============================")!;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
io::fprintn(out, "* NO ALLOCATIONS FOUND *")!;
|
||||
}
|
||||
io::fprintfn(out, "- Total currently allocated memory: %d", total)!;
|
||||
io::fprintfn(out, "- Total current allocations: %d", entries)!;
|
||||
io::fprintfn(out, "- Total allocations (freed and retained): %d", self.allocs_total)!;
|
||||
io::fprintfn(out, "- Total allocated memory (freed and retained): %d", self.mem_total)!;
|
||||
if (leaks)
|
||||
{
|
||||
io::fprintn(out)!;
|
||||
io::fprintn(out, "Full leak report:")!;
|
||||
io::fprintn(out, "================================== Memory Report ==================================")!;
|
||||
io::fprintn(out, "Size in bytes Address Function ")!;
|
||||
foreach (i, &allocation : allocs)
|
||||
{
|
||||
if (!allocation.backtrace[3])
|
||||
{
|
||||
io::fprintfn(out, "Allocation %d (%d bytes) - no backtrace available.", i + 1, allocation.size)!;
|
||||
continue;
|
||||
}
|
||||
entries++;
|
||||
total += allocation.size;
|
||||
BacktraceList backtraces = {};
|
||||
usz end = MAX_BACKTRACE;
|
||||
foreach (j, val : allocation.backtrace)
|
||||
Backtrace trace = backtrace::BACKTRACE_UNKNOWN;
|
||||
if (allocation.backtrace[3])
|
||||
{
|
||||
if (!val)
|
||||
{
|
||||
end = j;
|
||||
break;
|
||||
}
|
||||
trace = backtrace::symbolize_backtrace(allocation.backtrace[3:1], allocator::temp()).get(0) ?? backtrace::BACKTRACE_UNKNOWN;
|
||||
}
|
||||
BacktraceList list = backtrace::symbolize_backtrace(allocation.backtrace[3..(end - 1)], allocator::temp())!;
|
||||
io::fprintfn(out, "Allocation %d (%d bytes): ", i + 1, allocation.size)!;
|
||||
foreach (trace : list)
|
||||
if (trace.function.len) leaks = true;
|
||||
io::fprintfn(out, "%13s %p %s:%d", allocation.size,
|
||||
allocation.ptr, trace.function.len ? trace.function : "???",
|
||||
trace.line ? trace.line : 0)!;
|
||||
}
|
||||
io::fprintn(out, "===================================================================================")!;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
io::fprintn(out, "* NO ALLOCATIONS FOUND *")!;
|
||||
}
|
||||
io::fprintfn(out, "- Total currently allocated memory: %d", total)!;
|
||||
io::fprintfn(out, "- Total current allocations: %d", entries)!;
|
||||
io::fprintfn(out, "- Total allocations (freed and retained): %d", self.allocs_total)!;
|
||||
io::fprintfn(out, "- Total allocated memory (freed and retained): %d", self.mem_total)!;
|
||||
if (leaks)
|
||||
{
|
||||
io::fprintn(out)!;
|
||||
io::fprintn(out, "Full leak report:")!;
|
||||
foreach (i, &allocation : allocs)
|
||||
{
|
||||
if (!allocation.backtrace[3])
|
||||
{
|
||||
io::fprintfn(out, "Allocation %d (%d bytes) - no backtrace available.", i + 1, allocation.size)!;
|
||||
continue;
|
||||
}
|
||||
BacktraceList backtraces = {};
|
||||
usz end = MAX_BACKTRACE;
|
||||
foreach (j, val : allocation.backtrace)
|
||||
{
|
||||
if (!val)
|
||||
{
|
||||
if (trace.has_file())
|
||||
{
|
||||
io::fprintfn(out, " %s (in %s:%d)", trace.function, trace.file, trace.line);
|
||||
continue;
|
||||
}
|
||||
if (trace.is_unknown())
|
||||
{
|
||||
io::fprintfn(out, " ??? (in unknown)");
|
||||
continue;
|
||||
}
|
||||
io::fprintfn(out, " %s (source unavailable)", trace.function);
|
||||
end = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
BacktraceList list = backtrace::symbolize_backtrace(allocation.backtrace[3..(end - 1)], allocator::temp())!;
|
||||
io::fprintfn(out, "Allocation %d (%d bytes): ", i + 1, allocation.size)!;
|
||||
foreach (trace : list)
|
||||
{
|
||||
if (trace.has_file())
|
||||
{
|
||||
io::fprintfn(out, " %s (in %s:%d)", trace.function, trace.file, trace.line);
|
||||
continue;
|
||||
}
|
||||
if (trace.is_unknown())
|
||||
{
|
||||
io::fprintfn(out, " ??? (in unknown)");
|
||||
continue;
|
||||
}
|
||||
io::fprintfn(out, " %s (source unavailable)", trace.function);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -17,15 +17,16 @@ macro index_of(array, element)
|
||||
}
|
||||
|
||||
<*
|
||||
@require @typekind(array) == VECTOR || @typekind(array) == ARRAY
|
||||
@require @typekind(array[0]) == VECTOR || @typekind(array[0]) == ARRAY
|
||||
@require @typekind(array_ptr) == POINTER
|
||||
@require @typekind(*array_ptr) == VECTOR || @typekind(*array_ptr) == ARRAY
|
||||
@require @typekind((*array_ptr)[0]) == VECTOR || @typekind((*array_ptr)[0]) == ARRAY
|
||||
*>
|
||||
macro slice2d(array, x = 0, xlen = 0, y = 0, ylen = 0)
|
||||
macro slice2d(array_ptr, x = 0, xlen = 0, y = 0, ylen = 0)
|
||||
{
|
||||
if (xlen < 1) xlen = $typeof(array[0]).len + xlen;
|
||||
if (ylen < 1) ylen = $typeof(array).len + ylen;
|
||||
var $ElementType = $typeof(array[0][0]);
|
||||
return Slice2d(<$ElementType>) { ($ElementType*)&array, $typeof(array[0]).len, y, ylen, x, xlen };
|
||||
if (xlen < 1) xlen = $typeof((*array_ptr)[0]).len + xlen;
|
||||
if (ylen < 1) ylen = $typeof((*array_ptr)).len + ylen;
|
||||
var $ElementType = $typeof((*array_ptr)[0][0]);
|
||||
return Slice2d(<$ElementType>) { ($ElementType*)array_ptr, $typeof((*array_ptr)[0]).len, y, ylen, x, xlen };
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +79,7 @@ macro concat(arr1, arr2, Allocator allocator) @nodiscard
|
||||
@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"
|
||||
@ensure result.len == arr1.len + arr2.len
|
||||
@ensure return.len == arr1.len + arr2.len
|
||||
*>
|
||||
macro concat_new(arr1, arr2, Allocator allocator = allocator::heap()) @nodiscard
|
||||
{
|
||||
@@ -94,7 +95,7 @@ macro concat_new(arr1, arr2, Allocator allocator = allocator::heap()) @nodiscard
|
||||
@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"
|
||||
@ensure result.len == arr1.len + arr2.len
|
||||
@ensure return.len == arr1.len + arr2.len
|
||||
*>
|
||||
macro tconcat(arr1, arr2) @nodiscard => concat(arr1, arr2, allocator::temp());
|
||||
|
||||
@@ -145,11 +146,41 @@ macro void Slice2d.@each_ref(&self; @body(usz[<2>], Type*))
|
||||
<*
|
||||
@require idy >= 0 && idy < self.ylen
|
||||
*>
|
||||
macro Type[] Slice2d.get(self, usz idy) @operator([])
|
||||
macro Type[] Slice2d.get_row(self, usz idy) @operator([])
|
||||
{
|
||||
return (self.ptr + self.inner_len * (idy + self.ystart))[self.xstart:self.xlen];
|
||||
}
|
||||
|
||||
macro Type Slice2d.get_coord(self, usz[<2>] coord)
|
||||
{
|
||||
return *self.get_coord_ref(coord);
|
||||
}
|
||||
|
||||
macro Type Slice2d.get_xy(self, x, y)
|
||||
{
|
||||
return *self.get_xy_ref(x, y);
|
||||
}
|
||||
|
||||
macro Type* Slice2d.get_xy_ref(self, x, y)
|
||||
{
|
||||
return self.ptr + self.inner_len * (y + self.ystart) + self.xstart + x;
|
||||
}
|
||||
|
||||
macro Type* Slice2d.get_coord_ref(self, usz[<2>] coord)
|
||||
{
|
||||
return self.get_xy_ref(coord.x, coord.y);
|
||||
}
|
||||
|
||||
macro void Slice2d.set_coord(self, usz[<2>] coord, Type value)
|
||||
{
|
||||
*self.get_coord_ref(coord) = value;
|
||||
}
|
||||
|
||||
macro void Slice2d.set_xy(self, x, y, Type value)
|
||||
{
|
||||
*self.get_xy_ref(x, y) = value;
|
||||
}
|
||||
|
||||
<*
|
||||
@require y >= 0 && y < self.ylen
|
||||
@require x >= 0 && x < self.xlen
|
||||
|
||||
@@ -4,45 +4,47 @@
|
||||
module std::core::builtin;
|
||||
import libc, std::hash, std::io, std::os::backtrace;
|
||||
|
||||
<*
|
||||
/*
|
||||
Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds.
|
||||
*>
|
||||
*/
|
||||
fault IteratorResult { NO_MORE_ELEMENT }
|
||||
|
||||
<*
|
||||
/*
|
||||
Use `SearchResult` when trying to return a value from some collection but the element is missing.
|
||||
*>
|
||||
*/
|
||||
fault SearchResult { MISSING }
|
||||
|
||||
<*
|
||||
/*
|
||||
Use `CastResult` when an attempt at conversion fails.
|
||||
*>
|
||||
*/
|
||||
fault CastResult { TYPE_MISMATCH }
|
||||
|
||||
|
||||
def VoidFn = fn void();
|
||||
|
||||
<*
|
||||
Stores a variable on the stack, then restores it at the end of the
|
||||
macro scope.
|
||||
|
||||
@param variable `the variable to store and restore`
|
||||
@param #variable `the variable to store and restore`
|
||||
@require values::@is_lvalue(#variable)
|
||||
*>
|
||||
macro void @scope(&variable; @body) @builtin
|
||||
macro void @scope(#variable; @body) @builtin
|
||||
{
|
||||
var temp = *variable;
|
||||
defer *variable = temp;
|
||||
var temp = #variable;
|
||||
defer #variable = temp;
|
||||
@body();
|
||||
}
|
||||
|
||||
<*
|
||||
Swap two variables
|
||||
@require $assignable(*b, $typeof(*a)) && $assignable(*a, $typeof(*b))
|
||||
@require $defined(#a = #b, #b = #a) `The values must be mutually assignable`
|
||||
*>
|
||||
macro void @swap(&a, &b) @builtin
|
||||
macro void @swap(#a, #b) @builtin
|
||||
{
|
||||
var temp = *a;
|
||||
*a = *b;
|
||||
*b = temp;
|
||||
var temp = #a;
|
||||
#a = #b;
|
||||
#b = temp;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -60,45 +62,43 @@ macro anycast(any v, $Type) @builtin
|
||||
return ($Type*)v.ptr;
|
||||
}
|
||||
|
||||
fn bool print_backtrace(String message, int backtraces_to_ignore) @if(env::NATIVE_STACKTRACE)
|
||||
fn bool print_backtrace(String message, int backtraces_to_ignore) @if(env::NATIVE_STACKTRACE) => @pool()
|
||||
{
|
||||
@pool()
|
||||
void*[256] buffer;
|
||||
void*[] backtraces = backtrace::capture_current(&buffer);
|
||||
backtraces_to_ignore++;
|
||||
BacktraceList! backtrace = backtrace::symbolize_backtrace(backtraces, allocator::temp());
|
||||
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)
|
||||
{
|
||||
void*[256] buffer;
|
||||
void*[] backtraces = backtrace::capture_current(&buffer);
|
||||
backtraces_to_ignore++;
|
||||
BacktraceList! backtrace = backtrace::symbolize_backtrace(backtraces, allocator::temp());
|
||||
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)
|
||||
if (i < backtraces_to_ignore) continue;
|
||||
String inline_suffix = trace.is_inline ? " [inline]" : "";
|
||||
if (trace.is_unknown())
|
||||
{
|
||||
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);
|
||||
io::eprintfn(" in ???%s", inline_suffix);
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
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);
|
||||
return;
|
||||
}
|
||||
$endif
|
||||
$$trap();
|
||||
@@ -229,7 +229,25 @@ macro enum_by_name($Type, String enum_name) @builtin
|
||||
typeid x = $Type.typeid;
|
||||
foreach (i, name : x.names)
|
||||
{
|
||||
if (name == enum_name) return ($Type)i;
|
||||
if (name == enum_name) return $Type.from_ordinal(i);
|
||||
}
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
<*
|
||||
@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`
|
||||
@ensure @typeis(return, $Type)
|
||||
@return! SearchResult.MISSING
|
||||
*>
|
||||
macro @enum_from_value($Type, #value, value) @builtin
|
||||
{
|
||||
usz elements = $Type.elements;
|
||||
foreach (e : $Type.values)
|
||||
{
|
||||
if (e.#value == value) return e;
|
||||
}
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
@@ -348,9 +366,12 @@ macro bool @ok(#expr) @builtin
|
||||
return true;
|
||||
}
|
||||
|
||||
macro char[] @as_char_view(&value) @builtin
|
||||
<*
|
||||
@require $defined(&#value, (char*)&#value) "This must be a value that can be viewed as a char array"
|
||||
*>
|
||||
macro char[] @as_char_view(#value) @builtin
|
||||
{
|
||||
return ((char*)value)[:$sizeof(*value)];
|
||||
return ((char*)&#value)[:$sizeof(#value)];
|
||||
}
|
||||
|
||||
macro isz @str_find(String $string, String $needle) @builtin => $$str_find($string, $needle);
|
||||
@@ -358,21 +379,39 @@ macro String @str_upper(String $str) @builtin => $$str_upper($str);
|
||||
macro String @str_lower(String $str) @builtin => $$str_lower($str);
|
||||
macro uint @str_hash(String $str) @builtin => $$str_hash($str);
|
||||
|
||||
macro uint int.hash(int i) => i;
|
||||
macro uint uint.hash(uint i) => i;
|
||||
macro uint short.hash(short s) => s;
|
||||
macro uint ushort.hash(ushort s) => s;
|
||||
macro uint char.hash(char c) => c;
|
||||
macro uint ichar.hash(ichar c) => c;
|
||||
macro uint long.hash(long i) => (uint)((i >> 32) ^ i);
|
||||
macro uint ulong.hash(ulong i) => (uint)((i >> 32) ^ i);
|
||||
macro uint int128.hash(int128 i) => (uint)((i >> 96) ^ (i >> 64) ^ (i >> 32) ^ i);
|
||||
macro uint uint128.hash(uint128 i) => (uint)((i >> 96) ^ (i >> 64) ^ (i >> 32) ^ i);
|
||||
macro uint bool.hash(bool b) => (uint)b;
|
||||
macro uint typeid.hash(typeid t) => ((ulong)(uptr)t).hash();
|
||||
macro @generic_hash_core(h, value)
|
||||
{
|
||||
h ^= (uint)value; // insert lowest 32 bits
|
||||
h *= 0x96f59e5b; // diffuse them up
|
||||
h ^= h >> 16; // diffuse them down
|
||||
return h;
|
||||
}
|
||||
|
||||
macro @generic_hash(value)
|
||||
{
|
||||
uint h = @generic_hash_core((uint)0x3efd4391, value);
|
||||
$for (var $cnt = 4; $cnt < $sizeof(value); $cnt += 4)
|
||||
value >>= 32; // reduce value
|
||||
h = @generic_hash_core(h, value);
|
||||
$endfor
|
||||
return h;
|
||||
}
|
||||
|
||||
macro uint int.hash(int i) => @generic_hash(i);
|
||||
macro uint uint.hash(uint i) => @generic_hash(i);
|
||||
macro uint short.hash(short s) => @generic_hash(s);
|
||||
macro uint ushort.hash(ushort s) => @generic_hash(s);
|
||||
macro uint char.hash(char c) => @generic_hash(c);
|
||||
macro uint ichar.hash(ichar c) => @generic_hash(c);
|
||||
macro uint long.hash(long i) => @generic_hash(i);
|
||||
macro uint ulong.hash(ulong i) => @generic_hash(i);
|
||||
macro uint int128.hash(int128 i) => @generic_hash(i);
|
||||
macro uint uint128.hash(uint128 i) => @generic_hash(i);
|
||||
macro uint bool.hash(bool b) => @generic_hash(b);
|
||||
macro uint typeid.hash(typeid t) => @generic_hash(((ulong)(uptr)t));
|
||||
macro uint String.hash(String c) => (uint)fnv32a::encode(c);
|
||||
macro uint char[].hash(char[] c) => (uint)fnv32a::encode(c);
|
||||
macro uint void*.hash(void* ptr) => ((ulong)(uptr)ptr).hash();
|
||||
macro uint void*.hash(void* ptr) => @generic_hash(((ulong)(uptr)ptr));
|
||||
|
||||
distinct EmptySlot = void*;
|
||||
const EmptySlot EMPTY_MACRO_SLOT @builtin = null;
|
||||
|
||||
@@ -29,7 +29,7 @@ def CUChar = char;
|
||||
|
||||
def CChar = $typefrom($$C_CHAR_IS_SIGNED ? ichar.typeid : char.typeid);
|
||||
|
||||
enum CBool : CInt
|
||||
enum CBool : char
|
||||
{
|
||||
FALSE,
|
||||
TRUE
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
module std::core::dstring;
|
||||
import std::io;
|
||||
|
||||
distinct DString (OutStream) = void*;
|
||||
distinct DString (OutStream) = DStringOpaque*;
|
||||
distinct DStringOpaque = void;
|
||||
|
||||
const usz MIN_CAPACITY @private = 16;
|
||||
|
||||
@@ -132,7 +133,7 @@ fn usz DString.capacity(self)
|
||||
return self.data().capacity;
|
||||
}
|
||||
|
||||
fn usz DString.len(&self) @dynamic
|
||||
fn usz DString.len(&self) @dynamic @operator(len)
|
||||
{
|
||||
if (!*self) return 0;
|
||||
return self.data().len;
|
||||
@@ -154,6 +155,24 @@ fn String DString.str_view(self)
|
||||
return (String)data.chars[:data.len];
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.len()
|
||||
@require self.data() != null "Empty string"
|
||||
*>
|
||||
fn char DString.char_at(self, usz index) @operator([])
|
||||
{
|
||||
return self.data().chars[index];
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.len()
|
||||
@require self.data() != null "Empty string"
|
||||
*>
|
||||
fn char* DString.char_ref(&self, usz index) @operator(&[])
|
||||
{
|
||||
return &self.data().chars[index];
|
||||
}
|
||||
|
||||
fn usz DString.append_utf32(&self, Char32[] chars)
|
||||
{
|
||||
self.reserve(chars.len);
|
||||
@@ -168,7 +187,7 @@ fn usz DString.append_utf32(&self, Char32[] chars)
|
||||
<*
|
||||
@require index < self.len()
|
||||
*>
|
||||
fn void DString.set(self, usz index, char c)
|
||||
fn void DString.set(self, usz index, char c) @operator([]=)
|
||||
{
|
||||
self.data().chars[index] = c;
|
||||
}
|
||||
@@ -641,5 +660,5 @@ struct StringData @private
|
||||
Allocator allocator;
|
||||
usz len;
|
||||
usz capacity;
|
||||
char[*] chars;
|
||||
char[?] chars;
|
||||
}
|
||||
|
||||
@@ -117,13 +117,13 @@ enum ArchType
|
||||
|
||||
const String COMPILER_BUILD_HASH = $$BUILD_HASH;
|
||||
const String COMPILER_BUILD_DATE = $$BUILD_DATE;
|
||||
const OsType OS_TYPE = (OsType)$$OS_TYPE;
|
||||
const ArchType ARCH_TYPE = (ArchType)$$ARCH_TYPE;
|
||||
const OsType OS_TYPE = OsType.from_ordinal($$OS_TYPE);
|
||||
const ArchType ARCH_TYPE = ArchType.from_ordinal($$ARCH_TYPE);
|
||||
const bool ARCH_32_BIT = $$REGISTER_SIZE == 32;
|
||||
const bool ARCH_64_BIT = $$REGISTER_SIZE == 64;
|
||||
const bool LIBC = $$COMPILER_LIBC_AVAILABLE;
|
||||
const bool NO_LIBC = !$$COMPILER_LIBC_AVAILABLE;
|
||||
const CompilerOptLevel COMPILER_OPT_LEVEL = (CompilerOptLevel)$$COMPILER_OPT_LEVEL;
|
||||
const CompilerOptLevel COMPILER_OPT_LEVEL = CompilerOptLevel.from_ordinal($$COMPILER_OPT_LEVEL);
|
||||
const bool BIG_ENDIAN = $$PLATFORM_BIG_ENDIAN;
|
||||
const bool I128_NATIVE_SUPPORT = $$PLATFORM_I128_SUPPORTED;
|
||||
const bool F16_SUPPORT = $$PLATFORM_F16_SUPPORTED;
|
||||
@@ -135,7 +135,7 @@ const bool BACKTRACE = $$BACKTRACE;
|
||||
const usz LLVM_VERSION = $$LLVM_VERSION;
|
||||
const bool BENCHMARKING = $$BENCHMARKING;
|
||||
const bool TESTING = $$TESTING;
|
||||
const MemoryEnvironment MEMORY_ENV = (MemoryEnvironment)$$MEMORY_ENVIRONMENT;
|
||||
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;
|
||||
|
||||
@@ -25,7 +25,7 @@ macro bool @constant_is_power_of_2($x) @const @private
|
||||
|
||||
@return "A vector with the loaded values where the mask is true, passthru where the mask is false"
|
||||
*>
|
||||
macro masked_load(ptr, bool[<*>] mask, passthru)
|
||||
macro masked_load(ptr, bool[<?>] mask, passthru)
|
||||
{
|
||||
return $$masked_load(ptr, mask, passthru, 0);
|
||||
}
|
||||
@@ -45,7 +45,7 @@ macro masked_load(ptr, bool[<*>] mask, passthru)
|
||||
|
||||
@return "A vector with the loaded values where the mask is true, passthru where the mask is false"
|
||||
*>
|
||||
macro @masked_load_aligned(ptr, bool[<*>] mask, passthru, usz $alignment)
|
||||
macro @masked_load_aligned(ptr, bool[<?>] mask, passthru, usz $alignment)
|
||||
{
|
||||
return $$masked_load(ptr, mask, passthru, $alignment);
|
||||
}
|
||||
@@ -65,7 +65,7 @@ macro @masked_load_aligned(ptr, bool[<*>] mask, passthru, usz $alignment)
|
||||
|
||||
@return "A vector with the loaded values where the mask is true, passthru where the mask is false"
|
||||
*>
|
||||
macro gather(ptrvec, bool[<*>] mask, passthru)
|
||||
macro gather(ptrvec, bool[<?>] mask, passthru)
|
||||
{
|
||||
return $$gather(ptrvec, mask, passthru, 0);
|
||||
}
|
||||
@@ -88,7 +88,7 @@ macro gather(ptrvec, bool[<*>] mask, passthru)
|
||||
|
||||
@return "A vector with the loaded values where the mask is true, passthru where the mask is false"
|
||||
*>
|
||||
macro @gather_aligned(ptrvec, bool[<*>] mask, passthru, usz $alignment)
|
||||
macro @gather_aligned(ptrvec, bool[<?>] mask, passthru, usz $alignment)
|
||||
{
|
||||
return $$gather(ptrvec, mask, passthru, $alignment);
|
||||
}
|
||||
@@ -105,7 +105,7 @@ macro @gather_aligned(ptrvec, bool[<*>] mask, passthru, usz $alignment)
|
||||
@require @typekind(value) == VECTOR : "Expected value to be a vector"
|
||||
@require value.len == mask.len : "Mask and value must have the same length"
|
||||
*>
|
||||
macro masked_store(ptr, value, bool[<*>] mask)
|
||||
macro masked_store(ptr, value, bool[<?>] mask)
|
||||
{
|
||||
return $$masked_store(ptr, value, mask, 0);
|
||||
}
|
||||
@@ -122,7 +122,7 @@ macro masked_store(ptr, value, bool[<*>] mask)
|
||||
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||
|
||||
*>
|
||||
macro @masked_store_aligned(ptr, value, bool[<*>] mask, usz $alignment)
|
||||
macro @masked_store_aligned(ptr, value, bool[<?>] mask, usz $alignment)
|
||||
{
|
||||
return $$masked_store(ptr, value, mask, $alignment);
|
||||
}
|
||||
@@ -138,7 +138,7 @@ macro @masked_store_aligned(ptr, value, bool[<*>] mask, usz $alignment)
|
||||
@require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
|
||||
|
||||
*>
|
||||
macro scatter(ptrvec, value, bool[<*>] mask)
|
||||
macro scatter(ptrvec, value, bool[<?>] mask)
|
||||
{
|
||||
return $$scatter(ptrvec, value, mask, 0);
|
||||
}
|
||||
@@ -156,48 +156,61 @@ macro scatter(ptrvec, value, bool[<*>] mask)
|
||||
@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"
|
||||
*>
|
||||
macro @scatter_aligned(ptrvec, value, bool[<*>] mask, usz $alignment)
|
||||
macro @scatter_aligned(ptrvec, value, bool[<?>] mask, usz $alignment)
|
||||
{
|
||||
return $$scatter(ptrvec, value, mask, $alignment);
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] x "The variable or dereferenced pointer to load."
|
||||
@param #x "The variable or dereferenced pointer to load."
|
||||
@param $alignment "The alignment to assume for the load"
|
||||
@return "The value of x"
|
||||
@return "The value of the variable"
|
||||
|
||||
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
|
||||
*>
|
||||
macro @unaligned_load(&x, usz $alignment) @builtin
|
||||
macro @unaligned_load(#x, usz $alignment) @builtin
|
||||
{
|
||||
return $$unaligned_load(x, $alignment);
|
||||
return $$unaligned_load(&#x, $alignment);
|
||||
}
|
||||
|
||||
<*
|
||||
@param [out] x "The variable or dereferenced pointer to store to."
|
||||
@param #x "The variable or dereferenced pointer to store to."
|
||||
@param value "The value to store."
|
||||
@param $alignment "The alignment to assume for the store"
|
||||
@return "The value of x"
|
||||
@return "The value stored"
|
||||
|
||||
@require $assignable(value, $typeof(*x)) : "The value doesn't match the variable"
|
||||
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
|
||||
@require $defined(#x = value) : "The value doesn't match the variable"
|
||||
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||
*>
|
||||
macro @unaligned_store(&x, value, usz $alignment) @builtin
|
||||
macro @unaligned_store(#x, value, usz $alignment) @builtin
|
||||
{
|
||||
return $$unaligned_store(x, ($typeof(*x))value, $alignment);
|
||||
}
|
||||
|
||||
macro @volatile_load(&x) @builtin
|
||||
{
|
||||
return $$volatile_load(x);
|
||||
return $$unaligned_store(&#x, ($typeof(#x))value, $alignment);
|
||||
}
|
||||
|
||||
<*
|
||||
@require $assignable(y, $typeof(*x)) : "The value doesn't match the variable"
|
||||
@param #x "The variable or dereferenced pointer to load."
|
||||
@return "The value of the variable"
|
||||
|
||||
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
|
||||
*>
|
||||
macro @volatile_store(&x, y) @builtin
|
||||
macro @volatile_load(#x) @builtin
|
||||
{
|
||||
return $$volatile_store(x, ($typeof(*x))y);
|
||||
return $$volatile_load(&#x);
|
||||
}
|
||||
|
||||
<*
|
||||
@param #x "The variable or dereferenced pointer to store to."
|
||||
@param value "The value to store."
|
||||
@return "The value stored"
|
||||
|
||||
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
|
||||
@require $defined(#x = value) : "The value doesn't match the variable"
|
||||
*>
|
||||
macro @volatile_store(#x, value) @builtin
|
||||
{
|
||||
return $$volatile_store(&#x, ($typeof(#x))value);
|
||||
}
|
||||
|
||||
enum AtomicOrdering : int
|
||||
@@ -212,34 +225,36 @@ enum AtomicOrdering : int
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] x "the variable or dereferenced pointer to load."
|
||||
@param #x "the variable or dereferenced pointer to load."
|
||||
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@param $volatile "whether the load should be volatile, defaults to 'false'"
|
||||
@return "returns the value of x"
|
||||
|
||||
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
|
||||
@require $ordering != AtomicOrdering.RELEASE "Release ordering is not valid for load."
|
||||
@require $ordering != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid for load."
|
||||
@require types::may_load_atomic($typeof(x)) "Only integer, float and pointers may be used."
|
||||
@require @typekind(x) == POINTER "You can only load from a pointer"
|
||||
@require types::may_load_atomic($typeof(#x)) "Only integer, float and pointers may be used."
|
||||
*>
|
||||
macro @atomic_load(&x, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
|
||||
macro @atomic_load(#x, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
|
||||
{
|
||||
return $$atomic_load(x, $volatile, (int)$ordering);
|
||||
return $$atomic_load(&#x, $volatile, $ordering.ordinal);
|
||||
}
|
||||
|
||||
<*
|
||||
@param [out] x "the variable or dereferenced pointer to store to."
|
||||
@param #x "the variable or dereferenced pointer to store to."
|
||||
@param value "the value to store."
|
||||
@param $ordering "the atomic ordering of the store, defaults to SEQ_CONSISTENT"
|
||||
@param $volatile "whether the store should be volatile, defaults to 'false'"
|
||||
|
||||
@require $ordering != AtomicOrdering.ACQUIRE "Acquire ordering is not valid for store."
|
||||
@require $ordering != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid for store."
|
||||
@require types::may_load_atomic($typeof(x)) "Only integer, float and pointers may be used."
|
||||
@require types::may_load_atomic($typeof(#x)) "Only integer, float and pointers may be used."
|
||||
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
|
||||
@require $defined(#x = value) : "The value doesn't match the variable"
|
||||
*>
|
||||
macro void @atomic_store(&x, value, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
|
||||
macro void @atomic_store(#x, value, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
|
||||
{
|
||||
$$atomic_store(x, value, $volatile, (int)$ordering);
|
||||
$$atomic_store(&#x, value, $volatile, $ordering.ordinal);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -286,7 +301,7 @@ macro void zero_volatile(char[] data)
|
||||
$$memset(data.ptr, (char)0, data.len, true, (usz)1);
|
||||
}
|
||||
|
||||
macro void clear(void* dst, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
|
||||
macro void clear(void* dst, usz len, usz $dst_align = 0, bool $is_volatile = false)
|
||||
{
|
||||
$$memset(dst, (char)0, len, $is_volatile, $dst_align);
|
||||
}
|
||||
@@ -308,7 +323,7 @@ macro void clear_inline(void* dst, usz $len, usz $dst_align = 0, bool $is_volati
|
||||
|
||||
@require len == 0 || dst + len <= src || src + len <= dst : "Ranges may not overlap"
|
||||
*>
|
||||
macro void copy(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false, bool $inlined = false)
|
||||
macro void copy(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false)
|
||||
{
|
||||
$$memcpy(dst, src, len, $is_volatile, $dst_align, $src_align);
|
||||
}
|
||||
@@ -455,19 +470,57 @@ macro void @scoped(Allocator allocator; @body())
|
||||
<*
|
||||
Run the tracking allocator in the scope, then
|
||||
print out stats.
|
||||
|
||||
@param $enabled "Set to false to disable tracking"
|
||||
*>
|
||||
macro void @report_heap_allocs_in_scope(;@body())
|
||||
macro void @report_heap_allocs_in_scope($enabled = true; @body())
|
||||
{
|
||||
TrackingAllocator tracker;
|
||||
tracker.init(allocator::thread_allocator);
|
||||
Allocator old_allocator = allocator::thread_allocator;
|
||||
allocator::thread_allocator = &tracker;
|
||||
defer
|
||||
{
|
||||
allocator::thread_allocator = old_allocator;
|
||||
tracker.print_report();
|
||||
tracker.free();
|
||||
}
|
||||
$if $enabled:
|
||||
TrackingAllocator tracker;
|
||||
tracker.init(allocator::thread_allocator);
|
||||
Allocator old_allocator = allocator::thread_allocator;
|
||||
allocator::thread_allocator = &tracker;
|
||||
defer
|
||||
{
|
||||
allocator::thread_allocator = old_allocator;
|
||||
tracker.print_report();
|
||||
tracker.free();
|
||||
}
|
||||
$endif
|
||||
@body();
|
||||
}
|
||||
|
||||
<*
|
||||
Assert on memory leak in the scope of the macro body.
|
||||
|
||||
@param $report "Set to false to disable memory report"
|
||||
*>
|
||||
macro void @assert_leak($report = true; @body()) @builtin
|
||||
{
|
||||
$if env::DEBUG_SYMBOLS || $feature(MEMORY_ASSERTS):
|
||||
TrackingAllocator tracker;
|
||||
tracker.init(allocator::thread_allocator);
|
||||
Allocator old_allocator = allocator::thread_allocator;
|
||||
allocator::thread_allocator = &tracker;
|
||||
defer
|
||||
{
|
||||
allocator::thread_allocator = old_allocator;
|
||||
defer tracker.free();
|
||||
usz allocated = tracker.allocated();
|
||||
if (allocated)
|
||||
{
|
||||
DString report = dstring::new();
|
||||
defer report.free();
|
||||
$if $report:
|
||||
report.append_char('\n');
|
||||
(void)tracker.fprint_report(&report);
|
||||
$endif
|
||||
assert(allocated == 0, "Memory leak detected"
|
||||
" (%d bytes allocated).%s",
|
||||
allocated, report.str_view());
|
||||
}
|
||||
}
|
||||
$endif
|
||||
@body();
|
||||
}
|
||||
|
||||
@@ -531,19 +584,21 @@ fn void temp_pop(TempState old_state)
|
||||
allocator::thread_temp_allocator = old_state.old;
|
||||
}
|
||||
|
||||
macro void @pool(TempAllocator* #other_temp = null; @body) @builtin
|
||||
<*
|
||||
@require @is_empty_macro_slot(#other_temp) ||| $assignable(#other_temp, Allocator) "Must be an allocator"
|
||||
*>
|
||||
macro void @pool(#other_temp = EMPTY_MACRO_SLOT; @body) @builtin
|
||||
{
|
||||
TempAllocator* current = allocator::temp();
|
||||
var $has_arg = !$is_const(#other_temp);
|
||||
$if $has_arg:
|
||||
$if @is_valid_macro_slot(#other_temp):
|
||||
TempAllocator* original = current;
|
||||
if (current == (void*)#other_temp) current = allocator::temp_allocator_next();
|
||||
if (current == #other_temp.ptr) current = allocator::temp_allocator_next();
|
||||
$endif
|
||||
usz mark = current.used;
|
||||
defer
|
||||
{
|
||||
current.reset(mark);
|
||||
$if $has_arg:
|
||||
$if @is_valid_macro_slot(#other_temp):
|
||||
allocator::thread_temp_allocator = original;
|
||||
$endif;
|
||||
}
|
||||
@@ -628,6 +683,22 @@ macro new($Type, ...) @nodiscard
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@require $vacount < 2 : "Too many arguments."
|
||||
@require $vacount == 0 ||| $assignable($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
|
||||
{
|
||||
$if $vacount == 0:
|
||||
return ($Type*)calloc($Type.sizeof + padding);
|
||||
$else
|
||||
$Type* val = malloc($Type.sizeof + padding);
|
||||
*val = $vaexpr[0];
|
||||
return val;
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
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.
|
||||
@@ -653,6 +724,14 @@ macro alloc($Type) @nodiscard
|
||||
return ($Type*)malloc($Type.sizeof);
|
||||
}
|
||||
|
||||
<*
|
||||
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
|
||||
*>
|
||||
macro alloc_with_padding($Type, usz padding) @nodiscard
|
||||
{
|
||||
return ($Type*)malloc($Type.sizeof + padding);
|
||||
}
|
||||
|
||||
<*
|
||||
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.
|
||||
@@ -677,11 +756,30 @@ macro temp_new($Type, ...) @nodiscard
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@require $vacount < 2 : "Too many arguments."
|
||||
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
*>
|
||||
macro temp_new_with_padding($Type, usz padding, ...) @nodiscard
|
||||
{
|
||||
$if $vacount == 0:
|
||||
return ($Type*)tcalloc($Type.sizeof + padding) @inline;
|
||||
$else
|
||||
$Type* val = tmalloc($Type.sizeof + padding) @inline;
|
||||
*val = $vaexpr[0];
|
||||
return val;
|
||||
$endif
|
||||
}
|
||||
|
||||
macro temp_alloc($Type) @nodiscard
|
||||
{
|
||||
return tmalloc($Type.sizeof);
|
||||
}
|
||||
|
||||
macro temp_alloc_with_padding($Type, usz padding) @nodiscard
|
||||
{
|
||||
return tmalloc($Type.sizeof + padding);
|
||||
}
|
||||
|
||||
<*
|
||||
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_array_aligned' instead"
|
||||
@@ -774,3 +872,40 @@ fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMEN
|
||||
return allocator::temp().resize(ptr, size, alignment)!!;
|
||||
}
|
||||
|
||||
module std::core::mem @if(env::NO_LIBC);
|
||||
|
||||
fn CInt __memcmp(void* s1, void* s2, usz n) @weak @export("memcmp")
|
||||
{
|
||||
char* p1 = s1;
|
||||
char* p2 = s2;
|
||||
for (usz i = 0; i < n; i++, p1++, p2++)
|
||||
{
|
||||
char c1 = *p1;
|
||||
char c2 = *p2;
|
||||
if (c1 < c2) return -1;
|
||||
if (c1 > c2) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn void* __memset(void* str, CInt c, usz n) @weak @export("memset")
|
||||
{
|
||||
char* p = str;
|
||||
char cc = (char)c;
|
||||
for (usz i = 0; i < n; i++, p++)
|
||||
{
|
||||
*p = cc;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
fn void* __memcpy(void* dst, void* src, usz n) @weak @export("memcpy")
|
||||
{
|
||||
char* d = dst;
|
||||
char* s = src;
|
||||
for (usz i = 0; i < n; i++, d++, s++)
|
||||
{
|
||||
*d = *s;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
@@ -21,20 +21,20 @@ interface Allocator
|
||||
fn void reset(usz mark) @optional;
|
||||
fn usz mark() @optional;
|
||||
<*
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
* @require size > 0
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
@require size > 0
|
||||
*>
|
||||
fn void*! acquire(usz size, AllocInitType init_type, usz alignment = 0);
|
||||
<*
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
* @require ptr != null
|
||||
* @require new_size > 0
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
@require ptr != null
|
||||
@require new_size > 0
|
||||
*>
|
||||
fn void*! resize(void* ptr, usz new_size, usz alignment = 0);
|
||||
<*
|
||||
* @require ptr != null
|
||||
@require ptr != null
|
||||
*>
|
||||
fn void release(void* ptr, bool aligned);
|
||||
}
|
||||
@@ -184,12 +184,12 @@ macro new_try(Allocator allocator, $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"
|
||||
*>
|
||||
macro new_aligned($Type, ...) @nodiscard
|
||||
macro new_aligned(Allocator allocator, $Type, ...) @nodiscard
|
||||
{
|
||||
$if $vacount == 0:
|
||||
return ($Type*)calloc_aligned(allocator, $Type.sizeof, $Type.alignof);
|
||||
$else
|
||||
$Type* val = malloc_aligned(allocator, $Type.sizeof, $Type.alignof);
|
||||
$Type* val = malloc_aligned(allocator, $Type.sizeof, $Type.alignof)!;
|
||||
*val = $vaexpr[0];
|
||||
return val;
|
||||
$endif
|
||||
|
||||
@@ -25,6 +25,11 @@ enum X86Feature
|
||||
{
|
||||
ADX,
|
||||
AES,
|
||||
AMX_AVX512,
|
||||
AMX_FP8,
|
||||
AMX_MOVRS,
|
||||
AMX_TF32,
|
||||
AMX_TRANSPOSE,
|
||||
AMX_BF16,
|
||||
AMX_COMPLEX,
|
||||
AMX_FP16,
|
||||
@@ -34,6 +39,8 @@ enum X86Feature
|
||||
AVX,
|
||||
AVX10_1_256,
|
||||
AVX10_1_512,
|
||||
AVX10_2_256,
|
||||
AVX10_2_512,
|
||||
AVX2,
|
||||
AVX5124FMAPS,
|
||||
AVX5124VNNIW,
|
||||
@@ -84,6 +91,7 @@ enum X86Feature
|
||||
MOVBE,
|
||||
MOVDIR64B,
|
||||
MOVDIRI,
|
||||
MOVRS,
|
||||
MWAITX,
|
||||
PCLMUL,
|
||||
PCONFIG,
|
||||
|
||||
@@ -214,7 +214,7 @@ struct TypeId
|
||||
usz sizeof;
|
||||
TypeId* inner;
|
||||
usz len;
|
||||
typeid[*] additional;
|
||||
typeid[?] additional;
|
||||
}
|
||||
|
||||
fn void dl_reg_callback(MachHeader* mh, isz vmaddr_slide)
|
||||
|
||||
@@ -47,6 +47,13 @@ macro int @main_to_int_main_args(#m, int argc, char** argv)
|
||||
return #m(list);
|
||||
}
|
||||
|
||||
macro int @_main_runner(#m, int argc, char** argv)
|
||||
{
|
||||
String[] list = args_to_strings(argc, argv);
|
||||
defer free(list.ptr);
|
||||
return #m(list) ? 0 : 1;
|
||||
}
|
||||
|
||||
macro int @main_to_void_main_args(#m, int argc, char** argv)
|
||||
{
|
||||
String[] list = args_to_strings(argc, argv);
|
||||
@@ -157,6 +164,13 @@ macro int @wmain_to_int_main_args(#m, int argc, Char16** argv)
|
||||
return #m(args);
|
||||
}
|
||||
|
||||
macro int @_wmain_runner(#m, int argc, Char16** argv)
|
||||
{
|
||||
String[] args = wargs_strings(argc, argv);
|
||||
defer release_wargs(args);
|
||||
return #m(args) ? 0 : 1;
|
||||
}
|
||||
|
||||
macro int @wmain_to_void_main_args(#m, int argc, Char16** argv)
|
||||
{
|
||||
String[] args = wargs_strings(argc, argv);
|
||||
|
||||
@@ -22,230 +22,6 @@ struct SliceRaw
|
||||
usz len;
|
||||
}
|
||||
|
||||
def BenchmarkFn = fn void!();
|
||||
|
||||
struct BenchmarkUnit
|
||||
{
|
||||
String name;
|
||||
BenchmarkFn func;
|
||||
}
|
||||
|
||||
fn BenchmarkUnit[] benchmark_collection_create(Allocator allocator = allocator::heap())
|
||||
{
|
||||
BenchmarkFn[] fns = $$BENCHMARK_FNS;
|
||||
String[] names = $$BENCHMARK_NAMES;
|
||||
BenchmarkUnit[] benchmarks = allocator::alloc_array(allocator, BenchmarkUnit, names.len);
|
||||
foreach (i, benchmark : fns)
|
||||
{
|
||||
benchmarks[i] = { names[i], fns[i] };
|
||||
}
|
||||
return benchmarks;
|
||||
}
|
||||
|
||||
const DEFAULT_BENCHMARK_WARMUP_ITERATIONS = 3;
|
||||
const DEFAULT_BENCHMARK_MAX_ITERATIONS = 10000;
|
||||
|
||||
uint benchmark_warmup_iterations @private = DEFAULT_BENCHMARK_WARMUP_ITERATIONS;
|
||||
uint benchmark_max_iterations @private = DEFAULT_BENCHMARK_MAX_ITERATIONS;
|
||||
|
||||
fn void set_benchmark_warmup_iterations(uint value) @builtin
|
||||
{
|
||||
benchmark_warmup_iterations = value;
|
||||
}
|
||||
|
||||
fn void set_benchmark_max_iterations(uint value) @builtin
|
||||
{
|
||||
assert(value > 0);
|
||||
benchmark_max_iterations = value;
|
||||
}
|
||||
|
||||
fn bool run_benchmarks(BenchmarkUnit[] benchmarks)
|
||||
{
|
||||
int benchmarks_passed = 0;
|
||||
int benchmark_count = benchmarks.len;
|
||||
usz max_name;
|
||||
|
||||
foreach (&unit : benchmarks)
|
||||
{
|
||||
if (max_name < unit.name.len) max_name = unit.name.len;
|
||||
}
|
||||
|
||||
usz len = max_name + 9;
|
||||
|
||||
DString name = dstring::temp_with_capacity(64);
|
||||
name.append_repeat('-', len / 2);
|
||||
name.append(" BENCHMARKS ");
|
||||
name.append_repeat('-', len - len / 2);
|
||||
|
||||
io::printn(name);
|
||||
|
||||
name.clear();
|
||||
|
||||
long sys_clock_started;
|
||||
long sys_clock_finished;
|
||||
long sys_clocks;
|
||||
Clock clock;
|
||||
anyfault err;
|
||||
|
||||
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());
|
||||
|
||||
for (uint i = 0; i < benchmark_warmup_iterations; i++)
|
||||
{
|
||||
err = @catch(unit.func()) @inline;
|
||||
@volatile_load(err);
|
||||
}
|
||||
|
||||
clock = std::time::clock::now();
|
||||
sys_clock_started = $$sysclock();
|
||||
|
||||
for (uint i = 0; i < benchmark_max_iterations; i++)
|
||||
{
|
||||
err = @catch(unit.func()) @inline;
|
||||
@volatile_load(err);
|
||||
}
|
||||
|
||||
sys_clock_finished = $$sysclock();
|
||||
NanoDuration nano_seconds = clock.mark();
|
||||
sys_clocks = sys_clock_finished - sys_clock_started;
|
||||
|
||||
if (err)
|
||||
{
|
||||
io::printfn("[failed] Failed due to: %s", err);
|
||||
continue;
|
||||
}
|
||||
|
||||
io::printfn("[ok] %.2f ns, %.2f CPU's clocks", (float)nano_seconds / benchmark_max_iterations, (float)sys_clocks / benchmark_max_iterations);
|
||||
benchmarks_passed++;
|
||||
}
|
||||
|
||||
io::printfn("\n%d benchmark%s run.\n", benchmark_count, benchmark_count > 1 ? "s" : "");
|
||||
io::printfn("Benchmarks Result: %s. %d passed, %d failed.",
|
||||
benchmarks_passed < benchmark_count ? "FAILED" : "ok",
|
||||
benchmarks_passed,
|
||||
benchmark_count - benchmarks_passed);
|
||||
|
||||
return benchmark_count == benchmarks_passed;
|
||||
}
|
||||
|
||||
fn bool default_benchmark_runner()
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
return run_benchmarks(benchmark_collection_create(allocator::temp()));
|
||||
};
|
||||
}
|
||||
|
||||
def TestFn = fn void!();
|
||||
|
||||
struct TestUnit
|
||||
{
|
||||
String name;
|
||||
TestFn func;
|
||||
}
|
||||
|
||||
fn TestUnit[] test_collection_create(Allocator allocator = allocator::heap())
|
||||
{
|
||||
TestFn[] fns = $$TEST_FNS;
|
||||
String[] names = $$TEST_NAMES;
|
||||
TestUnit[] tests = allocator::alloc_array(allocator, TestUnit, names.len);
|
||||
foreach (i, test : fns)
|
||||
{
|
||||
tests[i] = { names[i], fns[i] };
|
||||
}
|
||||
return tests;
|
||||
}
|
||||
|
||||
struct TestContext
|
||||
{
|
||||
JmpBuf buf;
|
||||
}
|
||||
|
||||
// Sort the tests by their name in ascending order.
|
||||
fn int cmp_test_unit(TestUnit a, TestUnit b)
|
||||
{
|
||||
usz an = a.name.len;
|
||||
usz bn = b.name.len;
|
||||
if (an > bn) @swap(a, b);
|
||||
foreach (i, ac : a.name)
|
||||
{
|
||||
char bc = b.name[i];
|
||||
if (ac != bc) return an > bn ? bc - ac : ac - bc;
|
||||
}
|
||||
return (int)(an - bn);
|
||||
}
|
||||
|
||||
TestContext* test_context @private;
|
||||
|
||||
fn void test_panic(String message, String file, String function, uint line)
|
||||
{
|
||||
io::printn("[error]");
|
||||
io::print("\n Error: ");
|
||||
io::print(message);
|
||||
io::printn();
|
||||
io::printfn(" - in %s %s:%s.\n", function, file, line);
|
||||
libc::longjmp(&test_context.buf, 1);
|
||||
}
|
||||
|
||||
fn bool run_tests(TestUnit[] tests)
|
||||
{
|
||||
usz max_name;
|
||||
foreach (&unit : tests)
|
||||
{
|
||||
if (max_name < unit.name.len) max_name = unit.name.len;
|
||||
}
|
||||
quicksort(tests, &cmp_test_unit);
|
||||
|
||||
TestContext context;
|
||||
test_context = &context;
|
||||
|
||||
PanicFn old_panic = builtin::panic;
|
||||
defer builtin::panic = old_panic;
|
||||
builtin::panic = &test_panic;
|
||||
int tests_passed = 0;
|
||||
int test_count = tests.len;
|
||||
DString name = dstring::temp_with_capacity(64);
|
||||
usz len = max_name + 9;
|
||||
name.append_repeat('-', len / 2);
|
||||
name.append(" TESTS ");
|
||||
name.append_repeat('-', len - len / 2);
|
||||
io::printn(name);
|
||||
name.clear();
|
||||
foreach(unit : tests)
|
||||
{
|
||||
defer name.clear();
|
||||
name.appendf("Testing %s ", unit.name);
|
||||
name.append_repeat('.', max_name - unit.name.len + 2);
|
||||
io::printf("%s ", name.str_view());
|
||||
(void)io::stdout().flush();
|
||||
if (libc::setjmp(&context.buf) == 0)
|
||||
{
|
||||
if (catch err = unit.func())
|
||||
{
|
||||
io::printfn("[failed] Failed due to: %s", err);
|
||||
continue;
|
||||
}
|
||||
io::printn("[ok]");
|
||||
tests_passed++;
|
||||
}
|
||||
}
|
||||
io::printfn("\n%d test%s run.\n", test_count, test_count > 1 ? "s" : "");
|
||||
io::printfn("Test Result: %s. %d passed, %d failed.",
|
||||
tests_passed < test_count ? "FAILED" : "ok", tests_passed, test_count - tests_passed);
|
||||
return test_count == tests_passed;
|
||||
}
|
||||
|
||||
fn bool default_test_runner()
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
return run_tests(test_collection_create(allocator::temp()));
|
||||
};
|
||||
}
|
||||
|
||||
module std::core::runtime @if(WASM_NOLIBC);
|
||||
|
||||
|
||||
174
lib/std/core/runtime_benchmark.c3
Normal file
174
lib/std/core/runtime_benchmark.c3
Normal file
@@ -0,0 +1,174 @@
|
||||
module std::core::runtime;
|
||||
import libc, std::time, std::io, std::sort;
|
||||
|
||||
def BenchmarkFn = fn void!() @if($$OLD_TEST);
|
||||
def BenchmarkFn = fn void() @if(!$$OLD_TEST);
|
||||
|
||||
struct BenchmarkUnit
|
||||
{
|
||||
String name;
|
||||
BenchmarkFn func;
|
||||
}
|
||||
|
||||
fn BenchmarkUnit[] benchmark_collection_create(Allocator allocator = allocator::heap())
|
||||
{
|
||||
BenchmarkFn[] fns = $$BENCHMARK_FNS;
|
||||
String[] names = $$BENCHMARK_NAMES;
|
||||
BenchmarkUnit[] benchmarks = allocator::alloc_array(allocator, BenchmarkUnit, names.len);
|
||||
foreach (i, benchmark : fns)
|
||||
{
|
||||
benchmarks[i] = { names[i], fns[i] };
|
||||
}
|
||||
return benchmarks;
|
||||
}
|
||||
|
||||
const DEFAULT_BENCHMARK_WARMUP_ITERATIONS = 3;
|
||||
const DEFAULT_BENCHMARK_MAX_ITERATIONS = 10000;
|
||||
|
||||
uint benchmark_warmup_iterations @private = DEFAULT_BENCHMARK_WARMUP_ITERATIONS;
|
||||
uint benchmark_max_iterations @private = DEFAULT_BENCHMARK_MAX_ITERATIONS;
|
||||
|
||||
fn void set_benchmark_warmup_iterations(uint value) @builtin
|
||||
{
|
||||
benchmark_warmup_iterations = value;
|
||||
}
|
||||
|
||||
fn void set_benchmark_max_iterations(uint value) @builtin
|
||||
{
|
||||
assert(value > 0);
|
||||
benchmark_max_iterations = value;
|
||||
}
|
||||
|
||||
fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @if($$OLD_TEST)
|
||||
{
|
||||
int benchmarks_passed = 0;
|
||||
int benchmark_count = benchmarks.len;
|
||||
usz max_name;
|
||||
|
||||
foreach (&unit : benchmarks)
|
||||
{
|
||||
if (max_name < unit.name.len) max_name = unit.name.len;
|
||||
}
|
||||
|
||||
usz len = max_name + 9;
|
||||
|
||||
DString name = dstring::temp_with_capacity(64);
|
||||
name.append_repeat('-', len / 2);
|
||||
name.append(" BENCHMARKS ");
|
||||
name.append_repeat('-', len - len / 2);
|
||||
|
||||
io::printn(name);
|
||||
|
||||
name.clear();
|
||||
|
||||
long sys_clock_started;
|
||||
long sys_clock_finished;
|
||||
long sys_clocks;
|
||||
Clock clock;
|
||||
anyfault err;
|
||||
|
||||
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());
|
||||
|
||||
for (uint i = 0; i < benchmark_warmup_iterations; i++)
|
||||
{
|
||||
err = @catch(unit.func()) @inline;
|
||||
@volatile_load(err);
|
||||
}
|
||||
|
||||
clock = std::time::clock::now();
|
||||
sys_clock_started = $$sysclock();
|
||||
|
||||
for (uint i = 0; i < benchmark_max_iterations; i++)
|
||||
{
|
||||
err = @catch(unit.func()) @inline;
|
||||
@volatile_load(err);
|
||||
}
|
||||
|
||||
sys_clock_finished = $$sysclock();
|
||||
NanoDuration nano_seconds = clock.mark();
|
||||
sys_clocks = sys_clock_finished - sys_clock_started;
|
||||
|
||||
if (err)
|
||||
{
|
||||
io::printfn("[failed] Failed due to: %s", err);
|
||||
continue;
|
||||
}
|
||||
|
||||
io::printfn("[ok] %.2f ns, %.2f CPU's clocks", (float)nano_seconds / benchmark_max_iterations, (float)sys_clocks / benchmark_max_iterations);
|
||||
benchmarks_passed++;
|
||||
}
|
||||
|
||||
io::printfn("\n%d benchmark%s run.\n", benchmark_count, benchmark_count > 1 ? "s" : "");
|
||||
io::printfn("Benchmarks Result: %s. %d passed, %d failed.",
|
||||
benchmarks_passed < benchmark_count ? "FAILED" : "ok",
|
||||
benchmarks_passed,
|
||||
benchmark_count - benchmarks_passed);
|
||||
|
||||
return benchmark_count == benchmarks_passed;
|
||||
}
|
||||
|
||||
fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @if(!$$OLD_TEST)
|
||||
{
|
||||
usz max_name;
|
||||
|
||||
foreach (&unit : benchmarks)
|
||||
{
|
||||
if (max_name < unit.name.len) max_name = unit.name.len;
|
||||
}
|
||||
|
||||
usz len = max_name + 9;
|
||||
|
||||
DString name = dstring::temp_with_capacity(64);
|
||||
name.append_repeat('-', len / 2);
|
||||
name.append(" BENCHMARKS ");
|
||||
name.append_repeat('-', len - len / 2);
|
||||
|
||||
io::printn(name);
|
||||
|
||||
name.clear();
|
||||
|
||||
long sys_clock_started;
|
||||
long sys_clock_finished;
|
||||
long sys_clocks;
|
||||
Clock clock;
|
||||
|
||||
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());
|
||||
|
||||
for (uint i = 0; i < benchmark_warmup_iterations; i++)
|
||||
{
|
||||
unit.func() @inline;
|
||||
}
|
||||
|
||||
clock = std::time::clock::now();
|
||||
sys_clock_started = $$sysclock();
|
||||
|
||||
for (uint i = 0; i < benchmark_max_iterations; i++)
|
||||
{
|
||||
unit.func() @inline;
|
||||
}
|
||||
|
||||
sys_clock_finished = $$sysclock();
|
||||
NanoDuration nano_seconds = clock.mark();
|
||||
sys_clocks = sys_clock_finished - sys_clock_started;
|
||||
|
||||
io::printfn("[COMPLETE] %.2f ns, %.2f CPU's clocks", (float)nano_seconds / benchmark_max_iterations, (float)sys_clocks / benchmark_max_iterations);
|
||||
}
|
||||
|
||||
io::printfn("\n%d benchmark%s run.\n", benchmarks.len, benchmarks.len > 1 ? "s" : "");
|
||||
return true;
|
||||
}
|
||||
|
||||
fn bool default_benchmark_runner(String[] args) => @pool()
|
||||
{
|
||||
return run_benchmarks(benchmark_collection_create(allocator::temp()));
|
||||
}
|
||||
322
lib/std/core/runtime_test.c3
Normal file
322
lib/std/core/runtime_test.c3
Normal file
@@ -0,0 +1,322 @@
|
||||
// Copyright (c) 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::core::runtime;
|
||||
import std::core::test @public;
|
||||
import libc, std::time, std::io, std::sort;
|
||||
import std::os::env;
|
||||
|
||||
def TestFn = fn void!() @if($$OLD_TEST);
|
||||
def TestFn = fn void() @if(!$$OLD_TEST);
|
||||
|
||||
TestContext* test_context @private;
|
||||
|
||||
struct TestContext
|
||||
{
|
||||
JmpBuf buf;
|
||||
// Allows filtering test cased or modules by substring, e.g. 'foo::', 'foo::test_add'
|
||||
String test_filter;
|
||||
// Triggers debugger breakpoint when assert or test:: checks failed
|
||||
bool breakpoint_on_assert;
|
||||
|
||||
// internal state
|
||||
bool assert_print_backtrace;
|
||||
bool has_ansi_codes;
|
||||
bool is_in_panic;
|
||||
String current_test_name;
|
||||
TestFn setup_fn;
|
||||
TestFn teardown_fn;
|
||||
|
||||
char* error_buffer;
|
||||
usz error_buffer_capacity;
|
||||
File fake_stdout;
|
||||
File orig_stdout;
|
||||
File orig_stderr;
|
||||
}
|
||||
|
||||
struct TestUnit
|
||||
{
|
||||
String name;
|
||||
TestFn func;
|
||||
}
|
||||
|
||||
fn TestUnit[] test_collection_create(Allocator allocator = allocator::heap())
|
||||
{
|
||||
TestFn[] fns = $$TEST_FNS;
|
||||
String[] names = $$TEST_NAMES;
|
||||
TestUnit[] tests = allocator::alloc_array(allocator, TestUnit, names.len);
|
||||
foreach (i, test : fns)
|
||||
{
|
||||
tests[i] = { names[i], fns[i] };
|
||||
}
|
||||
return tests;
|
||||
}
|
||||
|
||||
// Sort the tests by their name in ascending order.
|
||||
fn int cmp_test_unit(TestUnit a, TestUnit b)
|
||||
{
|
||||
usz an = a.name.len;
|
||||
usz bn = b.name.len;
|
||||
if (an > bn) @swap(a, b);
|
||||
foreach (i, ac : a.name)
|
||||
{
|
||||
char bc = b.name[i];
|
||||
if (ac != bc) return an > bn ? bc - ac : ac - bc;
|
||||
}
|
||||
return (int)(an - bn);
|
||||
}
|
||||
|
||||
fn bool terminal_has_ansi_codes() @local => @pool()
|
||||
{
|
||||
|
||||
if (try v = env::get_var_temp("TERM"))
|
||||
{
|
||||
if (v.contains("xterm") || v.contains("vt100") || v.contains("screen")) return true;
|
||||
}
|
||||
$if env::WIN32 || env::NO_LIBC:
|
||||
return false;
|
||||
$else
|
||||
return io::stdout().isatty();
|
||||
$endif
|
||||
}
|
||||
|
||||
fn void test_panic(String message, String file, String function, uint line) @local
|
||||
{
|
||||
if (test_context.is_in_panic) return;
|
||||
test_context.is_in_panic = true;
|
||||
|
||||
unmute_output(true);
|
||||
(void)io::stdout().flush();
|
||||
if (test_context.assert_print_backtrace)
|
||||
{
|
||||
$if env::NATIVE_STACKTRACE:
|
||||
builtin::print_backtrace(message, 0);
|
||||
$endif
|
||||
}
|
||||
io::printf("\nTest failed ^^^ ( %s:%s ) %s\n", file, line, message);
|
||||
test_context.assert_print_backtrace = true;
|
||||
|
||||
if (test_context.breakpoint_on_assert)
|
||||
{
|
||||
breakpoint();
|
||||
}
|
||||
|
||||
if (test_context.teardown_fn)
|
||||
{
|
||||
test_context.teardown_fn();
|
||||
}
|
||||
|
||||
test_context.is_in_panic = false;
|
||||
libc::longjmp(&test_context.buf, 1);
|
||||
}
|
||||
|
||||
fn void mute_output() @local
|
||||
{
|
||||
if (!test_context.fake_stdout.file) return;
|
||||
assert(!test_context.orig_stderr.file);
|
||||
assert(!test_context.orig_stdout.file);
|
||||
|
||||
File* stdout = io::stdout();
|
||||
File* stderr = io::stderr();
|
||||
|
||||
test_context.orig_stderr = *stderr;
|
||||
test_context.orig_stdout = *stdout;
|
||||
|
||||
*stderr = test_context.fake_stdout;
|
||||
*stdout = test_context.fake_stdout;
|
||||
|
||||
(void)test_context.fake_stdout.seek(0, Seek.SET)!!;
|
||||
}
|
||||
|
||||
fn void unmute_output(bool has_error) @local
|
||||
{
|
||||
if (!test_context.fake_stdout.file)
|
||||
{
|
||||
return;
|
||||
}
|
||||
assert(test_context.orig_stderr.file);
|
||||
assert(test_context.orig_stdout.file);
|
||||
|
||||
File* stdout = io::stdout();
|
||||
File* stderr = io::stderr();
|
||||
|
||||
*stderr = test_context.orig_stderr;
|
||||
*stdout = test_context.orig_stdout;
|
||||
test_context.orig_stderr.file = null;
|
||||
test_context.orig_stdout.file = null;
|
||||
|
||||
usz log_size = test_context.fake_stdout.seek(0, Seek.CURSOR)!!;
|
||||
if (has_error)
|
||||
{
|
||||
io::printn(test_context.has_ansi_codes ? "[\e[0;31mFAIL\e[0m]" : "[FAIL]");
|
||||
}
|
||||
|
||||
if (has_error && log_size > 0)
|
||||
{
|
||||
test_context.fake_stdout.write_byte('\n')!!;
|
||||
test_context.fake_stdout.write_byte('\0')!!;
|
||||
(void)test_context.fake_stdout.seek(0, Seek.SET)!!;
|
||||
|
||||
io::printfn("\n========== TEST LOG ============");
|
||||
io::printfn("%s\n", test_context.current_test_name);
|
||||
while (try c = test_context.fake_stdout.read_byte())
|
||||
{
|
||||
if (@unlikely(c == '\0'))
|
||||
{
|
||||
// ignore junk from previous tests
|
||||
break;
|
||||
}
|
||||
libc::putchar(c);
|
||||
}
|
||||
io::printf("========== TEST END ============");
|
||||
}
|
||||
(void)stdout.flush();
|
||||
}
|
||||
|
||||
fn bool run_tests(String[] args, TestUnit[] tests) @private
|
||||
{
|
||||
usz max_name;
|
||||
bool sort_tests = true;
|
||||
foreach (&unit : tests)
|
||||
{
|
||||
if (max_name < unit.name.len) max_name = unit.name.len;
|
||||
}
|
||||
TestContext context =
|
||||
{
|
||||
.assert_print_backtrace = true,
|
||||
.breakpoint_on_assert = false,
|
||||
.test_filter = "",
|
||||
.has_ansi_codes = terminal_has_ansi_codes(),
|
||||
};
|
||||
for (int i = 1; i < args.len; i++)
|
||||
{
|
||||
switch (args[i])
|
||||
{
|
||||
case "breakpoint":
|
||||
context.breakpoint_on_assert = true;
|
||||
case "nosort":
|
||||
sort_tests = false;
|
||||
case "noansi":
|
||||
context.has_ansi_codes = false;
|
||||
case "useansi":
|
||||
context.has_ansi_codes = true;
|
||||
case "filter":
|
||||
if (i == args.len - 1)
|
||||
{
|
||||
io::printn("Invalid arguments to test runner.");
|
||||
return false;
|
||||
}
|
||||
context.test_filter = args[i + 1];
|
||||
i++;
|
||||
default:
|
||||
io::printfn("Unknown argument: %s", args[i]);
|
||||
}
|
||||
}
|
||||
test_context = &context;
|
||||
|
||||
if (sort_tests)
|
||||
{
|
||||
quicksort(tests, &cmp_test_unit);
|
||||
}
|
||||
|
||||
// Buffer for hijacking the output
|
||||
$if (!env::NO_LIBC):
|
||||
test_context.fake_stdout.file = libc::tmpfile();
|
||||
$endif
|
||||
if (test_context.fake_stdout.file == null)
|
||||
{
|
||||
io::print("Failed to hijack stdout, tests will print everything");
|
||||
}
|
||||
|
||||
PanicFn old_panic = builtin::panic;
|
||||
defer builtin::panic = old_panic;
|
||||
builtin::panic = &test_panic;
|
||||
int tests_passed = 0;
|
||||
int tests_skipped = 0;
|
||||
int test_count = tests.len;
|
||||
DString name = dstring::temp_with_capacity(64);
|
||||
usz len = max_name + 9;
|
||||
name.append_repeat('-', len / 2);
|
||||
name.append(" TESTS ");
|
||||
name.append_repeat('-', len - len / 2);
|
||||
io::printn(name);
|
||||
name.clear();
|
||||
foreach(unit : tests)
|
||||
{
|
||||
if (test_context.test_filter && !unit.name.contains(test_context.test_filter))
|
||||
{
|
||||
tests_skipped++;
|
||||
continue;
|
||||
}
|
||||
test_context.setup_fn = null;
|
||||
test_context.teardown_fn = null;
|
||||
test_context.current_test_name = unit.name;
|
||||
|
||||
defer name.clear();
|
||||
name.appendf("Testing %s ", unit.name);
|
||||
name.append_repeat('.', max_name - unit.name.len + 2);
|
||||
io::printf("%s ", name.str_view());
|
||||
TrackingAllocator mem;
|
||||
mem.init(allocator::heap());
|
||||
if (libc::setjmp(&context.buf) == 0)
|
||||
{
|
||||
mute_output();
|
||||
mem.clear();
|
||||
mem::@scoped(&mem)
|
||||
{
|
||||
$if(!$$OLD_TEST):
|
||||
unit.func();
|
||||
$else
|
||||
if (catch err = unit.func())
|
||||
{
|
||||
io::printf("[FAIL] Failed due to: %s", err);
|
||||
continue;
|
||||
}
|
||||
$endif
|
||||
};
|
||||
unmute_output(false); // all good, discard output
|
||||
if (mem.has_leaks())
|
||||
{
|
||||
io::print(test_context.has_ansi_codes ? "[\e[0;31mFAIL\e[0m]" : "[FAIL]");
|
||||
io::printn(" LEAKS DETECTED!");
|
||||
mem.print_report();
|
||||
}
|
||||
else
|
||||
{
|
||||
io::printfn(test_context.has_ansi_codes ? "[\e[0;32mPASS\e[0m]" : "[PASS]");
|
||||
tests_passed++;
|
||||
}
|
||||
if (test_context.teardown_fn)
|
||||
{
|
||||
test_context.teardown_fn();
|
||||
}
|
||||
}
|
||||
mem.free();
|
||||
}
|
||||
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: ",
|
||||
test_context.has_ansi_codes ? (n_failed ? "\e[0;31m" : "\e[0;32m") : "",
|
||||
n_failed ? "FAILED" : "PASSED",
|
||||
test_context.has_ansi_codes ? "\e[0m" : "",
|
||||
);
|
||||
|
||||
io::printfn("%d passed, %d failed, %d skipped.",
|
||||
tests_passed,
|
||||
n_failed,
|
||||
tests_skipped);
|
||||
|
||||
// cleanup fake_stdout file
|
||||
if (test_context.fake_stdout.file) libc::fclose(test_context.fake_stdout.file);
|
||||
test_context.fake_stdout.file = null;
|
||||
|
||||
return n_failed == 0;
|
||||
}
|
||||
|
||||
fn bool default_test_runner(String[] args) => @pool()
|
||||
{
|
||||
assert(test_context == null, "test suite is already running");
|
||||
return run_tests(args, test_collection_create(allocator::temp()));
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ macro bool address_is_poisoned(void* addr)
|
||||
macro void* region_is_poisoned(void* beg, usz size)
|
||||
{
|
||||
$if env::ADDRESS_SANITIZER:
|
||||
return __asan_region_is_poisoned(addr);
|
||||
return __asan_region_is_poisoned(beg, size);
|
||||
$else
|
||||
return null;
|
||||
$endif
|
||||
|
||||
@@ -52,14 +52,11 @@ fn ZString tformat_zstr(String fmt, args...)
|
||||
@param [inout] allocator `The allocator to use`
|
||||
@param [in] fmt `The formatting string`
|
||||
*>
|
||||
fn String format(String fmt, args..., Allocator allocator)
|
||||
fn String format(String fmt, args..., Allocator allocator) => @pool(allocator)
|
||||
{
|
||||
@pool(allocator)
|
||||
{
|
||||
DString str = dstring::temp_with_capacity(fmt.len + args.len * 8);
|
||||
str.appendf(fmt, ...args);
|
||||
return str.copy_str(allocator);
|
||||
};
|
||||
DString str = dstring::temp_with_capacity(fmt.len + args.len * 8);
|
||||
str.appendf(fmt, ...args);
|
||||
return str.copy_str(allocator);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -87,14 +84,11 @@ fn String tformat(String fmt, args...)
|
||||
@param [in] fmt `The formatting string`
|
||||
@param [inout] allocator `The allocator to use`
|
||||
*>
|
||||
fn ZString new_format_zstr(String fmt, args..., Allocator allocator = allocator::heap())
|
||||
fn ZString new_format_zstr(String fmt, args..., Allocator allocator = allocator::heap()) => @pool(allocator)
|
||||
{
|
||||
@pool(allocator)
|
||||
{
|
||||
DString str = dstring::temp_with_capacity(fmt.len + args.len * 8);
|
||||
str.appendf(fmt, ...args);
|
||||
return str.copy_zstr(allocator);
|
||||
};
|
||||
DString str = dstring::temp_with_capacity(fmt.len + args.len * 8);
|
||||
str.appendf(fmt, ...args);
|
||||
return str.copy_zstr(allocator);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -145,14 +139,40 @@ fn String join_new(String[] s, String joiner, Allocator allocator = allocator::h
|
||||
@return `a substring of the string passed in`
|
||||
*>
|
||||
fn String String.trim(string, String to_trim = "\t\n\r ")
|
||||
{
|
||||
return string.trim_left(to_trim).trim_right(to_trim);
|
||||
}
|
||||
|
||||
<*
|
||||
Remove characters from the front of a string.
|
||||
|
||||
@param [in] string `The string to trim`
|
||||
@param [in] to_trim `The set of characters to trim, defaults to whitespace`
|
||||
@pure
|
||||
@return `a substring of the string passed in`
|
||||
*>
|
||||
fn String String.trim_left(string, String to_trim = "\t\n\r ")
|
||||
{
|
||||
usz start = 0;
|
||||
usz len = string.len;
|
||||
while (start < len && char_in_set(string[start], to_trim)) start++;
|
||||
if (start == len) return string[:0];
|
||||
usz end = len - 1;
|
||||
while (end > start && char_in_set(string[end], to_trim)) end--;
|
||||
return string[start..end];
|
||||
return string[start..];
|
||||
}
|
||||
|
||||
<*
|
||||
Remove characters from the end of a string.
|
||||
|
||||
@param [in] string `The string to trim`
|
||||
@param [in] to_trim `The set of characters to trim, defaults to whitespace`
|
||||
@pure
|
||||
@return `a substring of the string passed in`
|
||||
*>
|
||||
fn String String.trim_right(string, String to_trim = "\t\n\r ")
|
||||
{
|
||||
usz len = string.len;
|
||||
while (len > 0 && char_in_set(string[len - 1], to_trim)) len--;
|
||||
return string[:len];
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -219,12 +239,14 @@ fn String String.strip_end(string, String needle)
|
||||
|
||||
@param [in] s
|
||||
@param [in] needle
|
||||
@param [&inout] allocator "The allocator to use for the String[]"
|
||||
@param max "Max number of elements, 0 means no limit, defaults to 0"
|
||||
@param skip_empty "True to skip empty elements"
|
||||
@param [&inout] allocator "The allocator to use for the String[]"
|
||||
|
||||
@require needle.len > 0 "The needle must be at least 1 character long"
|
||||
@ensure return.len > 0
|
||||
*>
|
||||
fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = allocator::heap())
|
||||
fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = allocator::heap(), bool skip_empty = false)
|
||||
{
|
||||
usz capacity = 16;
|
||||
usz i = 0;
|
||||
@@ -244,6 +266,11 @@ fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = al
|
||||
res = s;
|
||||
no_more = true;
|
||||
}
|
||||
if (!res.len && skip_empty)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == capacity)
|
||||
{
|
||||
capacity *= 2;
|
||||
@@ -261,10 +288,11 @@ fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = al
|
||||
@param [in] s
|
||||
@param [in] needle
|
||||
@param max "Max number of elements, 0 means no limit, defaults to 0"
|
||||
@param skip_empty "True to skip empty elements"
|
||||
@require needle.len > 0 "The needle must be at least 1 character long"
|
||||
@ensure return.len > 0
|
||||
*>
|
||||
fn String[] String.new_split(s, String needle, usz max = 0) => s.split(needle, max, allocator::heap()) @inline;
|
||||
fn String[] String.new_split(s, String needle, usz max = 0, bool skip_empty) => s.split(needle, max, allocator::heap(), skip_empty) @inline;
|
||||
|
||||
<*
|
||||
This function is identical to String.split, but implicitly uses the
|
||||
@@ -273,8 +301,54 @@ fn String[] String.new_split(s, String needle, usz max = 0) => s.split(needle, m
|
||||
@param [in] s
|
||||
@param [in] needle
|
||||
@param max "Max number of elements, 0 means no limit, defaults to 0"
|
||||
@param skip_empty "True to skip empty elements"
|
||||
*>
|
||||
fn String[] String.tsplit(s, String needle, usz max = 0) => s.split(needle, max, allocator::temp()) @inline;
|
||||
fn String[] String.tsplit(s, String needle, usz max = 0, bool skip_empty = false) => s.split(needle, max, allocator::temp(), skip_empty) @inline;
|
||||
|
||||
fault SplitResult { BUFFER_EXCEEDED }
|
||||
|
||||
<*
|
||||
Split a string into parts, e.g "a|b|c" split with "|" yields { "a", "b", "c" }
|
||||
|
||||
@param [in] s
|
||||
@param [in] needle
|
||||
@param [inout] buffer
|
||||
@param max "Max number of elements, 0 means no limit, defaults to 0"
|
||||
@require needle.len > 0 "The needle must be at least 1 character long"
|
||||
@ensure return.len > 0
|
||||
@return! SplitResult.BUFFER_EXCEEDED `If there are more elements than would fit the buffer`
|
||||
*>
|
||||
fn String[]! String.split_to_buffer(s, String needle, String[] buffer, usz max = 0, bool skip_empty = false)
|
||||
{
|
||||
usz max_capacity = buffer.len;
|
||||
usz i = 0;
|
||||
bool no_more = false;
|
||||
while (!no_more)
|
||||
{
|
||||
usz! index = i == max - 1 ? SearchResult.MISSING? : s.index_of(needle);
|
||||
String res @noinit;
|
||||
if (try index)
|
||||
{
|
||||
res = s[:index];
|
||||
s = s[index + needle.len..];
|
||||
}
|
||||
else
|
||||
{
|
||||
res = s;
|
||||
no_more = true;
|
||||
}
|
||||
if (!res.len && skip_empty)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (i == max_capacity)
|
||||
{
|
||||
return SplitResult.BUFFER_EXCEEDED?;
|
||||
}
|
||||
buffer[i++] = res;
|
||||
}
|
||||
return buffer[:i];
|
||||
}
|
||||
|
||||
<*
|
||||
Check if a substring is found in the string.
|
||||
@@ -308,6 +382,29 @@ fn usz! String.index_of_char(s, char needle)
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
<*
|
||||
Find the index of the first incidence of a one of the chars.
|
||||
|
||||
@param [in] s
|
||||
@param [in] needle "The characters to look for"
|
||||
@pure
|
||||
@ensure return < s.len
|
||||
@return "the index of the needle"
|
||||
@return! SearchResult.MISSING "if the needle cannot be found"
|
||||
*>
|
||||
fn usz! String.index_of_chars(String s, char[] needle)
|
||||
{
|
||||
foreach (i, c : s)
|
||||
{
|
||||
foreach (j, pin : needle)
|
||||
{
|
||||
if (c == pin) return i;
|
||||
}
|
||||
}
|
||||
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
<*
|
||||
Find the index of the first incidence of a character.
|
||||
|
||||
@@ -449,6 +546,11 @@ fn String String.tconcat(s1, String s2) => s1.concat(s2, allocator::temp());
|
||||
|
||||
fn ZString String.zstr_tcopy(s) => s.zstr_copy(allocator::temp()) @inline;
|
||||
|
||||
<*
|
||||
Copy this string, by duplicating the string, always adding a zero byte
|
||||
sentinel, so that it safely can be converted to a ZString by a
|
||||
cast.
|
||||
*>
|
||||
fn String String.copy(s, Allocator allocator = allocator::heap())
|
||||
{
|
||||
usz len = s.len;
|
||||
@@ -460,7 +562,7 @@ fn String String.copy(s, Allocator allocator = allocator::heap())
|
||||
|
||||
fn void String.free(&s, Allocator allocator = allocator::heap())
|
||||
{
|
||||
if (!s.len) return;
|
||||
if (!s.ptr) return;
|
||||
allocator::free(allocator, s.ptr);
|
||||
*s = "";
|
||||
}
|
||||
@@ -719,7 +821,12 @@ fn float! String.to_float(s) => s.to_real(float);
|
||||
|
||||
fn Splitter String.splitter(self, String split)
|
||||
{
|
||||
return Splitter { self, split, 0 };
|
||||
return { .string = self, .split = split };
|
||||
}
|
||||
|
||||
fn Splitter String.tokenize(self, String split)
|
||||
{
|
||||
return { .string = self, .split = split, .tokenize = true };
|
||||
}
|
||||
|
||||
struct Splitter
|
||||
@@ -727,6 +834,8 @@ struct Splitter
|
||||
String string;
|
||||
String split;
|
||||
usz current;
|
||||
bool tokenize;
|
||||
int last_index;
|
||||
}
|
||||
|
||||
fn void Splitter.reset(&self)
|
||||
@@ -736,18 +845,22 @@ fn void Splitter.reset(&self)
|
||||
|
||||
fn String! Splitter.next(&self)
|
||||
{
|
||||
usz len = self.string.len;
|
||||
usz current = self.current;
|
||||
if (current >= len) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
String remaining = self.string[current..];
|
||||
usz! next = remaining.index_of(self.split);
|
||||
if (try next)
|
||||
while (true)
|
||||
{
|
||||
defer self.current = current + next + self.split.len;
|
||||
return remaining[:next];
|
||||
usz len = self.string.len;
|
||||
usz current = self.current;
|
||||
if (current >= len) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
String remaining = self.string[current..];
|
||||
usz! next = remaining.index_of(self.split);
|
||||
if (try next)
|
||||
{
|
||||
self.current = current + next + self.split.len;
|
||||
if (!next && self.tokenize) continue;
|
||||
return remaining[:next];
|
||||
}
|
||||
self.current = len;
|
||||
return remaining;
|
||||
}
|
||||
self.current = len;
|
||||
return remaining;
|
||||
}
|
||||
|
||||
macro String new_struct_to_str(x, Allocator allocator = allocator::heap())
|
||||
@@ -757,8 +870,8 @@ macro String new_struct_to_str(x, Allocator allocator = allocator::heap())
|
||||
{
|
||||
s.new_init(allocator: mem);
|
||||
io::fprint(&s, x)!!;
|
||||
return s.copy_str(allocator);
|
||||
};
|
||||
return s.copy_str(allocator);
|
||||
}
|
||||
|
||||
macro String temp_struct_to_str(x) => new_struct_to_str(x, allocator::temp());
|
||||
macro String temp_struct_to_str(x) => new_struct_to_str(x, allocator::temp());
|
||||
|
||||
@@ -40,7 +40,7 @@ macro double! decfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
const uint[2] TH = B1B_MAX;
|
||||
int emax = - $emin - $bits + 3;
|
||||
|
||||
const int[*] P10S = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
|
||||
const int[?] P10S = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
|
||||
usz index;
|
||||
bool got_digit = chars[0] == '0';
|
||||
bool got_rad;
|
||||
|
||||
223
lib/std/core/test.c3
Normal file
223
lib/std/core/test.c3
Normal file
@@ -0,0 +1,223 @@
|
||||
<*
|
||||
Unit test module
|
||||
|
||||
This module provides a toolset of macros for running unit test checks
|
||||
|
||||
Example:
|
||||
```c3
|
||||
module sample::m;
|
||||
import std::io;
|
||||
|
||||
fault MathError
|
||||
{
|
||||
DIVISION_BY_ZERO
|
||||
}
|
||||
|
||||
fn double! divide(int a, int b)
|
||||
{
|
||||
if (b == 0) return MathError.DIVISION_BY_ZERO?;
|
||||
return (double)(a) / (double)(b);
|
||||
}
|
||||
|
||||
fn void! test_div() @test
|
||||
{
|
||||
test::eq(2, divide(6, 3)!);
|
||||
test::ne(1, 2);
|
||||
test::ge(3, 3);
|
||||
test::gt(2, divide(3, 3)!);
|
||||
test::lt(2, 3);
|
||||
test::le(2, 3);
|
||||
test::eq_approx(m::divide(1, 3)!, 0.333, places: 3);
|
||||
test::@check(2 == 2, "divide: %d", divide(6, 3)!);
|
||||
test::@error(m::divide(3, 0), MathError.DIVISION_BY_ZERO);
|
||||
}
|
||||
|
||||
```
|
||||
*>
|
||||
// Copyright (c) 2025 Alex Veden <i@alexveden.com>. 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::core::test;
|
||||
import std::core::runtime @public;
|
||||
import std::math, std::io, libc;
|
||||
|
||||
<*
|
||||
Initializes test case context.
|
||||
|
||||
@param setup_fn `initializer function for test case`
|
||||
@param teardown_fn `cleanup function for test context (may be null)`
|
||||
|
||||
@require runtime::test_context != null "Only allowed in @test functions"
|
||||
@require setup_fn != null "setup_fn must always be set"
|
||||
*>
|
||||
macro @setup(TestFn setup_fn, TestFn teardown_fn = null)
|
||||
{
|
||||
runtime::test_context.setup_fn = setup_fn;
|
||||
runtime::test_context.teardown_fn = teardown_fn;
|
||||
runtime::test_context.setup_fn();
|
||||
}
|
||||
|
||||
<*
|
||||
Checks condition and fails assertion if not true
|
||||
|
||||
@param #condition `any boolean condition, will be expanded by text`
|
||||
@param format `printf compatible format`
|
||||
@param args `vargs for format`
|
||||
@require runtime::test_context != null "Only allowed in @test functions"
|
||||
*>
|
||||
macro @check(#condition, String format = "", args...)
|
||||
{
|
||||
if (!#condition)
|
||||
{
|
||||
@stack_mem(512; Allocator allocator)
|
||||
{
|
||||
DString s;
|
||||
s.new_init(allocator: allocator);
|
||||
s.appendf("check `%s` failed. ", $stringify(#condition));
|
||||
s.appendf(format, ...args);
|
||||
print_panicf(s.str_view());
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Check if function returns specific error
|
||||
|
||||
@param #funcresult `result of function execution`
|
||||
@param error_expected `expected error of function execution`
|
||||
@require runtime::test_context != null "Only allowed in @test functions"
|
||||
*>
|
||||
macro @error(#funcresult, anyfault error_expected)
|
||||
{
|
||||
if (catch err = #funcresult)
|
||||
{
|
||||
if (err != error_expected)
|
||||
{
|
||||
print_panicf("`%s` expected to return error [%s], got [%s]",
|
||||
$stringify(#funcresult), error_expected, err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
print_panicf("`%s` error [%s] was not returned.", $stringify(#funcresult), error_expected);
|
||||
}
|
||||
|
||||
<*
|
||||
Check if left == right
|
||||
|
||||
@param left `left argument of any comparable type`
|
||||
@param right `right argument of any comparable type`
|
||||
@require runtime::test_context != null "Only allowed in @test functions"
|
||||
*>
|
||||
macro eq(left, right)
|
||||
{
|
||||
if (!equals(left, right))
|
||||
{
|
||||
print_panicf("`%s` != `%s`", left, right);
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Check left floating point value is approximately equals to right value
|
||||
|
||||
@param places `number of decimal places to compare (default: 7)`
|
||||
@param delta `minimal allowed difference (overrides places parameter)`
|
||||
@param equal_nan `allows comparing nan values, if left and right both nans result is ok`
|
||||
|
||||
@require places > 0, places <= 20 "too many decimal places"
|
||||
@require delta >= 0, delta <= 1 "delta must be a small number"
|
||||
@require runtime::test_context != null "Only allowed in @test functions"
|
||||
*>
|
||||
macro void eq_approx(double left, double right, uint places = 7, double delta = 0, bool equal_nan = true)
|
||||
{
|
||||
double diff = left - right;
|
||||
double eps = delta;
|
||||
if (eps == 0) eps = 1.0 / math::pow(10.0, places);
|
||||
|
||||
if (!math::is_approx(left, right, eps))
|
||||
{
|
||||
if (equal_nan && math::is_nan(left) && math::is_nan(right)) return;
|
||||
print_panicf("Not almost equal: `%s` !~~ `%s` delta=%e diff: %e", left, right, eps, diff);
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Check if left != right
|
||||
|
||||
@param left `left argument of any comparable type`
|
||||
@param right `right argument of any comparable type`
|
||||
@require runtime::test_context != null "Only allowed in @test functions"
|
||||
*>
|
||||
macro void ne(left, right)
|
||||
{
|
||||
if (equals(left, right))
|
||||
{
|
||||
print_panicf("`%s` == `%s`", left, right);
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Check if left > right
|
||||
|
||||
@param left `left argument of any comparable type`
|
||||
@param right `right argument of any comparable type`
|
||||
@require runtime::test_context != null "Only allowed in @test functions"
|
||||
*>
|
||||
macro gt(left, right)
|
||||
{
|
||||
if (!builtin::greater(left, right))
|
||||
{
|
||||
print_panicf("`%s` <= `%s`", left, right);
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Check if left >= right
|
||||
|
||||
@param left `left argument of any comparable type`
|
||||
@param right `right argument of any comparable type`
|
||||
@require runtime::test_context != null "Only allowed in @test functions"
|
||||
*>
|
||||
macro ge(left, right)
|
||||
{
|
||||
if (!builtin::greater_eq(left, right))
|
||||
{
|
||||
print_panicf("`%s` < `%s`", left, right);
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Check if left < right
|
||||
|
||||
@param left `left argument of any comparable type`
|
||||
@param right `right argument of any comparable type`
|
||||
@require runtime::test_context != null "Only allowed in @test functions"
|
||||
*>
|
||||
macro lt(left, right)
|
||||
{
|
||||
if (!builtin::less(left, right))
|
||||
{
|
||||
print_panicf("`%s` >= `%s`", left, right);
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Check if left <= right
|
||||
|
||||
@param left `left argument of any comparable type`
|
||||
@param right `right argument of any comparable type`
|
||||
@require runtime::test_context != null "Only allowed in @test functions"
|
||||
*>
|
||||
macro le(left, right)
|
||||
{
|
||||
if (!builtin::less_eq(left, right))
|
||||
{
|
||||
print_panicf("`%s` > `%s`", left, right);
|
||||
}
|
||||
}
|
||||
|
||||
macro void print_panicf(format, ...) @local
|
||||
{
|
||||
runtime::test_context.assert_print_backtrace = false;
|
||||
builtin::panicf(format, $$FILE, $$FUNC, $$LINE, $vasplat);
|
||||
}
|
||||
|
||||
@@ -8,18 +8,24 @@ fault ConversionResult
|
||||
VALUE_OUT_OF_RANGE,
|
||||
VALUE_OUT_OF_UNSIGNED_RANGE,
|
||||
}
|
||||
|
||||
<*
|
||||
@require $Type.kindof.is_int() || $Type.kindof == TypeKind.ENUM "Argument was not an integer"
|
||||
@require $Type.kindof.is_int() "Type was not an integer"
|
||||
@require v.type.kindof == ENUM "Value was not an enum"
|
||||
*>
|
||||
macro any_to_enum_ordinal(any v, $Type)
|
||||
{
|
||||
return any_to_int(v.as_inner(), $Type);
|
||||
}
|
||||
|
||||
<*
|
||||
@require $Type.kindof.is_int() "Type was not an integer"
|
||||
@require v.type.kindof.is_int() "Value was not an integer"
|
||||
*>
|
||||
macro any_to_int(any v, $Type)
|
||||
{
|
||||
typeid any_type = v.type;
|
||||
TypeKind kind = any_type.kindof;
|
||||
if (kind == TypeKind.ENUM)
|
||||
{
|
||||
any_type = any_type.inner;
|
||||
kind = any_type.kindof;
|
||||
}
|
||||
bool is_mixed_signed = $Type.kindof != any_type.kindof;
|
||||
$Type max = $Type.max;
|
||||
$Type min = $Type.min;
|
||||
|
||||
@@ -16,6 +16,7 @@ macro bool @is_promotable_to_float(#value) @const => types::is_promotable_to_flo
|
||||
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 @is_lvalue(#value) => $defined(#value = #value);
|
||||
|
||||
macro promote_int(x)
|
||||
{
|
||||
@@ -26,6 +27,25 @@ macro promote_int(x)
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
Select between two values at compile time,
|
||||
the values do not have to be of the same type.
|
||||
|
||||
This acts like `$bool ? #value_1 : #value_2` but at compile time.
|
||||
|
||||
@param $bool `true for picking the first value, false for the other`
|
||||
@param #value_1
|
||||
@param #value_2
|
||||
@returns `The selected value.`
|
||||
*>
|
||||
macro @select(bool $bool, #value_1, #value_2) @builtin
|
||||
{
|
||||
$if $bool:
|
||||
return #value_1;
|
||||
$else
|
||||
return #value_2;
|
||||
$endif
|
||||
}
|
||||
macro promote_int_same(x, y)
|
||||
{
|
||||
$if @is_int(x):
|
||||
|
||||
@@ -3,232 +3,117 @@ module std::encoding::base32;
|
||||
// This module implements base32 encoding according to RFC 4648
|
||||
// (https://www.rfc-editor.org/rfc/rfc4648)
|
||||
|
||||
distinct Alphabet = inline char[32];
|
||||
|
||||
// Standard base32 Alphabet
|
||||
const Alphabet STD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
|
||||
// Extended Hex Alphabet
|
||||
const Alphabet HEX_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
|
||||
|
||||
const uint MASK @private = 0b11111;
|
||||
const char INVALID @private = 0xff;
|
||||
|
||||
const int STD_PADDING = '=';
|
||||
const int NO_PADDING = -1;
|
||||
|
||||
fault Base32Error
|
||||
struct Base32Alphabet
|
||||
{
|
||||
DUPLICATE_IN_ALPHABET,
|
||||
PADDING_IN_ALPHABET,
|
||||
INVALID_CHARACTER_IN_ALPHABET,
|
||||
DESTINATION_TOO_SMALL,
|
||||
INVALID_PADDING,
|
||||
CORRUPT_INPUT
|
||||
char[32] encoding;
|
||||
char[256] reverse;
|
||||
}
|
||||
|
||||
struct Base32Encoder
|
||||
const char NO_PAD = 0;
|
||||
const char DEFAULT_PAD = '=';
|
||||
|
||||
<*
|
||||
Encode the content of src into a newly allocated string
|
||||
@param [in] src "The input to be encoded."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@param alphabet "The alphabet to use"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "The encoded string."
|
||||
*>
|
||||
fn String! encode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
Alphabet alphabet;
|
||||
int padding;
|
||||
char[] dst = allocator::alloc_array(allocator, char, encode_len(src.len, padding));
|
||||
return encode_buffer(src, dst, padding, alphabet);
|
||||
}
|
||||
|
||||
<*
|
||||
@param encoder "The 32-character alphabet for encoding."
|
||||
@param padding "Set to a negative value to disable padding."
|
||||
@require padding < 256
|
||||
Decode the content of src into a newly allocated char array.
|
||||
@param [in] src "The input to be encoded."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@param alphabet "The alphabet to use"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "The decoded data."
|
||||
*>
|
||||
fn void! Base32Encoder.init(&self, Alphabet encoder = STD_ALPHABET, int padding = STD_PADDING)
|
||||
fn char[]! decode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
encoder.validate(padding)!;
|
||||
*self = { .alphabet = encoder, .padding = padding };
|
||||
char[] dst = allocator::alloc_array(allocator, char, decode_len(src.len, padding));
|
||||
return decode_buffer(src, dst, padding, alphabet);
|
||||
}
|
||||
|
||||
fn String! encode_new(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::heap(), padding, alphabet);
|
||||
fn String! encode_temp(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::temp(), padding, alphabet);
|
||||
fn char[]! decode_new(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::heap(), padding, alphabet);
|
||||
fn char[]! decode_temp(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::temp(), padding, alphabet);
|
||||
|
||||
<*
|
||||
Calculate the length in bytes of the decoded data.
|
||||
@param n "Length in bytes of input."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "Length in bytes of the decoded data."
|
||||
*>
|
||||
fn usz decode_len(usz n, char padding)
|
||||
{
|
||||
if (padding) return (n / 8) * 5;
|
||||
// no padding
|
||||
usz trailing = n % 8;
|
||||
return n / 8 * 5 + (trailing * 5 ) / 8;
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the length in bytes of the encoded data.
|
||||
@param n "Length in bytes on input."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "Length in bytes of the encoded data."
|
||||
*>
|
||||
fn usz Base32Encoder.encode_len(&self, usz n)
|
||||
fn usz encode_len(usz n, char padding)
|
||||
{
|
||||
// A character is encoded into 8 x 5-bit blocks.
|
||||
if (self.padding >= 0)
|
||||
{
|
||||
// with padding
|
||||
return (n + 4) / 5 * 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no padding
|
||||
usz trailing = n % 5;
|
||||
return n / 5 * 8 + (trailing * 8 + 4) / 5;
|
||||
}
|
||||
}
|
||||
if (padding) return (n + 4) / 5 * 8;
|
||||
|
||||
<*
|
||||
Encode the content of src into dst, which must be properly sized.
|
||||
@param [in] src "The input to be encoded."
|
||||
@param [inout] dst "The encoded input."
|
||||
@return "The encoded size."
|
||||
@return! Base32Error.DESTINATION_TOO_SMALL
|
||||
*>
|
||||
fn usz! Base32Encoder.encode(&self, char[] src, char[] dst)
|
||||
{
|
||||
if (src.len == 0) return 0;
|
||||
|
||||
usz n = (src.len / 5) * 5;
|
||||
usz dn = self.encode_len(src.len);
|
||||
if (dst.len < dn) return Base32Error.DESTINATION_TOO_SMALL?;
|
||||
|
||||
uint msb, lsb;
|
||||
for (usz i = 0; i < n; i += 5)
|
||||
{
|
||||
// to fit 40 bits we need two 32-bit uints
|
||||
msb = (uint)src[i] << 24 | (uint)src[i+1] << 16
|
||||
| (uint)src[i+2] << 8 | (uint)src[i+3];
|
||||
lsb = msb << 8 | (uint)src[i+4];
|
||||
|
||||
// now slice them into 5-bit chunks and translate to the
|
||||
// alphabet.
|
||||
dst[0] = self.alphabet[(msb >> 27) & MASK];
|
||||
dst[1] = self.alphabet[(msb >> 22) & MASK];
|
||||
dst[2] = self.alphabet[(msb >> 17) & MASK];
|
||||
dst[3] = self.alphabet[(msb >> 12) & MASK];
|
||||
dst[4] = self.alphabet[(msb >> 7) & MASK];
|
||||
dst[5] = self.alphabet[(msb >> 2) & MASK];
|
||||
dst[6] = self.alphabet[(lsb >> 5) & MASK];
|
||||
dst[7] = self.alphabet[lsb & MASK];
|
||||
|
||||
dst = dst[8..];
|
||||
}
|
||||
|
||||
usz trailing = src.len - n;
|
||||
if (trailing == 0) return dn;
|
||||
|
||||
msb = 0;
|
||||
switch (trailing)
|
||||
{
|
||||
case 4:
|
||||
msb |= (uint)src[n+3];
|
||||
lsb = msb << 8;
|
||||
dst[6] = self.alphabet[(lsb >> 5) & MASK];
|
||||
dst[5] = self.alphabet[(msb >> 2) & MASK];
|
||||
nextcase 3;
|
||||
case 3:
|
||||
msb |= (uint)src[n+2] << 8;
|
||||
dst[4] = self.alphabet[(msb >> 7) & MASK];
|
||||
nextcase 2;
|
||||
case 2:
|
||||
msb |= (uint)src[n+1] << 16;
|
||||
dst[3] = self.alphabet[(msb >> 12) & MASK];
|
||||
dst[2] = self.alphabet[(msb >> 17) & MASK];
|
||||
nextcase 1;
|
||||
case 1:
|
||||
msb |= (uint)src[n] << 24;
|
||||
dst[1] = self.alphabet[(msb >> 22) & MASK];
|
||||
dst[0] = self.alphabet[(msb >> 27) & MASK];
|
||||
}
|
||||
|
||||
// add the padding
|
||||
if (self.padding >= 0)
|
||||
{
|
||||
char pad = (char)self.padding;
|
||||
for (usz i = (trailing * 8 / 5) + 1; i < 8; i++)
|
||||
{
|
||||
dst[i] = pad;
|
||||
}
|
||||
}
|
||||
|
||||
return dn;
|
||||
}
|
||||
|
||||
struct Base32Decoder
|
||||
{
|
||||
Alphabet alphabet;
|
||||
int padding;
|
||||
char[256] reverse;
|
||||
}
|
||||
|
||||
<*
|
||||
@param decoder "The alphabet used for decoding."
|
||||
@param padding "Set to a negative value to disable padding."
|
||||
@require padding < 256
|
||||
*>
|
||||
fn void! Base32Decoder.init(&self, Alphabet decoder = STD_ALPHABET, int padding = STD_PADDING)
|
||||
{
|
||||
decoder.validate(padding)!;
|
||||
*self = { .alphabet = decoder, .padding = padding };
|
||||
|
||||
self.reverse[..] = INVALID;
|
||||
foreach (char i, c : decoder)
|
||||
{
|
||||
self.reverse[c] = i;
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the length in bytes of the decoded data.
|
||||
@param n "Length in bytes of input."
|
||||
@return "Length in bytes of the decoded data."
|
||||
*>
|
||||
fn usz Base32Decoder.decode_len(&self, usz n)
|
||||
{
|
||||
if (self.padding >= 0)
|
||||
{
|
||||
// with padding
|
||||
return (n / 8) * 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no padding
|
||||
usz trailing = n % 8;
|
||||
return n / 8 * 5 + (trailing * 5 ) / 8;
|
||||
}
|
||||
// no padding
|
||||
usz trailing = n % 5;
|
||||
return n / 5 * 8 + (trailing * 8 + 4) / 5;
|
||||
}
|
||||
|
||||
<*
|
||||
Decode the content of src into dst, which must be properly sized.
|
||||
@param src "The input to be decoded."
|
||||
@param dst "The decoded input."
|
||||
@return "The decoded size."
|
||||
@return! Base32Error.DESTINATION_TOO_SMALL, Base32Error.CORRUPT_INPUT
|
||||
@param padding "The padding character or 0 if none"
|
||||
@param alphabet "The alphabet to use"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@require dst.len >= decode_len(src.len, padding) "Destination buffer too small"
|
||||
@return "The resulting dst buffer"
|
||||
@return! DecodingFailure
|
||||
*>
|
||||
fn usz! Base32Decoder.decode(&self, char[] src, char[] dst)
|
||||
fn char[]! decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
if (src.len == 0) return 0;
|
||||
usz dn = self.decode_len(src.len);
|
||||
if (dst.len < dn) return Base32Error.DESTINATION_TOO_SMALL?;
|
||||
|
||||
usz j, n;
|
||||
if (src.len == 0) return dst[:0];
|
||||
char* dst_ptr = dst;
|
||||
usz dn = decode_len(src.len, padding);
|
||||
usz n;
|
||||
char[8] buf;
|
||||
while (src.len > 0 && dst.len > 0)
|
||||
{
|
||||
|
||||
usz i @noinit;
|
||||
// load 8 bytes into buffer
|
||||
for (j = 0; j < 8; j++)
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
if (src.len == 0)
|
||||
{
|
||||
if (self.padding >= 0)
|
||||
{
|
||||
return Base32Error.CORRUPT_INPUT?;
|
||||
}
|
||||
if (padding > 0) return DecodingFailure.INVALID_PADDING?;
|
||||
break;
|
||||
}
|
||||
if (src[0] == (char)self.padding)
|
||||
{
|
||||
break;
|
||||
}
|
||||
buf[j] = self.reverse[src[0]];
|
||||
if (buf[j] == INVALID)
|
||||
{
|
||||
return Base32Error.CORRUPT_INPUT?;
|
||||
}
|
||||
if (src[0] == padding) break;
|
||||
buf[i] = alphabet.reverse[src[0]];
|
||||
if (buf[i] == INVALID) return DecodingFailure.INVALID_CHARACTER?;
|
||||
src = src[1..];
|
||||
}
|
||||
|
||||
// extract 5-bytes from the buffer which contains 8 x 5 bit chunks
|
||||
switch (j)
|
||||
switch (i)
|
||||
{
|
||||
case 8:
|
||||
// |66677777| dst[4]
|
||||
@@ -267,14 +152,195 @@ fn usz! Base32Decoder.decode(&self, char[] src, char[] dst)
|
||||
dst[0] = buf[1] >> 2 | buf[0] << 3;
|
||||
n++;
|
||||
default:
|
||||
return Base32Error.CORRUPT_INPUT?;
|
||||
return DecodingFailure.INVALID_CHARACTER?;
|
||||
}
|
||||
|
||||
if (dst.len < 5) break;
|
||||
dst = dst[5..];
|
||||
}
|
||||
return dst_ptr[:n];
|
||||
}
|
||||
|
||||
return n;
|
||||
<*
|
||||
Encode the content of src into dst, which must be properly sized.
|
||||
@param [in] src "The input to be encoded."
|
||||
@param [inout] dst "The encoded input."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@param alphabet "The alphabet to use"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@require dst.len >= encode_len(src.len, padding) "Destination buffer too small"
|
||||
@return "The encoded size."
|
||||
*>
|
||||
fn String encode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
if (src.len == 0) return (String)dst[:0];
|
||||
|
||||
char* dst_ptr = dst;
|
||||
usz n = (src.len / 5) * 5;
|
||||
usz dn = encode_len(src.len, padding);
|
||||
|
||||
uint msb, lsb;
|
||||
for (usz i = 0; i < n; i += 5)
|
||||
{
|
||||
// to fit 40 bits we need two 32-bit uints
|
||||
msb = (uint)src[i] << 24 | (uint)src[i+1] << 16
|
||||
| (uint)src[i+2] << 8 | (uint)src[i+3];
|
||||
lsb = msb << 8 | (uint)src[i+4];
|
||||
|
||||
// now slice them into 5-bit chunks and translate to the
|
||||
// alphabet.
|
||||
dst[0] = alphabet.encoding[(msb >> 27) & MASK];
|
||||
dst[1] = alphabet.encoding[(msb >> 22) & MASK];
|
||||
dst[2] = alphabet.encoding[(msb >> 17) & MASK];
|
||||
dst[3] = alphabet.encoding[(msb >> 12) & MASK];
|
||||
dst[4] = alphabet.encoding[(msb >> 7) & MASK];
|
||||
dst[5] = alphabet.encoding[(msb >> 2) & MASK];
|
||||
dst[6] = alphabet.encoding[(lsb >> 5) & MASK];
|
||||
dst[7] = alphabet.encoding[lsb & MASK];
|
||||
|
||||
dst = dst[8..];
|
||||
}
|
||||
|
||||
usz trailing = src.len - n;
|
||||
if (trailing == 0) return (String)dst_ptr[:dn];
|
||||
|
||||
msb = 0;
|
||||
switch (trailing)
|
||||
{
|
||||
case 4:
|
||||
msb |= (uint)src[n+3];
|
||||
lsb = msb << 8;
|
||||
dst[6] = alphabet.encoding[(lsb >> 5) & MASK];
|
||||
dst[5] = alphabet.encoding[(msb >> 2) & MASK];
|
||||
nextcase 3;
|
||||
case 3:
|
||||
msb |= (uint)src[n+2] << 8;
|
||||
dst[4] = alphabet.encoding[(msb >> 7) & MASK];
|
||||
nextcase 2;
|
||||
case 2:
|
||||
msb |= (uint)src[n+1] << 16;
|
||||
dst[3] = alphabet.encoding[(msb >> 12) & MASK];
|
||||
dst[2] = alphabet.encoding[(msb >> 17) & MASK];
|
||||
nextcase 1;
|
||||
case 1:
|
||||
msb |= (uint)src[n] << 24;
|
||||
dst[1] = alphabet.encoding[(msb >> 22) & MASK];
|
||||
dst[0] = alphabet.encoding[(msb >> 27) & MASK];
|
||||
}
|
||||
|
||||
// add the padding
|
||||
if (padding > 0)
|
||||
{
|
||||
for (usz i = (trailing * 8 / 5) + 1; i < 8; i++)
|
||||
{
|
||||
dst[i] = padding;
|
||||
}
|
||||
}
|
||||
return (String)dst_ptr[:dn];
|
||||
}
|
||||
|
||||
const uint MASK @private = 0b11111;
|
||||
const char INVALID @private = 0xff;
|
||||
|
||||
const int STD_PADDING = '=';
|
||||
const int NO_PADDING = -1;
|
||||
|
||||
fault Base32Error
|
||||
{
|
||||
DUPLICATE_IN_ALPHABET,
|
||||
PADDING_IN_ALPHABET,
|
||||
INVALID_CHARACTER_IN_ALPHABET,
|
||||
DESTINATION_TOO_SMALL,
|
||||
INVALID_PADDING,
|
||||
CORRUPT_INPUT
|
||||
}
|
||||
|
||||
struct Base32Encoder @deprecated
|
||||
{
|
||||
Base32Alphabet alphabet;
|
||||
char padding;
|
||||
}
|
||||
|
||||
<*
|
||||
@param encoder "The 32-character alphabet for encoding."
|
||||
@param padding "Set to a negative value to disable padding."
|
||||
@require padding < 256
|
||||
*>
|
||||
fn void! Base32Encoder.init(&self, Alphabet encoder = STD_ALPHABET, int padding = STD_PADDING)
|
||||
{
|
||||
encoder.validate(padding)!;
|
||||
*self = { .alphabet = { .encoding = (char[32])encoder }, .padding = padding < 0 ? (char)0 : (char)padding};
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the length in bytes of the encoded data.
|
||||
@param n "Length in bytes on input."
|
||||
@return "Length in bytes of the encoded data."
|
||||
*>
|
||||
fn usz Base32Encoder.encode_len(&self, usz n)
|
||||
{
|
||||
return encode_len(n, self.padding);
|
||||
}
|
||||
|
||||
<*
|
||||
Encode the content of src into dst, which must be properly sized.
|
||||
@param [in] src "The input to be encoded."
|
||||
@param [inout] dst "The encoded input."
|
||||
@return "The encoded size."
|
||||
@return! Base32Error.DESTINATION_TOO_SMALL
|
||||
*>
|
||||
fn usz! Base32Encoder.encode(&self, char[] src, char[] dst)
|
||||
{
|
||||
usz dn = self.encode_len(src.len);
|
||||
if (dst.len < dn) return Base32Error.DESTINATION_TOO_SMALL?;
|
||||
return encode_buffer(src, dst, self.padding, &self.alphabet).len;
|
||||
}
|
||||
|
||||
struct Base32Decoder @deprecated
|
||||
{
|
||||
Base32Alphabet alphabet;
|
||||
char padding;
|
||||
}
|
||||
|
||||
<*
|
||||
@param decoder "The alphabet used for decoding."
|
||||
@param padding "Set to a negative value to disable padding."
|
||||
@require padding < 256
|
||||
*>
|
||||
fn void! Base32Decoder.init(&self, Alphabet decoder = STD_ALPHABET, int padding = STD_PADDING)
|
||||
{
|
||||
decoder.validate(padding)!;
|
||||
*self = { .alphabet = { .encoding = (char[32])decoder }, .padding = padding < 0 ? (char)0 : (char)padding };
|
||||
|
||||
self.alphabet.reverse[..] = INVALID;
|
||||
foreach (char i, c : decoder)
|
||||
{
|
||||
self.alphabet.reverse[c] = i;
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the length in bytes of the decoded data.
|
||||
@param n "Length in bytes of input."
|
||||
@return "Length in bytes of the decoded data."
|
||||
*>
|
||||
fn usz Base32Decoder.decode_len(&self, usz n)
|
||||
{
|
||||
return decode_len(n, self.padding);
|
||||
}
|
||||
|
||||
<*
|
||||
Decode the content of src into dst, which must be properly sized.
|
||||
@param src "The input to be decoded."
|
||||
@param dst "The decoded input."
|
||||
@return "The decoded size."
|
||||
@return! Base32Error.DESTINATION_TOO_SMALL, Base32Error.CORRUPT_INPUT
|
||||
*>
|
||||
fn usz! Base32Decoder.decode(&self, char[] src, char[] dst)
|
||||
{
|
||||
if (src.len == 0) return 0;
|
||||
usz dn = self.decode_len(src.len);
|
||||
if (dst.len < dn) return Base32Error.DESTINATION_TOO_SMALL?;
|
||||
return decode_buffer(src, dst, self.padding, &self.alphabet).len;
|
||||
}
|
||||
|
||||
|
||||
@@ -308,3 +374,33 @@ fn void! Alphabet.validate(&self, int padding)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
distinct Alphabet = char[32];
|
||||
// Standard base32 Alphabet
|
||||
const Alphabet STD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
// Extended Hex Alphabet
|
||||
const Alphabet HEX_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
|
||||
|
||||
const Base32Alphabet STANDARD = {
|
||||
.encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",
|
||||
.reverse = x`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffff1a1b1c1d1e1fffffffffffffffff
|
||||
ff000102030405060708090a0b0c0d0e0f10111213141516171819ffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`
|
||||
};
|
||||
|
||||
const Base32Alphabet HEX = {
|
||||
.encoding = "0123456789ABCDEFGHIJKLMNOPQRSTUV",
|
||||
.reverse = x`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff00010203040506070809ffffffffffff
|
||||
ff0a0b0c0d0e0f101112131415161718191a1b1c1d1e1fffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`
|
||||
};
|
||||
|
||||
@@ -5,14 +5,260 @@ import std::core::bitorder;
|
||||
// Specifically this section:
|
||||
// https://www.rfc-editor.org/rfc/rfc4648#section-4
|
||||
|
||||
const char NO_PAD = 0;
|
||||
const char DEFAULT_PAD = '=';
|
||||
|
||||
struct Base64Alphabet
|
||||
{
|
||||
char[64] encoding;
|
||||
char[256] reverse;
|
||||
}
|
||||
|
||||
const Base64Alphabet STANDARD = {
|
||||
.encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
|
||||
.reverse =
|
||||
x`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffff3effffff3f3435363738393a3b3c3dffffffffffff
|
||||
ff000102030405060708090a0b0c0d0e0f10111213141516171819ffffffffff
|
||||
ff1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233ffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`
|
||||
};
|
||||
|
||||
const Base64Alphabet URL = {
|
||||
.encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
|
||||
.reverse =
|
||||
x`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffff3effff3435363738393a3b3c3dffffffffffff
|
||||
ff000102030405060708090a0b0c0d0e0f10111213141516171819ffffffff3f
|
||||
ff1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233ffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`
|
||||
};
|
||||
|
||||
const STD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
const URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||
|
||||
fn String encode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
char[] dst = allocator::alloc_array(allocator, char, encode_len(src.len, padding));
|
||||
return encode_buffer(src, dst, padding, alphabet);
|
||||
}
|
||||
|
||||
fn char[]! decode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
char[] dst = allocator::alloc_array(allocator, char, decode_len(src.len, padding))!;
|
||||
return decode_buffer(src, dst, padding, alphabet);
|
||||
}
|
||||
|
||||
fn String encode_new(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::heap(), padding, alphabet);
|
||||
fn String encode_temp(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::temp(), padding, alphabet);
|
||||
fn char[]! decode_new(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::heap(), padding, alphabet);
|
||||
fn char[]! decode_temp(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::temp(), padding, alphabet);
|
||||
|
||||
|
||||
<*
|
||||
Calculate the size of the encoded data.
|
||||
@param n "Size of the input to be encoded."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "The size of the input once encoded."
|
||||
*>
|
||||
fn usz encode_len(usz n, char padding)
|
||||
{
|
||||
if (padding) return (n + 2) / 3 * 4;
|
||||
usz trailing = n % 3;
|
||||
return n / 3 * 4 + (trailing * 4 + 2) / 3;
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the size of the decoded data.
|
||||
@param n "Size of the input to be decoded."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "The size of the input once decoded."
|
||||
@return! DecodingFailure.INVALID_PADDING
|
||||
*>
|
||||
fn usz! decode_len(usz n, char padding)
|
||||
{
|
||||
usz dn = n / 4 * 3;
|
||||
usz trailing = n % 4;
|
||||
if (padding)
|
||||
{
|
||||
if (trailing != 0) return DecodingFailure.INVALID_PADDING?;
|
||||
// source size is multiple of 4
|
||||
return dn;
|
||||
}
|
||||
if (trailing == 1) return DecodingFailure.INVALID_PADDING?;
|
||||
return dn + trailing * 3 / 4;
|
||||
}
|
||||
|
||||
<*
|
||||
Encode the content of src into dst, which must be properly sized.
|
||||
@param src "The input to be encoded."
|
||||
@param dst "The encoded input."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@param alphabet "The alphabet to use"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "The encoded size."
|
||||
@return! Base64Error.DESTINATION_TOO_SMALL
|
||||
*>
|
||||
fn String encode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
if (src.len == 0) return (String)dst[:0];
|
||||
usz dn = encode_len(src.len, padding);
|
||||
char* dst_ptr = dst;
|
||||
assert(dst.len >= dn);
|
||||
usz trailing = src.len % 3;
|
||||
char[] src3 = src[:^trailing];
|
||||
|
||||
while (src3.len > 0)
|
||||
{
|
||||
uint group = (uint)src3[0] << 16 | (uint)src3[1] << 8 | (uint)src3[2];
|
||||
dst[0] = alphabet.encoding[group >> 18 & MASK];
|
||||
dst[1] = alphabet.encoding[group >> 12 & MASK];
|
||||
dst[2] = alphabet.encoding[group >> 6 & MASK];
|
||||
dst[3] = alphabet.encoding[group & MASK];
|
||||
dst = dst[4..];
|
||||
src3 = src3[3..];
|
||||
}
|
||||
|
||||
// Encode the remaining bytes according to:
|
||||
// https://www.rfc-editor.org/rfc/rfc4648#section-3.5
|
||||
switch (trailing)
|
||||
{
|
||||
case 1:
|
||||
uint group = (uint)src[^1] << 16;
|
||||
dst[0] = alphabet.encoding[group >> 18 & MASK];
|
||||
dst[1] = alphabet.encoding[group >> 12 & MASK];
|
||||
if (padding > 0)
|
||||
{
|
||||
dst[2] = padding;
|
||||
dst[3] = padding;
|
||||
}
|
||||
case 2:
|
||||
uint group = (uint)src[^2] << 16 | (uint)src[^1] << 8;
|
||||
dst[0] = alphabet.encoding[group >> 18 & MASK];
|
||||
dst[1] = alphabet.encoding[group >> 12 & MASK];
|
||||
dst[2] = alphabet.encoding[group >> 6 & MASK];
|
||||
if (padding > 0)
|
||||
{
|
||||
dst[3] = padding;
|
||||
}
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
return (String)dst_ptr[:dn];
|
||||
}
|
||||
|
||||
<*
|
||||
Decode the content of src into dst, which must be properly sized.
|
||||
@param src "The input to be decoded."
|
||||
@param dst "The decoded input."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@param alphabet "The alphabet to use"
|
||||
@require (decode_len(src.len, padding) ?? 0) <= dst.len "Destination buffer too small"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "The decoded data."
|
||||
@return! DecodingFailure
|
||||
*>
|
||||
fn char[]! decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
if (src.len == 0) return dst[:0];
|
||||
usz dn = decode_len(src.len, padding)!;
|
||||
assert(dst.len >= dn);
|
||||
|
||||
usz trailing = src.len % 4;
|
||||
char* dst_ptr = dst;
|
||||
char[] src4 = src;
|
||||
switch
|
||||
{
|
||||
case !padding:
|
||||
src4 = src[:^trailing];
|
||||
default:
|
||||
// If there is padding, keep the last 4 bytes for later.
|
||||
// NB. src.len >= 4 as decode_len passed
|
||||
trailing = 4;
|
||||
if (src[^1] == padding) src4 = src[:^4];
|
||||
}
|
||||
while (src4.len > 0)
|
||||
{
|
||||
char c0 = alphabet.reverse[src4[0]];
|
||||
char c1 = alphabet.reverse[src4[1]];
|
||||
char c2 = alphabet.reverse[src4[2]];
|
||||
char c3 = alphabet.reverse[src4[3]];
|
||||
switch (0xFF)
|
||||
{
|
||||
case c0:
|
||||
case c1:
|
||||
case c2:
|
||||
case c3:
|
||||
return DecodingFailure.INVALID_CHARACTER?;
|
||||
}
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6 | (uint)c3;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dst[1] = (char)(group >> 8);
|
||||
dst[2] = (char)group;
|
||||
dst = dst[3..];
|
||||
src4 = src4[4..];
|
||||
}
|
||||
|
||||
if (trailing == 0) return dst_ptr[:dn];
|
||||
|
||||
src = src[^trailing..];
|
||||
char c0 = alphabet.reverse[src[0]];
|
||||
char c1 = alphabet.reverse[src[1]];
|
||||
if (c0 == 0xFF || c1 == 0xFF) return DecodingFailure.INVALID_PADDING?;
|
||||
if (!padding)
|
||||
{
|
||||
switch (src.len)
|
||||
{
|
||||
case 2:
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12;
|
||||
dst[0] = (char)(group >> 16);
|
||||
case 3:
|
||||
char c2 = alphabet.reverse[src[2]];
|
||||
if (c2 == 0xFF) return DecodingFailure.INVALID_CHARACTER?;
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dst[1] = (char)(group >> 8);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Valid paddings are:
|
||||
// 2: xx==
|
||||
// 1: xxx=
|
||||
switch (padding)
|
||||
{
|
||||
case src[2]:
|
||||
if (src[3] != padding) return DecodingFailure.INVALID_PADDING?;
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dn -= 2;
|
||||
case src[3]:
|
||||
char c2 = alphabet.reverse[src[2]];
|
||||
if (c2 == 0xFF) return DecodingFailure.INVALID_CHARACTER?;
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dst[1] = (char)(group >> 8);
|
||||
dn -= 1;
|
||||
}
|
||||
}
|
||||
return dst_ptr[:dn];
|
||||
}
|
||||
|
||||
const MASK @private = 0b111111;
|
||||
|
||||
struct Base64Encoder
|
||||
struct Base64Encoder @deprecated
|
||||
{
|
||||
int padding;
|
||||
char padding;
|
||||
String alphabet;
|
||||
}
|
||||
|
||||
@@ -32,10 +278,11 @@ fault Base64Error
|
||||
@require padding < 256
|
||||
@return! Base64Error.DUPLICATE_IN_ALPHABET, Base64Error.PADDING_IN_ALPHABET
|
||||
*>
|
||||
fn void! Base64Encoder.init(&self, String alphabet, int padding = '=')
|
||||
fn Base64Encoder*! Base64Encoder.init(&self, String alphabet, int padding = '=')
|
||||
{
|
||||
check_alphabet(alphabet, padding)!;
|
||||
*self = { .padding = padding, .alphabet = alphabet };
|
||||
*self = { .padding = padding < 0 ? 0 : (char)padding, .alphabet = alphabet };
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -45,9 +292,7 @@ fn void! Base64Encoder.init(&self, String alphabet, int padding = '=')
|
||||
*>
|
||||
fn usz Base64Encoder.encode_len(&self, usz n)
|
||||
{
|
||||
if (self.padding >= 0) return (n + 2) / 3 * 4;
|
||||
usz trailing = n % 3;
|
||||
return n / 3 * 4 + (trailing * 4 + 2) / 3;
|
||||
return encode_len(n, self.padding);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -62,56 +307,18 @@ fn usz! Base64Encoder.encode(&self, char[] src, char[] dst)
|
||||
if (src.len == 0) return 0;
|
||||
usz dn = self.encode_len(src.len);
|
||||
if (dst.len < dn) return Base64Error.DESTINATION_TOO_SMALL?;
|
||||
usz trailing = src.len % 3;
|
||||
char[] src3 = src[:^trailing];
|
||||
|
||||
while (src3.len > 0)
|
||||
{
|
||||
uint group = (uint)src3[0] << 16 | (uint)src3[1] << 8 | (uint)src3[2];
|
||||
dst[0] = self.alphabet[group >> 18 & MASK];
|
||||
dst[1] = self.alphabet[group >> 12 & MASK];
|
||||
dst[2] = self.alphabet[group >> 6 & MASK];
|
||||
dst[3] = self.alphabet[group & MASK];
|
||||
dst = dst[4..];
|
||||
src3 = src3[3..];
|
||||
}
|
||||
|
||||
// Encode the remaining bytes according to:
|
||||
// https://www.rfc-editor.org/rfc/rfc4648#section-3.5
|
||||
switch (trailing)
|
||||
{
|
||||
case 1:
|
||||
uint group = (uint)src[^1] << 16;
|
||||
dst[0] = self.alphabet[group >> 18 & MASK];
|
||||
dst[1] = self.alphabet[group >> 12 & MASK];
|
||||
if (self.padding >= 0)
|
||||
{
|
||||
char pad = (char)self.padding;
|
||||
dst[2] = pad;
|
||||
dst[3] = pad;
|
||||
}
|
||||
case 2:
|
||||
uint group = (uint)src[^2] << 16 | (uint)src[^1] << 8;
|
||||
dst[0] = self.alphabet[group >> 18 & MASK];
|
||||
dst[1] = self.alphabet[group >> 12 & MASK];
|
||||
dst[2] = self.alphabet[group >> 6 & MASK];
|
||||
if (self.padding >= 0)
|
||||
{
|
||||
char pad = (char)self.padding;
|
||||
dst[3] = pad;
|
||||
}
|
||||
}
|
||||
return dn;
|
||||
Base64Alphabet a = { .encoding = self.alphabet[:64] };
|
||||
return encode_buffer(src, dst, self.padding, &a).len;
|
||||
}
|
||||
|
||||
struct Base64Decoder
|
||||
struct Base64Decoder @deprecated
|
||||
{
|
||||
int padding;
|
||||
String alphabet;
|
||||
char[256] reverse;
|
||||
char invalid;
|
||||
char padding;
|
||||
Base64Alphabet encoding;
|
||||
bool init_done;
|
||||
}
|
||||
|
||||
import std;
|
||||
<*
|
||||
@param alphabet "The alphabet used for encoding."
|
||||
@param padding "Set to a negative value to disable padding."
|
||||
@@ -121,29 +328,15 @@ struct Base64Decoder
|
||||
*>
|
||||
fn void! Base64Decoder.init(&self, String alphabet, int padding = '=')
|
||||
{
|
||||
self.init_done = true;
|
||||
check_alphabet(alphabet, padding)!;
|
||||
*self = { .padding = padding, .alphabet = alphabet };
|
||||
*self = { .padding = padding < 0 ? 0 : (char)padding, .encoding.encoding = alphabet[:64] };
|
||||
|
||||
self.encoding.reverse[..] = 0xFF;
|
||||
|
||||
bool[256] checked;
|
||||
foreach (i, c : alphabet)
|
||||
{
|
||||
checked[c] = true;
|
||||
self.reverse[c] = (char)i;
|
||||
}
|
||||
if (padding < 0)
|
||||
{
|
||||
self.invalid = 255;
|
||||
return;
|
||||
}
|
||||
// Find a character for invalid neither in the alphabet nor equal to the padding.
|
||||
char pad = (char)padding;
|
||||
foreach (i, ok : checked)
|
||||
{
|
||||
if (!ok && (char)i != pad)
|
||||
{
|
||||
self.invalid = (char)i;
|
||||
break;
|
||||
}
|
||||
self.encoding.reverse[c] = (char)i;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,19 +348,7 @@ fn void! Base64Decoder.init(&self, String alphabet, int padding = '=')
|
||||
*>
|
||||
fn usz! Base64Decoder.decode_len(&self, usz n)
|
||||
{
|
||||
usz dn = n / 4 * 3;
|
||||
usz trailing = n % 4;
|
||||
if (self.padding >= 0)
|
||||
{
|
||||
if (trailing != 0) return Base64Error.INVALID_PADDING?;
|
||||
// source size is multiple of 4
|
||||
}
|
||||
else
|
||||
{
|
||||
if (trailing == 1) return Base64Error.INVALID_PADDING?;
|
||||
dn += trailing * 3 / 4;
|
||||
}
|
||||
return dn;
|
||||
return decode_len(n, self.padding) ?? Base64Error.INVALID_PADDING?;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -182,86 +363,17 @@ fn usz! Base64Decoder.decode(&self, char[] src, char[] dst)
|
||||
if (src.len == 0) return 0;
|
||||
usz dn = self.decode_len(src.len)!;
|
||||
if (dst.len < dn) return Base64Error.DESTINATION_TOO_SMALL?;
|
||||
|
||||
usz trailing = src.len % 4;
|
||||
char[] src4 = src;
|
||||
switch
|
||||
char[]! decoded = decode_buffer(src, dst, self.padding, &self.encoding);
|
||||
if (catch err = decoded)
|
||||
{
|
||||
case self.padding < 0:
|
||||
src4 = src[:^trailing];
|
||||
case DecodingFailure.INVALID_PADDING:
|
||||
return Base64Error.INVALID_PADDING?;
|
||||
case DecodingFailure.INVALID_CHARACTER:
|
||||
return Base64Error.INVALID_CHARACTER?;
|
||||
default:
|
||||
// If there is padding, keep the last 4 bytes for later.
|
||||
// NB. src.len >= 4 as decode_len passed
|
||||
trailing = 4;
|
||||
char pad = (char)self.padding;
|
||||
if (src[^1] == pad) src4 = src[:^4];
|
||||
return err?;
|
||||
}
|
||||
while (src4.len > 0)
|
||||
{
|
||||
char c0 = self.reverse[src4[0]];
|
||||
char c1 = self.reverse[src4[1]];
|
||||
char c2 = self.reverse[src4[2]];
|
||||
char c3 = self.reverse[src4[3]];
|
||||
switch (self.invalid)
|
||||
{
|
||||
case c0:
|
||||
case c1:
|
||||
case c2:
|
||||
case c3:
|
||||
return Base64Error.INVALID_CHARACTER?;
|
||||
}
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6 | (uint)c3;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dst[1] = (char)(group >> 8);
|
||||
dst[2] = (char)group;
|
||||
dst = dst[3..];
|
||||
src4 = src4[4..];
|
||||
}
|
||||
|
||||
if (trailing == 0) return dn;
|
||||
|
||||
src = src[^trailing..];
|
||||
char c0 = self.reverse[src[0]];
|
||||
char c1 = self.reverse[src[1]];
|
||||
if (c0 == self.invalid || c1 == self.invalid) return Base64Error.INVALID_PADDING?;
|
||||
if (self.padding < 0)
|
||||
{
|
||||
switch (src.len)
|
||||
{
|
||||
case 2:
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12;
|
||||
dst[0] = (char)(group >> 16);
|
||||
case 3:
|
||||
char c2 = self.reverse[src[2]];
|
||||
if (c2 == self.invalid) return Base64Error.INVALID_CHARACTER?;
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dst[1] = (char)(group >> 8);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Valid paddings are:
|
||||
// 2: xx==
|
||||
// 1: xxx=
|
||||
char pad = (char)self.padding;
|
||||
switch (pad)
|
||||
{
|
||||
case src[2]:
|
||||
if (src[3] != pad) return Base64Error.INVALID_PADDING?;
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dn -= 2;
|
||||
case src[3]:
|
||||
char c2 = self.reverse[src[2]];
|
||||
if (c2 == self.invalid) return Base64Error.INVALID_CHARACTER?;
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dst[1] = (char)(group >> 8);
|
||||
dn -= 1;
|
||||
}
|
||||
}
|
||||
return dn;
|
||||
return decoded.len;
|
||||
}
|
||||
|
||||
// Make sure that all bytes in the alphabet are unique and
|
||||
@@ -285,4 +397,5 @@ fn void! check_alphabet(String alphabet, int padding) @local
|
||||
if (checked[c]) return Base64Error.DUPLICATE_IN_ALPHABET?;
|
||||
checked[c] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ fn CsvRow! CsvReader.read_temp_row(self)
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.allocator `Row already freed`
|
||||
@require self.allocator != null `Row already freed`
|
||||
*>
|
||||
fn void CsvRow.free(&self)
|
||||
{
|
||||
@@ -70,15 +70,12 @@ fn void CsvRow.free(&self)
|
||||
self.allocator = null;
|
||||
}
|
||||
|
||||
fn void! CsvReader.skip_row(self) @maydiscard
|
||||
fn void! CsvReader.skip_row(self) @maydiscard => @pool()
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
(void)io::treadline(self.stream);
|
||||
};
|
||||
(void)io::treadline(self.stream);
|
||||
}
|
||||
|
||||
macro CsvReader.@each_row(self, int rows = int.max; @body(String[] row))
|
||||
macro void! CsvReader.@each_row(self, int rows = int.max; @body(String[] row)) @maydiscard
|
||||
{
|
||||
InStream stream = self.stream;
|
||||
String sep = self.separator;
|
||||
|
||||
7
lib/std/encoding/encoding.c3
Normal file
7
lib/std/encoding/encoding.c3
Normal file
@@ -0,0 +1,7 @@
|
||||
module std::encoding;
|
||||
|
||||
fault DecodingFailure
|
||||
{
|
||||
INVALID_CHARACTER,
|
||||
INVALID_PADDING,
|
||||
}
|
||||
109
lib/std/encoding/hex.c3
Normal file
109
lib/std/encoding/hex.c3
Normal file
@@ -0,0 +1,109 @@
|
||||
module std::encoding::hex;
|
||||
import std::encoding @norecurse;
|
||||
|
||||
// The implementation is based on https://www.rfc-editor.org/rfc/rfc4648
|
||||
|
||||
fn String encode_buffer(char[] code, char[] buffer)
|
||||
{
|
||||
return (String)buffer[:encode_bytes(code, buffer)];
|
||||
}
|
||||
|
||||
fn char[]! decode_buffer(char[] code, char[] buffer)
|
||||
{
|
||||
return buffer[:decode_bytes(code, buffer)!];
|
||||
}
|
||||
|
||||
fn String encode(char[] code, Allocator allocator)
|
||||
{
|
||||
char[] data = allocator::alloc_array(allocator, char, encode_len(code.len));
|
||||
return (String)data[:encode_bytes(code, data)];
|
||||
}
|
||||
|
||||
fn char[]! decode(char[] code, Allocator allocator)
|
||||
{
|
||||
char[] data = allocator::alloc_array(allocator, char, decode_len(code.len));
|
||||
return data[:decode_bytes(code, data)!];
|
||||
}
|
||||
|
||||
fn String encode_new(char[] code) @inline => encode(code, allocator::heap());
|
||||
fn String encode_temp(char[] code) @inline => encode(code, allocator::temp());
|
||||
fn char[]! decode_new(char[] code) @inline => decode(code, allocator::heap());
|
||||
fn char[]! decode_temp(char[] code) @inline => decode(code, allocator::temp());
|
||||
|
||||
<*
|
||||
Calculate the size of the encoded data.
|
||||
@param n "Size of the input to be encoded."
|
||||
@return "The size of the input once encoded."
|
||||
*>
|
||||
fn usz encode_len(usz n) => n * 2;
|
||||
|
||||
<*
|
||||
Encode the content of src into dst, which must be properly sized.
|
||||
@param src "The input to be encoded."
|
||||
@param dst "The encoded input."
|
||||
@return "The encoded size."
|
||||
@require dst.len >= encode_len(src.len) "Destination array is not large enough"
|
||||
*>
|
||||
fn usz encode_bytes(char[] src, char[] dst)
|
||||
{
|
||||
usz j = 0;
|
||||
foreach (v : src)
|
||||
{
|
||||
dst[j] = HEXALPHABET[v >> 4];
|
||||
dst[j + 1] = HEXALPHABET[v & 0x0f];
|
||||
j = j + 2;
|
||||
}
|
||||
return src.len * 2;
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the size of the decoded data.
|
||||
@param n "Size of the input to be decoded."
|
||||
@return "The size of the input once decoded."
|
||||
*>
|
||||
macro usz decode_len(usz n) => n / 2;
|
||||
|
||||
<*
|
||||
Decodes src into bytes. Returns the actual number of bytes written to dst.
|
||||
|
||||
Expects that src only contains hexadecimal characters and that src has even
|
||||
length.
|
||||
|
||||
@param src "The input to be decoded."
|
||||
@param dst "The decoded input."
|
||||
@require src.len % 2 == 0 "src is not of even length"
|
||||
@require dst.len >= decode_len(src.len) "Destination array is not large enough"
|
||||
@return! DecodingFailure.INVALID_CHARACTER
|
||||
*>
|
||||
fn usz! decode_bytes(char[] src, char[] dst)
|
||||
{
|
||||
usz i;
|
||||
for (usz j = 1; j < src.len; j += 2)
|
||||
{
|
||||
char a = HEXREVERSE[src[j - 1]];
|
||||
char b = HEXREVERSE[src[j]];
|
||||
if (a > 0x0f || b > 0x0f) return DecodingFailure.INVALID_CHARACTER?;
|
||||
dst[i] = (a << 4) | b;
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
const char[?] HEXALPHABET @private = "0123456789abcdef";
|
||||
const char[?] HEXREVERSE @private =
|
||||
x`ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
00010203040506070809ffffffffffff
|
||||
ff0a0b0c0d0e0fffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ff0a0b0c0d0e0fffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff`;
|
||||
@@ -8,7 +8,7 @@ distinct Fnv32a = uint;
|
||||
const FNV32A_START @private = 0x811c9dc5;
|
||||
const FNV32A_MUL @private = 0x01000193;
|
||||
|
||||
macro void @update(uint* &h, char x) @private => *h = (*h * FNV32A_MUL) ^ x;
|
||||
macro void update(h, char x) @private => *h = (*h ^ ($typeof(*h))x) * FNV32A_MUL;
|
||||
|
||||
fn void Fnv32a.init(&self)
|
||||
{
|
||||
@@ -17,17 +17,17 @@ fn void Fnv32a.init(&self)
|
||||
|
||||
fn void Fnv32a.update(&self, char[] data)
|
||||
{
|
||||
uint h = (uint)*self;
|
||||
Fnv32a h = *self;
|
||||
foreach (char x : data)
|
||||
{
|
||||
@update(h, x);
|
||||
update(&h, x);
|
||||
}
|
||||
*self = (Fnv32a)h;
|
||||
*self = h;
|
||||
}
|
||||
|
||||
macro void Fnv32a.update_char(&self, char c)
|
||||
{
|
||||
@update(*self, x);
|
||||
update(self, c);
|
||||
}
|
||||
|
||||
fn uint encode(char[] data)
|
||||
@@ -35,7 +35,7 @@ fn uint encode(char[] data)
|
||||
uint h = FNV32A_START;
|
||||
foreach (char x : data)
|
||||
{
|
||||
@update(h, x);
|
||||
update(&h, x);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ distinct Fnv64a = ulong;
|
||||
const FNV64A_START @private = 0xcbf29ce484222325;
|
||||
const FNV64A_MUL @private = 0x00000100000001b3;
|
||||
|
||||
macro void @update(ulong* &h, char x) @private => *h = (*h * FNV64A_MUL) ^ x;
|
||||
macro void update(h, char x) @private => *h = (*h ^ ($typeof(*h))x) * FNV64A_MUL;
|
||||
|
||||
fn void Fnv64a.init(&self)
|
||||
{
|
||||
@@ -17,17 +17,17 @@ fn void Fnv64a.init(&self)
|
||||
|
||||
fn void Fnv64a.update(&self, char[] data)
|
||||
{
|
||||
ulong h = (ulong)*self;
|
||||
Fnv64a h = *self;
|
||||
foreach (char x : data)
|
||||
{
|
||||
@update(h, x);
|
||||
update(&h, x);
|
||||
}
|
||||
*self = (Fnv64a)h;
|
||||
*self = h;
|
||||
}
|
||||
|
||||
macro void Fnv64a.update_char(&self, char c)
|
||||
{
|
||||
@update(*self, x);
|
||||
update(self, c);
|
||||
}
|
||||
|
||||
fn ulong encode(char[] data)
|
||||
@@ -35,7 +35,7 @@ fn ulong encode(char[] data)
|
||||
ulong h = FNV64A_START;
|
||||
foreach (char x : data)
|
||||
{
|
||||
@update(h, x);
|
||||
update(&h, x);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,9 +106,9 @@ macro @h(x, y, z) => (x ^ y) ^ z;
|
||||
macro @h2(x, y, z) => x ^ (y ^ z);
|
||||
macro @i(x, y, z) => y ^ (x | ~z);
|
||||
|
||||
macro @step(#f, &a, b, c, d, ptr, n, t, s)
|
||||
macro @step(#f, a, b, c, d, ptr, n, t, s)
|
||||
{
|
||||
*a += #f(b, c, d) + *(uint *)&ptr[n * 4] + t;
|
||||
*a += #f(b, c, d) + @unaligned_load(*(uint *)&ptr[n * 4], 2) + t;
|
||||
*a = (*a << s) | ((*a & 0xffffffff) >> (32 - s));
|
||||
*a += b;
|
||||
}
|
||||
@@ -133,76 +133,76 @@ fn char* body(Md5* ctx, void* data, usz size)
|
||||
saved_d = d;
|
||||
|
||||
/* Round 1 */
|
||||
@step(@f, a, b, c, d, ptr, 0, 0xd76aa478, 7) ;
|
||||
@step(@f, d, a, b, c, ptr, 1, 0xe8c7b756, 12) ;
|
||||
@step(@f, c, d, a, b, ptr, 2, 0x242070db, 17) ;
|
||||
@step(@f, b, c, d, a, ptr, 3, 0xc1bdceee, 22) ;
|
||||
@step(@f, a, b, c, d, ptr, 4, 0xf57c0faf, 7) ;
|
||||
@step(@f, d, a, b, c, ptr, 5, 0x4787c62a, 12) ;
|
||||
@step(@f, c, d, a, b, ptr, 6, 0xa8304613, 17) ;
|
||||
@step(@f, b, c, d, a, ptr, 7, 0xfd469501, 22) ;
|
||||
@step(@f, a, b, c, d, ptr, 8, 0x698098d8, 7) ;
|
||||
@step(@f, d, a, b, c, ptr, 9, 0x8b44f7af, 12) ;
|
||||
@step(@f, c, d, a, b, ptr, 10, 0xffff5bb1, 17);
|
||||
@step(@f, b, c, d, a, ptr, 11, 0x895cd7be, 22);
|
||||
@step(@f, a, b, c, d, ptr, 12, 0x6b901122, 7) ;
|
||||
@step(@f, d, a, b, c, ptr, 13, 0xfd987193, 12);
|
||||
@step(@f, c, d, a, b, ptr, 14, 0xa679438e, 17);
|
||||
@step(@f, b, c, d, a, ptr, 15, 0x49b40821, 22);
|
||||
@step(@f, &a, b, c, d, ptr, 0, 0xd76aa478, 7) ;
|
||||
@step(@f, &d, a, b, c, ptr, 1, 0xe8c7b756, 12) ;
|
||||
@step(@f, &c, d, a, b, ptr, 2, 0x242070db, 17) ;
|
||||
@step(@f, &b, c, d, a, ptr, 3, 0xc1bdceee, 22) ;
|
||||
@step(@f, &a, b, c, d, ptr, 4, 0xf57c0faf, 7) ;
|
||||
@step(@f, &d, a, b, c, ptr, 5, 0x4787c62a, 12) ;
|
||||
@step(@f, &c, d, a, b, ptr, 6, 0xa8304613, 17) ;
|
||||
@step(@f, &b, c, d, a, ptr, 7, 0xfd469501, 22) ;
|
||||
@step(@f, &a, b, c, d, ptr, 8, 0x698098d8, 7) ;
|
||||
@step(@f, &d, a, b, c, ptr, 9, 0x8b44f7af, 12) ;
|
||||
@step(@f, &c, d, a, b, ptr, 10, 0xffff5bb1, 17);
|
||||
@step(@f, &b, c, d, a, ptr, 11, 0x895cd7be, 22);
|
||||
@step(@f, &a, b, c, d, ptr, 12, 0x6b901122, 7) ;
|
||||
@step(@f, &d, a, b, c, ptr, 13, 0xfd987193, 12);
|
||||
@step(@f, &c, d, a, b, ptr, 14, 0xa679438e, 17);
|
||||
@step(@f, &b, c, d, a, ptr, 15, 0x49b40821, 22);
|
||||
|
||||
/* Round 2 */
|
||||
@step(@g, a, b, c, d, ptr, 1, 0xf61e2562, 5) ;
|
||||
@step(@g, d, a, b, c, ptr, 6, 0xc040b340, 9) ;
|
||||
@step(@g, c, d, a, b, ptr, 11, 0x265e5a51, 14);
|
||||
@step(@g, b, c, d, a, ptr, 0, 0xe9b6c7aa, 20) ;
|
||||
@step(@g, a, b, c, d, ptr, 5, 0xd62f105d, 5) ;
|
||||
@step(@g, d, a, b, c, ptr, 10, 0x02441453, 9) ;
|
||||
@step(@g, c, d, a, b, ptr, 15, 0xd8a1e681, 14);
|
||||
@step(@g, b, c, d, a, ptr, 4, 0xe7d3fbc8, 20) ;
|
||||
@step(@g, a, b, c, d, ptr, 9, 0x21e1cde6, 5) ;
|
||||
@step(@g, d, a, b, c, ptr, 14, 0xc33707d6, 9) ;
|
||||
@step(@g, c, d, a, b, ptr, 3, 0xf4d50d87, 14) ;
|
||||
@step(@g, b, c, d, a, ptr, 8, 0x455a14ed, 20) ;
|
||||
@step(@g, a, b, c, d, ptr, 13, 0xa9e3e905, 5) ;
|
||||
@step(@g, d, a, b, c, ptr, 2, 0xfcefa3f8, 9) ;
|
||||
@step(@g, c, d, a, b, ptr, 7, 0x676f02d9, 14) ;
|
||||
@step(@g, b, c, d, a, ptr, 12, 0x8d2a4c8a, 20);
|
||||
@step(@g, &a, b, c, d, ptr, 1, 0xf61e2562, 5) ;
|
||||
@step(@g, &d, a, b, c, ptr, 6, 0xc040b340, 9) ;
|
||||
@step(@g, &c, d, a, b, ptr, 11, 0x265e5a51, 14);
|
||||
@step(@g, &b, c, d, a, ptr, 0, 0xe9b6c7aa, 20) ;
|
||||
@step(@g, &a, b, c, d, ptr, 5, 0xd62f105d, 5) ;
|
||||
@step(@g, &d, a, b, c, ptr, 10, 0x02441453, 9) ;
|
||||
@step(@g, &c, d, a, b, ptr, 15, 0xd8a1e681, 14);
|
||||
@step(@g, &b, c, d, a, ptr, 4, 0xe7d3fbc8, 20) ;
|
||||
@step(@g, &a, b, c, d, ptr, 9, 0x21e1cde6, 5) ;
|
||||
@step(@g, &d, a, b, c, ptr, 14, 0xc33707d6, 9) ;
|
||||
@step(@g, &c, d, a, b, ptr, 3, 0xf4d50d87, 14) ;
|
||||
@step(@g, &b, c, d, a, ptr, 8, 0x455a14ed, 20) ;
|
||||
@step(@g, &a, b, c, d, ptr, 13, 0xa9e3e905, 5) ;
|
||||
@step(@g, &d, a, b, c, ptr, 2, 0xfcefa3f8, 9) ;
|
||||
@step(@g, &c, d, a, b, ptr, 7, 0x676f02d9, 14) ;
|
||||
@step(@g, &b, c, d, a, ptr, 12, 0x8d2a4c8a, 20);
|
||||
|
||||
/* Round 3 */
|
||||
@step(@h, a, b, c, d, ptr, 5, 0xfffa3942, 4);
|
||||
@step(@h2, d, a, b, c, ptr, 8, 0x8771f681, 11);
|
||||
@step(@h, c, d, a, b, ptr, 11, 0x6d9d6122, 16);
|
||||
@step(@h2, b, c, d, a, ptr, 14, 0xfde5380c, 23);
|
||||
@step(@h, a, b, c, d, ptr, 1, 0xa4beea44, 4);
|
||||
@step(@h2, d, a, b, c, ptr, 4, 0x4bdecfa9, 11);
|
||||
@step(@h, c, d, a, b, ptr, 7, 0xf6bb4b60, 16);
|
||||
@step(@h2, b, c, d, a, ptr, 10, 0xbebfbc70, 23);
|
||||
@step(@h, a, b, c, d, ptr, 13, 0x289b7ec6, 4) ;
|
||||
@step(@h2, d, a, b, c, ptr, 0, 0xeaa127fa, 11) ;
|
||||
@step(@h, c, d, a, b, ptr, 3, 0xd4ef3085, 16) ;
|
||||
@step(@h2, b, c, d, a, ptr, 6, 0x04881d05, 23) ;
|
||||
@step(@h, a, b, c, d, ptr, 9, 0xd9d4d039, 4) ;
|
||||
@step(@h2, d, a, b, c, ptr, 12, 0xe6db99e5, 11) ;
|
||||
@step(@h, c, d, a, b, ptr, 15, 0x1fa27cf8, 16) ;
|
||||
@step(@h2, b, c, d, a, ptr, 2, 0xc4ac5665, 23) ;
|
||||
@step(@h, &a, b, c, d, ptr, 5, 0xfffa3942, 4);
|
||||
@step(@h2, &d, a, b, c, ptr, 8, 0x8771f681, 11);
|
||||
@step(@h, &c, d, a, b, ptr, 11, 0x6d9d6122, 16);
|
||||
@step(@h2, &b, c, d, a, ptr, 14, 0xfde5380c, 23);
|
||||
@step(@h, &a, b, c, d, ptr, 1, 0xa4beea44, 4);
|
||||
@step(@h2, &d, a, b, c, ptr, 4, 0x4bdecfa9, 11);
|
||||
@step(@h, &c, d, a, b, ptr, 7, 0xf6bb4b60, 16);
|
||||
@step(@h2, &b, c, d, a, ptr, 10, 0xbebfbc70, 23);
|
||||
@step(@h, &a, b, c, d, ptr, 13, 0x289b7ec6, 4) ;
|
||||
@step(@h2, &d, a, b, c, ptr, 0, 0xeaa127fa, 11) ;
|
||||
@step(@h, &c, d, a, b, ptr, 3, 0xd4ef3085, 16) ;
|
||||
@step(@h2, &b, c, d, a, ptr, 6, 0x04881d05, 23) ;
|
||||
@step(@h, &a, b, c, d, ptr, 9, 0xd9d4d039, 4) ;
|
||||
@step(@h2, &d, a, b, c, ptr, 12, 0xe6db99e5, 11) ;
|
||||
@step(@h, &c, d, a, b, ptr, 15, 0x1fa27cf8, 16) ;
|
||||
@step(@h2, &b, c, d, a, ptr, 2, 0xc4ac5665, 23) ;
|
||||
|
||||
/* Round 4 */
|
||||
@step(@i, a, b, c, d, ptr, 0, 0xf4292244, 6) ;
|
||||
@step(@i, d, a, b, c, ptr, 7, 0x432aff97, 10) ;
|
||||
@step(@i, c, d, a, b, ptr, 14, 0xab9423a7, 15) ;
|
||||
@step(@i, b, c, d, a, ptr, 5, 0xfc93a039, 21) ;
|
||||
@step(@i, a, b, c, d, ptr, 12, 0x655b59c3, 6) ;
|
||||
@step(@i, d, a, b, c, ptr, 3, 0x8f0ccc92, 10) ;
|
||||
@step(@i, c, d, a, b, ptr, 10, 0xffeff47d, 15) ;
|
||||
@step(@i, b, c, d, a, ptr, 1, 0x85845dd1, 21) ;
|
||||
@step(@i, a, b, c, d, ptr, 8, 0x6fa87e4f, 6) ;
|
||||
@step(@i, d, a, b, c, ptr, 15, 0xfe2ce6e0, 10) ;
|
||||
@step(@i, c, d, a, b, ptr, 6, 0xa3014314, 15) ;
|
||||
@step(@i, b, c, d, a, ptr, 13, 0x4e0811a1, 21) ;
|
||||
@step(@i, a, b, c, d, ptr, 4, 0xf7537e82, 6) ;
|
||||
@step(@i, d, a, b, c, ptr, 11, 0xbd3af235, 10) ;
|
||||
@step(@i, c, d, a, b, ptr, 2, 0x2ad7d2bb, 15) ;
|
||||
@step(@i, b, c, d, a, ptr, 9, 0xeb86d391, 21) ;
|
||||
@step(@i, &a, b, c, d, ptr, 0, 0xf4292244, 6) ;
|
||||
@step(@i, &d, a, b, c, ptr, 7, 0x432aff97, 10) ;
|
||||
@step(@i, &c, d, a, b, ptr, 14, 0xab9423a7, 15) ;
|
||||
@step(@i, &b, c, d, a, ptr, 5, 0xfc93a039, 21) ;
|
||||
@step(@i, &a, b, c, d, ptr, 12, 0x655b59c3, 6) ;
|
||||
@step(@i, &d, a, b, c, ptr, 3, 0x8f0ccc92, 10) ;
|
||||
@step(@i, &c, d, a, b, ptr, 10, 0xffeff47d, 15) ;
|
||||
@step(@i, &b, c, d, a, ptr, 1, 0x85845dd1, 21) ;
|
||||
@step(@i, &a, b, c, d, ptr, 8, 0x6fa87e4f, 6) ;
|
||||
@step(@i, &d, a, b, c, ptr, 15, 0xfe2ce6e0, 10) ;
|
||||
@step(@i, &c, d, a, b, ptr, 6, 0xa3014314, 15) ;
|
||||
@step(@i, &b, c, d, a, ptr, 13, 0x4e0811a1, 21) ;
|
||||
@step(@i, &a, b, c, d, ptr, 4, 0xf7537e82, 6) ;
|
||||
@step(@i, &d, a, b, c, ptr, 11, 0xbd3af235, 10) ;
|
||||
@step(@i, &c, d, a, b, ptr, 2, 0x2ad7d2bb, 15) ;
|
||||
@step(@i, &b, c, d, a, ptr, 9, 0xeb86d391, 21) ;
|
||||
|
||||
a += saved_a;
|
||||
b += saved_b;
|
||||
|
||||
@@ -103,13 +103,13 @@ union Long16 @local
|
||||
uint[16] l;
|
||||
}
|
||||
|
||||
macro @blk(&block, i) @local
|
||||
macro blk(Long16* block, i) @local
|
||||
{
|
||||
return (block.l[i & 15] = (block.l[(i + 13) & 15] ^ block.l[(i + 8) & 15]
|
||||
^ block.l[(i + 2) & 15] ^ block.l[i & 15]).rotl(1));
|
||||
}
|
||||
|
||||
macro @blk0(&block, i) @local
|
||||
macro blk0(Long16* block, i) @local
|
||||
{
|
||||
$if env::BIG_ENDIAN:
|
||||
return block.l[i];
|
||||
@@ -119,38 +119,38 @@ macro @blk0(&block, i) @local
|
||||
$endif
|
||||
}
|
||||
|
||||
macro @r0(&block, v, &wref, x, y, &z, i) @local
|
||||
macro r0(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
|
||||
{
|
||||
var w = *wref;
|
||||
*z += ((w & (x ^ y)) ^ y) + @blk0(*block, i) + 0x5A827999 + v.rotl(5);
|
||||
*z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + v.rotl(5);
|
||||
*wref = w.rotl(30);
|
||||
}
|
||||
|
||||
macro @r1(&block, v, &wref, x, y, &z, i) @local
|
||||
macro r1(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
|
||||
{
|
||||
var w = *wref;
|
||||
*z += ((w & (x ^ y)) ^ y) + @blk(*block, i) + 0x5A827999 + v.rotl(5);
|
||||
*z += ((w & (x ^ y)) ^ y) + blk(block, i) + 0x5A827999 + v.rotl(5);
|
||||
*wref = w.rotl(30);
|
||||
}
|
||||
|
||||
macro @r2(&block, v, &wref, x, y, &z, i) @local
|
||||
macro r2(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
|
||||
{
|
||||
var w = *wref;
|
||||
*z += (w ^ x ^ y) + @blk(*block, i) + 0x6ED9EBA1 + v.rotl(5);
|
||||
*z += (w ^ x ^ y) + blk(block, i) + 0x6ED9EBA1 + v.rotl(5);
|
||||
*wref = w.rotl(30);
|
||||
}
|
||||
|
||||
macro @r3(&block, v, &wref, x, y, &z, i) @local
|
||||
macro r3(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
|
||||
{
|
||||
var w = *wref;
|
||||
*z += (((w | x) & y) | (w & x)) + @blk(*block, i) + 0x8F1BBCDC + v.rotl(5);
|
||||
*z += (((w | x) & y) | (w & x)) + blk(block, i) + 0x8F1BBCDC + v.rotl(5);
|
||||
*wref = w.rotl(30);
|
||||
}
|
||||
|
||||
macro @r4(&block, v, &wref, x, y, &z, i) @local
|
||||
macro r4(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
|
||||
{
|
||||
var w = *wref;
|
||||
*z += (w ^ x ^ y) + @blk(*block, i) + 0xCA62C1D6 + v.rotl(5);
|
||||
*z += (w ^ x ^ y) + blk(block, i) + 0xCA62C1D6 + v.rotl(5);
|
||||
*wref = w.rotl(30);
|
||||
}
|
||||
|
||||
@@ -158,100 +158,100 @@ macro @r4(&block, v, &wref, x, y, &z, i) @local
|
||||
@param [&inout] state
|
||||
@param [&in] buffer
|
||||
*>
|
||||
fn void sha1_transform(uint* state, char* buffer) @local
|
||||
fn void sha1_transform(uint[5]* state, char* buffer) @local
|
||||
{
|
||||
Long16 block;
|
||||
block.c[..] = buffer[:64];
|
||||
uint a = state[0];
|
||||
uint b = state[1];
|
||||
uint c = state[2];
|
||||
uint d = state[3];
|
||||
uint e = state[4];
|
||||
@r0(block, a, b, c, d, e, 0);
|
||||
@r0(block, e, a, b, c, d, 1);
|
||||
@r0(block, d, e, a, b, c, 2);
|
||||
@r0(block, c, d, e, a, b, 3);
|
||||
@r0(block, b, c, d, e, a, 4);
|
||||
@r0(block, a, b, c, d, e, 5);
|
||||
@r0(block, e, a, b, c, d, 6);
|
||||
@r0(block, d, e, a, b, c, 7);
|
||||
@r0(block, c, d, e, a, b, 8);
|
||||
@r0(block, b, c, d, e, a, 9);
|
||||
@r0(block, a, b, c, d, e, 10);
|
||||
@r0(block, e, a, b, c, d, 11);
|
||||
@r0(block, d, e, a, b, c, 12);
|
||||
@r0(block, c, d, e, a, b, 13);
|
||||
@r0(block, b, c, d, e, a, 14);
|
||||
@r0(block, a, b, c, d, e, 15);
|
||||
@r1(block, e, a, b, c, d, 16);
|
||||
@r1(block, d, e, a, b, c, 17);
|
||||
@r1(block, c, d, e, a, b, 18);
|
||||
@r1(block, b, c, d, e, a, 19);
|
||||
@r2(block, a, b, c, d, e, 20);
|
||||
@r2(block, e, a, b, c, d, 21);
|
||||
@r2(block, d, e, a, b, c, 22);
|
||||
@r2(block, c, d, e, a, b, 23);
|
||||
@r2(block, b, c, d, e, a, 24);
|
||||
@r2(block, a, b, c, d, e, 25);
|
||||
@r2(block, e, a, b, c, d, 26);
|
||||
@r2(block, d, e, a, b, c, 27);
|
||||
@r2(block, c, d, e, a, b, 28);
|
||||
@r2(block, b, c, d, e, a, 29);
|
||||
@r2(block, a, b, c, d, e, 30);
|
||||
@r2(block, e, a, b, c, d, 31);
|
||||
@r2(block, d, e, a, b, c, 32);
|
||||
@r2(block, c, d, e, a, b, 33);
|
||||
@r2(block, b, c, d, e, a, 34);
|
||||
@r2(block, a, b, c, d, e, 35);
|
||||
@r2(block, e, a, b, c, d, 36);
|
||||
@r2(block, d, e, a, b, c, 37);
|
||||
@r2(block, c, d, e, a, b, 38);
|
||||
@r2(block, b, c, d, e, a, 39);
|
||||
@r3(block, a, b, c, d, e, 40);
|
||||
@r3(block, e, a, b, c, d, 41);
|
||||
@r3(block, d, e, a, b, c, 42);
|
||||
@r3(block, c, d, e, a, b, 43);
|
||||
@r3(block, b, c, d, e, a, 44);
|
||||
@r3(block, a, b, c, d, e, 45);
|
||||
@r3(block, e, a, b, c, d, 46);
|
||||
@r3(block, d, e, a, b, c, 47);
|
||||
@r3(block, c, d, e, a, b, 48);
|
||||
@r3(block, b, c, d, e, a, 49);
|
||||
@r3(block, a, b, c, d, e, 50);
|
||||
@r3(block, e, a, b, c, d, 51);
|
||||
@r3(block, d, e, a, b, c, 52);
|
||||
@r3(block, c, d, e, a, b, 53);
|
||||
@r3(block, b, c, d, e, a, 54);
|
||||
@r3(block, a, b, c, d, e, 55);
|
||||
@r3(block, e, a, b, c, d, 56);
|
||||
@r3(block, d, e, a, b, c, 57);
|
||||
@r3(block, c, d, e, a, b, 58);
|
||||
@r3(block, b, c, d, e, a, 59);
|
||||
@r4(block, a, b, c, d, e, 60);
|
||||
@r4(block, e, a, b, c, d, 61);
|
||||
@r4(block, d, e, a, b, c, 62);
|
||||
@r4(block, c, d, e, a, b, 63);
|
||||
@r4(block, b, c, d, e, a, 64);
|
||||
@r4(block, a, b, c, d, e, 65);
|
||||
@r4(block, e, a, b, c, d, 66);
|
||||
@r4(block, d, e, a, b, c, 67);
|
||||
@r4(block, c, d, e, a, b, 68);
|
||||
@r4(block, b, c, d, e, a, 69);
|
||||
@r4(block, a, b, c, d, e, 70);
|
||||
@r4(block, e, a, b, c, d, 71);
|
||||
@r4(block, d, e, a, b, c, 72);
|
||||
@r4(block, c, d, e, a, b, 73);
|
||||
@r4(block, b, c, d, e, a, 74);
|
||||
@r4(block, a, b, c, d, e, 75);
|
||||
@r4(block, e, a, b, c, d, 76);
|
||||
@r4(block, d, e, a, b, c, 77);
|
||||
@r4(block, c, d, e, a, b, 78);
|
||||
@r4(block, b, c, d, e, a, 79);
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
uint a = (*state)[0];
|
||||
uint b = (*state)[1];
|
||||
uint c = (*state)[2];
|
||||
uint d = (*state)[3];
|
||||
uint e = (*state)[4];
|
||||
r0(&block, a, &b, c, d, &e, 0);
|
||||
r0(&block, e, &a, b, c, &d, 1);
|
||||
r0(&block, d, &e, a, b, &c, 2);
|
||||
r0(&block, c, &d, e, a, &b, 3);
|
||||
r0(&block, b, &c, d, e, &a, 4);
|
||||
r0(&block, a, &b, c, d, &e, 5);
|
||||
r0(&block, e, &a, b, c, &d, 6);
|
||||
r0(&block, d, &e, a, b, &c, 7);
|
||||
r0(&block, c, &d, e, a, &b, 8);
|
||||
r0(&block, b, &c, d, e, &a, 9);
|
||||
r0(&block, a, &b, c, d, &e, 10);
|
||||
r0(&block, e, &a, b, c, &d, 11);
|
||||
r0(&block, d, &e, a, b, &c, 12);
|
||||
r0(&block, c, &d, e, a, &b, 13);
|
||||
r0(&block, b, &c, d, e, &a, 14);
|
||||
r0(&block, a, &b, c, d, &e, 15);
|
||||
r1(&block, e, &a, b, c, &d, 16);
|
||||
r1(&block, d, &e, a, b, &c, 17);
|
||||
r1(&block, c, &d, e, a, &b, 18);
|
||||
r1(&block, b, &c, d, e, &a, 19);
|
||||
r2(&block, a, &b, c, d, &e, 20);
|
||||
r2(&block, e, &a, b, c, &d, 21);
|
||||
r2(&block, d, &e, a, b, &c, 22);
|
||||
r2(&block, c, &d, e, a, &b, 23);
|
||||
r2(&block, b, &c, d, e, &a, 24);
|
||||
r2(&block, a, &b, c, d, &e, 25);
|
||||
r2(&block, e, &a, b, c, &d, 26);
|
||||
r2(&block, d, &e, a, b, &c, 27);
|
||||
r2(&block, c, &d, e, a, &b, 28);
|
||||
r2(&block, b, &c, d, e, &a, 29);
|
||||
r2(&block, a, &b, c, d, &e, 30);
|
||||
r2(&block, e, &a, b, c, &d, 31);
|
||||
r2(&block, d, &e, a, b, &c, 32);
|
||||
r2(&block, c, &d, e, a, &b, 33);
|
||||
r2(&block, b, &c, d, e, &a, 34);
|
||||
r2(&block, a, &b, c, d, &e, 35);
|
||||
r2(&block, e, &a, b, c, &d, 36);
|
||||
r2(&block, d, &e, a, b, &c, 37);
|
||||
r2(&block, c, &d, e, a, &b, 38);
|
||||
r2(&block, b, &c, d, e, &a, 39);
|
||||
r3(&block, a, &b, c, d, &e, 40);
|
||||
r3(&block, e, &a, b, c, &d, 41);
|
||||
r3(&block, d, &e, a, b, &c, 42);
|
||||
r3(&block, c, &d, e, a, &b, 43);
|
||||
r3(&block, b, &c, d, e, &a, 44);
|
||||
r3(&block, a, &b, c, d, &e, 45);
|
||||
r3(&block, e, &a, b, c, &d, 46);
|
||||
r3(&block, d, &e, a, b, &c, 47);
|
||||
r3(&block, c, &d, e, a, &b, 48);
|
||||
r3(&block, b, &c, d, e, &a, 49);
|
||||
r3(&block, a, &b, c, d, &e, 50);
|
||||
r3(&block, e, &a, b, c, &d, 51);
|
||||
r3(&block, d, &e, a, b, &c, 52);
|
||||
r3(&block, c, &d, e, a, &b, 53);
|
||||
r3(&block, b, &c, d, e, &a, 54);
|
||||
r3(&block, a, &b, c, d, &e, 55);
|
||||
r3(&block, e, &a, b, c, &d, 56);
|
||||
r3(&block, d, &e, a, b, &c, 57);
|
||||
r3(&block, c, &d, e, a, &b, 58);
|
||||
r3(&block, b, &c, d, e, &a, 59);
|
||||
r4(&block, a, &b, c, d, &e, 60);
|
||||
r4(&block, e, &a, b, c, &d, 61);
|
||||
r4(&block, d, &e, a, b, &c, 62);
|
||||
r4(&block, c, &d, e, a, &b, 63);
|
||||
r4(&block, b, &c, d, e, &a, 64);
|
||||
r4(&block, a, &b, c, d, &e, 65);
|
||||
r4(&block, e, &a, b, c, &d, 66);
|
||||
r4(&block, d, &e, a, b, &c, 67);
|
||||
r4(&block, c, &d, e, a, &b, 68);
|
||||
r4(&block, b, &c, d, e, &a, 69);
|
||||
r4(&block, a, &b, c, d, &e, 70);
|
||||
r4(&block, e, &a, b, c, &d, 71);
|
||||
r4(&block, d, &e, a, b, &c, 72);
|
||||
r4(&block, c, &d, e, a, &b, 73);
|
||||
r4(&block, b, &c, d, e, &a, 74);
|
||||
r4(&block, a, &b, c, d, &e, 75);
|
||||
r4(&block, e, &a, b, c, &d, 76);
|
||||
r4(&block, d, &e, a, b, &c, 77);
|
||||
r4(&block, c, &d, e, a, &b, 78);
|
||||
r4(&block, b, &c, d, e, &a, 79);
|
||||
(*state)[0] += a;
|
||||
(*state)[1] += b;
|
||||
(*state)[2] += c;
|
||||
(*state)[3] += d;
|
||||
(*state)[4] += e;
|
||||
a = b = c = d = e = 0;
|
||||
buffer[:64] = 0;
|
||||
block = {};
|
||||
}
|
||||
@@ -45,6 +45,10 @@ struct BitWriter
|
||||
uint len;
|
||||
}
|
||||
|
||||
// c3 doesn't allow to shift more than bit width of a variable,
|
||||
// so use closest byte boundary of 24 instead of 32
|
||||
const int WRITER_BITS = 24;
|
||||
|
||||
fn void BitWriter.init(&self, OutStream byte_writer)
|
||||
{
|
||||
*self = { .writer = byte_writer };
|
||||
@@ -53,7 +57,9 @@ fn void BitWriter.init(&self, OutStream byte_writer)
|
||||
fn void! BitWriter.flush(&self)
|
||||
{
|
||||
if (self.len == 0) return;
|
||||
uint bits = self.bits << (32 - self.len);
|
||||
|
||||
int padding = ($sizeof(self.bits) * 8 - self.len);
|
||||
uint bits = self.bits << padding;
|
||||
uint n = (self.len + 7) / 8;
|
||||
char[4] buffer;
|
||||
bitorder::write(bits, &buffer, UIntBE);
|
||||
@@ -62,24 +68,27 @@ fn void! BitWriter.flush(&self)
|
||||
}
|
||||
|
||||
<*
|
||||
@require nbits <= 8
|
||||
@require nbits <= 32
|
||||
*>
|
||||
fn void! BitWriter.write_bits(&self, uint bits, uint nbits)
|
||||
{
|
||||
if (nbits == 0) return;
|
||||
uint n = self.len + nbits;
|
||||
uint to_write = n / 8;
|
||||
uint left = n % 8;
|
||||
if (to_write > 0)
|
||||
while (self.len + nbits > WRITER_BITS)
|
||||
{
|
||||
ulong lbits;
|
||||
if (self.len > 0) lbits = (ulong)self.bits << (64 - self.len);
|
||||
lbits |= (ulong)(bits >> left) << (64 - (n - left));
|
||||
char[8] buffer;
|
||||
bitorder::write(lbits, &buffer, ULongBE);
|
||||
io::write_all(self.writer, buffer[:to_write])!;
|
||||
uint to_push = WRITER_BITS - self.len;
|
||||
uint bits_to_push = (bits >> (nbits - to_push)) & ((1 << to_push) - 1);
|
||||
|
||||
self.bits <<= to_push;
|
||||
self.bits |= bits_to_push;
|
||||
self.len += to_push;
|
||||
nbits -= to_push;
|
||||
|
||||
self.flush()!;
|
||||
}
|
||||
self.bits <<= left;
|
||||
self.bits |= bits & ((1 << left) - 1);
|
||||
self.len = left;
|
||||
|
||||
if (nbits == 0) return;
|
||||
|
||||
self.bits <<= nbits;
|
||||
self.bits |= bits & ((1 << nbits) - 1);
|
||||
self.len += nbits;
|
||||
}
|
||||
@@ -106,7 +106,7 @@ fn void! File.close(&self) @inline @dynamic
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.file
|
||||
@require self.file != null
|
||||
*>
|
||||
fn bool File.eof(&self) @inline
|
||||
{
|
||||
@@ -123,13 +123,22 @@ fn usz! File.read(&self, char[] buffer) @dynamic
|
||||
|
||||
<*
|
||||
@param [out] buffer
|
||||
@require self.file `File must be initialized`
|
||||
@require self.file != null `File must be initialized`
|
||||
*>
|
||||
fn usz! File.write(&self, char[] buffer) @dynamic
|
||||
{
|
||||
return os::native_fwrite(self.file, buffer);
|
||||
}
|
||||
|
||||
fn Fd File.fd(self) @if(env::LIBC)
|
||||
{
|
||||
return libc::fileno(self.file);
|
||||
}
|
||||
|
||||
fn bool File.isatty(self) @if(env::LIBC)
|
||||
{
|
||||
return libc::isatty(self.fd()) > 0;
|
||||
}
|
||||
|
||||
fn char! File.read_byte(&self) @dynamic
|
||||
{
|
||||
@@ -182,8 +191,19 @@ fn char[]! load_temp(String filename)
|
||||
return load_new(filename, allocator::temp());
|
||||
}
|
||||
|
||||
fn void! save(String filename, char[] data)
|
||||
{
|
||||
File file = open(filename, "wb")!;
|
||||
defer (void)file.close();
|
||||
while (data.len)
|
||||
{
|
||||
usz written = file.write(data)!;
|
||||
data = data[written..];
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.file `File must be initialized`
|
||||
@require self.file != null `File must be initialized`
|
||||
*>
|
||||
fn void! File.flush(&self) @dynamic
|
||||
{
|
||||
|
||||
@@ -28,7 +28,8 @@ macro bool is_struct_with_default_print($Type)
|
||||
{
|
||||
return $Type.kindof == STRUCT
|
||||
&&& !$defined($Type.to_format)
|
||||
&&& !$defined($Type.to_new_string);
|
||||
&&& !$defined($Type.to_new_string)
|
||||
&&& !$defined($Type.to_string);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -194,7 +195,7 @@ fn usz! Formatter.out_str(&self, any arg) @private
|
||||
switch (arg.type.kindof)
|
||||
{
|
||||
case ENUM:
|
||||
usz i = types::any_to_int(arg, usz)!!;
|
||||
usz i = types::any_to_enum_ordinal(arg, usz)!!;
|
||||
assert(i < arg.type.names.len, "Illegal enum value found, numerical value was %d.", i);
|
||||
return self.out_substr(arg.type.names[i]);
|
||||
case STRUCT:
|
||||
@@ -237,10 +238,9 @@ fn usz! Formatter.out_str(&self, any arg) @private
|
||||
self.width = width;
|
||||
}
|
||||
self.width = 0;
|
||||
self.out_substr("0x")!;
|
||||
return self.ntoa_any(arg, 16);
|
||||
return self.out_substr("0x")! + self.ntoa_any(arg, 16);
|
||||
case ARRAY:
|
||||
// this is SomeType[*] so grab the "SomeType"
|
||||
// this is SomeType[?] so grab the "SomeType"
|
||||
PrintFlags flags = self.flags;
|
||||
uint width = self.width;
|
||||
defer
|
||||
@@ -274,7 +274,7 @@ fn usz! Formatter.out_str(&self, any arg) @private
|
||||
}
|
||||
self.flags = {};
|
||||
self.width = 0;
|
||||
// this is SomeType[*] so grab the "SomeType"
|
||||
// this is SomeType[?] so grab the "SomeType"
|
||||
typeid inner = arg.type.inner;
|
||||
usz size = inner.sizeof;
|
||||
usz vlen = arg.type.len;
|
||||
@@ -476,6 +476,39 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys)
|
||||
case 'c':
|
||||
total_len += self.out_char(current)!;
|
||||
continue;
|
||||
case 'H':
|
||||
self.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'h':
|
||||
char[] out @noinit;
|
||||
switch (current)
|
||||
{
|
||||
case char[]:
|
||||
out = *current;
|
||||
case ichar[]:
|
||||
out = *(char[]*)current;
|
||||
default:
|
||||
if (current.type.kindof == ARRAY && (current.type.inner == char.typeid || current.type.inner == ichar.typeid))
|
||||
{
|
||||
out = ((char*)current.ptr)[:current.type.sizeof];
|
||||
break;
|
||||
}
|
||||
total_len += self.out_substr("<INVALID>")!;
|
||||
continue;
|
||||
}
|
||||
if (self.flags.left)
|
||||
{
|
||||
usz len = print_hex_chars(self, out, self.flags.uppercase)!;
|
||||
total_len += len;
|
||||
total_len += self.pad(' ', self.width, len)!;
|
||||
continue;
|
||||
}
|
||||
if (self.width)
|
||||
{
|
||||
total_len += self.pad(' ', self.width, out.len * 2)!;
|
||||
}
|
||||
total_len += print_hex_chars(self, out, self.flags.uppercase)!;
|
||||
continue;
|
||||
case 's':
|
||||
if (self.flags.left)
|
||||
{
|
||||
@@ -484,11 +517,14 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys)
|
||||
total_len += self.pad(' ', self.width, len)!;
|
||||
continue;
|
||||
}
|
||||
OutputFn out_fn = self.out_fn;
|
||||
self.out_fn = (OutputFn)&out_null_fn;
|
||||
usz len = self.out_str(current)!;
|
||||
self.out_fn = out_fn;
|
||||
total_len += self.pad(' ', self.width, len)!;
|
||||
if (self.width)
|
||||
{
|
||||
OutputFn out_fn = self.out_fn;
|
||||
self.out_fn = (OutputFn)&out_null_fn;
|
||||
usz len = self.out_str(current)!;
|
||||
self.out_fn = out_fn;
|
||||
total_len += self.pad(' ', self.width, len)!;
|
||||
}
|
||||
total_len += self.out_str(current)!;
|
||||
continue;
|
||||
case 'p':
|
||||
@@ -529,4 +565,4 @@ fn usz! Formatter.print(&self, String str)
|
||||
}
|
||||
foreach (c : str) self.out(c)!;
|
||||
return self.idx;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,22 @@ fault FormattingFault
|
||||
BAD_FORMAT
|
||||
}
|
||||
|
||||
fn usz! print_hex_chars(Formatter* f, char[] out, bool uppercase) @inline
|
||||
{
|
||||
char past_10 = (uppercase ? 'A' : 'a') - 10;
|
||||
usz len = 0;
|
||||
foreach (c : out)
|
||||
{
|
||||
char digit = c >> 4;
|
||||
f.out(digit + (digit < 10 ? '0' : past_10))!;
|
||||
len++;
|
||||
digit = c & 0xf;
|
||||
f.out(digit + (digit < 10 ? '0' : past_10))!;
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
macro Formatter.first_err(&self, anyfault f)
|
||||
{
|
||||
if (self.first_fault) return self.first_fault;
|
||||
@@ -215,7 +231,6 @@ fn usz! Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
|
||||
if (!self.flags.left) len += self.pad(' ', self.width, 3 + pl)!;
|
||||
String s = self.flags.uppercase ? "INF" : "inf";
|
||||
if (math::is_nan(y)) s = self.flags.uppercase ? "NAN" : "nan";
|
||||
len += s.len;
|
||||
if (pl) len += self.out(is_neg ? '-' : '+')!;
|
||||
len += self.out_chars(s)!;
|
||||
if (self.flags.left) len += self.pad(' ', self.width, 3 + pl)!;
|
||||
@@ -606,6 +621,10 @@ fn usz! Formatter.ntoa_any(&self, any arg, uint base) @private
|
||||
|
||||
fn usz! Formatter.out_char(&self, any arg) @private
|
||||
{
|
||||
if (!arg.type.kindof.is_int())
|
||||
{
|
||||
return self.out_substr("<NOT CHAR>");
|
||||
}
|
||||
usz len = 1;
|
||||
uint l = 1;
|
||||
// pre padding
|
||||
|
||||
@@ -227,7 +227,14 @@ fn void! out_putstream_fn(void* data, char c) @private
|
||||
|
||||
fn void! out_putchar_fn(void* data @unused, char c) @private
|
||||
{
|
||||
libc::putchar(c);
|
||||
$if env::TESTING:
|
||||
// HACK: this is used for the purpose of unit test output hijacking
|
||||
File* stdout = io::stdout();
|
||||
assert(stdout.file);
|
||||
libc::fputc(c, stdout.file);
|
||||
$else
|
||||
libc::putchar(c);
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -263,7 +270,7 @@ fn usz! printfn(String format, args...) @maydiscard
|
||||
Formatter formatter;
|
||||
formatter.init(&out_putchar_fn);
|
||||
usz! len = formatter.vprintf(format, args);
|
||||
putchar('\n');
|
||||
out_putchar_fn(null, '\n')!;
|
||||
io::stdout().flush()!;
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
@@ -5,62 +5,53 @@ import libc;
|
||||
@require mode.len > 0
|
||||
@require filename.len > 0
|
||||
*>
|
||||
fn void*! native_fopen(String filename, String mode) @inline
|
||||
fn void*! native_fopen(String filename, String mode) @inline => @pool()
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
$if env::WIN32:
|
||||
void* file = libc::_wfopen(filename.to_temp_wstring(), mode.to_temp_wstring())!;
|
||||
$else
|
||||
void* file = libc::fopen(filename.zstr_tcopy(), mode.zstr_tcopy());
|
||||
$endif
|
||||
return file ?: file_open_errno()?;
|
||||
};
|
||||
$if env::WIN32:
|
||||
void* file = libc::_wfopen(filename.to_temp_wstring(), mode.to_temp_wstring())!;
|
||||
$else
|
||||
void* file = libc::fopen(filename.zstr_tcopy(), mode.zstr_tcopy());
|
||||
$endif
|
||||
return file ?: file_open_errno()?;
|
||||
}
|
||||
|
||||
fn void! native_remove(String filename)
|
||||
fn void! native_remove(String filename) => @pool()
|
||||
{
|
||||
@pool()
|
||||
$if env::WIN32:
|
||||
CInt result = libc::_wremove(filename.to_temp_wstring())!;
|
||||
$else
|
||||
CInt result = libc::remove(filename.zstr_tcopy());
|
||||
$endif
|
||||
if (result)
|
||||
{
|
||||
$if env::WIN32:
|
||||
CInt result = libc::_wremove(filename.to_temp_wstring())!;
|
||||
$else
|
||||
CInt result = libc::remove(filename.zstr_tcopy());
|
||||
$endif
|
||||
if (result)
|
||||
switch (libc::errno())
|
||||
{
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::ENOENT:
|
||||
return IoError.FILE_NOT_FOUND?;
|
||||
case errno::EACCES:
|
||||
default:
|
||||
return IoError.FILE_CANNOT_DELETE?;
|
||||
}
|
||||
case errno::ENOENT:
|
||||
return IoError.FILE_NOT_FOUND?;
|
||||
case errno::EACCES:
|
||||
default:
|
||||
return IoError.FILE_CANNOT_DELETE?;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@require mode.len > 0
|
||||
@require filename.len > 0
|
||||
*>
|
||||
fn void*! native_freopen(void* file, String filename, String mode) @inline
|
||||
fn void*! native_freopen(void* file, String filename, String mode) @inline => @pool()
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
$if env::WIN32:
|
||||
file = libc::_wfreopen(filename.to_temp_wstring(), mode.to_temp_wstring(), file)!;
|
||||
$else
|
||||
file = libc::freopen(filename.zstr_tcopy(), mode.zstr_tcopy(), file);
|
||||
$endif
|
||||
return file ?: file_open_errno()?;
|
||||
};
|
||||
$if env::WIN32:
|
||||
file = libc::_wfreopen(filename.to_temp_wstring(), mode.to_temp_wstring(), file)!;
|
||||
$else
|
||||
file = libc::freopen(filename.zstr_tcopy(), mode.zstr_tcopy(), file);
|
||||
$endif
|
||||
return file ?: file_open_errno()?;
|
||||
}
|
||||
|
||||
fn void! native_fseek(void* file, isz offset, Seek seek_mode) @inline
|
||||
{
|
||||
if (libc::fseek(file, (SeekIndex)offset, (CInt)seek_mode)) return file_seek_errno()?;
|
||||
if (libc::fseek(file, (SeekIndex)offset, seek_mode.ordinal)) return file_seek_errno()?;
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +68,7 @@ fn usz! native_fwrite(CFile file, char[] buffer) @inline
|
||||
|
||||
fn void! native_fputc(CInt c, CFile stream) @inline
|
||||
{
|
||||
if (!libc::fputc(c, stream)) return IoError.EOF?;
|
||||
if (libc::fputc(c, stream) == libc::EOF) return IoError.EOF?;
|
||||
}
|
||||
|
||||
fn usz! native_fread(CFile file, char[] buffer) @inline
|
||||
|
||||
@@ -1,56 +1,50 @@
|
||||
module std::io::os;
|
||||
import libc, std::os, std::io;
|
||||
|
||||
fn void! native_stat(Stat* stat, String path) @if(env::DARWIN || env::LINUX || env::BSD_FAMILY)
|
||||
fn void! native_stat(Stat* stat, String path) @if(env::DARWIN || env::LINUX || env::BSD_FAMILY) => @pool()
|
||||
{
|
||||
@pool()
|
||||
$if env::DARWIN || env::LINUX || env::BSD_FAMILY:
|
||||
int res = libc::stat(path.zstr_tcopy(), stat);
|
||||
$else
|
||||
unreachable("Stat unimplemented");
|
||||
int res = 0;
|
||||
$endif
|
||||
if (res != 0)
|
||||
{
|
||||
$if env::DARWIN || env::LINUX || env::BSD_FAMILY:
|
||||
int res = libc::stat(path.zstr_tcopy(), stat);
|
||||
$else
|
||||
unreachable("Stat unimplemented");
|
||||
int res = 0;
|
||||
$endif
|
||||
if (res != 0)
|
||||
switch (libc::errno())
|
||||
{
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EBADF:
|
||||
return IoError.FILE_NOT_VALID?;
|
||||
case errno::EFAULT:
|
||||
unreachable("Invalid stat");
|
||||
case errno::EIO:
|
||||
return IoError.GENERAL_ERROR?;
|
||||
case errno::EACCES:
|
||||
return IoError.NO_PERMISSION?;
|
||||
case errno::ELOOP:
|
||||
return IoError.NO_PERMISSION?;
|
||||
case errno::ENAMETOOLONG:
|
||||
return IoError.NAME_TOO_LONG?;
|
||||
case errno::ENOENT:
|
||||
return IoError.FILE_NOT_FOUND?;
|
||||
case errno::ENOTDIR:
|
||||
return IoError.FILE_NOT_DIR?;
|
||||
case errno::EOVERFLOW:
|
||||
return IoError.GENERAL_ERROR?;
|
||||
default:
|
||||
return IoError.UNKNOWN_ERROR?;
|
||||
}
|
||||
case errno::EBADF:
|
||||
return IoError.FILE_NOT_VALID?;
|
||||
case errno::EFAULT:
|
||||
unreachable("Invalid stat");
|
||||
case errno::EIO:
|
||||
return IoError.GENERAL_ERROR?;
|
||||
case errno::EACCES:
|
||||
return IoError.NO_PERMISSION?;
|
||||
case errno::ELOOP:
|
||||
return IoError.NO_PERMISSION?;
|
||||
case errno::ENAMETOOLONG:
|
||||
return IoError.NAME_TOO_LONG?;
|
||||
case errno::ENOENT:
|
||||
return IoError.FILE_NOT_FOUND?;
|
||||
case errno::ENOTDIR:
|
||||
return IoError.FILE_NOT_DIR?;
|
||||
case errno::EOVERFLOW:
|
||||
return IoError.GENERAL_ERROR?;
|
||||
default:
|
||||
return IoError.UNKNOWN_ERROR?;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn usz! native_file_size(String path) @if(env::WIN32)
|
||||
fn usz! native_file_size(String path) @if(env::WIN32) => @pool()
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
Win32_FILE_ATTRIBUTE_DATA data;
|
||||
win32::getFileAttributesExW(path.to_temp_wstring()!, Win32_GET_FILEEX_INFO_LEVELS.STANDARD, &data);
|
||||
Win32_LARGE_INTEGER size;
|
||||
size.lowPart = data.nFileSizeLow;
|
||||
size.highPart = data.nFileSizeHigh;
|
||||
return (usz)size.quadPart;
|
||||
};
|
||||
Win32_FILE_ATTRIBUTE_DATA data;
|
||||
win32::getFileAttributesExW(path.to_temp_wstring()!, Win32_GET_FILEEX_INFO_LEVELS.STANDARD, &data);
|
||||
Win32_LARGE_INTEGER size;
|
||||
size.lowPart = data.nFileSizeLow;
|
||||
size.highPart = data.nFileSizeHigh;
|
||||
return (usz)size.quadPart;
|
||||
}
|
||||
|
||||
fn usz! native_file_size(String path) @if(!env::WIN32 && !env::DARWIN)
|
||||
|
||||
@@ -2,7 +2,7 @@ module std::io::os @if(env::POSIX);
|
||||
import std::io, std::os, libc;
|
||||
|
||||
<*
|
||||
@require dir.str_view()
|
||||
@require dir.str_view().len > 0
|
||||
*>
|
||||
fn void! native_rmtree(Path dir)
|
||||
{
|
||||
|
||||
@@ -11,16 +11,13 @@ fn Path! native_temp_directory(Allocator allocator = allocator::heap()) @if(!env
|
||||
return path::new("/tmp", allocator);
|
||||
}
|
||||
|
||||
fn Path! native_temp_directory(Allocator allocator = allocator::heap()) @if(env::WIN32)
|
||||
fn Path! native_temp_directory(Allocator allocator = allocator::heap()) @if(env::WIN32) => @pool(allocator)
|
||||
{
|
||||
@pool(allocator)
|
||||
{
|
||||
Win32_DWORD len = win32::getTempPathW(0, null);
|
||||
if (!len) return IoError.GENERAL_ERROR?;
|
||||
Char16[] buff = mem::temp_alloc_array(Char16, len + (usz)1);
|
||||
if (!win32::getTempPathW(len, buff)) return IoError.GENERAL_ERROR?;
|
||||
return path::new(string::temp_from_utf16(buff[:len]), allocator);
|
||||
};
|
||||
Win32_DWORD len = win32::getTempPathW(0, null);
|
||||
if (!len) return IoError.GENERAL_ERROR?;
|
||||
Char16[] buff = mem::temp_alloc_array(Char16, len + (usz)1);
|
||||
if (!win32::getTempPathW(len, buff)) return IoError.GENERAL_ERROR?;
|
||||
return path::new(string::temp_from_utf16(buff[:len]), allocator);
|
||||
}
|
||||
|
||||
module std::io::os @if(env::NO_LIBC);
|
||||
|
||||
@@ -29,12 +29,9 @@ enum PathEnv
|
||||
POSIX
|
||||
}
|
||||
|
||||
fn Path! new_cwd(Allocator allocator = allocator::heap())
|
||||
fn Path! new_cwd(Allocator allocator = allocator::heap()) => @pool(allocator)
|
||||
{
|
||||
@pool(allocator)
|
||||
{
|
||||
return new(os::getcwd(allocator::temp()), allocator);
|
||||
};
|
||||
return new(os::getcwd(allocator::temp()), allocator);
|
||||
}
|
||||
|
||||
fn Path! getcwd(Allocator allocator = allocator::heap()) @deprecated("Use new_cwd()")
|
||||
@@ -164,12 +161,9 @@ fn Path! temp_new(String path, PathEnv path_env = DEFAULT_PATH_ENV)
|
||||
return new(path, allocator::temp(), path_env);
|
||||
}
|
||||
|
||||
fn Path! new_win32_wstring(WString path, Allocator allocator = allocator::heap())
|
||||
fn Path! new_win32_wstring(WString path, Allocator allocator = allocator::heap()) => @pool(allocator)
|
||||
{
|
||||
@pool(allocator)
|
||||
{
|
||||
return path::new(string::temp_from_wstring(path)!, allocator: allocator);
|
||||
};
|
||||
return path::new(string::temp_from_wstring(path)!, allocator: allocator);
|
||||
}
|
||||
|
||||
fn Path! new_windows(String path, Allocator allocator = allocator::heap())
|
||||
@@ -401,7 +395,7 @@ fn Path! Path.parent(self)
|
||||
|
||||
fn String! normalize(String path_str, PathEnv path_env = DEFAULT_PATH_ENV)
|
||||
{
|
||||
if (!path_str.len) return "";
|
||||
if (!path_str.len) return path_str;
|
||||
usz path_start = volume_name_len(path_str, path_env)!;
|
||||
if (path_start > 0 && path_env == PathEnv.WIN32)
|
||||
{
|
||||
@@ -512,7 +506,11 @@ fn String! normalize(String path_str, PathEnv path_env = DEFAULT_PATH_ENV)
|
||||
if (len > path_start + 1 && is_separator(path_str[len - 1], path_env)) len--;
|
||||
if (path_str.len > len) path_str.ptr[len] = 0;
|
||||
// Empty path after normalization -> "."
|
||||
if (!len) return ".";
|
||||
if (!len)
|
||||
{
|
||||
path_str[0] = '.';
|
||||
return path_str[:1];
|
||||
}
|
||||
return path_str[:len];
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,12 @@ macro usz! write_all(stream, char[] buffer)
|
||||
return n;
|
||||
}
|
||||
|
||||
macro usz! @read_using_read_byte(&s, char[] buffer)
|
||||
macro usz! @read_using_read_byte(&s, char[] buffer) @deprecated
|
||||
{
|
||||
return read_using_read_byte(*s, buffer);
|
||||
}
|
||||
|
||||
macro usz! read_using_read_byte(s, char[] buffer)
|
||||
{
|
||||
usz len = 0;
|
||||
foreach (&cptr : buffer)
|
||||
@@ -121,17 +126,26 @@ macro usz! @read_using_read_byte(&s, char[] buffer)
|
||||
return len;
|
||||
}
|
||||
|
||||
macro void! @write_byte_using_write(&s, char c)
|
||||
macro void! write_byte_using_write(s, char c)
|
||||
{
|
||||
char[1] buff = { c };
|
||||
(*s).write(&buff)!;
|
||||
s.write(&buff)!;
|
||||
}
|
||||
|
||||
macro void! @write_byte_using_write(&s, char c) @deprecated
|
||||
{
|
||||
return write_byte_using_write(*s, c);
|
||||
}
|
||||
|
||||
macro char! @read_byte_using_read(&s)
|
||||
macro char! @read_byte_using_read(&s) @deprecated
|
||||
{
|
||||
return read_byte_using_read(*s);
|
||||
}
|
||||
|
||||
macro char! read_byte_using_read(s)
|
||||
{
|
||||
char[1] buffer;
|
||||
usz read = (*s).read(&buffer)!;
|
||||
usz read = s.read(&buffer)!;
|
||||
if (read != 1) return IoError.EOF?;
|
||||
return buffer[0];
|
||||
}
|
||||
@@ -139,13 +153,23 @@ macro char! @read_byte_using_read(&s)
|
||||
def ReadByteFn = fn char!();
|
||||
|
||||
|
||||
macro usz! @write_using_write_byte(&s, char[] bytes)
|
||||
macro usz! write_using_write_byte(s, char[] bytes)
|
||||
{
|
||||
foreach (c : bytes) s.write_byte(self, c)!;
|
||||
return bytes.len;
|
||||
}
|
||||
|
||||
macro void! @pushback_using_seek(&s)
|
||||
macro usz! @write_using_write_byte(&s, char[] bytes) @deprecated
|
||||
{
|
||||
return write_using_write_byte(*s, bytes);
|
||||
}
|
||||
|
||||
macro void! pushback_using_seek(s)
|
||||
{
|
||||
s.seek(-1, CURSOR)!;
|
||||
}
|
||||
|
||||
macro void! @pushback_using_seek(&s) @deprecated
|
||||
{
|
||||
s.seek(-1, CURSOR)!;
|
||||
}
|
||||
@@ -184,7 +208,7 @@ macro usz! copy_through_buffer(InStream in, OutStream dst, char[] buffer) @local
|
||||
}
|
||||
}
|
||||
|
||||
const char[*] MAX_VARS @private = { [2] = 3, [4] = 5, [8] = 10 };
|
||||
const char[?] MAX_VARS @private = { [2] = 3, [4] = 5, [8] = 10 };
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
|
||||
@@ -121,13 +121,16 @@ fn usz! WriteBuffer.write(&self, char[] bytes) @dynamic
|
||||
fn void! WriteBuffer.write_byte(&self, char c) @dynamic
|
||||
{
|
||||
usz n = self.bytes.len - self.index;
|
||||
if (n == 0) self.write_pending()!;
|
||||
self.bytes[0] = c;
|
||||
self.index = 1;
|
||||
if (n == 0)
|
||||
{
|
||||
self.write_pending()!;
|
||||
}
|
||||
self.bytes[self.index] = c;
|
||||
self.index += 1;
|
||||
}
|
||||
|
||||
fn void! WriteBuffer.write_pending(&self) @local
|
||||
{
|
||||
self.index -= self.wrapped_stream.write(self.bytes[:self.index])!;
|
||||
if (self.index != 0) return IoError.INCOMPLETE_WRITE?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,15 +16,15 @@ struct ByteBuffer (InStream, OutStream)
|
||||
max_read defines how many bytes might be kept before its internal buffer is shrinked.
|
||||
@require self.bytes.len == 0 "Buffer already initialized."
|
||||
*>
|
||||
fn ByteBuffer*! ByteBuffer.new_init(&self, usz max_read, usz initial_capacity = 16, Allocator allocator = allocator::heap())
|
||||
fn ByteBuffer* ByteBuffer.new_init(&self, usz max_read, usz initial_capacity = 16, Allocator allocator = allocator::heap())
|
||||
{
|
||||
*self = { .allocator = allocator, .max_read = max_read };
|
||||
initial_capacity = max(initial_capacity, 16);
|
||||
self.grow(initial_capacity)!;
|
||||
self.grow(initial_capacity);
|
||||
return self;
|
||||
}
|
||||
|
||||
fn ByteBuffer*! ByteBuffer.temp_init(&self, usz max_read, usz initial_capacity = 16)
|
||||
fn ByteBuffer* ByteBuffer.temp_init(&self, usz max_read, usz initial_capacity = 16)
|
||||
{
|
||||
return self.new_init(max_read, initial_capacity, allocator::temp());
|
||||
}
|
||||
@@ -33,7 +33,7 @@ fn ByteBuffer*! ByteBuffer.temp_init(&self, usz max_read, usz initial_capacity =
|
||||
@require buf.len > 0
|
||||
@require self.bytes.len == 0 "Buffer already initialized."
|
||||
*>
|
||||
fn ByteBuffer*! ByteBuffer.init_with_buffer(&self, char[] buf)
|
||||
fn ByteBuffer* ByteBuffer.init_with_buffer(&self, char[] buf)
|
||||
{
|
||||
*self = { .max_read = buf.len, .bytes = buf };
|
||||
return self;
|
||||
@@ -48,7 +48,7 @@ fn void ByteBuffer.free(&self)
|
||||
fn usz! ByteBuffer.write(&self, char[] bytes) @dynamic
|
||||
{
|
||||
usz cap = self.bytes.len - self.write_idx;
|
||||
if (cap < bytes.len) self.grow(bytes.len)!;
|
||||
if (cap < bytes.len) self.grow(bytes.len);
|
||||
self.bytes[self.write_idx:bytes.len] = bytes[..];
|
||||
self.write_idx += bytes.len;
|
||||
return bytes.len;
|
||||
@@ -57,7 +57,7 @@ fn usz! ByteBuffer.write(&self, char[] bytes) @dynamic
|
||||
fn void! ByteBuffer.write_byte(&self, char c) @dynamic
|
||||
{
|
||||
usz cap = self.bytes.len - self.write_idx;
|
||||
if (cap == 0) self.grow(1)!;
|
||||
if (cap == 0) self.grow(1);
|
||||
self.bytes[self.write_idx] = c;
|
||||
self.write_idx++;
|
||||
}
|
||||
@@ -128,10 +128,10 @@ fn usz! ByteBuffer.available(&self) @inline @dynamic
|
||||
return self.write_idx - self.read_idx;
|
||||
}
|
||||
|
||||
fn void! ByteBuffer.grow(&self, usz n)
|
||||
fn void ByteBuffer.grow(&self, usz n)
|
||||
{
|
||||
n = math::next_power_of_2(n);
|
||||
char* p = allocator::realloc_aligned(self.allocator, self.bytes, n, alignment: char.alignof)!;
|
||||
char* p = allocator::realloc(self.allocator, self.bytes, n);
|
||||
self.bytes = p[:n];
|
||||
}
|
||||
|
||||
|
||||
70
lib/std/io/stream/multireader.c3
Normal file
70
lib/std/io/stream/multireader.c3
Normal file
@@ -0,0 +1,70 @@
|
||||
module std::io;
|
||||
|
||||
/* MultiReader implements the InStream interface and provides a logical
|
||||
* concatenation of the provided readers. They are read sequentially. If all the
|
||||
* data has been read, IoError.EOF is returned.
|
||||
*/
|
||||
struct MultiReader (InStream)
|
||||
{
|
||||
InStream[] readers;
|
||||
usz index;
|
||||
Allocator allocator;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
@param [&inout] self
|
||||
@param [&inout] allocator
|
||||
@require self.readers.len == 0 "Init may not run on already initialized data"
|
||||
@ensure self.index == 0
|
||||
*>
|
||||
fn MultiReader* MultiReader.new_init(&self, InStream... readers, Allocator allocator = allocator::heap())
|
||||
{
|
||||
InStream []copy = allocator::new_array(allocator, InStream, readers.len);
|
||||
copy[..] = readers[..];
|
||||
*self = { .readers = copy, .allocator = allocator };
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] self
|
||||
@require self.readers.len == 0 "Init may not run on already initialized data"
|
||||
@ensure self.index == 0
|
||||
*>
|
||||
fn MultiReader* MultiReader.temp_init(&self, InStream... readers)
|
||||
{
|
||||
return self.new_init(...readers, allocator: allocator::temp());
|
||||
}
|
||||
|
||||
fn void MultiReader.free(&self)
|
||||
{
|
||||
if (!self.allocator) return;
|
||||
allocator::free(self.allocator, self.readers);
|
||||
*self = {};
|
||||
}
|
||||
|
||||
fn usz! MultiReader.read(&self, char[] bytes) @dynamic
|
||||
{
|
||||
InStream r = self.readers[self.index];
|
||||
usz! n = r.read(bytes);
|
||||
if (catch err = n)
|
||||
{
|
||||
case IoError.EOF:
|
||||
self.index++;
|
||||
if (self.index >= self.readers.len)
|
||||
{
|
||||
return IoError.EOF?;
|
||||
}
|
||||
return self.read(bytes);
|
||||
default:
|
||||
return err?;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
fn char! MultiReader.read_byte(&self) @dynamic
|
||||
{
|
||||
char[1] data;
|
||||
self.read(data[..])!;
|
||||
return data[0];
|
||||
}
|
||||
59
lib/std/io/stream/multiwriter.c3
Normal file
59
lib/std/io/stream/multiwriter.c3
Normal file
@@ -0,0 +1,59 @@
|
||||
module std::io;
|
||||
|
||||
/* MultiWriter implements the OutStream interface and duplicates any write
|
||||
* operation to all the wrapped writers.
|
||||
*/
|
||||
struct MultiWriter (OutStream)
|
||||
{
|
||||
OutStream[] writers;
|
||||
Allocator allocator;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] self
|
||||
@param [&inout] allocator
|
||||
@require writers.len > 0
|
||||
@require self.writers.len == 0 "Init may not run on already initialized data"
|
||||
*>
|
||||
fn MultiWriter* MultiWriter.new_init(&self, OutStream... writers, Allocator allocator = allocator::heap())
|
||||
{
|
||||
OutStream[] copy = allocator::new_array(allocator, OutStream, writers.len);
|
||||
copy[..] = writers[..];
|
||||
*self = { .writers = copy, .allocator = allocator };
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] self
|
||||
@require writers.len > 0
|
||||
@require self.writers.len == 0 "Init may not run on already initialized data"
|
||||
*>
|
||||
fn MultiWriter* MultiWriter.temp_init(&self, OutStream... writers)
|
||||
{
|
||||
return self.new_init(...writers, allocator: allocator::temp());
|
||||
}
|
||||
|
||||
fn void MultiWriter.free(&self)
|
||||
{
|
||||
if (!self.allocator) return;
|
||||
allocator::free(self.allocator, self.writers);
|
||||
*self = {};
|
||||
}
|
||||
|
||||
fn usz! MultiWriter.write(&self, char[] bytes) @dynamic
|
||||
{
|
||||
usz n;
|
||||
foreach (w : self.writers)
|
||||
{
|
||||
n = w.write(bytes)!;
|
||||
if (n != bytes.len) return IoError.INCOMPLETE_WRITE?;
|
||||
}
|
||||
return bytes.len;
|
||||
}
|
||||
|
||||
fn void! MultiWriter.write_byte(&self, char c) @dynamic
|
||||
{
|
||||
char[1] data;
|
||||
data[0] = c;
|
||||
self.write(data[..])!;
|
||||
}
|
||||
42
lib/std/io/stream/teereader.c3
Normal file
42
lib/std/io/stream/teereader.c3
Normal file
@@ -0,0 +1,42 @@
|
||||
module std::io;
|
||||
|
||||
struct TeeReader (InStream)
|
||||
{
|
||||
InStream r;
|
||||
OutStream w;
|
||||
}
|
||||
|
||||
<* Returns a reader that implements InStream and that will write any data read
|
||||
from the wrapped reader r to the writer w. There is no internal buffering.
|
||||
|
||||
@param [&inout] r "Stream r to read from."
|
||||
@param [&inout] w "Stream w to write to what it reads from r."
|
||||
*>
|
||||
macro TeeReader tee_reader(InStream r, OutStream w) => { r, w };
|
||||
|
||||
<*
|
||||
@param [&inout] self
|
||||
@param [&inout] r "Stream r to read from."
|
||||
@param [&inout] w "Stream w to write to what it reads from r."
|
||||
*>
|
||||
fn TeeReader* TeeReader.init(&self, InStream r, OutStream w)
|
||||
{
|
||||
*self = tee_reader(r, w);
|
||||
return self;
|
||||
}
|
||||
|
||||
fn usz! TeeReader.read(&self, char[] bytes) @dynamic
|
||||
{
|
||||
usz nr, nw;
|
||||
nr = self.r.read(bytes)!;
|
||||
nw = self.w.write(bytes[:nr])!;
|
||||
if (nr != nw) return IoError.GENERAL_ERROR?;
|
||||
return nr;
|
||||
}
|
||||
|
||||
fn char! TeeReader.read_byte(&self) @dynamic
|
||||
{
|
||||
char[1] data;
|
||||
self.read(data[..])!;
|
||||
return data[0];
|
||||
}
|
||||
@@ -43,7 +43,8 @@ const CInt SIGINT = 2;
|
||||
const CInt SIGQUIT = 3;
|
||||
const CInt SIGILL = 4;
|
||||
const CInt SIGTRAP = 5;
|
||||
const CInt SIGABTR = 6;
|
||||
const CInt SIGABRT = 6;
|
||||
const CInt SIGABTR @deprecated("use SIGABRT") = SIGABRT;
|
||||
const CInt SIGBUS = BSD_FLAVOR_SIG ? 10 : 7; // Or Mips
|
||||
const CInt SIGFPE = 8;
|
||||
const CInt SIGKILL = 9;
|
||||
@@ -63,12 +64,6 @@ const bool BSD_FLAVOR_SIG @local = env::DARWIN || env::BSD_FAMILY;
|
||||
def Time_t = $typefrom(env::WIN32 ? long.typeid : CLong.typeid);
|
||||
def Off_t = $typefrom(env::WIN32 ? int.typeid : usz.typeid);
|
||||
|
||||
struct Timespec
|
||||
{
|
||||
Time_t tv_sec;
|
||||
CLong tv_nsec;
|
||||
}
|
||||
|
||||
module libc @if(env::LIBC);
|
||||
|
||||
extern fn void abort();
|
||||
@@ -114,6 +109,7 @@ extern fn ZString getenv(ZString name);
|
||||
extern fn ZString gets(char* buffer);
|
||||
extern fn Tm* gmtime(Time_t* timer);
|
||||
extern fn Tm* gmtime_r(Time_t *timer, Tm* buf) @if(!env::WIN32);
|
||||
extern fn CInt isatty(Fd fd) @if(!env::WIN32);
|
||||
extern fn CLong labs(CLong x);
|
||||
extern fn LongDivResult ldiv(CLong number, CLong denom);
|
||||
extern fn Tm* localtime(Time_t* timer);
|
||||
@@ -171,11 +167,12 @@ extern fn double strtod(char* str, char** endptr);
|
||||
extern fn float strtof(char* str, char** endptr);
|
||||
extern fn ZString strtok(ZString str, ZString delim);
|
||||
extern fn CLong strtol(char* str, char** endptr, CInt base);
|
||||
extern fn CULong strtul(char* str, char** endptr, CInt base);
|
||||
extern fn CULong strtoul(char* str, char** endptr, CInt base);
|
||||
extern fn usz strxfrm(char* dest, ZString src, usz n);
|
||||
extern fn CInt system(ZString str);
|
||||
extern fn Time_t timegm(Tm *timeptr) @if(!env::WIN32);
|
||||
extern fn ZString tmpnam(ZString str);
|
||||
extern fn CFile tmpfile();
|
||||
extern fn CInt ungetc(CInt c, CFile stream);
|
||||
extern fn CInt unsetenv(ZString name);
|
||||
extern fn isz write(Fd fd, void* buffer, usz count) @if(!env::WIN32);
|
||||
|
||||
@@ -17,10 +17,10 @@ struct Stat
|
||||
Gid_t st_gid;
|
||||
Dev_t st_rdev;
|
||||
|
||||
Timespec st_atimespec; // time of last access
|
||||
Timespec st_mtimespec; // time of last data modification
|
||||
Timespec st_ctimespec; // time of last status change
|
||||
Timespec st_birthtimespec; // time of file creation(birth)
|
||||
TimeSpec st_atimespec; // time of last access
|
||||
TimeSpec st_mtimespec; // time of last data modification
|
||||
TimeSpec st_ctimespec; // time of last status change
|
||||
TimeSpec st_birthtimespec; // time of file creation(birth)
|
||||
Off_t st_size; // file size, in bytes
|
||||
Blkcnt_t st_blocks; // blocks allocated for file
|
||||
Blksize_t st_blocksize; // optimal blocksize for I/O
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
module libc @if(env::POSIX);
|
||||
|
||||
const CInt SHUT_RD = 0;
|
||||
const CInt SHUT_WR = 1;
|
||||
const CInt SHUT_RDWR = 2;
|
||||
extern fn CInt shutdown(Fd sockfd, CInt how);
|
||||
|
||||
extern fn isz recv(Fd socket, void *buffer, usz length, CInt flags);
|
||||
extern fn isz send(Fd socket, void *buffer, usz length, CInt flags);
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ extern fn CInt _fseeki64(CFile, long, int); def fseek = _fseeki64;
|
||||
extern fn CLong _ftelli64(CFile); def ftell = _ftelli64;
|
||||
extern fn Errno _get_timezone(CLong *timezone);
|
||||
extern fn Tm* _gmtime64_s(Tm* buf, Time_t *timer);
|
||||
extern fn CInt _isatty(Fd fd); def isatty = _isatty;
|
||||
extern fn Tm* _localtime64_s(Tm* buf, Time_t *timer);
|
||||
extern fn Time_t _mkgmtime64(Tm* timeptr); def timegm = _mkgmtime64;
|
||||
extern fn Time_t _mktime64(Tm *timeptr); def mktime = _mktime64;
|
||||
@@ -23,6 +24,11 @@ extern fn CInt _wremove(WString);
|
||||
extern fn int recv(Win32_SOCKET s, void* buf, int len, int flags);
|
||||
extern fn int send(Win32_SOCKET s, void* buf, int len, int flags);
|
||||
|
||||
const CInt SD_RECEIVE = 0;
|
||||
const CInt SD_SEND = 1;
|
||||
const CInt SD_BOTH = 2;
|
||||
extern fn CInt shutdown(Win32_SOCKET s, CInt how);
|
||||
|
||||
struct SystemInfo
|
||||
{
|
||||
union {
|
||||
@@ -50,4 +56,4 @@ macro Tm* localtime_r(Time_t* timer, Tm* buf) => _localtime64_s(buf, timer);
|
||||
macro CInt setjmp(JmpBuf* buffer) => _setjmp(buffer, null);
|
||||
macro Tm* gmtime_r(Time_t* timer, Tm* buf) => _gmtime64_s(buf, timer);
|
||||
macro isz read(Fd fd, void* buffer, usz buffer_size) => _read(fd, buffer, (CUInt)buffer_size);
|
||||
macro isz write(Fd fd, void* buffer, usz count) => _write(fd, buffer, (CUInt)count);
|
||||
macro isz write(Fd fd, void* buffer, usz count) => _write(fd, buffer, (CUInt)count);
|
||||
|
||||
@@ -35,7 +35,7 @@ fn BigInt* BigInt.init(&self, int128 value)
|
||||
len++;
|
||||
}
|
||||
assert(value < 0 || tmp == 0 || !self.is_negative());
|
||||
assert(value > 0 || tmp == -1 || self.is_negative());
|
||||
assert(value >= 0 || tmp == -1 || self.is_negative());
|
||||
self.len = len;
|
||||
self.reduce_len();
|
||||
return self;
|
||||
@@ -92,9 +92,9 @@ fn BigInt*! BigInt.init_string_radix(&self, String value, int radix)
|
||||
case '0'..'9':
|
||||
pos_val -= '0';
|
||||
case 'A'..'Z':
|
||||
pos_val -= 'A' + 10;
|
||||
pos_val -= 'A' - 10;
|
||||
case 'a'..'z':
|
||||
pos_val -= 'a' + 10;
|
||||
pos_val -= 'a' - 10;
|
||||
default:
|
||||
return NumberConversion.MALFORMED_INTEGER?;
|
||||
}
|
||||
@@ -328,7 +328,7 @@ fn BigInt BigInt.unary_minus(&self)
|
||||
}
|
||||
|
||||
|
||||
macro void BigInt.div(self, BigInt other)
|
||||
macro BigInt BigInt.div(self, BigInt other)
|
||||
{
|
||||
self.div_this(other);
|
||||
return self;
|
||||
@@ -445,13 +445,13 @@ fn BigInt BigInt.shl(self, int shift)
|
||||
return self;
|
||||
}
|
||||
|
||||
macro bool BigInt.equals(&self, BigInt* &other) @safemacro
|
||||
macro bool BigInt.equals(&self, BigInt other)
|
||||
{
|
||||
if (self.len != other.len) return false;
|
||||
return self.data[:self.len] == other.data[:self.len];
|
||||
}
|
||||
|
||||
macro bool BigInt.greater_than(&self, BigInt* &other) @safemacro
|
||||
macro bool BigInt.greater_than(&self, BigInt other)
|
||||
{
|
||||
if (self.is_negative() && !other.is_negative()) return false;
|
||||
if (!self.is_negative() && other.is_negative()) return true;
|
||||
@@ -461,7 +461,7 @@ macro bool BigInt.greater_than(&self, BigInt* &other) @safemacro
|
||||
for (pos = len - 1; pos >= 0 && self.data[pos] == other.data[pos]; pos--);
|
||||
return pos >= 0 && self.data[pos] > other.data[pos];
|
||||
}
|
||||
macro bool BigInt.less_than(&self, BigInt* &other) @safemacro
|
||||
macro bool BigInt.less_than(&self, BigInt other)
|
||||
{
|
||||
if (self.is_negative() && !other.is_negative()) return true;
|
||||
if (!self.is_negative() && other.is_negative()) return false;
|
||||
@@ -485,14 +485,14 @@ fn bool BigInt.is_one(&self)
|
||||
}
|
||||
|
||||
|
||||
macro bool BigInt.greater_or_equal(&self, BigInt* &other) @safemacro
|
||||
macro bool BigInt.greater_or_equal(&self, BigInt other)
|
||||
{
|
||||
return self.greater_than(*other) || self.equals(*other);
|
||||
return self.greater_than(other) || self.equals(other);
|
||||
}
|
||||
|
||||
macro bool BigInt.less_or_equal(&self, BigInt* &other) @safemacro
|
||||
macro bool BigInt.less_or_equal(&self, BigInt)
|
||||
{
|
||||
return self.less_than(*other) || self.equals(*other);
|
||||
return self.less_than(other) || self.equals(other);
|
||||
}
|
||||
|
||||
fn BigInt BigInt.abs(&self)
|
||||
@@ -520,7 +520,7 @@ fn String BigInt.to_string_with_radix(&self, int radix, Allocator allocator)
|
||||
{
|
||||
if (self.is_zero()) return "0".copy(allocator);
|
||||
|
||||
const char[*] CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
const char[?] CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
@stack_mem(4100; Allocator mem)
|
||||
{
|
||||
BigInt a = *self;
|
||||
@@ -831,6 +831,14 @@ fn BigInt BigInt.gcd(&self, BigInt other)
|
||||
return g;
|
||||
}
|
||||
|
||||
fn BigInt BigInt.lcm(&self, BigInt other)
|
||||
{
|
||||
BigInt x = self.abs();
|
||||
BigInt y = other.abs();
|
||||
BigInt g = y.mult(x);
|
||||
return g.div(x.gcd(y));
|
||||
}
|
||||
|
||||
<*
|
||||
@require bits >> 5 < MAX_LEN "Required bits > maxlength"
|
||||
*>
|
||||
|
||||
@@ -131,6 +131,28 @@ macro deg_to_rad(x) {
|
||||
*>
|
||||
macro abs(x) => $$abs(x);
|
||||
|
||||
<*
|
||||
@require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
|
||||
@require values::@is_int(y) || values::@is_float(y) "Expected an integer or floating point value"
|
||||
*>
|
||||
macro is_approx(x, y, eps)
|
||||
{
|
||||
if (x == y) return true;
|
||||
if (is_nan(x) || is_nan(y)) return false;
|
||||
return abs(x-y) <= eps;
|
||||
}
|
||||
|
||||
<*
|
||||
@require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
|
||||
@require values::@is_int(y) || values::@is_float(y) "Expected an integer or floating point value"
|
||||
*>
|
||||
macro is_approx_rel(x, y, eps)
|
||||
{
|
||||
if (x == y) return true;
|
||||
if (is_nan(x) || is_nan(y)) return false;
|
||||
return abs(x-y) <= eps * max(abs(x), abs(y));
|
||||
}
|
||||
|
||||
<*
|
||||
@require values::@is_int(x) `The input must be an integer`
|
||||
*>
|
||||
@@ -290,7 +312,7 @@ macro copysign(mag, sgn) => $$copysign(values::promote_int_same(mag, sgn), ($typ
|
||||
<*
|
||||
@require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
|
||||
*>
|
||||
macro cos(x) => $$cos(x);
|
||||
macro cos(x) => $$cos(values::promote_int(x));
|
||||
|
||||
<*
|
||||
@require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
|
||||
@@ -607,7 +629,7 @@ macro normalize(x) @private
|
||||
|
||||
@return "a vector of the same type as then/else"
|
||||
*>
|
||||
macro select(bool[<*>] mask, then_value, else_value)
|
||||
macro select(bool[<?>] mask, then_value, else_value)
|
||||
{
|
||||
return $$select(mask, then_value, else_value);
|
||||
}
|
||||
@@ -625,35 +647,35 @@ macro float float.round(float x) => $$round(x);
|
||||
macro float float.roundeven(float x) => $$roundeven(x);
|
||||
macro float float.trunc(float x) => $$trunc(x);
|
||||
|
||||
macro float float[<*>].sum(float[<*>] x, float start = 0.0) => $$reduce_fadd(x, start);
|
||||
macro float float[<*>].product(float[<*>] x, float start = 1.0) => $$reduce_fmul(x, start);
|
||||
macro float float[<*>].max(float[<*>] x) => $$reduce_max(x);
|
||||
macro float float[<*>].min(float[<*>] x) => $$reduce_min(x);
|
||||
macro float[<*>] float[<*>].ceil(float[<*>] x) => $$ceil(x);
|
||||
macro float[<*>] float[<*>].clamp(float[<*>] x, float[<*>] lower, float[<*>] upper) => $$max(lower, $$min(x, upper));
|
||||
macro float[<*>] float[<*>].copysign(float[<*>] mag, float[<*>] sgn) => $$copysign(mag, sgn);
|
||||
macro float[<*>] float[<*>].fma(float[<*>] a, float[<*>] b, float[<*>] c) => $$fma(a, b, c);
|
||||
macro float[<*>] float[<*>].floor(float[<*>] x) => $$floor(x);
|
||||
macro float[<*>] float[<*>].nearbyint(float[<*>] x) => $$nearbyint(x);
|
||||
macro float[<*>] float[<*>].pow(float[<*>] x, exp) => pow(x, exp);
|
||||
macro float[<*>] float[<*>].rint(float[<*>] x) => $$rint(x);
|
||||
macro float[<*>] float[<*>].round(float[<*>] x) => $$round(x);
|
||||
macro float[<*>] float[<*>].roundeven(float[<*>] x) => $$roundeven(x);
|
||||
macro float[<*>] float[<*>].trunc(float[<*>] x) => $$trunc(x);
|
||||
macro float float[<*>].dot(float[<*>] x, float[<*>] y) => (x * y).sum();
|
||||
macro float float[<*>].length(float[<*>] x) => $$sqrt(x.dot(x));
|
||||
macro float float[<*>].distance(float[<*>] x, float[<*>] y) => (x - y).length();
|
||||
macro float[<*>] float[<*>].normalize(float[<*>] x) => normalize(x);
|
||||
macro float[<*>] float[<*>].lerp(float[<*>] x, float[<*>] y, float amount) => lerp(x, y, amount);
|
||||
macro float[<*>] float[<*>].reflect(float[<*>] x, float[<*>] y) => reflect(x, y);
|
||||
macro bool float[<*>].equals(float[<*>] x, float[<*>] y) => equals_vec(x, y);
|
||||
macro float float[<?>].sum(float[<?>] x, float start = 0.0) => $$reduce_fadd(x, start);
|
||||
macro float float[<?>].product(float[<?>] x, float start = 1.0) => $$reduce_fmul(x, start);
|
||||
macro float float[<?>].max(float[<?>] x) => $$reduce_max(x);
|
||||
macro float float[<?>].min(float[<?>] x) => $$reduce_min(x);
|
||||
macro float[<?>] float[<?>].ceil(float[<?>] x) => $$ceil(x);
|
||||
macro float[<?>] float[<?>].clamp(float[<?>] x, float[<?>] lower, float[<?>] upper) => $$max(lower, $$min(x, upper));
|
||||
macro float[<?>] float[<?>].copysign(float[<?>] mag, float[<?>] sgn) => $$copysign(mag, sgn);
|
||||
macro float[<?>] float[<?>].fma(float[<?>] a, float[<?>] b, float[<?>] c) => $$fma(a, b, c);
|
||||
macro float[<?>] float[<?>].floor(float[<?>] x) => $$floor(x);
|
||||
macro float[<?>] float[<?>].nearbyint(float[<?>] x) => $$nearbyint(x);
|
||||
macro float[<?>] float[<?>].pow(float[<?>] x, exp) => pow(x, exp);
|
||||
macro float[<?>] float[<?>].rint(float[<?>] x) => $$rint(x);
|
||||
macro float[<?>] float[<?>].round(float[<?>] x) => $$round(x);
|
||||
macro float[<?>] float[<?>].roundeven(float[<?>] x) => $$roundeven(x);
|
||||
macro float[<?>] float[<?>].trunc(float[<?>] x) => $$trunc(x);
|
||||
macro float float[<?>].dot(float[<?>] x, float[<?>] y) => (x * y).sum();
|
||||
macro float float[<?>].length(float[<?>] x) => $$sqrt(x.dot(x));
|
||||
macro float float[<?>].distance(float[<?>] x, float[<?>] y) => (x - y).length();
|
||||
macro float[<?>] float[<?>].normalize(float[<?>] x) => normalize(x);
|
||||
macro float[<?>] float[<?>].lerp(float[<?>] x, float[<?>] y, float amount) => lerp(x, y, amount);
|
||||
macro float[<?>] float[<?>].reflect(float[<?>] x, float[<?>] y) => reflect(x, y);
|
||||
macro bool float[<?>].equals(float[<?>] x, float[<?>] y) => equals_vec(x, y);
|
||||
|
||||
macro bool[<*>] float[<*>].comp_lt(float[<*>] x, float[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] float[<*>].comp_le(float[<*>] x, float[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] float[<*>].comp_eq(float[<*>] x, float[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] float[<*>].comp_gt(float[<*>] x, float[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] float[<*>].comp_ge(float[<*>] x, float[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] float[<*>].comp_ne(float[<*>] x, float[<*>] y) => $$veccompne(x, y);
|
||||
macro bool[<?>] float[<?>].comp_lt(float[<?>] x, float[<?>] y) => $$veccomplt(x, y);
|
||||
macro bool[<?>] float[<?>].comp_le(float[<?>] x, float[<?>] y) => $$veccomple(x, y);
|
||||
macro bool[<?>] float[<?>].comp_eq(float[<?>] x, float[<?>] y) => $$veccompeq(x, y);
|
||||
macro bool[<?>] float[<?>].comp_gt(float[<?>] x, float[<?>] y) => $$veccompgt(x, y);
|
||||
macro bool[<?>] float[<?>].comp_ge(float[<?>] x, float[<?>] y) => $$veccompge(x, y);
|
||||
macro bool[<?>] float[<?>].comp_ne(float[<?>] x, float[<?>] y) => $$veccompne(x, y);
|
||||
|
||||
macro double double.ceil(double x) => $$ceil(x);
|
||||
macro double double.clamp(double x, double lower, double upper) => $$max(lower, $$min(x, upper));
|
||||
@@ -668,208 +690,208 @@ macro double double.round(double x) => $$round(x);
|
||||
macro double double.roundeven(double x) => $$roundeven(x);
|
||||
macro double double.trunc(double x) => $$trunc(x);
|
||||
|
||||
macro double double[<*>].sum(double[<*>] x, double start = 0.0) => $$reduce_fadd(x, start);
|
||||
macro double double[<*>].product(double[<*>] x, double start = 1.0) => $$reduce_fmul(x, start);
|
||||
macro double double[<*>].max(double[<*>] x) => $$reduce_fmax(x);
|
||||
macro double double[<*>].min(double[<*>] x) => $$reduce_fmin(x);
|
||||
macro double[<*>] double[<*>].ceil(double[<*>] x) => $$ceil(x);
|
||||
macro double[<*>] double[<*>].clamp(double[<*>] x, double[<*>] lower, double[<*>] upper) => $$max(lower, $$min(x, upper));
|
||||
macro double[<*>] double[<*>].copysign(double[<*>] mag, double[<*>] sgn) => $$copysign(mag, sgn);
|
||||
macro double[<*>] double[<*>].floor(double[<*>] x) => $$floor(x);
|
||||
macro double[<*>] double[<*>].fma(double[<*>] a, double[<*>] b, double[<*>] c) => $$fma(a, b, c);
|
||||
macro double[<*>] double[<*>].nearbyint(double[<*>] x) => $$nearbyint(x);
|
||||
macro double[<*>] double[<*>].pow(double[<*>] x, exp) => pow(x, exp);
|
||||
macro double[<*>] double[<*>].rint(double[<*>] x) => $$rint(x);
|
||||
macro double[<*>] double[<*>].round(double[<*>] x) => $$round(x);
|
||||
macro double[<*>] double[<*>].roundeven(double[<*>] x) => $$roundeven(x);
|
||||
macro double[<*>] double[<*>].trunc(double[<*>] x) => $$trunc(x);
|
||||
macro double double[<*>].dot(double[<*>] x, double[<*>] y) => (x * y).sum();
|
||||
macro double double[<*>].length(double[<*>] x) => $$sqrt(x.dot(x));
|
||||
macro double double[<*>].distance(double[<*>] x, double[<*>] y) => (x - y).length();
|
||||
macro double[<*>] double[<*>].normalize(double[<*>] x) => normalize(x);
|
||||
macro double[<*>] double[<*>].reflect(double[<*>] x, double[<*>] y) => reflect(x, y);
|
||||
macro double[<*>] double[<*>].lerp(double[<*>] x, double[<*>] y, double amount) => lerp(x, y, amount);
|
||||
macro bool double[<*>].equals(double[<*>] x, double[<*>] y) => equals_vec(x, y);
|
||||
macro double double[<?>].sum(double[<?>] x, double start = 0.0) => $$reduce_fadd(x, start);
|
||||
macro double double[<?>].product(double[<?>] x, double start = 1.0) => $$reduce_fmul(x, start);
|
||||
macro double double[<?>].max(double[<?>] x) => $$reduce_fmax(x);
|
||||
macro double double[<?>].min(double[<?>] x) => $$reduce_fmin(x);
|
||||
macro double[<?>] double[<?>].ceil(double[<?>] x) => $$ceil(x);
|
||||
macro double[<?>] double[<?>].clamp(double[<?>] x, double[<?>] lower, double[<?>] upper) => $$max(lower, $$min(x, upper));
|
||||
macro double[<?>] double[<?>].copysign(double[<?>] mag, double[<?>] sgn) => $$copysign(mag, sgn);
|
||||
macro double[<?>] double[<?>].floor(double[<?>] x) => $$floor(x);
|
||||
macro double[<?>] double[<?>].fma(double[<?>] a, double[<?>] b, double[<?>] c) => $$fma(a, b, c);
|
||||
macro double[<?>] double[<?>].nearbyint(double[<?>] x) => $$nearbyint(x);
|
||||
macro double[<?>] double[<?>].pow(double[<?>] x, exp) => pow(x, exp);
|
||||
macro double[<?>] double[<?>].rint(double[<?>] x) => $$rint(x);
|
||||
macro double[<?>] double[<?>].round(double[<?>] x) => $$round(x);
|
||||
macro double[<?>] double[<?>].roundeven(double[<?>] x) => $$roundeven(x);
|
||||
macro double[<?>] double[<?>].trunc(double[<?>] x) => $$trunc(x);
|
||||
macro double double[<?>].dot(double[<?>] x, double[<?>] y) => (x * y).sum();
|
||||
macro double double[<?>].length(double[<?>] x) => $$sqrt(x.dot(x));
|
||||
macro double double[<?>].distance(double[<?>] x, double[<?>] y) => (x - y).length();
|
||||
macro double[<?>] double[<?>].normalize(double[<?>] x) => normalize(x);
|
||||
macro double[<?>] double[<?>].reflect(double[<?>] x, double[<?>] y) => reflect(x, y);
|
||||
macro double[<?>] double[<?>].lerp(double[<?>] x, double[<?>] y, double amount) => lerp(x, y, amount);
|
||||
macro bool double[<?>].equals(double[<?>] x, double[<?>] y) => equals_vec(x, y);
|
||||
|
||||
macro bool[<*>] double[<*>].comp_lt(double[<*>] x, double[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] double[<*>].comp_le(double[<*>] x, double[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] double[<*>].comp_eq(double[<*>] x, double[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] double[<*>].comp_gt(double[<*>] x, double[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] double[<*>].comp_ge(double[<*>] x, double[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] double[<*>].comp_ne(double[<*>] x, double[<*>] y) => $$veccompne(x, y);
|
||||
macro bool[<?>] double[<?>].comp_lt(double[<?>] x, double[<?>] y) => $$veccomplt(x, y);
|
||||
macro bool[<?>] double[<?>].comp_le(double[<?>] x, double[<?>] y) => $$veccomple(x, y);
|
||||
macro bool[<?>] double[<?>].comp_eq(double[<?>] x, double[<?>] y) => $$veccompeq(x, y);
|
||||
macro bool[<?>] double[<?>].comp_gt(double[<?>] x, double[<?>] y) => $$veccompgt(x, y);
|
||||
macro bool[<?>] double[<?>].comp_ge(double[<?>] x, double[<?>] y) => $$veccompge(x, y);
|
||||
macro bool[<?>] double[<?>].comp_ne(double[<?>] x, double[<?>] y) => $$veccompne(x, y);
|
||||
|
||||
macro bool[<*>] ichar[<*>].comp_lt(ichar[<*>] x, ichar[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] ichar[<*>].comp_le(ichar[<*>] x, ichar[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] ichar[<*>].comp_eq(ichar[<*>] x, ichar[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] ichar[<*>].comp_gt(ichar[<*>] x, ichar[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] ichar[<*>].comp_ge(ichar[<*>] x, ichar[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] ichar[<*>].comp_ne(ichar[<*>] x, ichar[<*>] y) => $$veccompne(x, y);
|
||||
macro bool[<?>] ichar[<?>].comp_lt(ichar[<?>] x, ichar[<?>] y) => $$veccomplt(x, y);
|
||||
macro bool[<?>] ichar[<?>].comp_le(ichar[<?>] x, ichar[<?>] y) => $$veccomple(x, y);
|
||||
macro bool[<?>] ichar[<?>].comp_eq(ichar[<?>] x, ichar[<?>] y) => $$veccompeq(x, y);
|
||||
macro bool[<?>] ichar[<?>].comp_gt(ichar[<?>] x, ichar[<?>] y) => $$veccompgt(x, y);
|
||||
macro bool[<?>] ichar[<?>].comp_ge(ichar[<?>] x, ichar[<?>] y) => $$veccompge(x, y);
|
||||
macro bool[<?>] ichar[<?>].comp_ne(ichar[<?>] x, ichar[<?>] y) => $$veccompne(x, y);
|
||||
|
||||
macro ichar ichar[<*>].sum(ichar[<*>] x) => $$reduce_add(x);
|
||||
macro ichar ichar[<*>].product(ichar[<*>] x) => $$reduce_mul(x);
|
||||
macro ichar ichar[<*>].and(ichar[<*>] x) => $$reduce_and(x);
|
||||
macro ichar ichar[<*>].or(ichar[<*>] x) => $$reduce_or(x);
|
||||
macro ichar ichar[<*>].xor(ichar[<*>] x) => $$reduce_xor(x);
|
||||
macro ichar ichar[<*>].max(ichar[<*>] x) => $$reduce_max(x);
|
||||
macro ichar ichar[<*>].min(ichar[<*>] x) => $$reduce_min(x);
|
||||
macro ichar ichar[<*>].dot(ichar[<*>] x, ichar[<*>] y) => (x * y).sum();
|
||||
macro ichar ichar[<?>].sum(ichar[<?>] x) => $$reduce_add(x);
|
||||
macro ichar ichar[<?>].product(ichar[<?>] x) => $$reduce_mul(x);
|
||||
macro ichar ichar[<?>].and(ichar[<?>] x) => $$reduce_and(x);
|
||||
macro ichar ichar[<?>].or(ichar[<?>] x) => $$reduce_or(x);
|
||||
macro ichar ichar[<?>].xor(ichar[<?>] x) => $$reduce_xor(x);
|
||||
macro ichar ichar[<?>].max(ichar[<?>] x) => $$reduce_max(x);
|
||||
macro ichar ichar[<?>].min(ichar[<?>] x) => $$reduce_min(x);
|
||||
macro ichar ichar[<?>].dot(ichar[<?>] x, ichar[<?>] y) => (x * y).sum();
|
||||
|
||||
macro bool[<*>] short[<*>].comp_lt(short[<*>] x, short[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] short[<*>].comp_le(short[<*>] x, short[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] short[<*>].comp_eq(short[<*>] x, short[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] short[<*>].comp_gt(short[<*>] x, short[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] short[<*>].comp_ge(short[<*>] x, short[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] short[<*>].comp_ne(short[<*>] x, short[<*>] y) => $$veccompne(x, y);
|
||||
macro bool[<?>] short[<?>].comp_lt(short[<?>] x, short[<?>] y) => $$veccomplt(x, y);
|
||||
macro bool[<?>] short[<?>].comp_le(short[<?>] x, short[<?>] y) => $$veccomple(x, y);
|
||||
macro bool[<?>] short[<?>].comp_eq(short[<?>] x, short[<?>] y) => $$veccompeq(x, y);
|
||||
macro bool[<?>] short[<?>].comp_gt(short[<?>] x, short[<?>] y) => $$veccompgt(x, y);
|
||||
macro bool[<?>] short[<?>].comp_ge(short[<?>] x, short[<?>] y) => $$veccompge(x, y);
|
||||
macro bool[<?>] short[<?>].comp_ne(short[<?>] x, short[<?>] y) => $$veccompne(x, y);
|
||||
|
||||
macro short short[<*>].sum(short[<*>] x) => $$reduce_add(x);
|
||||
macro short short[<*>].product(short[<*>] x) => $$reduce_mul(x);
|
||||
macro short short[<*>].and(short[<*>] x) => $$reduce_and(x);
|
||||
macro short short[<*>].or(short[<*>] x) => $$reduce_or(x);
|
||||
macro short short[<*>].xor(short[<*>] x) => $$reduce_xor(x);
|
||||
macro short short[<*>].max(short[<*>] x) => $$reduce_max(x);
|
||||
macro short short[<*>].min(short[<*>] x) => $$reduce_min(x);
|
||||
macro short short[<*>].dot(short[<*>] x, short[<*>] y) => (x * y).sum();
|
||||
macro short short[<?>].sum(short[<?>] x) => $$reduce_add(x);
|
||||
macro short short[<?>].product(short[<?>] x) => $$reduce_mul(x);
|
||||
macro short short[<?>].and(short[<?>] x) => $$reduce_and(x);
|
||||
macro short short[<?>].or(short[<?>] x) => $$reduce_or(x);
|
||||
macro short short[<?>].xor(short[<?>] x) => $$reduce_xor(x);
|
||||
macro short short[<?>].max(short[<?>] x) => $$reduce_max(x);
|
||||
macro short short[<?>].min(short[<?>] x) => $$reduce_min(x);
|
||||
macro short short[<?>].dot(short[<?>] x, short[<?>] y) => (x * y).sum();
|
||||
|
||||
macro bool[<*>] int[<*>].comp_lt(int[<*>] x, int[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] int[<*>].comp_le(int[<*>] x, int[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] int[<*>].comp_eq(int[<*>] x, int[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] int[<*>].comp_gt(int[<*>] x, int[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] int[<*>].comp_ge(int[<*>] x, int[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] int[<*>].comp_ne(int[<*>] x, int[<*>] y) => $$veccompne(x, y);
|
||||
macro bool[<?>] int[<?>].comp_lt(int[<?>] x, int[<?>] y) => $$veccomplt(x, y);
|
||||
macro bool[<?>] int[<?>].comp_le(int[<?>] x, int[<?>] y) => $$veccomple(x, y);
|
||||
macro bool[<?>] int[<?>].comp_eq(int[<?>] x, int[<?>] y) => $$veccompeq(x, y);
|
||||
macro bool[<?>] int[<?>].comp_gt(int[<?>] x, int[<?>] y) => $$veccompgt(x, y);
|
||||
macro bool[<?>] int[<?>].comp_ge(int[<?>] x, int[<?>] y) => $$veccompge(x, y);
|
||||
macro bool[<?>] int[<?>].comp_ne(int[<?>] x, int[<?>] y) => $$veccompne(x, y);
|
||||
|
||||
macro int int[<*>].sum(int[<*>] x) => $$reduce_add(x);
|
||||
macro int int[<*>].product(int[<*>] x) => $$reduce_mul(x);
|
||||
macro int int[<*>].and(int[<*>] x) => $$reduce_and(x);
|
||||
macro int int[<*>].or(int[<*>] x) => $$reduce_or(x);
|
||||
macro int int[<*>].xor(int[<*>] x) => $$reduce_xor(x);
|
||||
macro int int[<*>].max(int[<*>] x) => $$reduce_max(x);
|
||||
macro int int[<*>].min(int[<*>] x) => $$reduce_min(x);
|
||||
macro int int[<*>].dot(int[<*>] x, int[<*>] y) => (x * y).sum();
|
||||
macro int int[<?>].sum(int[<?>] x) => $$reduce_add(x);
|
||||
macro int int[<?>].product(int[<?>] x) => $$reduce_mul(x);
|
||||
macro int int[<?>].and(int[<?>] x) => $$reduce_and(x);
|
||||
macro int int[<?>].or(int[<?>] x) => $$reduce_or(x);
|
||||
macro int int[<?>].xor(int[<?>] x) => $$reduce_xor(x);
|
||||
macro int int[<?>].max(int[<?>] x) => $$reduce_max(x);
|
||||
macro int int[<?>].min(int[<?>] x) => $$reduce_min(x);
|
||||
macro int int[<?>].dot(int[<?>] x, int[<?>] y) => (x * y).sum();
|
||||
|
||||
macro bool[<*>] long[<*>].comp_lt(long[<*>] x, long[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] long[<*>].comp_le(long[<*>] x, long[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] long[<*>].comp_eq(long[<*>] x, long[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] long[<*>].comp_gt(long[<*>] x, long[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] long[<*>].comp_ge(long[<*>] x, long[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] long[<*>].comp_ne(long[<*>] x, long[<*>] y) => $$veccompne(x, y);
|
||||
macro long long[<*>].sum(long[<*>] x) => $$reduce_add(x);
|
||||
macro long long[<*>].product(long[<*>] x) => $$reduce_mul(x);
|
||||
macro long long[<*>].and(long[<*>] x) => $$reduce_and(x);
|
||||
macro long long[<*>].or(long[<*>] x) => $$reduce_or(x);
|
||||
macro long long[<*>].xor(long[<*>] x) => $$reduce_xor(x);
|
||||
macro long long[<*>].max(long[<*>] x) => $$reduce_max(x);
|
||||
macro long long[<*>].min(long[<*>] x) => $$reduce_min(x);
|
||||
macro long long[<*>].dot(long[<*>] x, long[<*>] y) => (x * y).sum();
|
||||
macro bool[<?>] long[<?>].comp_lt(long[<?>] x, long[<?>] y) => $$veccomplt(x, y);
|
||||
macro bool[<?>] long[<?>].comp_le(long[<?>] x, long[<?>] y) => $$veccomple(x, y);
|
||||
macro bool[<?>] long[<?>].comp_eq(long[<?>] x, long[<?>] y) => $$veccompeq(x, y);
|
||||
macro bool[<?>] long[<?>].comp_gt(long[<?>] x, long[<?>] y) => $$veccompgt(x, y);
|
||||
macro bool[<?>] long[<?>].comp_ge(long[<?>] x, long[<?>] y) => $$veccompge(x, y);
|
||||
macro bool[<?>] long[<?>].comp_ne(long[<?>] x, long[<?>] y) => $$veccompne(x, y);
|
||||
macro long long[<?>].sum(long[<?>] x) => $$reduce_add(x);
|
||||
macro long long[<?>].product(long[<?>] x) => $$reduce_mul(x);
|
||||
macro long long[<?>].and(long[<?>] x) => $$reduce_and(x);
|
||||
macro long long[<?>].or(long[<?>] x) => $$reduce_or(x);
|
||||
macro long long[<?>].xor(long[<?>] x) => $$reduce_xor(x);
|
||||
macro long long[<?>].max(long[<?>] x) => $$reduce_max(x);
|
||||
macro long long[<?>].min(long[<?>] x) => $$reduce_min(x);
|
||||
macro long long[<?>].dot(long[<?>] x, long[<?>] y) => (x * y).sum();
|
||||
|
||||
macro bool[<*>] int128[<*>].comp_lt(int128[<*>] x, int128[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] int128[<*>].comp_le(int128[<*>] x, int128[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] int128[<*>].comp_eq(int128[<*>] x, int128[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] int128[<*>].comp_gt(int128[<*>] x, int128[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] int128[<*>].comp_ge(int128[<*>] x, int128[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] int128[<*>].comp_ne(int128[<*>] x, int128[<*>] y) => $$veccompne(x, y);
|
||||
macro int128 int128[<*>].sum(int128[<*>] x) => $$reduce_add(x);
|
||||
macro int128 int128[<*>].product(int128[<*>] x) => $$reduce_mul(x);
|
||||
macro int128 int128[<*>].and(int128[<*>] x) => $$reduce_and(x);
|
||||
macro int128 int128[<*>].or(int128[<*>] x) => $$reduce_or(x);
|
||||
macro int128 int128[<*>].xor(int128[<*>] x) => $$reduce_xor(x);
|
||||
macro int128 int128[<*>].max(int128[<*>] x) => $$reduce_max(x);
|
||||
macro int128 int128[<*>].min(int128[<*>] x) => $$reduce_min(x);
|
||||
macro int128 int128[<*>].dot(int128[<*>] x, int128[<*>] y) => (x * y).sum();
|
||||
macro bool[<?>] int128[<?>].comp_lt(int128[<?>] x, int128[<?>] y) => $$veccomplt(x, y);
|
||||
macro bool[<?>] int128[<?>].comp_le(int128[<?>] x, int128[<?>] y) => $$veccomple(x, y);
|
||||
macro bool[<?>] int128[<?>].comp_eq(int128[<?>] x, int128[<?>] y) => $$veccompeq(x, y);
|
||||
macro bool[<?>] int128[<?>].comp_gt(int128[<?>] x, int128[<?>] y) => $$veccompgt(x, y);
|
||||
macro bool[<?>] int128[<?>].comp_ge(int128[<?>] x, int128[<?>] y) => $$veccompge(x, y);
|
||||
macro bool[<?>] int128[<?>].comp_ne(int128[<?>] x, int128[<?>] y) => $$veccompne(x, y);
|
||||
macro int128 int128[<?>].sum(int128[<?>] x) => $$reduce_add(x);
|
||||
macro int128 int128[<?>].product(int128[<?>] x) => $$reduce_mul(x);
|
||||
macro int128 int128[<?>].and(int128[<?>] x) => $$reduce_and(x);
|
||||
macro int128 int128[<?>].or(int128[<?>] x) => $$reduce_or(x);
|
||||
macro int128 int128[<?>].xor(int128[<?>] x) => $$reduce_xor(x);
|
||||
macro int128 int128[<?>].max(int128[<?>] x) => $$reduce_max(x);
|
||||
macro int128 int128[<?>].min(int128[<?>] x) => $$reduce_min(x);
|
||||
macro int128 int128[<?>].dot(int128[<?>] x, int128[<?>] y) => (x * y).sum();
|
||||
|
||||
macro bool[<*>] bool[<*>].comp_lt(bool[<*>] x, bool[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] bool[<*>].comp_le(bool[<*>] x, bool[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] bool[<*>].comp_eq(bool[<*>] x, bool[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] bool[<*>].comp_gt(bool[<*>] x, bool[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] bool[<*>].comp_ge(bool[<*>] x, bool[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] bool[<*>].comp_ne(bool[<*>] x, bool[<*>] y) => $$veccompne(x, y);
|
||||
macro bool[<?>] bool[<?>].comp_lt(bool[<?>] x, bool[<?>] y) => $$veccomplt(x, y);
|
||||
macro bool[<?>] bool[<?>].comp_le(bool[<?>] x, bool[<?>] y) => $$veccomple(x, y);
|
||||
macro bool[<?>] bool[<?>].comp_eq(bool[<?>] x, bool[<?>] y) => $$veccompeq(x, y);
|
||||
macro bool[<?>] bool[<?>].comp_gt(bool[<?>] x, bool[<?>] y) => $$veccompgt(x, y);
|
||||
macro bool[<?>] bool[<?>].comp_ge(bool[<?>] x, bool[<?>] y) => $$veccompge(x, y);
|
||||
macro bool[<?>] bool[<?>].comp_ne(bool[<?>] x, bool[<?>] y) => $$veccompne(x, y);
|
||||
|
||||
macro bool bool[<*>].sum(bool[<*>] x) => $$reduce_add(x);
|
||||
macro bool bool[<*>].product(bool[<*>] x) => $$reduce_mul(x);
|
||||
macro bool bool[<*>].and(bool[<*>] x) => $$reduce_and(x);
|
||||
macro bool bool[<*>].or(bool[<*>] x) => $$reduce_or(x);
|
||||
macro bool bool[<*>].xor(bool[<*>] x) => $$reduce_xor(x);
|
||||
macro bool bool[<*>].max(bool[<*>] x) => $$reduce_max(x);
|
||||
macro bool bool[<*>].min(bool[<*>] x) => $$reduce_min(x);
|
||||
macro bool bool[<?>].sum(bool[<?>] x) => $$reduce_add(x);
|
||||
macro bool bool[<?>].product(bool[<?>] x) => $$reduce_mul(x);
|
||||
macro bool bool[<?>].and(bool[<?>] x) => $$reduce_and(x);
|
||||
macro bool bool[<?>].or(bool[<?>] x) => $$reduce_or(x);
|
||||
macro bool bool[<?>].xor(bool[<?>] x) => $$reduce_xor(x);
|
||||
macro bool bool[<?>].max(bool[<?>] x) => $$reduce_max(x);
|
||||
macro bool bool[<?>].min(bool[<?>] x) => $$reduce_min(x);
|
||||
|
||||
macro bool[<*>] char[<*>].comp_lt(char[<*>] x, char[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] char[<*>].comp_le(char[<*>] x, char[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] char[<*>].comp_eq(char[<*>] x, char[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] char[<*>].comp_gt(char[<*>] x, char[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] char[<*>].comp_ge(char[<*>] x, char[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] char[<*>].comp_ne(char[<*>] x, char[<*>] y) => $$veccompne(x, y);
|
||||
macro bool[<?>] char[<?>].comp_lt(char[<?>] x, char[<?>] y) => $$veccomplt(x, y);
|
||||
macro bool[<?>] char[<?>].comp_le(char[<?>] x, char[<?>] y) => $$veccomple(x, y);
|
||||
macro bool[<?>] char[<?>].comp_eq(char[<?>] x, char[<?>] y) => $$veccompeq(x, y);
|
||||
macro bool[<?>] char[<?>].comp_gt(char[<?>] x, char[<?>] y) => $$veccompgt(x, y);
|
||||
macro bool[<?>] char[<?>].comp_ge(char[<?>] x, char[<?>] y) => $$veccompge(x, y);
|
||||
macro bool[<?>] char[<?>].comp_ne(char[<?>] x, char[<?>] y) => $$veccompne(x, y);
|
||||
|
||||
macro char char[<*>].sum(char[<*>] x) => $$reduce_add(x);
|
||||
macro char char[<*>].product(char[<*>] x) => $$reduce_mul(x);
|
||||
macro char char[<*>].and(char[<*>] x) => $$reduce_and(x);
|
||||
macro char char[<*>].or(char[<*>] x) => $$reduce_or(x);
|
||||
macro char char[<*>].xor(char[<*>] x) => $$reduce_xor(x);
|
||||
macro char char[<*>].max(char[<*>] x) => $$reduce_max(x);
|
||||
macro char char[<*>].min(char[<*>] x) => $$reduce_min(x);
|
||||
macro char char[<*>].dot(char[<*>] x, char[<*>] y) => (x * y).sum();
|
||||
macro char char[<?>].sum(char[<?>] x) => $$reduce_add(x);
|
||||
macro char char[<?>].product(char[<?>] x) => $$reduce_mul(x);
|
||||
macro char char[<?>].and(char[<?>] x) => $$reduce_and(x);
|
||||
macro char char[<?>].or(char[<?>] x) => $$reduce_or(x);
|
||||
macro char char[<?>].xor(char[<?>] x) => $$reduce_xor(x);
|
||||
macro char char[<?>].max(char[<?>] x) => $$reduce_max(x);
|
||||
macro char char[<?>].min(char[<?>] x) => $$reduce_min(x);
|
||||
macro char char[<?>].dot(char[<?>] x, char[<?>] y) => (x * y).sum();
|
||||
|
||||
macro bool[<*>] ushort[<*>].comp_lt(ushort[<*>] x, ushort[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] ushort[<*>].comp_le(ushort[<*>] x, ushort[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] ushort[<*>].comp_eq(ushort[<*>] x, ushort[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] ushort[<*>].comp_gt(ushort[<*>] x, ushort[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] ushort[<*>].comp_ge(ushort[<*>] x, ushort[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] ushort[<*>].comp_ne(ushort[<*>] x, ushort[<*>] y) => $$veccompne(x, y);
|
||||
macro bool[<?>] ushort[<?>].comp_lt(ushort[<?>] x, ushort[<?>] y) => $$veccomplt(x, y);
|
||||
macro bool[<?>] ushort[<?>].comp_le(ushort[<?>] x, ushort[<?>] y) => $$veccomple(x, y);
|
||||
macro bool[<?>] ushort[<?>].comp_eq(ushort[<?>] x, ushort[<?>] y) => $$veccompeq(x, y);
|
||||
macro bool[<?>] ushort[<?>].comp_gt(ushort[<?>] x, ushort[<?>] y) => $$veccompgt(x, y);
|
||||
macro bool[<?>] ushort[<?>].comp_ge(ushort[<?>] x, ushort[<?>] y) => $$veccompge(x, y);
|
||||
macro bool[<?>] ushort[<?>].comp_ne(ushort[<?>] x, ushort[<?>] y) => $$veccompne(x, y);
|
||||
|
||||
macro ushort ushort[<*>].sum(ushort[<*>] x) => $$reduce_add(x);
|
||||
macro ushort ushort[<*>].product(ushort[<*>] x) => $$reduce_mul(x);
|
||||
macro ushort ushort[<*>].and(ushort[<*>] x) => $$reduce_and(x);
|
||||
macro ushort ushort[<*>].or(ushort[<*>] x) => $$reduce_or(x);
|
||||
macro ushort ushort[<*>].xor(ushort[<*>] x) => $$reduce_xor(x);
|
||||
macro ushort ushort[<*>].max(ushort[<*>] x) => $$reduce_max(x);
|
||||
macro ushort ushort[<*>].min(ushort[<*>] x) => $$reduce_min(x);
|
||||
macro ushort ushort[<*>].dot(ushort[<*>] x, ushort[<*>] y) => (x * y).sum();
|
||||
macro ushort ushort[<?>].sum(ushort[<?>] x) => $$reduce_add(x);
|
||||
macro ushort ushort[<?>].product(ushort[<?>] x) => $$reduce_mul(x);
|
||||
macro ushort ushort[<?>].and(ushort[<?>] x) => $$reduce_and(x);
|
||||
macro ushort ushort[<?>].or(ushort[<?>] x) => $$reduce_or(x);
|
||||
macro ushort ushort[<?>].xor(ushort[<?>] x) => $$reduce_xor(x);
|
||||
macro ushort ushort[<?>].max(ushort[<?>] x) => $$reduce_max(x);
|
||||
macro ushort ushort[<?>].min(ushort[<?>] x) => $$reduce_min(x);
|
||||
macro ushort ushort[<?>].dot(ushort[<?>] x, ushort[<?>] y) => (x * y).sum();
|
||||
|
||||
macro bool[<*>] uint[<*>].comp_lt(uint[<*>] x, uint[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] uint[<*>].comp_le(uint[<*>] x, uint[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] uint[<*>].comp_eq(uint[<*>] x, uint[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] uint[<*>].comp_gt(uint[<*>] x, uint[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] uint[<*>].comp_ge(uint[<*>] x, uint[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] uint[<*>].comp_ne(uint[<*>] x, uint[<*>] y) => $$veccompne(x, y);
|
||||
macro bool[<?>] uint[<?>].comp_lt(uint[<?>] x, uint[<?>] y) => $$veccomplt(x, y);
|
||||
macro bool[<?>] uint[<?>].comp_le(uint[<?>] x, uint[<?>] y) => $$veccomple(x, y);
|
||||
macro bool[<?>] uint[<?>].comp_eq(uint[<?>] x, uint[<?>] y) => $$veccompeq(x, y);
|
||||
macro bool[<?>] uint[<?>].comp_gt(uint[<?>] x, uint[<?>] y) => $$veccompgt(x, y);
|
||||
macro bool[<?>] uint[<?>].comp_ge(uint[<?>] x, uint[<?>] y) => $$veccompge(x, y);
|
||||
macro bool[<?>] uint[<?>].comp_ne(uint[<?>] x, uint[<?>] y) => $$veccompne(x, y);
|
||||
|
||||
macro uint uint[<*>].sum(uint[<*>] x) => $$reduce_add(x);
|
||||
macro uint uint[<*>].product(uint[<*>] x) => $$reduce_mul(x);
|
||||
macro uint uint[<*>].and(uint[<*>] x) => $$reduce_and(x);
|
||||
macro uint uint[<*>].or(uint[<*>] x) => $$reduce_or(x);
|
||||
macro uint uint[<*>].xor(uint[<*>] x) => $$reduce_xor(x);
|
||||
macro uint uint[<*>].max(uint[<*>] x) => $$reduce_max(x);
|
||||
macro uint uint[<*>].min(uint[<*>] x) => $$reduce_min(x);
|
||||
macro uint uint[<*>].dot(uint[<*>] x, uint[<*>] y) => (x * y).sum();
|
||||
macro uint uint[<?>].sum(uint[<?>] x) => $$reduce_add(x);
|
||||
macro uint uint[<?>].product(uint[<?>] x) => $$reduce_mul(x);
|
||||
macro uint uint[<?>].and(uint[<?>] x) => $$reduce_and(x);
|
||||
macro uint uint[<?>].or(uint[<?>] x) => $$reduce_or(x);
|
||||
macro uint uint[<?>].xor(uint[<?>] x) => $$reduce_xor(x);
|
||||
macro uint uint[<?>].max(uint[<?>] x) => $$reduce_max(x);
|
||||
macro uint uint[<?>].min(uint[<?>] x) => $$reduce_min(x);
|
||||
macro uint uint[<?>].dot(uint[<?>] x, uint[<?>] y) => (x * y).sum();
|
||||
|
||||
macro bool[<*>] ulong[<*>].comp_lt(ulong[<*>] x, ulong[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] ulong[<*>].comp_le(ulong[<*>] x, ulong[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] ulong[<*>].comp_eq(ulong[<*>] x, ulong[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] ulong[<*>].comp_gt(ulong[<*>] x, ulong[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] ulong[<*>].comp_ge(ulong[<*>] x, ulong[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] ulong[<*>].comp_ne(ulong[<*>] x, ulong[<*>] y) => $$veccompne(x, y);
|
||||
macro bool[<?>] ulong[<?>].comp_lt(ulong[<?>] x, ulong[<?>] y) => $$veccomplt(x, y);
|
||||
macro bool[<?>] ulong[<?>].comp_le(ulong[<?>] x, ulong[<?>] y) => $$veccomple(x, y);
|
||||
macro bool[<?>] ulong[<?>].comp_eq(ulong[<?>] x, ulong[<?>] y) => $$veccompeq(x, y);
|
||||
macro bool[<?>] ulong[<?>].comp_gt(ulong[<?>] x, ulong[<?>] y) => $$veccompgt(x, y);
|
||||
macro bool[<?>] ulong[<?>].comp_ge(ulong[<?>] x, ulong[<?>] y) => $$veccompge(x, y);
|
||||
macro bool[<?>] ulong[<?>].comp_ne(ulong[<?>] x, ulong[<?>] y) => $$veccompne(x, y);
|
||||
|
||||
macro ulong ulong[<*>].sum(ulong[<*>] x) => $$reduce_add(x);
|
||||
macro ulong ulong[<*>].product(ulong[<*>] x) => $$reduce_mul(x);
|
||||
macro ulong ulong[<*>].and(ulong[<*>] x) => $$reduce_and(x);
|
||||
macro ulong ulong[<*>].or(ulong[<*>] x) => $$reduce_or(x);
|
||||
macro ulong ulong[<*>].xor(ulong[<*>] x) => $$reduce_xor(x);
|
||||
macro ulong ulong[<*>].max(ulong[<*>] x) => $$reduce_max(x);
|
||||
macro ulong ulong[<*>].min(ulong[<*>] x) => $$reduce_min(x);
|
||||
macro ulong ulong[<*>].dot(ulong[<*>] x, ulong[<*>] y) => (x * y).sum();
|
||||
macro ulong ulong[<?>].sum(ulong[<?>] x) => $$reduce_add(x);
|
||||
macro ulong ulong[<?>].product(ulong[<?>] x) => $$reduce_mul(x);
|
||||
macro ulong ulong[<?>].and(ulong[<?>] x) => $$reduce_and(x);
|
||||
macro ulong ulong[<?>].or(ulong[<?>] x) => $$reduce_or(x);
|
||||
macro ulong ulong[<?>].xor(ulong[<?>] x) => $$reduce_xor(x);
|
||||
macro ulong ulong[<?>].max(ulong[<?>] x) => $$reduce_max(x);
|
||||
macro ulong ulong[<?>].min(ulong[<?>] x) => $$reduce_min(x);
|
||||
macro ulong ulong[<?>].dot(ulong[<?>] x, ulong[<?>] y) => (x * y).sum();
|
||||
|
||||
macro bool[<*>] uint128[<*>].comp_lt(uint128[<*>] x, uint128[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] uint128[<*>].comp_le(uint128[<*>] x, uint128[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] uint128[<*>].comp_eq(uint128[<*>] x, uint128[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] uint128[<*>].comp_gt(uint128[<*>] x, uint128[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] uint128[<*>].comp_ge(uint128[<*>] x, uint128[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] uint128[<*>].comp_ne(uint128[<*>] x, uint128[<*>] y) => $$veccompne(x, y);
|
||||
macro bool[<?>] uint128[<?>].comp_lt(uint128[<?>] x, uint128[<?>] y) => $$veccomplt(x, y);
|
||||
macro bool[<?>] uint128[<?>].comp_le(uint128[<?>] x, uint128[<?>] y) => $$veccomple(x, y);
|
||||
macro bool[<?>] uint128[<?>].comp_eq(uint128[<?>] x, uint128[<?>] y) => $$veccompeq(x, y);
|
||||
macro bool[<?>] uint128[<?>].comp_gt(uint128[<?>] x, uint128[<?>] y) => $$veccompgt(x, y);
|
||||
macro bool[<?>] uint128[<?>].comp_ge(uint128[<?>] x, uint128[<?>] y) => $$veccompge(x, y);
|
||||
macro bool[<?>] uint128[<?>].comp_ne(uint128[<?>] x, uint128[<?>] y) => $$veccompne(x, y);
|
||||
|
||||
macro uint128 uint128[<*>].sum(uint128[<*>] x) => $$reduce_add(x);
|
||||
macro uint128 uint128[<*>].product(uint128[<*>] x) => $$reduce_mul(x);
|
||||
macro uint128 uint128[<*>].and(uint128[<*>] x) => $$reduce_and(x);
|
||||
macro uint128 uint128[<*>].or(uint128[<*>] x) => $$reduce_or(x);
|
||||
macro uint128 uint128[<*>].xor(uint128[<*>] x) => $$reduce_xor(x);
|
||||
macro uint128 uint128[<*>].max(uint128[<*>] x) => $$reduce_max(x);
|
||||
macro uint128 uint128[<*>].min(uint128[<*>] x) => $$reduce_min(x);
|
||||
macro uint128 uint128[<*>].dot(uint128[<*>] x, uint128[<*>] y) => (x * y).sum();
|
||||
macro uint128 uint128[<?>].sum(uint128[<?>] x) => $$reduce_add(x);
|
||||
macro uint128 uint128[<?>].product(uint128[<?>] x) => $$reduce_mul(x);
|
||||
macro uint128 uint128[<?>].and(uint128[<?>] x) => $$reduce_and(x);
|
||||
macro uint128 uint128[<?>].or(uint128[<?>] x) => $$reduce_or(x);
|
||||
macro uint128 uint128[<?>].xor(uint128[<?>] x) => $$reduce_xor(x);
|
||||
macro uint128 uint128[<?>].max(uint128[<?>] x) => $$reduce_max(x);
|
||||
macro uint128 uint128[<?>].min(uint128[<?>] x) => $$reduce_min(x);
|
||||
macro uint128 uint128[<?>].dot(uint128[<?>] x, uint128[<?>] y) => (x * y).sum();
|
||||
|
||||
macro char char.sat_add(char x, char y) => $$sat_add(x, y);
|
||||
macro char char.sat_sub(char x, char y) => $$sat_sub(x, y);
|
||||
@@ -1020,6 +1042,21 @@ macro uint double.high_word(double d) => (uint)(bitcast(d, ulong) >> 32);
|
||||
macro uint double.low_word(double d) => (uint)bitcast(d, ulong);
|
||||
macro uint float.word(float d) => bitcast(d, uint);
|
||||
|
||||
macro void double.set_high_word(double* d, uint u)
|
||||
{
|
||||
ulong rep = bitcast(*d, ulong);
|
||||
rep = ((ulong)u << 32) | (rep & 0xffffffff);
|
||||
*d = bitcast(rep, double);
|
||||
}
|
||||
|
||||
macro void double.set_low_word(double* d, uint u)
|
||||
{
|
||||
ulong rep = bitcast(*d, ulong);
|
||||
rep = (rep & 0xffffffff00000000) | (ulong)u;
|
||||
*d = bitcast(rep, double);
|
||||
}
|
||||
|
||||
macro void float.set_word(float* f, uint u) => *f = bitcast(u, float);
|
||||
|
||||
macro double scalbn(double x, int n) => _scalbn(x, n);
|
||||
|
||||
@@ -1141,46 +1178,104 @@ 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, mul, 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, mul, 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, mul, 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, mul, 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, mul, 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, mul, 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, mul, 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, mul, div);
|
||||
|
||||
<*
|
||||
@require types::is_int($typeof(a)) `The input must be an integer`
|
||||
@require types::is_int($typeof(b)) `The input must be an integer`
|
||||
*>
|
||||
macro _gcd(a, b) @private
|
||||
{
|
||||
if (a == 0) return b;
|
||||
if (b == 0) return a;
|
||||
|
||||
var $Type = $typeof(a);
|
||||
$Type r, aa, ab;
|
||||
aa = abs(a);
|
||||
ab = abs(b);
|
||||
while (ab != 0)
|
||||
{
|
||||
r = aa % ab;
|
||||
aa = ab;
|
||||
ab = r;
|
||||
}
|
||||
return aa;
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the least common multiple for the provided arguments.
|
||||
|
||||
@require $vacount >= 2 `At least two arguments are required.`
|
||||
*>
|
||||
macro lcm(...)
|
||||
{
|
||||
$typeof($vaarg[0]) result = $vaarg[0];
|
||||
$for (var $i = 1; $i < $vacount; $i++)
|
||||
$if $defined(result.lcm):
|
||||
result = result.lcm($vaarg[$i]);
|
||||
$else
|
||||
result = (abs($vaarg[$i]) * abs(result)) / (_gcd($vaarg[$i], result));
|
||||
$endif
|
||||
$endfor
|
||||
return result;
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the greatest common divisor for the provided arguments.
|
||||
|
||||
@require $vacount >= 2 `At least two arguments are required.`
|
||||
*>
|
||||
macro gcd(...)
|
||||
{
|
||||
$typeof($vaarg[0]) result = $vaarg[0];
|
||||
$for (var $i = 1; $i < $vacount; $i++)
|
||||
$if $defined(result.gcd):
|
||||
result = result.gcd($vaarg[$i]);
|
||||
$else
|
||||
result = _gcd($vaarg[$i], result);
|
||||
$endif
|
||||
$endfor
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -11,3 +11,17 @@ fn double __roundeven(double d) @extern("roundeven") @weak @nostrip
|
||||
// Slow implementation
|
||||
return round(d / 2) * 2;
|
||||
}
|
||||
|
||||
fn double __powidf2(double a, int b) @extern("__powidf2") @weak @nostrip
|
||||
{
|
||||
bool recip = b < 0;
|
||||
double r = 1;
|
||||
while (1)
|
||||
{
|
||||
if (b & 1) r *= a;
|
||||
b /= 2;
|
||||
if (b == 0) break;
|
||||
a *= a;
|
||||
}
|
||||
return recip ? 1 / r : r;
|
||||
}
|
||||
@@ -9,8 +9,8 @@ union Complex
|
||||
Real[<2>] v;
|
||||
}
|
||||
|
||||
|
||||
const Complex IDENTITY = { 1, 0 };
|
||||
const Complex IMAGINARY = { 0, 1 };
|
||||
macro Complex Complex.add(self, Complex b) => Complex { .v = self.v + b.v };
|
||||
macro Complex Complex.add_each(self, Real b) => Complex { .v = self.v + b };
|
||||
macro Complex Complex.sub(self, Complex b) => Complex { .v = self.v - b.v };
|
||||
@@ -22,3 +22,10 @@ macro Complex Complex.div(self, Complex b)
|
||||
Real div = b.v.dot(b.v);
|
||||
return Complex{ (self.r * b.r + self.c * b.c) / div, (self.c * b.r - self.r * b.c) / div };
|
||||
}
|
||||
macro Complex Complex.inverse(self)
|
||||
{
|
||||
Real sqr = self.v.dot(self.v);
|
||||
return Complex{ self.r / sqr, -self.c / sqr };
|
||||
}
|
||||
macro Complex Complex.conjugate(self) => Complex { .r = self.r, .c = -self.c };
|
||||
macro bool Complex.equals(self, Complex b) => self.v == b.v;
|
||||
|
||||
@@ -197,7 +197,7 @@ fn Real Matrix4x4.determinant(&self)
|
||||
|
||||
fn Matrix2x2 Matrix2x2.adjoint(&self)
|
||||
{
|
||||
return { self.m00, -self.m01, -self.m10, self.m11 };
|
||||
return { self.m11, -self.m01, -self.m10, self.m00 };
|
||||
}
|
||||
|
||||
fn Matrix3x3 Matrix3x3.adjoint(&self)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/k_cos.c */
|
||||
/*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/k_cosf.c */
|
||||
/*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
union DoubleInternal
|
||||
{
|
||||
@@ -6,6 +6,12 @@ union DoubleInternal
|
||||
ulong i;
|
||||
}
|
||||
|
||||
union FloatInternal
|
||||
{
|
||||
float f;
|
||||
uint i;
|
||||
}
|
||||
|
||||
// Based on the musl implementation
|
||||
fn double fmod(double x, double y) @extern("fmod") @weak @nostrip
|
||||
{
|
||||
@@ -75,4 +81,74 @@ fn double fmod(double x, double y) @extern("fmod") @weak @nostrip
|
||||
uxi |= (ulong)sx << 63;
|
||||
ux.i = uxi;
|
||||
return ux.f;
|
||||
}
|
||||
}
|
||||
|
||||
fn float fmodf(float x, float y) @extern("fmodf") @weak @nostrip
|
||||
{
|
||||
FloatInternal ux = { .f = x };
|
||||
FloatInternal uy = { .f = y };
|
||||
int ex = (int)((ux.i >> 23) & 0xff);
|
||||
int ey = (int)((uy.i >> 23) & 0xff);
|
||||
int sx = (int)(ux.i >> 31);
|
||||
uint uxi = ux.i;
|
||||
if (uy.i << 1 == 0 || math::is_nan(y) || ex == 0xff) return (x * y)/(x * y);
|
||||
if (uxi << 1 <= uy.i << 1)
|
||||
{
|
||||
if (uxi << 1 == uy.i << 1) return 0 * x;
|
||||
return x;
|
||||
}
|
||||
|
||||
if (!ex)
|
||||
{
|
||||
for (uint i = uxi << 9; i >> 31 == 0; ex--, i <<= 1);
|
||||
uxi <<= -ex + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
uxi &= -1U >> 9;
|
||||
uxi |= 1U << 23;
|
||||
}
|
||||
if (!ey)
|
||||
{
|
||||
for (uint i = uy.i << 9; i >> 31 == 0; ey--, i <<= 1);
|
||||
uy.i <<= -ey + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
uy.i &= -1U >> 9;
|
||||
uy.i |= 1U << 23;
|
||||
}
|
||||
|
||||
/* x mod y */
|
||||
for (; ex > ey; ex--)
|
||||
{
|
||||
uint i = uxi - uy.i;
|
||||
if (i >> 31 == 0)
|
||||
{
|
||||
if (i == 0) return 0 * x;
|
||||
uxi = i;
|
||||
}
|
||||
uxi <<= 1;
|
||||
}
|
||||
uint i = uxi - uy.i;
|
||||
if (i >> 31 == 0)
|
||||
{
|
||||
if (i == 0) return 0*x;
|
||||
uxi = i;
|
||||
}
|
||||
for (; uxi>>23 == 0; uxi <<= 1, ex--);
|
||||
|
||||
/* scale result */
|
||||
if (ex > 0)
|
||||
{
|
||||
uxi -= 1U << 23;
|
||||
uxi |= (uint)ex << 23;
|
||||
}
|
||||
else
|
||||
{
|
||||
uxi >>= -ex + 1;
|
||||
}
|
||||
uxi |= (uint)sx << 31;
|
||||
ux.i = uxi;
|
||||
return ux.f;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/k_sin.c */
|
||||
/*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/k_sinf.c */
|
||||
/*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/k_tan.c */
|
||||
/*
|
||||
@@ -11,7 +11,7 @@ module std::math::nolibc @if(env::NO_LIBC);
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const double[*] TAN_T = {
|
||||
const double[?] TAN_T = {
|
||||
3.33333333333334091986e-01, /* 3FD55555, 55555563 */
|
||||
1.33333333333201242699e-01, /* 3FC11111, 1110FE7A */
|
||||
5.39682539762260521377e-02, /* 3FABA1BA, 1BB341FE */
|
||||
@@ -32,7 +32,7 @@ fn double __tan(double x, double y, int odd) @extern("__tan") @weak @nostrip
|
||||
const double PIO4 = 7.85398163397448278999e-01; /* 3FE921FB, 54442D18 */
|
||||
const double PIO4LO = 3.06161699786838301793e-17; /* 3C81A626, 33145C07 */
|
||||
|
||||
uint hx = (uint)(bitcast(x, ulong) >> 32);
|
||||
uint hx = x.high_word();
|
||||
bool big = (hx &0x7fffffff) >= 0x3FE59428; // |x| >= 0.6744
|
||||
int sign @noinit;
|
||||
if (big)
|
||||
@@ -68,12 +68,12 @@ fn double __tan(double x, double y, int odd) @extern("__tan") @weak @nostrip
|
||||
// -1.0/(x+r) has up to 2ulp error, so compute it accurately
|
||||
|
||||
// Clear low word
|
||||
ulong d = bitcast(w, ulong) & 0xFFFF_FFFF_0000_0000;
|
||||
double w0 = bitcast(d, double);
|
||||
double w0 = w;
|
||||
w0.set_low_word(0);
|
||||
|
||||
v = r - (w0 - x); // w0+v = r+x
|
||||
double a = -1.0 / w;
|
||||
d = bitcast(a, ulong) & 0xFFFF_FFFF_0000_0000;
|
||||
double a0 = bitcast(d, double);
|
||||
double a0 = a;
|
||||
a0.set_low_word(0);
|
||||
return a0 + a * (1.0 + a0 * w0 + a0 * v);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/k_tanf.c */
|
||||
/*
|
||||
@@ -16,7 +16,7 @@ module std::math::nolibc @if(env::NO_LIBC);
|
||||
*/
|
||||
|
||||
// |tan(x)/x - t(x)| < 2**-25.5 (~[-2e-08, 2e-08]).
|
||||
const double[*] TANDF = {
|
||||
const double[?] TANDF = {
|
||||
0x15554d3418c99f.0p-54, /* 0.333331395030791399758 */
|
||||
0x1112fd38999f72.0p-55, /* 0.133392002712976742718 */
|
||||
0x1b54c91d865afe.0p-57, /* 0.0533812378445670393523 */
|
||||
|
||||
139
lib/std/math/math_nolibc/acos.c3
Normal file
139
lib/std/math/math_nolibc/acos.c3
Normal file
@@ -0,0 +1,139 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/e_acos.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunSoft, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const PIO2_HI @local = 1.57079632679489655800e+00; /* 0x3FF921FB, 0x54442D18 */
|
||||
const PIO2_LO @local = 6.12323399573676603587e-17; /* 0x3C91A626, 0x33145C07 */
|
||||
const PS0 @local = 1.66666666666666657415e-01; /* 0x3FC55555, 0x55555555 */
|
||||
const PS1 @local = -3.25565818622400915405e-01; /* 0xBFD4D612, 0x03EB6F7D */
|
||||
const PS2 @local = 2.01212532134862925881e-01; /* 0x3FC9C155, 0x0E884455 */
|
||||
const PS3 @local = -4.00555345006794114027e-02; /* 0xBFA48228, 0xB5688F3B */
|
||||
const PS4 @local = 7.91534994289814532176e-04; /* 0x3F49EFE0, 0x7501B288 */
|
||||
const PS5 @local = 3.47933107596021167570e-05; /* 0x3F023DE1, 0x0DFDF709 */
|
||||
const QS1 @local = -2.40339491173441421878e+00; /* 0xC0033A27, 0x1C8A2D4B */
|
||||
const QS2 @local = 2.02094576023350569471e+00; /* 0x40002AE5, 0x9C598AC8 */
|
||||
const QS3 @local = -6.88283971605453293030e-01; /* 0xBFE6066C, 0x1B8D0159 */
|
||||
const QS4 @local = 7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */
|
||||
|
||||
fn double _r(double z) @local
|
||||
{
|
||||
double p = z * (PS0 + z * (PS1 + z * (PS2 + z * (PS3 + z * (PS4 + z * PS5)))));
|
||||
double q = 1.0 + z * (QS1 + z * (QS2 + z * (QS3 + z * QS4)));
|
||||
return p / q;
|
||||
}
|
||||
|
||||
fn double _acos(double x) @weak @extern("acos") @nostrip
|
||||
{
|
||||
uint hx = x.high_word();
|
||||
uint ix = hx & 0x7fffffff;
|
||||
switch
|
||||
{
|
||||
/* |x| >= 1 or nan */
|
||||
case ix >= 0x3ff00000:
|
||||
uint lx = x.low_word();
|
||||
if ((ix - 0x3ff00000 | lx) == 0)
|
||||
{
|
||||
/* acos(1)=0, acos(-1)=pi */
|
||||
if (hx >> 31) return 2. * PIO2_HI + 0x1p-120f;
|
||||
return 0.;
|
||||
}
|
||||
return double.nan;
|
||||
/* |x| < 0.5 */
|
||||
case ix < 0x3fe00000:
|
||||
/* |x| < 2**-57 */
|
||||
if (ix <= 0x3c600000) return PIO2_HI + 0x1p-120f;
|
||||
return PIO2_HI - (x - (PIO2_LO - x * _r(x * x)));
|
||||
/* x < -0.5 */
|
||||
case (hx >> 31) != 0:
|
||||
double z = (1. + x) * 0.5;
|
||||
double s = math::sqrt(z);
|
||||
double w = _r(z) * s - PIO2_LO;
|
||||
return 2. * (PIO2_HI - (s + w));
|
||||
/* x > 0.5 */
|
||||
default:
|
||||
double z = (1. - x) * 0.5;
|
||||
double s = math::sqrt(z);
|
||||
double df = s;
|
||||
df.set_low_word(0);
|
||||
double c = (z - df * df) / (s + df);
|
||||
double w = _r(z) * s + c;
|
||||
return 2. * (df + w);
|
||||
}
|
||||
}
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/e_acosf.c */
|
||||
/*
|
||||
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
|
||||
*/
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const float PIO2_HI_F @local = 1.5707962513e+00; /* 0x3fc90fda */
|
||||
const float PIO2_LO_F @local = 7.5497894159e-08; /* 0x33a22168 */
|
||||
const float PS0_F @local = 1.6666586697e-01;
|
||||
const float PS1_F @local = -4.2743422091e-02;
|
||||
const float PS2_F @local = -8.6563630030e-03;
|
||||
const float QS1_F @local = -7.0662963390e-01;
|
||||
|
||||
fn float _r_f(float z) @local
|
||||
{
|
||||
float p = z * ( PS0_F + z * (PS1_F + z * PS2_F));
|
||||
float q = 1.0f + z * QS1_F;
|
||||
return p / q;
|
||||
}
|
||||
|
||||
fn float _acosf(float x) @weak @extern("acosf") @nostrip
|
||||
{
|
||||
uint hx = bitcast(x, uint);
|
||||
uint ix = hx & 0x7fffffff;
|
||||
switch
|
||||
{
|
||||
/* |x| >= 1 or nan */
|
||||
case ix >= 0x3f800000:
|
||||
if (ix == 0x3f800000)
|
||||
{
|
||||
if (hx >> 31) return 2.f * PIO2_HI_F + 0x1p-120f;
|
||||
return 0;
|
||||
}
|
||||
return float.nan;
|
||||
/* |x| < 0.5 */
|
||||
case ix < 0x3f000000:
|
||||
/* |x| < 2**-26 */
|
||||
if (ix <= 0x32800000) return PIO2_HI_F + 0x1p-120f;
|
||||
return PIO2_HI_F - (x - (PIO2_LO_F - x * _r_f(x * x)));
|
||||
/* x < -0.5 */
|
||||
case (hx >> 31) != 0:
|
||||
float z = (1.f + x) * 0.5f;
|
||||
float s = math::sqrt(z);
|
||||
float w = _r_f(z) * s - PIO2_LO_F;
|
||||
return 2.f * (PIO2_HI_F - (s + w));
|
||||
/* x > 0.5 */
|
||||
default:
|
||||
float z = (1.f - x) * 0.5f;
|
||||
float s = math::sqrt(z);
|
||||
float df = s;
|
||||
uint idf = df.word();
|
||||
df.set_word(idf & 0xfffff000);
|
||||
float c = (z - df * df) / (s + df);
|
||||
float w = _r_f(z) * s + c;
|
||||
return 2.f * (df + w);
|
||||
}
|
||||
}
|
||||
132
lib/std/math/math_nolibc/asin.c3
Normal file
132
lib/std/math/math_nolibc/asin.c3
Normal file
@@ -0,0 +1,132 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/e_asin.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunSoft, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const PIO2_HI @local = 1.57079632679489655800e+00; /* 0x3FF921FB, 0x54442D18 */
|
||||
const PIO2_LO @local = 6.12323399573676603587e-17; /* 0x3C91A626, 0x33145C07 */
|
||||
const PS0 @local = 1.66666666666666657415e-01; /* 0x3FC55555, 0x55555555 */
|
||||
const PS1 @local = -3.25565818622400915405e-01; /* 0xBFD4D612, 0x03EB6F7D */
|
||||
const PS2 @local = 2.01212532134862925881e-01; /* 0x3FC9C155, 0x0E884455 */
|
||||
const PS3 @local = -4.00555345006794114027e-02; /* 0xBFA48228, 0xB5688F3B */
|
||||
const PS4 @local = 7.91534994289814532176e-04; /* 0x3F49EFE0, 0x7501B288 */
|
||||
const PS5 @local = 3.47933107596021167570e-05; /* 0x3F023DE1, 0x0DFDF709 */
|
||||
const QS1 @local = -2.40339491173441421878e+00; /* 0xC0033A27, 0x1C8A2D4B */
|
||||
const QS2 @local = 2.02094576023350569471e+00; /* 0x40002AE5, 0x9C598AC8 */
|
||||
const QS3 @local = -6.88283971605453293030e-01; /* 0xBFE6066C, 0x1B8D0159 */
|
||||
const QS4 @local = 7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */
|
||||
|
||||
fn double _r(double z) @local
|
||||
{
|
||||
double p = z * (PS0 + z * (PS1 + z * (PS2 + z * (PS3 + z * (PS4 + z * PS5)))));
|
||||
double q = 1.0 + z * (QS1 + z * (QS2 + z * (QS3 + z * QS4)));
|
||||
return p / q;
|
||||
}
|
||||
|
||||
fn double _asin(double x) @weak @extern("asin") @nostrip
|
||||
{
|
||||
uint hx = x.high_word();
|
||||
uint ix = hx & 0x7fffffff;
|
||||
switch
|
||||
{
|
||||
/* |x| >= 1 or nan */
|
||||
case ix >= 0x3ff00000:
|
||||
uint lx = x.low_word();
|
||||
if ((ix-0x3ff00000 | lx) == 0)
|
||||
{
|
||||
/* asin(1) = +-pi/2 with inexact */
|
||||
return x * PIO2_HI + 0x1p-120f;
|
||||
}
|
||||
return double.nan;
|
||||
/* |x| < 0.5 */
|
||||
case ix < 0x3fe00000:
|
||||
/* if 0x1p-1022 <= |x| < 0x1p-26, avoid raising underflow */
|
||||
if (ix < 0x3e500000 && ix >= 0x00100000) return x;
|
||||
return x + x * _r(x * x);
|
||||
/* 1 > |x| >= 0.5 */
|
||||
default:
|
||||
double z = (1. - math::abs(x)) * 0.5;
|
||||
double s = math::sqrt(z);
|
||||
double r = _r(z);
|
||||
/* if |x| > 0.975 */
|
||||
if (ix >= 0x3fef3333)
|
||||
{
|
||||
x = PIO2_HI - (2. * (s + s * r) - PIO2_LO);
|
||||
}
|
||||
else
|
||||
{
|
||||
double f = s;
|
||||
f.set_low_word(0);
|
||||
double c = (z - f * f) / (s + f);
|
||||
x = 0.5 * PIO2_HI - (2. * s * r - (PIO2_LO - 2. * c) - (0.5 * PIO2_HI - 2. * f));
|
||||
}
|
||||
if (hx >> 31 != 0) return -x;
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/e_asinf.c */
|
||||
/*
|
||||
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
|
||||
*/
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const PIO2 @local = 1.570796326794896558e+00;
|
||||
const float PS0_F @local = 1.6666586697e-01;
|
||||
const float PS1_F @local = -4.2743422091e-02;
|
||||
const float PS2_F @local = -8.6563630030e-03;
|
||||
const float QS1_F @local = -7.0662963390e-01;
|
||||
|
||||
fn float _r_f(float z) @local
|
||||
{
|
||||
float p = z * ( PS0_F + z * (PS1_F + z * PS2_F));
|
||||
float q = 1.0f + z * QS1_F;
|
||||
return p / q;
|
||||
}
|
||||
|
||||
fn float _asinf(float x) @weak @extern("asinf") @nostrip
|
||||
{
|
||||
uint hx = bitcast(x, uint);
|
||||
uint ix = hx & 0x7fffffff;
|
||||
switch
|
||||
{
|
||||
/* |x| >= 1 */
|
||||
case ix >= 0x3f800000:
|
||||
if (ix == 0x3f800000)
|
||||
{
|
||||
/* asin(+-1) = +-pi/2 with inexact */
|
||||
return x * (float)PIO2 + 0x1p-120f;
|
||||
}
|
||||
return float.nan;
|
||||
/* |x| < 0.5 */
|
||||
case ix < 0x3f000000:
|
||||
/* if 0x1p-126 <= |x| < 0x1p-12, avoid raising underflow */
|
||||
if (ix < 0x39800000 && ix >= 0x00800000) return x;
|
||||
return x + x * _r_f(x * x);
|
||||
/* 1 > |x| >= 0.5 */
|
||||
default:
|
||||
float z = (1.f - math::abs(x)) * 0.5f;
|
||||
float s = math::sqrt(z);
|
||||
x = (float)PIO2 - 2.f * (s + s * _r_f(z));
|
||||
if (hx >> 31) return -x;
|
||||
return x;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,32 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
const double[*] ATANHI @private = {
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/s_atan.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const double[?] ATANHI @private = {
|
||||
4.63647609000806093515e-01, /* atan(0.5)hi 0x3FDDAC67, 0x0561BB4F */
|
||||
7.85398163397448278999e-01, /* atan(1.0)hi 0x3FE921FB, 0x54442D18 */
|
||||
9.82793723247329054082e-01, /* atan(1.5)hi 0x3FEF730B, 0xD281F69B */
|
||||
1.57079632679489655800e+00, /* atan(inf)hi 0x3FF921FB, 0x54442D18 */
|
||||
};
|
||||
|
||||
const double[*] ATANLO @private = {
|
||||
const double[?] ATANLO @private = {
|
||||
2.26987774529616870924e-17, /* atan(0.5)lo 0x3C7A2B7F, 0x222F65E2 */
|
||||
3.06161699786838301793e-17, /* atan(1.0)lo 0x3C81A626, 0x33145C07 */
|
||||
1.39033110312309984516e-17, /* atan(1.5)lo 0x3C700788, 0x7AF0CBBD */
|
||||
6.12323399573676603587e-17, /* atan(inf)lo 0x3C91A626, 0x33145C07 */
|
||||
};
|
||||
|
||||
const double[*] AT @private = {
|
||||
const double[?] AT @private = {
|
||||
3.33333333333329318027e-01, /* 0x3FD55555, 0x5555550D */
|
||||
-1.99999999998764832476e-01, /* 0xBFC99999, 0x9998EBC4 */
|
||||
1.42857142725034663711e-01, /* 0x3FC24924, 0x920083FF */
|
||||
@@ -89,21 +101,36 @@ fn double _atan(double x) @weak @extern("atan") @nostrip
|
||||
return sign ? -z : z;
|
||||
}
|
||||
|
||||
const float[*] ATANHIF @private = {
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/s_atanf.c */
|
||||
/*
|
||||
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
|
||||
*/
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const float[?] ATANHIF @private = {
|
||||
4.6364760399e-01, /* atan(0.5)hi 0x3eed6338 */
|
||||
7.8539812565e-01, /* atan(1.0)hi 0x3f490fda */
|
||||
9.8279368877e-01, /* atan(1.5)hi 0x3f7b985e */
|
||||
1.5707962513e+00, /* atan(inf)hi 0x3fc90fda */
|
||||
};
|
||||
|
||||
const float[*] ATANLOF @private = {
|
||||
const float[?] ATANLOF @private = {
|
||||
5.0121582440e-09, /* atan(0.5)lo 0x31ac3769 */
|
||||
3.7748947079e-08, /* atan(1.0)lo 0x33222168 */
|
||||
3.4473217170e-08, /* atan(1.5)lo 0x33140fb4 */
|
||||
7.5497894159e-08, /* atan(inf)lo 0x33a22168 */
|
||||
};
|
||||
|
||||
const float[*] ATF @private = {
|
||||
const float[?] ATF @private = {
|
||||
3.3333328366e-01,
|
||||
-1.9999158382e-01,
|
||||
1.4253635705e-01,
|
||||
@@ -170,37 +197,41 @@ fn float _atanf(float x) @weak @extern("atanf") @nostrip
|
||||
/* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */
|
||||
float s1 = z * (ATF[0] + w * (ATF[2] + w * ATF[4]));
|
||||
float s2 = w * (ATF[1] + w * ATF[3]);
|
||||
if (id < 0) return x - x * (s1 + s2) * 10000;
|
||||
if (id < 0) return x - x * (s1 + s2);
|
||||
z = ATANHIF[id] - ((x * (s1 + s2) - ATANLOF[id]) - x);
|
||||
return sign ? -z : z;
|
||||
}
|
||||
|
||||
const PI_LO @private = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/e_atan2.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunSoft, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*
|
||||
*/
|
||||
|
||||
macro void extract_words(double d, uint* hi, uint* lo) @private
|
||||
{
|
||||
ulong rep = bitcast(d, ulong);
|
||||
*hi = (uint)(rep >> 32);
|
||||
*lo = (uint)rep;
|
||||
}
|
||||
const PI_LO @private = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */
|
||||
|
||||
fn double _atan2(double y, double x) @weak @extern("atan2") @nostrip
|
||||
{
|
||||
if (math::is_nan(x) || math::is_nan(y)) return x + y;
|
||||
|
||||
uint lx @noinit;
|
||||
uint ix @noinit;
|
||||
extract_words(x, &ix, &lx);
|
||||
uint ly @noinit;
|
||||
uint iy @noinit;
|
||||
extract_words(y, &iy, &ly);
|
||||
uint lx = x.low_word();
|
||||
uint ix = x.high_word();
|
||||
uint ly = y.low_word();
|
||||
uint iy = y.high_word();
|
||||
|
||||
// x = 1.0
|
||||
if ((ix - 0x3ff00000) | lx == 0) return _atan(y);
|
||||
// 2*sign(x) + sign(y)
|
||||
uint m = ((iy >> 31) & 1) | ((ix >> 30) & 2);
|
||||
ix = ix & 0x7fffffff;
|
||||
iy = iy & 0x7fffffff;
|
||||
ix &= 0x7fffffff;
|
||||
iy &= 0x7fffffff;
|
||||
|
||||
// when y = 0
|
||||
if (iy | ly == 0)
|
||||
@@ -252,14 +283,29 @@ fn double _atan2(double y, double x) @weak @extern("atan2") @nostrip
|
||||
}
|
||||
}
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/e_atan2f.c */
|
||||
/*
|
||||
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
|
||||
*/
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const float PI_F @private = 3.1415927410e+00; /* 0x40490fdb */
|
||||
const float PI_LO_F @private = -8.7422776573e-08; /* 0xb3bbbd2e */
|
||||
|
||||
fn float _atan2f(float y, float x) @weak @extern("atan2f") @nostrip
|
||||
{
|
||||
if (math::is_nan(x) || math::is_nan(y)) return x + y;
|
||||
uint ix = bitcast(x, uint);
|
||||
uint iy = bitcast(y, uint);
|
||||
uint ix = x.word();
|
||||
uint iy = y.word();
|
||||
/* x=1.0 */
|
||||
if (ix == 0x3f800000) return _atanf(y);
|
||||
/* 2*sign(x)+sign(y) */
|
||||
|
||||
95
lib/std/math/math_nolibc/atanh.c3
Normal file
95
lib/std/math/math_nolibc/atanh.c3
Normal file
@@ -0,0 +1,95 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD usr/src/lib/msun/src/e_atanh.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunSoft, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
fn double _atanh(double x) @weak @extern("atanh") @nostrip
|
||||
{
|
||||
double t @noinit;
|
||||
uint hx = x.high_word();
|
||||
uint ix = hx & 0x7fffffff;
|
||||
uint sign = hx >> 31;
|
||||
switch
|
||||
{
|
||||
/* |x| >= 1 or nan */
|
||||
case ix >= 0x3ff00000:
|
||||
uint lx = x.low_word();
|
||||
if ((ix - 0x3ff00000 | lx) == 0)
|
||||
{
|
||||
return sign ? -double.inf : double.inf;
|
||||
}
|
||||
return double.nan;
|
||||
/* x<2**-28 */
|
||||
case ix < 0x3e300000 && (1e300 + x) > 0.:
|
||||
return x;
|
||||
}
|
||||
x.set_high_word(ix);
|
||||
/* |x| < 0.5 */
|
||||
if (ix < 0x3fe00000)
|
||||
{
|
||||
t = x + x;
|
||||
t = 0.5 * _log1p(t + t * x / (1. - x));
|
||||
}
|
||||
else
|
||||
{
|
||||
t = 0.5 * _log1p((x + x) / (1. - x));
|
||||
}
|
||||
return sign ? -t : t;
|
||||
}
|
||||
|
||||
/* origin: FreeBSD usr/src/lib/msun/src/e_atanhf.c */
|
||||
/*
|
||||
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
|
||||
*/
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
fn float _atanhf(float x) @weak @extern("atanhf") @nostrip
|
||||
{
|
||||
float t @noinit;
|
||||
uint hx = bitcast(x, uint);
|
||||
uint ix = hx & 0x7fffffff;
|
||||
uint sign = hx >> 31;
|
||||
switch
|
||||
{
|
||||
/* |x| >= 1 or nan */
|
||||
case ix >= 0x3f800000:
|
||||
if (ix == 0x3f800000)
|
||||
{
|
||||
return sign ? -float.inf : float.inf;
|
||||
}
|
||||
return float.nan;
|
||||
/* x<2**-28 */
|
||||
case ix < 0x31800000 && (1e30 + x) > 0.f:
|
||||
return x;
|
||||
}
|
||||
x.set_word(ix);
|
||||
/* |x| < 0.5 */
|
||||
if (ix < 0x3f000000)
|
||||
{
|
||||
t = x + x;
|
||||
t = 0.5f * _log1pf(t + t * x / (1.f - x));
|
||||
}
|
||||
else
|
||||
{
|
||||
t = 0.5f * _log1pf((x + x) / (1.f - x));
|
||||
}
|
||||
return sign ? -t : t;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double _ceil(double x) @weak @extern("ceil") @nostrip
|
||||
{
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn float _cosf(float x) @extern("cosf") @weak @nostrip
|
||||
{
|
||||
uint ix = bitcast(x, uint);
|
||||
uint ix = x.word();
|
||||
uint sign = ix >> 31;
|
||||
ix &= 0x7fffffff;
|
||||
|
||||
@@ -51,11 +51,10 @@ fn float _cosf(float x) @extern("cosf") @weak @nostrip
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
fn double _cos(double x) @weak @nostrip
|
||||
fn double _cos(double x) @extern("cos") @weak @nostrip
|
||||
{
|
||||
// High word of x.
|
||||
uint ix = (uint)(bitcast(x, ulong) >> 32);
|
||||
ix &= 0x7fffffff;
|
||||
uint ix = x.high_word() & 0x7fffffff;
|
||||
|
||||
switch
|
||||
{
|
||||
|
||||
60
lib/std/math/math_nolibc/exp.c3
Normal file
60
lib/std/math/math_nolibc/exp.c3
Normal file
@@ -0,0 +1,60 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
const double EXP_LN2_HI = 6.93147180369123816490e-01;
|
||||
const double EXP_LN2_LO = 1.90821492927058770002e-10;
|
||||
const double EXP_INV_LN2 = 1.44269504088896338700e+00;
|
||||
const double EXP_P1 = 1.66666666666666019037e-01;
|
||||
const double EXP_P2 = -2.77777777770155933842e-03;
|
||||
const double EXP_P3 = 6.61375632143793436117e-05;
|
||||
const double EXP_P4 = -1.65339022054652515390e-06;
|
||||
const double EXP_P5 = 4.13813679705723846039e-08;
|
||||
/*--------------------------------------------*/
|
||||
const float EXPF_LN2_HI = 6.9314575195e-01f;
|
||||
const float EXPF_LN2_LO = 1.4286067653e-06f;
|
||||
const float EXPF_INV_LN2 = 1.4426950216e+00f;
|
||||
const float EXPF_P1 = 1.6666667163e-01f;
|
||||
const float EXPF_P2 = -2.7777778450e-03f;
|
||||
const float EXPF_P3 = 6.6137559770e-05f;
|
||||
const float EXPF_P4 = -1.6533901999e-06f;
|
||||
|
||||
fn double exp(double x) @extern("exp")
|
||||
{
|
||||
if (x != x) return x;
|
||||
if (x == double.inf) return double.inf;
|
||||
if (x == -double.inf) return 0.0; // IEEE 754 spec
|
||||
// Overflow threshold for exp (approx +709.78 for double)
|
||||
if (x > 709.782712893384) return double.inf;
|
||||
// Underflow threshold for exp (approx -745.13 for double)
|
||||
if (x < -745.133219101941) return 0.0;
|
||||
|
||||
double px = x * EXP_INV_LN2;
|
||||
double k = _floor(px + 0.5);
|
||||
double r = x - k * EXP_LN2_HI - k * EXP_LN2_LO;
|
||||
|
||||
double r2 = r * r;
|
||||
double p = r2 * (EXP_P1 + r2 * (EXP_P2 + r2 * (EXP_P3 + r2 * (EXP_P4 + r2 * EXP_P5))));
|
||||
double exp_r = 1.0 + r + r * p;
|
||||
|
||||
return ldexp(exp_r, (int)k);
|
||||
}
|
||||
|
||||
fn float expf(float x) @extern("expf")
|
||||
{
|
||||
if (x != x) return x;
|
||||
if (x == float.inf) return float.inf;
|
||||
if (x == -float.inf) return 0.0f; // IEEE 754 spec
|
||||
// Overflow threshold (approx +88.72 for float)
|
||||
if (x > 88.7228f) return float.inf;
|
||||
// Underflow threshold (approx -103.97 for float)
|
||||
if (x < -103.972084f) return 0.0f;
|
||||
|
||||
float px = x * EXPF_INV_LN2;
|
||||
float k = _floorf(px + 0.5f);
|
||||
float r = x - k * EXPF_LN2_HI - k * EXPF_LN2_LO;
|
||||
|
||||
float r2 = r * r;
|
||||
float p = r2 * (EXPF_P1 + r2 * (EXPF_P2 + r2 * (EXPF_P3 + r2 * EXPF_P4)));
|
||||
float exp_r = 1.0f + r + r * p;
|
||||
|
||||
return ldexpf(exp_r, (int)k);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
macro uint _top12f(float x) @private => bitcast(x, uint) >> 20;
|
||||
|
||||
|
||||
15
lib/std/math/math_nolibc/fabs.c3
Normal file
15
lib/std/math/math_nolibc/fabs.c3
Normal file
@@ -0,0 +1,15 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double _fabs(double x) @weak @extern("fabs") @nostrip
|
||||
{
|
||||
ulong ix = bitcast(x, ulong);
|
||||
ix &= ~(1ul << 63);
|
||||
return bitcast(ix, double);
|
||||
}
|
||||
|
||||
fn float _fabsf(float x) @weak @extern("fabsf") @nostrip
|
||||
{
|
||||
uint ix = bitcast(x, uint);
|
||||
ix &= 0x7fffffff;
|
||||
return bitcast(ix, float);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double _floor(double x) @weak @extern("floor") @nostrip
|
||||
{
|
||||
|
||||
59
lib/std/math/math_nolibc/frexp.c3
Normal file
59
lib/std/math/math_nolibc/frexp.c3
Normal file
@@ -0,0 +1,59 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double frexp(double x, int* exp) @extern("frexp")
|
||||
{
|
||||
uint hx = x.high_word();
|
||||
uint ix = hx & 0x7fffffff;
|
||||
uint lx = x.low_word();
|
||||
|
||||
if (ix >= 0x7ff00000 || (ix | lx) == 0)
|
||||
{
|
||||
*exp = 0;
|
||||
return x;
|
||||
}
|
||||
|
||||
// exponent extraction and normalization
|
||||
int e = (int)((ix >> 20) & 0x7ff);
|
||||
if (e == 0)
|
||||
{
|
||||
// subnormal number
|
||||
x *= 0x1p64;
|
||||
hx = x.high_word();
|
||||
e = (int)((hx >> 20) & 0x7ff) - 64;
|
||||
}
|
||||
*exp = e - 1022;
|
||||
|
||||
// set exponent to -1 (fraction in [0.5, 1))
|
||||
hx = (hx & 0x800fffff) | 0x3fe00000;
|
||||
{
|
||||
ulong rep = ((ulong)hx << 32) | lx;
|
||||
return bitcast(rep, double);
|
||||
}
|
||||
}
|
||||
|
||||
fn float frexpf(float x, int* exp) @extern("frexpf")
|
||||
{
|
||||
uint ix = x.word();
|
||||
uint hx = ix & 0x7fffffff;
|
||||
|
||||
if (hx >= 0x7f800000 || hx == 0)
|
||||
{
|
||||
*exp = 0;
|
||||
return x;
|
||||
}
|
||||
|
||||
// exponent extraction and normalization
|
||||
int e = (int)((hx >> 23) & 0xff);
|
||||
if (e == 0)
|
||||
{
|
||||
// subnormal number
|
||||
x *= 0x1p64f;
|
||||
ix = x.word();
|
||||
e = (int)((ix >> 23) & 0xff) - 64;
|
||||
}
|
||||
*exp = e - 126;
|
||||
|
||||
// set exponent to -1 (fraction in [0.5, 1))
|
||||
ix = (ix & 0x807fffff) | 0x3f000000;
|
||||
return bitcast(ix, float);
|
||||
}
|
||||
67
lib/std/math/math_nolibc/ldexp.c3
Normal file
67
lib/std/math/math_nolibc/ldexp.c3
Normal file
@@ -0,0 +1,67 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double ldexp(double x, int exp) @extern("ldexp")
|
||||
{
|
||||
uint hx = x.high_word();
|
||||
int hexp = (int)((hx & 0x7ff00000) >> 20);
|
||||
|
||||
|
||||
if (hexp == 0x7ff) return x;
|
||||
if (hexp == 0)
|
||||
{
|
||||
// subnormal number handling
|
||||
x *= 0x1p64;
|
||||
hx = x.high_word();
|
||||
hexp = (int)((hx & 0x7ff00000) >> 20) - 64;
|
||||
}
|
||||
|
||||
// new exponent calculation
|
||||
hexp += exp;
|
||||
|
||||
if (hexp > 0x7fe) return x * double.inf;
|
||||
if (hexp < 1)
|
||||
{
|
||||
x *= 0x1p-1022;
|
||||
hexp += 1022;
|
||||
if (hexp < 1) x *= 0x1p-1022;
|
||||
return x;
|
||||
}
|
||||
|
||||
// set new exponent
|
||||
hx = ((ulong)hx & 0x800fffff) | ((ulong)hexp << 20);
|
||||
{
|
||||
ulong rep = ((ulong)hx << 32) | x.low_word();
|
||||
return bitcast(rep, double);
|
||||
}
|
||||
}
|
||||
|
||||
fn float ldexpf(float x, int exp) @extern("ldexpf")
|
||||
{
|
||||
uint ix = x.word();
|
||||
int hexp = (int)((ix & 0x7f800000) >> 23);
|
||||
|
||||
if (hexp == 0xff) return x;
|
||||
if (hexp == 0)
|
||||
{
|
||||
// subnormal number handling
|
||||
x *= 0x1p64f;
|
||||
ix = x.word();
|
||||
hexp = (int)((ix & 0x7f800000) >> 23) - 64;
|
||||
}
|
||||
|
||||
// new exponent calculation
|
||||
hexp += exp;
|
||||
|
||||
if (hexp > 0xfe) return x * float.inf;
|
||||
if (hexp < 1)
|
||||
{
|
||||
x *= 0x1p-126f;
|
||||
hexp += 126;
|
||||
if (hexp < 1) x *= 0x1p-126f;
|
||||
return x;
|
||||
}
|
||||
|
||||
// set new exponent
|
||||
ix = (ix & 0x807fffff) | (hexp << 23);
|
||||
return bitcast(ix, float);
|
||||
}
|
||||
86
lib/std/math/math_nolibc/log.c3
Normal file
86
lib/std/math/math_nolibc/log.c3
Normal file
@@ -0,0 +1,86 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
const double LOG_LN2_HI = 6.93147180369123816490e-01;
|
||||
const double LOG_LN2_LO = 1.90821492927058770002e-10;
|
||||
const double LOG_L1 = 6.666666666666735130e-01;
|
||||
const double LOG_L2 = 3.999999999940941908e-01;
|
||||
const double LOG_L3 = 2.857142874366239149e-01;
|
||||
const double LOG_L4 = 2.222219843214978396e-01;
|
||||
const double LOG_L5 = 1.818357216161805012e-01;
|
||||
const double LOG_L6 = 1.531383769920937332e-01;
|
||||
/*--------------------------------------------*/
|
||||
const float LOGF_LN2_HI = 6.9313812256e-01f;
|
||||
const float LOGF_LN2_LO = 9.0580006145e-06f;
|
||||
const float LOGF_L1 = 6.6666662693e-01f;
|
||||
const float LOGF_L2 = 4.0000972152e-01f;
|
||||
const float LOGF_L3 = 2.8498786688e-01f;
|
||||
const float LOGF_L4 = 2.4279078841e-01f;
|
||||
|
||||
const double SQRT2 = 1.41421356237309504880;
|
||||
const float SQRT2F = 1.41421356237309504880f;
|
||||
|
||||
fn double log(double x) @extern("log")
|
||||
{
|
||||
if (x != x) return x;
|
||||
if (x < 0.0) return double.nan;
|
||||
if (x == 0.0) return -double.inf;
|
||||
if (x == double.inf) return double.inf;
|
||||
|
||||
int k;
|
||||
double f = frexp(x, &k);
|
||||
if (f < SQRT2 * 0.5)
|
||||
{
|
||||
f *= 2.0;
|
||||
k--;
|
||||
}
|
||||
|
||||
// polynomial approximation of log(1 + f), with f in [0, sqrt(2) - 1]
|
||||
f -= 1.0;
|
||||
double s = f / (2.0 + f);
|
||||
double z = s * s;
|
||||
double w = z * z;
|
||||
|
||||
// even-part polynomial terms (t1) and odd-part polynomial terms (t2)
|
||||
double t1 = w * (LOG_L1 + w * (LOG_L3 + w * LOG_L5));
|
||||
double t2 = z * (LOG_L2 + w * (LOG_L4 + w * LOG_L6));
|
||||
double r = t1 + t2;
|
||||
|
||||
double hfsq = 0.5 * f * f;
|
||||
|
||||
return k * LOG_LN2_HI - ((hfsq - (s * (hfsq + r) + k * LOG_LN2_LO)) - f);
|
||||
}
|
||||
|
||||
fn float logf(float x) @extern("logf")
|
||||
{
|
||||
if (x != x) return x;
|
||||
if (x < 0.0f) return float.nan;
|
||||
if (x == 0.0f) return -float.inf;
|
||||
if (x == float.inf) return float.inf;
|
||||
|
||||
int k;
|
||||
float f = frexpf(x, &k);
|
||||
if (f < SQRT2F * 0.5f)
|
||||
{
|
||||
f *= 2.0f;
|
||||
k--;
|
||||
}
|
||||
|
||||
// polynomial approximation for log(1 + f)
|
||||
f -= 1.0f;
|
||||
float s = f / (2.0f + f);
|
||||
float z = s * s;
|
||||
float w = z * z;
|
||||
|
||||
/*
|
||||
logf uses fewer terms in its polynomial approximation
|
||||
compared to the double precision version because
|
||||
single-precision floating point doesn't benefit from the additional terms.
|
||||
LOGF_L1, ... ,LOGF_L4 provide sufficient accuracy for 32-bit float calculations.
|
||||
*/
|
||||
float t1 = w * (LOGF_L1 + w * LOGF_L3);
|
||||
float t2 = z * (LOGF_L2 + w * LOGF_L4);
|
||||
float r = t1 + t2;
|
||||
|
||||
float hfsq = 0.5f * f * f;
|
||||
return k * LOGF_LN2_HI - ((hfsq - (s * (hfsq + r) + k * LOGF_LN2_LO)) - f);
|
||||
}
|
||||
228
lib/std/math/math_nolibc/log1p.c3
Normal file
228
lib/std/math/math_nolibc/log1p.c3
Normal file
@@ -0,0 +1,228 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/s_log1p.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
/* origin: musl libc /src/math/log1p.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (c) 2005-2020 Rich Felker, et al.
|
||||
*
|
||||
* 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.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const LN2_HI @local = 6.93147180369123816490e-01; /* 3fe62e42 fee00000 */
|
||||
const LN2_LO @local = 1.90821492927058770002e-10; /* 3dea39ef 35793c76 */
|
||||
const LG1 @local = 6.666666666666735130e-01; /* 3FE55555 55555593 */
|
||||
const LG2 @local = 3.999999999940941908e-01; /* 3FD99999 9997FA04 */
|
||||
const LG3 @local = 2.857142874366239149e-01; /* 3FD24924 94229359 */
|
||||
const LG4 @local = 2.222219843214978396e-01; /* 3FCC71C5 1D8E78AF */
|
||||
const LG5 @local = 1.818357216161805012e-01; /* 3FC74664 96CB03DE */
|
||||
const LG6 @local = 1.531383769920937332e-01; /* 3FC39A09 D078C69F */
|
||||
const LG7 @local = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */
|
||||
|
||||
fn double _log1p(double x) @weak @extern("log1p") @nostrip
|
||||
{
|
||||
uint hx = x.high_word();
|
||||
int k = 1;
|
||||
double c @noinit;
|
||||
double f @noinit;
|
||||
switch
|
||||
{
|
||||
/* 1+x < sqrt(2)+ */
|
||||
case hx < 0x3fda827a || hx >> 31 != 0:
|
||||
switch
|
||||
{
|
||||
/* x <= -1.0 */
|
||||
case hx >= 0xbff00000:
|
||||
if (x == -1) return -double.inf;
|
||||
return double.nan;
|
||||
/* |x| < 2**-53 */
|
||||
case hx << 1 < 0x3ca00000 << 1:
|
||||
/* underflow if subnormal */
|
||||
if ((hx & 0x7ff00000) == 0)
|
||||
{
|
||||
(float)@volatile_load(x);
|
||||
}
|
||||
return x;
|
||||
/* sqrt(2)/2- <= 1+x < sqrt(2)+ */
|
||||
case hx <= 0xbfd2bec4:
|
||||
k = 0;
|
||||
c = 0;
|
||||
f = x;
|
||||
}
|
||||
case hx >= 0x7ff00000:
|
||||
return x;
|
||||
}
|
||||
if (k)
|
||||
{
|
||||
double u = 1 + x;
|
||||
uint hu = u.high_word();
|
||||
hu += 0x3ff00000 - 0x3fe6a09e;
|
||||
k = (int)(hu >> 20) - 0x3ff;
|
||||
/* correction term ~ log(1+x)-log(u), avoid underflow in c/u */
|
||||
if (k < 54)
|
||||
{
|
||||
c = (k >= 2) ? 1. - (u - x) : x - (u - 1.);
|
||||
c /= u;
|
||||
}
|
||||
else
|
||||
{
|
||||
c = 0;
|
||||
}
|
||||
/* reduce u into [sqrt(2)/2, sqrt(2)] */
|
||||
hu = (hu & 0x000fffff) + 0x3fe6a09e;
|
||||
u = bitcast(((ulong)hu << 32) | (bitcast(u, ulong) & 0xffffffff) , double);
|
||||
f = u - 1.;
|
||||
}
|
||||
double hfsq = 0.5 * f * f;
|
||||
double s = f / (2. + f);
|
||||
double z = s * s;
|
||||
double w = z * z;
|
||||
double t1 = w * (LG2 + w * (LG4 + w * LG6));
|
||||
double t2 = z * (LG1 + w * (LG3 + w * (LG5 + w * LG7)));
|
||||
double r = t1 + t2;
|
||||
double dk = k;
|
||||
return s * (hfsq + r) + (dk * LN2_LO + c) - hfsq + f + dk * LN2_HI;
|
||||
}
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/s_log1pf.c */
|
||||
/*
|
||||
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
|
||||
*/
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
/* origin: musl libc /src/math/log1pf.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (c) 2005-2020 Rich Felker, et al.
|
||||
*
|
||||
* 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.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const float LN2_HI_F @local = 6.9313812256e-01; /* 0x3f317180 */
|
||||
const float LN2_LO_F @local = 9.0580006145e-06; /* 0x3717f7d1 */
|
||||
/* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */
|
||||
const float LG1_F @local = 0xaaaaaa.0p-24; /* 0.66666662693 */
|
||||
const float LG2_F @local = 0xccce13.0p-25; /* 0.40000972152 */
|
||||
const float LG3_F @local = 0x91e9ee.0p-25; /* 0.28498786688 */
|
||||
const float LG4_F @local = 0xf89e26.0p-26; /* 0.24279078841 */
|
||||
|
||||
fn float _log1pf(float x) @weak @extern("log1pf") @nostrip
|
||||
{
|
||||
uint ix = x.word();
|
||||
int k = 1;
|
||||
float c @noinit;
|
||||
float f @noinit;
|
||||
switch
|
||||
{
|
||||
/* 1+x < sqrt(2)+ */
|
||||
case ix < 0x3ed413d0 || ix >> 31 != 0:
|
||||
switch
|
||||
{
|
||||
/* x <= -1.0 */
|
||||
case ix >= 0xbf800000:
|
||||
if (x == -1) return -float.inf;
|
||||
return float.nan;
|
||||
/* |x| < 2**-24 */
|
||||
case ix << 1 < 0x33800000 << 1:
|
||||
/* underflow if subnormal */
|
||||
if ((ix & 0x7f800000) == 0)
|
||||
{
|
||||
float v = @volatile_load(x);
|
||||
v = v * v;
|
||||
}
|
||||
return x;
|
||||
/* sqrt(2)/2- <= 1+x < sqrt(2)+ */
|
||||
case ix <= 0xbe95f619:
|
||||
k = 0;
|
||||
c = 0;
|
||||
f = x;
|
||||
}
|
||||
case ix >= 0x7f800000:
|
||||
return x;
|
||||
}
|
||||
if (k)
|
||||
{
|
||||
float u = 1 + x;
|
||||
uint iu = u.word();
|
||||
iu += 0x3f800000 - 0x3f3504f3;
|
||||
k = (int)(iu >> 23) - 0x7f;
|
||||
/* correction term ~ log(1+x)-log(u), avoid underflow in c/u */
|
||||
if (k < 25)
|
||||
{
|
||||
c = (k >= 2) ? 1.f - (u - x) : x - (u - 1.f);
|
||||
c /= u;
|
||||
}
|
||||
else
|
||||
{
|
||||
c = 0;
|
||||
}
|
||||
/* reduce u into [sqrt(2)/2, sqrt(2)] */
|
||||
iu = (iu & 0x007fffff) + 0x3f3504f3;
|
||||
f = bitcast(iu, float) - 1.f;
|
||||
}
|
||||
float hfsq = 0.5f * f * f;
|
||||
float s = f / (2.f + f);
|
||||
float z = s * s;
|
||||
float w = z * z;
|
||||
float t1 = w * (LG2_F + w * LG4_F);
|
||||
float t2 = z * (LG1_F + w * LG3_F);
|
||||
float r = t1 + t2;
|
||||
float dk = k;
|
||||
return s * (hfsq + r) + (dk * LN2_LO_F + c) - hfsq + f + dk * LN2_HI_F;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc;
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
const double TOINT = 1 / math::DOUBLE_EPSILON;
|
||||
const double TOINT15 = 1.5 / math::DOUBLE_EPSILON;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user