mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Compare commits
214 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd8e280835 | ||
|
|
34c2d8ce77 | ||
|
|
28b9ff8016 | ||
|
|
76f226f536 | ||
|
|
1b0ac13d76 | ||
|
|
f134b8b67a | ||
|
|
33b05bcfeb | ||
|
|
6d3c1f5d2f | ||
|
|
96943ca66f | ||
|
|
374d73af12 | ||
|
|
81397f0726 | ||
|
|
0c33b78a2f | ||
|
|
ee5b9e5826 | ||
|
|
5d3c3781e4 | ||
|
|
c13c0d04b1 | ||
|
|
88f44f1eac | ||
|
|
50680d6893 | ||
|
|
31096531e1 | ||
|
|
062a67fe75 | ||
|
|
1dfc24822e | ||
|
|
e35c7f0b90 | ||
|
|
7083b2b8e5 | ||
|
|
135213388d | ||
|
|
ed62268997 | ||
|
|
38110b0269 | ||
|
|
e34d56327a | ||
|
|
b50e6bd0e4 | ||
|
|
3ba68f85fe | ||
|
|
9f5c5a9acf | ||
|
|
b6f5938eda | ||
|
|
87725a3a9e | ||
|
|
70029cc4b8 | ||
|
|
4f72bc4be9 | ||
|
|
3a1aa8bdf0 | ||
|
|
a986d053c0 | ||
|
|
43943c1f33 | ||
|
|
3da9f73338 | ||
|
|
855be92881 | ||
|
|
e674deb486 | ||
|
|
3ef094a3d3 | ||
|
|
9c60c2cb33 | ||
|
|
b54d994475 | ||
|
|
80e360d8dd | ||
|
|
9f165342e2 | ||
|
|
a2bfeb156d | ||
|
|
bb8c03777d | ||
|
|
8338888976 | ||
|
|
79db06ecd1 | ||
|
|
341a70bd5d | ||
|
|
8bf9ca89a1 | ||
|
|
5046608d1f | ||
|
|
535151a2a5 | ||
|
|
b45cb22950 | ||
|
|
d6485ca08b | ||
|
|
d9e5926d57 | ||
|
|
cbacd64987 | ||
|
|
168c11e006 | ||
|
|
26362d5068 | ||
|
|
e77d1fb646 | ||
|
|
c41d551ead | ||
|
|
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 |
89
.github/workflows/main.yml
vendored
89
.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,17 +51,17 @@ 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 -vvv --emit-llvm run hello_world_win32
|
||||
dir build\llvm_ir
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe -vvv --emit-llvm run hello_world_win32 --trust=full
|
||||
dir build\llvm\windows-x64
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe clean
|
||||
dir build\llvm_ir
|
||||
dir build\llvm\windows-x64
|
||||
|
||||
|
||||
- name: Build testproject lib
|
||||
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 -vvv 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: |
|
||||
@@ -87,16 +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: 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: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run -O1 src/test_suite_runner.c3 --stdlib ..\lib7 --enable-new-generics -- ..\build\${{ matrix.build_type }}\c3c.exe test_suite7/ --stdlib ../lib7 --no-terminal
|
||||
|
||||
- name: Test python script
|
||||
run: |
|
||||
py msvc_build_libraries.py --accept-license
|
||||
@@ -132,8 +132,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-19.1.6-1-any.pkg.tar.zst
|
||||
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-19.1.6-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 }}
|
||||
@@ -143,6 +143,7 @@ jobs:
|
||||
run: |
|
||||
cd resources
|
||||
../build/c3c compile-run --print-linking examples/hello_world_many.c3
|
||||
../build/c3c compile-run --print-linking examples/process.c3
|
||||
../build/c3c compile-run --print-linking examples/time.c3
|
||||
../build/c3c compile-run --print-linking examples/fannkuch-redux.c3
|
||||
../build/c3c compile-run --print-linking examples/contextfree/boolerr.c3
|
||||
@@ -153,7 +154,7 @@ jobs:
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run -vvv
|
||||
../../build/c3c run -vvv --trust=full
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
@@ -162,13 +163,13 @@ jobs:
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build hello_world_lib --cc cc -vvv
|
||||
../../build/c3c build hello_world_lib --cc cc -vvv --trust=full
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
python3 src/tester.py ../build/c3c.exe test_suite/
|
||||
|
||||
../build/c3c.exe compile --target windows-x64 -O1 src/test_suite_runner.c3 --stdlib ../lib7 --enable-new-generics
|
||||
./test_suite_runner.exe ../build/c3c.exe test_suite7/ --stdlib ../lib7 --no-terminal
|
||||
|
||||
build-msys2-clang:
|
||||
runs-on: windows-latest
|
||||
@@ -209,26 +210,26 @@ jobs:
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run -vvv
|
||||
../../build/c3c run -vvv --trust=full
|
||||
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build hello_world_lib -vvv
|
||||
../../build/c3c build hello_world_lib -vvv --trust=full
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
python3 src/tester.py ../build/c3c.exe test_suite/
|
||||
../build/c3c.exe compile-run -O1 --stdlib ../lib7 src/test_suite_runner.c3 --enable-new-generics -- ../build/c3c.exe test_suite7/ --stdlib ../lib7 --no-terminal
|
||||
|
||||
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
|
||||
@@ -349,7 +350,7 @@ jobs:
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run -vvv
|
||||
../../build/c3c run -vvv --trust=full
|
||||
|
||||
- name: Test WASM
|
||||
run: |
|
||||
@@ -368,7 +369,7 @@ jobs:
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run -vvv --linker=builtin
|
||||
../../build/c3c run -vvv --linker=builtin --trust=full
|
||||
|
||||
- name: Init a library & a project
|
||||
run: |
|
||||
@@ -380,7 +381,7 @@ jobs:
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
python3 src/tester.py ../build/c3c test_suite/
|
||||
../build/c3c compile-run -O1 src/test_suite_runner.c3 --stdlib ../lib7 --enable-new-generics -- ../build/c3c test_suite7/ --stdlib ../lib7
|
||||
|
||||
- name: bundle_output
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_LINUX
|
||||
@@ -405,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
|
||||
@@ -486,22 +487,22 @@ 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 -vvv
|
||||
../../build/c3c run -vvv --trust=full
|
||||
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run -vvv --linker=builtin
|
||||
../../build/c3c run -vvv --linker=builtin --trust=full
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
python3 src/tester.py ../build/c3c test_suite/
|
||||
../build/c3c compile-run -O1 src/test_suite_runner.c3 --stdlib ../lib7 --enable-new-generics -- ../build/c3c test_suite7/ --stdlib ../lib7
|
||||
|
||||
- name: bundle_output
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU20
|
||||
@@ -520,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
|
||||
@@ -583,7 +584,7 @@ jobs:
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run -vvv
|
||||
../../build/c3c run -vvv --trust=full
|
||||
|
||||
- name: Test WASM
|
||||
run: |
|
||||
@@ -593,7 +594,7 @@ jobs:
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run -vvv --linker=builtin
|
||||
../../build/c3c run -vvv --linker=builtin --trust=full
|
||||
|
||||
- name: Init a library & a project
|
||||
run: |
|
||||
@@ -605,7 +606,7 @@ jobs:
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
python3 src/tester.py ../build/c3c test_suite/
|
||||
../build/c3c compile-run -O1 src/test_suite_runner.c3 --stdlib ../lib7 --enable-new-generics -- ../build/c3c test_suite7/ --stdlib ../lib7
|
||||
|
||||
build-mac:
|
||||
runs-on: macos-latest
|
||||
@@ -660,7 +661,7 @@ jobs:
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
../build/c3c compile-test unit
|
||||
../build/c3c compile-test unit -O1
|
||||
|
||||
- name: Test WASM
|
||||
run: |
|
||||
@@ -670,22 +671,28 @@ jobs:
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run -vvv
|
||||
../../build/c3c run -vvv --trust=full
|
||||
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run -vvv --linker=builtin
|
||||
../../build/c3c run -vvv --linker=builtin --trust=full
|
||||
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build hello_world_lib -vvv
|
||||
../../build/c3c build hello_world_lib -vvv --trust=full
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
python3 src/tester.py ../build/c3c test_suite/
|
||||
../build/c3c compile -O1 src/test_suite_runner.c3 --stdlib ../lib7 --enable-new-generics
|
||||
./test_suite_runner ../build/c3c test_suite7/ --stdlib ../lib7
|
||||
|
||||
- name: run build test suite runner
|
||||
run: |
|
||||
cd test
|
||||
../build/c3c compile -O1 src/test_suite_runner.c3 --stdlib ../lib7 --enable-new-generics
|
||||
|
||||
- name: bundle_output
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_MAC
|
||||
@@ -704,7 +711,7 @@ jobs:
|
||||
path: c3-macos-${{matrix.build_type}}.zip
|
||||
|
||||
build-nix:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
# Don't abort runners if a single one fails
|
||||
fail-fast: false
|
||||
@@ -735,7 +742,7 @@ jobs:
|
||||
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'
|
||||
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -80,4 +80,8 @@ TAGS
|
||||
result
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
|
||||
# tests
|
||||
/test/tmp/*
|
||||
/test/testrun
|
||||
|
||||
@@ -44,8 +44,8 @@ if(MSVC)
|
||||
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")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -gdwarf-3 -fno-exceptions")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O0 -fno-exceptions")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -gdwarf-3 -O0 -fno-exceptions")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3 -fno-exceptions")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3 -fno-exceptions")
|
||||
else()
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
@@ -261,10 +262,10 @@ if(C3_WITH_LLVM)
|
||||
|
||||
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}
|
||||
|
||||
10
README.md
10
README.md
@@ -138,7 +138,7 @@ fn void main()
|
||||
|
||||
### Current status
|
||||
|
||||
The current stable version of the compiler is **version 0.6.5**.
|
||||
The current stable version of the compiler is **version 0.6.7**.
|
||||
|
||||
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:
|
||||
|
||||
12
flake.lock
generated
12
flake.lock
generated
@@ -5,11 +5,11 @@
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1726560853,
|
||||
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -20,11 +20,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1730958623,
|
||||
"narHash": "sha256-JwQZIGSYnRNOgDDoIgqKITrPVil+RMWHsZH1eE1VGN0=",
|
||||
"lastModified": 1738297584,
|
||||
"narHash": "sha256-AYvaFBzt8dU0fcSK2jKD0Vg23K2eIRxfsVXIPCW9a0E=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "85f7e662eda4fa3a995556527c87b2524b691933",
|
||||
"rev": "9189ac18287c599860e878e905da550aa6dec1cd",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
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);
|
||||
|
||||
@@ -21,7 +21,7 @@ struct AnyList (Printable)
|
||||
|
||||
@param initial_capacity "The initial capacity to reserve"
|
||||
*>
|
||||
fn AnyList* AnyList.new_init(&self, usz initial_capacity = 16, Allocator allocator = null)
|
||||
fn AnyList* AnyList.new_init(&self, usz initial_capacity = 16, Allocator allocator = null) @deprecated("Use init(mem)")
|
||||
{
|
||||
return self.init(allocator ?: allocator::heap(), initial_capacity) @inline;
|
||||
}
|
||||
@@ -52,7 +52,18 @@ fn AnyList* AnyList.init(&self, Allocator allocator, usz initial_capacity = 16)
|
||||
|
||||
@param initial_capacity "The initial capacity to reserve"
|
||||
*>
|
||||
fn AnyList* AnyList.temp_init(&self, usz initial_capacity = 16)
|
||||
fn AnyList* AnyList.temp_init(&self, usz initial_capacity = 16) @deprecated("Use tinit")
|
||||
{
|
||||
return self.init(allocator::temp(), initial_capacity) @inline;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Initialize the list using the temp allocator.
|
||||
|
||||
@param initial_capacity "The initial capacity to reserve"
|
||||
*>
|
||||
fn AnyList* AnyList.tinit(&self, usz initial_capacity = 16)
|
||||
{
|
||||
return self.init(allocator::temp(), initial_capacity) @inline;
|
||||
}
|
||||
|
||||
@@ -86,15 +86,30 @@ struct GrowableBitSet
|
||||
@param initial_capacity
|
||||
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
||||
*>
|
||||
fn GrowableBitSet* GrowableBitSet.new_init(&self, usz initial_capacity = 1, Allocator allocator = allocator::heap())
|
||||
fn GrowableBitSet* GrowableBitSet.new_init(&self, usz initial_capacity = 1, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)")
|
||||
{
|
||||
self.data.new_init(initial_capacity, allocator);
|
||||
self.data.init(allocator, initial_capacity);
|
||||
return self;
|
||||
}
|
||||
|
||||
fn GrowableBitSet* GrowableBitSet.temp_init(&self, usz initial_capacity = 1)
|
||||
<*
|
||||
@param initial_capacity
|
||||
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
||||
*>
|
||||
fn GrowableBitSet* GrowableBitSet.init(&self, Allocator allocator, usz initial_capacity = 1)
|
||||
{
|
||||
return self.new_init(initial_capacity, allocator::temp()) @inline;
|
||||
self.data.init(allocator, initial_capacity);
|
||||
return self;
|
||||
}
|
||||
|
||||
fn GrowableBitSet* GrowableBitSet.temp_init(&self, usz initial_capacity = 1) @deprecated("Use tinit()")
|
||||
{
|
||||
return self.init(allocator::temp(), initial_capacity) @inline;
|
||||
}
|
||||
|
||||
fn GrowableBitSet* GrowableBitSet.tinit(&self, usz initial_capacity = 1)
|
||||
{
|
||||
return self.init(allocator::temp(), initial_capacity) @inline;
|
||||
}
|
||||
|
||||
fn void GrowableBitSet.free(&self)
|
||||
@@ -117,15 +132,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));
|
||||
}
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
<*
|
||||
@require $defined(Key{}.hash()) `No .hash function found on the key`
|
||||
@require $defined((Key){}.hash()) `No .hash function found on the key`
|
||||
*>
|
||||
module std::collections::map(<Key, Value>);
|
||||
import std::math;
|
||||
import std::io @norecurse;
|
||||
|
||||
struct HashMap
|
||||
struct HashMap (Printable)
|
||||
{
|
||||
Entry*[] table;
|
||||
Allocator allocator;
|
||||
@@ -23,7 +24,7 @@ struct HashMap
|
||||
@require !self.allocator "Map was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn HashMap* HashMap.new_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = null)
|
||||
fn HashMap* HashMap.new_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = null) @deprecated("Use init(mem)")
|
||||
{
|
||||
return self.init(allocator ?: allocator::heap(), capacity, load_factor);
|
||||
}
|
||||
@@ -51,7 +52,18 @@ fn HashMap* HashMap.init(&self, Allocator allocator, uint capacity = DEFAULT_INI
|
||||
@require !self.allocator "Map was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn HashMap* HashMap.temp_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
fn HashMap* HashMap.temp_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) @deprecated("Use tinit()")
|
||||
{
|
||||
return self.init(allocator::temp(), capacity, load_factor) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
@require capacity > 0 "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 "The load factor must be higher than 0"
|
||||
@require !self.allocator "Map was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn HashMap* HashMap.tinit(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
return self.init(allocator::temp(), capacity, load_factor) @inline;
|
||||
}
|
||||
@@ -64,9 +76,9 @@ fn HashMap* HashMap.temp_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, f
|
||||
@require !self.allocator "Map was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
*>
|
||||
macro HashMap* HashMap.new_init_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
|
||||
macro HashMap* HashMap.new_init_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap()) @deprecated("Use init_with_key_values(mem)")
|
||||
{
|
||||
self.new_init(capacity, load_factor, allocator);
|
||||
self.init(capacity, load_factor, allocator);
|
||||
$for (var $i = 0; $i < $vacount; $i += 2)
|
||||
self.set($vaarg[$i], $vaarg[$i+1]);
|
||||
$endfor
|
||||
@@ -83,10 +95,10 @@ macro HashMap* HashMap.new_init_with_key_values(&self, ..., uint capacity = DEFA
|
||||
@require !self.allocator "Map was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn HashMap* HashMap.new_init_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
|
||||
fn HashMap* HashMap.new_init_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap()) @deprecated("Use init_from_keys_and_values(mem)")
|
||||
{
|
||||
assert(keys.len == values.len);
|
||||
self.new_init(capacity, load_factor, allocator);
|
||||
self.init(allocator, capacity, load_factor);
|
||||
for (usz i = 0; i < keys.len; i++)
|
||||
{
|
||||
self.set(keys[i], values[i]);
|
||||
@@ -101,9 +113,25 @@ fn HashMap* HashMap.new_init_from_keys_and_values(&self, Key[] keys, Value[] val
|
||||
@require !self.allocator "Map was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
*>
|
||||
macro HashMap* HashMap.temp_init_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
macro HashMap* HashMap.temp_init_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) @deprecated("Use tinit_with_key_values")
|
||||
{
|
||||
self.temp_init(capacity, load_factor);
|
||||
self.tinit(capacity, load_factor);
|
||||
$for (var $i = 0; $i < $vacount; $i += 2)
|
||||
self.set($vaarg[$i], $vaarg[$i+1]);
|
||||
$endfor
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values"
|
||||
@require capacity > 0 "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 "The load factor must be higher than 0"
|
||||
@require !self.allocator "Map was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
*>
|
||||
macro HashMap* HashMap.tinit_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
self.tinit(capacity, load_factor);
|
||||
$for (var $i = 0; $i < $vacount; $i += 2)
|
||||
self.set($vaarg[$i], $vaarg[$i+1]);
|
||||
$endfor
|
||||
@@ -120,10 +148,31 @@ macro HashMap* HashMap.temp_init_with_key_values(&self, ..., uint capacity = DEF
|
||||
@require !self.allocator "Map was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn HashMap* HashMap.temp_init_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
|
||||
fn HashMap* HashMap.temp_init_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap()) @deprecated("Use tinit_from_keys_and_values")
|
||||
{
|
||||
assert(keys.len == values.len);
|
||||
self.temp_init(capacity, load_factor);
|
||||
self.tinit(capacity, load_factor);
|
||||
for (usz i = 0; i < keys.len; i++)
|
||||
{
|
||||
self.set(keys[i], values[i]);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] keys "The keys for the HashMap entries"
|
||||
@param [in] values "The values for the HashMap entries"
|
||||
@param [&inout] allocator "The allocator to use"
|
||||
@require keys.len == values.len "Both keys and values arrays must be the same length"
|
||||
@require capacity > 0 "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 "The load factor must be higher than 0"
|
||||
@require !self.allocator "Map was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn HashMap* HashMap.tinit_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
|
||||
{
|
||||
assert(keys.len == values.len);
|
||||
self.tinit(capacity, load_factor);
|
||||
for (usz i = 0; i < keys.len; i++)
|
||||
{
|
||||
self.set(keys[i], values[i]);
|
||||
@@ -145,18 +194,18 @@ fn bool HashMap.is_initialized(&map)
|
||||
<*
|
||||
@param [&in] other_map "The map to copy from."
|
||||
*>
|
||||
fn HashMap* HashMap.new_init_from_map(&self, HashMap* other_map)
|
||||
fn HashMap* HashMap.new_init_from_map(&self, HashMap* other_map) @deprecated("Use init_from_map(mem, map)")
|
||||
{
|
||||
return self.init_from_map(other_map, allocator::heap()) @inline;
|
||||
return self.init_from_map(allocator::heap(), other_map) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] allocator "The allocator to use"
|
||||
@param [&in] other_map "The map to copy from."
|
||||
*>
|
||||
fn HashMap* HashMap.init_from_map(&self, HashMap* other_map, Allocator allocator)
|
||||
fn HashMap* HashMap.init_from_map(&self, Allocator allocator, HashMap* other_map)
|
||||
{
|
||||
self.new_init(other_map.table.len, other_map.load_factor, allocator);
|
||||
self.init(allocator, other_map.table.len, other_map.load_factor);
|
||||
self.put_all_for_create(other_map);
|
||||
return self;
|
||||
}
|
||||
@@ -164,9 +213,17 @@ fn HashMap* HashMap.init_from_map(&self, HashMap* other_map, Allocator allocator
|
||||
<*
|
||||
@param [&in] other_map "The map to copy from."
|
||||
*>
|
||||
fn HashMap* HashMap.temp_init_from_map(&map, HashMap* other_map)
|
||||
fn HashMap* HashMap.temp_init_from_map(&map, HashMap* other_map) @deprecated("Use tinit_from_map")
|
||||
{
|
||||
return map.init_from_map(other_map, allocator::temp()) @inline;
|
||||
return map.init_from_map(allocator::temp(), other_map) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] other_map "The map to copy from."
|
||||
*>
|
||||
fn HashMap* HashMap.tinit_from_map(&map, HashMap* other_map)
|
||||
{
|
||||
return map.init_from_map(allocator::temp(), other_map) @inline;
|
||||
}
|
||||
|
||||
fn bool HashMap.is_empty(&map) @inline
|
||||
@@ -239,7 +296,7 @@ fn bool HashMap.set(&map, Key key, Value value) @operator([]=)
|
||||
// If the map isn't initialized, use the defaults to initialize it.
|
||||
if (!map.allocator)
|
||||
{
|
||||
map.new_init();
|
||||
map.init(allocator::heap());
|
||||
}
|
||||
uint hash = rehash(key.hash());
|
||||
uint index = index_for(hash, map.table.len);
|
||||
@@ -446,6 +503,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;
|
||||
|
||||
@@ -33,19 +33,24 @@ fn LinkedList* LinkedList.init(&self, Allocator allocator)
|
||||
<*
|
||||
@return "the initialized list"
|
||||
*>
|
||||
fn LinkedList* LinkedList.new_init(&self)
|
||||
fn LinkedList* LinkedList.new_init(&self) @deprecated("Use init(mem)")
|
||||
{
|
||||
return self.init(allocator::heap()) @inline;
|
||||
}
|
||||
|
||||
|
||||
fn LinkedList* LinkedList.temp_init(&self)
|
||||
fn LinkedList* LinkedList.temp_init(&self) @deprecated("Use tinit()")
|
||||
{
|
||||
return self.init(allocator::temp()) @inline;
|
||||
}
|
||||
|
||||
fn LinkedList* LinkedList.tinit(&self)
|
||||
{
|
||||
return self.init(allocator::temp()) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.allocator
|
||||
@require self.allocator != null
|
||||
*>
|
||||
macro void LinkedList.free_node(&self, Node* node) @private
|
||||
{
|
||||
@@ -200,7 +205,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 +301,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
|
||||
{
|
||||
|
||||
@@ -23,7 +23,21 @@ struct List (Printable)
|
||||
@param initial_capacity "The initial capacity to reserve"
|
||||
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
||||
*>
|
||||
fn List* List.new_init(&self, usz initial_capacity = 16, Allocator allocator = allocator::heap())
|
||||
fn List* List.init(&self, Allocator allocator, usz initial_capacity = 16)
|
||||
{
|
||||
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"
|
||||
*>
|
||||
fn List* List.new_init(&self, usz initial_capacity = 16, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)")
|
||||
{
|
||||
self.allocator = allocator;
|
||||
self.size = 0;
|
||||
@@ -38,9 +52,19 @@ fn List* List.new_init(&self, usz initial_capacity = 16, Allocator allocator = a
|
||||
|
||||
@param initial_capacity "The initial capacity to reserve"
|
||||
*>
|
||||
fn List* List.temp_init(&self, usz initial_capacity = 16)
|
||||
fn List* List.temp_init(&self, usz initial_capacity = 16) @deprecated("Use tinit()")
|
||||
{
|
||||
return self.new_init(initial_capacity, allocator::temp()) @inline;
|
||||
return self.init(allocator::temp(), initial_capacity) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
Initialize the list using the temp allocator.
|
||||
|
||||
@param initial_capacity "The initial capacity to reserve"
|
||||
*>
|
||||
fn List* List.tinit(&self, usz initial_capacity = 16)
|
||||
{
|
||||
return self.init(allocator::temp(), initial_capacity) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -49,9 +73,20 @@ fn List* List.temp_init(&self, usz initial_capacity = 16)
|
||||
@param [in] values `The values to initialize the list with.`
|
||||
@require self.size == 0 "The List must be empty"
|
||||
*>
|
||||
fn List* List.new_init_with_array(&self, Type[] values, Allocator allocator = allocator::heap())
|
||||
fn List* List.new_init_with_array(&self, Type[] values, Allocator allocator = allocator::heap()) @deprecated("Use init_with_array(mem)")
|
||||
{
|
||||
self.new_init(values.len, allocator) @inline;
|
||||
return self.init_with_array(allocator, values);
|
||||
}
|
||||
|
||||
<*
|
||||
Initialize a new list with an array.
|
||||
|
||||
@param [in] values `The values to initialize the list with.`
|
||||
@require self.size == 0 "The List must be empty"
|
||||
*>
|
||||
fn List* List.init_with_array(&self, Allocator allocator, Type[] values)
|
||||
{
|
||||
self.init(allocator, values.len) @inline;
|
||||
self.add_array(values) @inline;
|
||||
return self;
|
||||
}
|
||||
@@ -62,9 +97,22 @@ fn List* List.new_init_with_array(&self, Type[] values, Allocator allocator = al
|
||||
@param [in] values `The values to initialize the list with.`
|
||||
@require self.size == 0 "The List must be empty"
|
||||
*>
|
||||
fn List* List.temp_init_with_array(&self, Type[] values)
|
||||
fn List* List.temp_init_with_array(&self, Type[] values) @deprecated("Use tinit_with_array()")
|
||||
{
|
||||
self.temp_init(values.len) @inline;
|
||||
self.tinit(values.len) @inline;
|
||||
self.add_array(values) @inline;
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
Initialize a temporary list with an array.
|
||||
|
||||
@param [in] values `The values to initialize the list with.`
|
||||
@require self.size == 0 "The List must be empty"
|
||||
*>
|
||||
fn List* List.tinit_with_array(&self, Type[] values)
|
||||
{
|
||||
self.tinit(values.len) @inline;
|
||||
self.add_array(values) @inline;
|
||||
return self;
|
||||
}
|
||||
@@ -220,11 +268,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 +376,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 +474,7 @@ macro void List.pre_free(&self) @private
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.capacity
|
||||
@require self.capacity > 0
|
||||
*>
|
||||
macro void List.post_alloc(&self) @private
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ module std::collections::list_common;
|
||||
*>
|
||||
macro list_to_new_aligned_array($Type, self, Allocator allocator)
|
||||
{
|
||||
if (!self.size) return $Type[] {};
|
||||
if (!self.size) return ($Type[]){};
|
||||
$Type[] result = allocator::alloc_array_aligned(allocator, $Type, self.size);
|
||||
result[..] = self.entries[:self.size];
|
||||
return result;
|
||||
@@ -13,7 +13,7 @@ macro list_to_new_aligned_array($Type, self, Allocator allocator)
|
||||
|
||||
macro list_to_new_array($Type, self, Allocator allocator)
|
||||
{
|
||||
if (!self.size) return $Type[] {};
|
||||
if (!self.size) return ($Type[]){};
|
||||
$Type[] result = allocator::alloc_array(allocator, $Type, self.size);
|
||||
result[..] = self.entries[:self.size];
|
||||
return result;
|
||||
|
||||
@@ -26,7 +26,7 @@ struct MapImpl
|
||||
@require load_factor > 0.0 "The load factor must be higher than 0"
|
||||
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn Map new(uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
|
||||
fn Map new(uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap()) @deprecated("Map is deprecated")
|
||||
{
|
||||
MapImpl* map = allocator::alloc(allocator, MapImpl);
|
||||
_init(map, capacity, load_factor, allocator);
|
||||
|
||||
@@ -28,12 +28,12 @@ fn Maybe value(Type val)
|
||||
return { .value = val, .has_value = true };
|
||||
}
|
||||
|
||||
fn Maybe Maybe.with_value(Type val) @operator(construct)
|
||||
fn Maybe Maybe.with_value(Type val) @deprecated("Use maybe::value instead.") @operator(construct)
|
||||
{
|
||||
return { .value = val, .has_value = true };
|
||||
}
|
||||
|
||||
fn Maybe Maybe.empty() @operator(construct)
|
||||
fn Maybe Maybe.empty() @deprecated("Use maybe::EMPTY instead.") @operator(construct)
|
||||
{
|
||||
return { };
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ fn void Object.init_map_if_needed(&self) @private
|
||||
if (self.is_empty())
|
||||
{
|
||||
self.type = ObjectInternalMap.typeid;
|
||||
self.map.new_init(allocator: self.allocator);
|
||||
self.map.init(self.allocator);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ fn void Object.init_array_if_needed(&self) @private
|
||||
if (self.is_empty())
|
||||
{
|
||||
self.type = ObjectInternalList.typeid;
|
||||
self.array.new_init(allocator: self.allocator);
|
||||
self.array.init(self.allocator);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,11 @@ struct PrivatePriorityQueue (Printable)
|
||||
Heap heap;
|
||||
}
|
||||
|
||||
fn void PrivatePriorityQueue.init(&self, Allocator allocator, usz initial_capacity = 16, ) @inline
|
||||
{
|
||||
self.heap.new_init(initial_capacity, allocator);
|
||||
}
|
||||
|
||||
fn void PrivatePriorityQueue.new_init(&self, usz initial_capacity = 16, Allocator allocator = allocator::heap()) @inline
|
||||
{
|
||||
self.heap.new_init(initial_capacity, allocator);
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -164,14 +164,21 @@ fn void*! DynamicArenaAllocator.acquire(&self, usz size, AllocInitType init_type
|
||||
{
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
DynamicArenaPage* page = self.page;
|
||||
void* ptr = {|
|
||||
|
||||
void* ptr @noinit;
|
||||
do SET_DONE:
|
||||
{
|
||||
if (!page && self.unused_page)
|
||||
{
|
||||
self.page = page = self.unused_page;
|
||||
self.unused_page = page.prev_arena;
|
||||
page.prev_arena = null;
|
||||
}
|
||||
if (!page) return self._alloc_new(size, alignment);
|
||||
if (!page)
|
||||
{
|
||||
ptr = self._alloc_new(size, alignment)!;
|
||||
break SET_DONE;
|
||||
}
|
||||
void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment);
|
||||
usz new_used = start - page.memory + size;
|
||||
if ALLOCATE_NEW: (new_used > page.total)
|
||||
@@ -188,15 +195,15 @@ fn void*! DynamicArenaAllocator.acquire(&self, usz size, AllocInitType init_type
|
||||
break ALLOCATE_NEW;
|
||||
}
|
||||
}
|
||||
return self._alloc_new(size, alignment);
|
||||
ptr = self._alloc_new(size, alignment)!;
|
||||
break SET_DONE;
|
||||
}
|
||||
page.used = new_used;
|
||||
assert(start + size == page.memory + page.used);
|
||||
void* mem = start;
|
||||
DynamicArenaChunk* chunk = (DynamicArenaChunk*)mem - 1;
|
||||
ptr = start;
|
||||
DynamicArenaChunk* chunk = (DynamicArenaChunk*)ptr - 1;
|
||||
chunk.size = size;
|
||||
return mem;
|
||||
|}!;
|
||||
};
|
||||
if (init_type == ZERO) mem::clear(ptr, size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -52,11 +52,11 @@ 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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -34,7 +34,7 @@ struct TrackingAllocator (Allocator)
|
||||
fn void TrackingAllocator.init(&self, Allocator allocator)
|
||||
{
|
||||
*self = { .inner_allocator = allocator };
|
||||
self.map.new_init(allocator: allocator);
|
||||
self.map.init(allocator);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -62,38 +62,37 @@ 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):
|
||||
@@ -147,7 +146,7 @@ fn void panicf(String fmt, String file, String function, uint line, args...)
|
||||
@stack_mem(512; Allocator allocator)
|
||||
{
|
||||
DString s;
|
||||
s.new_init(allocator: allocator);
|
||||
s.init(allocator);
|
||||
s.appendf(fmt, ...args);
|
||||
in_panic = false;
|
||||
panic(s.str_view(), file, function, line);
|
||||
@@ -239,7 +238,7 @@ macro enum_by_name($Type, String enum_name) @builtin
|
||||
@param $Type `The type of the enum`
|
||||
@require $Type.kindof == ENUM `Only enums may be used`
|
||||
@require $defined($Type.#value) `Expected '#value' to match an enum associated value`
|
||||
@require $assignable(value, $typeof($Type{}.#value)) `Expected the value to match the type of the associated value`
|
||||
@require $assignable(value, $typeof(($Type){}.#value)) `Expected the value to match the type of the associated value`
|
||||
@ensure @typeis(return, $Type)
|
||||
@return! SearchResult.MISSING
|
||||
*>
|
||||
@@ -352,7 +351,7 @@ macro swizzle2(v, v2, ...) @builtin
|
||||
macro anyfault @catch(#expr) @builtin
|
||||
{
|
||||
if (catch f = #expr) return f;
|
||||
return anyfault {};
|
||||
return {};
|
||||
}
|
||||
|
||||
<*
|
||||
|
||||
@@ -9,7 +9,7 @@ const usz MIN_CAPACITY @private = 16;
|
||||
<*
|
||||
@require !self.data() "String already initialized"
|
||||
*>
|
||||
fn DString DString.new_init(&self, usz capacity = MIN_CAPACITY, Allocator allocator = allocator::heap())
|
||||
fn DString DString.new_init(&self, usz capacity = MIN_CAPACITY, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)")
|
||||
{
|
||||
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
|
||||
StringData* data = allocator::alloc_with_padding(allocator, StringData, capacity)!!;
|
||||
@@ -22,15 +22,37 @@ fn DString DString.new_init(&self, usz capacity = MIN_CAPACITY, Allocator alloca
|
||||
<*
|
||||
@require !self.data() "String already initialized"
|
||||
*>
|
||||
fn DString DString.temp_init(&self, usz capacity = MIN_CAPACITY)
|
||||
fn DString DString.init(&self, Allocator allocator, usz capacity = MIN_CAPACITY)
|
||||
{
|
||||
self.new_init(capacity, allocator::temp()) @inline;
|
||||
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
|
||||
StringData* data = allocator::alloc_with_padding(allocator, StringData, capacity)!!;
|
||||
data.allocator = allocator;
|
||||
data.len = 0;
|
||||
data.capacity = capacity;
|
||||
return *self = (DString)data;
|
||||
}
|
||||
|
||||
<*
|
||||
@require !self.data() "String already initialized"
|
||||
*>
|
||||
fn DString DString.temp_init(&self, usz capacity = MIN_CAPACITY) @deprecated("Use tinit()")
|
||||
{
|
||||
self.init(allocator::temp(), capacity) @inline;
|
||||
return *self;
|
||||
}
|
||||
|
||||
<*
|
||||
@require !self.data() "String already initialized"
|
||||
*>
|
||||
fn DString DString.tinit(&self, usz capacity = MIN_CAPACITY)
|
||||
{
|
||||
self.init(allocator::temp(), capacity) @inline;
|
||||
return *self;
|
||||
}
|
||||
|
||||
fn DString new_with_capacity(usz capacity, Allocator allocator = allocator::heap())
|
||||
{
|
||||
return DString{}.new_init(capacity, allocator);
|
||||
return (DString){}.init(allocator, capacity);
|
||||
}
|
||||
|
||||
fn DString temp_with_capacity(usz capacity) => new_with_capacity(capacity, allocator::temp()) @inline;
|
||||
@@ -99,16 +121,25 @@ fn void DString.replace(&self, String needle, String replacement)
|
||||
};
|
||||
}
|
||||
|
||||
fn DString DString.new_concat(self, DString b, Allocator allocator = allocator::heap())
|
||||
fn DString DString.new_concat(self, DString b, Allocator allocator = allocator::heap()) @deprecated("Use concat(mem)")
|
||||
{
|
||||
DString string;
|
||||
string.new_init(self.len() + b.len(), allocator);
|
||||
string.init(allocator, self.len() + b.len());
|
||||
string.append(self);
|
||||
string.append(b);
|
||||
return string;
|
||||
}
|
||||
|
||||
fn DString DString.temp_concat(self, DString b) => self.new_concat(b, allocator::temp());
|
||||
fn DString DString.concat(self, Allocator allocator, DString b)
|
||||
{
|
||||
DString string;
|
||||
string.init(allocator, self.len() + b.len());
|
||||
string.append(self);
|
||||
string.append(b);
|
||||
return string;
|
||||
}
|
||||
|
||||
fn DString DString.temp_concat(self, DString b) => self.concat(allocator::temp(), b);
|
||||
|
||||
fn ZString DString.zstr_view(&self)
|
||||
{
|
||||
@@ -157,7 +188,7 @@ fn String DString.str_view(self)
|
||||
|
||||
<*
|
||||
@require index < self.len()
|
||||
@require self.data() "Empty string"
|
||||
@require self.data() != null "Empty string"
|
||||
*>
|
||||
fn char DString.char_at(self, usz index) @operator([])
|
||||
{
|
||||
@@ -166,7 +197,7 @@ fn char DString.char_at(self, usz index) @operator([])
|
||||
|
||||
<*
|
||||
@require index < self.len()
|
||||
@require self.data() "Empty string"
|
||||
@require self.data() != null "Empty string"
|
||||
*>
|
||||
fn char* DString.char_ref(&self, usz index) @operator(&[])
|
||||
{
|
||||
@@ -543,7 +574,7 @@ macro void DString.insert_at(&self, usz index, value)
|
||||
|
||||
fn usz! DString.appendf(&self, String format, args...) @maydiscard
|
||||
{
|
||||
if (!self.data()) self.new_init(format.len + 20);
|
||||
if (!self.data()) self.init(mem, format.len + 20);
|
||||
@pool(self.data().allocator)
|
||||
{
|
||||
Formatter formatter;
|
||||
@@ -554,7 +585,7 @@ fn usz! DString.appendf(&self, String format, args...) @maydiscard
|
||||
|
||||
fn usz! DString.appendfn(&self, String format, args...) @maydiscard
|
||||
{
|
||||
if (!self.data()) self.new_init(format.len + 20);
|
||||
if (!self.data()) self.init(mem, format.len + 20);
|
||||
@pool(self.data().allocator)
|
||||
{
|
||||
Formatter formatter;
|
||||
@@ -660,5 +691,5 @@ struct StringData @private
|
||||
Allocator allocator;
|
||||
usz len;
|
||||
usz capacity;
|
||||
char[*] chars;
|
||||
char[?] chars;
|
||||
}
|
||||
|
||||
@@ -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,7 +156,7 @@ 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);
|
||||
}
|
||||
@@ -301,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);
|
||||
}
|
||||
@@ -323,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);
|
||||
}
|
||||
@@ -683,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.
|
||||
@@ -708,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.
|
||||
@@ -732,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"
|
||||
|
||||
@@ -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
|
||||
@@ -360,6 +360,7 @@ macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes
|
||||
// All allocators
|
||||
|
||||
|
||||
def mem = thread_allocator @builtin;
|
||||
tlocal Allocator thread_allocator @private = base_allocator();
|
||||
Allocator temp_base_allocator @private = base_allocator();
|
||||
|
||||
|
||||
@@ -150,14 +150,14 @@ fn void x86_initialize_cpu_features()
|
||||
{
|
||||
uint max_level = x86_cpuid(0).eax;
|
||||
CpuId feat = x86_cpuid(1);
|
||||
CpuId leaf7 = max_level >= 8 ? x86_cpuid(7) : CpuId {};
|
||||
CpuId leaf7s1 = leaf7.eax >= 1 ? x86_cpuid(7, 1) : CpuId {};
|
||||
CpuId ext1 = x86_cpuid(0x80000000).eax >= 0x80000001 ? x86_cpuid(0x80000001) : CpuId {};
|
||||
CpuId ext8 = x86_cpuid(0x80000000).eax >= 0x80000008 ? x86_cpuid(0x80000008) : CpuId {};
|
||||
CpuId leaf_d = max_level >= 0xd ? x86_cpuid(0xd, 0x1) : CpuId {};
|
||||
CpuId leaf_14 = max_level >= 0x14 ? x86_cpuid(0x14) : CpuId {};
|
||||
CpuId leaf_19 = max_level >= 0x19 ? x86_cpuid(0x19) : CpuId {};
|
||||
CpuId leaf_24 = max_level >= 0x24 ? x86_cpuid(0x24) : CpuId {};
|
||||
CpuId leaf7 = max_level >= 8 ? x86_cpuid(7) : {};
|
||||
CpuId leaf7s1 = leaf7.eax >= 1 ? x86_cpuid(7, 1) : {};
|
||||
CpuId ext1 = x86_cpuid(0x80000000).eax >= 0x80000001 ? x86_cpuid(0x80000001) : {};
|
||||
CpuId ext8 = x86_cpuid(0x80000000).eax >= 0x80000008 ? x86_cpuid(0x80000008) : {};
|
||||
CpuId leaf_d = max_level >= 0xd ? x86_cpuid(0xd, 0x1) : {};
|
||||
CpuId leaf_14 = max_level >= 0x14 ? x86_cpuid(0x14) : {};
|
||||
CpuId leaf_19 = max_level >= 0x19 ? x86_cpuid(0x19) : {};
|
||||
CpuId leaf_24 = max_level >= 0x24 ? x86_cpuid(0x24) : {};
|
||||
add_feature_if_bit(ADX, leaf7.ebx, 19);
|
||||
add_feature_if_bit(AES, feat.ecx, 25);
|
||||
add_feature_if_bit(AMX_BF16, leaf7.edx, 22);
|
||||
|
||||
@@ -101,7 +101,7 @@ macro find_segment_section_body(MachHeader* header, char* segname, char* sectnam
|
||||
Section64*! section = find_section(find_segment(header, segname), sectname);
|
||||
if (catch section)
|
||||
{
|
||||
return $Type[] {};
|
||||
return ($Type[]){};
|
||||
}
|
||||
$Type* ptr = (void*)header + section.offset;
|
||||
return ptr[:section.size / $Type.sizeof];
|
||||
@@ -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)
|
||||
|
||||
@@ -168,10 +168,7 @@ fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @if(!$$OLD_TEST)
|
||||
return true;
|
||||
}
|
||||
|
||||
fn bool default_benchmark_runner(String[] args)
|
||||
fn bool default_benchmark_runner(String[] args) => @pool()
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
return run_benchmarks(benchmark_collection_create(allocator::temp()));
|
||||
};
|
||||
return run_benchmarks(benchmark_collection_create(allocator::temp()));
|
||||
}
|
||||
|
||||
@@ -2,11 +2,45 @@
|
||||
// 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 std::core::mem::allocator @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;
|
||||
bool is_quiet_mode;
|
||||
bool is_no_capture;
|
||||
String current_test_name;
|
||||
TestFn setup_fn;
|
||||
TestFn teardown_fn;
|
||||
|
||||
char* error_buffer;
|
||||
usz error_buffer_capacity;
|
||||
File fake_stdout;
|
||||
struct stored
|
||||
{
|
||||
File stdout;
|
||||
File stderr;
|
||||
Allocator allocator;
|
||||
}
|
||||
}
|
||||
|
||||
struct TestUnit
|
||||
{
|
||||
String name;
|
||||
@@ -25,11 +59,6 @@ fn TestUnit[] test_collection_create(Allocator allocator = allocator::heap())
|
||||
return tests;
|
||||
}
|
||||
|
||||
struct TestContext
|
||||
{
|
||||
JmpBuf buf;
|
||||
}
|
||||
|
||||
// Sort the tests by their name in ascending order.
|
||||
fn int cmp_test_unit(TestUnit a, TestUnit b)
|
||||
{
|
||||
@@ -44,114 +73,270 @@ fn int cmp_test_unit(TestUnit a, TestUnit b)
|
||||
return (int)(an - bn);
|
||||
}
|
||||
|
||||
TestContext* test_context @private;
|
||||
|
||||
fn void test_panic(String message, String file, String function, uint line)
|
||||
fn bool terminal_has_ansi_codes() @local => @pool()
|
||||
{
|
||||
io::printn("[error]");
|
||||
io::print("\n Error: ");
|
||||
io::print(message);
|
||||
io::printn();
|
||||
io::printfn(" - in %s %s:%s.\n", function, file, line);
|
||||
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;
|
||||
allocator::thread_allocator = test_context.stored.allocator;
|
||||
libc::longjmp(&test_context.buf, 1);
|
||||
}
|
||||
|
||||
fn bool run_tests(TestUnit[] tests) @if($$OLD_TEST)
|
||||
fn void mute_output() @local
|
||||
{
|
||||
usz max_name;
|
||||
foreach (&unit : tests)
|
||||
if (test_context.is_no_capture || !test_context.fake_stdout.file) return;
|
||||
File* stdout = io::stdout();
|
||||
File* stderr = io::stderr();
|
||||
*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.is_no_capture || !test_context.fake_stdout.file) return;
|
||||
|
||||
File* stdout = io::stdout();
|
||||
File* stderr = io::stderr();
|
||||
|
||||
*stderr = test_context.stored.stderr;
|
||||
*stdout = test_context.stored.stdout;
|
||||
|
||||
usz log_size = test_context.fake_stdout.seek(0, Seek.CURSOR)!!;
|
||||
if (has_error)
|
||||
{
|
||||
if (max_name < unit.name.len) max_name = unit.name.len;
|
||||
io::printf("\nTesting %s ", test_context.current_test_name);
|
||||
io::printn(test_context.has_ansi_codes ? "[\e[0;31mFAIL\e[0m]" : "[FAIL]");
|
||||
}
|
||||
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)
|
||||
if (has_error && log_size > 0)
|
||||
{
|
||||
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)
|
||||
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 (catch err = unit.func())
|
||||
if (@unlikely(c == '\0'))
|
||||
{
|
||||
io::printfn("[failed] Failed due to: %s", err);
|
||||
continue;
|
||||
// ignore junk from previous tests
|
||||
break;
|
||||
}
|
||||
io::printn("[ok]");
|
||||
tests_passed++;
|
||||
libc::putchar(c);
|
||||
}
|
||||
io::printf("========== TEST END ============");
|
||||
}
|
||||
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;
|
||||
(void)stdout.flush();
|
||||
}
|
||||
|
||||
fn bool run_tests(TestUnit[] tests) @if(!$$OLD_TEST)
|
||||
fn bool run_tests(String[] args, TestUnit[] tests) @private
|
||||
{
|
||||
usz max_name;
|
||||
bool sort_tests = true;
|
||||
bool check_leaks = true;
|
||||
foreach (&unit : tests)
|
||||
{
|
||||
if (max_name < unit.name.len) max_name = unit.name.len;
|
||||
}
|
||||
quicksort(tests, &cmp_test_unit);
|
||||
|
||||
TestContext context;
|
||||
TestContext context =
|
||||
{
|
||||
.assert_print_backtrace = true,
|
||||
.breakpoint_on_assert = false,
|
||||
.test_filter = "",
|
||||
.has_ansi_codes = terminal_has_ansi_codes(),
|
||||
.stored.allocator = allocator::heap(),
|
||||
.stored.stderr = *io::stderr(),
|
||||
.stored.stdout = *io::stdout(),
|
||||
};
|
||||
for (int i = 1; i < args.len; i++)
|
||||
{
|
||||
switch (args[i])
|
||||
{
|
||||
case "--test-breakpoint":
|
||||
context.breakpoint_on_assert = true;
|
||||
case "--test-nosort":
|
||||
sort_tests = false;
|
||||
case "--test-noleak":
|
||||
check_leaks = false;
|
||||
case "--test-nocapture":
|
||||
context.is_no_capture = true;
|
||||
case "--noansi":
|
||||
context.has_ansi_codes = false;
|
||||
case "--useansi":
|
||||
context.has_ansi_codes = true;
|
||||
case "--test-quiet":
|
||||
context.is_quiet_mode = true;
|
||||
case "--test-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):
|
||||
context.fake_stdout.file = libc::tmpfile();
|
||||
$endif
|
||||
if (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);
|
||||
if (!context.is_quiet_mode) io::printn(name);
|
||||
name.clear();
|
||||
TempState temp_state = mem::temp_push();
|
||||
defer mem::temp_pop(temp_state);
|
||||
foreach(unit : tests)
|
||||
{
|
||||
mem::temp_pop(temp_state);
|
||||
if (context.test_filter && !unit.name.contains(context.test_filter))
|
||||
{
|
||||
tests_skipped++;
|
||||
continue;
|
||||
}
|
||||
context.setup_fn = null;
|
||||
context.teardown_fn = null;
|
||||
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());
|
||||
if (context.is_quiet_mode)
|
||||
{
|
||||
io::print(".");
|
||||
}
|
||||
else
|
||||
{
|
||||
io::printf("%s ", name.str_view());
|
||||
}
|
||||
(void)io::stdout().flush();
|
||||
TrackingAllocator mem;
|
||||
|
||||
mem.init(context.stored.allocator);
|
||||
if (libc::setjmp(&context.buf) == 0)
|
||||
{
|
||||
unit.func();
|
||||
io::printn("[ok]");
|
||||
tests_passed++;
|
||||
mute_output();
|
||||
mem.clear();
|
||||
if (check_leaks) allocator::thread_allocator = &mem;
|
||||
$if(!$$OLD_TEST):
|
||||
unit.func();
|
||||
$else
|
||||
if (catch err = unit.func())
|
||||
{
|
||||
io::printf("[FAIL] Failed due to: %s", err);
|
||||
continue;
|
||||
}
|
||||
$endif
|
||||
// track cleanup that may take place in teardown_fn
|
||||
if (context.teardown_fn)
|
||||
{
|
||||
context.teardown_fn();
|
||||
}
|
||||
if (check_leaks) allocator::thread_allocator = context.stored.allocator;
|
||||
|
||||
unmute_output(false); // all good, discard output
|
||||
if (mem.has_leaks())
|
||||
{
|
||||
if(context.is_quiet_mode) io::printf("\n%s ", context.current_test_name);
|
||||
io::print(context.has_ansi_codes ? "[\e[0;31mFAIL\e[0m]" : "[FAIL]");
|
||||
io::printn(" LEAKS DETECTED!");
|
||||
mem.print_report();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!context.is_quiet_mode)
|
||||
{
|
||||
io::printfn(context.has_ansi_codes ? "[\e[0;32mPASS\e[0m]" : "[PASS]");
|
||||
}
|
||||
tests_passed++;
|
||||
}
|
||||
}
|
||||
mem.free();
|
||||
}
|
||||
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;
|
||||
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: ",
|
||||
context.has_ansi_codes ? (n_failed ? "\e[0;31m" : "\e[0;32m") : "",
|
||||
n_failed ? "FAILED" : "PASSED",
|
||||
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 (context.fake_stdout.file) libc::fclose(context.fake_stdout.file);
|
||||
context.fake_stdout.file = null;
|
||||
|
||||
return n_failed == 0;
|
||||
}
|
||||
|
||||
fn bool default_test_runner(String[] args)
|
||||
fn bool default_test_runner(String[] args) => @pool()
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
return run_tests(test_collection_create(allocator::temp()));
|
||||
};
|
||||
assert(test_context == null, "test suite is already running");
|
||||
return run_tests(args, test_collection_create(allocator::temp()));
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -786,26 +780,28 @@ macro String.to_integer(string, $Type, int base = 10)
|
||||
$Type value = 0;
|
||||
while (index != len)
|
||||
{
|
||||
char c = {|
|
||||
char ch = string[index++];
|
||||
if (base_used != 16 || ch < 'A') return (char)(ch - '0');
|
||||
if (ch <= 'F') return (char)(ch - 'A' + 10);
|
||||
if (ch < 'a') return NumberConversion.MALFORMED_INTEGER?;
|
||||
if (ch > 'f') return NumberConversion.MALFORMED_INTEGER?;
|
||||
return (char)(ch - 'a' + 10);
|
||||
|}!;
|
||||
char c = string[index++];
|
||||
switch
|
||||
{
|
||||
case base_used != 16 || c < 'A': c -= '0';
|
||||
case c <= 'F': c -= 'A' - 10;
|
||||
case c < 'a' || c > 'f': return NumberConversion.MALFORMED_INTEGER?;
|
||||
default: c -= 'a' - 10;
|
||||
}
|
||||
if (c >= base_used) return NumberConversion.MALFORMED_INTEGER?;
|
||||
value = {|
|
||||
do
|
||||
{
|
||||
if (is_negative)
|
||||
{
|
||||
$Type new_value = value * base_used - c;
|
||||
if (new_value > value) return NumberConversion.INTEGER_OVERFLOW?;
|
||||
return new_value;
|
||||
value = new_value;
|
||||
break;
|
||||
}
|
||||
$Type new_value = value * base_used + c;
|
||||
if (new_value < value) return NumberConversion.INTEGER_OVERFLOW?;
|
||||
return new_value;
|
||||
|}!;
|
||||
value = new_value;
|
||||
};
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -874,7 +870,7 @@ macro String new_struct_to_str(x, Allocator allocator = allocator::heap())
|
||||
DString s;
|
||||
@stack_mem(512; Allocator mem)
|
||||
{
|
||||
s.new_init(allocator: mem);
|
||||
s.init(mem);
|
||||
io::fprint(&s, x)!!;
|
||||
return s.copy_str(allocator);
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
@@ -376,10 +376,7 @@ macro double! hexfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
else
|
||||
{
|
||||
got_digit = true;
|
||||
int d = {|
|
||||
if (c > '9') return (c | 32) + 10 - 'a';
|
||||
return c - '0';
|
||||
|};
|
||||
int d = c > '9' ? ((c | 32) + 10 - 'a') : (c - '0');
|
||||
switch
|
||||
{
|
||||
case dc < 8:
|
||||
|
||||
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.init(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);
|
||||
}
|
||||
|
||||
@@ -162,12 +162,12 @@ macro bool is_unsigned($Type) @const
|
||||
|
||||
macro bool is_indexable($Type) @const
|
||||
{
|
||||
return $defined($Type{}[0]);
|
||||
return $defined(($Type){}[0]);
|
||||
}
|
||||
|
||||
macro bool is_ref_indexable($Type) @const
|
||||
{
|
||||
return $defined(&$Type{}[0]);
|
||||
return $defined(&($Type){}[0]);
|
||||
}
|
||||
|
||||
macro bool is_intlike($Type) @const
|
||||
@@ -251,6 +251,7 @@ macro bool @has_same(#a, #b, ...) @const
|
||||
macro bool may_load_atomic($Type) @const
|
||||
{
|
||||
$switch ($Type.kindof)
|
||||
$case BOOL:
|
||||
$case SIGNED_INT:
|
||||
$case UNSIGNED_INT:
|
||||
$case POINTER:
|
||||
|
||||
@@ -27,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):
|
||||
|
||||
@@ -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,12 +70,9 @@ 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 void! CsvReader.@each_row(self, int rows = int.max; @body(String[] row)) @maydiscard
|
||||
|
||||
@@ -89,8 +89,8 @@ fn usz! decode_bytes(char[] src, char[] dst)
|
||||
return i;
|
||||
}
|
||||
|
||||
const char[*] HEXALPHABET @private = "0123456789abcdef";
|
||||
const char[*] HEXREVERSE @private =
|
||||
const char[?] HEXALPHABET @private = "0123456789abcdef";
|
||||
const char[?] HEXREVERSE @private =
|
||||
x`ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
|
||||
@@ -17,12 +17,12 @@ fault JsonParsingError
|
||||
|
||||
fn Object*! parse_string(String s, Allocator allocator = allocator::heap())
|
||||
{
|
||||
return parse(ByteReader{}.init(s), allocator);
|
||||
return parse((ByteReader){}.init(s), allocator);
|
||||
}
|
||||
|
||||
fn Object*! temp_parse_string(String s)
|
||||
{
|
||||
return parse(ByteReader{}.init(s), allocator::temp());
|
||||
return parse((ByteReader){}.init(s), allocator::temp());
|
||||
}
|
||||
|
||||
fn Object*! parse(InStream s, Allocator allocator = allocator::heap())
|
||||
|
||||
94
lib/std/experimental/FrameScheduler.c3
Normal file
94
lib/std/experimental/FrameScheduler.c3
Normal file
@@ -0,0 +1,94 @@
|
||||
module std::experimental::scheduler(<Event>);
|
||||
import std::collections, std::thread, std::time;
|
||||
|
||||
struct DelayedSchedulerEvent @local
|
||||
{
|
||||
inline Event event;
|
||||
Clock execution_time;
|
||||
}
|
||||
|
||||
fn int DelayedSchedulerEvent.compare_to(self, DelayedSchedulerEvent other) @local
|
||||
{
|
||||
switch
|
||||
{
|
||||
case self.execution_time < other.execution_time: return -1;
|
||||
case self.execution_time > other.execution_time: return 1;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct FrameScheduler
|
||||
{
|
||||
PriorityQueue(<DelayedSchedulerEvent>) delayed_events;
|
||||
List(<Event>) events;
|
||||
List(<Event>) pending_events;
|
||||
bool pending;
|
||||
Mutex mtx;
|
||||
}
|
||||
|
||||
fn void FrameScheduler.init(&self)
|
||||
{
|
||||
self.events.init(mem);
|
||||
self.pending_events.init(mem);
|
||||
self.delayed_events.init(mem);
|
||||
(void)self.mtx.init();
|
||||
bool pending;
|
||||
}
|
||||
|
||||
macro void FrameScheduler.@destroy(&self; @destruct(Event e))
|
||||
{
|
||||
foreach (e : self.events) @destruct(e);
|
||||
foreach (e : self.pending_events) @destruct(e);
|
||||
foreach (e : self.delayed_events.heap) @destruct(e.event);
|
||||
self.events.free();
|
||||
self.pending_events.free();
|
||||
self.delayed_events.free();
|
||||
(void)self.mtx.destroy();
|
||||
}
|
||||
|
||||
fn void FrameScheduler.queue_delayed_event(&self, Event event, Duration delay)
|
||||
{
|
||||
self.mtx.@in_lock()
|
||||
{
|
||||
self.delayed_events.push({ event, clock::now().add_duration(delay)});
|
||||
@atomic_store(self.pending, true);
|
||||
};
|
||||
}
|
||||
|
||||
fn bool FrameScheduler.has_delayed(&self)
|
||||
{
|
||||
self.mtx.@in_lock()
|
||||
{
|
||||
return @ok(self.delayed_events.first());
|
||||
};
|
||||
}
|
||||
|
||||
fn void FrameScheduler.queue_event(&self, Event event)
|
||||
{
|
||||
self.mtx.@in_lock()
|
||||
{
|
||||
self.pending_events.push(event);
|
||||
@atomic_store(self.pending, true);
|
||||
};
|
||||
}
|
||||
fn Event! FrameScheduler.pop_event(&self)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (try event = self.events.pop()) return event;
|
||||
if (!@atomic_load(self.pending)) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
self.mtx.@in_lock()
|
||||
{
|
||||
self.events.add_all(&self.pending_events);
|
||||
self.pending_events.clear();
|
||||
Clock c = clock::now();
|
||||
while (try top = self.delayed_events.first())
|
||||
{
|
||||
if (top.execution_time > c) break;
|
||||
self.events.push(self.delayed_events.pop()!!);
|
||||
}
|
||||
@atomic_store(self.pending, self.delayed_events.len() > 0);
|
||||
if (!self.events.len()) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,7 @@ macro @i(x, y, z) => y ^ (x | ~z);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -78,10 +78,10 @@ fn char[HASH_BYTES] Sha1.final(&self)
|
||||
{
|
||||
finalcount[i] = (char)((self.count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 0xFF);
|
||||
}
|
||||
self.update(char[] { 0o200 });
|
||||
self.update((char[]){ 0o200 });
|
||||
while ((self.count[0] & 504) != 448)
|
||||
{
|
||||
self.update(char[] { 0 });
|
||||
self.update((char[]){ 0 });
|
||||
}
|
||||
|
||||
self.update(&finalcount);
|
||||
@@ -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,35 +119,35 @@ 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);
|
||||
*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);
|
||||
*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);
|
||||
*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);
|
||||
*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);
|
||||
@@ -158,15 +158,15 @@ 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];
|
||||
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);
|
||||
@@ -247,11 +247,11 @@ fn void sha1_transform(uint* state, char* buffer) @local
|
||||
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;
|
||||
(*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 = {};
|
||||
}
|
||||
@@ -19,6 +19,11 @@ fn File! open_path(Path path, String mode)
|
||||
return from_handle(os::native_fopen(path.str_view(), mode));
|
||||
}
|
||||
|
||||
fn bool exists(String file) => @pool()
|
||||
{
|
||||
return path::exists(path::temp_new(file)) ?? false;
|
||||
}
|
||||
|
||||
fn File from_handle(CFile file)
|
||||
{
|
||||
return { .file = file };
|
||||
@@ -106,7 +111,7 @@ fn void! File.close(&self) @inline @dynamic
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.file
|
||||
@require self.file != null
|
||||
*>
|
||||
fn bool File.eof(&self) @inline
|
||||
{
|
||||
@@ -123,13 +128,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
|
||||
{
|
||||
@@ -161,6 +175,8 @@ fn char[]! load_buffer(String filename, char[] buffer)
|
||||
|
||||
}
|
||||
|
||||
fn char[]! load(Allocator allocator, String filename) => load_new(filename, allocator);
|
||||
|
||||
fn char[]! load_new(String filename, Allocator allocator = allocator::heap())
|
||||
{
|
||||
File file = open(filename, "rb")!;
|
||||
@@ -177,11 +193,15 @@ fn char[]! load_new(String filename, Allocator allocator = allocator::heap())
|
||||
return data[:len];
|
||||
}
|
||||
|
||||
fn char[]! load_path_new(Path path, Allocator allocator = allocator::heap()) => load_new(path.str_view(), allocator);
|
||||
|
||||
fn char[]! load_temp(String filename)
|
||||
{
|
||||
return load_new(filename, allocator::temp());
|
||||
}
|
||||
|
||||
fn char[]! load_path_temp(Path path) => load_temp(path.str_view());
|
||||
|
||||
fn void! save(String filename, char[] data)
|
||||
{
|
||||
File file = open(filename, "wb")!;
|
||||
@@ -194,7 +214,7 @@ fn void! save(String filename, char[] data)
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.file `File must be initialized`
|
||||
@require self.file != null `File must be initialized`
|
||||
*>
|
||||
fn void! File.flush(&self) @dynamic
|
||||
{
|
||||
|
||||
@@ -145,7 +145,10 @@ fn usz! Formatter.print_with_function(&self, Printable arg)
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
|
||||
fn usz! Formatter.out_unknown(&self, String category, any arg) @private
|
||||
{
|
||||
return self.out_substr("[") + self.out_substr(category) + self.out_substr(" type:") + self.ntoa((iptr)arg.type, false, 16) + self.out_substr(", addr:") + self.ntoa((iptr)arg.ptr, false, 16) + self.out_substr("]");
|
||||
}
|
||||
fn usz! Formatter.out_str(&self, any arg) @private
|
||||
{
|
||||
switch (arg.type.kindof)
|
||||
@@ -199,13 +202,13 @@ fn usz! Formatter.out_str(&self, any arg) @private
|
||||
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:
|
||||
return self.out_substr("<struct>");
|
||||
return self.out_unknown("struct", arg);
|
||||
case UNION:
|
||||
return self.out_substr("<union>");
|
||||
return self.out_unknown("union", arg);
|
||||
case BITSTRUCT:
|
||||
return self.out_substr("<bitstruct>");
|
||||
return self.out_unknown("bitstruct", arg);
|
||||
case FUNC:
|
||||
return self.out_substr("<function>");
|
||||
return self.out_unknown("function", arg);
|
||||
case DISTINCT:
|
||||
if (arg.type == String.typeid)
|
||||
{
|
||||
@@ -240,7 +243,7 @@ fn usz! Formatter.out_str(&self, any arg) @private
|
||||
self.width = 0;
|
||||
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 +277,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 +479,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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,57 +5,48 @@ 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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -4,7 +4,7 @@ import std::io, std::os;
|
||||
fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator allocator)
|
||||
{
|
||||
PathList list;
|
||||
list.new_init(allocator: allocator);
|
||||
list.init(allocator);
|
||||
DIRPtr directory = posix::opendir(dir.str_view() ? dir.as_zstr() : (ZString)".");
|
||||
defer if (directory) posix::closedir(directory);
|
||||
if (!directory) return (path::is_dir(dir) ? IoError.CANNOT_READ_DIR : IoError.FILE_NOT_DIR)?;
|
||||
@@ -27,7 +27,7 @@ import std::time, std::os, std::io;
|
||||
fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator allocator)
|
||||
{
|
||||
PathList list;
|
||||
list.new_init(allocator: allocator);
|
||||
list.init(allocator);
|
||||
|
||||
@pool(allocator)
|
||||
{
|
||||
|
||||
@@ -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())
|
||||
@@ -280,7 +274,7 @@ fn Path! Path.new_absolute(self, Allocator allocator = allocator::heap())
|
||||
};
|
||||
$else
|
||||
String cwd = os::getcwd(allocator::temp())!;
|
||||
return Path { cwd, self.env }.new_append(path_str, allocator)!;
|
||||
return (Path){ cwd, self.env }.new_append(path_str, allocator)!;
|
||||
$endif
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,23 @@ macro usz! read_all(stream, char[] buffer)
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro char[]! read_new_fully(stream, Allocator allocator = allocator::heap())
|
||||
macro char[]! read_new_fully(stream, Allocator allocator = allocator::heap()) @deprecated("Use read_fully(mem)")
|
||||
{
|
||||
usz len = available(stream)!;
|
||||
char* data = allocator::malloc_try(allocator, len)!;
|
||||
defer catch allocator::free(allocator, data);
|
||||
usz read = 0;
|
||||
while (read < len)
|
||||
{
|
||||
read += stream.read(data[read:len - read])!;
|
||||
}
|
||||
return data[:len];
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro char[]! read_fully(Allocator allocator, stream)
|
||||
{
|
||||
usz len = available(stream)!;
|
||||
char* data = allocator::malloc_try(allocator, len)!;
|
||||
@@ -181,12 +197,12 @@ fn usz! copy_to(InStream in, OutStream dst, char[] buffer = {})
|
||||
if (&dst.read_to) return dst.read_to(in);
|
||||
$switch (env::MEMORY_ENV)
|
||||
$case NORMAL:
|
||||
return copy_through_buffer(in, dst, &&char[4096]{});
|
||||
return copy_through_buffer(in, dst, &&(char[4096]){});
|
||||
$case SMALL:
|
||||
return copy_through_buffer(in, dst, &&char[1024]{});
|
||||
return copy_through_buffer(in, dst, &&(char[1024]){});
|
||||
$case TINY:
|
||||
$case NONE:
|
||||
return copy_through_buffer(in, dst, &&(char[256]{}));
|
||||
return copy_through_buffer(in, dst, &&(char[256]){});
|
||||
$endswitch
|
||||
}
|
||||
|
||||
@@ -208,7 +224,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)
|
||||
|
||||
@@ -16,24 +16,42 @@ 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.init(&self, Allocator allocator, usz max_read, usz initial_capacity = 16)
|
||||
{
|
||||
*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)
|
||||
<*
|
||||
ByteBuffer provides a streamable read/write buffer.
|
||||
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()) @deprecated("Use init(mem)")
|
||||
{
|
||||
return self.new_init(max_read, initial_capacity, allocator::temp());
|
||||
*self = { .allocator = allocator, .max_read = max_read };
|
||||
initial_capacity = max(initial_capacity, 16);
|
||||
self.grow(initial_capacity);
|
||||
return self;
|
||||
}
|
||||
|
||||
fn ByteBuffer* ByteBuffer.tinit(&self, usz max_read, usz initial_capacity = 16)
|
||||
{
|
||||
return self.init(allocator::temp(), max_read, initial_capacity);
|
||||
}
|
||||
|
||||
fn ByteBuffer* ByteBuffer.temp_init(&self, usz max_read, usz initial_capacity = 16) @deprecated("Use tinit()")
|
||||
{
|
||||
return self.init(allocator::temp(), max_read, 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 +66,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 +75,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 +146,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];
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,19 @@ struct ByteWriter (OutStream)
|
||||
@require self.bytes.len == 0 "Init may not run on already initialized data"
|
||||
@ensure (bool)allocator, self.index == 0
|
||||
*>
|
||||
fn ByteWriter* ByteWriter.new_init(&self, Allocator allocator = allocator::heap())
|
||||
fn ByteWriter* ByteWriter.new_init(&self, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)")
|
||||
{
|
||||
*self = { .bytes = {}, .allocator = allocator };
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] self
|
||||
@param [&inout] allocator
|
||||
@require self.bytes.len == 0 "Init may not run on already initialized data"
|
||||
@ensure (bool)allocator, self.index == 0
|
||||
*>
|
||||
fn ByteWriter* ByteWriter.init(&self, Allocator allocator)
|
||||
{
|
||||
*self = { .bytes = {}, .allocator = allocator };
|
||||
return self;
|
||||
@@ -25,9 +37,19 @@ fn ByteWriter* ByteWriter.new_init(&self, Allocator allocator = allocator::heap(
|
||||
@require self.bytes.len == 0 "Init may not run on already initialized data"
|
||||
@ensure self.index == 0
|
||||
*>
|
||||
fn ByteWriter* ByteWriter.temp_init(&self)
|
||||
fn ByteWriter* ByteWriter.tinit(&self)
|
||||
{
|
||||
return self.new_init(allocator::temp()) @inline;
|
||||
return self.init(allocator::temp()) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] self
|
||||
@require self.bytes.len == 0 "Init may not run on already initialized data"
|
||||
@ensure self.index == 0
|
||||
*>
|
||||
fn ByteWriter* ByteWriter.temp_init(&self) @deprecated("Use tinit")
|
||||
{
|
||||
return self.init(allocator::temp()) @inline;
|
||||
}
|
||||
|
||||
fn ByteWriter* ByteWriter.init_with_buffer(&self, char[] data)
|
||||
|
||||
@@ -18,7 +18,21 @@ struct MultiReader (InStream)
|
||||
@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())
|
||||
fn MultiReader* MultiReader.new_init(&self, InStream... readers, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)")
|
||||
{
|
||||
InStream []copy = allocator::new_array(allocator, InStream, readers.len);
|
||||
copy[..] = readers[..];
|
||||
*self = { .readers = copy, .allocator = allocator };
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@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.init(&self, Allocator allocator, InStream... readers)
|
||||
{
|
||||
InStream []copy = allocator::new_array(allocator, InStream, readers.len);
|
||||
copy[..] = readers[..];
|
||||
@@ -31,9 +45,19 @@ fn MultiReader* MultiReader.new_init(&self, InStream... readers, Allocator alloc
|
||||
@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)
|
||||
fn MultiReader* MultiReader.temp_init(&self, InStream... readers) @deprecated("Use tinit()")
|
||||
{
|
||||
return self.new_init(...readers, allocator: allocator::temp());
|
||||
return self.init(allocator::temp(), ...readers);
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] self
|
||||
@require self.readers.len == 0 "Init may not run on already initialized data"
|
||||
@ensure self.index == 0
|
||||
*>
|
||||
fn MultiReader* MultiReader.tinit(&self, InStream... readers)
|
||||
{
|
||||
return self.init(allocator::temp(), ...readers);
|
||||
}
|
||||
|
||||
fn void MultiReader.free(&self)
|
||||
|
||||
@@ -15,7 +15,21 @@ struct MultiWriter (OutStream)
|
||||
@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())
|
||||
fn MultiWriter* MultiWriter.init(&self, Allocator allocator, OutStream... writers)
|
||||
{
|
||||
OutStream[] copy = allocator::new_array(allocator, OutStream, writers.len);
|
||||
copy[..] = writers[..];
|
||||
*self = { .writers = copy, .allocator = allocator };
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@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()) @deprecated("Use init(mem)")
|
||||
{
|
||||
OutStream[] copy = allocator::new_array(allocator, OutStream, writers.len);
|
||||
copy[..] = writers[..];
|
||||
@@ -28,9 +42,19 @@ fn MultiWriter* MultiWriter.new_init(&self, OutStream... writers, Allocator allo
|
||||
@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)
|
||||
fn MultiWriter* MultiWriter.temp_init(&self, OutStream... writers) @deprecated("Use tinit")
|
||||
{
|
||||
return self.new_init(...writers, allocator: allocator::temp());
|
||||
return self.init(allocator::temp(), ...writers);
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] self
|
||||
@require writers.len > 0
|
||||
@require self.writers.len == 0 "Init may not run on already initialized data"
|
||||
*>
|
||||
fn MultiWriter* MultiWriter.tinit(&self, OutStream... writers)
|
||||
{
|
||||
return self.init(allocator::temp(), ...writers);
|
||||
}
|
||||
|
||||
fn void MultiWriter.free(&self)
|
||||
|
||||
@@ -167,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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -24,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 {
|
||||
@@ -51,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;
|
||||
@@ -44,16 +44,15 @@ fn BigInt* BigInt.init(&self, int128 value)
|
||||
fn BigInt* BigInt.init_with_u128(&self, uint128 value)
|
||||
{
|
||||
self.data[..] = 0;
|
||||
int128 tmp = value;
|
||||
uint128 tmp = value;
|
||||
uint len = 0;
|
||||
while (tmp != 0 && len < MAX_LEN)
|
||||
while (tmp != 0)
|
||||
{
|
||||
self.data[len] = (uint)(tmp & 0xFFFFFFFF);
|
||||
tmp >>= 32;
|
||||
len++;
|
||||
}
|
||||
self.len = len;
|
||||
assert(value == 0 || tmp == 0 || !self.is_negative());
|
||||
assert(!self.is_negative());
|
||||
self.len = max(len, 1);
|
||||
return self;
|
||||
}
|
||||
@@ -64,11 +63,18 @@ fn BigInt* BigInt.init_with_u128(&self, uint128 value)
|
||||
fn BigInt* BigInt.init_with_array(&self, uint[] values)
|
||||
{
|
||||
self.data[..] = 0;
|
||||
|
||||
if (values.len == 0)
|
||||
{
|
||||
self.len = 1;
|
||||
return self;
|
||||
}
|
||||
|
||||
self.len = values.len;
|
||||
|
||||
foreach_r(i, val : values)
|
||||
{
|
||||
values[values.len - 1 - i] = val;
|
||||
self.data[values.len - 1 - i] = val;
|
||||
}
|
||||
while (self.len > 1 && self.data[self.len - 1] == 0)
|
||||
{
|
||||
@@ -92,9 +98,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?;
|
||||
}
|
||||
@@ -520,12 +526,12 @@ 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;
|
||||
DString str;
|
||||
str.new_init(4096, allocator: mem);
|
||||
str.init(mem, 4096);
|
||||
bool negative = self.is_negative();
|
||||
if (negative)
|
||||
{
|
||||
|
||||
@@ -92,13 +92,13 @@ fault MatrixError
|
||||
|
||||
def Complexf = Complex(<float>);
|
||||
def Complex = Complex(<double>);
|
||||
def COMPLEX_IDENTITY = complex::IDENTITY(<double>);
|
||||
def COMPLEXF_IDENTITY = complex::IDENTITY(<float>);
|
||||
def COMPLEX_IDENTITY = complex::IDENTITY(<double>) @builtin;
|
||||
def COMPLEXF_IDENTITY = complex::IDENTITY(<float>) @builtin;
|
||||
|
||||
def Quaternionf = Quaternion(<float>);
|
||||
def Quaternion = Quaternion(<double>);
|
||||
def QUATERNION_IDENTITY = quaternion::IDENTITY(<double>);
|
||||
def QUATERNIONF_IDENTITY = quaternion::IDENTITY(<float>);
|
||||
def QUATERNION_IDENTITY = quaternion::IDENTITY(<double>) @builtin;
|
||||
def QUATERNIONF_IDENTITY = quaternion::IDENTITY(<float>) @builtin;
|
||||
|
||||
def Matrix2f = Matrix2x2(<float>);
|
||||
def Matrix2 = Matrix2x2(<double>);
|
||||
@@ -106,17 +106,17 @@ def Matrix3f = Matrix3x3(<float>);
|
||||
def Matrix3 = Matrix3x3(<double>);
|
||||
def Matrix4f = Matrix4x4(<float>);
|
||||
def Matrix4 = Matrix4x4(<double>);
|
||||
def matrix4_ortho = matrix::ortho(<double>);
|
||||
def matrix4_perspective = matrix::perspective(<double>);
|
||||
def matrix4f_ortho = matrix::ortho(<float>);
|
||||
def matrix4f_perspective = matrix::perspective(<float>);
|
||||
def matrix4_ortho = matrix::ortho(<double>) @builtin;
|
||||
def matrix4_perspective = matrix::perspective(<double>) @builtin;
|
||||
def matrix4f_ortho = matrix::ortho(<float>) @builtin;
|
||||
def matrix4f_perspective = matrix::perspective(<float>) @builtin;
|
||||
|
||||
def MATRIX2_IDENTITY = matrix::IDENTITY2(<double>);
|
||||
def MATRIX2F_IDENTITY = matrix::IDENTITY2(<float>);
|
||||
def MATRIX3_IDENTITY = matrix::IDENTITY3(<double>);
|
||||
def MATRIX3F_IDENTITY = matrix::IDENTITY3(<float>);
|
||||
def MATRIX4_IDENTITY = matrix::IDENTITY4(<double>);
|
||||
def MATRIX4F_IDENTITY = matrix::IDENTITY4(<float>);
|
||||
def MATRIX2_IDENTITY = matrix::IDENTITY2(<double>) @builtin;
|
||||
def MATRIX2F_IDENTITY = matrix::IDENTITY2(<float>) @builtin;
|
||||
def MATRIX3_IDENTITY = matrix::IDENTITY3(<double>) @builtin;
|
||||
def MATRIX3F_IDENTITY = matrix::IDENTITY3(<float>) @builtin;
|
||||
def MATRIX4_IDENTITY = matrix::IDENTITY4(<double>) @builtin;
|
||||
def MATRIX4F_IDENTITY = matrix::IDENTITY4(<float>) @builtin;
|
||||
|
||||
|
||||
<*
|
||||
@@ -629,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);
|
||||
}
|
||||
@@ -647,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));
|
||||
@@ -690,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);
|
||||
@@ -1178,49 +1178,49 @@ macro bool @is_same_vector_or_scalar(#vector_value, #vector_or_scalar) @private
|
||||
@require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
|
||||
@require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
|
||||
*>
|
||||
macro char[<*>] char[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
|
||||
macro char[<?>] char[<?>].muldiv(self, mul, div) => mul_div_helper(self, 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`
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -11,21 +11,21 @@ union Complex
|
||||
|
||||
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 };
|
||||
macro Complex Complex.sub_each(self, Real b) => Complex { .v = self.v - b };
|
||||
macro Complex Complex.scale(self, Real s) => Complex { .v = self.v * s };
|
||||
macro Complex Complex.add(self, Complex b) => { .v = self.v + b.v };
|
||||
macro Complex Complex.add_each(self, Real b) => { .v = self.v + b };
|
||||
macro Complex Complex.sub(self, Complex b) => { .v = self.v - b.v };
|
||||
macro Complex Complex.sub_each(self, Real b) => { .v = self.v - b };
|
||||
macro Complex Complex.scale(self, Real s) => { .v = self.v * s };
|
||||
macro Complex Complex.mul(self, Complex b) => { self.r * b.r - self.c * b.c, self.r * b.c + b.r * self.c };
|
||||
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 };
|
||||
return { (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 };
|
||||
return { self.r / sqr, -self.c / sqr };
|
||||
}
|
||||
macro Complex Complex.conjugate(self) => Complex { .r = self.r, .c = -self.c };
|
||||
macro Complex Complex.conjugate(self) => { .r = self.r, .c = -self.c };
|
||||
macro bool Complex.equals(self, Complex b) => self.v == b.v;
|
||||
|
||||
@@ -420,19 +420,19 @@ const Matrix4x4 IDENTITY4 = { .m = { [0] = 1, [5] = 1, [10] = 1, [15] = 1 } };
|
||||
macro matrix_component_mul(mat, val) @private
|
||||
{
|
||||
var $Type = Real[<$typeof(mat.m).len>];
|
||||
return $typeof(*mat) { .m = val * ($Type)mat.m };
|
||||
return ($typeof(*mat)) { .m = val * ($Type)mat.m };
|
||||
}
|
||||
|
||||
macro matrix_add(mat, mat2) @private
|
||||
{
|
||||
var $Type = Real[<$typeof(mat.m).len>];
|
||||
return $typeof(*mat) { .m = ($Type)mat.m + ($Type)mat2.m };
|
||||
return ($typeof(*mat)) { .m = ($Type)mat.m + ($Type)mat2.m };
|
||||
}
|
||||
|
||||
macro matrix_sub(mat, mat2) @private
|
||||
{
|
||||
var $Type = Real[<$typeof(mat.m).len>];
|
||||
return $typeof(*mat) { .m = ($Type)mat.m - ($Type)mat2.m };
|
||||
return ($typeof(*mat)) { .m = ($Type)mat.m - ($Type)mat2.m };
|
||||
}
|
||||
|
||||
macro matrix_look_at($Type, eye, target, up) @private
|
||||
@@ -441,7 +441,7 @@ macro matrix_look_at($Type, eye, target, up) @private
|
||||
var vx = up.cross(vz).normalize();
|
||||
var vy = vz.cross(vx);
|
||||
|
||||
return $Type {
|
||||
return ($Type){
|
||||
vx[0], vx[1], vx[2], - (Real)vx.dot(eye),
|
||||
vy[0], vy[1], vy[2], - (Real)vy.dot(eye),
|
||||
vz[0], vz[1], vz[2], - (Real)vz.dot(eye),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const double[*] TAN_T = {
|
||||
const double[?] TAN_T = {
|
||||
3.33333333333334091986e-01, /* 3FD55555, 55555563 */
|
||||
1.33333333333201242699e-01, /* 3FC11111, 1110FE7A */
|
||||
5.39682539762260521377e-02, /* 3FABA1BA, 1BB341FE */
|
||||
|
||||
@@ -16,7 +16,7 @@ module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
*/
|
||||
|
||||
// |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 */
|
||||
|
||||
@@ -12,21 +12,21 @@ module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const double[*] ATANHI @private = {
|
||||
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 */
|
||||
@@ -116,21 +116,21 @@ fn double _atan(double x) @weak @extern("atan") @nostrip
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const float[*] ATANHIF @private = {
|
||||
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,
|
||||
|
||||
@@ -94,9 +94,9 @@ fn int __rem_pio2f(float x, double *y)
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const int[*] INIT_JK = {3,4,4,6}; /* initial value for jk */
|
||||
const int[?] INIT_JK = {3,4,4,6}; /* initial value for jk */
|
||||
|
||||
const int[*] IPIO2 = {
|
||||
const int[?] IPIO2 = {
|
||||
0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62,
|
||||
0x95993C, 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, 0x246E3A,
|
||||
0x424DD2, 0xE00649, 0x2EEA09, 0xD1921C, 0xFE1DEB, 0x1CB129,
|
||||
@@ -109,7 +109,7 @@ const int[*] IPIO2 = {
|
||||
0x91615E, 0xE61B08, 0x659985, 0x5F14A0, 0x68408D, 0xFFD880,
|
||||
0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B, };
|
||||
|
||||
const double[*] PIO2 = {
|
||||
const double[?] PIO2 = {
|
||||
1.57079625129699707031e+00, /* 0x3FF921FB, 0x40000000 */
|
||||
7.54978941586159635335e-08, /* 0x3E74442D, 0x00000000 */
|
||||
5.39030252995776476554e-15, /* 0x3CF84698, 0x80000000 */
|
||||
@@ -238,8 +238,7 @@ fn int __rem_pio2_large(double* x, double* y, int e0, int nx, int prec)
|
||||
{
|
||||
/* add q[jz+1] to q[jz+k] */
|
||||
f[jx + i] = (double)IPIO2[jv + i];
|
||||
for (j = 0, fw = 0.0; j <= jx; j++)
|
||||
fw += x[j] * f[jx + i - j];
|
||||
for (j = 0, fw = 0.0; j <= jx; j++) fw += x[j] * f[jx + i - j];
|
||||
q[i] = fw;
|
||||
}
|
||||
jz += k;
|
||||
@@ -303,8 +302,7 @@ fn int __rem_pio2_large(double* x, double* y, int e0, int nx, int prec)
|
||||
case 1:
|
||||
case 2:
|
||||
fw = 0.0;
|
||||
for (int i = jz; i >= 0; i--)
|
||||
fw += fq[i];
|
||||
for (int i = jz; i >= 0; i--) fw += fq[i];
|
||||
// TODO: drop excess precision here once double_t is used
|
||||
fw = (double)fw;
|
||||
y[0] = ih == 0 ? fw : -fw;
|
||||
@@ -324,8 +322,7 @@ fn int __rem_pio2_large(double* x, double* y, int e0, int nx, int prec)
|
||||
fq[i] += fq[i - 1] - fw;
|
||||
fq[i - 1] = fw;
|
||||
}
|
||||
for (fw = 0.0, int i = jz; i >= 2; i--)
|
||||
fw += fq[i];
|
||||
for (fw = 0.0, int i = jz; i >= 2; i--) fw += fq[i];
|
||||
if (ih == 0)
|
||||
{
|
||||
y[0] = fq[0];
|
||||
|
||||
@@ -11,11 +11,11 @@ union Quaternion
|
||||
|
||||
const Quaternion IDENTITY = { 0, 0, 0, 1 };
|
||||
|
||||
macro Quaternion Quaternion.add(Quaternion a, Quaternion b) => Quaternion { .v = a.v + b.v };
|
||||
macro Quaternion Quaternion.add_each(Quaternion a, Real b) => Quaternion { .v = a.v + b };
|
||||
macro Quaternion Quaternion.sub(Quaternion a, Quaternion b) => Quaternion { .v = a.v - b.v };
|
||||
macro Quaternion Quaternion.sub_each(Quaternion a, Real b) => Quaternion { .v = a.v - b };
|
||||
macro Quaternion Quaternion.scale(Quaternion a, Real s) => Quaternion { .v = a.v * s };
|
||||
macro Quaternion Quaternion.add(Quaternion a, Quaternion b) => { .v = a.v + b.v };
|
||||
macro Quaternion Quaternion.add_each(Quaternion a, Real b) => { .v = a.v + b };
|
||||
macro Quaternion Quaternion.sub(Quaternion a, Quaternion b) => { .v = a.v - b.v };
|
||||
macro Quaternion Quaternion.sub_each(Quaternion a, Real b) => { .v = a.v - b };
|
||||
macro Quaternion Quaternion.scale(Quaternion a, Real s) => { .v = a.v * s };
|
||||
macro Quaternion Quaternion.normalize(Quaternion q) => { .v = q.v.normalize() };
|
||||
macro Real Quaternion.length(Quaternion q) => q.v.length();
|
||||
macro Quaternion Quaternion.lerp(Quaternion q1, Quaternion q2, Real amount) => { .v = q1.v.lerp(q2.v, amount) };
|
||||
@@ -76,7 +76,7 @@ macro into_matrix(Quaternion* q, $Type) @private
|
||||
var z = rotation.k;
|
||||
var w = rotation.l;
|
||||
|
||||
return $Type {
|
||||
return ($Type) {
|
||||
1 - 2*y*y - 2*z*z, 2*x*y - 2*z*w, 2*x*z + 2*y*w, 0,
|
||||
2*x*y + 2*z*w, 1 - 2*x*x - 2*z*z, 2*y*z - 2*x*w, 0,
|
||||
2*x*z - 2*y*w, 2*y*z + 2*x*w , 1 - 2*x*x - 2*y*y, 0,
|
||||
|
||||
@@ -145,7 +145,7 @@ macro transform2(v, mat) @private
|
||||
|
||||
macro transform3(v, mat) @private
|
||||
{
|
||||
return $typeof(v) {
|
||||
return ($typeof(v)){
|
||||
mat.m00 * v[0] + mat.m10 * v[1] + mat.m20 * v[2] + mat.m30,
|
||||
mat.m01 * v[0] + mat.m11 * v[1] + mat.m21 * v[2] + mat.m31,
|
||||
mat.m02 * v[0] + mat.m12 * v[1] + mat.m22 * v[2] + mat.m32
|
||||
@@ -169,7 +169,7 @@ macro void ortho_normalize3(v1, v2) @private
|
||||
|
||||
macro rotate_by_quat3(v, q) @private
|
||||
{
|
||||
return $typeof(v) {
|
||||
return ($typeof(v)){
|
||||
v[0] * (q.i * q.i + q.l * q.l - q.j * q.j - q.k * q.k)
|
||||
+ v[1] * (2 * q.i * q.j - 2 * q.l * q.k)
|
||||
+ v[2] * (2 * q.i * q.k - 2 * q.l * q.j),
|
||||
@@ -233,7 +233,7 @@ macro barycenter3(p, a, b, c) @private
|
||||
var denom = d00 * d11 - d01 * d01;
|
||||
var y = (d11 * d20 - d01 * d21) / denom;
|
||||
var z = (d00 * d21 - d01 * d20) / denom;
|
||||
return $typeof(p) { 1 - y - z, y, z };
|
||||
return ($typeof(p)){ 1 - y - z, y, z };
|
||||
}
|
||||
|
||||
macro refract3(v, n, r) @private
|
||||
|
||||
@@ -86,7 +86,7 @@ fn char[8 * 4] entropy() @if(!env::WASM_NOLIBC)
|
||||
hash(&entropy),
|
||||
random_int,
|
||||
hash(clock::now()),
|
||||
hash(&DString.new_init),
|
||||
hash(&DString.init),
|
||||
hash(allocator::heap())
|
||||
};
|
||||
return bitcast(entropy_data, char[8 * 4]);
|
||||
|
||||
@@ -257,16 +257,13 @@ fn bool InetAddress.is_multicast_link_local(InetAddress* addr)
|
||||
return addr.ipv4.a == 224 && addr.ipv4.b == 0 && addr.ipv4.c == 0;
|
||||
}
|
||||
|
||||
fn AddrInfo*! addrinfo(String host, uint port, AIFamily ai_family, AISockType ai_socktype) @if(os::SUPPORTS_INET)
|
||||
fn AddrInfo*! addrinfo(String host, uint port, AIFamily ai_family, AISockType ai_socktype) @if(os::SUPPORTS_INET) => @pool()
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
ZString zhost = host.zstr_tcopy();
|
||||
DString str = dstring::temp_with_capacity(32);
|
||||
str.appendf("%d", port);
|
||||
AddrInfo hints = { .ai_family = ai_family, .ai_socktype = ai_socktype };
|
||||
AddrInfo* ai;
|
||||
if (os::getaddrinfo(zhost, str.zstr_view(), &hints, &ai)) return NetError.ADDRINFO_FAILED?;
|
||||
return ai;
|
||||
};
|
||||
ZString zhost = host.zstr_tcopy();
|
||||
DString str = dstring::temp_with_capacity(32);
|
||||
str.appendf("%d", port);
|
||||
AddrInfo hints = { .ai_family = ai_family, .ai_socktype = ai_socktype };
|
||||
AddrInfo* ai;
|
||||
if (os::getaddrinfo(zhost, str.zstr_view(), &hints, &ai)) return NetError.ADDRINFO_FAILED?;
|
||||
return ai;
|
||||
}
|
||||
|
||||
@@ -94,3 +94,4 @@ const CShort POLLATTRIB = 0x0400; // file attributes may have changed
|
||||
const CShort POLLNLINK = 0x0800; // (un)link/rename may have happened
|
||||
const CShort POLLWRITE = 0x1000; // file's contents may have changed
|
||||
|
||||
const CInt MSG_PEEK = 0x0002;
|
||||
@@ -88,3 +88,5 @@ const CUShort POLLREMOVE = 0x1000;
|
||||
const CUShort POLLRDHUP = 0x2000;
|
||||
const CUShort POLLFREE = 0x4000;
|
||||
const CUShort POLL_BUSY_LOOP = 0x8000;
|
||||
|
||||
const CInt MSG_PEEK = 0x0002;
|
||||
@@ -101,3 +101,5 @@ const CUShort POLLRDNORM = win32::POLLRDNORM;
|
||||
const CUShort POLLRDBAND = win32::POLLRDBAND;
|
||||
const CUShort POLLWRNORM = win32::POLLWRNORM;
|
||||
const CUShort POLLWRBAND = win32::POLLWRBAND;
|
||||
|
||||
const int MSG_PEEK = 0x0002;
|
||||
@@ -58,7 +58,7 @@ struct Poll
|
||||
*>
|
||||
fn ulong! poll(Poll[] polls, Duration timeout)
|
||||
{
|
||||
return poll_ms(polls, timeout.to_ms()) @inline;
|
||||
return poll_ms(polls, timeout == POLL_FOREVER ? -1 : timeout.to_ms()) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -155,3 +155,29 @@ fn void! Socket.close(&self) @inline @dynamic
|
||||
{
|
||||
self.sock.close()!;
|
||||
}
|
||||
|
||||
fn usz! Socket.peek(&self, char[] bytes) @dynamic
|
||||
{
|
||||
$if env::WIN32:
|
||||
isz n = libc::recv(self.sock, bytes.ptr, (int)bytes.len, os::MSG_PEEK);
|
||||
$else
|
||||
isz n = libc::recv(self.sock, bytes.ptr, bytes.len, os::MSG_PEEK);
|
||||
$endif
|
||||
if (n < 0) return os::socket_error()?;
|
||||
return (usz)n;
|
||||
}
|
||||
|
||||
enum SocketShutdownHow : (inline CInt native_value)
|
||||
{
|
||||
RECEIVE = @select(env::WIN32, libc::SD_RECEIVE, libc::SHUT_RD),
|
||||
SEND = @select(env::WIN32, libc::SD_SEND, libc::SHUT_WR),
|
||||
BOTH = @select(env::WIN32, libc::SD_BOTH, libc::SHUT_RDWR),
|
||||
}
|
||||
|
||||
fn void! Socket.shutdown(&self, SocketShutdownHow how)
|
||||
{
|
||||
if (libc::shutdown(self.sock, how) < 0)
|
||||
{
|
||||
return os::socket_error()?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,70 +177,67 @@ fn Url! new_parse(String url_string, Allocator allocator = allocator::heap())
|
||||
@param [inout] allocator
|
||||
@return "Url as a string"
|
||||
*>
|
||||
fn String Url.to_string(&self, Allocator allocator = allocator::heap()) @dynamic
|
||||
fn String Url.to_string(&self, Allocator allocator = allocator::heap()) @dynamic => @pool(allocator)
|
||||
{
|
||||
@pool(allocator)
|
||||
DString builder = dstring::temp_new();
|
||||
|
||||
// Add scheme if it exists
|
||||
if (self.scheme != "")
|
||||
{
|
||||
DString builder = dstring::temp_new();
|
||||
builder.append_chars(self.scheme);
|
||||
builder.append_char(':');
|
||||
if (self.host.len > 0) builder.append_chars("//");
|
||||
}
|
||||
|
||||
// Add scheme if it exists
|
||||
if (self.scheme != "")
|
||||
{
|
||||
builder.append_chars(self.scheme);
|
||||
builder.append_char(':');
|
||||
if (self.host.len > 0) builder.append_chars("//");
|
||||
}
|
||||
// Add username and password if they exist
|
||||
if (self.username != "")
|
||||
{
|
||||
String username = temp_encode(self.username, USERPASS);
|
||||
builder.append_chars(username);
|
||||
|
||||
// Add username and password if they exist
|
||||
if (self.username != "")
|
||||
{
|
||||
String username = temp_encode(self.username, USERPASS);
|
||||
builder.append_chars(username);
|
||||
|
||||
if (self.password != "")
|
||||
{
|
||||
builder.append_char(':');
|
||||
|
||||
String password = temp_encode(self.password, USERPASS);
|
||||
builder.append_chars(password);
|
||||
}
|
||||
builder.append_char('@');
|
||||
}
|
||||
|
||||
// Add host
|
||||
String host = temp_encode(self.host, HOST);
|
||||
builder.append_chars(host);
|
||||
|
||||
// Add port
|
||||
if (self.port != 0)
|
||||
if (self.password != "")
|
||||
{
|
||||
builder.append_char(':');
|
||||
builder.appendf("%d", self.port);
|
||||
|
||||
String password = temp_encode(self.password, USERPASS);
|
||||
builder.append_chars(password);
|
||||
}
|
||||
builder.append_char('@');
|
||||
}
|
||||
|
||||
// Add path
|
||||
String path = temp_encode(self.path, PATH);
|
||||
builder.append_chars(path);
|
||||
// Add host
|
||||
String host = temp_encode(self.host, HOST);
|
||||
builder.append_chars(host);
|
||||
|
||||
// Add query if it exists (note that `query` is expected to
|
||||
// be already properly encoded).
|
||||
if (self.query != "")
|
||||
{
|
||||
builder.append_char('?');
|
||||
builder.append_chars(self.query);
|
||||
}
|
||||
// Add port
|
||||
if (self.port != 0)
|
||||
{
|
||||
builder.append_char(':');
|
||||
builder.appendf("%d", self.port);
|
||||
}
|
||||
|
||||
// Add fragment if it exists
|
||||
if (self.fragment != "")
|
||||
{
|
||||
builder.append_char('#');
|
||||
// Add path
|
||||
String path = temp_encode(self.path, PATH);
|
||||
builder.append_chars(path);
|
||||
|
||||
String fragment = temp_encode(self.fragment, FRAGMENT);
|
||||
builder.append_chars(fragment);
|
||||
}
|
||||
// Add query if it exists (note that `query` is expected to
|
||||
// be already properly encoded).
|
||||
if (self.query != "")
|
||||
{
|
||||
builder.append_char('?');
|
||||
builder.append_chars(self.query);
|
||||
}
|
||||
|
||||
return builder.copy_str(allocator);
|
||||
};
|
||||
// Add fragment if it exists
|
||||
if (self.fragment != "")
|
||||
{
|
||||
builder.append_char('#');
|
||||
|
||||
String fragment = temp_encode(self.fragment, FRAGMENT);
|
||||
builder.append_chars(fragment);
|
||||
}
|
||||
|
||||
return builder.copy_str(allocator);
|
||||
}
|
||||
|
||||
def UrlQueryValueList = List(<String>);
|
||||
@@ -278,7 +275,7 @@ fn UrlQueryValues parse_query(String query, Allocator allocator)
|
||||
{
|
||||
UrlQueryValues vals;
|
||||
vals.map.init(allocator);
|
||||
vals.key_order.new_init(allocator: allocator);
|
||||
vals.key_order.init(allocator);
|
||||
|
||||
Splitter raw_vals = query.tokenize("&");
|
||||
while (try String rv = raw_vals.next())
|
||||
@@ -312,7 +309,7 @@ fn UrlQueryValues* UrlQueryValues.add(&self, String key, String value)
|
||||
else
|
||||
{
|
||||
UrlQueryValueList new_list;
|
||||
new_list.new_init_with_array({ value_copy }, self.allocator);
|
||||
new_list.init_with_array(self.allocator, { value_copy });
|
||||
(*self)[key] = new_list;
|
||||
self.key_order.push(key.copy(self.allocator));
|
||||
}
|
||||
@@ -327,35 +324,32 @@ fn UrlQueryValues* UrlQueryValues.add(&self, String key, String value)
|
||||
@param [inout] allocator
|
||||
@return "a percent-encoded query string"
|
||||
*>
|
||||
fn String UrlQueryValues.to_string(&self, Allocator allocator = allocator::heap()) @dynamic
|
||||
fn String UrlQueryValues.to_string(&self, Allocator allocator = allocator::heap()) @dynamic => @pool(allocator)
|
||||
{
|
||||
@pool(allocator)
|
||||
DString builder = dstring::temp_new();
|
||||
|
||||
usz i;
|
||||
foreach (key: self.key_order)
|
||||
{
|
||||
DString builder = dstring::temp_new();
|
||||
String encoded_key = temp_encode(key, QUERY);
|
||||
|
||||
usz i;
|
||||
foreach (key: self.key_order)
|
||||
UrlQueryValueList! values = self.map.get(key);
|
||||
if (catch values) continue;
|
||||
|
||||
foreach (value: values)
|
||||
{
|
||||
String encoded_key = temp_encode(key, QUERY);
|
||||
if (i > 0) builder.append_char('&');
|
||||
|
||||
UrlQueryValueList! values = self.map.get(key);
|
||||
if (catch values) continue;
|
||||
builder.append_chars(encoded_key);
|
||||
builder.append_char('=');
|
||||
|
||||
foreach (value: values)
|
||||
{
|
||||
if (i > 0) builder.append_char('&');
|
||||
|
||||
builder.append_chars(encoded_key);
|
||||
builder.append_char('=');
|
||||
|
||||
String encoded_value = temp_encode(value, QUERY);
|
||||
builder.append_chars(encoded_value);
|
||||
i++;
|
||||
}
|
||||
};
|
||||
|
||||
return builder.copy_str(allocator);
|
||||
String encoded_value = temp_encode(value, QUERY);
|
||||
builder.append_chars(encoded_value);
|
||||
i++;
|
||||
}
|
||||
};
|
||||
|
||||
return builder.copy_str(allocator);
|
||||
}
|
||||
|
||||
fn void UrlQueryValues.free(&self)
|
||||
|
||||
@@ -67,35 +67,32 @@ fn usz encode_len(String s, UrlEncodingMode mode) @inline
|
||||
@param [inout] allocator
|
||||
@return "Percent-encoded String"
|
||||
*>
|
||||
fn String encode(String s, UrlEncodingMode mode, Allocator allocator)
|
||||
fn String encode(String s, UrlEncodingMode mode, Allocator allocator) => @pool(allocator)
|
||||
{
|
||||
usz n = encode_len(s, mode);
|
||||
@pool(allocator)
|
||||
DString builder = dstring::temp_with_capacity(n);
|
||||
|
||||
foreach(i, c: s)
|
||||
{
|
||||
DString builder = dstring::temp_with_capacity(n);
|
||||
|
||||
foreach(i, c: s)
|
||||
switch
|
||||
{
|
||||
switch
|
||||
{
|
||||
// encode spaces in queries
|
||||
case c == ' ' && mode == QUERY:
|
||||
builder.append_char('+');
|
||||
// encode spaces in queries
|
||||
case c == ' ' && mode == QUERY:
|
||||
builder.append_char('+');
|
||||
|
||||
// add encoded char
|
||||
case should_encode(c, mode):
|
||||
builder.append_char('%');
|
||||
String hex = hex::encode_temp(s[i:1]);
|
||||
builder.append(hex.temp_ascii_to_upper());
|
||||
// add encoded char
|
||||
case should_encode(c, mode):
|
||||
builder.append_char('%');
|
||||
String hex = hex::encode_temp(s[i:1]);
|
||||
builder.append(hex.temp_ascii_to_upper());
|
||||
|
||||
// use char, no encoding needed
|
||||
default:
|
||||
builder.append_char(c);
|
||||
}
|
||||
// use char, no encoding needed
|
||||
default:
|
||||
builder.append_char(c);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.copy_str(allocator);
|
||||
};
|
||||
return builder.copy_str(allocator);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -146,35 +143,32 @@ fn usz! decode_len(String s, UrlEncodingMode mode) @inline
|
||||
@param [inout] allocator
|
||||
@return "Percent-decoded String"
|
||||
*>
|
||||
fn String! decode(String s, UrlEncodingMode mode, Allocator allocator)
|
||||
fn String! decode(String s, UrlEncodingMode mode, Allocator allocator) => @pool(allocator)
|
||||
{
|
||||
usz n = decode_len(s, mode)!;
|
||||
@pool(allocator)
|
||||
DString builder = dstring::temp_with_capacity(n);
|
||||
|
||||
for (usz i = 0; i < s.len; i++)
|
||||
{
|
||||
DString builder = dstring::temp_with_capacity(n);
|
||||
|
||||
for (usz i = 0; i < s.len; i++)
|
||||
switch (s[i])
|
||||
{
|
||||
switch (s[i])
|
||||
{
|
||||
// decode encoded char
|
||||
case '%':
|
||||
char[] hex = hex::decode_temp(s[i+1:2])!;
|
||||
builder.append(hex);
|
||||
i += 2;
|
||||
// decode encoded char
|
||||
case '%':
|
||||
char[] hex = hex::decode_temp(s[i+1:2])!;
|
||||
builder.append(hex);
|
||||
i += 2;
|
||||
|
||||
// decode space when in queries
|
||||
case '+':
|
||||
builder.append_char((mode == QUERY) ? ' ' : '+');
|
||||
// decode space when in queries
|
||||
case '+':
|
||||
builder.append_char((mode == QUERY) ? ' ' : '+');
|
||||
|
||||
// use char, no decoding needed
|
||||
default:
|
||||
builder.append_char(s[i]);
|
||||
}
|
||||
// use char, no decoding needed
|
||||
default:
|
||||
builder.append_char(s[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.copy_str(allocator);
|
||||
};
|
||||
return builder.copy_str(allocator);
|
||||
}
|
||||
|
||||
<*
|
||||
|
||||
@@ -9,10 +9,9 @@ import std::io::path, libc, std::os;
|
||||
@require name.len > 0
|
||||
@return! SearchResult.MISSING
|
||||
*>
|
||||
fn String! get_var(String name, Allocator allocator = allocator::heap())
|
||||
fn String! get_var(String name, Allocator allocator = allocator::heap()) => @pool(allocator)
|
||||
{
|
||||
@pool(allocator)
|
||||
{
|
||||
|
||||
$switch
|
||||
$case env::LIBC && !env::WIN32:
|
||||
ZString val = libc::getenv(name.zstr_tcopy());
|
||||
@@ -33,7 +32,6 @@ fn String! get_var(String name, Allocator allocator = allocator::heap())
|
||||
$default:
|
||||
return "";
|
||||
$endswitch
|
||||
};
|
||||
}
|
||||
|
||||
fn String! get_var_temp(String name)
|
||||
@@ -46,10 +44,8 @@ fn String! get_var_temp(String name)
|
||||
@param [in] value
|
||||
@require name.len > 0
|
||||
*>
|
||||
fn bool set_var(String name, String value, bool overwrite = true)
|
||||
fn bool set_var(String name, String value, bool overwrite = true) => @pool()
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
$switch
|
||||
$case env::WIN32:
|
||||
WString wname = name.to_temp_wstring()!!;
|
||||
@@ -61,12 +57,10 @@ fn bool set_var(String name, String value, bool overwrite = true)
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setenvironmentvariable
|
||||
return (win32::setEnvironmentVariableW(wname, value.to_temp_wstring()) ?? 1) == 0;
|
||||
$case env::LIBC && !env::WIN32:
|
||||
return libc::setenv(name.zstr_tcopy(), value.zstr_copy(), (int)overwrite) == 0;
|
||||
return libc::setenv(name.zstr_tcopy(), value.zstr_tcopy(), (int)overwrite) == 0;
|
||||
$default:
|
||||
return false;
|
||||
$endswitch
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -91,10 +85,8 @@ fn Path! get_config_dir(Allocator allocator = allocator::heap()) @deprecated("us
|
||||
<*
|
||||
Returns the current user's config directory.
|
||||
*>
|
||||
fn Path! new_get_config_dir(Allocator allocator = allocator::heap())
|
||||
fn Path! new_get_config_dir(Allocator allocator = allocator::heap()) => @pool(allocator)
|
||||
{
|
||||
@pool(allocator)
|
||||
{
|
||||
$if env::WIN32:
|
||||
return path::new(get_var_temp("AppData"), allocator);
|
||||
$else
|
||||
@@ -107,7 +99,6 @@ fn Path! new_get_config_dir(Allocator allocator = allocator::heap())
|
||||
$endif
|
||||
return path::temp_new(s).new_append(DIR, allocator: allocator);
|
||||
$endif
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -115,10 +106,8 @@ fn Path! new_get_config_dir(Allocator allocator = allocator::heap())
|
||||
@param [in] name
|
||||
@require name.len > 0
|
||||
*>
|
||||
fn bool clear_var(String name)
|
||||
fn bool clear_var(String name) => @pool()
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
$switch
|
||||
$case env::WIN32:
|
||||
WString wname = name.to_temp_wstring()!!;
|
||||
@@ -128,7 +117,6 @@ fn bool clear_var(String name)
|
||||
$default:
|
||||
return false;
|
||||
$endswitch
|
||||
};
|
||||
}
|
||||
|
||||
fn String! executable_path(Allocator allocator = allocator::heap()) @deprecated("use new_executable_path()")
|
||||
|
||||
@@ -95,7 +95,7 @@ fn ulong! elf_module_image_base(String path) @local
|
||||
defer (void)file.close();
|
||||
char[4] buffer;
|
||||
io::read_all(&file, &buffer)!;
|
||||
if (buffer != char[4]{ 0x7f, 'E', 'L', 'F'}) return BacktraceFault.IMAGE_NOT_FOUND?;
|
||||
if (buffer != { 0x7f, 'E', 'L', 'F'}) return BacktraceFault.IMAGE_NOT_FOUND?;
|
||||
bool is_64 = file.read_byte()! == 2;
|
||||
bool is_little_endian = file.read_byte()! == 1;
|
||||
// Actually, not supported.
|
||||
@@ -204,7 +204,8 @@ fn void! backtrace_add_element(BacktraceList *list, void* addr, Allocator alloca
|
||||
return;
|
||||
}
|
||||
|
||||
@pool(allocator) {
|
||||
@pool(allocator)
|
||||
{
|
||||
Linux_Dl_info info;
|
||||
if (dladdr(addr, &info) == 0)
|
||||
{
|
||||
@@ -217,7 +218,7 @@ fn void! backtrace_add_element(BacktraceList *list, void* addr, Allocator alloca
|
||||
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator)
|
||||
{
|
||||
BacktraceList list;
|
||||
list.new_init(backtrace.len, allocator);
|
||||
list.init(allocator, backtrace.len);
|
||||
defer catch
|
||||
{
|
||||
foreach (trace : list)
|
||||
|
||||
@@ -136,7 +136,7 @@ fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator)
|
||||
{
|
||||
void *load_addr = (void *)load_address()!;
|
||||
BacktraceList list;
|
||||
list.new_init(backtrace.len, allocator);
|
||||
list.init(allocator, backtrace.len);
|
||||
defer catch
|
||||
{
|
||||
foreach (trace : list)
|
||||
|
||||
@@ -22,7 +22,7 @@ struct Posix_dirent
|
||||
char[255+1] name @if(env::FREEBSD || env::OPENBSD);
|
||||
char[511+1] name @if(env::NETBSD);
|
||||
char[1024] name @if(env::DARWIN);
|
||||
char[*] name @if(!env::DARWIN && !env::BSD_FAMILY);
|
||||
char[?] name @if(!env::DARWIN && !env::BSD_FAMILY);
|
||||
}
|
||||
|
||||
extern fn int rmdir(ZString);
|
||||
|
||||
@@ -75,10 +75,12 @@ fn void! create_named_pipe_helper(void** rd, void **wr) @local @if(env::WIN32)
|
||||
fn WString convert_command_line_win32(String[] command_line) @inline @if(env::WIN32) @local
|
||||
{
|
||||
DString str = dstring::temp_with_capacity(512);
|
||||
foreach (i, s : command_line)
|
||||
foreach LINE: (i, s : command_line)
|
||||
{
|
||||
if (i != 0) str.append(' ');
|
||||
bool needs_escape = {|
|
||||
|
||||
do CHECK_WS:
|
||||
{
|
||||
foreach (c : s)
|
||||
{
|
||||
switch (c)
|
||||
@@ -86,16 +88,12 @@ fn WString convert_command_line_win32(String[] command_line) @inline @if(env::WI
|
||||
case '\t':
|
||||
case ' ':
|
||||
case '\v':
|
||||
return true;
|
||||
break CHECK_WS;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|};
|
||||
if (!needs_escape)
|
||||
{
|
||||
str.append(s);
|
||||
continue;
|
||||
}
|
||||
continue LINE;
|
||||
};
|
||||
str.append('"');
|
||||
foreach (j, c : s)
|
||||
{
|
||||
@@ -178,12 +176,13 @@ fn SubProcess! create(String[] command_line, SubProcessOptions options = {}, Str
|
||||
|
||||
start_info.hStdOutput = wr;
|
||||
|
||||
{|
|
||||
do
|
||||
{
|
||||
if (options.combined_stdout_stderr)
|
||||
{
|
||||
stderr = stdout;
|
||||
start_info.hStdError = start_info.hStdOutput;
|
||||
return;
|
||||
break;
|
||||
}
|
||||
if (options.read_async)
|
||||
{
|
||||
@@ -202,8 +201,7 @@ fn SubProcess! create(String[] command_line, SubProcessOptions options = {}, Str
|
||||
if (!stderr) return SubProcessResult.FAILED_TO_OPEN_STDERR?;
|
||||
}
|
||||
start_info.hStdError = wr;
|
||||
|
||||
|}!;
|
||||
};
|
||||
void *event_output;
|
||||
void *event_error;
|
||||
if (options.read_async)
|
||||
@@ -323,11 +321,17 @@ fn SubProcess! create(String[] command_line, SubProcessOptions options = {}, Str
|
||||
CFile stdin = libc::fdopen(stdinfd[1], "wb");
|
||||
libc::close(stdoutfd[1]);
|
||||
CFile stdout = libc::fdopen(stdoutfd[0], "rb");
|
||||
CFile stderr = {|
|
||||
if (options.combined_stdout_stderr) return stdout;
|
||||
CFile stderr @noinit;
|
||||
do
|
||||
{
|
||||
if (options.combined_stdout_stderr)
|
||||
{
|
||||
stderr = stdout;
|
||||
break;
|
||||
}
|
||||
libc::close(stderrfd[1]);
|
||||
return libc::fdopen(stderrfd[0], "rb");
|
||||
|};
|
||||
stderr = libc::fdopen(stderrfd[0], "rb");
|
||||
};
|
||||
return {
|
||||
.stdin_file = stdin,
|
||||
.stdout_file = stdout,
|
||||
@@ -358,6 +362,11 @@ fn File SubProcess.stdout(&self)
|
||||
return file::from_handle(self.stdout_file);
|
||||
}
|
||||
|
||||
fn File SubProcess.stderr(&self)
|
||||
{
|
||||
return file::from_handle(self.stderr_file);
|
||||
}
|
||||
|
||||
fn CInt! SubProcess.join(&self) @if(env::WIN32)
|
||||
{
|
||||
if (self.stdin_file)
|
||||
|
||||
@@ -157,7 +157,7 @@ Win32_DWORD64 displacement;
|
||||
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator)
|
||||
{
|
||||
BacktraceList list;
|
||||
list.new_init(backtrace.len, allocator);
|
||||
list.init(allocator, backtrace.len);
|
||||
Win32_HANDLE process = getCurrentProcess();
|
||||
symInitialize(process, null, 1);
|
||||
defer symCleanup(process);
|
||||
|
||||
@@ -81,6 +81,7 @@ def Win32_LPVOID = void*;
|
||||
def Win32_LPWORD = Win32_WORD*;
|
||||
def Win32_LPWSTR = Win32_WCHAR*;
|
||||
def Win32_LRESULT = Win32_LONG_PTR;
|
||||
def Win32_NTSTATUS = Win32_LONG;
|
||||
def Win32_PBOOL = Win32_BOOL*;
|
||||
def Win32_PBOOLEAN = Win32_BOOLEAN*;
|
||||
def Win32_PBYTE = Win32_BYTE*;
|
||||
|
||||
61
lib/std/os/win32/wincon.c3
Normal file
61
lib/std/os/win32/wincon.c3
Normal file
@@ -0,0 +1,61 @@
|
||||
// console
|
||||
|
||||
module std::os::win32 @if(env::WIN32);
|
||||
|
||||
struct Win32_KEY_EVENT_RECORD
|
||||
{
|
||||
Win32_BOOL bKeyDown;
|
||||
Win32_WORD wRepeatCount;
|
||||
Win32_WORD wVirtualKeyCode;
|
||||
Win32_WORD wVirtualScanCode;
|
||||
union uChar
|
||||
{
|
||||
Win32_WCHAR unicodeChar;
|
||||
Win32_CHAR asciiChar;
|
||||
}
|
||||
Win32_DWORD dwControlKeyState;
|
||||
}
|
||||
|
||||
struct Win32_COORD
|
||||
{
|
||||
Win32_SHORT x;
|
||||
Win32_SHORT y;
|
||||
}
|
||||
|
||||
struct Win32_MOUSE_EVENT_RECORD
|
||||
{
|
||||
Win32_COORD dwMousePosition;
|
||||
Win32_DWORD dwButtonState;
|
||||
Win32_DWORD dwControlKeyState;
|
||||
Win32_DWORD dwEventFlags;
|
||||
}
|
||||
|
||||
struct Win32_WINDOW_BUFFER_SIZE_RECORD
|
||||
{
|
||||
Win32_COORD dwSize;
|
||||
}
|
||||
|
||||
struct Win32_MENU_EVENT_RECORD
|
||||
{
|
||||
Win32_UINT dwCommandId;
|
||||
}
|
||||
|
||||
struct Win32_FOCUS_EVENT_RECORD
|
||||
{
|
||||
Win32_BOOL bSetFocus;
|
||||
}
|
||||
|
||||
struct Win32_INPUT_RECORD
|
||||
{
|
||||
Win32_WORD eventType;
|
||||
union event
|
||||
{
|
||||
Win32_KEY_EVENT_RECORD keyEvent;
|
||||
Win32_MOUSE_EVENT_RECORD mouseEvent;
|
||||
Win32_WINDOW_BUFFER_SIZE_RECORD windowBufferSizeEvent;
|
||||
Win32_MENU_EVENT_RECORD menuEvent;
|
||||
Win32_FOCUS_EVENT_RECORD focusEvent;
|
||||
}
|
||||
}
|
||||
|
||||
def Win32_PCOORD = Win32_COORD*;
|
||||
@@ -20,6 +20,18 @@ struct Win32_SIZE
|
||||
Win32_LONG cy;
|
||||
}
|
||||
|
||||
struct Win32_WSABUF
|
||||
{
|
||||
Win32_ULONG len;
|
||||
Win32_CHAR* buf;
|
||||
}
|
||||
|
||||
struct Win32_SOCKADDR
|
||||
{
|
||||
Win32_USHORT sa_family;
|
||||
Win32_CHAR[14]* sa_data;
|
||||
}
|
||||
|
||||
def Win32_PSIZE = Win32_SIZE*;
|
||||
def Win32_NPSIZE = Win32_SIZE*;
|
||||
def Win32_LPSIZE = Win32_SIZE*;
|
||||
@@ -31,3 +43,9 @@ def Win32_LPOINT = Win32_POINT*;
|
||||
def Win32_PRECT = Win32_RECT*;
|
||||
def Win32_NPRECT = Win32_RECT*;
|
||||
def Win32_LPRECT = Win32_RECT*;
|
||||
|
||||
def Win32_PWSABUF = Win32_WSABUF*;
|
||||
def Win32_LPWSABUF = Win32_WSABUF*;
|
||||
|
||||
def Win32_PSOCKADDR = Win32_SOCKADDR*;
|
||||
def Win32_LPSOCKADDR = Win32_SOCKADDR*;
|
||||
31
lib/std/os/win32/ws2def.c3
Normal file
31
lib/std/os/win32/ws2def.c3
Normal file
@@ -0,0 +1,31 @@
|
||||
module std::os::win32 @if(env::WIN32);
|
||||
|
||||
struct Win32_addrinfo
|
||||
{
|
||||
Win32_INT ai_flags;
|
||||
Win32_INT ai_family;
|
||||
Win32_INT ai_socktype;
|
||||
Win32_INT ai_protocol;
|
||||
Win32_SIZE_T ai_addrlen;
|
||||
Win32_CHAR* ai_canonname;
|
||||
Win32_SOCKADDR* ai_addr;
|
||||
Win32_ADDRINFO* ai_next;
|
||||
}
|
||||
|
||||
def Win32_ADDRINFO = Win32_addrinfo;
|
||||
def Win32_ADDRINFOA = Win32_ADDRINFO;
|
||||
def Win32_PADDRINFOA = Win32_ADDRINFO*;
|
||||
|
||||
struct Win32_addrinfoW {
|
||||
Win32_INT ai_flags;
|
||||
Win32_INT ai_family;
|
||||
Win32_INT ai_socktype;
|
||||
Win32_INT ai_protocol;
|
||||
Win32_SIZE_T ai_addrlen;
|
||||
Win32_PWSTR ai_canonname;
|
||||
Win32_SOCKADDR *ai_addr;
|
||||
Win32_addrinfo *ai_next;
|
||||
}
|
||||
|
||||
def Win32_ADDRINFOW = Win32_addrinfoW;
|
||||
def Win32_PADDRINFOW = Win32_addrinfoW*;
|
||||
@@ -3,6 +3,7 @@ module std::os::win32 @if(env::WIN32);
|
||||
// See https://github.com/wine-mirror/wine/blob/master/include/winsock2.h
|
||||
|
||||
distinct WSAError = int;
|
||||
|
||||
struct Win32_pollfd
|
||||
{
|
||||
Win32_SOCKET fd;
|
||||
@@ -13,6 +14,99 @@ def Win32_WSAPOLLFD = Win32_pollfd;
|
||||
def Win32_PWSAPOLLFD = Win32_WSAPOLLFD*;
|
||||
def Win32_LPWSAPOLLFD = Win32_WSAPOLLFD*;
|
||||
|
||||
struct Win32_InAddr
|
||||
{
|
||||
union
|
||||
{
|
||||
struct s_un_b
|
||||
{
|
||||
Win32_UCHAR s_b1;
|
||||
Win32_UCHAR s_b2;
|
||||
Win32_UCHAR s_b3;
|
||||
Win32_UCHAR s_b4;
|
||||
}
|
||||
struct s_un_w
|
||||
{
|
||||
Win32_USHORT s_w1;
|
||||
Win32_USHORT s_w2;
|
||||
}
|
||||
Win32_ULONG s_addr;
|
||||
}
|
||||
}
|
||||
|
||||
struct Win32_SOCKADDR_IN
|
||||
{
|
||||
Win32_SHORT sin_family;
|
||||
Win32_USHORT sin_port;
|
||||
Win32_InAddr sin_addr;
|
||||
Win32_CHAR[8]* sin_zero;
|
||||
}
|
||||
|
||||
const usz _SS_PAD1SIZE = 6;
|
||||
const usz _SS_PAD2SIZE = 112;
|
||||
|
||||
struct Win32_SOCKADDR_STORAGE
|
||||
{
|
||||
Win32_USHORT ss_family;
|
||||
Win32_CHAR[_SS_PAD1SIZE]* __ss_pad1;
|
||||
Win32_INT64 __ss_align;
|
||||
Win32_CHAR[_SS_PAD2SIZE]* __ss_pad2;
|
||||
}
|
||||
|
||||
def Win32_WSAOVERLAPPED = Win32_OVERLAPPED;
|
||||
def Win32_LPWSAOVERLAPPED = Win32_WSAOVERLAPPED*;
|
||||
|
||||
def Win32_LPWSAOVERLAPPED_COMPLETION_ROUTINE = fn void (
|
||||
Win32_DWORD dwError,
|
||||
Win32_DWORD cbTransferred,
|
||||
Win32_LPWSAOVERLAPPED
|
||||
lpOverlapped,
|
||||
Win32_DWORD dwFlags
|
||||
);
|
||||
|
||||
def Win32_LPFN_WSARECV = fn CInt(
|
||||
Win32_SOCKET socket,
|
||||
Win32_LPWSABUF buffers,
|
||||
Win32_DWORD buffer_count,
|
||||
Win32_LPDWORD bytes,
|
||||
Win32_LPDWORD flags,
|
||||
Win32_LPWSAOVERLAPPED overlapped,
|
||||
Win32_LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine
|
||||
);
|
||||
|
||||
def Win32_LPFN_WSARECVFROM = fn CInt(
|
||||
Win32_SOCKET socket,
|
||||
Win32_LPWSABUF buffers,
|
||||
Win32_DWORD buffer_count,
|
||||
Win32_LPDWORD bytes,
|
||||
Win32_LPDWORD flags,
|
||||
Win32_SOCKADDR* addr,
|
||||
Win32_LPINT addr_len,
|
||||
Win32_LPWSAOVERLAPPED overlapped,
|
||||
Win32_LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine
|
||||
);
|
||||
|
||||
def Win32_LPFn_CONNECTEX = fn bool(
|
||||
Win32_SOCKET,
|
||||
Win32_SOCKADDR*,
|
||||
Win32_INT,
|
||||
Win32_PVOID,
|
||||
Win32_DWORD,
|
||||
Win32_LPDWORD,
|
||||
void*
|
||||
);
|
||||
|
||||
def Win32_LPFn_ACCEPTEX = fn bool(
|
||||
Win32_SOCKET,
|
||||
Win32_SOCKET,
|
||||
Win32_PVOID,
|
||||
Win32_DWORD,
|
||||
Win32_DWORD,
|
||||
Win32_DWORD,
|
||||
Win32_LPDWORD,
|
||||
void*
|
||||
);
|
||||
|
||||
const Win32_SHORT POLLERR = 0x0001;
|
||||
const Win32_SHORT POLLHUP = 0x0002;
|
||||
const Win32_SHORT POLLNVAL = 0x0004;
|
||||
|
||||
@@ -29,11 +29,11 @@ module std::sort::cs(<Type, KeyFn>);
|
||||
def Counts = usz[256] @private;
|
||||
def Ranges = usz[257] @private;
|
||||
def Indexs = char[256] @private;
|
||||
def ElementType = $typeof(Type{}[0]);
|
||||
def ElementType = $typeof((Type){}[0]);
|
||||
|
||||
const bool NO_KEY_FN @private = types::is_same(KeyFn, EmptySlot);
|
||||
const bool KEY_BY_VALUE @private = NO_KEY_FN ||| $assignable(Type{}[0], $typefrom(KeyFn.paramsof[0].type));
|
||||
const bool LIST_HAS_REF @private = $defined(&Type{}[0]);
|
||||
const bool KEY_BY_VALUE @private = NO_KEY_FN ||| $assignable((Type){}[0], $typefrom(KeyFn.paramsof[0].type));
|
||||
const bool LIST_HAS_REF @private = $defined(&(Type){}[0]);
|
||||
|
||||
def KeyFnReturnType = $typefrom(KeyFn.returns) @if(!NO_KEY_FN);
|
||||
def KeyFnReturnType = ElementType @if(NO_KEY_FN);
|
||||
|
||||
@@ -19,7 +19,7 @@ macro insertionsort(list, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @b
|
||||
|
||||
module std::sort::is(<Type, CmpFn, Context>);
|
||||
|
||||
def ElementType = $typeof(Type{}[0]);
|
||||
def ElementType = $typeof(((Type){})[0]);
|
||||
|
||||
fn void isort(Type list, usz low, usz high, CmpFn comp, Context context)
|
||||
{
|
||||
|
||||
@@ -35,7 +35,7 @@ macro quickselect(list, isz k, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLO
|
||||
|
||||
module std::sort::qs(<Type, CmpFn, Context>);
|
||||
|
||||
def ElementType = $typeof(Type{}[0]);
|
||||
def ElementType = $typeof(((Type){})[0]);
|
||||
|
||||
struct StackElementItem @private
|
||||
{
|
||||
|
||||
165
lib/std/threads/buffered_channel.c3
Normal file
165
lib/std/threads/buffered_channel.c3
Normal file
@@ -0,0 +1,165 @@
|
||||
module std::thread::channel(<Type>);
|
||||
|
||||
distinct BufferedChannel = void*;
|
||||
|
||||
struct BufferedChannelImpl @private
|
||||
{
|
||||
Allocator allocator;
|
||||
Mutex mu;
|
||||
bool closed;
|
||||
usz size;
|
||||
usz elems;
|
||||
|
||||
usz sendx;
|
||||
usz send_waiting;
|
||||
ConditionVariable send_cond;
|
||||
|
||||
usz readx;
|
||||
usz read_waiting;
|
||||
ConditionVariable read_cond;
|
||||
|
||||
Type[?] buf;
|
||||
}
|
||||
|
||||
fn void! BufferedChannel.new_init(&self, usz size = 1) @deprecated("Use init(mem)")
|
||||
{
|
||||
return self.init(mem, size);
|
||||
}
|
||||
|
||||
fn void! BufferedChannel.init(&self, Allocator allocator, usz size = 1)
|
||||
{
|
||||
BufferedChannelImpl* channel = allocator::new_with_padding(allocator, BufferedChannelImpl, Type.sizeof * size)!;
|
||||
defer catch allocator::free(allocator, channel);
|
||||
|
||||
channel.allocator = allocator;
|
||||
channel.size = size;
|
||||
channel.elems = 0;
|
||||
channel.sendx = 0;
|
||||
channel.send_waiting = 0;
|
||||
channel.readx = 0;
|
||||
channel.read_waiting = 0;
|
||||
|
||||
channel.mu.init()!;
|
||||
defer catch (void)channel.mu.destroy();
|
||||
channel.send_cond.init()!;
|
||||
defer catch (void)channel.send_cond.destroy();
|
||||
channel.read_cond.init()!;
|
||||
defer catch (void)channel.read_cond.destroy();
|
||||
|
||||
*self = (BufferedChannel)channel;
|
||||
}
|
||||
|
||||
fn void! BufferedChannel.destroy(&self)
|
||||
{
|
||||
BufferedChannelImpl* channel = (BufferedChannelImpl*)(*self);
|
||||
|
||||
anyfault err = @catch(channel.mu.destroy());
|
||||
err = @catch(channel.send_cond.destroy()) ?: err;
|
||||
err = @catch(channel.read_cond.destroy()) ?: err;
|
||||
allocator::free(channel.allocator, channel);
|
||||
|
||||
*self = null;
|
||||
|
||||
if (err) return err?;
|
||||
}
|
||||
|
||||
fn void! BufferedChannel.push(self, Type val)
|
||||
{
|
||||
BufferedChannelImpl* channel = (BufferedChannelImpl*)self;
|
||||
|
||||
channel.mu.lock()!;
|
||||
defer catch (void)channel.mu.unlock();
|
||||
|
||||
// if channel is full -> wait
|
||||
while (channel.elems == channel.size && !channel.closed)
|
||||
{
|
||||
channel.send_waiting++;
|
||||
channel.send_cond.wait(&channel.mu)!;
|
||||
channel.send_waiting--;
|
||||
}
|
||||
|
||||
// check if channel is closed
|
||||
if (channel.closed) return ThreadFault.CHANNEL_CLOSED?;
|
||||
|
||||
// save value to buf
|
||||
channel.buf[channel.sendx] = val;
|
||||
|
||||
// move pointer
|
||||
channel.sendx++;
|
||||
if (channel.sendx == channel.size)
|
||||
{
|
||||
channel.sendx = 0;
|
||||
}
|
||||
|
||||
// channelge elems counter
|
||||
channel.elems++;
|
||||
|
||||
// if someone is waiting -> awake him
|
||||
if (channel.read_waiting > 0)
|
||||
{
|
||||
channel.read_cond.signal()!;
|
||||
}
|
||||
|
||||
channel.mu.unlock()!;
|
||||
}
|
||||
|
||||
fn Type! BufferedChannel.pop(self)
|
||||
{
|
||||
BufferedChannelImpl* channel = (BufferedChannelImpl*)self;
|
||||
|
||||
channel.mu.lock()!;
|
||||
defer catch (void)channel.mu.unlock();
|
||||
|
||||
// if chan is empty -> wait for sender
|
||||
while (channel.elems == 0 && !channel.closed)
|
||||
{
|
||||
channel.read_waiting++;
|
||||
channel.read_cond.wait(&channel.mu)!;
|
||||
channel.read_waiting--;
|
||||
}
|
||||
|
||||
// check if chan is closed and empty
|
||||
if (channel.closed && channel.elems == 0)
|
||||
{
|
||||
return ThreadFault.CHANNEL_CLOSED?;
|
||||
}
|
||||
|
||||
// read from buf
|
||||
Type ret = channel.buf[channel.readx];
|
||||
|
||||
// move pointer
|
||||
channel.readx++;
|
||||
if (channel.readx == channel.size)
|
||||
{
|
||||
channel.readx = 0;
|
||||
}
|
||||
|
||||
// change elems counter
|
||||
channel.elems--;
|
||||
|
||||
// if someone is waiting -> awake him
|
||||
if (channel.send_waiting > 0)
|
||||
{
|
||||
channel.send_cond.signal()!;
|
||||
}
|
||||
|
||||
channel.mu.unlock()!;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
fn void! BufferedChannel.close(self)
|
||||
{
|
||||
BufferedChannelImpl* channel = (BufferedChannelImpl*)self;
|
||||
|
||||
anyfault err = @catch(channel.mu.lock());
|
||||
|
||||
channel.closed = true;
|
||||
|
||||
err = @catch(channel.read_cond.broadcast()) ?: err;
|
||||
err = @catch(channel.send_cond.broadcast()) ?: err;
|
||||
err = @catch(channel.mu.unlock()) ?: err;
|
||||
|
||||
if (err) return err?;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,14 @@ struct NativeMutex
|
||||
}
|
||||
|
||||
def NativeConditionVariable = Pthread_cond_t;
|
||||
def NativeThread = Pthread_t;
|
||||
|
||||
struct NativeThread
|
||||
{
|
||||
inline Pthread_t pthread;
|
||||
ThreadFn thread_fn;
|
||||
void* arg;
|
||||
}
|
||||
|
||||
def NativeOnceFlag = Pthread_once_t;
|
||||
|
||||
<*
|
||||
@@ -148,59 +155,49 @@ fn void! NativeConditionVariable.wait_timeout(&cond, NativeMutex* mtx, ulong ms)
|
||||
}
|
||||
}
|
||||
|
||||
tlocal PosixThreadData *_thread_data @private;
|
||||
tlocal NativeThread current_thread @private;
|
||||
|
||||
fn void free_thread_data() @private
|
||||
{
|
||||
if (_thread_data)
|
||||
{
|
||||
allocator::free(_thread_data.allocator, _thread_data);
|
||||
_thread_data = null;
|
||||
}
|
||||
}
|
||||
fn void* callback(void* arg) @private
|
||||
{
|
||||
_thread_data = arg;
|
||||
defer free_thread_data();
|
||||
return (void*)(iptr)_thread_data.thread_fn(_thread_data.arg);
|
||||
NativeThread* thread = arg;
|
||||
current_thread = *thread;
|
||||
return (void*)(iptr)thread.thread_fn(thread.arg);
|
||||
}
|
||||
|
||||
fn void! NativeThread.create(&thread, ThreadFn thread_fn, void* arg)
|
||||
{
|
||||
PosixThreadData *thread_data = mem::new(PosixThreadData, { .thread_fn = thread_fn, .arg = arg, .allocator = allocator::heap() });
|
||||
if (posix::pthread_create(thread, null, &callback, thread_data) != 0)
|
||||
thread.thread_fn = thread_fn;
|
||||
thread.arg = arg;
|
||||
if (posix::pthread_create(&thread.pthread, null, &callback, thread) != 0)
|
||||
{
|
||||
*thread = null;
|
||||
free(thread_data);
|
||||
return ThreadFault.INIT_FAILED?;
|
||||
}
|
||||
}
|
||||
|
||||
fn void! NativeThread.detach(thread)
|
||||
{
|
||||
if (posix::pthread_detach(thread)) return ThreadFault.DETACH_FAILED?;
|
||||
if (posix::pthread_detach(thread.pthread)) return ThreadFault.DETACH_FAILED?;
|
||||
}
|
||||
|
||||
fn void native_thread_exit(int result)
|
||||
{
|
||||
free_thread_data();
|
||||
posix::pthread_exit((void*)(iptr)result);
|
||||
}
|
||||
|
||||
fn NativeThread native_thread_current()
|
||||
{
|
||||
return (NativeThread)posix::pthread_self();
|
||||
return current_thread;
|
||||
}
|
||||
|
||||
fn bool NativeThread.equals(thread, NativeThread other)
|
||||
{
|
||||
return (bool)posix::pthread_equal(thread, other);
|
||||
return (bool)posix::pthread_equal(thread.pthread, other.pthread);
|
||||
}
|
||||
|
||||
fn int! NativeThread.join(thread)
|
||||
{
|
||||
void *pres;
|
||||
if (posix::pthread_join(thread, &pres)) return ThreadFault.JOIN_FAILED?;
|
||||
if (posix::pthread_join(thread.pthread, &pres)) return ThreadFault.JOIN_FAILED?;
|
||||
return (int)(iptr)pres;
|
||||
}
|
||||
|
||||
@@ -214,13 +211,6 @@ fn void native_thread_yield()
|
||||
posix::sched_yield();
|
||||
}
|
||||
|
||||
struct PosixThreadData @private
|
||||
{
|
||||
ThreadFn thread_fn;
|
||||
void* arg;
|
||||
Allocator allocator;
|
||||
}
|
||||
|
||||
fn void! native_sleep_nano(NanoDuration nano)
|
||||
{
|
||||
if (nano <= 0) return;
|
||||
|
||||
@@ -13,7 +13,7 @@ distinct TimedMutex = inline Mutex;
|
||||
distinct RecursiveMutex = inline Mutex;
|
||||
distinct TimedRecursiveMutex = inline Mutex;
|
||||
distinct ConditionVariable = NativeConditionVariable;
|
||||
distinct Thread = NativeThread;
|
||||
distinct Thread = inline NativeThread;
|
||||
distinct OnceFlag = NativeOnceFlag;
|
||||
def OnceFn = fn void();
|
||||
|
||||
@@ -32,6 +32,7 @@ fault ThreadFault
|
||||
DETACH_FAILED,
|
||||
JOIN_FAILED,
|
||||
INTERRUPTED,
|
||||
CHANNEL_CLOSED,
|
||||
}
|
||||
|
||||
macro void! Mutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, MUTEX_PLAIN);
|
||||
@@ -44,6 +45,12 @@ macro void! TimedMutex.lock_timeout(&mutex, ulong ms) => NativeMutex.lock_timeou
|
||||
macro void! TimedRecursiveMutex.lock_timeout(&mutex, ulong ms) => NativeMutex.lock_timeout((NativeMutex*)mutex, ms);
|
||||
macro bool Mutex.try_lock(&mutex) => NativeMutex.try_lock((NativeMutex*)mutex);
|
||||
macro void! Mutex.unlock(&mutex) => NativeMutex.unlock((NativeMutex*)mutex);
|
||||
macro void Mutex.@in_lock(&mutex; @body)
|
||||
{
|
||||
(void)mutex.lock();
|
||||
defer (void)mutex.unlock();
|
||||
@body();
|
||||
}
|
||||
|
||||
macro void! ConditionVariable.init(&cond) => NativeConditionVariable.init((NativeConditionVariable*)cond);
|
||||
macro void! ConditionVariable.destroy(&cond) => NativeConditionVariable.destroy((NativeConditionVariable*)cond);
|
||||
@@ -58,11 +65,10 @@ macro void! ConditionVariable.wait_timeout(&cond, Mutex* mutex, ulong ms)
|
||||
return NativeConditionVariable.wait_timeout((NativeConditionVariable*)cond, (NativeMutex*)mutex, ms);
|
||||
}
|
||||
|
||||
|
||||
macro void! Thread.create(&thread, ThreadFn thread_fn, void* arg) => NativeThread.create((NativeThread*)thread, thread_fn, arg);
|
||||
macro void! Thread.detach(thread) => NativeThread.detach((NativeThread)thread);
|
||||
macro int! Thread.join(thread) => NativeThread.join((NativeThread)thread);
|
||||
macro bool Thread.equals(thread, Thread other) => NativeThread.equals((NativeThread)thread, (NativeThread)other);
|
||||
macro void! Thread.create(&thread, ThreadFn thread_fn, void* arg) => NativeThread.create(thread, thread_fn, arg);
|
||||
macro void! Thread.detach(thread) => NativeThread.detach(thread);
|
||||
macro int! Thread.join(thread) => NativeThread.join(thread);
|
||||
macro bool Thread.equals(thread, Thread other) => NativeThread.equals(thread, other);
|
||||
|
||||
macro void OnceFlag.call(&flag, OnceFn func) => NativeOnceFlag.call_once((NativeOnceFlag*)flag, func);
|
||||
|
||||
|
||||
140
lib/std/threads/unbuffered_channel.c3
Normal file
140
lib/std/threads/unbuffered_channel.c3
Normal file
@@ -0,0 +1,140 @@
|
||||
module std::thread::channel(<Type>);
|
||||
|
||||
distinct UnbufferedChannel = void*;
|
||||
|
||||
struct UnbufferedChannelImpl @private
|
||||
{
|
||||
Allocator allocator;
|
||||
Mutex mu;
|
||||
Type buf;
|
||||
bool closed;
|
||||
|
||||
Mutex send_mu;
|
||||
usz send_waiting;
|
||||
ConditionVariable send_cond;
|
||||
|
||||
Mutex read_mu;
|
||||
usz read_waiting;
|
||||
ConditionVariable read_cond;
|
||||
}
|
||||
|
||||
fn void! UnbufferedChannel.new_init(&self) @deprecated("Use init") => self.init(allocator::heap());
|
||||
|
||||
fn void! UnbufferedChannel.init(&self, Allocator allocator)
|
||||
{
|
||||
UnbufferedChannelImpl* channel = allocator::alloc(allocator, UnbufferedChannelImpl);
|
||||
defer catch (void)allocator::free(allocator, channel);
|
||||
|
||||
channel.allocator = allocator;
|
||||
channel.send_waiting = 0;
|
||||
channel.read_waiting = 0;
|
||||
|
||||
channel.mu.init()!;
|
||||
defer catch (void)channel.mu.destroy();
|
||||
channel.send_mu.init()!;
|
||||
defer catch (void)channel.send_mu.destroy();
|
||||
channel.send_cond.init()!;
|
||||
defer catch (void)channel.send_cond.destroy();
|
||||
channel.read_mu.init()!;
|
||||
defer catch (void)channel.read_mu.destroy();
|
||||
channel.read_cond.init()!;
|
||||
|
||||
*self = (UnbufferedChannel)channel;
|
||||
}
|
||||
|
||||
fn void! UnbufferedChannel.destroy(&self)
|
||||
{
|
||||
UnbufferedChannelImpl* channel = (UnbufferedChannelImpl*)(*self);
|
||||
|
||||
anyfault err = @catch(channel.mu.destroy());
|
||||
err = @catch(channel.send_mu.destroy()) ?: err;
|
||||
err = @catch(channel.send_cond.destroy()) ?: err;
|
||||
err = @catch(channel.read_mu.destroy()) ?: err;
|
||||
err = @catch(channel.read_cond.destroy()) ?: err;
|
||||
allocator::free(channel.allocator, channel);
|
||||
|
||||
if (err) return err?;
|
||||
|
||||
*self = null;
|
||||
}
|
||||
|
||||
fn void! UnbufferedChannel.push(self, Type val)
|
||||
{
|
||||
UnbufferedChannelImpl* channel = (UnbufferedChannelImpl*)self;
|
||||
|
||||
channel.mu.lock()!;
|
||||
defer catch (void)channel.mu.unlock();
|
||||
channel.send_mu.lock()!;
|
||||
defer catch (void)channel.send_mu.unlock();
|
||||
|
||||
if (channel.closed)
|
||||
{
|
||||
return ThreadFault.CHANNEL_CLOSED?;
|
||||
}
|
||||
|
||||
// store value in the buffer
|
||||
channel.buf = val;
|
||||
// show that we are waiting for reader
|
||||
channel.send_waiting++;
|
||||
|
||||
// if reader is already waiting for us -> awake him
|
||||
if (channel.read_waiting > 0)
|
||||
{
|
||||
channel.read_cond.signal()!;
|
||||
}
|
||||
|
||||
// wait until reader takes value from buffer
|
||||
channel.send_cond.wait(&channel.mu)!;
|
||||
|
||||
if (channel.closed) return ThreadFault.CHANNEL_CLOSED?;
|
||||
|
||||
channel.mu.unlock()!;
|
||||
channel.send_mu.unlock()!;
|
||||
}
|
||||
|
||||
fn Type! UnbufferedChannel.pop(self)
|
||||
{
|
||||
UnbufferedChannelImpl* channel = (UnbufferedChannelImpl*)self;
|
||||
|
||||
channel.mu.lock()!;
|
||||
defer catch (void)channel.mu.unlock();
|
||||
channel.read_mu.lock()!;
|
||||
defer catch (void)channel.read_mu.unlock();
|
||||
|
||||
// if no one is waiting, then there is nothing in the buffer
|
||||
while (!channel.closed && channel.send_waiting == 0)
|
||||
{
|
||||
channel.read_waiting++;
|
||||
channel.read_cond.wait(&channel.mu)!;
|
||||
channel.read_waiting--;
|
||||
}
|
||||
|
||||
if (channel.closed) return ThreadFault.CHANNEL_CLOSED?;
|
||||
|
||||
// take value from buffer
|
||||
Type ret = channel.buf;
|
||||
|
||||
// awake sender
|
||||
channel.send_waiting--;
|
||||
channel.send_cond.signal()!;
|
||||
|
||||
channel.mu.unlock()!;
|
||||
channel.read_mu.unlock()!;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
fn void! UnbufferedChannel.close(self)
|
||||
{
|
||||
UnbufferedChannelImpl* channel = (UnbufferedChannelImpl*)self;
|
||||
|
||||
anyfault err = @catch(channel.mu.lock());
|
||||
|
||||
channel.closed = true;
|
||||
|
||||
err = @catch(channel.read_cond.broadcast()) ?: err;
|
||||
err = @catch(channel.send_cond.broadcast()) ?: err;
|
||||
err = @catch(channel.mu.unlock()) ?: err;
|
||||
|
||||
if (err) return err?;
|
||||
}
|
||||
@@ -18,6 +18,16 @@ fn NanoDuration Clock.mark(&self)
|
||||
return diff;
|
||||
}
|
||||
|
||||
fn Clock Clock.add_nano_duration(self, NanoDuration nano)
|
||||
{
|
||||
return (Clock)((NanoDuration)self + nano);
|
||||
}
|
||||
|
||||
fn Clock Clock.add_duration(self, Duration duration)
|
||||
{
|
||||
return self.add_nano_duration(duration.to_nano());
|
||||
}
|
||||
|
||||
fn NanoDuration Clock.to_now(self)
|
||||
{
|
||||
return (NanoDuration)(now() - self);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module std::time::os @if(env::POSIX);
|
||||
import libc;
|
||||
|
||||
extern fn void clock_gettime(int type, TimeSpec *time);
|
||||
extern fn CInt clock_gettime(int type, TimeSpec *time);
|
||||
|
||||
fn Time native_timestamp()
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user