mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Compare commits
265 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15503a9054 | ||
|
|
660654f9e0 | ||
|
|
2f7d18bfb8 | ||
|
|
29a6a0db32 | ||
|
|
7b2fe92241 | ||
|
|
70da1f748a | ||
|
|
3033295884 | ||
|
|
8c12f92aff | ||
|
|
76da7936e5 | ||
|
|
2a924ae3b0 | ||
|
|
5ba9acad5d | ||
|
|
4cb984e56d | ||
|
|
70606a2bbe | ||
|
|
259112e178 | ||
|
|
a2cde1e072 | ||
|
|
de04c52379 | ||
|
|
27970085e5 | ||
|
|
e0afc0f9ea | ||
|
|
0e44e63fa8 | ||
|
|
2623d7d525 | ||
|
|
4e78e32ced | ||
|
|
f65ca07b62 | ||
|
|
7a805340c5 | ||
|
|
a863d7fe9e | ||
|
|
f60bfa8442 | ||
|
|
50fdf9900d | ||
|
|
8785c2c46f | ||
|
|
c8fa7b0cb3 | ||
|
|
f2e69f8fdc | ||
|
|
8a9edc02b6 | ||
|
|
d173ba0377 | ||
|
|
fbb4ae056a | ||
|
|
ae10ae6847 | ||
|
|
64ab67bb58 | ||
|
|
dd650bc334 | ||
|
|
48923a2237 | ||
|
|
1f29110271 | ||
|
|
e133f4406a | ||
|
|
2fa258a066 | ||
|
|
87d29a62e5 | ||
|
|
190dc246b3 | ||
|
|
6ae84aac78 | ||
|
|
c8c58f946c | ||
|
|
0d1fb2843e | ||
|
|
d67fcb3956 | ||
|
|
f1325f6539 | ||
|
|
3a1bba19af | ||
|
|
0857363470 | ||
|
|
713199d7be | ||
|
|
b941f93416 | ||
|
|
c22b7d45c1 | ||
|
|
c78bb45f2f | ||
|
|
11f9365eb0 | ||
|
|
cdc1656f3a | ||
|
|
8fb3ec73ff | ||
|
|
214e806a33 | ||
|
|
8e0d6d11b9 | ||
|
|
9412b58d80 | ||
|
|
dad97fc2d9 | ||
|
|
ff33cc4dad | ||
|
|
51e0e5e66d | ||
|
|
5fa6ecf9ae | ||
|
|
34c7f4e6b7 | ||
|
|
737559d3f8 | ||
|
|
f801372074 | ||
|
|
ea2dce0ab4 | ||
|
|
314c6f94f0 | ||
|
|
8fd119e546 | ||
|
|
8612476103 | ||
|
|
35812bd7ba | ||
|
|
f1ef2e8138 | ||
|
|
c47cb512ab | ||
|
|
fe7d4230d8 | ||
|
|
b6e166f44d | ||
|
|
ab2d223e71 | ||
|
|
c6c7baa3b4 | ||
|
|
218f293cd4 | ||
|
|
4d641d193c | ||
|
|
67ff78f1ca | ||
|
|
4f0716ab13 | ||
|
|
07c59e6a6c | ||
|
|
86a674b87e | ||
|
|
9957ab259c | ||
|
|
4c3944f626 | ||
|
|
6f9b466d7c | ||
|
|
61badb6af7 | ||
|
|
e31e57c7e7 | ||
|
|
469188044d | ||
|
|
38063e5602 | ||
|
|
ba5e2b7fa6 | ||
|
|
eed806962d | ||
|
|
a1ce5e15ce | ||
|
|
f0735c945a | ||
|
|
d84e131b73 | ||
|
|
ad1511e69c | ||
|
|
db4dc114f2 | ||
|
|
a7f363ea43 | ||
|
|
0ccbba61ce | ||
|
|
d921a4e168 | ||
|
|
819a85ee06 | ||
|
|
a3d15fe16c | ||
|
|
56d25cdeeb | ||
|
|
d027a15b4a | ||
|
|
a16316d7b4 | ||
|
|
14d8e93004 | ||
|
|
37c62bf9b7 | ||
|
|
72839d7654 | ||
|
|
1994cba73e | ||
|
|
55cdcbb39b | ||
|
|
c7ce6230db | ||
|
|
7c45ae24ae | ||
|
|
99c350fc43 | ||
|
|
faf1c5cb64 | ||
|
|
ece6efc75e | ||
|
|
0a809ab5f0 | ||
|
|
78ff1a4af5 | ||
|
|
c0dcae4f1d | ||
|
|
5e32c8a828 | ||
|
|
4d15a2f45e | ||
|
|
a913f21c45 | ||
|
|
322c70433b | ||
|
|
ad9cfcdcc7 | ||
|
|
4232c9d2b0 | ||
|
|
9edd59d280 | ||
|
|
df74cbf06f | ||
|
|
5af224ab16 | ||
|
|
7b734df09e | ||
|
|
1340a47bc2 | ||
|
|
4f4476ba75 | ||
|
|
5c7a183f8a | ||
|
|
53bada2a1e | ||
|
|
43efb7df2f | ||
|
|
f5cea221a6 | ||
|
|
b7082f34a1 | ||
|
|
d20d957881 | ||
|
|
008274cda5 | ||
|
|
e07ab7547f | ||
|
|
cf10837eb8 | ||
|
|
291b26f230 | ||
|
|
08e8c9bf57 | ||
|
|
625152440c | ||
|
|
fbd51821d1 | ||
|
|
c3ebf51295 | ||
|
|
9a9ff7f32c | ||
|
|
7b73eec82b | ||
|
|
75ba4a1cdb | ||
|
|
e5ca9065bd | ||
|
|
1042d0825f | ||
|
|
17942925f5 | ||
|
|
7424317d03 | ||
|
|
dbf1d91961 | ||
|
|
eb1644b302 | ||
|
|
cde5bc3263 | ||
|
|
e995e289db | ||
|
|
5020caa9c3 | ||
|
|
f34eb7d9f3 | ||
|
|
bf74ef0e5e | ||
|
|
0ff52311c3 | ||
|
|
e453e6f9ca | ||
|
|
6078598aff | ||
|
|
9fdb3b3b4a | ||
|
|
1362aa655f | ||
|
|
9c22ab8925 | ||
|
|
ca2dbb2f4b | ||
|
|
16bbc5a026 | ||
|
|
627f10cd18 | ||
|
|
13509b9231 | ||
|
|
ca88afbf5b | ||
|
|
42c9c9894b | ||
|
|
b4de62cfc2 | ||
|
|
226fbc191b | ||
|
|
4839d8861d | ||
|
|
789b47d565 | ||
|
|
c13cdcdd36 | ||
|
|
4ae3d0150f | ||
|
|
7d153a162a | ||
|
|
7bc3e94ff3 | ||
|
|
9c1fb26660 | ||
|
|
3dd725a0f0 | ||
|
|
a8aad53038 | ||
|
|
68c60f58c0 | ||
|
|
c9c3f33acc | ||
|
|
5ffc5187eb | ||
|
|
5d31cdfa16 | ||
|
|
e8ff4af5b9 | ||
|
|
723e1dd9a6 | ||
|
|
369a4558a3 | ||
|
|
5e6a3d9d8e | ||
|
|
62dca4f1c5 | ||
|
|
061c02306f | ||
|
|
f006b05010 | ||
|
|
c5a727aa9b | ||
|
|
e67e9d3bbf | ||
|
|
ea86c9d37a | ||
|
|
d1a2e6e5bd | ||
|
|
c96985f1db | ||
|
|
b7010c83e0 | ||
|
|
20a3d19ac7 | ||
|
|
0ded93ab9b | ||
|
|
ec82ec0426 | ||
|
|
6281f8ff89 | ||
|
|
2c9d2d4fd7 | ||
|
|
8569239bc1 | ||
|
|
5463c398cb | ||
|
|
7381734913 | ||
|
|
462322026f | ||
|
|
b5e5c719ed | ||
|
|
a0f4976b07 | ||
|
|
44c2486a74 | ||
|
|
5fc6672784 | ||
|
|
bcb1edba90 | ||
|
|
8099e7a75d | ||
|
|
cc9a501351 | ||
|
|
b536a23124 | ||
|
|
6ca5bcc6b8 | ||
|
|
ac966f118a | ||
|
|
f13472a8c3 | ||
|
|
0e213ae777 | ||
|
|
a0c82a6a47 | ||
|
|
a087ba608b | ||
|
|
9112d63655 | ||
|
|
3f7f7a0aa7 | ||
|
|
8d03aafe72 | ||
|
|
b0c0fd7dc8 | ||
|
|
c273f26cb3 | ||
|
|
60101830cc | ||
|
|
a58d782704 | ||
|
|
9b94c1dda9 | ||
|
|
201a6b350e | ||
|
|
b2724caeda | ||
|
|
9d99d556a1 | ||
|
|
a1a6511e26 | ||
|
|
652456646f | ||
|
|
ca0dc49f64 | ||
|
|
ae1b39eb60 | ||
|
|
22f7faf60e | ||
|
|
f3bf9eb14d | ||
|
|
347a1a48d4 | ||
|
|
c9793457f3 | ||
|
|
50d31ba398 | ||
|
|
2788c4cc00 | ||
|
|
ba54232b8d | ||
|
|
489bb70901 | ||
|
|
dd06dfa5ba | ||
|
|
f39e339726 | ||
|
|
295b374b48 | ||
|
|
8ed390c394 | ||
|
|
f9e9cac6e8 | ||
|
|
f3304acc93 | ||
|
|
a233771433 | ||
|
|
ea9a871d90 | ||
|
|
84d010bb2f | ||
|
|
e0ba468b7e | ||
|
|
f88c0dd645 | ||
|
|
758918c077 | ||
|
|
7b516e6113 | ||
|
|
61a76bb834 | ||
|
|
e6b6edefaf | ||
|
|
a228eb020d | ||
|
|
c46933a81a | ||
|
|
746046c8c0 | ||
|
|
acab95792f | ||
|
|
b882265e52 | ||
|
|
547f2ef189 | ||
|
|
69004943a7 |
92
.github/workflows/main.yml
vendored
92
.github/workflows/main.yml
vendored
@@ -51,7 +51,7 @@ jobs:
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
cd resources/testproject
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe --debug-log --emit-llvm run hello_world_win32
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe -vvv --emit-llvm run hello_world_win32
|
||||
dir build\llvm_ir
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe clean
|
||||
dir build\llvm_ir
|
||||
@@ -61,13 +61,19 @@ jobs:
|
||||
run: |
|
||||
cd resources/testproject
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe --debug-log build hello_world_win32_lib
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe -vvv build hello_world_win32_lib
|
||||
|
||||
- name: Compile and run dynlib-test
|
||||
run: |
|
||||
cd resources/examples/dynlib-test
|
||||
..\..\..\build\${{ matrix.build_type }}\c3c.exe dynamic-lib add.c3
|
||||
..\..\..\build\${{ matrix.build_type }}\c3c.exe compile-run test.c3 -l ./add.lib
|
||||
..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv dynamic-lib add.c3
|
||||
..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv compile-run test.c3 -l ./add.lib
|
||||
|
||||
- name: Compile and run staticlib-test
|
||||
run: |
|
||||
cd resources/examples/staticlib-test
|
||||
..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv static-lib add.c3
|
||||
..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv compile-run test.c3 -l ./add.lib
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
@@ -126,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-18.1.8-1-any.pkg.tar.zst
|
||||
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-18.1.8-1-any.pkg.tar.zst
|
||||
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-llvm-19.1.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
|
||||
- name: CMake
|
||||
run: |
|
||||
cmake -B build -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
@@ -147,7 +153,7 @@ jobs:
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log
|
||||
../../build/c3c run -vvv
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
@@ -156,7 +162,7 @@ jobs:
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build hello_world_lib --cc cc --debug-log
|
||||
../../build/c3c build hello_world_lib --cc cc -vvv
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
@@ -203,12 +209,12 @@ jobs:
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log
|
||||
../../build/c3c run -vvv
|
||||
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build hello_world_lib --debug-log
|
||||
../../build/c3c build hello_world_lib -vvv
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
@@ -320,12 +326,21 @@ jobs:
|
||||
- name: Compile and run dynlib-test
|
||||
run: |
|
||||
cd resources/examples/dynlib-test
|
||||
../../../build/c3c dynamic-lib add.c3
|
||||
../../../build/c3c -vv dynamic-lib add.c3
|
||||
mv add.so libadd.so
|
||||
cc test.c -L. -ladd -Wl,-rpath=.
|
||||
./a.out
|
||||
../../../build/c3c compile-run test.c3 -L . -l add -z -Wl,-rpath=.
|
||||
|
||||
- name: Compile and run staticlib-test
|
||||
run: |
|
||||
cd resources/examples/staticlib-test
|
||||
../../../build/c3c -vv static-lib add.c3
|
||||
mv add.a libadd.a
|
||||
cc test.c -L. -ladd
|
||||
./a.out
|
||||
../../../build/c3c compile-run test.c3 -L . -l add
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
@@ -334,7 +349,7 @@ jobs:
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log
|
||||
../../build/c3c run -vvv
|
||||
|
||||
- name: Test WASM
|
||||
run: |
|
||||
@@ -353,7 +368,7 @@ jobs:
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log --linker=builtin
|
||||
../../build/c3c run -vvv --linker=builtin
|
||||
|
||||
- name: Init a library & a project
|
||||
run: |
|
||||
@@ -476,12 +491,12 @@ jobs:
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log
|
||||
../../build/c3c run -vvv
|
||||
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log --linker=builtin
|
||||
../../build/c3c run -vvv --linker=builtin
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
@@ -568,7 +583,7 @@ jobs:
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log
|
||||
../../build/c3c run -vvv
|
||||
|
||||
- name: Test WASM
|
||||
run: |
|
||||
@@ -578,7 +593,7 @@ jobs:
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log --linker=builtin
|
||||
../../build/c3c run -vvv --linker=builtin
|
||||
|
||||
- name: Init a library & a project
|
||||
run: |
|
||||
@@ -639,7 +654,7 @@ jobs:
|
||||
- name: Compile and run dynlib-test
|
||||
run: |
|
||||
cd resources/examples/dynlib-test
|
||||
../../../build/c3c dynamic-lib add.c3
|
||||
../../../build/c3c -vv dynamic-lib add.c3
|
||||
../../../build/c3c compile-run test.c3 -l ./add.dylib
|
||||
|
||||
- name: Compile run unit tests
|
||||
@@ -647,20 +662,25 @@ jobs:
|
||||
cd test
|
||||
../build/c3c compile-test unit
|
||||
|
||||
- name: Test WASM
|
||||
run: |
|
||||
cd resources/testfragments
|
||||
../../build/c3c compile --target wasm32 -g0 --no-entry -Os wasm4.c3
|
||||
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log
|
||||
../../build/c3c run -vvv
|
||||
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log --linker=builtin
|
||||
../../build/c3c run -vvv --linker=builtin
|
||||
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build hello_world_lib --debug-log
|
||||
../../build/c3c build hello_world_lib -vvv
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
@@ -683,6 +703,36 @@ jobs:
|
||||
name: c3-macos-${{matrix.build_type}}
|
||||
path: c3-macos-${{matrix.build_type}}.zip
|
||||
|
||||
build-nix:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
# Don't abort runners if a single one fails
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [ Release, Debug ]
|
||||
nixpkgs: [ Lock, Latest ]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
github_access_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Update flake (if necessary)
|
||||
run: |
|
||||
if [[ matrix.nixpkgs == "Latest" ]]; then
|
||||
nix flake update
|
||||
fi
|
||||
nix flake info
|
||||
|
||||
- name: Build and check
|
||||
run: |
|
||||
if [[ ${{ matrix.build_type }} = "Debug" ]]; then
|
||||
nix build -L ".#c3c-debug-checks"
|
||||
else
|
||||
nix build -L ".#c3c-checks"
|
||||
fi
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -19,6 +19,7 @@
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.tlb
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
@@ -75,3 +76,8 @@ TAGS
|
||||
/.cache/
|
||||
/compile_commands.json
|
||||
|
||||
# 'nix build' resulting symlink
|
||||
result
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
176
CMakeLists.txt
176
CMakeLists.txt
@@ -38,10 +38,10 @@ set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
if(MSVC)
|
||||
message(STATUS "MSVC version ${MSVC_VERSION}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2 /EHsc")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /O2 /EHsc")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi /EHa")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Od /Zi /EHa")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2 /EHsc /utf-8")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /O2 /EHsc /utf-8")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi /EHa /utf-8")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Od /Zi /EHa /utf-8")
|
||||
else()
|
||||
if (true)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -fno-exceptions")
|
||||
@@ -73,9 +73,9 @@ if(C3_USE_MIMALLOC)
|
||||
option(MI_PADDING OFF)
|
||||
option(MI_DEBUG_FULL OFF)
|
||||
FetchContent_Declare(
|
||||
mimalloc
|
||||
GIT_REPOSITORY https://github.com/microsoft/mimalloc.git
|
||||
GIT_TAG ${C3_MIMALLOC_TAG}
|
||||
mimalloc
|
||||
GIT_REPOSITORY https://github.com/microsoft/mimalloc.git
|
||||
GIT_TAG ${C3_MIMALLOC_TAG}
|
||||
)
|
||||
FetchContent_MakeAvailable(mimalloc)
|
||||
endif()
|
||||
@@ -108,9 +108,9 @@ endif()
|
||||
# Clangd LSP support
|
||||
option(C3_ENABLE_CLANGD_LSP "Enable/Disable output of compile commands during generation." OFF)
|
||||
if(C3_ENABLE_CLANGD_LSP)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink
|
||||
${CMAKE_BINARY_DIR}/compile_commands.json
|
||||
${CMAKE_SOURCE_DIR}/compile_commands.json
|
||||
)
|
||||
@@ -119,15 +119,15 @@ endif(C3_ENABLE_CLANGD_LSP)
|
||||
if(C3_WITH_LLVM)
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||
if (C3_LLVM_VERSION STREQUAL "auto")
|
||||
set(C3_LLVM_VERSION "18")
|
||||
set(C3_LLVM_VERSION "19")
|
||||
endif()
|
||||
FetchContent_Declare(
|
||||
LLVM_Windows
|
||||
URL https://github.com/c3lang/win-llvm/releases/download/llvm_18_1_8_with_rt/llvm-18.1.8-windows-amd64-msvc17-libcmt.7z
|
||||
URL https://github.com/c3lang/win-llvm/releases/download/llvm_19_1_5/llvm-19.1.5-windows-amd64-msvc17-libcmt.7z
|
||||
)
|
||||
FetchContent_Declare(
|
||||
LLVM_Windows_debug
|
||||
URL https://github.com/c3lang/win-llvm/releases/download/llvm_18_1_8_with_rt/llvm-18.1.8-windows-amd64-msvc17-libcmt-dbg.7z
|
||||
URL https://github.com/c3lang/win-llvm/releases/download/llvm_19_1_5/llvm-19.1.5-windows-amd64-msvc17-libcmt-dbg.7z
|
||||
)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
message("Loading Windows LLVM debug libraries, this may take a while...")
|
||||
@@ -150,6 +150,13 @@ if(C3_WITH_LLVM)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (EXISTS /usr/lib)
|
||||
# Some systems (such as Alpine Linux) seem to put some of the relevant
|
||||
# LLVM files in /usr/lib, but this doesn't seem to be included in the
|
||||
# value of LLVM_LIBRARY_DIRS.
|
||||
list(APPEND LLVM_LIBRARY_DIRS /usr/lib)
|
||||
endif()
|
||||
|
||||
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
|
||||
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
|
||||
message(STATUS "Libraries located in: ${LLVM_LIBRARY_DIRS}")
|
||||
@@ -173,54 +180,54 @@ if(C3_WITH_LLVM)
|
||||
|
||||
if(NOT C3_LINK_DYNAMIC)
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
AllTargetsAsmParsers
|
||||
AllTargetsCodeGens
|
||||
AllTargetsDescs
|
||||
AllTargetsDisassemblers
|
||||
AllTargetsInfos
|
||||
Analysis
|
||||
AsmPrinter
|
||||
BitReader
|
||||
Core
|
||||
DebugInfoPDB
|
||||
InstCombine
|
||||
IrReader
|
||||
LibDriver
|
||||
Linker
|
||||
LTO
|
||||
MC
|
||||
MCDisassembler
|
||||
native
|
||||
nativecodegen
|
||||
Object
|
||||
Option
|
||||
ScalarOpts
|
||||
Support
|
||||
Target
|
||||
TransformUtils
|
||||
WindowsManifest
|
||||
WindowsDriver
|
||||
AllTargetsAsmParsers
|
||||
AllTargetsCodeGens
|
||||
AllTargetsDescs
|
||||
AllTargetsDisassemblers
|
||||
AllTargetsInfos
|
||||
Analysis
|
||||
AsmPrinter
|
||||
BitReader
|
||||
Core
|
||||
DebugInfoPDB
|
||||
InstCombine
|
||||
IrReader
|
||||
LibDriver
|
||||
Linker
|
||||
LTO
|
||||
MC
|
||||
MCDisassembler
|
||||
native
|
||||
nativecodegen
|
||||
Object
|
||||
Option
|
||||
ScalarOpts
|
||||
Support
|
||||
Target
|
||||
TransformUtils
|
||||
WindowsManifest
|
||||
WindowsDriver
|
||||
)
|
||||
|
||||
llvm_map_components_to_libnames(llvm_libs ${LLVM_LINK_COMPONENTS})
|
||||
|
||||
if(NOT ${C3_LLD_DIR} EQUAL "" AND EXISTS ${C3_LLD_DIR})
|
||||
if(NOT ${C3_LLD_DIR} EQUAL "" AND EXISTS ${C3_LLD_DIR})
|
||||
message("C3_LLD_DIR: " ${C3_LLD_DIR})
|
||||
set(LLVM_LIBRARY_DIRS
|
||||
"${LLVM_LIBRARY_DIRS}"
|
||||
"${C3_LLD_DIR}"
|
||||
"${LLVM_LIBRARY_DIRS}"
|
||||
"${C3_LLD_DIR}"
|
||||
)
|
||||
list(REMOVE_DUPLICATES LLVM_LIBRARY_DIRS)
|
||||
endif()
|
||||
|
||||
# These don't seem to be reliable on windows.
|
||||
message(STATUS "using find_library")
|
||||
find_library(LLD_COFF NAMES liblldCOFF.dylib lldCOFF.lib lldCOFF.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_COMMON NAMES liblldCommon.dylib lldCommon.lib lldCommon.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_ELF NAMES liblldELF.dylib lldELF.lib lldELF.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_MACHO NAMES liblldMachO.dylib lldMachO.lib lldMachO.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_MINGW NAMES liblldMinGW.dylib lldMinGW.lib lldMinGW.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_WASM NAMES liblldWasm.dylib lldWasm.lib lldWasm.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_COFF NAMES liblldCOFF.dylib lldCOFF.lib lldCOFF.a liblldCOFF.dll.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_COMMON NAMES liblldCommon.dylib lldCommon.lib lldCommon.a liblldCommon.dll.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_ELF NAMES liblldELF.dylib lldELF.lib lldELF.a liblldELF.dll.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_MACHO NAMES liblldMachO.dylib lldMachO.lib lldMachO.a liblldMachO.dll.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_MINGW NAMES liblldMinGW.dylib lldMinGW.lib lldMinGW.a liblldMinGW.dll.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_WASM NAMES liblldWasm.dylib lldWasm.lib lldWasm.a liblldWasm.dll.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
else()
|
||||
find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
set(llvm_libs ${LLVM})
|
||||
@@ -244,13 +251,13 @@ endif()
|
||||
if(C3_WITH_LLVM)
|
||||
find_library(LLD_LOONG NAMES libLLVMLoongArchCodeGen.lib libLLVMLoongArchAsmParser.lib libLLVMLoongArchCodeGen.a libLLVMLoongArchAsmParser.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
set(lld_libs
|
||||
${LLD_COFF}
|
||||
${LLD_COMMON}
|
||||
${LLD_WASM}
|
||||
${LLD_MINGW}
|
||||
${LLD_ELF}
|
||||
${LLD_MACHO}
|
||||
)
|
||||
${LLD_COFF}
|
||||
${LLD_WASM}
|
||||
${LLD_MINGW}
|
||||
${LLD_ELF}
|
||||
${LLD_MACHO}
|
||||
${LLD_COMMON}
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
set(lld_libs ${lld_libs} xar)
|
||||
@@ -264,7 +271,7 @@ if(C3_WITH_LLVM)
|
||||
# Unused
|
||||
# ${RT_UBSAN_DYNAMIC}
|
||||
# ${RT_LSAN_DYNAMIC}
|
||||
)
|
||||
)
|
||||
endif()
|
||||
|
||||
message(STATUS "linking to llvm libs ${lld_libs}")
|
||||
@@ -342,11 +349,12 @@ add_executable(c3c
|
||||
src/utils/whereami.c
|
||||
src/utils/cpus.c
|
||||
src/utils/unzipper.c
|
||||
src/compiler/c_codegen.c
|
||||
src/compiler/decltable.c
|
||||
src/compiler/mac_support.c
|
||||
src/compiler/mac_support.c
|
||||
src/compiler/windows_support.c
|
||||
src/compiler/codegen_asm.c
|
||||
src/compiler/asm_target.c
|
||||
src/compiler/asm_target.c
|
||||
src/compiler/expr.c
|
||||
src/utils/time.c
|
||||
src/utils/http.c
|
||||
@@ -354,7 +362,7 @@ add_executable(c3c
|
||||
src/build/common_build.c
|
||||
src/compiler/sema_const.c
|
||||
${CMAKE_BINARY_DIR}/git_hash.h
|
||||
)
|
||||
)
|
||||
|
||||
if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
# We are inside of a git repository so rebuilding the hash every time something changes.
|
||||
@@ -370,18 +378,18 @@ else()
|
||||
endif()
|
||||
|
||||
if(C3_WITH_LLVM)
|
||||
target_sources(c3c PRIVATE
|
||||
src/compiler/llvm_codegen.c
|
||||
src/compiler/llvm_codegen_debug_info.c
|
||||
src/compiler/llvm_codegen_expr.c
|
||||
src/compiler/llvm_codegen_function.c
|
||||
src/compiler/llvm_codegen_instr.c
|
||||
src/compiler/llvm_codegen_module.c
|
||||
src/compiler/llvm_codegen_stmt.c
|
||||
src/compiler/llvm_codegen_type.c
|
||||
src/compiler/llvm_codegen_value.c
|
||||
src/compiler/llvm_codegen_storeload.c
|
||||
src/compiler/llvm_codegen_builtins.c)
|
||||
target_sources(c3c PRIVATE
|
||||
src/compiler/llvm_codegen.c
|
||||
src/compiler/llvm_codegen_debug_info.c
|
||||
src/compiler/llvm_codegen_expr.c
|
||||
src/compiler/llvm_codegen_function.c
|
||||
src/compiler/llvm_codegen_instr.c
|
||||
src/compiler/llvm_codegen_module.c
|
||||
src/compiler/llvm_codegen_stmt.c
|
||||
src/compiler/llvm_codegen_type.c
|
||||
src/compiler/llvm_codegen_value.c
|
||||
src/compiler/llvm_codegen_storeload.c
|
||||
src/compiler/llvm_codegen_builtins.c)
|
||||
|
||||
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=1)
|
||||
add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp)
|
||||
@@ -409,7 +417,7 @@ if (C3_USE_TB)
|
||||
tilde-backend/src/tb/x64/*.c
|
||||
tilde-backend/src/tb/wasm/*.c
|
||||
tilde-backend/src/tb/aarch64/*.c
|
||||
)
|
||||
)
|
||||
target_sources(c3c PRIVATE
|
||||
src/compiler/tilde_codegen.c
|
||||
src/compiler/tilde_codegen_instr.c
|
||||
@@ -447,7 +455,7 @@ if(C3_WITH_LLVM)
|
||||
|
||||
target_link_libraries(c3c_wrappers ${llvm_libs} ${lld_libs})
|
||||
|
||||
else()
|
||||
else()
|
||||
|
||||
target_link_libraries(c3c ${llvm_libs} miniz ${lld_libs})
|
||||
|
||||
@@ -507,10 +515,10 @@ if(MSVC)
|
||||
if(C3_WITH_LLVM)
|
||||
set(clang_lib_dir ${llvm_dir}/lib/clang/${C3_LLVM_VERSION}/lib/windows)
|
||||
set(sanitizer_runtime_libraries
|
||||
${clang_lib_dir}/clang_rt.asan-x86_64.lib
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.lib
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.dll
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib)
|
||||
${clang_lib_dir}/clang_rt.asan-x86_64.lib
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.lib
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.dll
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "using gcc/clang warning switches")
|
||||
@@ -532,17 +540,17 @@ endif()
|
||||
|
||||
if (C3_WITH_LLVM AND DEFINED sanitizer_runtime_libraries)
|
||||
add_custom_command(TARGET c3c POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E rm -rf -- $<TARGET_FILE_DIR:c3c>/c3c_rt
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory $<TARGET_FILE_DIR:c3c>/c3c_rt
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy ${sanitizer_runtime_libraries} $<TARGET_FILE_DIR:c3c>/c3c_rt
|
||||
VERBATIM
|
||||
COMMENT "Copying sanitizer runtime libraries to output directory")
|
||||
COMMAND "${CMAKE_COMMAND}" -E rm -rf -- $<TARGET_FILE_DIR:c3c>/c3c_rt
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory $<TARGET_FILE_DIR:c3c>/c3c_rt
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy ${sanitizer_runtime_libraries} $<TARGET_FILE_DIR:c3c>/c3c_rt
|
||||
VERBATIM
|
||||
COMMENT "Copying sanitizer runtime libraries to output directory")
|
||||
|
||||
if (APPLE)
|
||||
# Change LC_ID_DYLIB to be rpath-based instead of having an absolute path
|
||||
add_custom_command(TARGET c3c POST_BUILD
|
||||
COMMAND find $<TARGET_FILE_DIR:c3c>/c3c_rt -type f -name "*.dylib" -execdir ${LLVM_TOOLS_BINARY_DIR}/llvm-install-name-tool -id @rpath/{} {} $<SEMICOLON>
|
||||
VERBATIM)
|
||||
COMMAND find $<TARGET_FILE_DIR:c3c>/c3c_rt -type f -name "*.dylib" -execdir ${LLVM_TOOLS_BINARY_DIR}/llvm-install-name-tool -id @rpath/{} {} $<SEMICOLON>
|
||||
VERBATIM)
|
||||
endif()
|
||||
|
||||
install(DIRECTORY $<TARGET_FILE_DIR:c3c>/c3c_rt/ DESTINATION bin/c3c_rt)
|
||||
|
||||
20
README.md
20
README.md
@@ -55,7 +55,7 @@ fn void Stack.push(Stack* this, Type element)
|
||||
if (this.capacity == this.size)
|
||||
{
|
||||
this.capacity *= 2;
|
||||
if (this.capacity < 16) this.capacity = 16;
|
||||
if (this.capacity < 16) this.capacity = 16;
|
||||
this.elems = realloc(this.elems, Type.sizeof * this.capacity);
|
||||
}
|
||||
this.elems[this.size++] = element;
|
||||
@@ -138,7 +138,7 @@ fn void main()
|
||||
|
||||
### Current status
|
||||
|
||||
The current stable version of the compiler is **version 0.6.3**.
|
||||
The current stable version of the compiler is **version 0.6.5**.
|
||||
|
||||
The upcoming 0.6.x releases will focus on expanding the standard library.
|
||||
Follow the issues [here](https://github.com/c3lang/c3c/issues).
|
||||
@@ -303,7 +303,7 @@ called `hello_world` or `hello_world.exe`depending on platform.
|
||||
|
||||
#### Compiling on Windows
|
||||
|
||||
1. Make sure you have Visual Studio 17 2022 installed or alternatively install the "Buildtools for Visual Studio" (https://aka.ms/vs/17/release/vs_BuildTools.exe) and then select "Desktop development with C++" (there is also `c3c/resources/install_win_reqs.bat` to automate this)
|
||||
1. Make sure you have Visual Studio 17 2022 installed or alternatively install the "Buildtools for Visual Studio" (https://aka.ms/vs/17/release/vs_BuildTools.exe) and then select "Desktop development with C++"
|
||||
2. Install CMake
|
||||
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
|
||||
4. Enter the C3C directory `cd c3c`.
|
||||
@@ -349,6 +349,20 @@ Your c3c executable should have compiled properly. You may want to test it: `./c
|
||||
For a sytem-wide installation, run the following as root: `cmake --install .`
|
||||
|
||||
|
||||
#### Compiling on Fedora
|
||||
|
||||
1. Install required project dependencies: `dnf install cmake clang git llvm llvm-devel lld lld-devel ncurses-devel`
|
||||
2. Optionally, install additional dependencies: `dnf install libcurl-devel zlib-devel libzstd-devel libxml2-devel libffi-devel`
|
||||
3. Clone the C3C repository: `git clone https://github.com/c3lang/c3c.git`
|
||||
- If you only need the latest commit, you may want to make a shallow clone: `git clone https://github.com/c3lang/c3c.git --depth=1`
|
||||
4. Enter the C3C directory: `cd c3c`
|
||||
5. Create a build directory and navigate into it: `mkdir build && cd build`
|
||||
6. Create the CMake build cache. The Fedora repositories provide `.so` libraries for lld, so you need to set the C3_LINK_DYNAMIC flag: `cmake .. -DC3_LINK_DYNAMIC=1`
|
||||
7. Build the project: `cmake --build .`
|
||||
|
||||
The c3c binary should be created in the build directory. You can try it out by running some sample code: `./c3c compile ../resources/examples/hash.c3`
|
||||
|
||||
|
||||
#### Compiling on other Linux / Unix variants
|
||||
|
||||
1. Install CMake.
|
||||
|
||||
69
benchmarks/stdlib/sort/quicksort.c3
Normal file
69
benchmarks/stdlib/sort/quicksort.c3
Normal file
@@ -0,0 +1,69 @@
|
||||
module sort_bench;
|
||||
|
||||
import std::sort;
|
||||
|
||||
fn void init() @init
|
||||
{
|
||||
set_benchmark_warmup_iterations(5);
|
||||
set_benchmark_max_iterations(10_000);
|
||||
}
|
||||
|
||||
fn void quicksort_bench() @benchmark
|
||||
{
|
||||
// test set: 500 numbers between 0 and 99;
|
||||
int[] data = {
|
||||
71, 28, 2, 13, 62, 10, 54, 78, 63, 86,
|
||||
33, 65, 89, 51, 58, 0, 51, 16, 87, 30,
|
||||
89, 14, 52, 41, 88, 25, 83, 91, 56, 86,
|
||||
14, 64, 76, 18, 39, 24, 79, 62, 34, 58,
|
||||
90, 24, 56, 73, 85, 82, 79, 63, 47, 69,
|
||||
78, 29, 49, 28, 43, 47, 56, 53, 79, 56,
|
||||
19, 63, 29, 52, 71, 93, 61, 46, 30, 11,
|
||||
21, 26, 37, 86, 93, 74, 62, 0, 41, 17,
|
||||
26, 27, 34, 11, 54, 69, 72, 44, 74, 3,
|
||||
61, 62, 80, 90, 3, 82, 16, 12, 28, 1,
|
||||
2, 49, 4, 44, 57, 86, 63, 74, 33, 41,
|
||||
76, 77, 56, 57, 56, 88, 74, 71, 6, 59,
|
||||
40, 42, 94, 55, 21, 17, 17, 63, 21, 83,
|
||||
73, 19, 39, 88, 93, 74, 21, 0, 63, 45,
|
||||
69, 66, 22, 68, 86, 86, 85, 67, 8, 50,
|
||||
23, 98, 64, 80, 64, 36, 40, 30, 73, 36,
|
||||
23, 14, 1, 77, 82, 8, 18, 73, 37, 86,
|
||||
29, 70, 27, 87, 64, 81, 13, 0, 4, 83,
|
||||
90, 17, 71, 66, 38, 39, 54, 22, 86, 18,
|
||||
84, 66, 77, 25, 64, 93, 80, 91, 2, 92,
|
||||
47, 32, 90, 16, 46, 29, 56, 87, 70, 73,
|
||||
89, 41, 5, 54, 93, 63, 16, 39, 71, 84,
|
||||
74, 91, 69, 59, 49, 87, 74, 37, 75, 83,
|
||||
77, 19, 51, 44, 79, 62, 94, 20, 24, 83,
|
||||
37, 70, 57, 32, 93, 8, 29, 11, 7, 92,
|
||||
8, 23, 20, 21, 7, 70, 28, 20, 96, 6,
|
||||
50, 58, 30, 61, 66, 42, 50, 54, 64, 7,
|
||||
10, 53, 63, 44, 16, 39, 83, 73, 3, 29,
|
||||
97, 32, 36, 68, 84, 64, 73, 5, 29, 13,
|
||||
48, 3, 84, 65, 75, 68, 66, 22, 39, 33,
|
||||
39, 24, 27, 85, 18, 34, 3, 63, 32, 9,
|
||||
29, 66, 24, 90, 75, 50, 11, 95, 47, 14,
|
||||
92, 1, 76, 45, 76, 41, 55, 54, 38, 67,
|
||||
43, 40, 5, 61, 97, 11, 61, 24, 92, 24,
|
||||
76, 53, 60, 34, 78, 80, 70, 75, 30, 90,
|
||||
65, 99, 80, 61, 94, 75, 63, 67, 10, 35,
|
||||
23, 42, 31, 48, 14, 68, 84, 14, 79, 1,
|
||||
25, 94, 23, 53, 49, 69, 44, 73, 63, 51,
|
||||
44, 96, 88, 51, 94, 24, 64, 72, 59, 81,
|
||||
73, 93, 14, 35, 9, 53, 25, 48, 50, 88,
|
||||
46, 97, 67, 40, 27, 17, 2, 42, 11, 82,
|
||||
0, 46, 44, 38, 31, 88, 63, 88, 10, 82,
|
||||
77, 61, 24, 39, 27, 33, 10, 91, 69, 22,
|
||||
42, 74, 71, 13, 32, 56, 12, 46, 81, 74,
|
||||
17, 26, 45, 50, 76, 84, 76, 36, 43, 65,
|
||||
81, 64, 0, 49, 70, 11, 76, 19, 60, 55,
|
||||
15, 98, 31, 91, 56, 8, 97, 9, 3, 94,
|
||||
3, 88, 7, 2, 3, 98, 10, 51, 21, 79,
|
||||
99, 3, 8, 76, 52, 13, 40, 90, 85, 15,
|
||||
70, 77, 43, 30, 4, 89, 18, 21, 59, 17,
|
||||
};
|
||||
sort::quicksort(data);
|
||||
}
|
||||
|
||||
|
||||
61
flake.lock
generated
Normal file
61
flake.lock
generated
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1726560853,
|
||||
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1730958623,
|
||||
"narHash": "sha256-JwQZIGSYnRNOgDDoIgqKITrPVil+RMWHsZH1eE1VGN0=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "85f7e662eda4fa3a995556527c87b2524b691933",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
44
flake.nix
Normal file
44
flake.nix
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
description = "C3 compiler flake";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs?ref=nixpkgs-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = { self, ... } @ inputs: inputs.flake-utils.lib.eachDefaultSystem
|
||||
(system:
|
||||
let pkgs = import inputs.nixpkgs { inherit system; };
|
||||
call = set: pkgs.callPackage ./nix/default.nix (
|
||||
set // {
|
||||
rev = self.rev or "unknown";
|
||||
}
|
||||
);
|
||||
in {
|
||||
packages = {
|
||||
default = self.packages.${system}.c3c;
|
||||
|
||||
c3c = call {};
|
||||
|
||||
c3c-checks = pkgs.callPackage ./nix/default.nix {
|
||||
checks = true;
|
||||
};
|
||||
|
||||
c3c-debug = pkgs.callPackage ./nix/default.nix {
|
||||
debug = true;
|
||||
};
|
||||
|
||||
c3c-debug-checks = pkgs.callPackage ./nix/default.nix {
|
||||
debug = true;
|
||||
checks = true;
|
||||
};
|
||||
};
|
||||
|
||||
devShells = {
|
||||
default = pkgs.callPackage ./nix/shell.nix {
|
||||
c3c = self.packages.${system}.c3c-debug;
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -4,6 +4,7 @@ set(GIT_HASH "unknown")
|
||||
|
||||
if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/.git")
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
|
||||
OUTPUT_VARIABLE GIT_HASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
COMMAND_ERROR_IS_FATAL ANY)
|
||||
|
||||
@@ -22,7 +22,7 @@ fn usz! EnumMap.to_format(&self, Formatter* formatter) @dynamic
|
||||
foreach (i, &value : self.values)
|
||||
{
|
||||
if (i != 0) formatter.print(", ")!;
|
||||
n += formatter.printf("%s: %s", (Enum)i, *value)!;
|
||||
n += formatter.printf("%s: %s", Enum.from_ordinal(i), *value)!;
|
||||
}
|
||||
n += formatter.print(" }")!;
|
||||
return n;
|
||||
|
||||
@@ -16,9 +16,9 @@ distinct EnumSet (Printable) = EnumSetType;
|
||||
fn void EnumSet.add(&self, Enum v)
|
||||
{
|
||||
$if IS_CHAR_ARRAY:
|
||||
(*self)[(usz)v / 8] |= (char)(1u << ((usz)v % 8));
|
||||
(*self)[(usz)v.ordinal / 8] |= (char)(1u << ((usz)v.ordinal % 8));
|
||||
$else
|
||||
*self = (EnumSet)((EnumSetType)*self | 1u << (EnumSetType)v);
|
||||
*self = (EnumSet)((EnumSetType)*self | 1u << (EnumSetType)v.ordinal);
|
||||
$endif
|
||||
}
|
||||
|
||||
@@ -35,11 +35,11 @@ fn bool EnumSet.remove(&self, Enum v)
|
||||
{
|
||||
$if IS_CHAR_ARRAY:
|
||||
if (!self.has(v) @inline) return false;
|
||||
(*self)[(usz)v / 8] &= (char)~(1u << ((usz)v % 8));
|
||||
(*self)[(usz)v.ordinal / 8] &= (char)~(1u << ((usz)v.ordinal % 8));
|
||||
return true;
|
||||
$else
|
||||
EnumSetType old = (EnumSetType)*self;
|
||||
EnumSetType new = old & ~(1u << (EnumSetType)v);
|
||||
EnumSetType new = old & ~(1u << (EnumSetType)v.ordinal);
|
||||
*self = (EnumSet)new;
|
||||
return old != new;
|
||||
$endif
|
||||
@@ -48,9 +48,9 @@ fn bool EnumSet.remove(&self, Enum v)
|
||||
fn bool EnumSet.has(&self, Enum v)
|
||||
{
|
||||
$if IS_CHAR_ARRAY:
|
||||
return (bool)(((*self)[(usz)v / 8] << ((usz)v % 8)) & 0x01);
|
||||
return (bool)(((*self)[(usz)v.ordinal / 8] << ((usz)v.ordinal % 8)) & 0x01);
|
||||
$else
|
||||
return ((EnumSetType)*self & (1u << (EnumSetType)v)) != 0;
|
||||
return ((EnumSetType)*self & (1u << (EnumSetType)v.ordinal)) != 0;
|
||||
$endif
|
||||
}
|
||||
|
||||
|
||||
@@ -400,6 +400,21 @@ fn bool HashMap.has_value(&map, Value v) @if(VALUE_IS_EQUATABLE)
|
||||
return false;
|
||||
}
|
||||
|
||||
fn HashMapIterator HashMap.iter(&self)
|
||||
{
|
||||
return { .map = self, .index = -1 };
|
||||
}
|
||||
|
||||
fn HashMapValueIterator HashMap.value_iter(&self)
|
||||
{
|
||||
return { .map = self, .index = -1 };
|
||||
}
|
||||
|
||||
fn HashMapKeyIterator HashMap.key_iter(&self)
|
||||
{
|
||||
return { .map = self, .index = -1 };
|
||||
}
|
||||
|
||||
// --- private methods
|
||||
|
||||
fn void HashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
|
||||
@@ -455,8 +470,11 @@ fn void HashMap.put_all_for_create(&map, HashMap* other_map) @private
|
||||
if (!other_map.count) return;
|
||||
foreach (Entry *e : other_map.table)
|
||||
{
|
||||
if (!e) continue;
|
||||
map.put_for_create(e.key, e.value);
|
||||
while (e)
|
||||
{
|
||||
map.put_for_create(e.key, e.value);
|
||||
e = e.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,6 +500,7 @@ fn void HashMap.free_internal(&map, void* ptr) @inline @private
|
||||
|
||||
fn bool HashMap.remove_entry_for_key(&map, Key key) @private
|
||||
{
|
||||
if (!map.count) return false;
|
||||
uint hash = rehash(key.hash());
|
||||
uint i = index_for(hash, map.table.len);
|
||||
Entry* prev = map.table[i];
|
||||
@@ -528,3 +547,54 @@ fn void HashMap.free_entry(&self, Entry *entry) @local
|
||||
self.free_internal(entry);
|
||||
}
|
||||
|
||||
|
||||
struct HashMapIterator
|
||||
{
|
||||
HashMap* map;
|
||||
int top_index;
|
||||
int index;
|
||||
Entry* current_entry;
|
||||
}
|
||||
|
||||
distinct HashMapValueIterator = HashMapIterator;
|
||||
distinct HashMapKeyIterator = HashMapIterator;
|
||||
|
||||
|
||||
<*
|
||||
@require idx < self.map.count
|
||||
*>
|
||||
fn Entry HashMapIterator.get(&self, usz idx) @operator([])
|
||||
{
|
||||
if (idx < self.index)
|
||||
{
|
||||
self.top_index = 0;
|
||||
self.current_entry = null;
|
||||
self.index = -1;
|
||||
}
|
||||
while (self.index != idx)
|
||||
{
|
||||
if (self.current_entry)
|
||||
{
|
||||
self.current_entry = self.current_entry.next;
|
||||
if (self.current_entry) self.index++;
|
||||
continue;
|
||||
}
|
||||
self.current_entry = self.map.table[self.top_index++];
|
||||
if (self.current_entry) self.index++;
|
||||
}
|
||||
return *self.current_entry;
|
||||
}
|
||||
|
||||
fn Value HashMapValueIterator.get(&self, usz idx) @operator([])
|
||||
{
|
||||
return ((HashMapIterator*)self).get(idx).value;
|
||||
}
|
||||
|
||||
fn Key HashMapKeyIterator.get(&self, usz idx) @operator([])
|
||||
{
|
||||
return ((HashMapIterator*)self).get(idx).key;
|
||||
}
|
||||
|
||||
fn usz HashMapValueIterator.len(self) @operator(len) => self.map.count;
|
||||
fn usz HashMapKeyIterator.len(self) @operator(len) => self.map.count;
|
||||
fn usz HashMapIterator.len(self) @operator(len) => self.map.count;
|
||||
|
||||
@@ -131,8 +131,11 @@ fn Map new_from_map(Map other_map, Allocator allocator = null)
|
||||
if (!other_map_impl.count) return (Map)map;
|
||||
foreach (Entry *e : other_map_impl.table)
|
||||
{
|
||||
if (!e) continue;
|
||||
map._put_for_create(e.key, e.value);
|
||||
while (e)
|
||||
{
|
||||
map._put_for_create(e.key, e.value);
|
||||
e = e.next;
|
||||
}
|
||||
}
|
||||
return (Map)map;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,43 @@
|
||||
module std::collections::maybe(<Type>);
|
||||
import std::io;
|
||||
|
||||
struct Maybe
|
||||
struct Maybe (Printable)
|
||||
{
|
||||
Type value;
|
||||
bool has_value;
|
||||
}
|
||||
|
||||
fn usz! Maybe.to_format(&self, Formatter* f) @dynamic
|
||||
{
|
||||
if (self.has_value) return f.printf("[%s]", self.value);
|
||||
return f.printf("[EMPTY]");
|
||||
}
|
||||
|
||||
fn void Maybe.set(&self, Type val)
|
||||
{
|
||||
*self = { .value = val, .has_value = true };
|
||||
}
|
||||
|
||||
fn void Maybe.reset(&self)
|
||||
{
|
||||
*self = {};
|
||||
}
|
||||
|
||||
fn Maybe value(Type val)
|
||||
{
|
||||
return { .value = val, .has_value = true };
|
||||
}
|
||||
|
||||
fn Maybe Maybe.with_value(Type val) @operator(construct)
|
||||
{
|
||||
return { .value = val, .has_value = true };
|
||||
}
|
||||
|
||||
fn Maybe Maybe.empty() @operator(construct)
|
||||
{
|
||||
return { };
|
||||
}
|
||||
|
||||
const Maybe EMPTY = { };
|
||||
|
||||
macro Type! Maybe.get(self)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
<*
|
||||
@require values::@is_int(SIZE) &&& SIZE > 0 "The size must be positive integer"
|
||||
*>
|
||||
module std::collections::ringbuffer(<Type, SIZE>);
|
||||
|
||||
struct RingBuffer
|
||||
|
||||
@@ -98,7 +98,7 @@ fn void*! SimpleHeapAllocator._alloc(&self, usz bytes) @local
|
||||
return current + 1;
|
||||
case current.size > aligned_bytes:
|
||||
Header* unallocated = (Header*)((char*)current + aligned_bytes + Header.sizeof);
|
||||
unallocated.size = current.size - aligned_bytes;
|
||||
unallocated.size = current.size - aligned_bytes - Header.sizeof;
|
||||
unallocated.next = current.next;
|
||||
if (current == self.free_list)
|
||||
{
|
||||
|
||||
@@ -62,7 +62,7 @@ fn void OnStackAllocator.release(&self, void* old_pointer, bool aligned) @dynami
|
||||
{
|
||||
if (allocation_in_stack_mem(self, old_pointer)) return;
|
||||
on_stack_allocator_remove_chunk(self, old_pointer);
|
||||
self.release(old_pointer, aligned);
|
||||
self.backing_allocator.release(old_pointer, aligned);
|
||||
}
|
||||
|
||||
fn bool allocation_in_stack_mem(OnStackAllocator* a, void* ptr) @local
|
||||
|
||||
@@ -80,7 +80,7 @@ fn void TempAllocator.reset(&self, usz mark) @dynamic
|
||||
usz cleaned = self.used - mark;
|
||||
if (cleaned > 0)
|
||||
{
|
||||
$if env::COMPILER_SAFE_MODE:
|
||||
$if env::COMPILER_SAFE_MODE && !env::ADDRESS_SANITIZER:
|
||||
self.data[mark : cleaned] = 0xAA;
|
||||
$endif
|
||||
asan::poison_memory_region(&self.data[mark], cleaned);
|
||||
@@ -179,8 +179,10 @@ fn void*! TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz al
|
||||
{
|
||||
mem = allocator::malloc_aligned(self.backing_allocator, total_alloc_size, alignment)!;
|
||||
}
|
||||
void* start = mem;
|
||||
mem += mem::aligned_offset(TempAllocatorPage.sizeof, alignment);
|
||||
page = (TempAllocatorPage*)mem - 1;
|
||||
page.start = mem;
|
||||
page.start = start;
|
||||
page.size = size | PAGE_IS_ALIGNED;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -17,15 +17,16 @@ macro index_of(array, element)
|
||||
}
|
||||
|
||||
<*
|
||||
@require @typekind(array) == VECTOR || @typekind(array) == ARRAY
|
||||
@require @typekind(array[0]) == VECTOR || @typekind(array[0]) == ARRAY
|
||||
@require @typekind(array_ptr) == POINTER
|
||||
@require @typekind(*array_ptr) == VECTOR || @typekind(*array_ptr) == ARRAY
|
||||
@require @typekind((*array_ptr)[0]) == VECTOR || @typekind((*array_ptr)[0]) == ARRAY
|
||||
*>
|
||||
macro slice2d(array, x = 0, xlen = 0, y = 0, ylen = 0)
|
||||
macro slice2d(array_ptr, x = 0, xlen = 0, y = 0, ylen = 0)
|
||||
{
|
||||
if (xlen < 1) xlen = $typeof(array[0]).len + xlen;
|
||||
if (ylen < 1) ylen = $typeof(array).len + ylen;
|
||||
var $ElementType = $typeof(array[0][0]);
|
||||
return Slice2d(<$ElementType>) { ($ElementType*)&array, $typeof(array[0]).len, y, ylen, x, xlen };
|
||||
if (xlen < 1) xlen = $typeof((*array_ptr)[0]).len + xlen;
|
||||
if (ylen < 1) ylen = $typeof((*array_ptr)).len + ylen;
|
||||
var $ElementType = $typeof((*array_ptr)[0][0]);
|
||||
return Slice2d(<$ElementType>) { ($ElementType*)array_ptr, $typeof((*array_ptr)[0]).len, y, ylen, x, xlen };
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +79,7 @@ macro concat(arr1, arr2, Allocator allocator) @nodiscard
|
||||
@require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY
|
||||
@require @typekind(arr2) == SLICE || @typekind(arr2) == ARRAY
|
||||
@require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
|
||||
@ensure result.len == arr1.len + arr2.len
|
||||
@ensure return.len == arr1.len + arr2.len
|
||||
*>
|
||||
macro concat_new(arr1, arr2, Allocator allocator = allocator::heap()) @nodiscard
|
||||
{
|
||||
@@ -94,7 +95,7 @@ macro concat_new(arr1, arr2, Allocator allocator = allocator::heap()) @nodiscard
|
||||
@require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY
|
||||
@require @typekind(arr2) == SLICE || @typekind(arr2) == ARRAY
|
||||
@require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
|
||||
@ensure result.len == arr1.len + arr2.len
|
||||
@ensure return.len == arr1.len + arr2.len
|
||||
*>
|
||||
macro tconcat(arr1, arr2) @nodiscard => concat(arr1, arr2, allocator::temp());
|
||||
|
||||
@@ -145,11 +146,41 @@ macro void Slice2d.@each_ref(&self; @body(usz[<2>], Type*))
|
||||
<*
|
||||
@require idy >= 0 && idy < self.ylen
|
||||
*>
|
||||
macro Type[] Slice2d.get(self, usz idy) @operator([])
|
||||
macro Type[] Slice2d.get_row(self, usz idy) @operator([])
|
||||
{
|
||||
return (self.ptr + self.inner_len * (idy + self.ystart))[self.xstart:self.xlen];
|
||||
}
|
||||
|
||||
macro Type Slice2d.get_coord(self, usz[<2>] coord)
|
||||
{
|
||||
return *self.get_coord_ref(coord);
|
||||
}
|
||||
|
||||
macro Type Slice2d.get_xy(self, x, y)
|
||||
{
|
||||
return *self.get_xy_ref(x, y);
|
||||
}
|
||||
|
||||
macro Type* Slice2d.get_xy_ref(self, x, y)
|
||||
{
|
||||
return self.ptr + self.inner_len * (y + self.ystart) + self.xstart + x;
|
||||
}
|
||||
|
||||
macro Type* Slice2d.get_coord_ref(self, usz[<2>] coord)
|
||||
{
|
||||
return self.get_xy_ref(coord.x, coord.y);
|
||||
}
|
||||
|
||||
macro void Slice2d.set_coord(self, usz[<2>] coord, Type value)
|
||||
{
|
||||
*self.get_coord_ref(coord) = value;
|
||||
}
|
||||
|
||||
macro void Slice2d.set_xy(self, x, y, Type value)
|
||||
{
|
||||
*self.get_xy_ref(x, y) = value;
|
||||
}
|
||||
|
||||
<*
|
||||
@require y >= 0 && y < self.ylen
|
||||
@require x >= 0 && x < self.xlen
|
||||
|
||||
@@ -4,45 +4,47 @@
|
||||
module std::core::builtin;
|
||||
import libc, std::hash, std::io, std::os::backtrace;
|
||||
|
||||
<*
|
||||
/*
|
||||
Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds.
|
||||
*>
|
||||
*/
|
||||
fault IteratorResult { NO_MORE_ELEMENT }
|
||||
|
||||
<*
|
||||
/*
|
||||
Use `SearchResult` when trying to return a value from some collection but the element is missing.
|
||||
*>
|
||||
*/
|
||||
fault SearchResult { MISSING }
|
||||
|
||||
<*
|
||||
/*
|
||||
Use `CastResult` when an attempt at conversion fails.
|
||||
*>
|
||||
*/
|
||||
fault CastResult { TYPE_MISMATCH }
|
||||
|
||||
|
||||
def VoidFn = fn void();
|
||||
|
||||
<*
|
||||
Stores a variable on the stack, then restores it at the end of the
|
||||
macro scope.
|
||||
|
||||
@param variable `the variable to store and restore`
|
||||
@param #variable `the variable to store and restore`
|
||||
@require values::@is_lvalue(#variable)
|
||||
*>
|
||||
macro void @scope(&variable; @body) @builtin
|
||||
macro void @scope(#variable; @body) @builtin
|
||||
{
|
||||
var temp = *variable;
|
||||
defer *variable = temp;
|
||||
var temp = #variable;
|
||||
defer #variable = temp;
|
||||
@body();
|
||||
}
|
||||
|
||||
<*
|
||||
Swap two variables
|
||||
@require $assignable(*b, $typeof(*a)) && $assignable(*a, $typeof(*b))
|
||||
@require $defined(#a = #b, #b = #a) `The values must be mutually assignable`
|
||||
*>
|
||||
macro void @swap(&a, &b) @builtin
|
||||
macro void @swap(#a, #b) @builtin
|
||||
{
|
||||
var temp = *a;
|
||||
*a = *b;
|
||||
*b = temp;
|
||||
var temp = #a;
|
||||
#a = #b;
|
||||
#b = temp;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -98,7 +100,6 @@ fn void default_panic(String message, String file, String function, uint line) @
|
||||
if (!print_backtrace(message, 2))
|
||||
{
|
||||
io::eprintfn("\nERROR: '%s', in %s (%s:%d)", message, function, file, line);
|
||||
return;
|
||||
}
|
||||
$endif
|
||||
$$trap();
|
||||
@@ -229,7 +230,25 @@ macro enum_by_name($Type, String enum_name) @builtin
|
||||
typeid x = $Type.typeid;
|
||||
foreach (i, name : x.names)
|
||||
{
|
||||
if (name == enum_name) return ($Type)i;
|
||||
if (name == enum_name) return $Type.from_ordinal(i);
|
||||
}
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
<*
|
||||
@param $Type `The type of the enum`
|
||||
@require $Type.kindof == ENUM `Only enums may be used`
|
||||
@require $defined($Type.#value) `Expected '#value' to match an enum associated value`
|
||||
@require $assignable(value, $typeof($Type{}.#value)) `Expected the value to match the type of the associated value`
|
||||
@ensure @typeis(return, $Type)
|
||||
@return! SearchResult.MISSING
|
||||
*>
|
||||
macro @enum_from_value($Type, #value, value) @builtin
|
||||
{
|
||||
usz elements = $Type.elements;
|
||||
foreach (e : $Type.values)
|
||||
{
|
||||
if (e.#value == value) return e;
|
||||
}
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
@@ -348,9 +367,12 @@ macro bool @ok(#expr) @builtin
|
||||
return true;
|
||||
}
|
||||
|
||||
macro char[] @as_char_view(&value) @builtin
|
||||
<*
|
||||
@require $defined(&#value, (char*)&#value) "This must be a value that can be viewed as a char array"
|
||||
*>
|
||||
macro char[] @as_char_view(#value) @builtin
|
||||
{
|
||||
return ((char*)value)[:$sizeof(*value)];
|
||||
return ((char*)&#value)[:$sizeof(#value)];
|
||||
}
|
||||
|
||||
macro isz @str_find(String $string, String $needle) @builtin => $$str_find($string, $needle);
|
||||
@@ -358,21 +380,39 @@ macro String @str_upper(String $str) @builtin => $$str_upper($str);
|
||||
macro String @str_lower(String $str) @builtin => $$str_lower($str);
|
||||
macro uint @str_hash(String $str) @builtin => $$str_hash($str);
|
||||
|
||||
macro uint int.hash(int i) => i;
|
||||
macro uint uint.hash(uint i) => i;
|
||||
macro uint short.hash(short s) => s;
|
||||
macro uint ushort.hash(ushort s) => s;
|
||||
macro uint char.hash(char c) => c;
|
||||
macro uint ichar.hash(ichar c) => c;
|
||||
macro uint long.hash(long i) => (uint)((i >> 32) ^ i);
|
||||
macro uint ulong.hash(ulong i) => (uint)((i >> 32) ^ i);
|
||||
macro uint int128.hash(int128 i) => (uint)((i >> 96) ^ (i >> 64) ^ (i >> 32) ^ i);
|
||||
macro uint uint128.hash(uint128 i) => (uint)((i >> 96) ^ (i >> 64) ^ (i >> 32) ^ i);
|
||||
macro uint bool.hash(bool b) => (uint)b;
|
||||
macro uint typeid.hash(typeid t) => ((ulong)(uptr)t).hash();
|
||||
macro @generic_hash_core(h, value)
|
||||
{
|
||||
h ^= (uint)value; // insert lowest 32 bits
|
||||
h *= 0x96f59e5b; // diffuse them up
|
||||
h ^= h >> 16; // diffuse them down
|
||||
return h;
|
||||
}
|
||||
|
||||
macro @generic_hash(value)
|
||||
{
|
||||
uint h = @generic_hash_core((uint)0x3efd4391, value);
|
||||
$for (var $cnt = 4; $cnt < $sizeof(value); $cnt += 4)
|
||||
value >>= 32; // reduce value
|
||||
h = @generic_hash_core(h, value);
|
||||
$endfor
|
||||
return h;
|
||||
}
|
||||
|
||||
macro uint int.hash(int i) => @generic_hash(i);
|
||||
macro uint uint.hash(uint i) => @generic_hash(i);
|
||||
macro uint short.hash(short s) => @generic_hash(s);
|
||||
macro uint ushort.hash(ushort s) => @generic_hash(s);
|
||||
macro uint char.hash(char c) => @generic_hash(c);
|
||||
macro uint ichar.hash(ichar c) => @generic_hash(c);
|
||||
macro uint long.hash(long i) => @generic_hash(i);
|
||||
macro uint ulong.hash(ulong i) => @generic_hash(i);
|
||||
macro uint int128.hash(int128 i) => @generic_hash(i);
|
||||
macro uint uint128.hash(uint128 i) => @generic_hash(i);
|
||||
macro uint bool.hash(bool b) => @generic_hash(b);
|
||||
macro uint typeid.hash(typeid t) => @generic_hash(((ulong)(uptr)t));
|
||||
macro uint String.hash(String c) => (uint)fnv32a::encode(c);
|
||||
macro uint char[].hash(char[] c) => (uint)fnv32a::encode(c);
|
||||
macro uint void*.hash(void* ptr) => ((ulong)(uptr)ptr).hash();
|
||||
macro uint void*.hash(void* ptr) => @generic_hash(((ulong)(uptr)ptr));
|
||||
|
||||
distinct EmptySlot = void*;
|
||||
const EmptySlot EMPTY_MACRO_SLOT @builtin = null;
|
||||
|
||||
@@ -29,7 +29,7 @@ def CUChar = char;
|
||||
|
||||
def CChar = $typefrom($$C_CHAR_IS_SIGNED ? ichar.typeid : char.typeid);
|
||||
|
||||
enum CBool : CInt
|
||||
enum CBool : char
|
||||
{
|
||||
FALSE,
|
||||
TRUE
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
module std::core::dstring;
|
||||
import std::io;
|
||||
|
||||
distinct DString (OutStream) = void*;
|
||||
distinct DString (OutStream) = DStringOpaque*;
|
||||
distinct DStringOpaque = void;
|
||||
|
||||
const usz MIN_CAPACITY @private = 16;
|
||||
|
||||
@@ -132,7 +133,7 @@ fn usz DString.capacity(self)
|
||||
return self.data().capacity;
|
||||
}
|
||||
|
||||
fn usz DString.len(&self) @dynamic
|
||||
fn usz DString.len(&self) @dynamic @operator(len)
|
||||
{
|
||||
if (!*self) return 0;
|
||||
return self.data().len;
|
||||
@@ -154,6 +155,24 @@ fn String DString.str_view(self)
|
||||
return (String)data.chars[:data.len];
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.len()
|
||||
@require self.data() "Empty string"
|
||||
*>
|
||||
fn char DString.char_at(self, usz index) @operator([])
|
||||
{
|
||||
return self.data().chars[index];
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.len()
|
||||
@require self.data() "Empty string"
|
||||
*>
|
||||
fn char* DString.char_ref(&self, usz index) @operator(&[])
|
||||
{
|
||||
return &self.data().chars[index];
|
||||
}
|
||||
|
||||
fn usz DString.append_utf32(&self, Char32[] chars)
|
||||
{
|
||||
self.reserve(chars.len);
|
||||
@@ -168,7 +187,7 @@ fn usz DString.append_utf32(&self, Char32[] chars)
|
||||
<*
|
||||
@require index < self.len()
|
||||
*>
|
||||
fn void DString.set(self, usz index, char c)
|
||||
fn void DString.set(self, usz index, char c) @operator([]=)
|
||||
{
|
||||
self.data().chars[index] = c;
|
||||
}
|
||||
|
||||
@@ -117,13 +117,13 @@ enum ArchType
|
||||
|
||||
const String COMPILER_BUILD_HASH = $$BUILD_HASH;
|
||||
const String COMPILER_BUILD_DATE = $$BUILD_DATE;
|
||||
const OsType OS_TYPE = (OsType)$$OS_TYPE;
|
||||
const ArchType ARCH_TYPE = (ArchType)$$ARCH_TYPE;
|
||||
const OsType OS_TYPE = OsType.from_ordinal($$OS_TYPE);
|
||||
const ArchType ARCH_TYPE = ArchType.from_ordinal($$ARCH_TYPE);
|
||||
const bool ARCH_32_BIT = $$REGISTER_SIZE == 32;
|
||||
const bool ARCH_64_BIT = $$REGISTER_SIZE == 64;
|
||||
const bool LIBC = $$COMPILER_LIBC_AVAILABLE;
|
||||
const bool NO_LIBC = !$$COMPILER_LIBC_AVAILABLE;
|
||||
const CompilerOptLevel COMPILER_OPT_LEVEL = (CompilerOptLevel)$$COMPILER_OPT_LEVEL;
|
||||
const CompilerOptLevel COMPILER_OPT_LEVEL = CompilerOptLevel.from_ordinal($$COMPILER_OPT_LEVEL);
|
||||
const bool BIG_ENDIAN = $$PLATFORM_BIG_ENDIAN;
|
||||
const bool I128_NATIVE_SUPPORT = $$PLATFORM_I128_SUPPORTED;
|
||||
const bool F16_SUPPORT = $$PLATFORM_F16_SUPPORTED;
|
||||
@@ -135,7 +135,7 @@ const bool BACKTRACE = $$BACKTRACE;
|
||||
const usz LLVM_VERSION = $$LLVM_VERSION;
|
||||
const bool BENCHMARKING = $$BENCHMARKING;
|
||||
const bool TESTING = $$TESTING;
|
||||
const MemoryEnvironment MEMORY_ENV = (MemoryEnvironment)$$MEMORY_ENVIRONMENT;
|
||||
const MemoryEnvironment MEMORY_ENV = MemoryEnvironment.from_ordinal($$MEMORY_ENVIRONMENT);
|
||||
const bool TRACK_MEMORY = DEBUG_SYMBOLS && (COMPILER_SAFE_MODE || TESTING);
|
||||
const bool X86_64 = ARCH_TYPE == X86_64;
|
||||
const bool X86 = ARCH_TYPE == X86;
|
||||
|
||||
@@ -162,42 +162,55 @@ macro @scatter_aligned(ptrvec, value, bool[<*>] mask, usz $alignment)
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] x "The variable or dereferenced pointer to load."
|
||||
@param #x "The variable or dereferenced pointer to load."
|
||||
@param $alignment "The alignment to assume for the load"
|
||||
@return "The value of x"
|
||||
@return "The value of the variable"
|
||||
|
||||
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
|
||||
*>
|
||||
macro @unaligned_load(&x, usz $alignment) @builtin
|
||||
macro @unaligned_load(#x, usz $alignment) @builtin
|
||||
{
|
||||
return $$unaligned_load(x, $alignment);
|
||||
return $$unaligned_load(&#x, $alignment);
|
||||
}
|
||||
|
||||
<*
|
||||
@param [out] x "The variable or dereferenced pointer to store to."
|
||||
@param #x "The variable or dereferenced pointer to store to."
|
||||
@param value "The value to store."
|
||||
@param $alignment "The alignment to assume for the store"
|
||||
@return "The value of x"
|
||||
@return "The value stored"
|
||||
|
||||
@require $assignable(value, $typeof(*x)) : "The value doesn't match the variable"
|
||||
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
|
||||
@require $defined(#x = value) : "The value doesn't match the variable"
|
||||
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||
*>
|
||||
macro @unaligned_store(&x, value, usz $alignment) @builtin
|
||||
macro @unaligned_store(#x, value, usz $alignment) @builtin
|
||||
{
|
||||
return $$unaligned_store(x, ($typeof(*x))value, $alignment);
|
||||
}
|
||||
|
||||
macro @volatile_load(&x) @builtin
|
||||
{
|
||||
return $$volatile_load(x);
|
||||
return $$unaligned_store(&#x, ($typeof(#x))value, $alignment);
|
||||
}
|
||||
|
||||
<*
|
||||
@require $assignable(y, $typeof(*x)) : "The value doesn't match the variable"
|
||||
@param #x "The variable or dereferenced pointer to load."
|
||||
@return "The value of the variable"
|
||||
|
||||
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
|
||||
*>
|
||||
macro @volatile_store(&x, y) @builtin
|
||||
macro @volatile_load(#x) @builtin
|
||||
{
|
||||
return $$volatile_store(x, ($typeof(*x))y);
|
||||
return $$volatile_load(&#x);
|
||||
}
|
||||
|
||||
<*
|
||||
@param #x "The variable or dereferenced pointer to store to."
|
||||
@param value "The value to store."
|
||||
@return "The value stored"
|
||||
|
||||
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
|
||||
@require $defined(#x = value) : "The value doesn't match the variable"
|
||||
*>
|
||||
macro @volatile_store(#x, value) @builtin
|
||||
{
|
||||
return $$volatile_store(&#x, ($typeof(#x))value);
|
||||
}
|
||||
|
||||
enum AtomicOrdering : int
|
||||
@@ -212,34 +225,36 @@ enum AtomicOrdering : int
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] x "the variable or dereferenced pointer to load."
|
||||
@param #x "the variable or dereferenced pointer to load."
|
||||
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@param $volatile "whether the load should be volatile, defaults to 'false'"
|
||||
@return "returns the value of x"
|
||||
|
||||
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
|
||||
@require $ordering != AtomicOrdering.RELEASE "Release ordering is not valid for load."
|
||||
@require $ordering != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid for load."
|
||||
@require types::may_load_atomic($typeof(x)) "Only integer, float and pointers may be used."
|
||||
@require @typekind(x) == POINTER "You can only load from a pointer"
|
||||
@require types::may_load_atomic($typeof(#x)) "Only integer, float and pointers may be used."
|
||||
*>
|
||||
macro @atomic_load(&x, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
|
||||
macro @atomic_load(#x, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
|
||||
{
|
||||
return $$atomic_load(x, $volatile, (int)$ordering);
|
||||
return $$atomic_load(&#x, $volatile, $ordering.ordinal);
|
||||
}
|
||||
|
||||
<*
|
||||
@param [out] x "the variable or dereferenced pointer to store to."
|
||||
@param #x "the variable or dereferenced pointer to store to."
|
||||
@param value "the value to store."
|
||||
@param $ordering "the atomic ordering of the store, defaults to SEQ_CONSISTENT"
|
||||
@param $volatile "whether the store should be volatile, defaults to 'false'"
|
||||
|
||||
@require $ordering != AtomicOrdering.ACQUIRE "Acquire ordering is not valid for store."
|
||||
@require $ordering != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid for store."
|
||||
@require types::may_load_atomic($typeof(x)) "Only integer, float and pointers may be used."
|
||||
@require types::may_load_atomic($typeof(#x)) "Only integer, float and pointers may be used."
|
||||
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
|
||||
@require $defined(#x = value) : "The value doesn't match the variable"
|
||||
*>
|
||||
macro void @atomic_store(&x, value, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
|
||||
macro void @atomic_store(#x, value, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
|
||||
{
|
||||
$$atomic_store(x, value, $volatile, (int)$ordering);
|
||||
$$atomic_store(&#x, value, $volatile, $ordering.ordinal);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -455,19 +470,57 @@ macro void @scoped(Allocator allocator; @body())
|
||||
<*
|
||||
Run the tracking allocator in the scope, then
|
||||
print out stats.
|
||||
|
||||
@param $enabled "Set to false to disable tracking"
|
||||
*>
|
||||
macro void @report_heap_allocs_in_scope(;@body())
|
||||
macro void @report_heap_allocs_in_scope($enabled = true; @body())
|
||||
{
|
||||
TrackingAllocator tracker;
|
||||
tracker.init(allocator::thread_allocator);
|
||||
Allocator old_allocator = allocator::thread_allocator;
|
||||
allocator::thread_allocator = &tracker;
|
||||
defer
|
||||
{
|
||||
allocator::thread_allocator = old_allocator;
|
||||
tracker.print_report();
|
||||
tracker.free();
|
||||
}
|
||||
$if $enabled:
|
||||
TrackingAllocator tracker;
|
||||
tracker.init(allocator::thread_allocator);
|
||||
Allocator old_allocator = allocator::thread_allocator;
|
||||
allocator::thread_allocator = &tracker;
|
||||
defer
|
||||
{
|
||||
allocator::thread_allocator = old_allocator;
|
||||
tracker.print_report();
|
||||
tracker.free();
|
||||
}
|
||||
$endif
|
||||
@body();
|
||||
}
|
||||
|
||||
<*
|
||||
Assert on memory leak in the scope of the macro body.
|
||||
|
||||
@param $report "Set to false to disable memory report"
|
||||
*>
|
||||
macro void @assert_leak($report = true; @body()) @builtin
|
||||
{
|
||||
$if env::DEBUG_SYMBOLS || $feature(MEMORY_ASSERTS):
|
||||
TrackingAllocator tracker;
|
||||
tracker.init(allocator::thread_allocator);
|
||||
Allocator old_allocator = allocator::thread_allocator;
|
||||
allocator::thread_allocator = &tracker;
|
||||
defer
|
||||
{
|
||||
allocator::thread_allocator = old_allocator;
|
||||
defer tracker.free();
|
||||
usz allocated = tracker.allocated();
|
||||
if (allocated)
|
||||
{
|
||||
DString report = dstring::new();
|
||||
defer report.free();
|
||||
$if $report:
|
||||
report.append_char('\n');
|
||||
(void)tracker.fprint_report(&report);
|
||||
$endif
|
||||
assert(allocated == 0, "Memory leak detected"
|
||||
" (%d bytes allocated).%s",
|
||||
allocated, report.str_view());
|
||||
}
|
||||
}
|
||||
$endif
|
||||
@body();
|
||||
}
|
||||
|
||||
@@ -531,19 +584,21 @@ fn void temp_pop(TempState old_state)
|
||||
allocator::thread_temp_allocator = old_state.old;
|
||||
}
|
||||
|
||||
macro void @pool(TempAllocator* #other_temp = null; @body) @builtin
|
||||
<*
|
||||
@require @is_empty_macro_slot(#other_temp) ||| $assignable(#other_temp, Allocator) "Must be an allocator"
|
||||
*>
|
||||
macro void @pool(#other_temp = EMPTY_MACRO_SLOT; @body) @builtin
|
||||
{
|
||||
TempAllocator* current = allocator::temp();
|
||||
var $has_arg = !$is_const(#other_temp);
|
||||
$if $has_arg:
|
||||
$if @is_valid_macro_slot(#other_temp):
|
||||
TempAllocator* original = current;
|
||||
if (current == (void*)#other_temp) current = allocator::temp_allocator_next();
|
||||
if (current == #other_temp.ptr) current = allocator::temp_allocator_next();
|
||||
$endif
|
||||
usz mark = current.used;
|
||||
defer
|
||||
{
|
||||
current.reset(mark);
|
||||
$if $has_arg:
|
||||
$if @is_valid_macro_slot(#other_temp):
|
||||
allocator::thread_temp_allocator = original;
|
||||
$endif;
|
||||
}
|
||||
@@ -774,3 +829,40 @@ fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMEN
|
||||
return allocator::temp().resize(ptr, size, alignment)!!;
|
||||
}
|
||||
|
||||
module std::core::mem @if(env::NO_LIBC);
|
||||
|
||||
fn CInt __memcmp(void* s1, void* s2, usz n) @weak @export("memcmp")
|
||||
{
|
||||
char* p1 = s1;
|
||||
char* p2 = s2;
|
||||
for (usz i = 0; i < n; i++, p1++, p2++)
|
||||
{
|
||||
char c1 = *p1;
|
||||
char c2 = *p2;
|
||||
if (c1 < c2) return -1;
|
||||
if (c1 > c2) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn void* __memset(void* str, CInt c, usz n) @weak @export("memset")
|
||||
{
|
||||
char* p = str;
|
||||
char cc = (char)c;
|
||||
for (usz i = 0; i < n; i++, p++)
|
||||
{
|
||||
*p = cc;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
fn void* __memcpy(void* dst, void* src, usz n) @weak @export("memcpy")
|
||||
{
|
||||
char* d = dst;
|
||||
char* s = src;
|
||||
for (usz i = 0; i < n; i++, d++, s++)
|
||||
{
|
||||
*d = *s;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
@@ -21,20 +21,20 @@ interface Allocator
|
||||
fn void reset(usz mark) @optional;
|
||||
fn usz mark() @optional;
|
||||
<*
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
* @require size > 0
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
@require size > 0
|
||||
*>
|
||||
fn void*! acquire(usz size, AllocInitType init_type, usz alignment = 0);
|
||||
<*
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
* @require ptr != null
|
||||
* @require new_size > 0
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
@require ptr != null
|
||||
@require new_size > 0
|
||||
*>
|
||||
fn void*! resize(void* ptr, usz new_size, usz alignment = 0);
|
||||
<*
|
||||
* @require ptr != null
|
||||
@require ptr != null
|
||||
*>
|
||||
fn void release(void* ptr, bool aligned);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,11 @@ enum X86Feature
|
||||
{
|
||||
ADX,
|
||||
AES,
|
||||
AMX_AVX512,
|
||||
AMX_FP8,
|
||||
AMX_MOVRS,
|
||||
AMX_TF32,
|
||||
AMX_TRANSPOSE,
|
||||
AMX_BF16,
|
||||
AMX_COMPLEX,
|
||||
AMX_FP16,
|
||||
@@ -34,6 +39,8 @@ enum X86Feature
|
||||
AVX,
|
||||
AVX10_1_256,
|
||||
AVX10_1_512,
|
||||
AVX10_2_256,
|
||||
AVX10_2_512,
|
||||
AVX2,
|
||||
AVX5124FMAPS,
|
||||
AVX5124VNNIW,
|
||||
@@ -84,6 +91,7 @@ enum X86Feature
|
||||
MOVBE,
|
||||
MOVDIR64B,
|
||||
MOVDIRI,
|
||||
MOVRS,
|
||||
MWAITX,
|
||||
PCLMUL,
|
||||
PCONFIG,
|
||||
|
||||
@@ -47,6 +47,13 @@ macro int @main_to_int_main_args(#m, int argc, char** argv)
|
||||
return #m(list);
|
||||
}
|
||||
|
||||
macro int @_main_runner(#m, int argc, char** argv)
|
||||
{
|
||||
String[] list = args_to_strings(argc, argv);
|
||||
defer free(list.ptr);
|
||||
return #m(list) ? 0 : 1;
|
||||
}
|
||||
|
||||
macro int @main_to_void_main_args(#m, int argc, char** argv)
|
||||
{
|
||||
String[] list = args_to_strings(argc, argv);
|
||||
@@ -157,6 +164,13 @@ macro int @wmain_to_int_main_args(#m, int argc, Char16** argv)
|
||||
return #m(args);
|
||||
}
|
||||
|
||||
macro int @_wmain_runner(#m, int argc, Char16** argv)
|
||||
{
|
||||
String[] args = wargs_strings(argc, argv);
|
||||
defer release_wargs(args);
|
||||
return #m(args) ? 0 : 1;
|
||||
}
|
||||
|
||||
macro int @wmain_to_void_main_args(#m, int argc, Char16** argv)
|
||||
{
|
||||
String[] args = wargs_strings(argc, argv);
|
||||
|
||||
@@ -22,230 +22,6 @@ struct SliceRaw
|
||||
usz len;
|
||||
}
|
||||
|
||||
def BenchmarkFn = fn void!();
|
||||
|
||||
struct BenchmarkUnit
|
||||
{
|
||||
String name;
|
||||
BenchmarkFn func;
|
||||
}
|
||||
|
||||
fn BenchmarkUnit[] benchmark_collection_create(Allocator allocator = allocator::heap())
|
||||
{
|
||||
BenchmarkFn[] fns = $$BENCHMARK_FNS;
|
||||
String[] names = $$BENCHMARK_NAMES;
|
||||
BenchmarkUnit[] benchmarks = allocator::alloc_array(allocator, BenchmarkUnit, names.len);
|
||||
foreach (i, benchmark : fns)
|
||||
{
|
||||
benchmarks[i] = { names[i], fns[i] };
|
||||
}
|
||||
return benchmarks;
|
||||
}
|
||||
|
||||
const DEFAULT_BENCHMARK_WARMUP_ITERATIONS = 3;
|
||||
const DEFAULT_BENCHMARK_MAX_ITERATIONS = 10000;
|
||||
|
||||
uint benchmark_warmup_iterations @private = DEFAULT_BENCHMARK_WARMUP_ITERATIONS;
|
||||
uint benchmark_max_iterations @private = DEFAULT_BENCHMARK_MAX_ITERATIONS;
|
||||
|
||||
fn void set_benchmark_warmup_iterations(uint value) @builtin
|
||||
{
|
||||
benchmark_warmup_iterations = value;
|
||||
}
|
||||
|
||||
fn void set_benchmark_max_iterations(uint value) @builtin
|
||||
{
|
||||
assert(value > 0);
|
||||
benchmark_max_iterations = value;
|
||||
}
|
||||
|
||||
fn bool run_benchmarks(BenchmarkUnit[] benchmarks)
|
||||
{
|
||||
int benchmarks_passed = 0;
|
||||
int benchmark_count = benchmarks.len;
|
||||
usz max_name;
|
||||
|
||||
foreach (&unit : benchmarks)
|
||||
{
|
||||
if (max_name < unit.name.len) max_name = unit.name.len;
|
||||
}
|
||||
|
||||
usz len = max_name + 9;
|
||||
|
||||
DString name = dstring::temp_with_capacity(64);
|
||||
name.append_repeat('-', len / 2);
|
||||
name.append(" BENCHMARKS ");
|
||||
name.append_repeat('-', len - len / 2);
|
||||
|
||||
io::printn(name);
|
||||
|
||||
name.clear();
|
||||
|
||||
long sys_clock_started;
|
||||
long sys_clock_finished;
|
||||
long sys_clocks;
|
||||
Clock clock;
|
||||
anyfault err;
|
||||
|
||||
foreach(unit : benchmarks)
|
||||
{
|
||||
defer name.clear();
|
||||
name.appendf("Benchmarking %s ", unit.name);
|
||||
name.append_repeat('.', max_name - unit.name.len + 2);
|
||||
io::printf("%s ", name.str_view());
|
||||
|
||||
for (uint i = 0; i < benchmark_warmup_iterations; i++)
|
||||
{
|
||||
err = @catch(unit.func()) @inline;
|
||||
@volatile_load(err);
|
||||
}
|
||||
|
||||
clock = std::time::clock::now();
|
||||
sys_clock_started = $$sysclock();
|
||||
|
||||
for (uint i = 0; i < benchmark_max_iterations; i++)
|
||||
{
|
||||
err = @catch(unit.func()) @inline;
|
||||
@volatile_load(err);
|
||||
}
|
||||
|
||||
sys_clock_finished = $$sysclock();
|
||||
NanoDuration nano_seconds = clock.mark();
|
||||
sys_clocks = sys_clock_finished - sys_clock_started;
|
||||
|
||||
if (err)
|
||||
{
|
||||
io::printfn("[failed] Failed due to: %s", err);
|
||||
continue;
|
||||
}
|
||||
|
||||
io::printfn("[ok] %.2f ns, %.2f CPU's clocks", (float)nano_seconds / benchmark_max_iterations, (float)sys_clocks / benchmark_max_iterations);
|
||||
benchmarks_passed++;
|
||||
}
|
||||
|
||||
io::printfn("\n%d benchmark%s run.\n", benchmark_count, benchmark_count > 1 ? "s" : "");
|
||||
io::printfn("Benchmarks Result: %s. %d passed, %d failed.",
|
||||
benchmarks_passed < benchmark_count ? "FAILED" : "ok",
|
||||
benchmarks_passed,
|
||||
benchmark_count - benchmarks_passed);
|
||||
|
||||
return benchmark_count == benchmarks_passed;
|
||||
}
|
||||
|
||||
fn bool default_benchmark_runner()
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
return run_benchmarks(benchmark_collection_create(allocator::temp()));
|
||||
};
|
||||
}
|
||||
|
||||
def TestFn = fn void!();
|
||||
|
||||
struct TestUnit
|
||||
{
|
||||
String name;
|
||||
TestFn func;
|
||||
}
|
||||
|
||||
fn TestUnit[] test_collection_create(Allocator allocator = allocator::heap())
|
||||
{
|
||||
TestFn[] fns = $$TEST_FNS;
|
||||
String[] names = $$TEST_NAMES;
|
||||
TestUnit[] tests = allocator::alloc_array(allocator, TestUnit, names.len);
|
||||
foreach (i, test : fns)
|
||||
{
|
||||
tests[i] = { names[i], fns[i] };
|
||||
}
|
||||
return tests;
|
||||
}
|
||||
|
||||
struct TestContext
|
||||
{
|
||||
JmpBuf buf;
|
||||
}
|
||||
|
||||
// Sort the tests by their name in ascending order.
|
||||
fn int cmp_test_unit(TestUnit a, TestUnit b)
|
||||
{
|
||||
usz an = a.name.len;
|
||||
usz bn = b.name.len;
|
||||
if (an > bn) @swap(a, b);
|
||||
foreach (i, ac : a.name)
|
||||
{
|
||||
char bc = b.name[i];
|
||||
if (ac != bc) return an > bn ? bc - ac : ac - bc;
|
||||
}
|
||||
return (int)(an - bn);
|
||||
}
|
||||
|
||||
TestContext* test_context @private;
|
||||
|
||||
fn void test_panic(String message, String file, String function, uint line)
|
||||
{
|
||||
io::printn("[error]");
|
||||
io::print("\n Error: ");
|
||||
io::print(message);
|
||||
io::printn();
|
||||
io::printfn(" - in %s %s:%s.\n", function, file, line);
|
||||
libc::longjmp(&test_context.buf, 1);
|
||||
}
|
||||
|
||||
fn bool run_tests(TestUnit[] tests)
|
||||
{
|
||||
usz max_name;
|
||||
foreach (&unit : tests)
|
||||
{
|
||||
if (max_name < unit.name.len) max_name = unit.name.len;
|
||||
}
|
||||
quicksort(tests, &cmp_test_unit);
|
||||
|
||||
TestContext context;
|
||||
test_context = &context;
|
||||
|
||||
PanicFn old_panic = builtin::panic;
|
||||
defer builtin::panic = old_panic;
|
||||
builtin::panic = &test_panic;
|
||||
int tests_passed = 0;
|
||||
int test_count = tests.len;
|
||||
DString name = dstring::temp_with_capacity(64);
|
||||
usz len = max_name + 9;
|
||||
name.append_repeat('-', len / 2);
|
||||
name.append(" TESTS ");
|
||||
name.append_repeat('-', len - len / 2);
|
||||
io::printn(name);
|
||||
name.clear();
|
||||
foreach(unit : tests)
|
||||
{
|
||||
defer name.clear();
|
||||
name.appendf("Testing %s ", unit.name);
|
||||
name.append_repeat('.', max_name - unit.name.len + 2);
|
||||
io::printf("%s ", name.str_view());
|
||||
(void)io::stdout().flush();
|
||||
if (libc::setjmp(&context.buf) == 0)
|
||||
{
|
||||
if (catch err = unit.func())
|
||||
{
|
||||
io::printfn("[failed] Failed due to: %s", err);
|
||||
continue;
|
||||
}
|
||||
io::printn("[ok]");
|
||||
tests_passed++;
|
||||
}
|
||||
}
|
||||
io::printfn("\n%d test%s run.\n", test_count, test_count > 1 ? "s" : "");
|
||||
io::printfn("Test Result: %s. %d passed, %d failed.",
|
||||
tests_passed < test_count ? "FAILED" : "ok", tests_passed, test_count - tests_passed);
|
||||
return test_count == tests_passed;
|
||||
}
|
||||
|
||||
fn bool default_test_runner()
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
return run_tests(test_collection_create(allocator::temp()));
|
||||
};
|
||||
}
|
||||
|
||||
module std::core::runtime @if(WASM_NOLIBC);
|
||||
|
||||
|
||||
177
lib/std/core/runtime_benchmark.c3
Normal file
177
lib/std/core/runtime_benchmark.c3
Normal file
@@ -0,0 +1,177 @@
|
||||
module std::core::runtime;
|
||||
import libc, std::time, std::io, std::sort;
|
||||
|
||||
def BenchmarkFn = fn void!() @if($$OLD_TEST);
|
||||
def BenchmarkFn = fn void() @if(!$$OLD_TEST);
|
||||
|
||||
struct BenchmarkUnit
|
||||
{
|
||||
String name;
|
||||
BenchmarkFn func;
|
||||
}
|
||||
|
||||
fn BenchmarkUnit[] benchmark_collection_create(Allocator allocator = allocator::heap())
|
||||
{
|
||||
BenchmarkFn[] fns = $$BENCHMARK_FNS;
|
||||
String[] names = $$BENCHMARK_NAMES;
|
||||
BenchmarkUnit[] benchmarks = allocator::alloc_array(allocator, BenchmarkUnit, names.len);
|
||||
foreach (i, benchmark : fns)
|
||||
{
|
||||
benchmarks[i] = { names[i], fns[i] };
|
||||
}
|
||||
return benchmarks;
|
||||
}
|
||||
|
||||
const DEFAULT_BENCHMARK_WARMUP_ITERATIONS = 3;
|
||||
const DEFAULT_BENCHMARK_MAX_ITERATIONS = 10000;
|
||||
|
||||
uint benchmark_warmup_iterations @private = DEFAULT_BENCHMARK_WARMUP_ITERATIONS;
|
||||
uint benchmark_max_iterations @private = DEFAULT_BENCHMARK_MAX_ITERATIONS;
|
||||
|
||||
fn void set_benchmark_warmup_iterations(uint value) @builtin
|
||||
{
|
||||
benchmark_warmup_iterations = value;
|
||||
}
|
||||
|
||||
fn void set_benchmark_max_iterations(uint value) @builtin
|
||||
{
|
||||
assert(value > 0);
|
||||
benchmark_max_iterations = value;
|
||||
}
|
||||
|
||||
fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @if($$OLD_TEST)
|
||||
{
|
||||
int benchmarks_passed = 0;
|
||||
int benchmark_count = benchmarks.len;
|
||||
usz max_name;
|
||||
|
||||
foreach (&unit : benchmarks)
|
||||
{
|
||||
if (max_name < unit.name.len) max_name = unit.name.len;
|
||||
}
|
||||
|
||||
usz len = max_name + 9;
|
||||
|
||||
DString name = dstring::temp_with_capacity(64);
|
||||
name.append_repeat('-', len / 2);
|
||||
name.append(" BENCHMARKS ");
|
||||
name.append_repeat('-', len - len / 2);
|
||||
|
||||
io::printn(name);
|
||||
|
||||
name.clear();
|
||||
|
||||
long sys_clock_started;
|
||||
long sys_clock_finished;
|
||||
long sys_clocks;
|
||||
Clock clock;
|
||||
anyfault err;
|
||||
|
||||
foreach(unit : benchmarks)
|
||||
{
|
||||
defer name.clear();
|
||||
name.appendf("Benchmarking %s ", unit.name);
|
||||
name.append_repeat('.', max_name - unit.name.len + 2);
|
||||
io::printf("%s ", name.str_view());
|
||||
|
||||
for (uint i = 0; i < benchmark_warmup_iterations; i++)
|
||||
{
|
||||
err = @catch(unit.func()) @inline;
|
||||
@volatile_load(err);
|
||||
}
|
||||
|
||||
clock = std::time::clock::now();
|
||||
sys_clock_started = $$sysclock();
|
||||
|
||||
for (uint i = 0; i < benchmark_max_iterations; i++)
|
||||
{
|
||||
err = @catch(unit.func()) @inline;
|
||||
@volatile_load(err);
|
||||
}
|
||||
|
||||
sys_clock_finished = $$sysclock();
|
||||
NanoDuration nano_seconds = clock.mark();
|
||||
sys_clocks = sys_clock_finished - sys_clock_started;
|
||||
|
||||
if (err)
|
||||
{
|
||||
io::printfn("[failed] Failed due to: %s", err);
|
||||
continue;
|
||||
}
|
||||
|
||||
io::printfn("[ok] %.2f ns, %.2f CPU's clocks", (float)nano_seconds / benchmark_max_iterations, (float)sys_clocks / benchmark_max_iterations);
|
||||
benchmarks_passed++;
|
||||
}
|
||||
|
||||
io::printfn("\n%d benchmark%s run.\n", benchmark_count, benchmark_count > 1 ? "s" : "");
|
||||
io::printfn("Benchmarks Result: %s. %d passed, %d failed.",
|
||||
benchmarks_passed < benchmark_count ? "FAILED" : "ok",
|
||||
benchmarks_passed,
|
||||
benchmark_count - benchmarks_passed);
|
||||
|
||||
return benchmark_count == benchmarks_passed;
|
||||
}
|
||||
|
||||
fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @if(!$$OLD_TEST)
|
||||
{
|
||||
usz max_name;
|
||||
|
||||
foreach (&unit : benchmarks)
|
||||
{
|
||||
if (max_name < unit.name.len) max_name = unit.name.len;
|
||||
}
|
||||
|
||||
usz len = max_name + 9;
|
||||
|
||||
DString name = dstring::temp_with_capacity(64);
|
||||
name.append_repeat('-', len / 2);
|
||||
name.append(" BENCHMARKS ");
|
||||
name.append_repeat('-', len - len / 2);
|
||||
|
||||
io::printn(name);
|
||||
|
||||
name.clear();
|
||||
|
||||
long sys_clock_started;
|
||||
long sys_clock_finished;
|
||||
long sys_clocks;
|
||||
Clock clock;
|
||||
|
||||
foreach(unit : benchmarks)
|
||||
{
|
||||
defer name.clear();
|
||||
name.appendf("Benchmarking %s ", unit.name);
|
||||
name.append_repeat('.', max_name - unit.name.len + 2);
|
||||
io::printf("%s ", name.str_view());
|
||||
|
||||
for (uint i = 0; i < benchmark_warmup_iterations; i++)
|
||||
{
|
||||
unit.func() @inline;
|
||||
}
|
||||
|
||||
clock = std::time::clock::now();
|
||||
sys_clock_started = $$sysclock();
|
||||
|
||||
for (uint i = 0; i < benchmark_max_iterations; i++)
|
||||
{
|
||||
unit.func() @inline;
|
||||
}
|
||||
|
||||
sys_clock_finished = $$sysclock();
|
||||
NanoDuration nano_seconds = clock.mark();
|
||||
sys_clocks = sys_clock_finished - sys_clock_started;
|
||||
|
||||
io::printfn("[COMPLETE] %.2f ns, %.2f CPU's clocks", (float)nano_seconds / benchmark_max_iterations, (float)sys_clocks / benchmark_max_iterations);
|
||||
}
|
||||
|
||||
io::printfn("\n%d benchmark%s run.\n", benchmarks.len, benchmarks.len > 1 ? "s" : "");
|
||||
return true;
|
||||
}
|
||||
|
||||
fn bool default_benchmark_runner(String[] args)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
return run_benchmarks(benchmark_collection_create(allocator::temp()));
|
||||
};
|
||||
}
|
||||
157
lib/std/core/runtime_test.c3
Normal file
157
lib/std/core/runtime_test.c3
Normal file
@@ -0,0 +1,157 @@
|
||||
// Copyright (c) 2025 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::core::runtime;
|
||||
import libc, std::time, std::io, std::sort;
|
||||
|
||||
def TestFn = fn void!() @if($$OLD_TEST);
|
||||
def TestFn = fn void() @if(!$$OLD_TEST);
|
||||
|
||||
struct TestUnit
|
||||
{
|
||||
String name;
|
||||
TestFn func;
|
||||
}
|
||||
|
||||
fn TestUnit[] test_collection_create(Allocator allocator = allocator::heap())
|
||||
{
|
||||
TestFn[] fns = $$TEST_FNS;
|
||||
String[] names = $$TEST_NAMES;
|
||||
TestUnit[] tests = allocator::alloc_array(allocator, TestUnit, names.len);
|
||||
foreach (i, test : fns)
|
||||
{
|
||||
tests[i] = { names[i], fns[i] };
|
||||
}
|
||||
return tests;
|
||||
}
|
||||
|
||||
struct TestContext
|
||||
{
|
||||
JmpBuf buf;
|
||||
}
|
||||
|
||||
// Sort the tests by their name in ascending order.
|
||||
fn int cmp_test_unit(TestUnit a, TestUnit b)
|
||||
{
|
||||
usz an = a.name.len;
|
||||
usz bn = b.name.len;
|
||||
if (an > bn) @swap(a, b);
|
||||
foreach (i, ac : a.name)
|
||||
{
|
||||
char bc = b.name[i];
|
||||
if (ac != bc) return an > bn ? bc - ac : ac - bc;
|
||||
}
|
||||
return (int)(an - bn);
|
||||
}
|
||||
|
||||
TestContext* test_context @private;
|
||||
|
||||
fn void test_panic(String message, String file, String function, uint line)
|
||||
{
|
||||
io::printn("[error]");
|
||||
io::print("\n Error: ");
|
||||
io::print(message);
|
||||
io::printn();
|
||||
io::printfn(" - in %s %s:%s.\n", function, file, line);
|
||||
libc::longjmp(&test_context.buf, 1);
|
||||
}
|
||||
|
||||
fn bool run_tests(TestUnit[] tests) @if($$OLD_TEST)
|
||||
{
|
||||
usz max_name;
|
||||
foreach (&unit : tests)
|
||||
{
|
||||
if (max_name < unit.name.len) max_name = unit.name.len;
|
||||
}
|
||||
quicksort(tests, &cmp_test_unit);
|
||||
|
||||
TestContext context;
|
||||
test_context = &context;
|
||||
|
||||
PanicFn old_panic = builtin::panic;
|
||||
defer builtin::panic = old_panic;
|
||||
builtin::panic = &test_panic;
|
||||
int tests_passed = 0;
|
||||
int test_count = tests.len;
|
||||
DString name = dstring::temp_with_capacity(64);
|
||||
usz len = max_name + 9;
|
||||
name.append_repeat('-', len / 2);
|
||||
name.append(" TESTS ");
|
||||
name.append_repeat('-', len - len / 2);
|
||||
io::printn(name);
|
||||
name.clear();
|
||||
foreach(unit : tests)
|
||||
{
|
||||
defer name.clear();
|
||||
name.appendf("Testing %s ", unit.name);
|
||||
name.append_repeat('.', max_name - unit.name.len + 2);
|
||||
io::printf("%s ", name.str_view());
|
||||
(void)io::stdout().flush();
|
||||
if (libc::setjmp(&context.buf) == 0)
|
||||
{
|
||||
if (catch err = unit.func())
|
||||
{
|
||||
io::printfn("[failed] Failed due to: %s", err);
|
||||
continue;
|
||||
}
|
||||
io::printn("[ok]");
|
||||
tests_passed++;
|
||||
}
|
||||
}
|
||||
io::printfn("\n%d test%s run.\n", test_count, test_count > 1 ? "s" : "");
|
||||
io::printfn("Test Result: %s. %d passed, %d failed.",
|
||||
tests_passed < test_count ? "FAILED" : "ok", tests_passed, test_count - tests_passed);
|
||||
return test_count == tests_passed;
|
||||
}
|
||||
|
||||
fn bool run_tests(TestUnit[] tests) @if(!$$OLD_TEST)
|
||||
{
|
||||
usz max_name;
|
||||
foreach (&unit : tests)
|
||||
{
|
||||
if (max_name < unit.name.len) max_name = unit.name.len;
|
||||
}
|
||||
quicksort(tests, &cmp_test_unit);
|
||||
|
||||
TestContext context;
|
||||
test_context = &context;
|
||||
|
||||
PanicFn old_panic = builtin::panic;
|
||||
defer builtin::panic = old_panic;
|
||||
builtin::panic = &test_panic;
|
||||
int tests_passed = 0;
|
||||
int test_count = tests.len;
|
||||
DString name = dstring::temp_with_capacity(64);
|
||||
usz len = max_name + 9;
|
||||
name.append_repeat('-', len / 2);
|
||||
name.append(" TESTS ");
|
||||
name.append_repeat('-', len - len / 2);
|
||||
io::printn(name);
|
||||
name.clear();
|
||||
foreach(unit : tests)
|
||||
{
|
||||
defer name.clear();
|
||||
name.appendf("Testing %s ", unit.name);
|
||||
name.append_repeat('.', max_name - unit.name.len + 2);
|
||||
io::printf("%s ", name.str_view());
|
||||
(void)io::stdout().flush();
|
||||
if (libc::setjmp(&context.buf) == 0)
|
||||
{
|
||||
unit.func();
|
||||
io::printn("[ok]");
|
||||
tests_passed++;
|
||||
}
|
||||
}
|
||||
io::printfn("\n%d test%s run.\n", test_count, test_count > 1 ? "s" : "");
|
||||
io::printfn("Test Result: %s. %d passed, %d failed.",
|
||||
tests_passed < test_count ? "FAILED" : "ok", tests_passed, test_count - tests_passed);
|
||||
return test_count == tests_passed;
|
||||
}
|
||||
|
||||
fn bool default_test_runner(String[] args)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
return run_tests(test_collection_create(allocator::temp()));
|
||||
};
|
||||
}
|
||||
@@ -84,7 +84,7 @@ macro bool address_is_poisoned(void* addr)
|
||||
macro void* region_is_poisoned(void* beg, usz size)
|
||||
{
|
||||
$if env::ADDRESS_SANITIZER:
|
||||
return __asan_region_is_poisoned(addr);
|
||||
return __asan_region_is_poisoned(beg, size);
|
||||
$else
|
||||
return null;
|
||||
$endif
|
||||
|
||||
@@ -145,14 +145,40 @@ fn String join_new(String[] s, String joiner, Allocator allocator = allocator::h
|
||||
@return `a substring of the string passed in`
|
||||
*>
|
||||
fn String String.trim(string, String to_trim = "\t\n\r ")
|
||||
{
|
||||
return string.trim_left(to_trim).trim_right(to_trim);
|
||||
}
|
||||
|
||||
<*
|
||||
Remove characters from the front of a string.
|
||||
|
||||
@param [in] string `The string to trim`
|
||||
@param [in] to_trim `The set of characters to trim, defaults to whitespace`
|
||||
@pure
|
||||
@return `a substring of the string passed in`
|
||||
*>
|
||||
fn String String.trim_left(string, String to_trim = "\t\n\r ")
|
||||
{
|
||||
usz start = 0;
|
||||
usz len = string.len;
|
||||
while (start < len && char_in_set(string[start], to_trim)) start++;
|
||||
if (start == len) return string[:0];
|
||||
usz end = len - 1;
|
||||
while (end > start && char_in_set(string[end], to_trim)) end--;
|
||||
return string[start..end];
|
||||
return string[start..];
|
||||
}
|
||||
|
||||
<*
|
||||
Remove characters from the end of a string.
|
||||
|
||||
@param [in] string `The string to trim`
|
||||
@param [in] to_trim `The set of characters to trim, defaults to whitespace`
|
||||
@pure
|
||||
@return `a substring of the string passed in`
|
||||
*>
|
||||
fn String String.trim_right(string, String to_trim = "\t\n\r ")
|
||||
{
|
||||
usz len = string.len;
|
||||
while (len > 0 && char_in_set(string[len - 1], to_trim)) len--;
|
||||
return string[:len];
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -219,12 +245,14 @@ fn String String.strip_end(string, String needle)
|
||||
|
||||
@param [in] s
|
||||
@param [in] needle
|
||||
@param [&inout] allocator "The allocator to use for the String[]"
|
||||
@param max "Max number of elements, 0 means no limit, defaults to 0"
|
||||
@param skip_empty "True to skip empty elements"
|
||||
@param [&inout] allocator "The allocator to use for the String[]"
|
||||
|
||||
@require needle.len > 0 "The needle must be at least 1 character long"
|
||||
@ensure return.len > 0
|
||||
*>
|
||||
fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = allocator::heap())
|
||||
fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = allocator::heap(), bool skip_empty = false)
|
||||
{
|
||||
usz capacity = 16;
|
||||
usz i = 0;
|
||||
@@ -244,6 +272,11 @@ fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = al
|
||||
res = s;
|
||||
no_more = true;
|
||||
}
|
||||
if (!res.len && skip_empty)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == capacity)
|
||||
{
|
||||
capacity *= 2;
|
||||
@@ -261,10 +294,11 @@ fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = al
|
||||
@param [in] s
|
||||
@param [in] needle
|
||||
@param max "Max number of elements, 0 means no limit, defaults to 0"
|
||||
@param skip_empty "True to skip empty elements"
|
||||
@require needle.len > 0 "The needle must be at least 1 character long"
|
||||
@ensure return.len > 0
|
||||
*>
|
||||
fn String[] String.new_split(s, String needle, usz max = 0) => s.split(needle, max, allocator::heap()) @inline;
|
||||
fn String[] String.new_split(s, String needle, usz max = 0, bool skip_empty) => s.split(needle, max, allocator::heap(), skip_empty) @inline;
|
||||
|
||||
<*
|
||||
This function is identical to String.split, but implicitly uses the
|
||||
@@ -273,8 +307,54 @@ fn String[] String.new_split(s, String needle, usz max = 0) => s.split(needle, m
|
||||
@param [in] s
|
||||
@param [in] needle
|
||||
@param max "Max number of elements, 0 means no limit, defaults to 0"
|
||||
@param skip_empty "True to skip empty elements"
|
||||
*>
|
||||
fn String[] String.tsplit(s, String needle, usz max = 0) => s.split(needle, max, allocator::temp()) @inline;
|
||||
fn String[] String.tsplit(s, String needle, usz max = 0, bool skip_empty = false) => s.split(needle, max, allocator::temp(), skip_empty) @inline;
|
||||
|
||||
fault SplitResult { BUFFER_EXCEEDED }
|
||||
|
||||
<*
|
||||
Split a string into parts, e.g "a|b|c" split with "|" yields { "a", "b", "c" }
|
||||
|
||||
@param [in] s
|
||||
@param [in] needle
|
||||
@param [inout] buffer
|
||||
@param max "Max number of elements, 0 means no limit, defaults to 0"
|
||||
@require needle.len > 0 "The needle must be at least 1 character long"
|
||||
@ensure return.len > 0
|
||||
@return! SplitResult.BUFFER_EXCEEDED `If there are more elements than would fit the buffer`
|
||||
*>
|
||||
fn String[]! String.split_to_buffer(s, String needle, String[] buffer, usz max = 0, bool skip_empty = false)
|
||||
{
|
||||
usz max_capacity = buffer.len;
|
||||
usz i = 0;
|
||||
bool no_more = false;
|
||||
while (!no_more)
|
||||
{
|
||||
usz! index = i == max - 1 ? SearchResult.MISSING? : s.index_of(needle);
|
||||
String res @noinit;
|
||||
if (try index)
|
||||
{
|
||||
res = s[:index];
|
||||
s = s[index + needle.len..];
|
||||
}
|
||||
else
|
||||
{
|
||||
res = s;
|
||||
no_more = true;
|
||||
}
|
||||
if (!res.len && skip_empty)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (i == max_capacity)
|
||||
{
|
||||
return SplitResult.BUFFER_EXCEEDED?;
|
||||
}
|
||||
buffer[i++] = res;
|
||||
}
|
||||
return buffer[:i];
|
||||
}
|
||||
|
||||
<*
|
||||
Check if a substring is found in the string.
|
||||
@@ -308,6 +388,29 @@ fn usz! String.index_of_char(s, char needle)
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
<*
|
||||
Find the index of the first incidence of a one of the chars.
|
||||
|
||||
@param [in] s
|
||||
@param [in] needle "The characters to look for"
|
||||
@pure
|
||||
@ensure return < s.len
|
||||
@return "the index of the needle"
|
||||
@return! SearchResult.MISSING "if the needle cannot be found"
|
||||
*>
|
||||
fn usz! String.index_of_chars(String s, char[] needle)
|
||||
{
|
||||
foreach (i, c : s)
|
||||
{
|
||||
foreach (j, pin : needle)
|
||||
{
|
||||
if (c == pin) return i;
|
||||
}
|
||||
}
|
||||
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
<*
|
||||
Find the index of the first incidence of a character.
|
||||
|
||||
@@ -449,6 +552,11 @@ fn String String.tconcat(s1, String s2) => s1.concat(s2, allocator::temp());
|
||||
|
||||
fn ZString String.zstr_tcopy(s) => s.zstr_copy(allocator::temp()) @inline;
|
||||
|
||||
<*
|
||||
Copy this string, by duplicating the string, always adding a zero byte
|
||||
sentinel, so that it safely can be converted to a ZString by a
|
||||
cast.
|
||||
*>
|
||||
fn String String.copy(s, Allocator allocator = allocator::heap())
|
||||
{
|
||||
usz len = s.len;
|
||||
@@ -460,7 +568,7 @@ fn String String.copy(s, Allocator allocator = allocator::heap())
|
||||
|
||||
fn void String.free(&s, Allocator allocator = allocator::heap())
|
||||
{
|
||||
if (!s.len) return;
|
||||
if (!s.ptr) return;
|
||||
allocator::free(allocator, s.ptr);
|
||||
*s = "";
|
||||
}
|
||||
@@ -719,7 +827,12 @@ fn float! String.to_float(s) => s.to_real(float);
|
||||
|
||||
fn Splitter String.splitter(self, String split)
|
||||
{
|
||||
return Splitter { self, split, 0 };
|
||||
return { .string = self, .split = split };
|
||||
}
|
||||
|
||||
fn Splitter String.tokenize(self, String split)
|
||||
{
|
||||
return { .string = self, .split = split, .tokenize = true };
|
||||
}
|
||||
|
||||
struct Splitter
|
||||
@@ -727,6 +840,8 @@ struct Splitter
|
||||
String string;
|
||||
String split;
|
||||
usz current;
|
||||
bool tokenize;
|
||||
int last_index;
|
||||
}
|
||||
|
||||
fn void Splitter.reset(&self)
|
||||
@@ -736,18 +851,22 @@ fn void Splitter.reset(&self)
|
||||
|
||||
fn String! Splitter.next(&self)
|
||||
{
|
||||
usz len = self.string.len;
|
||||
usz current = self.current;
|
||||
if (current >= len) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
String remaining = self.string[current..];
|
||||
usz! next = remaining.index_of(self.split);
|
||||
if (try next)
|
||||
while (true)
|
||||
{
|
||||
defer self.current = current + next + self.split.len;
|
||||
return remaining[:next];
|
||||
usz len = self.string.len;
|
||||
usz current = self.current;
|
||||
if (current >= len) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
String remaining = self.string[current..];
|
||||
usz! next = remaining.index_of(self.split);
|
||||
if (try next)
|
||||
{
|
||||
self.current = current + next + self.split.len;
|
||||
if (!next && self.tokenize) continue;
|
||||
return remaining[:next];
|
||||
}
|
||||
self.current = len;
|
||||
return remaining;
|
||||
}
|
||||
self.current = len;
|
||||
return remaining;
|
||||
}
|
||||
|
||||
macro String new_struct_to_str(x, Allocator allocator = allocator::heap())
|
||||
@@ -757,8 +876,8 @@ macro String new_struct_to_str(x, Allocator allocator = allocator::heap())
|
||||
{
|
||||
s.new_init(allocator: mem);
|
||||
io::fprint(&s, x)!!;
|
||||
return s.copy_str(allocator);
|
||||
};
|
||||
return s.copy_str(allocator);
|
||||
}
|
||||
|
||||
macro String temp_struct_to_str(x) => new_struct_to_str(x, allocator::temp());
|
||||
macro String temp_struct_to_str(x) => new_struct_to_str(x, allocator::temp());
|
||||
|
||||
@@ -8,18 +8,24 @@ fault ConversionResult
|
||||
VALUE_OUT_OF_RANGE,
|
||||
VALUE_OUT_OF_UNSIGNED_RANGE,
|
||||
}
|
||||
|
||||
<*
|
||||
@require $Type.kindof.is_int() || $Type.kindof == TypeKind.ENUM "Argument was not an integer"
|
||||
@require $Type.kindof.is_int() "Type was not an integer"
|
||||
@require v.type.kindof == ENUM "Value was not an enum"
|
||||
*>
|
||||
macro any_to_enum_ordinal(any v, $Type)
|
||||
{
|
||||
return any_to_int(v.as_inner(), $Type);
|
||||
}
|
||||
|
||||
<*
|
||||
@require $Type.kindof.is_int() "Type was not an integer"
|
||||
@require v.type.kindof.is_int() "Value was not an integer"
|
||||
*>
|
||||
macro any_to_int(any v, $Type)
|
||||
{
|
||||
typeid any_type = v.type;
|
||||
TypeKind kind = any_type.kindof;
|
||||
if (kind == TypeKind.ENUM)
|
||||
{
|
||||
any_type = any_type.inner;
|
||||
kind = any_type.kindof;
|
||||
}
|
||||
bool is_mixed_signed = $Type.kindof != any_type.kindof;
|
||||
$Type max = $Type.max;
|
||||
$Type min = $Type.min;
|
||||
|
||||
@@ -16,6 +16,7 @@ macro bool @is_promotable_to_float(#value) @const => types::is_promotable_to_flo
|
||||
macro bool @is_vector(#value) @const => types::is_vector($typeof(#value));
|
||||
macro bool @is_same_vector_type(#value1, #value2) @const => types::is_same_vector_type($typeof(#value1), $typeof(#value2));
|
||||
macro bool @assign_to(#value1, #value2) @const => $assignable(#value1, $typeof(#value2));
|
||||
macro bool @is_lvalue(#value) => $defined(#value = #value);
|
||||
|
||||
macro promote_int(x)
|
||||
{
|
||||
|
||||
@@ -3,232 +3,117 @@ module std::encoding::base32;
|
||||
// This module implements base32 encoding according to RFC 4648
|
||||
// (https://www.rfc-editor.org/rfc/rfc4648)
|
||||
|
||||
distinct Alphabet = inline char[32];
|
||||
|
||||
// Standard base32 Alphabet
|
||||
const Alphabet STD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
|
||||
// Extended Hex Alphabet
|
||||
const Alphabet HEX_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
|
||||
|
||||
const uint MASK @private = 0b11111;
|
||||
const char INVALID @private = 0xff;
|
||||
|
||||
const int STD_PADDING = '=';
|
||||
const int NO_PADDING = -1;
|
||||
|
||||
fault Base32Error
|
||||
struct Base32Alphabet
|
||||
{
|
||||
DUPLICATE_IN_ALPHABET,
|
||||
PADDING_IN_ALPHABET,
|
||||
INVALID_CHARACTER_IN_ALPHABET,
|
||||
DESTINATION_TOO_SMALL,
|
||||
INVALID_PADDING,
|
||||
CORRUPT_INPUT
|
||||
char[32] encoding;
|
||||
char[256] reverse;
|
||||
}
|
||||
|
||||
struct Base32Encoder
|
||||
const char NO_PAD = 0;
|
||||
const char DEFAULT_PAD = '=';
|
||||
|
||||
<*
|
||||
Encode the content of src into a newly allocated string
|
||||
@param [in] src "The input to be encoded."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@param alphabet "The alphabet to use"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "The encoded string."
|
||||
*>
|
||||
fn String! encode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
Alphabet alphabet;
|
||||
int padding;
|
||||
char[] dst = allocator::alloc_array(allocator, char, encode_len(src.len, padding));
|
||||
return encode_buffer(src, dst, padding, alphabet);
|
||||
}
|
||||
|
||||
<*
|
||||
@param encoder "The 32-character alphabet for encoding."
|
||||
@param padding "Set to a negative value to disable padding."
|
||||
@require padding < 256
|
||||
Decode the content of src into a newly allocated char array.
|
||||
@param [in] src "The input to be encoded."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@param alphabet "The alphabet to use"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "The decoded data."
|
||||
*>
|
||||
fn void! Base32Encoder.init(&self, Alphabet encoder = STD_ALPHABET, int padding = STD_PADDING)
|
||||
fn char[]! decode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
encoder.validate(padding)!;
|
||||
*self = { .alphabet = encoder, .padding = padding };
|
||||
char[] dst = allocator::alloc_array(allocator, char, decode_len(src.len, padding));
|
||||
return decode_buffer(src, dst, padding, alphabet);
|
||||
}
|
||||
|
||||
fn String! encode_new(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::heap(), padding, alphabet);
|
||||
fn String! encode_temp(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::temp(), padding, alphabet);
|
||||
fn char[]! decode_new(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::heap(), padding, alphabet);
|
||||
fn char[]! decode_temp(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::temp(), padding, alphabet);
|
||||
|
||||
<*
|
||||
Calculate the length in bytes of the decoded data.
|
||||
@param n "Length in bytes of input."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "Length in bytes of the decoded data."
|
||||
*>
|
||||
fn usz decode_len(usz n, char padding)
|
||||
{
|
||||
if (padding) return (n / 8) * 5;
|
||||
// no padding
|
||||
usz trailing = n % 8;
|
||||
return n / 8 * 5 + (trailing * 5 ) / 8;
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the length in bytes of the encoded data.
|
||||
@param n "Length in bytes on input."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "Length in bytes of the encoded data."
|
||||
*>
|
||||
fn usz Base32Encoder.encode_len(&self, usz n)
|
||||
fn usz encode_len(usz n, char padding)
|
||||
{
|
||||
// A character is encoded into 8 x 5-bit blocks.
|
||||
if (self.padding >= 0)
|
||||
{
|
||||
// with padding
|
||||
return (n + 4) / 5 * 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no padding
|
||||
usz trailing = n % 5;
|
||||
return n / 5 * 8 + (trailing * 8 + 4) / 5;
|
||||
}
|
||||
}
|
||||
if (padding) return (n + 4) / 5 * 8;
|
||||
|
||||
<*
|
||||
Encode the content of src into dst, which must be properly sized.
|
||||
@param [in] src "The input to be encoded."
|
||||
@param [inout] dst "The encoded input."
|
||||
@return "The encoded size."
|
||||
@return! Base32Error.DESTINATION_TOO_SMALL
|
||||
*>
|
||||
fn usz! Base32Encoder.encode(&self, char[] src, char[] dst)
|
||||
{
|
||||
if (src.len == 0) return 0;
|
||||
|
||||
usz n = (src.len / 5) * 5;
|
||||
usz dn = self.encode_len(src.len);
|
||||
if (dst.len < dn) return Base32Error.DESTINATION_TOO_SMALL?;
|
||||
|
||||
uint msb, lsb;
|
||||
for (usz i = 0; i < n; i += 5)
|
||||
{
|
||||
// to fit 40 bits we need two 32-bit uints
|
||||
msb = (uint)src[i] << 24 | (uint)src[i+1] << 16
|
||||
| (uint)src[i+2] << 8 | (uint)src[i+3];
|
||||
lsb = msb << 8 | (uint)src[i+4];
|
||||
|
||||
// now slice them into 5-bit chunks and translate to the
|
||||
// alphabet.
|
||||
dst[0] = self.alphabet[(msb >> 27) & MASK];
|
||||
dst[1] = self.alphabet[(msb >> 22) & MASK];
|
||||
dst[2] = self.alphabet[(msb >> 17) & MASK];
|
||||
dst[3] = self.alphabet[(msb >> 12) & MASK];
|
||||
dst[4] = self.alphabet[(msb >> 7) & MASK];
|
||||
dst[5] = self.alphabet[(msb >> 2) & MASK];
|
||||
dst[6] = self.alphabet[(lsb >> 5) & MASK];
|
||||
dst[7] = self.alphabet[lsb & MASK];
|
||||
|
||||
dst = dst[8..];
|
||||
}
|
||||
|
||||
usz trailing = src.len - n;
|
||||
if (trailing == 0) return dn;
|
||||
|
||||
msb = 0;
|
||||
switch (trailing)
|
||||
{
|
||||
case 4:
|
||||
msb |= (uint)src[n+3];
|
||||
lsb = msb << 8;
|
||||
dst[6] = self.alphabet[(lsb >> 5) & MASK];
|
||||
dst[5] = self.alphabet[(msb >> 2) & MASK];
|
||||
nextcase 3;
|
||||
case 3:
|
||||
msb |= (uint)src[n+2] << 8;
|
||||
dst[4] = self.alphabet[(msb >> 7) & MASK];
|
||||
nextcase 2;
|
||||
case 2:
|
||||
msb |= (uint)src[n+1] << 16;
|
||||
dst[3] = self.alphabet[(msb >> 12) & MASK];
|
||||
dst[2] = self.alphabet[(msb >> 17) & MASK];
|
||||
nextcase 1;
|
||||
case 1:
|
||||
msb |= (uint)src[n] << 24;
|
||||
dst[1] = self.alphabet[(msb >> 22) & MASK];
|
||||
dst[0] = self.alphabet[(msb >> 27) & MASK];
|
||||
}
|
||||
|
||||
// add the padding
|
||||
if (self.padding >= 0)
|
||||
{
|
||||
char pad = (char)self.padding;
|
||||
for (usz i = (trailing * 8 / 5) + 1; i < 8; i++)
|
||||
{
|
||||
dst[i] = pad;
|
||||
}
|
||||
}
|
||||
|
||||
return dn;
|
||||
}
|
||||
|
||||
struct Base32Decoder
|
||||
{
|
||||
Alphabet alphabet;
|
||||
int padding;
|
||||
char[256] reverse;
|
||||
}
|
||||
|
||||
<*
|
||||
@param decoder "The alphabet used for decoding."
|
||||
@param padding "Set to a negative value to disable padding."
|
||||
@require padding < 256
|
||||
*>
|
||||
fn void! Base32Decoder.init(&self, Alphabet decoder = STD_ALPHABET, int padding = STD_PADDING)
|
||||
{
|
||||
decoder.validate(padding)!;
|
||||
*self = { .alphabet = decoder, .padding = padding };
|
||||
|
||||
self.reverse[..] = INVALID;
|
||||
foreach (char i, c : decoder)
|
||||
{
|
||||
self.reverse[c] = i;
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the length in bytes of the decoded data.
|
||||
@param n "Length in bytes of input."
|
||||
@return "Length in bytes of the decoded data."
|
||||
*>
|
||||
fn usz Base32Decoder.decode_len(&self, usz n)
|
||||
{
|
||||
if (self.padding >= 0)
|
||||
{
|
||||
// with padding
|
||||
return (n / 8) * 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no padding
|
||||
usz trailing = n % 8;
|
||||
return n / 8 * 5 + (trailing * 5 ) / 8;
|
||||
}
|
||||
// no padding
|
||||
usz trailing = n % 5;
|
||||
return n / 5 * 8 + (trailing * 8 + 4) / 5;
|
||||
}
|
||||
|
||||
<*
|
||||
Decode the content of src into dst, which must be properly sized.
|
||||
@param src "The input to be decoded."
|
||||
@param dst "The decoded input."
|
||||
@return "The decoded size."
|
||||
@return! Base32Error.DESTINATION_TOO_SMALL, Base32Error.CORRUPT_INPUT
|
||||
@param padding "The padding character or 0 if none"
|
||||
@param alphabet "The alphabet to use"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@require dst.len >= decode_len(src.len, padding) "Destination buffer too small"
|
||||
@return "The resulting dst buffer"
|
||||
@return! DecodingFailure
|
||||
*>
|
||||
fn usz! Base32Decoder.decode(&self, char[] src, char[] dst)
|
||||
fn char[]! decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
if (src.len == 0) return 0;
|
||||
usz dn = self.decode_len(src.len);
|
||||
if (dst.len < dn) return Base32Error.DESTINATION_TOO_SMALL?;
|
||||
|
||||
usz j, n;
|
||||
if (src.len == 0) return dst[:0];
|
||||
char* dst_ptr = dst;
|
||||
usz dn = decode_len(src.len, padding);
|
||||
usz n;
|
||||
char[8] buf;
|
||||
while (src.len > 0 && dst.len > 0)
|
||||
{
|
||||
|
||||
usz i @noinit;
|
||||
// load 8 bytes into buffer
|
||||
for (j = 0; j < 8; j++)
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
if (src.len == 0)
|
||||
{
|
||||
if (self.padding >= 0)
|
||||
{
|
||||
return Base32Error.CORRUPT_INPUT?;
|
||||
}
|
||||
if (padding > 0) return DecodingFailure.INVALID_PADDING?;
|
||||
break;
|
||||
}
|
||||
if (src[0] == (char)self.padding)
|
||||
{
|
||||
break;
|
||||
}
|
||||
buf[j] = self.reverse[src[0]];
|
||||
if (buf[j] == INVALID)
|
||||
{
|
||||
return Base32Error.CORRUPT_INPUT?;
|
||||
}
|
||||
if (src[0] == padding) break;
|
||||
buf[i] = alphabet.reverse[src[0]];
|
||||
if (buf[i] == INVALID) return DecodingFailure.INVALID_CHARACTER?;
|
||||
src = src[1..];
|
||||
}
|
||||
|
||||
// extract 5-bytes from the buffer which contains 8 x 5 bit chunks
|
||||
switch (j)
|
||||
switch (i)
|
||||
{
|
||||
case 8:
|
||||
// |66677777| dst[4]
|
||||
@@ -267,14 +152,195 @@ fn usz! Base32Decoder.decode(&self, char[] src, char[] dst)
|
||||
dst[0] = buf[1] >> 2 | buf[0] << 3;
|
||||
n++;
|
||||
default:
|
||||
return Base32Error.CORRUPT_INPUT?;
|
||||
return DecodingFailure.INVALID_CHARACTER?;
|
||||
}
|
||||
|
||||
if (dst.len < 5) break;
|
||||
dst = dst[5..];
|
||||
}
|
||||
return dst_ptr[:n];
|
||||
}
|
||||
|
||||
return n;
|
||||
<*
|
||||
Encode the content of src into dst, which must be properly sized.
|
||||
@param [in] src "The input to be encoded."
|
||||
@param [inout] dst "The encoded input."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@param alphabet "The alphabet to use"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@require dst.len >= encode_len(src.len, padding) "Destination buffer too small"
|
||||
@return "The encoded size."
|
||||
*>
|
||||
fn String encode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
if (src.len == 0) return (String)dst[:0];
|
||||
|
||||
char* dst_ptr = dst;
|
||||
usz n = (src.len / 5) * 5;
|
||||
usz dn = encode_len(src.len, padding);
|
||||
|
||||
uint msb, lsb;
|
||||
for (usz i = 0; i < n; i += 5)
|
||||
{
|
||||
// to fit 40 bits we need two 32-bit uints
|
||||
msb = (uint)src[i] << 24 | (uint)src[i+1] << 16
|
||||
| (uint)src[i+2] << 8 | (uint)src[i+3];
|
||||
lsb = msb << 8 | (uint)src[i+4];
|
||||
|
||||
// now slice them into 5-bit chunks and translate to the
|
||||
// alphabet.
|
||||
dst[0] = alphabet.encoding[(msb >> 27) & MASK];
|
||||
dst[1] = alphabet.encoding[(msb >> 22) & MASK];
|
||||
dst[2] = alphabet.encoding[(msb >> 17) & MASK];
|
||||
dst[3] = alphabet.encoding[(msb >> 12) & MASK];
|
||||
dst[4] = alphabet.encoding[(msb >> 7) & MASK];
|
||||
dst[5] = alphabet.encoding[(msb >> 2) & MASK];
|
||||
dst[6] = alphabet.encoding[(lsb >> 5) & MASK];
|
||||
dst[7] = alphabet.encoding[lsb & MASK];
|
||||
|
||||
dst = dst[8..];
|
||||
}
|
||||
|
||||
usz trailing = src.len - n;
|
||||
if (trailing == 0) return (String)dst_ptr[:dn];
|
||||
|
||||
msb = 0;
|
||||
switch (trailing)
|
||||
{
|
||||
case 4:
|
||||
msb |= (uint)src[n+3];
|
||||
lsb = msb << 8;
|
||||
dst[6] = alphabet.encoding[(lsb >> 5) & MASK];
|
||||
dst[5] = alphabet.encoding[(msb >> 2) & MASK];
|
||||
nextcase 3;
|
||||
case 3:
|
||||
msb |= (uint)src[n+2] << 8;
|
||||
dst[4] = alphabet.encoding[(msb >> 7) & MASK];
|
||||
nextcase 2;
|
||||
case 2:
|
||||
msb |= (uint)src[n+1] << 16;
|
||||
dst[3] = alphabet.encoding[(msb >> 12) & MASK];
|
||||
dst[2] = alphabet.encoding[(msb >> 17) & MASK];
|
||||
nextcase 1;
|
||||
case 1:
|
||||
msb |= (uint)src[n] << 24;
|
||||
dst[1] = alphabet.encoding[(msb >> 22) & MASK];
|
||||
dst[0] = alphabet.encoding[(msb >> 27) & MASK];
|
||||
}
|
||||
|
||||
// add the padding
|
||||
if (padding > 0)
|
||||
{
|
||||
for (usz i = (trailing * 8 / 5) + 1; i < 8; i++)
|
||||
{
|
||||
dst[i] = padding;
|
||||
}
|
||||
}
|
||||
return (String)dst_ptr[:dn];
|
||||
}
|
||||
|
||||
const uint MASK @private = 0b11111;
|
||||
const char INVALID @private = 0xff;
|
||||
|
||||
const int STD_PADDING = '=';
|
||||
const int NO_PADDING = -1;
|
||||
|
||||
fault Base32Error
|
||||
{
|
||||
DUPLICATE_IN_ALPHABET,
|
||||
PADDING_IN_ALPHABET,
|
||||
INVALID_CHARACTER_IN_ALPHABET,
|
||||
DESTINATION_TOO_SMALL,
|
||||
INVALID_PADDING,
|
||||
CORRUPT_INPUT
|
||||
}
|
||||
|
||||
struct Base32Encoder @deprecated
|
||||
{
|
||||
Base32Alphabet alphabet;
|
||||
char padding;
|
||||
}
|
||||
|
||||
<*
|
||||
@param encoder "The 32-character alphabet for encoding."
|
||||
@param padding "Set to a negative value to disable padding."
|
||||
@require padding < 256
|
||||
*>
|
||||
fn void! Base32Encoder.init(&self, Alphabet encoder = STD_ALPHABET, int padding = STD_PADDING)
|
||||
{
|
||||
encoder.validate(padding)!;
|
||||
*self = { .alphabet = { .encoding = (char[32])encoder }, .padding = padding < 0 ? (char)0 : (char)padding};
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the length in bytes of the encoded data.
|
||||
@param n "Length in bytes on input."
|
||||
@return "Length in bytes of the encoded data."
|
||||
*>
|
||||
fn usz Base32Encoder.encode_len(&self, usz n)
|
||||
{
|
||||
return encode_len(n, self.padding);
|
||||
}
|
||||
|
||||
<*
|
||||
Encode the content of src into dst, which must be properly sized.
|
||||
@param [in] src "The input to be encoded."
|
||||
@param [inout] dst "The encoded input."
|
||||
@return "The encoded size."
|
||||
@return! Base32Error.DESTINATION_TOO_SMALL
|
||||
*>
|
||||
fn usz! Base32Encoder.encode(&self, char[] src, char[] dst)
|
||||
{
|
||||
usz dn = self.encode_len(src.len);
|
||||
if (dst.len < dn) return Base32Error.DESTINATION_TOO_SMALL?;
|
||||
return encode_buffer(src, dst, self.padding, &self.alphabet).len;
|
||||
}
|
||||
|
||||
struct Base32Decoder @deprecated
|
||||
{
|
||||
Base32Alphabet alphabet;
|
||||
char padding;
|
||||
}
|
||||
|
||||
<*
|
||||
@param decoder "The alphabet used for decoding."
|
||||
@param padding "Set to a negative value to disable padding."
|
||||
@require padding < 256
|
||||
*>
|
||||
fn void! Base32Decoder.init(&self, Alphabet decoder = STD_ALPHABET, int padding = STD_PADDING)
|
||||
{
|
||||
decoder.validate(padding)!;
|
||||
*self = { .alphabet = { .encoding = (char[32])decoder }, .padding = padding < 0 ? (char)0 : (char)padding };
|
||||
|
||||
self.alphabet.reverse[..] = INVALID;
|
||||
foreach (char i, c : decoder)
|
||||
{
|
||||
self.alphabet.reverse[c] = i;
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the length in bytes of the decoded data.
|
||||
@param n "Length in bytes of input."
|
||||
@return "Length in bytes of the decoded data."
|
||||
*>
|
||||
fn usz Base32Decoder.decode_len(&self, usz n)
|
||||
{
|
||||
return decode_len(n, self.padding);
|
||||
}
|
||||
|
||||
<*
|
||||
Decode the content of src into dst, which must be properly sized.
|
||||
@param src "The input to be decoded."
|
||||
@param dst "The decoded input."
|
||||
@return "The decoded size."
|
||||
@return! Base32Error.DESTINATION_TOO_SMALL, Base32Error.CORRUPT_INPUT
|
||||
*>
|
||||
fn usz! Base32Decoder.decode(&self, char[] src, char[] dst)
|
||||
{
|
||||
if (src.len == 0) return 0;
|
||||
usz dn = self.decode_len(src.len);
|
||||
if (dst.len < dn) return Base32Error.DESTINATION_TOO_SMALL?;
|
||||
return decode_buffer(src, dst, self.padding, &self.alphabet).len;
|
||||
}
|
||||
|
||||
|
||||
@@ -308,3 +374,33 @@ fn void! Alphabet.validate(&self, int padding)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
distinct Alphabet = char[32];
|
||||
// Standard base32 Alphabet
|
||||
const Alphabet STD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
// Extended Hex Alphabet
|
||||
const Alphabet HEX_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
|
||||
|
||||
const Base32Alphabet STANDARD = {
|
||||
.encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",
|
||||
.reverse = x`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffff1a1b1c1d1e1fffffffffffffffff
|
||||
ff000102030405060708090a0b0c0d0e0f10111213141516171819ffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`
|
||||
};
|
||||
|
||||
const Base32Alphabet HEX = {
|
||||
.encoding = "0123456789ABCDEFGHIJKLMNOPQRSTUV",
|
||||
.reverse = x`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff00010203040506070809ffffffffffff
|
||||
ff0a0b0c0d0e0f101112131415161718191a1b1c1d1e1fffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`
|
||||
};
|
||||
|
||||
@@ -5,14 +5,260 @@ import std::core::bitorder;
|
||||
// Specifically this section:
|
||||
// https://www.rfc-editor.org/rfc/rfc4648#section-4
|
||||
|
||||
const char NO_PAD = 0;
|
||||
const char DEFAULT_PAD = '=';
|
||||
|
||||
struct Base64Alphabet
|
||||
{
|
||||
char[64] encoding;
|
||||
char[256] reverse;
|
||||
}
|
||||
|
||||
const Base64Alphabet STANDARD = {
|
||||
.encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
|
||||
.reverse =
|
||||
x`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffff3effffff3f3435363738393a3b3c3dffffffffffff
|
||||
ff000102030405060708090a0b0c0d0e0f10111213141516171819ffffffffff
|
||||
ff1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233ffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`
|
||||
};
|
||||
|
||||
const Base64Alphabet URL = {
|
||||
.encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
|
||||
.reverse =
|
||||
x`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffff3effff3435363738393a3b3c3dffffffffffff
|
||||
ff000102030405060708090a0b0c0d0e0f10111213141516171819ffffffff3f
|
||||
ff1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233ffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`
|
||||
};
|
||||
|
||||
const STD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
const URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||
|
||||
fn String encode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
char[] dst = allocator::alloc_array(allocator, char, encode_len(src.len, padding));
|
||||
return encode_buffer(src, dst, padding, alphabet);
|
||||
}
|
||||
|
||||
fn char[]! decode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
char[] dst = allocator::alloc_array(allocator, char, decode_len(src.len, padding))!;
|
||||
return decode_buffer(src, dst, padding, alphabet);
|
||||
}
|
||||
|
||||
fn String encode_new(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::heap(), padding, alphabet);
|
||||
fn String encode_temp(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::temp(), padding, alphabet);
|
||||
fn char[]! decode_new(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::heap(), padding, alphabet);
|
||||
fn char[]! decode_temp(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::temp(), padding, alphabet);
|
||||
|
||||
|
||||
<*
|
||||
Calculate the size of the encoded data.
|
||||
@param n "Size of the input to be encoded."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "The size of the input once encoded."
|
||||
*>
|
||||
fn usz encode_len(usz n, char padding)
|
||||
{
|
||||
if (padding) return (n + 2) / 3 * 4;
|
||||
usz trailing = n % 3;
|
||||
return n / 3 * 4 + (trailing * 4 + 2) / 3;
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the size of the decoded data.
|
||||
@param n "Size of the input to be decoded."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "The size of the input once decoded."
|
||||
@return! DecodingFailure.INVALID_PADDING
|
||||
*>
|
||||
fn usz! decode_len(usz n, char padding)
|
||||
{
|
||||
usz dn = n / 4 * 3;
|
||||
usz trailing = n % 4;
|
||||
if (padding)
|
||||
{
|
||||
if (trailing != 0) return DecodingFailure.INVALID_PADDING?;
|
||||
// source size is multiple of 4
|
||||
return dn;
|
||||
}
|
||||
if (trailing == 1) return DecodingFailure.INVALID_PADDING?;
|
||||
return dn + trailing * 3 / 4;
|
||||
}
|
||||
|
||||
<*
|
||||
Encode the content of src into dst, which must be properly sized.
|
||||
@param src "The input to be encoded."
|
||||
@param dst "The encoded input."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@param alphabet "The alphabet to use"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "The encoded size."
|
||||
@return! Base64Error.DESTINATION_TOO_SMALL
|
||||
*>
|
||||
fn String encode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
if (src.len == 0) return (String)dst[:0];
|
||||
usz dn = encode_len(src.len, padding);
|
||||
char* dst_ptr = dst;
|
||||
assert(dst.len >= dn);
|
||||
usz trailing = src.len % 3;
|
||||
char[] src3 = src[:^trailing];
|
||||
|
||||
while (src3.len > 0)
|
||||
{
|
||||
uint group = (uint)src3[0] << 16 | (uint)src3[1] << 8 | (uint)src3[2];
|
||||
dst[0] = alphabet.encoding[group >> 18 & MASK];
|
||||
dst[1] = alphabet.encoding[group >> 12 & MASK];
|
||||
dst[2] = alphabet.encoding[group >> 6 & MASK];
|
||||
dst[3] = alphabet.encoding[group & MASK];
|
||||
dst = dst[4..];
|
||||
src3 = src3[3..];
|
||||
}
|
||||
|
||||
// Encode the remaining bytes according to:
|
||||
// https://www.rfc-editor.org/rfc/rfc4648#section-3.5
|
||||
switch (trailing)
|
||||
{
|
||||
case 1:
|
||||
uint group = (uint)src[^1] << 16;
|
||||
dst[0] = alphabet.encoding[group >> 18 & MASK];
|
||||
dst[1] = alphabet.encoding[group >> 12 & MASK];
|
||||
if (padding > 0)
|
||||
{
|
||||
dst[2] = padding;
|
||||
dst[3] = padding;
|
||||
}
|
||||
case 2:
|
||||
uint group = (uint)src[^2] << 16 | (uint)src[^1] << 8;
|
||||
dst[0] = alphabet.encoding[group >> 18 & MASK];
|
||||
dst[1] = alphabet.encoding[group >> 12 & MASK];
|
||||
dst[2] = alphabet.encoding[group >> 6 & MASK];
|
||||
if (padding > 0)
|
||||
{
|
||||
dst[3] = padding;
|
||||
}
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
return (String)dst_ptr[:dn];
|
||||
}
|
||||
|
||||
<*
|
||||
Decode the content of src into dst, which must be properly sized.
|
||||
@param src "The input to be decoded."
|
||||
@param dst "The decoded input."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@param alphabet "The alphabet to use"
|
||||
@require (decode_len(src.len, padding) ?? 0) <= dst.len "Destination buffer too small"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "The decoded data."
|
||||
@return! DecodingFailure
|
||||
*>
|
||||
fn char[]! decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
if (src.len == 0) return dst[:0];
|
||||
usz dn = decode_len(src.len, padding)!;
|
||||
assert(dst.len >= dn);
|
||||
|
||||
usz trailing = src.len % 4;
|
||||
char* dst_ptr = dst;
|
||||
char[] src4 = src;
|
||||
switch
|
||||
{
|
||||
case !padding:
|
||||
src4 = src[:^trailing];
|
||||
default:
|
||||
// If there is padding, keep the last 4 bytes for later.
|
||||
// NB. src.len >= 4 as decode_len passed
|
||||
trailing = 4;
|
||||
if (src[^1] == padding) src4 = src[:^4];
|
||||
}
|
||||
while (src4.len > 0)
|
||||
{
|
||||
char c0 = alphabet.reverse[src4[0]];
|
||||
char c1 = alphabet.reverse[src4[1]];
|
||||
char c2 = alphabet.reverse[src4[2]];
|
||||
char c3 = alphabet.reverse[src4[3]];
|
||||
switch (0xFF)
|
||||
{
|
||||
case c0:
|
||||
case c1:
|
||||
case c2:
|
||||
case c3:
|
||||
return DecodingFailure.INVALID_CHARACTER?;
|
||||
}
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6 | (uint)c3;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dst[1] = (char)(group >> 8);
|
||||
dst[2] = (char)group;
|
||||
dst = dst[3..];
|
||||
src4 = src4[4..];
|
||||
}
|
||||
|
||||
if (trailing == 0) return dst_ptr[:dn];
|
||||
|
||||
src = src[^trailing..];
|
||||
char c0 = alphabet.reverse[src[0]];
|
||||
char c1 = alphabet.reverse[src[1]];
|
||||
if (c0 == 0xFF || c1 == 0xFF) return DecodingFailure.INVALID_PADDING?;
|
||||
if (!padding)
|
||||
{
|
||||
switch (src.len)
|
||||
{
|
||||
case 2:
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12;
|
||||
dst[0] = (char)(group >> 16);
|
||||
case 3:
|
||||
char c2 = alphabet.reverse[src[2]];
|
||||
if (c2 == 0xFF) return DecodingFailure.INVALID_CHARACTER?;
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dst[1] = (char)(group >> 8);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Valid paddings are:
|
||||
// 2: xx==
|
||||
// 1: xxx=
|
||||
switch (padding)
|
||||
{
|
||||
case src[2]:
|
||||
if (src[3] != padding) return DecodingFailure.INVALID_PADDING?;
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dn -= 2;
|
||||
case src[3]:
|
||||
char c2 = alphabet.reverse[src[2]];
|
||||
if (c2 == 0xFF) return DecodingFailure.INVALID_CHARACTER?;
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dst[1] = (char)(group >> 8);
|
||||
dn -= 1;
|
||||
}
|
||||
}
|
||||
return dst_ptr[:dn];
|
||||
}
|
||||
|
||||
const MASK @private = 0b111111;
|
||||
|
||||
struct Base64Encoder
|
||||
struct Base64Encoder @deprecated
|
||||
{
|
||||
int padding;
|
||||
char padding;
|
||||
String alphabet;
|
||||
}
|
||||
|
||||
@@ -32,10 +278,11 @@ fault Base64Error
|
||||
@require padding < 256
|
||||
@return! Base64Error.DUPLICATE_IN_ALPHABET, Base64Error.PADDING_IN_ALPHABET
|
||||
*>
|
||||
fn void! Base64Encoder.init(&self, String alphabet, int padding = '=')
|
||||
fn Base64Encoder*! Base64Encoder.init(&self, String alphabet, int padding = '=')
|
||||
{
|
||||
check_alphabet(alphabet, padding)!;
|
||||
*self = { .padding = padding, .alphabet = alphabet };
|
||||
*self = { .padding = padding < 0 ? 0 : (char)padding, .alphabet = alphabet };
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -45,9 +292,7 @@ fn void! Base64Encoder.init(&self, String alphabet, int padding = '=')
|
||||
*>
|
||||
fn usz Base64Encoder.encode_len(&self, usz n)
|
||||
{
|
||||
if (self.padding >= 0) return (n + 2) / 3 * 4;
|
||||
usz trailing = n % 3;
|
||||
return n / 3 * 4 + (trailing * 4 + 2) / 3;
|
||||
return encode_len(n, self.padding);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -62,56 +307,18 @@ fn usz! Base64Encoder.encode(&self, char[] src, char[] dst)
|
||||
if (src.len == 0) return 0;
|
||||
usz dn = self.encode_len(src.len);
|
||||
if (dst.len < dn) return Base64Error.DESTINATION_TOO_SMALL?;
|
||||
usz trailing = src.len % 3;
|
||||
char[] src3 = src[:^trailing];
|
||||
|
||||
while (src3.len > 0)
|
||||
{
|
||||
uint group = (uint)src3[0] << 16 | (uint)src3[1] << 8 | (uint)src3[2];
|
||||
dst[0] = self.alphabet[group >> 18 & MASK];
|
||||
dst[1] = self.alphabet[group >> 12 & MASK];
|
||||
dst[2] = self.alphabet[group >> 6 & MASK];
|
||||
dst[3] = self.alphabet[group & MASK];
|
||||
dst = dst[4..];
|
||||
src3 = src3[3..];
|
||||
}
|
||||
|
||||
// Encode the remaining bytes according to:
|
||||
// https://www.rfc-editor.org/rfc/rfc4648#section-3.5
|
||||
switch (trailing)
|
||||
{
|
||||
case 1:
|
||||
uint group = (uint)src[^1] << 16;
|
||||
dst[0] = self.alphabet[group >> 18 & MASK];
|
||||
dst[1] = self.alphabet[group >> 12 & MASK];
|
||||
if (self.padding >= 0)
|
||||
{
|
||||
char pad = (char)self.padding;
|
||||
dst[2] = pad;
|
||||
dst[3] = pad;
|
||||
}
|
||||
case 2:
|
||||
uint group = (uint)src[^2] << 16 | (uint)src[^1] << 8;
|
||||
dst[0] = self.alphabet[group >> 18 & MASK];
|
||||
dst[1] = self.alphabet[group >> 12 & MASK];
|
||||
dst[2] = self.alphabet[group >> 6 & MASK];
|
||||
if (self.padding >= 0)
|
||||
{
|
||||
char pad = (char)self.padding;
|
||||
dst[3] = pad;
|
||||
}
|
||||
}
|
||||
return dn;
|
||||
Base64Alphabet a = { .encoding = self.alphabet[:64] };
|
||||
return encode_buffer(src, dst, self.padding, &a).len;
|
||||
}
|
||||
|
||||
struct Base64Decoder
|
||||
struct Base64Decoder @deprecated
|
||||
{
|
||||
int padding;
|
||||
String alphabet;
|
||||
char[256] reverse;
|
||||
char invalid;
|
||||
char padding;
|
||||
Base64Alphabet encoding;
|
||||
bool init_done;
|
||||
}
|
||||
|
||||
import std;
|
||||
<*
|
||||
@param alphabet "The alphabet used for encoding."
|
||||
@param padding "Set to a negative value to disable padding."
|
||||
@@ -121,29 +328,15 @@ struct Base64Decoder
|
||||
*>
|
||||
fn void! Base64Decoder.init(&self, String alphabet, int padding = '=')
|
||||
{
|
||||
self.init_done = true;
|
||||
check_alphabet(alphabet, padding)!;
|
||||
*self = { .padding = padding, .alphabet = alphabet };
|
||||
*self = { .padding = padding < 0 ? 0 : (char)padding, .encoding.encoding = alphabet[:64] };
|
||||
|
||||
self.encoding.reverse[..] = 0xFF;
|
||||
|
||||
bool[256] checked;
|
||||
foreach (i, c : alphabet)
|
||||
{
|
||||
checked[c] = true;
|
||||
self.reverse[c] = (char)i;
|
||||
}
|
||||
if (padding < 0)
|
||||
{
|
||||
self.invalid = 255;
|
||||
return;
|
||||
}
|
||||
// Find a character for invalid neither in the alphabet nor equal to the padding.
|
||||
char pad = (char)padding;
|
||||
foreach (i, ok : checked)
|
||||
{
|
||||
if (!ok && (char)i != pad)
|
||||
{
|
||||
self.invalid = (char)i;
|
||||
break;
|
||||
}
|
||||
self.encoding.reverse[c] = (char)i;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,19 +348,7 @@ fn void! Base64Decoder.init(&self, String alphabet, int padding = '=')
|
||||
*>
|
||||
fn usz! Base64Decoder.decode_len(&self, usz n)
|
||||
{
|
||||
usz dn = n / 4 * 3;
|
||||
usz trailing = n % 4;
|
||||
if (self.padding >= 0)
|
||||
{
|
||||
if (trailing != 0) return Base64Error.INVALID_PADDING?;
|
||||
// source size is multiple of 4
|
||||
}
|
||||
else
|
||||
{
|
||||
if (trailing == 1) return Base64Error.INVALID_PADDING?;
|
||||
dn += trailing * 3 / 4;
|
||||
}
|
||||
return dn;
|
||||
return decode_len(n, self.padding) ?? Base64Error.INVALID_PADDING?;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -182,86 +363,17 @@ fn usz! Base64Decoder.decode(&self, char[] src, char[] dst)
|
||||
if (src.len == 0) return 0;
|
||||
usz dn = self.decode_len(src.len)!;
|
||||
if (dst.len < dn) return Base64Error.DESTINATION_TOO_SMALL?;
|
||||
|
||||
usz trailing = src.len % 4;
|
||||
char[] src4 = src;
|
||||
switch
|
||||
char[]! decoded = decode_buffer(src, dst, self.padding, &self.encoding);
|
||||
if (catch err = decoded)
|
||||
{
|
||||
case self.padding < 0:
|
||||
src4 = src[:^trailing];
|
||||
case DecodingFailure.INVALID_PADDING:
|
||||
return Base64Error.INVALID_PADDING?;
|
||||
case DecodingFailure.INVALID_CHARACTER:
|
||||
return Base64Error.INVALID_CHARACTER?;
|
||||
default:
|
||||
// If there is padding, keep the last 4 bytes for later.
|
||||
// NB. src.len >= 4 as decode_len passed
|
||||
trailing = 4;
|
||||
char pad = (char)self.padding;
|
||||
if (src[^1] == pad) src4 = src[:^4];
|
||||
return err?;
|
||||
}
|
||||
while (src4.len > 0)
|
||||
{
|
||||
char c0 = self.reverse[src4[0]];
|
||||
char c1 = self.reverse[src4[1]];
|
||||
char c2 = self.reverse[src4[2]];
|
||||
char c3 = self.reverse[src4[3]];
|
||||
switch (self.invalid)
|
||||
{
|
||||
case c0:
|
||||
case c1:
|
||||
case c2:
|
||||
case c3:
|
||||
return Base64Error.INVALID_CHARACTER?;
|
||||
}
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6 | (uint)c3;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dst[1] = (char)(group >> 8);
|
||||
dst[2] = (char)group;
|
||||
dst = dst[3..];
|
||||
src4 = src4[4..];
|
||||
}
|
||||
|
||||
if (trailing == 0) return dn;
|
||||
|
||||
src = src[^trailing..];
|
||||
char c0 = self.reverse[src[0]];
|
||||
char c1 = self.reverse[src[1]];
|
||||
if (c0 == self.invalid || c1 == self.invalid) return Base64Error.INVALID_PADDING?;
|
||||
if (self.padding < 0)
|
||||
{
|
||||
switch (src.len)
|
||||
{
|
||||
case 2:
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12;
|
||||
dst[0] = (char)(group >> 16);
|
||||
case 3:
|
||||
char c2 = self.reverse[src[2]];
|
||||
if (c2 == self.invalid) return Base64Error.INVALID_CHARACTER?;
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dst[1] = (char)(group >> 8);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Valid paddings are:
|
||||
// 2: xx==
|
||||
// 1: xxx=
|
||||
char pad = (char)self.padding;
|
||||
switch (pad)
|
||||
{
|
||||
case src[2]:
|
||||
if (src[3] != pad) return Base64Error.INVALID_PADDING?;
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dn -= 2;
|
||||
case src[3]:
|
||||
char c2 = self.reverse[src[2]];
|
||||
if (c2 == self.invalid) return Base64Error.INVALID_CHARACTER?;
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dst[1] = (char)(group >> 8);
|
||||
dn -= 1;
|
||||
}
|
||||
}
|
||||
return dn;
|
||||
return decoded.len;
|
||||
}
|
||||
|
||||
// Make sure that all bytes in the alphabet are unique and
|
||||
@@ -285,4 +397,5 @@ fn void! check_alphabet(String alphabet, int padding) @local
|
||||
if (checked[c]) return Base64Error.DUPLICATE_IN_ALPHABET?;
|
||||
checked[c] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ fn void! CsvReader.skip_row(self) @maydiscard
|
||||
};
|
||||
}
|
||||
|
||||
macro CsvReader.@each_row(self, int rows = int.max; @body(String[] row))
|
||||
macro void! CsvReader.@each_row(self, int rows = int.max; @body(String[] row)) @maydiscard
|
||||
{
|
||||
InStream stream = self.stream;
|
||||
String sep = self.separator;
|
||||
|
||||
7
lib/std/encoding/encoding.c3
Normal file
7
lib/std/encoding/encoding.c3
Normal file
@@ -0,0 +1,7 @@
|
||||
module std::encoding;
|
||||
|
||||
fault DecodingFailure
|
||||
{
|
||||
INVALID_CHARACTER,
|
||||
INVALID_PADDING,
|
||||
}
|
||||
109
lib/std/encoding/hex.c3
Normal file
109
lib/std/encoding/hex.c3
Normal file
@@ -0,0 +1,109 @@
|
||||
module std::encoding::hex;
|
||||
import std::encoding @norecurse;
|
||||
|
||||
// The implementation is based on https://www.rfc-editor.org/rfc/rfc4648
|
||||
|
||||
fn String encode_buffer(char[] code, char[] buffer)
|
||||
{
|
||||
return (String)buffer[:encode_bytes(code, buffer)];
|
||||
}
|
||||
|
||||
fn char[]! decode_buffer(char[] code, char[] buffer)
|
||||
{
|
||||
return buffer[:decode_bytes(code, buffer)!];
|
||||
}
|
||||
|
||||
fn String encode(char[] code, Allocator allocator)
|
||||
{
|
||||
char[] data = allocator::alloc_array(allocator, char, encode_len(code.len));
|
||||
return (String)data[:encode_bytes(code, data)];
|
||||
}
|
||||
|
||||
fn char[]! decode(char[] code, Allocator allocator)
|
||||
{
|
||||
char[] data = allocator::alloc_array(allocator, char, decode_len(code.len));
|
||||
return data[:decode_bytes(code, data)!];
|
||||
}
|
||||
|
||||
fn String encode_new(char[] code) @inline => encode(code, allocator::heap());
|
||||
fn String encode_temp(char[] code) @inline => encode(code, allocator::temp());
|
||||
fn char[]! decode_new(char[] code) @inline => decode(code, allocator::heap());
|
||||
fn char[]! decode_temp(char[] code) @inline => decode(code, allocator::temp());
|
||||
|
||||
<*
|
||||
Calculate the size of the encoded data.
|
||||
@param n "Size of the input to be encoded."
|
||||
@return "The size of the input once encoded."
|
||||
*>
|
||||
fn usz encode_len(usz n) => n * 2;
|
||||
|
||||
<*
|
||||
Encode the content of src into dst, which must be properly sized.
|
||||
@param src "The input to be encoded."
|
||||
@param dst "The encoded input."
|
||||
@return "The encoded size."
|
||||
@require dst.len >= encode_len(src.len) "Destination array is not large enough"
|
||||
*>
|
||||
fn usz encode_bytes(char[] src, char[] dst)
|
||||
{
|
||||
usz j = 0;
|
||||
foreach (v : src)
|
||||
{
|
||||
dst[j] = HEXALPHABET[v >> 4];
|
||||
dst[j + 1] = HEXALPHABET[v & 0x0f];
|
||||
j = j + 2;
|
||||
}
|
||||
return src.len * 2;
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the size of the decoded data.
|
||||
@param n "Size of the input to be decoded."
|
||||
@return "The size of the input once decoded."
|
||||
*>
|
||||
macro usz decode_len(usz n) => n / 2;
|
||||
|
||||
<*
|
||||
Decodes src into bytes. Returns the actual number of bytes written to dst.
|
||||
|
||||
Expects that src only contains hexadecimal characters and that src has even
|
||||
length.
|
||||
|
||||
@param src "The input to be decoded."
|
||||
@param dst "The decoded input."
|
||||
@require src.len % 2 == 0 "src is not of even length"
|
||||
@require dst.len >= decode_len(src.len) "Destination array is not large enough"
|
||||
@return! DecodingFailure.INVALID_CHARACTER
|
||||
*>
|
||||
fn usz! decode_bytes(char[] src, char[] dst)
|
||||
{
|
||||
usz i;
|
||||
for (usz j = 1; j < src.len; j += 2)
|
||||
{
|
||||
char a = HEXREVERSE[src[j - 1]];
|
||||
char b = HEXREVERSE[src[j]];
|
||||
if (a > 0x0f || b > 0x0f) return DecodingFailure.INVALID_CHARACTER?;
|
||||
dst[i] = (a << 4) | b;
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
const char[*] HEXALPHABET @private = "0123456789abcdef";
|
||||
const char[*] HEXREVERSE @private =
|
||||
x`ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
00010203040506070809ffffffffffff
|
||||
ff0a0b0c0d0e0fffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ff0a0b0c0d0e0fffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff`;
|
||||
@@ -8,7 +8,7 @@ distinct Fnv32a = uint;
|
||||
const FNV32A_START @private = 0x811c9dc5;
|
||||
const FNV32A_MUL @private = 0x01000193;
|
||||
|
||||
macro void @update(uint* &h, char x) @private => *h = (*h * FNV32A_MUL) ^ x;
|
||||
macro void update(h, char x) @private => *h = (*h ^ ($typeof(*h))x) * FNV32A_MUL;
|
||||
|
||||
fn void Fnv32a.init(&self)
|
||||
{
|
||||
@@ -17,17 +17,17 @@ fn void Fnv32a.init(&self)
|
||||
|
||||
fn void Fnv32a.update(&self, char[] data)
|
||||
{
|
||||
uint h = (uint)*self;
|
||||
Fnv32a h = *self;
|
||||
foreach (char x : data)
|
||||
{
|
||||
@update(h, x);
|
||||
update(&h, x);
|
||||
}
|
||||
*self = (Fnv32a)h;
|
||||
*self = h;
|
||||
}
|
||||
|
||||
macro void Fnv32a.update_char(&self, char c)
|
||||
{
|
||||
@update(*self, x);
|
||||
update(self, c);
|
||||
}
|
||||
|
||||
fn uint encode(char[] data)
|
||||
@@ -35,7 +35,7 @@ fn uint encode(char[] data)
|
||||
uint h = FNV32A_START;
|
||||
foreach (char x : data)
|
||||
{
|
||||
@update(h, x);
|
||||
update(&h, x);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ distinct Fnv64a = ulong;
|
||||
const FNV64A_START @private = 0xcbf29ce484222325;
|
||||
const FNV64A_MUL @private = 0x00000100000001b3;
|
||||
|
||||
macro void @update(ulong* &h, char x) @private => *h = (*h * FNV64A_MUL) ^ x;
|
||||
macro void update(h, char x) @private => *h = (*h ^ ($typeof(*h))x) * FNV64A_MUL;
|
||||
|
||||
fn void Fnv64a.init(&self)
|
||||
{
|
||||
@@ -17,17 +17,17 @@ fn void Fnv64a.init(&self)
|
||||
|
||||
fn void Fnv64a.update(&self, char[] data)
|
||||
{
|
||||
ulong h = (ulong)*self;
|
||||
Fnv64a h = *self;
|
||||
foreach (char x : data)
|
||||
{
|
||||
@update(h, x);
|
||||
update(&h, x);
|
||||
}
|
||||
*self = (Fnv64a)h;
|
||||
*self = h;
|
||||
}
|
||||
|
||||
macro void Fnv64a.update_char(&self, char c)
|
||||
{
|
||||
@update(*self, x);
|
||||
update(self, c);
|
||||
}
|
||||
|
||||
fn ulong encode(char[] data)
|
||||
@@ -35,7 +35,7 @@ fn ulong encode(char[] data)
|
||||
ulong h = FNV64A_START;
|
||||
foreach (char x : data)
|
||||
{
|
||||
@update(h, x);
|
||||
update(&h, x);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ macro @h(x, y, z) => (x ^ y) ^ z;
|
||||
macro @h2(x, y, z) => x ^ (y ^ z);
|
||||
macro @i(x, y, z) => y ^ (x | ~z);
|
||||
|
||||
macro @step(#f, &a, b, c, d, ptr, n, t, s)
|
||||
macro @step(#f, a, b, c, d, ptr, n, t, s)
|
||||
{
|
||||
*a += #f(b, c, d) + *(uint *)&ptr[n * 4] + t;
|
||||
*a = (*a << s) | ((*a & 0xffffffff) >> (32 - s));
|
||||
@@ -133,76 +133,76 @@ fn char* body(Md5* ctx, void* data, usz size)
|
||||
saved_d = d;
|
||||
|
||||
/* Round 1 */
|
||||
@step(@f, a, b, c, d, ptr, 0, 0xd76aa478, 7) ;
|
||||
@step(@f, d, a, b, c, ptr, 1, 0xe8c7b756, 12) ;
|
||||
@step(@f, c, d, a, b, ptr, 2, 0x242070db, 17) ;
|
||||
@step(@f, b, c, d, a, ptr, 3, 0xc1bdceee, 22) ;
|
||||
@step(@f, a, b, c, d, ptr, 4, 0xf57c0faf, 7) ;
|
||||
@step(@f, d, a, b, c, ptr, 5, 0x4787c62a, 12) ;
|
||||
@step(@f, c, d, a, b, ptr, 6, 0xa8304613, 17) ;
|
||||
@step(@f, b, c, d, a, ptr, 7, 0xfd469501, 22) ;
|
||||
@step(@f, a, b, c, d, ptr, 8, 0x698098d8, 7) ;
|
||||
@step(@f, d, a, b, c, ptr, 9, 0x8b44f7af, 12) ;
|
||||
@step(@f, c, d, a, b, ptr, 10, 0xffff5bb1, 17);
|
||||
@step(@f, b, c, d, a, ptr, 11, 0x895cd7be, 22);
|
||||
@step(@f, a, b, c, d, ptr, 12, 0x6b901122, 7) ;
|
||||
@step(@f, d, a, b, c, ptr, 13, 0xfd987193, 12);
|
||||
@step(@f, c, d, a, b, ptr, 14, 0xa679438e, 17);
|
||||
@step(@f, b, c, d, a, ptr, 15, 0x49b40821, 22);
|
||||
@step(@f, &a, b, c, d, ptr, 0, 0xd76aa478, 7) ;
|
||||
@step(@f, &d, a, b, c, ptr, 1, 0xe8c7b756, 12) ;
|
||||
@step(@f, &c, d, a, b, ptr, 2, 0x242070db, 17) ;
|
||||
@step(@f, &b, c, d, a, ptr, 3, 0xc1bdceee, 22) ;
|
||||
@step(@f, &a, b, c, d, ptr, 4, 0xf57c0faf, 7) ;
|
||||
@step(@f, &d, a, b, c, ptr, 5, 0x4787c62a, 12) ;
|
||||
@step(@f, &c, d, a, b, ptr, 6, 0xa8304613, 17) ;
|
||||
@step(@f, &b, c, d, a, ptr, 7, 0xfd469501, 22) ;
|
||||
@step(@f, &a, b, c, d, ptr, 8, 0x698098d8, 7) ;
|
||||
@step(@f, &d, a, b, c, ptr, 9, 0x8b44f7af, 12) ;
|
||||
@step(@f, &c, d, a, b, ptr, 10, 0xffff5bb1, 17);
|
||||
@step(@f, &b, c, d, a, ptr, 11, 0x895cd7be, 22);
|
||||
@step(@f, &a, b, c, d, ptr, 12, 0x6b901122, 7) ;
|
||||
@step(@f, &d, a, b, c, ptr, 13, 0xfd987193, 12);
|
||||
@step(@f, &c, d, a, b, ptr, 14, 0xa679438e, 17);
|
||||
@step(@f, &b, c, d, a, ptr, 15, 0x49b40821, 22);
|
||||
|
||||
/* Round 2 */
|
||||
@step(@g, a, b, c, d, ptr, 1, 0xf61e2562, 5) ;
|
||||
@step(@g, d, a, b, c, ptr, 6, 0xc040b340, 9) ;
|
||||
@step(@g, c, d, a, b, ptr, 11, 0x265e5a51, 14);
|
||||
@step(@g, b, c, d, a, ptr, 0, 0xe9b6c7aa, 20) ;
|
||||
@step(@g, a, b, c, d, ptr, 5, 0xd62f105d, 5) ;
|
||||
@step(@g, d, a, b, c, ptr, 10, 0x02441453, 9) ;
|
||||
@step(@g, c, d, a, b, ptr, 15, 0xd8a1e681, 14);
|
||||
@step(@g, b, c, d, a, ptr, 4, 0xe7d3fbc8, 20) ;
|
||||
@step(@g, a, b, c, d, ptr, 9, 0x21e1cde6, 5) ;
|
||||
@step(@g, d, a, b, c, ptr, 14, 0xc33707d6, 9) ;
|
||||
@step(@g, c, d, a, b, ptr, 3, 0xf4d50d87, 14) ;
|
||||
@step(@g, b, c, d, a, ptr, 8, 0x455a14ed, 20) ;
|
||||
@step(@g, a, b, c, d, ptr, 13, 0xa9e3e905, 5) ;
|
||||
@step(@g, d, a, b, c, ptr, 2, 0xfcefa3f8, 9) ;
|
||||
@step(@g, c, d, a, b, ptr, 7, 0x676f02d9, 14) ;
|
||||
@step(@g, b, c, d, a, ptr, 12, 0x8d2a4c8a, 20);
|
||||
@step(@g, &a, b, c, d, ptr, 1, 0xf61e2562, 5) ;
|
||||
@step(@g, &d, a, b, c, ptr, 6, 0xc040b340, 9) ;
|
||||
@step(@g, &c, d, a, b, ptr, 11, 0x265e5a51, 14);
|
||||
@step(@g, &b, c, d, a, ptr, 0, 0xe9b6c7aa, 20) ;
|
||||
@step(@g, &a, b, c, d, ptr, 5, 0xd62f105d, 5) ;
|
||||
@step(@g, &d, a, b, c, ptr, 10, 0x02441453, 9) ;
|
||||
@step(@g, &c, d, a, b, ptr, 15, 0xd8a1e681, 14);
|
||||
@step(@g, &b, c, d, a, ptr, 4, 0xe7d3fbc8, 20) ;
|
||||
@step(@g, &a, b, c, d, ptr, 9, 0x21e1cde6, 5) ;
|
||||
@step(@g, &d, a, b, c, ptr, 14, 0xc33707d6, 9) ;
|
||||
@step(@g, &c, d, a, b, ptr, 3, 0xf4d50d87, 14) ;
|
||||
@step(@g, &b, c, d, a, ptr, 8, 0x455a14ed, 20) ;
|
||||
@step(@g, &a, b, c, d, ptr, 13, 0xa9e3e905, 5) ;
|
||||
@step(@g, &d, a, b, c, ptr, 2, 0xfcefa3f8, 9) ;
|
||||
@step(@g, &c, d, a, b, ptr, 7, 0x676f02d9, 14) ;
|
||||
@step(@g, &b, c, d, a, ptr, 12, 0x8d2a4c8a, 20);
|
||||
|
||||
/* Round 3 */
|
||||
@step(@h, a, b, c, d, ptr, 5, 0xfffa3942, 4);
|
||||
@step(@h2, d, a, b, c, ptr, 8, 0x8771f681, 11);
|
||||
@step(@h, c, d, a, b, ptr, 11, 0x6d9d6122, 16);
|
||||
@step(@h2, b, c, d, a, ptr, 14, 0xfde5380c, 23);
|
||||
@step(@h, a, b, c, d, ptr, 1, 0xa4beea44, 4);
|
||||
@step(@h2, d, a, b, c, ptr, 4, 0x4bdecfa9, 11);
|
||||
@step(@h, c, d, a, b, ptr, 7, 0xf6bb4b60, 16);
|
||||
@step(@h2, b, c, d, a, ptr, 10, 0xbebfbc70, 23);
|
||||
@step(@h, a, b, c, d, ptr, 13, 0x289b7ec6, 4) ;
|
||||
@step(@h2, d, a, b, c, ptr, 0, 0xeaa127fa, 11) ;
|
||||
@step(@h, c, d, a, b, ptr, 3, 0xd4ef3085, 16) ;
|
||||
@step(@h2, b, c, d, a, ptr, 6, 0x04881d05, 23) ;
|
||||
@step(@h, a, b, c, d, ptr, 9, 0xd9d4d039, 4) ;
|
||||
@step(@h2, d, a, b, c, ptr, 12, 0xe6db99e5, 11) ;
|
||||
@step(@h, c, d, a, b, ptr, 15, 0x1fa27cf8, 16) ;
|
||||
@step(@h2, b, c, d, a, ptr, 2, 0xc4ac5665, 23) ;
|
||||
@step(@h, &a, b, c, d, ptr, 5, 0xfffa3942, 4);
|
||||
@step(@h2, &d, a, b, c, ptr, 8, 0x8771f681, 11);
|
||||
@step(@h, &c, d, a, b, ptr, 11, 0x6d9d6122, 16);
|
||||
@step(@h2, &b, c, d, a, ptr, 14, 0xfde5380c, 23);
|
||||
@step(@h, &a, b, c, d, ptr, 1, 0xa4beea44, 4);
|
||||
@step(@h2, &d, a, b, c, ptr, 4, 0x4bdecfa9, 11);
|
||||
@step(@h, &c, d, a, b, ptr, 7, 0xf6bb4b60, 16);
|
||||
@step(@h2, &b, c, d, a, ptr, 10, 0xbebfbc70, 23);
|
||||
@step(@h, &a, b, c, d, ptr, 13, 0x289b7ec6, 4) ;
|
||||
@step(@h2, &d, a, b, c, ptr, 0, 0xeaa127fa, 11) ;
|
||||
@step(@h, &c, d, a, b, ptr, 3, 0xd4ef3085, 16) ;
|
||||
@step(@h2, &b, c, d, a, ptr, 6, 0x04881d05, 23) ;
|
||||
@step(@h, &a, b, c, d, ptr, 9, 0xd9d4d039, 4) ;
|
||||
@step(@h2, &d, a, b, c, ptr, 12, 0xe6db99e5, 11) ;
|
||||
@step(@h, &c, d, a, b, ptr, 15, 0x1fa27cf8, 16) ;
|
||||
@step(@h2, &b, c, d, a, ptr, 2, 0xc4ac5665, 23) ;
|
||||
|
||||
/* Round 4 */
|
||||
@step(@i, a, b, c, d, ptr, 0, 0xf4292244, 6) ;
|
||||
@step(@i, d, a, b, c, ptr, 7, 0x432aff97, 10) ;
|
||||
@step(@i, c, d, a, b, ptr, 14, 0xab9423a7, 15) ;
|
||||
@step(@i, b, c, d, a, ptr, 5, 0xfc93a039, 21) ;
|
||||
@step(@i, a, b, c, d, ptr, 12, 0x655b59c3, 6) ;
|
||||
@step(@i, d, a, b, c, ptr, 3, 0x8f0ccc92, 10) ;
|
||||
@step(@i, c, d, a, b, ptr, 10, 0xffeff47d, 15) ;
|
||||
@step(@i, b, c, d, a, ptr, 1, 0x85845dd1, 21) ;
|
||||
@step(@i, a, b, c, d, ptr, 8, 0x6fa87e4f, 6) ;
|
||||
@step(@i, d, a, b, c, ptr, 15, 0xfe2ce6e0, 10) ;
|
||||
@step(@i, c, d, a, b, ptr, 6, 0xa3014314, 15) ;
|
||||
@step(@i, b, c, d, a, ptr, 13, 0x4e0811a1, 21) ;
|
||||
@step(@i, a, b, c, d, ptr, 4, 0xf7537e82, 6) ;
|
||||
@step(@i, d, a, b, c, ptr, 11, 0xbd3af235, 10) ;
|
||||
@step(@i, c, d, a, b, ptr, 2, 0x2ad7d2bb, 15) ;
|
||||
@step(@i, b, c, d, a, ptr, 9, 0xeb86d391, 21) ;
|
||||
@step(@i, &a, b, c, d, ptr, 0, 0xf4292244, 6) ;
|
||||
@step(@i, &d, a, b, c, ptr, 7, 0x432aff97, 10) ;
|
||||
@step(@i, &c, d, a, b, ptr, 14, 0xab9423a7, 15) ;
|
||||
@step(@i, &b, c, d, a, ptr, 5, 0xfc93a039, 21) ;
|
||||
@step(@i, &a, b, c, d, ptr, 12, 0x655b59c3, 6) ;
|
||||
@step(@i, &d, a, b, c, ptr, 3, 0x8f0ccc92, 10) ;
|
||||
@step(@i, &c, d, a, b, ptr, 10, 0xffeff47d, 15) ;
|
||||
@step(@i, &b, c, d, a, ptr, 1, 0x85845dd1, 21) ;
|
||||
@step(@i, &a, b, c, d, ptr, 8, 0x6fa87e4f, 6) ;
|
||||
@step(@i, &d, a, b, c, ptr, 15, 0xfe2ce6e0, 10) ;
|
||||
@step(@i, &c, d, a, b, ptr, 6, 0xa3014314, 15) ;
|
||||
@step(@i, &b, c, d, a, ptr, 13, 0x4e0811a1, 21) ;
|
||||
@step(@i, &a, b, c, d, ptr, 4, 0xf7537e82, 6) ;
|
||||
@step(@i, &d, a, b, c, ptr, 11, 0xbd3af235, 10) ;
|
||||
@step(@i, &c, d, a, b, ptr, 2, 0x2ad7d2bb, 15) ;
|
||||
@step(@i, &b, c, d, a, ptr, 9, 0xeb86d391, 21) ;
|
||||
|
||||
a += saved_a;
|
||||
b += saved_b;
|
||||
|
||||
@@ -103,13 +103,13 @@ union Long16 @local
|
||||
uint[16] l;
|
||||
}
|
||||
|
||||
macro @blk(&block, i) @local
|
||||
macro blk(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(block, i) @local
|
||||
{
|
||||
$if env::BIG_ENDIAN:
|
||||
return block.l[i];
|
||||
@@ -119,38 +119,38 @@ macro @blk0(&block, i) @local
|
||||
$endif
|
||||
}
|
||||
|
||||
macro @r0(&block, v, &wref, x, y, &z, i) @local
|
||||
macro r0(block, v, wref, x, y, z, i) @local
|
||||
{
|
||||
var w = *wref;
|
||||
*z += ((w & (x ^ y)) ^ y) + @blk0(*block, i) + 0x5A827999 + v.rotl(5);
|
||||
*z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + v.rotl(5);
|
||||
*wref = w.rotl(30);
|
||||
}
|
||||
|
||||
macro @r1(&block, v, &wref, x, y, &z, i) @local
|
||||
macro r1(block, v, wref, x, y, z, i) @local
|
||||
{
|
||||
var w = *wref;
|
||||
*z += ((w & (x ^ y)) ^ y) + @blk(*block, i) + 0x5A827999 + v.rotl(5);
|
||||
*z += ((w & (x ^ y)) ^ y) + blk(block, i) + 0x5A827999 + v.rotl(5);
|
||||
*wref = w.rotl(30);
|
||||
}
|
||||
|
||||
macro @r2(&block, v, &wref, x, y, &z, i) @local
|
||||
macro r2(block, v, wref, x, y, z, i) @local
|
||||
{
|
||||
var w = *wref;
|
||||
*z += (w ^ x ^ y) + @blk(*block, i) + 0x6ED9EBA1 + v.rotl(5);
|
||||
*z += (w ^ x ^ y) + blk(block, i) + 0x6ED9EBA1 + v.rotl(5);
|
||||
*wref = w.rotl(30);
|
||||
}
|
||||
|
||||
macro @r3(&block, v, &wref, x, y, &z, i) @local
|
||||
macro r3(block, v, wref, x, y, z, i) @local
|
||||
{
|
||||
var w = *wref;
|
||||
*z += (((w | x) & y) | (w & x)) + @blk(*block, i) + 0x8F1BBCDC + v.rotl(5);
|
||||
*z += (((w | x) & y) | (w & x)) + blk(block, i) + 0x8F1BBCDC + v.rotl(5);
|
||||
*wref = w.rotl(30);
|
||||
}
|
||||
|
||||
macro @r4(&block, v, &wref, x, y, &z, i) @local
|
||||
macro r4(block, v, wref, x, y, z, i) @local
|
||||
{
|
||||
var w = *wref;
|
||||
*z += (w ^ x ^ y) + @blk(*block, i) + 0xCA62C1D6 + v.rotl(5);
|
||||
*z += (w ^ x ^ y) + blk(block, i) + 0xCA62C1D6 + v.rotl(5);
|
||||
*wref = w.rotl(30);
|
||||
}
|
||||
|
||||
@@ -167,86 +167,86 @@ fn void sha1_transform(uint* state, char* buffer) @local
|
||||
uint c = state[2];
|
||||
uint d = state[3];
|
||||
uint e = state[4];
|
||||
@r0(block, a, b, c, d, e, 0);
|
||||
@r0(block, e, a, b, c, d, 1);
|
||||
@r0(block, d, e, a, b, c, 2);
|
||||
@r0(block, c, d, e, a, b, 3);
|
||||
@r0(block, b, c, d, e, a, 4);
|
||||
@r0(block, a, b, c, d, e, 5);
|
||||
@r0(block, e, a, b, c, d, 6);
|
||||
@r0(block, d, e, a, b, c, 7);
|
||||
@r0(block, c, d, e, a, b, 8);
|
||||
@r0(block, b, c, d, e, a, 9);
|
||||
@r0(block, a, b, c, d, e, 10);
|
||||
@r0(block, e, a, b, c, d, 11);
|
||||
@r0(block, d, e, a, b, c, 12);
|
||||
@r0(block, c, d, e, a, b, 13);
|
||||
@r0(block, b, c, d, e, a, 14);
|
||||
@r0(block, a, b, c, d, e, 15);
|
||||
@r1(block, e, a, b, c, d, 16);
|
||||
@r1(block, d, e, a, b, c, 17);
|
||||
@r1(block, c, d, e, a, b, 18);
|
||||
@r1(block, b, c, d, e, a, 19);
|
||||
@r2(block, a, b, c, d, e, 20);
|
||||
@r2(block, e, a, b, c, d, 21);
|
||||
@r2(block, d, e, a, b, c, 22);
|
||||
@r2(block, c, d, e, a, b, 23);
|
||||
@r2(block, b, c, d, e, a, 24);
|
||||
@r2(block, a, b, c, d, e, 25);
|
||||
@r2(block, e, a, b, c, d, 26);
|
||||
@r2(block, d, e, a, b, c, 27);
|
||||
@r2(block, c, d, e, a, b, 28);
|
||||
@r2(block, b, c, d, e, a, 29);
|
||||
@r2(block, a, b, c, d, e, 30);
|
||||
@r2(block, e, a, b, c, d, 31);
|
||||
@r2(block, d, e, a, b, c, 32);
|
||||
@r2(block, c, d, e, a, b, 33);
|
||||
@r2(block, b, c, d, e, a, 34);
|
||||
@r2(block, a, b, c, d, e, 35);
|
||||
@r2(block, e, a, b, c, d, 36);
|
||||
@r2(block, d, e, a, b, c, 37);
|
||||
@r2(block, c, d, e, a, b, 38);
|
||||
@r2(block, b, c, d, e, a, 39);
|
||||
@r3(block, a, b, c, d, e, 40);
|
||||
@r3(block, e, a, b, c, d, 41);
|
||||
@r3(block, d, e, a, b, c, 42);
|
||||
@r3(block, c, d, e, a, b, 43);
|
||||
@r3(block, b, c, d, e, a, 44);
|
||||
@r3(block, a, b, c, d, e, 45);
|
||||
@r3(block, e, a, b, c, d, 46);
|
||||
@r3(block, d, e, a, b, c, 47);
|
||||
@r3(block, c, d, e, a, b, 48);
|
||||
@r3(block, b, c, d, e, a, 49);
|
||||
@r3(block, a, b, c, d, e, 50);
|
||||
@r3(block, e, a, b, c, d, 51);
|
||||
@r3(block, d, e, a, b, c, 52);
|
||||
@r3(block, c, d, e, a, b, 53);
|
||||
@r3(block, b, c, d, e, a, 54);
|
||||
@r3(block, a, b, c, d, e, 55);
|
||||
@r3(block, e, a, b, c, d, 56);
|
||||
@r3(block, d, e, a, b, c, 57);
|
||||
@r3(block, c, d, e, a, b, 58);
|
||||
@r3(block, b, c, d, e, a, 59);
|
||||
@r4(block, a, b, c, d, e, 60);
|
||||
@r4(block, e, a, b, c, d, 61);
|
||||
@r4(block, d, e, a, b, c, 62);
|
||||
@r4(block, c, d, e, a, b, 63);
|
||||
@r4(block, b, c, d, e, a, 64);
|
||||
@r4(block, a, b, c, d, e, 65);
|
||||
@r4(block, e, a, b, c, d, 66);
|
||||
@r4(block, d, e, a, b, c, 67);
|
||||
@r4(block, c, d, e, a, b, 68);
|
||||
@r4(block, b, c, d, e, a, 69);
|
||||
@r4(block, a, b, c, d, e, 70);
|
||||
@r4(block, e, a, b, c, d, 71);
|
||||
@r4(block, d, e, a, b, c, 72);
|
||||
@r4(block, c, d, e, a, b, 73);
|
||||
@r4(block, b, c, d, e, a, 74);
|
||||
@r4(block, a, b, c, d, e, 75);
|
||||
@r4(block, e, a, b, c, d, 76);
|
||||
@r4(block, d, e, a, b, c, 77);
|
||||
@r4(block, c, d, e, a, b, 78);
|
||||
@r4(block, b, c, d, e, a, 79);
|
||||
r0(&block, a, &b, c, d, &e, 0);
|
||||
r0(&block, e, &a, b, c, &d, 1);
|
||||
r0(&block, d, &e, a, b, &c, 2);
|
||||
r0(&block, c, &d, e, a, &b, 3);
|
||||
r0(&block, b, &c, d, e, &a, 4);
|
||||
r0(&block, a, &b, c, d, &e, 5);
|
||||
r0(&block, e, &a, b, c, &d, 6);
|
||||
r0(&block, d, &e, a, b, &c, 7);
|
||||
r0(&block, c, &d, e, a, &b, 8);
|
||||
r0(&block, b, &c, d, e, &a, 9);
|
||||
r0(&block, a, &b, c, d, &e, 10);
|
||||
r0(&block, e, &a, b, c, &d, 11);
|
||||
r0(&block, d, &e, a, b, &c, 12);
|
||||
r0(&block, c, &d, e, a, &b, 13);
|
||||
r0(&block, b, &c, d, e, &a, 14);
|
||||
r0(&block, a, &b, c, d, &e, 15);
|
||||
r1(&block, e, &a, b, c, &d, 16);
|
||||
r1(&block, d, &e, a, b, &c, 17);
|
||||
r1(&block, c, &d, e, a, &b, 18);
|
||||
r1(&block, b, &c, d, e, &a, 19);
|
||||
r2(&block, a, &b, c, d, &e, 20);
|
||||
r2(&block, e, &a, b, c, &d, 21);
|
||||
r2(&block, d, &e, a, b, &c, 22);
|
||||
r2(&block, c, &d, e, a, &b, 23);
|
||||
r2(&block, b, &c, d, e, &a, 24);
|
||||
r2(&block, a, &b, c, d, &e, 25);
|
||||
r2(&block, e, &a, b, c, &d, 26);
|
||||
r2(&block, d, &e, a, b, &c, 27);
|
||||
r2(&block, c, &d, e, a, &b, 28);
|
||||
r2(&block, b, &c, d, e, &a, 29);
|
||||
r2(&block, a, &b, c, d, &e, 30);
|
||||
r2(&block, e, &a, b, c, &d, 31);
|
||||
r2(&block, d, &e, a, b, &c, 32);
|
||||
r2(&block, c, &d, e, a, &b, 33);
|
||||
r2(&block, b, &c, d, e, &a, 34);
|
||||
r2(&block, a, &b, c, d, &e, 35);
|
||||
r2(&block, e, &a, b, c, &d, 36);
|
||||
r2(&block, d, &e, a, b, &c, 37);
|
||||
r2(&block, c, &d, e, a, &b, 38);
|
||||
r2(&block, b, &c, d, e, &a, 39);
|
||||
r3(&block, a, &b, c, d, &e, 40);
|
||||
r3(&block, e, &a, b, c, &d, 41);
|
||||
r3(&block, d, &e, a, b, &c, 42);
|
||||
r3(&block, c, &d, e, a, &b, 43);
|
||||
r3(&block, b, &c, d, e, &a, 44);
|
||||
r3(&block, a, &b, c, d, &e, 45);
|
||||
r3(&block, e, &a, b, c, &d, 46);
|
||||
r3(&block, d, &e, a, b, &c, 47);
|
||||
r3(&block, c, &d, e, a, &b, 48);
|
||||
r3(&block, b, &c, d, e, &a, 49);
|
||||
r3(&block, a, &b, c, d, &e, 50);
|
||||
r3(&block, e, &a, b, c, &d, 51);
|
||||
r3(&block, d, &e, a, b, &c, 52);
|
||||
r3(&block, c, &d, e, a, &b, 53);
|
||||
r3(&block, b, &c, d, e, &a, 54);
|
||||
r3(&block, a, &b, c, d, &e, 55);
|
||||
r3(&block, e, &a, b, c, &d, 56);
|
||||
r3(&block, d, &e, a, b, &c, 57);
|
||||
r3(&block, c, &d, e, a, &b, 58);
|
||||
r3(&block, b, &c, d, e, &a, 59);
|
||||
r4(&block, a, &b, c, d, &e, 60);
|
||||
r4(&block, e, &a, b, c, &d, 61);
|
||||
r4(&block, d, &e, a, b, &c, 62);
|
||||
r4(&block, c, &d, e, a, &b, 63);
|
||||
r4(&block, b, &c, d, e, &a, 64);
|
||||
r4(&block, a, &b, c, d, &e, 65);
|
||||
r4(&block, e, &a, b, c, &d, 66);
|
||||
r4(&block, d, &e, a, b, &c, 67);
|
||||
r4(&block, c, &d, e, a, &b, 68);
|
||||
r4(&block, b, &c, d, e, &a, 69);
|
||||
r4(&block, a, &b, c, d, &e, 70);
|
||||
r4(&block, e, &a, b, c, &d, 71);
|
||||
r4(&block, d, &e, a, b, &c, 72);
|
||||
r4(&block, c, &d, e, a, &b, 73);
|
||||
r4(&block, b, &c, d, e, &a, 74);
|
||||
r4(&block, a, &b, c, d, &e, 75);
|
||||
r4(&block, e, &a, b, c, &d, 76);
|
||||
r4(&block, d, &e, a, b, &c, 77);
|
||||
r4(&block, c, &d, e, a, &b, 78);
|
||||
r4(&block, b, &c, d, e, &a, 79);
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
|
||||
@@ -45,6 +45,10 @@ struct BitWriter
|
||||
uint len;
|
||||
}
|
||||
|
||||
// c3 doesn't allow to shift more than bit width of a variable,
|
||||
// so use closest byte boundary of 24 instead of 32
|
||||
const int WRITER_BITS = 24;
|
||||
|
||||
fn void BitWriter.init(&self, OutStream byte_writer)
|
||||
{
|
||||
*self = { .writer = byte_writer };
|
||||
@@ -53,7 +57,9 @@ fn void BitWriter.init(&self, OutStream byte_writer)
|
||||
fn void! BitWriter.flush(&self)
|
||||
{
|
||||
if (self.len == 0) return;
|
||||
uint bits = self.bits << (32 - self.len);
|
||||
|
||||
int padding = ($sizeof(self.bits) * 8 - self.len);
|
||||
uint bits = self.bits << padding;
|
||||
uint n = (self.len + 7) / 8;
|
||||
char[4] buffer;
|
||||
bitorder::write(bits, &buffer, UIntBE);
|
||||
@@ -62,24 +68,27 @@ fn void! BitWriter.flush(&self)
|
||||
}
|
||||
|
||||
<*
|
||||
@require nbits <= 8
|
||||
@require nbits <= 32
|
||||
*>
|
||||
fn void! BitWriter.write_bits(&self, uint bits, uint nbits)
|
||||
{
|
||||
if (nbits == 0) return;
|
||||
uint n = self.len + nbits;
|
||||
uint to_write = n / 8;
|
||||
uint left = n % 8;
|
||||
if (to_write > 0)
|
||||
while (self.len + nbits > WRITER_BITS)
|
||||
{
|
||||
ulong lbits;
|
||||
if (self.len > 0) lbits = (ulong)self.bits << (64 - self.len);
|
||||
lbits |= (ulong)(bits >> left) << (64 - (n - left));
|
||||
char[8] buffer;
|
||||
bitorder::write(lbits, &buffer, ULongBE);
|
||||
io::write_all(self.writer, buffer[:to_write])!;
|
||||
uint to_push = WRITER_BITS - self.len;
|
||||
uint bits_to_push = (bits >> (nbits - to_push)) & ((1 << to_push) - 1);
|
||||
|
||||
self.bits <<= to_push;
|
||||
self.bits |= bits_to_push;
|
||||
self.len += to_push;
|
||||
nbits -= to_push;
|
||||
|
||||
self.flush()!;
|
||||
}
|
||||
self.bits <<= left;
|
||||
self.bits |= bits & ((1 << left) - 1);
|
||||
self.len = left;
|
||||
|
||||
if (nbits == 0) return;
|
||||
|
||||
self.bits <<= nbits;
|
||||
self.bits |= bits & ((1 << nbits) - 1);
|
||||
self.len += nbits;
|
||||
}
|
||||
@@ -182,6 +182,17 @@ fn char[]! load_temp(String filename)
|
||||
return load_new(filename, allocator::temp());
|
||||
}
|
||||
|
||||
fn void! save(String filename, char[] data)
|
||||
{
|
||||
File file = open(filename, "wb")!;
|
||||
defer (void)file.close();
|
||||
while (data.len)
|
||||
{
|
||||
usz written = file.write(data)!;
|
||||
data = data[written..];
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.file `File must be initialized`
|
||||
*>
|
||||
|
||||
@@ -28,7 +28,8 @@ macro bool is_struct_with_default_print($Type)
|
||||
{
|
||||
return $Type.kindof == STRUCT
|
||||
&&& !$defined($Type.to_format)
|
||||
&&& !$defined($Type.to_new_string);
|
||||
&&& !$defined($Type.to_new_string)
|
||||
&&& !$defined($Type.to_string);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -194,7 +195,7 @@ fn usz! Formatter.out_str(&self, any arg) @private
|
||||
switch (arg.type.kindof)
|
||||
{
|
||||
case ENUM:
|
||||
usz i = types::any_to_int(arg, usz)!!;
|
||||
usz i = types::any_to_enum_ordinal(arg, usz)!!;
|
||||
assert(i < arg.type.names.len, "Illegal enum value found, numerical value was %d.", i);
|
||||
return self.out_substr(arg.type.names[i]);
|
||||
case STRUCT:
|
||||
@@ -237,8 +238,7 @@ fn usz! Formatter.out_str(&self, any arg) @private
|
||||
self.width = width;
|
||||
}
|
||||
self.width = 0;
|
||||
self.out_substr("0x")!;
|
||||
return self.ntoa_any(arg, 16);
|
||||
return self.out_substr("0x")! + self.ntoa_any(arg, 16);
|
||||
case ARRAY:
|
||||
// this is SomeType[*] so grab the "SomeType"
|
||||
PrintFlags flags = self.flags;
|
||||
@@ -484,11 +484,14 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys)
|
||||
total_len += self.pad(' ', self.width, len)!;
|
||||
continue;
|
||||
}
|
||||
OutputFn out_fn = self.out_fn;
|
||||
self.out_fn = (OutputFn)&out_null_fn;
|
||||
usz len = self.out_str(current)!;
|
||||
self.out_fn = out_fn;
|
||||
total_len += self.pad(' ', self.width, len)!;
|
||||
if (self.width)
|
||||
{
|
||||
OutputFn out_fn = self.out_fn;
|
||||
self.out_fn = (OutputFn)&out_null_fn;
|
||||
usz len = self.out_str(current)!;
|
||||
self.out_fn = out_fn;
|
||||
total_len += self.pad(' ', self.width, len)!;
|
||||
}
|
||||
total_len += self.out_str(current)!;
|
||||
continue;
|
||||
case 'p':
|
||||
@@ -529,4 +532,4 @@ fn usz! Formatter.print(&self, String str)
|
||||
}
|
||||
foreach (c : str) self.out(c)!;
|
||||
return self.idx;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,7 +215,6 @@ fn usz! Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
|
||||
if (!self.flags.left) len += self.pad(' ', self.width, 3 + pl)!;
|
||||
String s = self.flags.uppercase ? "INF" : "inf";
|
||||
if (math::is_nan(y)) s = self.flags.uppercase ? "NAN" : "nan";
|
||||
len += s.len;
|
||||
if (pl) len += self.out(is_neg ? '-' : '+')!;
|
||||
len += self.out_chars(s)!;
|
||||
if (self.flags.left) len += self.pad(' ', self.width, 3 + pl)!;
|
||||
@@ -606,6 +605,10 @@ fn usz! Formatter.ntoa_any(&self, any arg, uint base) @private
|
||||
|
||||
fn usz! Formatter.out_char(&self, any arg) @private
|
||||
{
|
||||
if (!arg.type.kindof.is_int())
|
||||
{
|
||||
return self.out_substr("<NOT CHAR>");
|
||||
}
|
||||
usz len = 1;
|
||||
uint l = 1;
|
||||
// pre padding
|
||||
|
||||
@@ -60,7 +60,7 @@ fn void*! native_freopen(void* file, String filename, String mode) @inline
|
||||
|
||||
fn void! native_fseek(void* file, isz offset, Seek seek_mode) @inline
|
||||
{
|
||||
if (libc::fseek(file, (SeekIndex)offset, (CInt)seek_mode)) return file_seek_errno()?;
|
||||
if (libc::fseek(file, (SeekIndex)offset, seek_mode.ordinal)) return file_seek_errno()?;
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ fn usz! native_fwrite(CFile file, char[] buffer) @inline
|
||||
|
||||
fn void! native_fputc(CInt c, CFile stream) @inline
|
||||
{
|
||||
if (!libc::fputc(c, stream)) return IoError.EOF?;
|
||||
if (libc::fputc(c, stream) == libc::EOF) return IoError.EOF?;
|
||||
}
|
||||
|
||||
fn usz! native_fread(CFile file, char[] buffer) @inline
|
||||
|
||||
@@ -104,7 +104,12 @@ macro usz! write_all(stream, char[] buffer)
|
||||
return n;
|
||||
}
|
||||
|
||||
macro usz! @read_using_read_byte(&s, char[] buffer)
|
||||
macro usz! @read_using_read_byte(&s, char[] buffer) @deprecated
|
||||
{
|
||||
return read_using_read_byte(*s, buffer);
|
||||
}
|
||||
|
||||
macro usz! read_using_read_byte(s, char[] buffer)
|
||||
{
|
||||
usz len = 0;
|
||||
foreach (&cptr : buffer)
|
||||
@@ -121,17 +126,26 @@ macro usz! @read_using_read_byte(&s, char[] buffer)
|
||||
return len;
|
||||
}
|
||||
|
||||
macro void! @write_byte_using_write(&s, char c)
|
||||
macro void! write_byte_using_write(s, char c)
|
||||
{
|
||||
char[1] buff = { c };
|
||||
(*s).write(&buff)!;
|
||||
s.write(&buff)!;
|
||||
}
|
||||
|
||||
macro void! @write_byte_using_write(&s, char c) @deprecated
|
||||
{
|
||||
return write_byte_using_write(*s, c);
|
||||
}
|
||||
|
||||
macro char! @read_byte_using_read(&s)
|
||||
macro char! @read_byte_using_read(&s) @deprecated
|
||||
{
|
||||
return read_byte_using_read(*s);
|
||||
}
|
||||
|
||||
macro char! read_byte_using_read(s)
|
||||
{
|
||||
char[1] buffer;
|
||||
usz read = (*s).read(&buffer)!;
|
||||
usz read = s.read(&buffer)!;
|
||||
if (read != 1) return IoError.EOF?;
|
||||
return buffer[0];
|
||||
}
|
||||
@@ -139,13 +153,23 @@ macro char! @read_byte_using_read(&s)
|
||||
def ReadByteFn = fn char!();
|
||||
|
||||
|
||||
macro usz! @write_using_write_byte(&s, char[] bytes)
|
||||
macro usz! write_using_write_byte(s, char[] bytes)
|
||||
{
|
||||
foreach (c : bytes) s.write_byte(self, c)!;
|
||||
return bytes.len;
|
||||
}
|
||||
|
||||
macro void! @pushback_using_seek(&s)
|
||||
macro usz! @write_using_write_byte(&s, char[] bytes) @deprecated
|
||||
{
|
||||
return write_using_write_byte(*s, bytes);
|
||||
}
|
||||
|
||||
macro void! pushback_using_seek(s)
|
||||
{
|
||||
s.seek(-1, CURSOR)!;
|
||||
}
|
||||
|
||||
macro void! @pushback_using_seek(&s) @deprecated
|
||||
{
|
||||
s.seek(-1, CURSOR)!;
|
||||
}
|
||||
|
||||
@@ -121,13 +121,16 @@ fn usz! WriteBuffer.write(&self, char[] bytes) @dynamic
|
||||
fn void! WriteBuffer.write_byte(&self, char c) @dynamic
|
||||
{
|
||||
usz n = self.bytes.len - self.index;
|
||||
if (n == 0) self.write_pending()!;
|
||||
self.bytes[0] = c;
|
||||
self.index = 1;
|
||||
if (n == 0)
|
||||
{
|
||||
self.write_pending()!;
|
||||
}
|
||||
self.bytes[self.index] = c;
|
||||
self.index += 1;
|
||||
}
|
||||
|
||||
fn void! WriteBuffer.write_pending(&self) @local
|
||||
{
|
||||
self.index -= self.wrapped_stream.write(self.bytes[:self.index])!;
|
||||
if (self.index != 0) return IoError.INCOMPLETE_WRITE?;
|
||||
}
|
||||
}
|
||||
|
||||
70
lib/std/io/stream/multireader.c3
Normal file
70
lib/std/io/stream/multireader.c3
Normal file
@@ -0,0 +1,70 @@
|
||||
module std::io;
|
||||
|
||||
/* MultiReader implements the InStream interface and provides a logical
|
||||
* concatenation of the provided readers. They are read sequentially. If all the
|
||||
* data has been read, IoError.EOF is returned.
|
||||
*/
|
||||
struct MultiReader (InStream)
|
||||
{
|
||||
InStream[] readers;
|
||||
usz index;
|
||||
Allocator allocator;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
@param [&inout] self
|
||||
@param [&inout] allocator
|
||||
@require self.readers.len == 0 "Init may not run on already initialized data"
|
||||
@ensure self.index == 0
|
||||
*>
|
||||
fn MultiReader* MultiReader.new_init(&self, InStream... readers, Allocator allocator = allocator::heap())
|
||||
{
|
||||
InStream []copy = allocator::new_array(allocator, InStream, readers.len);
|
||||
copy[..] = readers[..];
|
||||
*self = { .readers = copy, .allocator = allocator };
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] self
|
||||
@require self.readers.len == 0 "Init may not run on already initialized data"
|
||||
@ensure self.index == 0
|
||||
*>
|
||||
fn MultiReader* MultiReader.temp_init(&self, InStream... readers)
|
||||
{
|
||||
return self.new_init(...readers, allocator: allocator::temp());
|
||||
}
|
||||
|
||||
fn void MultiReader.free(&self)
|
||||
{
|
||||
if (!self.allocator) return;
|
||||
allocator::free(self.allocator, self.readers);
|
||||
*self = {};
|
||||
}
|
||||
|
||||
fn usz! MultiReader.read(&self, char[] bytes) @dynamic
|
||||
{
|
||||
InStream r = self.readers[self.index];
|
||||
usz! n = r.read(bytes);
|
||||
if (catch err = n)
|
||||
{
|
||||
case IoError.EOF:
|
||||
self.index++;
|
||||
if (self.index >= self.readers.len)
|
||||
{
|
||||
return IoError.EOF?;
|
||||
}
|
||||
return self.read(bytes);
|
||||
default:
|
||||
return err?;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
fn char! MultiReader.read_byte(&self) @dynamic
|
||||
{
|
||||
char[1] data;
|
||||
self.read(data[..])!;
|
||||
return data[0];
|
||||
}
|
||||
59
lib/std/io/stream/multiwriter.c3
Normal file
59
lib/std/io/stream/multiwriter.c3
Normal file
@@ -0,0 +1,59 @@
|
||||
module std::io;
|
||||
|
||||
/* MultiWriter implements the OutStream interface and duplicates any write
|
||||
* operation to all the wrapped writers.
|
||||
*/
|
||||
struct MultiWriter (OutStream)
|
||||
{
|
||||
OutStream[] writers;
|
||||
Allocator allocator;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] self
|
||||
@param [&inout] allocator
|
||||
@require writers.len > 0
|
||||
@require self.writers.len == 0 "Init may not run on already initialized data"
|
||||
*>
|
||||
fn MultiWriter* MultiWriter.new_init(&self, OutStream... writers, Allocator allocator = allocator::heap())
|
||||
{
|
||||
OutStream[] copy = allocator::new_array(allocator, OutStream, writers.len);
|
||||
copy[..] = writers[..];
|
||||
*self = { .writers = copy, .allocator = allocator };
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] self
|
||||
@require writers.len > 0
|
||||
@require self.writers.len == 0 "Init may not run on already initialized data"
|
||||
*>
|
||||
fn MultiWriter* MultiWriter.temp_init(&self, OutStream... writers)
|
||||
{
|
||||
return self.new_init(...writers, allocator: allocator::temp());
|
||||
}
|
||||
|
||||
fn void MultiWriter.free(&self)
|
||||
{
|
||||
if (!self.allocator) return;
|
||||
allocator::free(self.allocator, self.writers);
|
||||
*self = {};
|
||||
}
|
||||
|
||||
fn usz! MultiWriter.write(&self, char[] bytes) @dynamic
|
||||
{
|
||||
usz n;
|
||||
foreach (w : self.writers)
|
||||
{
|
||||
n = w.write(bytes)!;
|
||||
if (n != bytes.len) return IoError.INCOMPLETE_WRITE?;
|
||||
}
|
||||
return bytes.len;
|
||||
}
|
||||
|
||||
fn void! MultiWriter.write_byte(&self, char c) @dynamic
|
||||
{
|
||||
char[1] data;
|
||||
data[0] = c;
|
||||
self.write(data[..])!;
|
||||
}
|
||||
42
lib/std/io/stream/teereader.c3
Normal file
42
lib/std/io/stream/teereader.c3
Normal file
@@ -0,0 +1,42 @@
|
||||
module std::io;
|
||||
|
||||
struct TeeReader (InStream)
|
||||
{
|
||||
InStream r;
|
||||
OutStream w;
|
||||
}
|
||||
|
||||
<* Returns a reader that implements InStream and that will write any data read
|
||||
from the wrapped reader r to the writer w. There is no internal buffering.
|
||||
|
||||
@param [&inout] r "Stream r to read from."
|
||||
@param [&inout] w "Stream w to write to what it reads from r."
|
||||
*>
|
||||
macro TeeReader tee_reader(InStream r, OutStream w) => { r, w };
|
||||
|
||||
<*
|
||||
@param [&inout] self
|
||||
@param [&inout] r "Stream r to read from."
|
||||
@param [&inout] w "Stream w to write to what it reads from r."
|
||||
*>
|
||||
fn TeeReader* TeeReader.init(&self, InStream r, OutStream w)
|
||||
{
|
||||
*self = tee_reader(r, w);
|
||||
return self;
|
||||
}
|
||||
|
||||
fn usz! TeeReader.read(&self, char[] bytes) @dynamic
|
||||
{
|
||||
usz nr, nw;
|
||||
nr = self.r.read(bytes)!;
|
||||
nw = self.w.write(bytes[:nr])!;
|
||||
if (nr != nw) return IoError.GENERAL_ERROR?;
|
||||
return nr;
|
||||
}
|
||||
|
||||
fn char! TeeReader.read_byte(&self) @dynamic
|
||||
{
|
||||
char[1] data;
|
||||
self.read(data[..])!;
|
||||
return data[0];
|
||||
}
|
||||
@@ -43,7 +43,8 @@ const CInt SIGINT = 2;
|
||||
const CInt SIGQUIT = 3;
|
||||
const CInt SIGILL = 4;
|
||||
const CInt SIGTRAP = 5;
|
||||
const CInt SIGABTR = 6;
|
||||
const CInt SIGABRT = 6;
|
||||
const CInt SIGABTR @deprecated("use SIGABRT") = SIGABRT;
|
||||
const CInt SIGBUS = BSD_FLAVOR_SIG ? 10 : 7; // Or Mips
|
||||
const CInt SIGFPE = 8;
|
||||
const CInt SIGKILL = 9;
|
||||
@@ -63,12 +64,6 @@ const bool BSD_FLAVOR_SIG @local = env::DARWIN || env::BSD_FAMILY;
|
||||
def Time_t = $typefrom(env::WIN32 ? long.typeid : CLong.typeid);
|
||||
def Off_t = $typefrom(env::WIN32 ? int.typeid : usz.typeid);
|
||||
|
||||
struct Timespec
|
||||
{
|
||||
Time_t tv_sec;
|
||||
CLong tv_nsec;
|
||||
}
|
||||
|
||||
module libc @if(env::LIBC);
|
||||
|
||||
extern fn void abort();
|
||||
@@ -114,6 +109,7 @@ extern fn ZString getenv(ZString name);
|
||||
extern fn ZString gets(char* buffer);
|
||||
extern fn Tm* gmtime(Time_t* timer);
|
||||
extern fn Tm* gmtime_r(Time_t *timer, Tm* buf) @if(!env::WIN32);
|
||||
extern fn CInt isatty(Fd fd) @if(!env::WIN32);
|
||||
extern fn CLong labs(CLong x);
|
||||
extern fn LongDivResult ldiv(CLong number, CLong denom);
|
||||
extern fn Tm* localtime(Time_t* timer);
|
||||
|
||||
@@ -17,10 +17,10 @@ struct Stat
|
||||
Gid_t st_gid;
|
||||
Dev_t st_rdev;
|
||||
|
||||
Timespec st_atimespec; // time of last access
|
||||
Timespec st_mtimespec; // time of last data modification
|
||||
Timespec st_ctimespec; // time of last status change
|
||||
Timespec st_birthtimespec; // time of file creation(birth)
|
||||
TimeSpec st_atimespec; // time of last access
|
||||
TimeSpec st_mtimespec; // time of last data modification
|
||||
TimeSpec st_ctimespec; // time of last status change
|
||||
TimeSpec st_birthtimespec; // time of file creation(birth)
|
||||
Off_t st_size; // file size, in bytes
|
||||
Blkcnt_t st_blocks; // blocks allocated for file
|
||||
Blksize_t st_blocksize; // optimal blocksize for I/O
|
||||
|
||||
@@ -10,6 +10,7 @@ extern fn CInt _fseeki64(CFile, long, int); def fseek = _fseeki64;
|
||||
extern fn CLong _ftelli64(CFile); def ftell = _ftelli64;
|
||||
extern fn Errno _get_timezone(CLong *timezone);
|
||||
extern fn Tm* _gmtime64_s(Tm* buf, Time_t *timer);
|
||||
extern fn CInt _isatty(Fd fd); def isatty = _isatty;
|
||||
extern fn Tm* _localtime64_s(Tm* buf, Time_t *timer);
|
||||
extern fn Time_t _mkgmtime64(Tm* timeptr); def timegm = _mkgmtime64;
|
||||
extern fn Time_t _mktime64(Tm *timeptr); def mktime = _mktime64;
|
||||
|
||||
@@ -328,7 +328,7 @@ fn BigInt BigInt.unary_minus(&self)
|
||||
}
|
||||
|
||||
|
||||
macro void BigInt.div(self, BigInt other)
|
||||
macro BigInt BigInt.div(self, BigInt other)
|
||||
{
|
||||
self.div_this(other);
|
||||
return self;
|
||||
@@ -445,13 +445,13 @@ fn BigInt BigInt.shl(self, int shift)
|
||||
return self;
|
||||
}
|
||||
|
||||
macro bool BigInt.equals(&self, BigInt* &other) @safemacro
|
||||
macro bool BigInt.equals(&self, BigInt other)
|
||||
{
|
||||
if (self.len != other.len) return false;
|
||||
return self.data[:self.len] == other.data[:self.len];
|
||||
}
|
||||
|
||||
macro bool BigInt.greater_than(&self, BigInt* &other) @safemacro
|
||||
macro bool BigInt.greater_than(&self, BigInt other)
|
||||
{
|
||||
if (self.is_negative() && !other.is_negative()) return false;
|
||||
if (!self.is_negative() && other.is_negative()) return true;
|
||||
@@ -461,7 +461,7 @@ macro bool BigInt.greater_than(&self, BigInt* &other) @safemacro
|
||||
for (pos = len - 1; pos >= 0 && self.data[pos] == other.data[pos]; pos--);
|
||||
return pos >= 0 && self.data[pos] > other.data[pos];
|
||||
}
|
||||
macro bool BigInt.less_than(&self, BigInt* &other) @safemacro
|
||||
macro bool BigInt.less_than(&self, BigInt other)
|
||||
{
|
||||
if (self.is_negative() && !other.is_negative()) return true;
|
||||
if (!self.is_negative() && other.is_negative()) return false;
|
||||
@@ -485,14 +485,14 @@ fn bool BigInt.is_one(&self)
|
||||
}
|
||||
|
||||
|
||||
macro bool BigInt.greater_or_equal(&self, BigInt* &other) @safemacro
|
||||
macro bool BigInt.greater_or_equal(&self, BigInt other)
|
||||
{
|
||||
return self.greater_than(*other) || self.equals(*other);
|
||||
return self.greater_than(other) || self.equals(other);
|
||||
}
|
||||
|
||||
macro bool BigInt.less_or_equal(&self, BigInt* &other) @safemacro
|
||||
macro bool BigInt.less_or_equal(&self, BigInt)
|
||||
{
|
||||
return self.less_than(*other) || self.equals(*other);
|
||||
return self.less_than(other) || self.equals(other);
|
||||
}
|
||||
|
||||
fn BigInt BigInt.abs(&self)
|
||||
@@ -831,6 +831,14 @@ fn BigInt BigInt.gcd(&self, BigInt other)
|
||||
return g;
|
||||
}
|
||||
|
||||
fn BigInt BigInt.lcm(&self, BigInt other)
|
||||
{
|
||||
BigInt x = self.abs();
|
||||
BigInt y = other.abs();
|
||||
BigInt g = y.mult(x);
|
||||
return g.div(x.gcd(y));
|
||||
}
|
||||
|
||||
<*
|
||||
@require bits >> 5 < MAX_LEN "Required bits > maxlength"
|
||||
*>
|
||||
|
||||
@@ -131,6 +131,28 @@ macro deg_to_rad(x) {
|
||||
*>
|
||||
macro abs(x) => $$abs(x);
|
||||
|
||||
<*
|
||||
@require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
|
||||
@require values::@is_int(y) || values::@is_float(y) "Expected an integer or floating point value"
|
||||
*>
|
||||
macro is_approx(x, y, eps)
|
||||
{
|
||||
if (x == y) return true;
|
||||
if (is_nan(x) || is_nan(y)) return false;
|
||||
return abs(x-y) <= eps;
|
||||
}
|
||||
|
||||
<*
|
||||
@require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
|
||||
@require values::@is_int(y) || values::@is_float(y) "Expected an integer or floating point value"
|
||||
*>
|
||||
macro is_approx_rel(x, y, eps)
|
||||
{
|
||||
if (x == y) return true;
|
||||
if (is_nan(x) || is_nan(y)) return false;
|
||||
return abs(x-y) <= eps * max(abs(x), abs(y));
|
||||
}
|
||||
|
||||
<*
|
||||
@require values::@is_int(x) `The input must be an integer`
|
||||
*>
|
||||
@@ -290,7 +312,7 @@ macro copysign(mag, sgn) => $$copysign(values::promote_int_same(mag, sgn), ($typ
|
||||
<*
|
||||
@require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
|
||||
*>
|
||||
macro cos(x) => $$cos(x);
|
||||
macro cos(x) => $$cos(values::promote_int(x));
|
||||
|
||||
<*
|
||||
@require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
|
||||
@@ -1020,6 +1042,21 @@ macro uint double.high_word(double d) => (uint)(bitcast(d, ulong) >> 32);
|
||||
macro uint double.low_word(double d) => (uint)bitcast(d, ulong);
|
||||
macro uint float.word(float d) => bitcast(d, uint);
|
||||
|
||||
macro void double.set_high_word(double* d, uint u)
|
||||
{
|
||||
ulong rep = bitcast(*d, ulong);
|
||||
rep = ((ulong)u << 32) | (rep & 0xffffffff);
|
||||
*d = bitcast(rep, double);
|
||||
}
|
||||
|
||||
macro void double.set_low_word(double* d, uint u)
|
||||
{
|
||||
ulong rep = bitcast(*d, ulong);
|
||||
rep = (rep & 0xffffffff00000000) | (ulong)u;
|
||||
*d = bitcast(rep, double);
|
||||
}
|
||||
|
||||
macro void float.set_word(float* f, uint u) => *f = bitcast(u, float);
|
||||
|
||||
macro double scalbn(double x, int n) => _scalbn(x, n);
|
||||
|
||||
@@ -1183,4 +1220,62 @@ macro long[<*>] long[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, di
|
||||
@require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
|
||||
@require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
|
||||
*>
|
||||
macro ulong[<*>] ulong[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
|
||||
macro ulong[<*>] ulong[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
|
||||
|
||||
<*
|
||||
@require types::is_int($typeof(a)) `The input must be an integer`
|
||||
@require types::is_int($typeof(b)) `The input must be an integer`
|
||||
*>
|
||||
macro _gcd(a, b) @private
|
||||
{
|
||||
if (a == 0) return b;
|
||||
if (b == 0) return a;
|
||||
|
||||
var $Type = $typeof(a);
|
||||
$Type r, aa, ab;
|
||||
aa = abs(a);
|
||||
ab = abs(b);
|
||||
while (ab != 0)
|
||||
{
|
||||
r = aa % ab;
|
||||
aa = ab;
|
||||
ab = r;
|
||||
}
|
||||
return aa;
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the least common multiple for the provided arguments.
|
||||
|
||||
@require $vacount >= 2 `At least two arguments are required.`
|
||||
*>
|
||||
macro lcm(...)
|
||||
{
|
||||
$typeof($vaarg[0]) result = $vaarg[0];
|
||||
$for (var $i = 1; $i < $vacount; $i++)
|
||||
$if $defined(result.lcm):
|
||||
result = result.lcm($vaarg[$i]);
|
||||
$else
|
||||
result = (abs($vaarg[$i]) * abs(result)) / (_gcd($vaarg[$i], result));
|
||||
$endif
|
||||
$endfor
|
||||
return result;
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the greatest common divisor for the provided arguments.
|
||||
|
||||
@require $vacount >= 2 `At least two arguments are required.`
|
||||
*>
|
||||
macro gcd(...)
|
||||
{
|
||||
$typeof($vaarg[0]) result = $vaarg[0];
|
||||
$for (var $i = 1; $i < $vacount; $i++)
|
||||
$if $defined(result.gcd):
|
||||
result = result.gcd($vaarg[$i]);
|
||||
$else
|
||||
result = _gcd($vaarg[$i], result);
|
||||
$endif
|
||||
$endfor
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ union Complex
|
||||
Real[<2>] v;
|
||||
}
|
||||
|
||||
|
||||
const Complex IDENTITY = { 1, 0 };
|
||||
const Complex IMAGINARY = { 0, 1 };
|
||||
macro Complex Complex.add(self, Complex b) => Complex { .v = self.v + b.v };
|
||||
macro Complex Complex.add_each(self, Real b) => Complex { .v = self.v + b };
|
||||
macro Complex Complex.sub(self, Complex b) => Complex { .v = self.v - b.v };
|
||||
@@ -22,3 +22,10 @@ macro Complex Complex.div(self, Complex b)
|
||||
Real div = b.v.dot(b.v);
|
||||
return Complex{ (self.r * b.r + self.c * b.c) / div, (self.c * b.r - self.r * b.c) / div };
|
||||
}
|
||||
macro Complex Complex.inverse(self)
|
||||
{
|
||||
Real sqr = self.v.dot(self.v);
|
||||
return Complex{ self.r / sqr, -self.c / sqr };
|
||||
}
|
||||
macro Complex Complex.conjugate(self) => Complex { .r = self.r, .c = -self.c };
|
||||
macro bool Complex.equals(self, Complex b) => self.v == b.v;
|
||||
|
||||
@@ -197,7 +197,7 @@ fn Real Matrix4x4.determinant(&self)
|
||||
|
||||
fn Matrix2x2 Matrix2x2.adjoint(&self)
|
||||
{
|
||||
return { self.m00, -self.m01, -self.m10, self.m11 };
|
||||
return { self.m11, -self.m01, -self.m10, self.m00 };
|
||||
}
|
||||
|
||||
fn Matrix3x3 Matrix3x3.adjoint(&self)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/k_cos.c */
|
||||
/*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/k_cosf.c */
|
||||
/*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
union DoubleInternal
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/k_sin.c */
|
||||
/*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/k_sinf.c */
|
||||
/*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/k_tan.c */
|
||||
/*
|
||||
@@ -32,7 +32,7 @@ fn double __tan(double x, double y, int odd) @extern("__tan") @weak @nostrip
|
||||
const double PIO4 = 7.85398163397448278999e-01; /* 3FE921FB, 54442D18 */
|
||||
const double PIO4LO = 3.06161699786838301793e-17; /* 3C81A626, 33145C07 */
|
||||
|
||||
uint hx = (uint)(bitcast(x, ulong) >> 32);
|
||||
uint hx = x.high_word();
|
||||
bool big = (hx &0x7fffffff) >= 0x3FE59428; // |x| >= 0.6744
|
||||
int sign @noinit;
|
||||
if (big)
|
||||
@@ -68,12 +68,12 @@ fn double __tan(double x, double y, int odd) @extern("__tan") @weak @nostrip
|
||||
// -1.0/(x+r) has up to 2ulp error, so compute it accurately
|
||||
|
||||
// Clear low word
|
||||
ulong d = bitcast(w, ulong) & 0xFFFF_FFFF_0000_0000;
|
||||
double w0 = bitcast(d, double);
|
||||
double w0 = w;
|
||||
w0.set_low_word(0);
|
||||
|
||||
v = r - (w0 - x); // w0+v = r+x
|
||||
double a = -1.0 / w;
|
||||
d = bitcast(a, ulong) & 0xFFFF_FFFF_0000_0000;
|
||||
double a0 = bitcast(d, double);
|
||||
double a0 = a;
|
||||
a0.set_low_word(0);
|
||||
return a0 + a * (1.0 + a0 * w0 + a0 * v);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/k_tanf.c */
|
||||
/*
|
||||
|
||||
139
lib/std/math/math_nolibc/acos.c3
Normal file
139
lib/std/math/math_nolibc/acos.c3
Normal file
@@ -0,0 +1,139 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/e_acos.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunSoft, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const PIO2_HI @local = 1.57079632679489655800e+00; /* 0x3FF921FB, 0x54442D18 */
|
||||
const PIO2_LO @local = 6.12323399573676603587e-17; /* 0x3C91A626, 0x33145C07 */
|
||||
const PS0 @local = 1.66666666666666657415e-01; /* 0x3FC55555, 0x55555555 */
|
||||
const PS1 @local = -3.25565818622400915405e-01; /* 0xBFD4D612, 0x03EB6F7D */
|
||||
const PS2 @local = 2.01212532134862925881e-01; /* 0x3FC9C155, 0x0E884455 */
|
||||
const PS3 @local = -4.00555345006794114027e-02; /* 0xBFA48228, 0xB5688F3B */
|
||||
const PS4 @local = 7.91534994289814532176e-04; /* 0x3F49EFE0, 0x7501B288 */
|
||||
const PS5 @local = 3.47933107596021167570e-05; /* 0x3F023DE1, 0x0DFDF709 */
|
||||
const QS1 @local = -2.40339491173441421878e+00; /* 0xC0033A27, 0x1C8A2D4B */
|
||||
const QS2 @local = 2.02094576023350569471e+00; /* 0x40002AE5, 0x9C598AC8 */
|
||||
const QS3 @local = -6.88283971605453293030e-01; /* 0xBFE6066C, 0x1B8D0159 */
|
||||
const QS4 @local = 7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */
|
||||
|
||||
fn double _r(double z) @local
|
||||
{
|
||||
double p = z * (PS0 + z * (PS1 + z * (PS2 + z * (PS3 + z * (PS4 + z * PS5)))));
|
||||
double q = 1.0 + z * (QS1 + z * (QS2 + z * (QS3 + z * QS4)));
|
||||
return p / q;
|
||||
}
|
||||
|
||||
fn double _acos(double x) @weak @extern("acos") @nostrip
|
||||
{
|
||||
uint hx = x.high_word();
|
||||
uint ix = hx & 0x7fffffff;
|
||||
switch
|
||||
{
|
||||
/* |x| >= 1 or nan */
|
||||
case ix >= 0x3ff00000:
|
||||
uint lx = x.low_word();
|
||||
if ((ix - 0x3ff00000 | lx) == 0)
|
||||
{
|
||||
/* acos(1)=0, acos(-1)=pi */
|
||||
if (hx >> 31) return 2. * PIO2_HI + 0x1p-120f;
|
||||
return 0.;
|
||||
}
|
||||
return double.nan;
|
||||
/* |x| < 0.5 */
|
||||
case ix < 0x3fe00000:
|
||||
/* |x| < 2**-57 */
|
||||
if (ix <= 0x3c600000) return PIO2_HI + 0x1p-120f;
|
||||
return PIO2_HI - (x - (PIO2_LO - x * _r(x * x)));
|
||||
/* x < -0.5 */
|
||||
case (hx >> 31) != 0:
|
||||
double z = (1. + x) * 0.5;
|
||||
double s = math::sqrt(z);
|
||||
double w = _r(z) * s - PIO2_LO;
|
||||
return 2. * (PIO2_HI - (s + w));
|
||||
/* x > 0.5 */
|
||||
default:
|
||||
double z = (1. - x) * 0.5;
|
||||
double s = math::sqrt(z);
|
||||
double df = s;
|
||||
df.set_low_word(0);
|
||||
double c = (z - df * df) / (s + df);
|
||||
double w = _r(z) * s + c;
|
||||
return 2. * (df + w);
|
||||
}
|
||||
}
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/e_acosf.c */
|
||||
/*
|
||||
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
|
||||
*/
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const float PIO2_HI_F @local = 1.5707962513e+00; /* 0x3fc90fda */
|
||||
const float PIO2_LO_F @local = 7.5497894159e-08; /* 0x33a22168 */
|
||||
const float PS0_F @local = 1.6666586697e-01;
|
||||
const float PS1_F @local = -4.2743422091e-02;
|
||||
const float PS2_F @local = -8.6563630030e-03;
|
||||
const float QS1_F @local = -7.0662963390e-01;
|
||||
|
||||
fn float _r_f(float z) @local
|
||||
{
|
||||
float p = z * ( PS0_F + z * (PS1_F + z * PS2_F));
|
||||
float q = 1.0f + z * QS1_F;
|
||||
return p / q;
|
||||
}
|
||||
|
||||
fn float _acosf(float x) @weak @extern("acosf") @nostrip
|
||||
{
|
||||
uint hx = bitcast(x, uint);
|
||||
uint ix = hx & 0x7fffffff;
|
||||
switch
|
||||
{
|
||||
/* |x| >= 1 or nan */
|
||||
case ix >= 0x3f800000:
|
||||
if (ix == 0x3f800000)
|
||||
{
|
||||
if (hx >> 31) return 2.f * PIO2_HI_F + 0x1p-120f;
|
||||
return 0;
|
||||
}
|
||||
return float.nan;
|
||||
/* |x| < 0.5 */
|
||||
case ix < 0x3f000000:
|
||||
/* |x| < 2**-26 */
|
||||
if (ix <= 0x32800000) return PIO2_HI_F + 0x1p-120f;
|
||||
return PIO2_HI_F - (x - (PIO2_LO_F - x * _r_f(x * x)));
|
||||
/* x < -0.5 */
|
||||
case (hx >> 31) != 0:
|
||||
float z = (1.f + x) * 0.5f;
|
||||
float s = math::sqrt(z);
|
||||
float w = _r_f(z) * s - PIO2_LO_F;
|
||||
return 2.f * (PIO2_HI_F - (s + w));
|
||||
/* x > 0.5 */
|
||||
default:
|
||||
float z = (1.f - x) * 0.5f;
|
||||
float s = math::sqrt(z);
|
||||
float df = s;
|
||||
uint idf = df.word();
|
||||
df.set_word(idf & 0xfffff000);
|
||||
float c = (z - df * df) / (s + df);
|
||||
float w = _r_f(z) * s + c;
|
||||
return 2.f * (df + w);
|
||||
}
|
||||
}
|
||||
132
lib/std/math/math_nolibc/asin.c3
Normal file
132
lib/std/math/math_nolibc/asin.c3
Normal file
@@ -0,0 +1,132 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/e_asin.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunSoft, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const PIO2_HI @local = 1.57079632679489655800e+00; /* 0x3FF921FB, 0x54442D18 */
|
||||
const PIO2_LO @local = 6.12323399573676603587e-17; /* 0x3C91A626, 0x33145C07 */
|
||||
const PS0 @local = 1.66666666666666657415e-01; /* 0x3FC55555, 0x55555555 */
|
||||
const PS1 @local = -3.25565818622400915405e-01; /* 0xBFD4D612, 0x03EB6F7D */
|
||||
const PS2 @local = 2.01212532134862925881e-01; /* 0x3FC9C155, 0x0E884455 */
|
||||
const PS3 @local = -4.00555345006794114027e-02; /* 0xBFA48228, 0xB5688F3B */
|
||||
const PS4 @local = 7.91534994289814532176e-04; /* 0x3F49EFE0, 0x7501B288 */
|
||||
const PS5 @local = 3.47933107596021167570e-05; /* 0x3F023DE1, 0x0DFDF709 */
|
||||
const QS1 @local = -2.40339491173441421878e+00; /* 0xC0033A27, 0x1C8A2D4B */
|
||||
const QS2 @local = 2.02094576023350569471e+00; /* 0x40002AE5, 0x9C598AC8 */
|
||||
const QS3 @local = -6.88283971605453293030e-01; /* 0xBFE6066C, 0x1B8D0159 */
|
||||
const QS4 @local = 7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */
|
||||
|
||||
fn double _r(double z) @local
|
||||
{
|
||||
double p = z * (PS0 + z * (PS1 + z * (PS2 + z * (PS3 + z * (PS4 + z * PS5)))));
|
||||
double q = 1.0 + z * (QS1 + z * (QS2 + z * (QS3 + z * QS4)));
|
||||
return p / q;
|
||||
}
|
||||
|
||||
fn double _asin(double x) @weak @extern("asin") @nostrip
|
||||
{
|
||||
uint hx = x.high_word();
|
||||
uint ix = hx & 0x7fffffff;
|
||||
switch
|
||||
{
|
||||
/* |x| >= 1 or nan */
|
||||
case ix >= 0x3ff00000:
|
||||
uint lx = x.low_word();
|
||||
if ((ix-0x3ff00000 | lx) == 0)
|
||||
{
|
||||
/* asin(1) = +-pi/2 with inexact */
|
||||
return x * PIO2_HI + 0x1p-120f;
|
||||
}
|
||||
return double.nan;
|
||||
/* |x| < 0.5 */
|
||||
case ix < 0x3fe00000:
|
||||
/* if 0x1p-1022 <= |x| < 0x1p-26, avoid raising underflow */
|
||||
if (ix < 0x3e500000 && ix >= 0x00100000) return x;
|
||||
return x + x * _r(x * x);
|
||||
/* 1 > |x| >= 0.5 */
|
||||
default:
|
||||
double z = (1. - math::abs(x)) * 0.5;
|
||||
double s = math::sqrt(z);
|
||||
double r = _r(z);
|
||||
/* if |x| > 0.975 */
|
||||
if (ix >= 0x3fef3333)
|
||||
{
|
||||
x = PIO2_HI - (2. * (s + s * r) - PIO2_LO);
|
||||
}
|
||||
else
|
||||
{
|
||||
double f = s;
|
||||
f.set_low_word(0);
|
||||
double c = (z - f * f) / (s + f);
|
||||
x = 0.5 * PIO2_HI - (2. * s * r - (PIO2_LO - 2. * c) - (0.5 * PIO2_HI - 2. * f));
|
||||
}
|
||||
if (hx >> 31 != 0) return -x;
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/e_asinf.c */
|
||||
/*
|
||||
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
|
||||
*/
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const PIO2 @local = 1.570796326794896558e+00;
|
||||
const float PS0_F @local = 1.6666586697e-01;
|
||||
const float PS1_F @local = -4.2743422091e-02;
|
||||
const float PS2_F @local = -8.6563630030e-03;
|
||||
const float QS1_F @local = -7.0662963390e-01;
|
||||
|
||||
fn float _r_f(float z) @local
|
||||
{
|
||||
float p = z * ( PS0_F + z * (PS1_F + z * PS2_F));
|
||||
float q = 1.0f + z * QS1_F;
|
||||
return p / q;
|
||||
}
|
||||
|
||||
fn float _asinf(float x) @weak @extern("asinf") @nostrip
|
||||
{
|
||||
uint hx = bitcast(x, uint);
|
||||
uint ix = hx & 0x7fffffff;
|
||||
switch
|
||||
{
|
||||
/* |x| >= 1 */
|
||||
case ix >= 0x3f800000:
|
||||
if (ix == 0x3f800000)
|
||||
{
|
||||
/* asin(+-1) = +-pi/2 with inexact */
|
||||
return x * (float)PIO2 + 0x1p-120f;
|
||||
}
|
||||
return float.nan;
|
||||
/* |x| < 0.5 */
|
||||
case ix < 0x3f000000:
|
||||
/* if 0x1p-126 <= |x| < 0x1p-12, avoid raising underflow */
|
||||
if (ix < 0x39800000 && ix >= 0x00800000) return x;
|
||||
return x + x * _r_f(x * x);
|
||||
/* 1 > |x| >= 0.5 */
|
||||
default:
|
||||
float z = (1.f - math::abs(x)) * 0.5f;
|
||||
float s = math::sqrt(z);
|
||||
x = (float)PIO2 - 2.f * (s + s * _r_f(z));
|
||||
if (hx >> 31) return -x;
|
||||
return x;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,16 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/s_atan.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const double[*] ATANHI @private = {
|
||||
4.63647609000806093515e-01, /* atan(0.5)hi 0x3FDDAC67, 0x0561BB4F */
|
||||
@@ -89,6 +101,21 @@ fn double _atan(double x) @weak @extern("atan") @nostrip
|
||||
return sign ? -z : z;
|
||||
}
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/s_atanf.c */
|
||||
/*
|
||||
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
|
||||
*/
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const float[*] ATANHIF @private = {
|
||||
4.6364760399e-01, /* atan(0.5)hi 0x3eed6338 */
|
||||
7.8539812565e-01, /* atan(1.0)hi 0x3f490fda */
|
||||
@@ -170,37 +197,41 @@ fn float _atanf(float x) @weak @extern("atanf") @nostrip
|
||||
/* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */
|
||||
float s1 = z * (ATF[0] + w * (ATF[2] + w * ATF[4]));
|
||||
float s2 = w * (ATF[1] + w * ATF[3]);
|
||||
if (id < 0) return x - x * (s1 + s2) * 10000;
|
||||
if (id < 0) return x - x * (s1 + s2);
|
||||
z = ATANHIF[id] - ((x * (s1 + s2) - ATANLOF[id]) - x);
|
||||
return sign ? -z : z;
|
||||
}
|
||||
|
||||
const PI_LO @private = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/e_atan2.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunSoft, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*
|
||||
*/
|
||||
|
||||
macro void extract_words(double d, uint* hi, uint* lo) @private
|
||||
{
|
||||
ulong rep = bitcast(d, ulong);
|
||||
*hi = (uint)(rep >> 32);
|
||||
*lo = (uint)rep;
|
||||
}
|
||||
const PI_LO @private = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */
|
||||
|
||||
fn double _atan2(double y, double x) @weak @extern("atan2") @nostrip
|
||||
{
|
||||
if (math::is_nan(x) || math::is_nan(y)) return x + y;
|
||||
|
||||
uint lx @noinit;
|
||||
uint ix @noinit;
|
||||
extract_words(x, &ix, &lx);
|
||||
uint ly @noinit;
|
||||
uint iy @noinit;
|
||||
extract_words(y, &iy, &ly);
|
||||
uint lx = x.low_word();
|
||||
uint ix = x.high_word();
|
||||
uint ly = y.low_word();
|
||||
uint iy = y.high_word();
|
||||
|
||||
// x = 1.0
|
||||
if ((ix - 0x3ff00000) | lx == 0) return _atan(y);
|
||||
// 2*sign(x) + sign(y)
|
||||
uint m = ((iy >> 31) & 1) | ((ix >> 30) & 2);
|
||||
ix = ix & 0x7fffffff;
|
||||
iy = iy & 0x7fffffff;
|
||||
ix &= 0x7fffffff;
|
||||
iy &= 0x7fffffff;
|
||||
|
||||
// when y = 0
|
||||
if (iy | ly == 0)
|
||||
@@ -252,14 +283,29 @@ fn double _atan2(double y, double x) @weak @extern("atan2") @nostrip
|
||||
}
|
||||
}
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/e_atan2f.c */
|
||||
/*
|
||||
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
|
||||
*/
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const float PI_F @private = 3.1415927410e+00; /* 0x40490fdb */
|
||||
const float PI_LO_F @private = -8.7422776573e-08; /* 0xb3bbbd2e */
|
||||
|
||||
fn float _atan2f(float y, float x) @weak @extern("atan2f") @nostrip
|
||||
{
|
||||
if (math::is_nan(x) || math::is_nan(y)) return x + y;
|
||||
uint ix = bitcast(x, uint);
|
||||
uint iy = bitcast(y, uint);
|
||||
uint ix = x.word();
|
||||
uint iy = y.word();
|
||||
/* x=1.0 */
|
||||
if (ix == 0x3f800000) return _atanf(y);
|
||||
/* 2*sign(x)+sign(y) */
|
||||
|
||||
95
lib/std/math/math_nolibc/atanh.c3
Normal file
95
lib/std/math/math_nolibc/atanh.c3
Normal file
@@ -0,0 +1,95 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD usr/src/lib/msun/src/e_atanh.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunSoft, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
fn double _atanh(double x) @weak @extern("atanh") @nostrip
|
||||
{
|
||||
double t @noinit;
|
||||
uint hx = x.high_word();
|
||||
uint ix = hx & 0x7fffffff;
|
||||
uint sign = hx >> 31;
|
||||
switch
|
||||
{
|
||||
/* |x| >= 1 or nan */
|
||||
case ix >= 0x3ff00000:
|
||||
uint lx = x.low_word();
|
||||
if ((ix - 0x3ff00000 | lx) == 0)
|
||||
{
|
||||
return sign ? -double.inf : double.inf;
|
||||
}
|
||||
return double.nan;
|
||||
/* x<2**-28 */
|
||||
case ix < 0x3e300000 && (1e300 + x) > 0.:
|
||||
return x;
|
||||
}
|
||||
x.set_high_word(ix);
|
||||
/* |x| < 0.5 */
|
||||
if (ix < 0x3fe00000)
|
||||
{
|
||||
t = x + x;
|
||||
t = 0.5 * _log1p(t + t * x / (1. - x));
|
||||
}
|
||||
else
|
||||
{
|
||||
t = 0.5 * _log1p((x + x) / (1. - x));
|
||||
}
|
||||
return sign ? -t : t;
|
||||
}
|
||||
|
||||
/* origin: FreeBSD usr/src/lib/msun/src/e_atanhf.c */
|
||||
/*
|
||||
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
|
||||
*/
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
fn float _atanhf(float x) @weak @extern("atanhf") @nostrip
|
||||
{
|
||||
float t @noinit;
|
||||
uint hx = bitcast(x, uint);
|
||||
uint ix = hx & 0x7fffffff;
|
||||
uint sign = hx >> 31;
|
||||
switch
|
||||
{
|
||||
/* |x| >= 1 or nan */
|
||||
case ix >= 0x3f800000:
|
||||
if (ix == 0x3f800000)
|
||||
{
|
||||
return sign ? -float.inf : float.inf;
|
||||
}
|
||||
return float.nan;
|
||||
/* x<2**-28 */
|
||||
case ix < 0x31800000 && (1e30 + x) > 0.f:
|
||||
return x;
|
||||
}
|
||||
x.set_word(ix);
|
||||
/* |x| < 0.5 */
|
||||
if (ix < 0x3f000000)
|
||||
{
|
||||
t = x + x;
|
||||
t = 0.5f * _log1pf(t + t * x / (1.f - x));
|
||||
}
|
||||
else
|
||||
{
|
||||
t = 0.5f * _log1pf((x + x) / (1.f - x));
|
||||
}
|
||||
return sign ? -t : t;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double _ceil(double x) @weak @extern("ceil") @nostrip
|
||||
{
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn float _cosf(float x) @extern("cosf") @weak @nostrip
|
||||
{
|
||||
uint ix = bitcast(x, uint);
|
||||
uint ix = x.word();
|
||||
uint sign = ix >> 31;
|
||||
ix &= 0x7fffffff;
|
||||
|
||||
@@ -51,11 +51,10 @@ fn float _cosf(float x) @extern("cosf") @weak @nostrip
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
fn double _cos(double x) @weak @nostrip
|
||||
fn double _cos(double x) @extern("cos") @weak @nostrip
|
||||
{
|
||||
// High word of x.
|
||||
uint ix = (uint)(bitcast(x, ulong) >> 32);
|
||||
ix &= 0x7fffffff;
|
||||
uint ix = x.high_word() & 0x7fffffff;
|
||||
|
||||
switch
|
||||
{
|
||||
|
||||
60
lib/std/math/math_nolibc/exp.c3
Normal file
60
lib/std/math/math_nolibc/exp.c3
Normal file
@@ -0,0 +1,60 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
const double EXP_LN2_HI = 6.93147180369123816490e-01;
|
||||
const double EXP_LN2_LO = 1.90821492927058770002e-10;
|
||||
const double EXP_INV_LN2 = 1.44269504088896338700e+00;
|
||||
const double EXP_P1 = 1.66666666666666019037e-01;
|
||||
const double EXP_P2 = -2.77777777770155933842e-03;
|
||||
const double EXP_P3 = 6.61375632143793436117e-05;
|
||||
const double EXP_P4 = -1.65339022054652515390e-06;
|
||||
const double EXP_P5 = 4.13813679705723846039e-08;
|
||||
/*--------------------------------------------*/
|
||||
const float EXPF_LN2_HI = 6.9314575195e-01f;
|
||||
const float EXPF_LN2_LO = 1.4286067653e-06f;
|
||||
const float EXPF_INV_LN2 = 1.4426950216e+00f;
|
||||
const float EXPF_P1 = 1.6666667163e-01f;
|
||||
const float EXPF_P2 = -2.7777778450e-03f;
|
||||
const float EXPF_P3 = 6.6137559770e-05f;
|
||||
const float EXPF_P4 = -1.6533901999e-06f;
|
||||
|
||||
fn double exp(double x) @extern("exp")
|
||||
{
|
||||
if (x != x) return x;
|
||||
if (x == double.inf) return double.inf;
|
||||
if (x == -double.inf) return 0.0; // IEEE 754 spec
|
||||
// Overflow threshold for exp (approx +709.78 for double)
|
||||
if (x > 709.782712893384) return double.inf;
|
||||
// Underflow threshold for exp (approx -745.13 for double)
|
||||
if (x < -745.133219101941) return 0.0;
|
||||
|
||||
double px = x * EXP_INV_LN2;
|
||||
double k = _floor(px + 0.5);
|
||||
double r = x - k * EXP_LN2_HI - k * EXP_LN2_LO;
|
||||
|
||||
double r2 = r * r;
|
||||
double p = r2 * (EXP_P1 + r2 * (EXP_P2 + r2 * (EXP_P3 + r2 * (EXP_P4 + r2 * EXP_P5))));
|
||||
double exp_r = 1.0 + r + r * p;
|
||||
|
||||
return ldexp(exp_r, (int)k);
|
||||
}
|
||||
|
||||
fn float expf(float x) @extern("expf")
|
||||
{
|
||||
if (x != x) return x;
|
||||
if (x == float.inf) return float.inf;
|
||||
if (x == -float.inf) return 0.0f; // IEEE 754 spec
|
||||
// Overflow threshold (approx +88.72 for float)
|
||||
if (x > 88.7228f) return float.inf;
|
||||
// Underflow threshold (approx -103.97 for float)
|
||||
if (x < -103.972084f) return 0.0f;
|
||||
|
||||
float px = x * EXPF_INV_LN2;
|
||||
float k = _floorf(px + 0.5f);
|
||||
float r = x - k * EXPF_LN2_HI - k * EXPF_LN2_LO;
|
||||
|
||||
float r2 = r * r;
|
||||
float p = r2 * (EXPF_P1 + r2 * (EXPF_P2 + r2 * (EXPF_P3 + r2 * EXPF_P4)));
|
||||
float exp_r = 1.0f + r + r * p;
|
||||
|
||||
return ldexpf(exp_r, (int)k);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
macro uint _top12f(float x) @private => bitcast(x, uint) >> 20;
|
||||
|
||||
|
||||
15
lib/std/math/math_nolibc/fabs.c3
Normal file
15
lib/std/math/math_nolibc/fabs.c3
Normal file
@@ -0,0 +1,15 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double _fabs(double x) @weak @extern("fabs") @nostrip
|
||||
{
|
||||
ulong ix = bitcast(x, ulong);
|
||||
ix &= ~(1ul << 63);
|
||||
return bitcast(ix, double);
|
||||
}
|
||||
|
||||
fn float _fabsf(float x) @weak @extern("fabsf") @nostrip
|
||||
{
|
||||
uint ix = bitcast(x, uint);
|
||||
ix &= 0x7fffffff;
|
||||
return bitcast(ix, float);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double _floor(double x) @weak @extern("floor") @nostrip
|
||||
{
|
||||
|
||||
59
lib/std/math/math_nolibc/frexp.c3
Normal file
59
lib/std/math/math_nolibc/frexp.c3
Normal file
@@ -0,0 +1,59 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double frexp(double x, int* exp) @extern("frexp")
|
||||
{
|
||||
uint hx = x.high_word();
|
||||
uint ix = hx & 0x7fffffff;
|
||||
uint lx = x.low_word();
|
||||
|
||||
if (ix >= 0x7ff00000 || (ix | lx) == 0)
|
||||
{
|
||||
*exp = 0;
|
||||
return x;
|
||||
}
|
||||
|
||||
// exponent extraction and normalization
|
||||
int e = (int)((ix >> 20) & 0x7ff);
|
||||
if (e == 0)
|
||||
{
|
||||
// subnormal number
|
||||
x *= 0x1p64;
|
||||
hx = x.high_word();
|
||||
e = (int)((hx >> 20) & 0x7ff) - 64;
|
||||
}
|
||||
*exp = e - 1022;
|
||||
|
||||
// set exponent to -1 (fraction in [0.5, 1))
|
||||
hx = (hx & 0x800fffff) | 0x3fe00000;
|
||||
{
|
||||
ulong rep = ((ulong)hx << 32) | lx;
|
||||
return bitcast(rep, double);
|
||||
}
|
||||
}
|
||||
|
||||
fn float frexpf(float x, int* exp) @extern("frexpf")
|
||||
{
|
||||
uint ix = x.word();
|
||||
uint hx = ix & 0x7fffffff;
|
||||
|
||||
if (hx >= 0x7f800000 || hx == 0)
|
||||
{
|
||||
*exp = 0;
|
||||
return x;
|
||||
}
|
||||
|
||||
// exponent extraction and normalization
|
||||
int e = (int)((hx >> 23) & 0xff);
|
||||
if (e == 0)
|
||||
{
|
||||
// subnormal number
|
||||
x *= 0x1p64f;
|
||||
ix = x.word();
|
||||
e = (int)((ix >> 23) & 0xff) - 64;
|
||||
}
|
||||
*exp = e - 126;
|
||||
|
||||
// set exponent to -1 (fraction in [0.5, 1))
|
||||
ix = (ix & 0x807fffff) | 0x3f000000;
|
||||
return bitcast(ix, float);
|
||||
}
|
||||
67
lib/std/math/math_nolibc/ldexp.c3
Normal file
67
lib/std/math/math_nolibc/ldexp.c3
Normal file
@@ -0,0 +1,67 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double ldexp(double x, int exp) @extern("ldexp")
|
||||
{
|
||||
uint hx = x.high_word();
|
||||
int hexp = (int)((hx & 0x7ff00000) >> 20);
|
||||
|
||||
|
||||
if (hexp == 0x7ff) return x;
|
||||
if (hexp == 0)
|
||||
{
|
||||
// subnormal number handling
|
||||
x *= 0x1p64;
|
||||
hx = x.high_word();
|
||||
hexp = (int)((hx & 0x7ff00000) >> 20) - 64;
|
||||
}
|
||||
|
||||
// new exponent calculation
|
||||
hexp += exp;
|
||||
|
||||
if (hexp > 0x7fe) return x * double.inf;
|
||||
if (hexp < 1)
|
||||
{
|
||||
x *= 0x1p-1022;
|
||||
hexp += 1022;
|
||||
if (hexp < 1) x *= 0x1p-1022;
|
||||
return x;
|
||||
}
|
||||
|
||||
// set new exponent
|
||||
hx = ((ulong)hx & 0x800fffff) | ((ulong)hexp << 20);
|
||||
{
|
||||
ulong rep = ((ulong)hx << 32) | x.low_word();
|
||||
return bitcast(rep, double);
|
||||
}
|
||||
}
|
||||
|
||||
fn float ldexpf(float x, int exp) @extern("ldexpf")
|
||||
{
|
||||
uint ix = x.word();
|
||||
int hexp = (int)((ix & 0x7f800000) >> 23);
|
||||
|
||||
if (hexp == 0xff) return x;
|
||||
if (hexp == 0)
|
||||
{
|
||||
// subnormal number handling
|
||||
x *= 0x1p64f;
|
||||
ix = x.word();
|
||||
hexp = (int)((ix & 0x7f800000) >> 23) - 64;
|
||||
}
|
||||
|
||||
// new exponent calculation
|
||||
hexp += exp;
|
||||
|
||||
if (hexp > 0xfe) return x * float.inf;
|
||||
if (hexp < 1)
|
||||
{
|
||||
x *= 0x1p-126f;
|
||||
hexp += 126;
|
||||
if (hexp < 1) x *= 0x1p-126f;
|
||||
return x;
|
||||
}
|
||||
|
||||
// set new exponent
|
||||
ix = (ix & 0x807fffff) | (hexp << 23);
|
||||
return bitcast(ix, float);
|
||||
}
|
||||
86
lib/std/math/math_nolibc/log.c3
Normal file
86
lib/std/math/math_nolibc/log.c3
Normal file
@@ -0,0 +1,86 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
const double LOG_LN2_HI = 6.93147180369123816490e-01;
|
||||
const double LOG_LN2_LO = 1.90821492927058770002e-10;
|
||||
const double LOG_L1 = 6.666666666666735130e-01;
|
||||
const double LOG_L2 = 3.999999999940941908e-01;
|
||||
const double LOG_L3 = 2.857142874366239149e-01;
|
||||
const double LOG_L4 = 2.222219843214978396e-01;
|
||||
const double LOG_L5 = 1.818357216161805012e-01;
|
||||
const double LOG_L6 = 1.531383769920937332e-01;
|
||||
/*--------------------------------------------*/
|
||||
const float LOGF_LN2_HI = 6.9313812256e-01f;
|
||||
const float LOGF_LN2_LO = 9.0580006145e-06f;
|
||||
const float LOGF_L1 = 6.6666662693e-01f;
|
||||
const float LOGF_L2 = 4.0000972152e-01f;
|
||||
const float LOGF_L3 = 2.8498786688e-01f;
|
||||
const float LOGF_L4 = 2.4279078841e-01f;
|
||||
|
||||
const double SQRT2 = 1.41421356237309504880;
|
||||
const float SQRT2F = 1.41421356237309504880f;
|
||||
|
||||
fn double log(double x) @extern("log")
|
||||
{
|
||||
if (x != x) return x;
|
||||
if (x < 0.0) return double.nan;
|
||||
if (x == 0.0) return -double.inf;
|
||||
if (x == double.inf) return double.inf;
|
||||
|
||||
int k;
|
||||
double f = frexp(x, &k);
|
||||
if (f < SQRT2 * 0.5)
|
||||
{
|
||||
f *= 2.0;
|
||||
k--;
|
||||
}
|
||||
|
||||
// polynomial approximation of log(1 + f), with f in [0, sqrt(2) - 1]
|
||||
f -= 1.0;
|
||||
double s = f / (2.0 + f);
|
||||
double z = s * s;
|
||||
double w = z * z;
|
||||
|
||||
// even-part polynomial terms (t1) and odd-part polynomial terms (t2)
|
||||
double t1 = w * (LOG_L1 + w * (LOG_L3 + w * LOG_L5));
|
||||
double t2 = z * (LOG_L2 + w * (LOG_L4 + w * LOG_L6));
|
||||
double r = t1 + t2;
|
||||
|
||||
double hfsq = 0.5 * f * f;
|
||||
|
||||
return k * LOG_LN2_HI - ((hfsq - (s * (hfsq + r) + k * LOG_LN2_LO)) - f);
|
||||
}
|
||||
|
||||
fn float logf(float x) @extern("logf")
|
||||
{
|
||||
if (x != x) return x;
|
||||
if (x < 0.0f) return float.nan;
|
||||
if (x == 0.0f) return -float.inf;
|
||||
if (x == float.inf) return float.inf;
|
||||
|
||||
int k;
|
||||
float f = frexpf(x, &k);
|
||||
if (f < SQRT2F * 0.5f)
|
||||
{
|
||||
f *= 2.0f;
|
||||
k--;
|
||||
}
|
||||
|
||||
// polynomial approximation for log(1 + f)
|
||||
f -= 1.0f;
|
||||
float s = f / (2.0f + f);
|
||||
float z = s * s;
|
||||
float w = z * z;
|
||||
|
||||
/*
|
||||
logf uses fewer terms in its polynomial approximation
|
||||
compared to the double precision version because
|
||||
single-precision floating point doesn't benefit from the additional terms.
|
||||
LOGF_L1, ... ,LOGF_L4 provide sufficient accuracy for 32-bit float calculations.
|
||||
*/
|
||||
float t1 = w * (LOGF_L1 + w * LOGF_L3);
|
||||
float t2 = z * (LOGF_L2 + w * LOGF_L4);
|
||||
float r = t1 + t2;
|
||||
|
||||
float hfsq = 0.5f * f * f;
|
||||
return k * LOGF_LN2_HI - ((hfsq - (s * (hfsq + r) + k * LOGF_LN2_LO)) - f);
|
||||
}
|
||||
228
lib/std/math/math_nolibc/log1p.c3
Normal file
228
lib/std/math/math_nolibc/log1p.c3
Normal file
@@ -0,0 +1,228 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/s_log1p.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
/* origin: musl libc /src/math/log1p.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (c) 2005-2020 Rich Felker, et al.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const LN2_HI @local = 6.93147180369123816490e-01; /* 3fe62e42 fee00000 */
|
||||
const LN2_LO @local = 1.90821492927058770002e-10; /* 3dea39ef 35793c76 */
|
||||
const LG1 @local = 6.666666666666735130e-01; /* 3FE55555 55555593 */
|
||||
const LG2 @local = 3.999999999940941908e-01; /* 3FD99999 9997FA04 */
|
||||
const LG3 @local = 2.857142874366239149e-01; /* 3FD24924 94229359 */
|
||||
const LG4 @local = 2.222219843214978396e-01; /* 3FCC71C5 1D8E78AF */
|
||||
const LG5 @local = 1.818357216161805012e-01; /* 3FC74664 96CB03DE */
|
||||
const LG6 @local = 1.531383769920937332e-01; /* 3FC39A09 D078C69F */
|
||||
const LG7 @local = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */
|
||||
|
||||
fn double _log1p(double x) @weak @extern("log1p") @nostrip
|
||||
{
|
||||
uint hx = x.high_word();
|
||||
int k = 1;
|
||||
double c @noinit;
|
||||
double f @noinit;
|
||||
switch
|
||||
{
|
||||
/* 1+x < sqrt(2)+ */
|
||||
case hx < 0x3fda827a || hx >> 31 != 0:
|
||||
switch
|
||||
{
|
||||
/* x <= -1.0 */
|
||||
case hx >= 0xbff00000:
|
||||
if (x == -1) return -double.inf;
|
||||
return double.nan;
|
||||
/* |x| < 2**-53 */
|
||||
case hx << 1 < 0x3ca00000 << 1:
|
||||
/* underflow if subnormal */
|
||||
if ((hx & 0x7ff00000) == 0)
|
||||
{
|
||||
(float)@volatile_load(x);
|
||||
}
|
||||
return x;
|
||||
/* sqrt(2)/2- <= 1+x < sqrt(2)+ */
|
||||
case hx <= 0xbfd2bec4:
|
||||
k = 0;
|
||||
c = 0;
|
||||
f = x;
|
||||
}
|
||||
case hx >= 0x7ff00000:
|
||||
return x;
|
||||
}
|
||||
if (k)
|
||||
{
|
||||
double u = 1 + x;
|
||||
uint hu = u.high_word();
|
||||
hu += 0x3ff00000 - 0x3fe6a09e;
|
||||
k = (int)(hu >> 20) - 0x3ff;
|
||||
/* correction term ~ log(1+x)-log(u), avoid underflow in c/u */
|
||||
if (k < 54)
|
||||
{
|
||||
c = (k >= 2) ? 1. - (u - x) : x - (u - 1.);
|
||||
c /= u;
|
||||
}
|
||||
else
|
||||
{
|
||||
c = 0;
|
||||
}
|
||||
/* reduce u into [sqrt(2)/2, sqrt(2)] */
|
||||
hu = (hu & 0x000fffff) + 0x3fe6a09e;
|
||||
u = bitcast(((ulong)hu << 32) | (bitcast(u, ulong) & 0xffffffff) , double);
|
||||
f = u - 1.;
|
||||
}
|
||||
double hfsq = 0.5 * f * f;
|
||||
double s = f / (2. + f);
|
||||
double z = s * s;
|
||||
double w = z * z;
|
||||
double t1 = w * (LG2 + w * (LG4 + w * LG6));
|
||||
double t2 = z * (LG1 + w * (LG3 + w * (LG5 + w * LG7)));
|
||||
double r = t1 + t2;
|
||||
double dk = k;
|
||||
return s * (hfsq + r) + (dk * LN2_LO + c) - hfsq + f + dk * LN2_HI;
|
||||
}
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/s_log1pf.c */
|
||||
/*
|
||||
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
|
||||
*/
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
/* origin: musl libc /src/math/log1pf.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (c) 2005-2020 Rich Felker, et al.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const float LN2_HI_F @local = 6.9313812256e-01; /* 0x3f317180 */
|
||||
const float LN2_LO_F @local = 9.0580006145e-06; /* 0x3717f7d1 */
|
||||
/* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */
|
||||
const float LG1_F @local = 0xaaaaaa.0p-24; /* 0.66666662693 */
|
||||
const float LG2_F @local = 0xccce13.0p-25; /* 0.40000972152 */
|
||||
const float LG3_F @local = 0x91e9ee.0p-25; /* 0.28498786688 */
|
||||
const float LG4_F @local = 0xf89e26.0p-26; /* 0.24279078841 */
|
||||
|
||||
fn float _log1pf(float x) @weak @extern("log1pf") @nostrip
|
||||
{
|
||||
uint ix = x.word();
|
||||
int k = 1;
|
||||
float c @noinit;
|
||||
float f @noinit;
|
||||
switch
|
||||
{
|
||||
/* 1+x < sqrt(2)+ */
|
||||
case ix < 0x3ed413d0 || ix >> 31 != 0:
|
||||
switch
|
||||
{
|
||||
/* x <= -1.0 */
|
||||
case ix >= 0xbf800000:
|
||||
if (x == -1) return -float.inf;
|
||||
return float.nan;
|
||||
/* |x| < 2**-24 */
|
||||
case ix << 1 < 0x33800000 << 1:
|
||||
/* underflow if subnormal */
|
||||
if ((ix & 0x7f800000) == 0)
|
||||
{
|
||||
float v = @volatile_load(x);
|
||||
v = v * v;
|
||||
}
|
||||
return x;
|
||||
/* sqrt(2)/2- <= 1+x < sqrt(2)+ */
|
||||
case ix <= 0xbe95f619:
|
||||
k = 0;
|
||||
c = 0;
|
||||
f = x;
|
||||
}
|
||||
case ix >= 0x7f800000:
|
||||
return x;
|
||||
}
|
||||
if (k)
|
||||
{
|
||||
float u = 1 + x;
|
||||
uint iu = u.word();
|
||||
iu += 0x3f800000 - 0x3f3504f3;
|
||||
k = (int)(iu >> 23) - 0x7f;
|
||||
/* correction term ~ log(1+x)-log(u), avoid underflow in c/u */
|
||||
if (k < 25)
|
||||
{
|
||||
c = (k >= 2) ? 1.f - (u - x) : x - (u - 1.f);
|
||||
c /= u;
|
||||
}
|
||||
else
|
||||
{
|
||||
c = 0;
|
||||
}
|
||||
/* reduce u into [sqrt(2)/2, sqrt(2)] */
|
||||
iu = (iu & 0x007fffff) + 0x3f3504f3;
|
||||
f = bitcast(iu, float) - 1.f;
|
||||
}
|
||||
float hfsq = 0.5f * f * f;
|
||||
float s = f / (2.f + f);
|
||||
float z = s * s;
|
||||
float w = z * z;
|
||||
float t1 = w * (LG2_F + w * LG4_F);
|
||||
float t2 = z * (LG1_F + w * LG3_F);
|
||||
float r = t1 + t2;
|
||||
float dk = k;
|
||||
return s * (hfsq + r) + (dk * LN2_LO_F + c) - hfsq + f + dk * LN2_HI_F;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc;
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
const double TOINT = 1 / math::DOUBLE_EPSILON;
|
||||
const double TOINT15 = 1.5 / math::DOUBLE_EPSILON;
|
||||
|
||||
@@ -1,11 +1,109 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn float powf_broken(float x, float f) @extern("powf") @weak @nostrip
|
||||
fn double pow(double x, double y) @extern("pow")
|
||||
{
|
||||
unreachable("'powf' not supported");
|
||||
if (x != x || y != y) return double.nan;
|
||||
|
||||
if (y == double.inf)
|
||||
{
|
||||
if (x == 1.0 || x == -1.0) return 1.0;
|
||||
return (_fabs(x) < 1.0) ? 0.0 : double.inf;
|
||||
}
|
||||
if (y == -double.inf)
|
||||
{
|
||||
if (x == 1.0 || x == -1.0) return 1.0;
|
||||
return (_fabs(x) < 1.0) ? double.inf : 0.0;
|
||||
}
|
||||
if (x == double.inf)
|
||||
{
|
||||
return (y < 0.0) ? 0.0 : double.inf;
|
||||
}
|
||||
if (x == -double.inf)
|
||||
{
|
||||
if (y != _floor(y)) return -double.nan;
|
||||
if (y < 0.0) return 0.0;
|
||||
return ((int)y & 1) ? -double.inf : double.inf;
|
||||
}
|
||||
|
||||
if (y == 0.0) return 1.0;
|
||||
|
||||
if (x == 0.0)
|
||||
{
|
||||
if (y < 0.0) return double.inf;
|
||||
if (y > 0.0) return 0.0;
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
if (y == 1.0) return x;
|
||||
if (x == 1.0) return 1.0;
|
||||
|
||||
if (x < 0.0)
|
||||
{
|
||||
if (y != _floor(y)) return double.nan;
|
||||
return ((int)y & 1) ? -pow(-x, y) : pow(-x, y);
|
||||
}
|
||||
|
||||
double result = exp(y * log(x));
|
||||
|
||||
if (result == double.inf || result == -double.inf)
|
||||
{
|
||||
return (y < 0.0) ? 0.0 : double.inf;
|
||||
}
|
||||
if (result == 0.0) return 0.0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn double pow_broken(double x, double y) @extern("pow") @weak @nostrip
|
||||
fn float powf(float x, float y) @extern("powf")
|
||||
{
|
||||
unreachable("'pow' not supported");
|
||||
}
|
||||
if (x != x || y != y) return float.nan;
|
||||
|
||||
if (y == float.inf)
|
||||
{
|
||||
if (x == 1.0f || x == -1.0f) return 1.0f;
|
||||
return (_fabsf(x) < 1.0f) ? 0.0f : float.inf;
|
||||
}
|
||||
if (y == -float.inf)
|
||||
{
|
||||
if (x == 1.0f || x == -1.0f) return 1.0f;
|
||||
return (_fabsf(x) < 1.0f) ? float.inf : 0.0f;
|
||||
}
|
||||
if (x == float.inf)
|
||||
{
|
||||
return (y < 0.0f) ? 0.0f : float.inf;
|
||||
}
|
||||
if (x == -float.inf)
|
||||
{
|
||||
if (y != _floorf(y)) return float.nan;
|
||||
if (y < 0.0f) return 0.0f;
|
||||
return ((int)y & 1) ? -float.inf : float.inf;
|
||||
}
|
||||
|
||||
if (y == 0.0f) return 1.0f;
|
||||
|
||||
if (x == 0.0f)
|
||||
{
|
||||
if (y < 0.0f) return float.inf;
|
||||
if (y > 0.0f) return 0.0f;
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
if (y == 1.0f) return x;
|
||||
if (x == 1.0f) return 1.0f;
|
||||
|
||||
if (x < 0.0f)
|
||||
{
|
||||
if (y != _floorf(y)) return float.nan;
|
||||
return ((int)y & 1) ? -powf(-x, y) : powf(-x, y);
|
||||
}
|
||||
|
||||
float result = expf(y * logf(x));
|
||||
|
||||
if (result == float.inf || result == -float.inf)
|
||||
{
|
||||
return (y < 0.0f) ? 0.0f : float.inf;
|
||||
}
|
||||
if (result == 0.0f) return 0.0f;
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
import std::math;
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/e_rem_pio2f.c */
|
||||
@@ -23,11 +23,11 @@ import std::math;
|
||||
* use __rem_pio2_large() for large x
|
||||
*/
|
||||
|
||||
<*
|
||||
/*
|
||||
* invpio2: 53 bits of 2/pi
|
||||
* pio2_1: first 25 bits of pi/2
|
||||
* pio2_1t: pi/2 - pio2_1
|
||||
*>
|
||||
*/
|
||||
fn int __rem_pio2f(float x, double *y)
|
||||
{
|
||||
const double PIO4 = 0x1.921fb6p-1;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double _round(double x) @extern("round") @weak @nostrip
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double _scalbn(double x, int n) @weak @extern("scalbn") @nostrip
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/s_sinf.c */
|
||||
/*
|
||||
@@ -18,7 +18,7 @@ module std::math::nolibc @if(env::NO_LIBC);
|
||||
|
||||
fn float _sinf(float x) @weak @extern("sinf") @nostrip
|
||||
{
|
||||
uint ix = bitcast(x, uint);
|
||||
uint ix = x.word();
|
||||
int sign = ix >> 31;
|
||||
ix &= 0x7fffffff;
|
||||
switch
|
||||
@@ -87,8 +87,7 @@ fn float _sinf(float x) @weak @extern("sinf") @nostrip
|
||||
fn double sin(double x) @extern("sin") @weak @nostrip
|
||||
{
|
||||
// High word of x.
|
||||
uint ix = (uint)(bitcast(x, ulong) >> 32);
|
||||
ix &= 0x7fffffff;
|
||||
uint ix = x.high_word() & 0x7fffffff;
|
||||
switch
|
||||
{
|
||||
// |x| ~< pi/4
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/s_sinf.c */
|
||||
/*
|
||||
@@ -18,8 +18,7 @@ module std::math::nolibc @if(env::NO_LIBC);
|
||||
|
||||
fn void sincosf(float x, float *sin, float *cos) @extern("__sincosf") @weak @nostrip
|
||||
{
|
||||
|
||||
uint ix = bitcast(x, uint);
|
||||
uint ix = x.word();
|
||||
uint sign = ix >> 31;
|
||||
ix &= 0x7fffffff;
|
||||
|
||||
@@ -108,8 +107,7 @@ fn void sincosf(float x, float *sin, float *cos) @extern("__sincosf") @weak @nos
|
||||
fn void sincos(double x, double *sin, double *cos) @extern("__sincos") @weak @nostrip
|
||||
{
|
||||
// High word of x.
|
||||
uint ix = (uint)(bitcast(x, ulong) >> 32);
|
||||
ix &= 0x7fffffff;
|
||||
uint ix = x.high_word() & 0x7fffffff;
|
||||
|
||||
// |x| ~< pi/4
|
||||
switch
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/s_tan.c */
|
||||
/*
|
||||
@@ -14,7 +14,7 @@ module std::math::nolibc @if(env::NO_LIBC);
|
||||
|
||||
fn double tan(double x) @extern("tan") @weak @nostrip
|
||||
{
|
||||
uint ix = (uint)(bitcast(x, ulong) >> 32);
|
||||
uint ix = x.high_word();
|
||||
ix &= 0x7fffffff;
|
||||
|
||||
// |x| ~< pi/4
|
||||
@@ -59,7 +59,7 @@ fn double tan(double x) @extern("tan") @weak @nostrip
|
||||
|
||||
fn float tanf(float x) @extern("tanf") @weak @nostrip
|
||||
{
|
||||
uint ix = bitcast(x, uint);
|
||||
uint ix = x.word();
|
||||
uint sign = ix >> 31;
|
||||
ix &= 0x7fffffff;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double sincos_broken(double x) @extern("sincos") @weak @nostrip
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC);
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double _trunc(double x) @weak @extern("trunc") @nostrip
|
||||
{
|
||||
|
||||
@@ -52,23 +52,26 @@ struct Poll
|
||||
PollEvents revents;
|
||||
}
|
||||
|
||||
fn ulong! poll_ms(Poll[] polls, long timeout_ms)
|
||||
<*
|
||||
@param [inout] polls
|
||||
@param timeout "duration to poll (clamped to CInt.max ms), or POLL_FOREVER."
|
||||
*>
|
||||
fn ulong! poll(Poll[] polls, Duration timeout)
|
||||
{
|
||||
return poll(polls, time::ms(timeout_ms)) @inline;
|
||||
return poll_ms(polls, timeout.to_ms()) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [inout] polls
|
||||
@param timeout "duration to poll."
|
||||
@param timeout_ms "duration to poll in ms or -1. Clamped to CInt.max"
|
||||
*>
|
||||
fn ulong! poll(Poll[] polls, Duration timeout)
|
||||
fn ulong! poll_ms(Poll[] polls, long timeout_ms)
|
||||
{
|
||||
long time_ms = timeout.to_ms();
|
||||
if (time_ms > CInt.max) time_ms = CInt.max;
|
||||
if (timeout_ms > CInt.max) timeout_ms = CInt.max;
|
||||
$if env::WIN32:
|
||||
CInt result = win32_WSAPoll((Win32_LPWSAPOLLFD)polls.ptr, (Win32_ULONG)polls.len, (CInt)time_ms);
|
||||
CInt result = win32_WSAPoll((Win32_LPWSAPOLLFD)polls.ptr, (Win32_ULONG)polls.len, (CInt)timeout_ms);
|
||||
$else
|
||||
CInt result = os::poll((Posix_pollfd*)polls.ptr, (Posix_nfds_t)polls.len, (CInt)time_ms);
|
||||
CInt result = os::poll((Posix_pollfd*)polls.ptr, (Posix_nfds_t)polls.len, (CInt)timeout_ms);
|
||||
$endif
|
||||
return result < 0 ? os::socket_error()? : (ulong)result;
|
||||
}
|
||||
@@ -129,7 +132,7 @@ $endif
|
||||
return (usz)n;
|
||||
}
|
||||
|
||||
fn char! Socket.read_byte(&self) @dynamic => io::@read_byte_using_read(self);
|
||||
fn char! Socket.read_byte(&self) @dynamic => io::read_byte_using_read(self);
|
||||
|
||||
fn usz! Socket.write(&self, char[] bytes) @dynamic
|
||||
{
|
||||
@@ -142,7 +145,7 @@ $endif
|
||||
return (usz)n;
|
||||
}
|
||||
|
||||
fn void! Socket.write_byte(&self, char byte) @dynamic => io::@write_byte_using_write(self, byte);
|
||||
fn void! Socket.write_byte(&self, char byte) @dynamic => io::write_byte_using_write(self, byte);
|
||||
|
||||
fn void! Socket.destroy(&self) @dynamic
|
||||
{
|
||||
|
||||
389
lib/std/net/url.c3
Normal file
389
lib/std/net/url.c3
Normal file
@@ -0,0 +1,389 @@
|
||||
module std::net::url;
|
||||
|
||||
import std::io, std::collections::map, std::collections::list;
|
||||
|
||||
fault UrlParsingResult
|
||||
{
|
||||
EMPTY,
|
||||
INVALID_SCHEME,
|
||||
INVALID_USER,
|
||||
INVALID_PASSWORD,
|
||||
INVALID_HOST,
|
||||
INVALID_PATH,
|
||||
INVALID_FRAGMENT,
|
||||
}
|
||||
|
||||
<*
|
||||
Represents the actual (decoded) Url.
|
||||
|
||||
An Url can be parsed from a String with `new_parse()` or `temp_parse()`. The
|
||||
parsed fields are decoded. The only field that is not decoded is `query`.
|
||||
To access the decoded query values, use `new_parse_query(query)`.
|
||||
|
||||
`Url.to_string()` will re-assemble the fields into a valid Url string with
|
||||
proper percent-encoded values.
|
||||
|
||||
If the Url struct fields are filled in manually, use the actual (un-encoded)
|
||||
values. To create a raw query string, initialize an `UrlQueryValues` map, use
|
||||
`UrlQueryValues.add()` to add the query parameters and, finally, call
|
||||
`UrlQueryValues.to_string()`.
|
||||
*>
|
||||
struct Url(Printable)
|
||||
{
|
||||
String scheme;
|
||||
String host;
|
||||
uint port;
|
||||
String username;
|
||||
String password;
|
||||
String path;
|
||||
String query;
|
||||
String fragment;
|
||||
|
||||
Allocator allocator;
|
||||
}
|
||||
|
||||
<*
|
||||
Parse a URL string into a Url struct.
|
||||
|
||||
@param [in] url_string
|
||||
@require url_string.len > 0 "the url_string must be len 1 or more"
|
||||
@return "the parsed Url"
|
||||
*>
|
||||
fn Url! temp_parse(String url_string) => new_parse(url_string, allocator::temp());
|
||||
|
||||
<*
|
||||
Parse a URL string into a Url struct.
|
||||
|
||||
@param [in] url_string
|
||||
@require url_string.len > 0 "the url_string must be len 1 or more"
|
||||
@return "the parsed Url"
|
||||
*>
|
||||
fn Url! new_parse(String url_string, Allocator allocator = allocator::heap())
|
||||
{
|
||||
url_string = url_string.trim();
|
||||
if (!url_string) return UrlParsingResult.EMPTY?;
|
||||
Url url = { .allocator = allocator };
|
||||
|
||||
// Parse scheme
|
||||
if (try pos = url_string.index_of("://"))
|
||||
{
|
||||
if (!pos) return UrlParsingResult.INVALID_SCHEME?;
|
||||
url.scheme = url_string[:pos].copy(allocator);
|
||||
url_string = url_string[url.scheme.len + 3 ..];
|
||||
}
|
||||
else if (try pos = url_string.index_of(":"))
|
||||
{
|
||||
// Handle schemes without authority like 'mailto:'
|
||||
if (!pos) return UrlParsingResult.INVALID_SCHEME?;
|
||||
url.scheme = url_string[:pos].copy(allocator);
|
||||
url.path = decode(url_string[pos + 1 ..], PATH, allocator) ?? UrlParsingResult.INVALID_PATH?!;
|
||||
return url;
|
||||
}
|
||||
|
||||
// Parse host, port
|
||||
if (url.scheme != "urn")
|
||||
{
|
||||
usz authority_end = url_string.index_of_chars("/?#") ?? url_string.len;
|
||||
String authority = url_string[:authority_end];
|
||||
|
||||
if (try user_info_end = authority.index_of_char('@'))
|
||||
{
|
||||
String userinfo = authority[:user_info_end];
|
||||
String username @noinit;
|
||||
String password;
|
||||
@pool(allocator)
|
||||
{
|
||||
String[] userpass = userinfo.tsplit(":", 2);
|
||||
username = userpass[0];
|
||||
if (!username.len) return UrlParsingResult.INVALID_USER?;
|
||||
url.host =
|
||||
|
||||
url.username = decode(username, HOST, allocator) ?? UrlParsingResult.INVALID_USER?!;
|
||||
if (userpass.len) url.password = decode(userpass[1], USERPASS, allocator) ?? UrlParsingResult.INVALID_PASSWORD?!;
|
||||
};
|
||||
authority = authority[userinfo.len + 1 ..];
|
||||
}
|
||||
|
||||
// Check for IPv6 address in square brackets
|
||||
String host;
|
||||
if (authority.starts_with("[") && authority.contains("]"))
|
||||
{
|
||||
usz ipv6_end = authority.index_of("]")!;
|
||||
host = authority[0 .. ipv6_end]; // Includes closing bracket
|
||||
if ((ipv6_end + 1) < authority.len && authority[.. ipv6_end] == ":")
|
||||
{
|
||||
url.port = authority[.. ipv6_end + 1].to_uint()!;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@pool(allocator)
|
||||
{
|
||||
String[] host_port = authority.tsplit(":", 2);
|
||||
if (host_port.len > 1)
|
||||
{
|
||||
host = host_port[0];
|
||||
url.port = host_port[1].to_uint()!;
|
||||
}
|
||||
else
|
||||
{
|
||||
host = authority;
|
||||
}
|
||||
};
|
||||
}
|
||||
url.host = decode(host, HOST, allocator) ?? UrlParsingResult.INVALID_HOST?!;
|
||||
url_string = url_string[authority_end ..];
|
||||
}
|
||||
|
||||
// Parse path
|
||||
usz! query_index = url_string.index_of_char('?');
|
||||
usz! fragment_index = url_string.index_of_char('#');
|
||||
|
||||
if (@ok(query_index) || @ok(fragment_index))
|
||||
{
|
||||
usz path_end = min(query_index ?? url_string.len, fragment_index ?? url_string.len);
|
||||
url.path = decode(url_string[:path_end], PATH, allocator) ?? UrlParsingResult.INVALID_PATH?!;
|
||||
url_string = url_string[path_end ..];
|
||||
}
|
||||
else
|
||||
{
|
||||
url.path = decode(url_string, PATH, allocator) ?? UrlParsingResult.INVALID_PATH?!;
|
||||
url_string = "";
|
||||
}
|
||||
|
||||
// Remove the path part from url for further parsing
|
||||
|
||||
|
||||
// Parse query
|
||||
if (url_string.starts_with("?"))
|
||||
{
|
||||
usz index = url_string.index_of_char('#') ?? url_string.len;
|
||||
url.query = url_string[1 .. index - 1].copy(allocator);
|
||||
url_string = url_string[index ..];
|
||||
}
|
||||
|
||||
// Parse fragment
|
||||
if (url_string.starts_with("#"))
|
||||
{
|
||||
url.fragment = decode(url_string[1..], FRAGMENT, allocator) ?? UrlParsingResult.INVALID_FRAGMENT?!;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
<*
|
||||
Stringify a Url struct.
|
||||
|
||||
@param [in] self
|
||||
@param [inout] allocator
|
||||
@return "Url as a string"
|
||||
*>
|
||||
fn String Url.to_string(&self, Allocator allocator = allocator::heap()) @dynamic
|
||||
{
|
||||
@pool(allocator)
|
||||
{
|
||||
DString builder = dstring::temp_new();
|
||||
|
||||
// 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);
|
||||
|
||||
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)
|
||||
{
|
||||
builder.append_char(':');
|
||||
builder.appendf("%d", self.port);
|
||||
}
|
||||
|
||||
// Add path
|
||||
String path = temp_encode(self.path, PATH);
|
||||
builder.append_chars(path);
|
||||
|
||||
// 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 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>);
|
||||
|
||||
struct UrlQueryValues
|
||||
{
|
||||
inline HashMap(<String, UrlQueryValueList>) map;
|
||||
UrlQueryValueList key_order;
|
||||
}
|
||||
|
||||
<*
|
||||
Parse the query parameters of the Url into a UrlQueryValues map.
|
||||
|
||||
@param [in] query
|
||||
@return "a UrlQueryValues HashMap"
|
||||
*>
|
||||
fn UrlQueryValues temp_parse_query(String query) => parse_query(query, allocator::temp());
|
||||
|
||||
<*
|
||||
Parse the query parameters of the Url into a UrlQueryValues map.
|
||||
|
||||
@param [in] query
|
||||
@return "a UrlQueryValues HashMap"
|
||||
*>
|
||||
fn UrlQueryValues new_parse_query(String query) => parse_query(query, allocator::heap());
|
||||
|
||||
<*
|
||||
Parse the query parameters of the Url into a UrlQueryValues map.
|
||||
|
||||
@param [in] query
|
||||
@param [inout] allocator
|
||||
@return "a UrlQueryValues HashMap"
|
||||
*>
|
||||
fn UrlQueryValues parse_query(String query, Allocator allocator)
|
||||
{
|
||||
UrlQueryValues vals;
|
||||
vals.map.init(allocator);
|
||||
vals.key_order.new_init(allocator: allocator);
|
||||
|
||||
Splitter raw_vals = query.tokenize("&");
|
||||
while (try String rv = raw_vals.next())
|
||||
{
|
||||
@pool(allocator)
|
||||
{
|
||||
String[] parts = rv.tsplit("=", 2);
|
||||
String key = temp_decode(parts[0], QUERY) ?? parts[0];
|
||||
vals.add(key, parts.len == 1 ? key : (temp_decode(parts[1], QUERY) ?? parts[1]));
|
||||
};
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
|
||||
<*
|
||||
Add copies of the key and value strings to the UrlQueryValues map. These
|
||||
copies are freed when the UrlQueryValues map is freed.
|
||||
|
||||
@param [in] self
|
||||
@param key
|
||||
@param value
|
||||
@return "a UrlQueryValues map"
|
||||
*>
|
||||
fn UrlQueryValues* UrlQueryValues.add(&self, String key, String value)
|
||||
{
|
||||
String value_copy = value.copy(self.allocator);
|
||||
if (try existing = self.get_ref(key))
|
||||
{
|
||||
existing.push(value_copy);
|
||||
}
|
||||
else
|
||||
{
|
||||
UrlQueryValueList new_list;
|
||||
new_list.new_init_with_array({ value_copy }, self.allocator);
|
||||
(*self)[key] = new_list;
|
||||
self.key_order.push(key.copy(self.allocator));
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Stringify UrlQueryValues into an encoded query string.
|
||||
|
||||
@param [in] self
|
||||
@param [inout] allocator
|
||||
@return "a percent-encoded query string"
|
||||
*>
|
||||
fn String UrlQueryValues.to_string(&self, Allocator allocator = allocator::heap()) @dynamic
|
||||
{
|
||||
@pool(allocator)
|
||||
{
|
||||
DString builder = dstring::temp_new();
|
||||
|
||||
usz i;
|
||||
foreach (key: self.key_order)
|
||||
{
|
||||
String encoded_key = temp_encode(key, QUERY);
|
||||
|
||||
UrlQueryValueList! values = self.map.get(key);
|
||||
if (catch values) continue;
|
||||
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
fn void UrlQueryValues.free(&self)
|
||||
{
|
||||
self.map.@each(;String key, UrlQueryValueList values)
|
||||
{
|
||||
foreach (value: values) value.free(self.allocator);
|
||||
values.free();
|
||||
};
|
||||
self.map.free();
|
||||
|
||||
foreach (&key: self.key_order) key.free(self.allocator);
|
||||
self.key_order.free();
|
||||
}
|
||||
|
||||
<*
|
||||
Free an Url struct.
|
||||
|
||||
@param [in] self
|
||||
*>
|
||||
fn void Url.free(&self)
|
||||
{
|
||||
if (!self.allocator) return;
|
||||
self.scheme.free(self.allocator);
|
||||
self.host.free(self.allocator);
|
||||
self.username.free(self.allocator);
|
||||
self.password.free(self.allocator);
|
||||
self.path.free(self.allocator);
|
||||
self.query.free(self.allocator);
|
||||
self.fragment.free(self.allocator);
|
||||
}
|
||||
197
lib/std/net/url_encoding.c3
Normal file
197
lib/std/net/url_encoding.c3
Normal file
@@ -0,0 +1,197 @@
|
||||
<*
|
||||
This module section provides encoding and decoding functions for URL
|
||||
components according to RFC 3986.
|
||||
*>
|
||||
module std::net::url;
|
||||
import std::encoding::hex;
|
||||
|
||||
enum UrlEncodingMode : char (String allowed)
|
||||
{
|
||||
UNRESERVED = "-_.~", // section 2.3
|
||||
PATH = "$&+,/:;=@", // section 3.3
|
||||
HOST = "!$&'()*+,;=:[]", // section 3.2.2 (also include ':', '[', ']' for ipv6 hosts)
|
||||
USERPASS = ";:&=+$,", // section 3.2.1
|
||||
QUERY = "", // section 3.4
|
||||
FRAGMENT = "$&+,/:;=?@!()*", // section 4.1
|
||||
}
|
||||
|
||||
fault UrlDecodingError
|
||||
{
|
||||
INVALID_HEX
|
||||
}
|
||||
|
||||
<*
|
||||
Returns true if char c should be encoded according to RFC 3986.
|
||||
|
||||
@param c "Character to check if it should be encoded."
|
||||
@param mode "Url encoding mode."
|
||||
*>
|
||||
fn bool should_encode(char c, UrlEncodingMode mode) @private
|
||||
{
|
||||
// alphanumeric characters are allowed
|
||||
if (c.is_alnum()) return false;
|
||||
|
||||
// unreserved characters are allowed
|
||||
if (try UrlEncodingMode.UNRESERVED.allowed.index_of_char(c)) return false;
|
||||
|
||||
// some mode-specific characters are allowed
|
||||
if (try mode.allowed.index_of_char(c)) return false;
|
||||
|
||||
// everything else must be encoded
|
||||
return true;
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the length of the percent-encoded string.
|
||||
*>
|
||||
fn usz encode_len(String s, UrlEncodingMode mode) @inline
|
||||
{
|
||||
usz n;
|
||||
foreach (c: s)
|
||||
{
|
||||
if (!should_encode(c, mode)) continue;
|
||||
if (c != ' ' || mode != QUERY)
|
||||
{
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return s.len + 2 * n;
|
||||
}
|
||||
|
||||
<*
|
||||
Encode the string s for a given encoding mode.
|
||||
Returned string must be freed.
|
||||
|
||||
@param s "String to encode"
|
||||
@param mode "Url encoding mode"
|
||||
@param [inout] allocator
|
||||
@return "Percent-encoded String"
|
||||
*>
|
||||
fn String encode(String s, UrlEncodingMode mode, Allocator allocator)
|
||||
{
|
||||
usz n = encode_len(s, mode);
|
||||
@pool(allocator)
|
||||
{
|
||||
DString builder = dstring::temp_with_capacity(n);
|
||||
|
||||
foreach(i, c: s)
|
||||
{
|
||||
switch
|
||||
{
|
||||
// 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());
|
||||
|
||||
// use char, no encoding needed
|
||||
default:
|
||||
builder.append_char(c);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.copy_str(allocator);
|
||||
};
|
||||
}
|
||||
|
||||
<*
|
||||
Encode the string s for a given encoding mode.
|
||||
Returned string must be freed.
|
||||
|
||||
@param s "String to encode"
|
||||
@param mode "Url encoding mode"
|
||||
@return "Percent-encoded String"
|
||||
*>
|
||||
fn String new_encode(String s, UrlEncodingMode mode) => encode(s, mode, allocator::heap());
|
||||
|
||||
<*
|
||||
Encode string s for a given encoding mode, stored on the temp allocator.
|
||||
|
||||
@param s "String to encode"
|
||||
@param mode "Url encoding mode"
|
||||
@return "Percent-encoded String"
|
||||
*>
|
||||
fn String temp_encode(String s, UrlEncodingMode mode) => encode(s, mode, allocator::temp());
|
||||
|
||||
<*
|
||||
Calculate the length of the percent-decoded string.
|
||||
|
||||
@return! UrlDecodingError.INVALID_HEX
|
||||
*>
|
||||
fn usz! decode_len(String s, UrlEncodingMode mode) @inline
|
||||
{
|
||||
usz n;
|
||||
foreach (i, c: s)
|
||||
{
|
||||
if (c != '%') continue;
|
||||
if (i + 2 >= s.len || !s[i+1].is_xdigit() || !s[i+2].is_xdigit())
|
||||
{
|
||||
return UrlDecodingError.INVALID_HEX?;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
return s.len - 2 * n;
|
||||
}
|
||||
|
||||
<*
|
||||
Decode string s for a given encoding mode.
|
||||
Returned string must be freed.
|
||||
|
||||
@param s "String to decode"
|
||||
@param mode "Url encoding mode"
|
||||
@param [inout] allocator
|
||||
@return "Percent-decoded String"
|
||||
*>
|
||||
fn String! decode(String s, UrlEncodingMode mode, Allocator allocator)
|
||||
{
|
||||
usz n = decode_len(s, mode)!;
|
||||
@pool(allocator)
|
||||
{
|
||||
DString builder = dstring::temp_with_capacity(n);
|
||||
|
||||
for (usz i = 0; i < s.len; i++)
|
||||
{
|
||||
switch (s[i])
|
||||
{
|
||||
// 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) ? ' ' : '+');
|
||||
|
||||
// use char, no decoding needed
|
||||
default:
|
||||
builder.append_char(s[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.copy_str(allocator);
|
||||
};
|
||||
}
|
||||
|
||||
<*
|
||||
Decode string s for a given encoding mode.
|
||||
Returned string must be freed.
|
||||
|
||||
@param s "String to decode"
|
||||
@param mode "Url encoding mode"
|
||||
@return "Percent-decoded String"
|
||||
*>
|
||||
fn String! new_decode(String s, UrlEncodingMode mode) => decode(s, mode, allocator::heap());
|
||||
|
||||
<*
|
||||
Decode string s for a given encoding mode, stored on the temp allocator.
|
||||
|
||||
@param s "String to decode"
|
||||
@param mode "Url encoding mode"
|
||||
@return "Percent-decoded String"
|
||||
*>
|
||||
fn String! temp_decode(String s, UrlEncodingMode mode) => decode(s, mode, allocator::temp());
|
||||
@@ -5,10 +5,12 @@ distinct ObjcMethod = void*;
|
||||
distinct ObjcIvar = void*;
|
||||
distinct ObjcSelector = void*;
|
||||
def ObjcId = void*;
|
||||
def SendVoid = fn void*(void*, ObjcSelector);
|
||||
|
||||
fault ObjcFailure
|
||||
{
|
||||
CLASS_NOT_FOUND
|
||||
CLASS_NOT_FOUND,
|
||||
UNKNOWN_EVENT
|
||||
}
|
||||
|
||||
macro ZString ObjcClass.name(ObjcClass cls) => class_getName(cls);
|
||||
@@ -19,6 +21,9 @@ macro ObjcMethod ObjcClass.method(ObjcClass cls, ObjcSelector name) => class_get
|
||||
macro bool ObjcSelector.equals(ObjcSelector a, ObjcSelector b) => a == b;
|
||||
macro bool ObjcClass.equals(ObjcClass a, ObjcClass b) => a == b;
|
||||
|
||||
fn ObjcId alloc(ObjcClass cls) => objc::msg_send(cls, SendVoid, "alloc");
|
||||
fn void release(ObjcId id) => objc::msg_send(id, SendVoid, "release");
|
||||
|
||||
macro ObjcClass! class_by_name(ZString c)
|
||||
{
|
||||
ObjcClass cls = objc::lookUpClass(c);
|
||||
@@ -51,3 +56,175 @@ extern fn ObjcClass class_getSuperclass(ObjcClass cls);
|
||||
extern fn ObjcMethod class_getClassMethod(ObjcClass cls, ObjcSelector name);
|
||||
extern fn bool class_respondsToSelector(ObjcClass cls, ObjcSelector name);
|
||||
extern fn ObjcSelector sel_registerName(ZString str);
|
||||
extern fn bool class_addIvar(ObjcClass cls, ZString name, int size, double alignment, ZString types);
|
||||
extern fn bool class_addMethod(ObjcClass cls, ObjcSelector name, void* imp, ZString types);
|
||||
|
||||
extern fn ObjcIvar getInstanceVariable(ObjcId id, ZString name, void* outValue) @extern("object_getInstanceVariable");
|
||||
extern fn ObjcIvar setInstanceVariable(ObjcId id, ZString name, void* value) @extern("object_setInstanceVariable");
|
||||
extern fn ObjcClass allocateClassPair(ObjcClass cls, ZString name, uint extraBytes) @extern("objc_allocateClassPair");
|
||||
|
||||
enum StatusItemLength : (double val)
|
||||
{
|
||||
VARIABLE = -1.0,
|
||||
SQUARE = -2.0,
|
||||
}
|
||||
|
||||
enum ApplicationActivationPolicy : (int val)
|
||||
{
|
||||
REGULAR = 0,
|
||||
ACCESSORY = 1,
|
||||
PROHIBITED = 2,
|
||||
}
|
||||
|
||||
enum WindowStyleMask : (int val)
|
||||
{
|
||||
BORDERLESS = 0,
|
||||
TITLED = 1 << 0,
|
||||
CLOSABLE = 1 << 1,
|
||||
MINIATURIZABLE = 1 << 2,
|
||||
RESIZABLE = 1 << 3,
|
||||
TEXTURED_BACKGROUND = 1 << 8,
|
||||
UNIFIED_TITLE_AND_TOOLBAR = 1 << 12,
|
||||
FULL_SCREEN = 1 << 14,
|
||||
FULL_SIZE_CONTENT_VIEW = 1 << 15,
|
||||
UTILITY_WINDOW = 1 << 4,
|
||||
DOC_MODAL_WINDOW = 1 << 6,
|
||||
NONACTIVATING_PANEL = 1 << 7,
|
||||
HUD_WINDOW = 1 << 13
|
||||
}
|
||||
|
||||
enum BackingStore : (int val)
|
||||
{
|
||||
RETAINED = 0,
|
||||
NONRETAINED = 1,
|
||||
BUFFERED = 2
|
||||
}
|
||||
|
||||
enum EventType : (long val)
|
||||
{
|
||||
LEFT_MOUSE_DOWN = 1,
|
||||
LEFT_MOUSE_UP = 2,
|
||||
RIGHT_MOUSE_DOWN = 3,
|
||||
RIGHT_MOUSE_UP = 4,
|
||||
MOUSE_MOVED = 5,
|
||||
LEFT_MOUSE_DRAGGED = 6,
|
||||
RIGHT_MOUSE_DRAGGED = 7,
|
||||
MOUSE_ENTERED = 8,
|
||||
MOUSE_EXITED = 9,
|
||||
KEY_DOWN = 10,
|
||||
KEY_UP = 11,
|
||||
FLAGS_CHANGED = 12,
|
||||
APPKIT_DEFINED = 13,
|
||||
SYSTEM_DEFINED = 14,
|
||||
APPLICATION_DEFINED = 15,
|
||||
PERIODIC = 16,
|
||||
CURSOR_UPDATE = 17,
|
||||
SCROLL_WHEEL = 22,
|
||||
TABLET_POINT = 23,
|
||||
TABLET_PROXIMITY = 24,
|
||||
OTHER_MOUSE_DOWN = 25,
|
||||
OTHER_MOUSE_UP = 26,
|
||||
OTHER_MOUSE_DRAGGED = 27,
|
||||
GESTURE = 29,
|
||||
MAGNIFY = 30,
|
||||
SWIPE = 31,
|
||||
ROTATE = 18,
|
||||
BEGIN_GESTURE = 19,
|
||||
END_GESTURE = 20,
|
||||
SMART_MAGNIFY = 32,
|
||||
QUICK_LOOK = 33,
|
||||
PRESSURE = 34,
|
||||
DIRECT_TOUCH = 37,
|
||||
CHANGE_MODE = 38,
|
||||
}
|
||||
|
||||
fn EventType! event_type_from(int val)
|
||||
{
|
||||
switch(val)
|
||||
{
|
||||
case EventType.LEFT_MOUSE_DOWN.val: return LEFT_MOUSE_DOWN;
|
||||
case EventType.LEFT_MOUSE_UP.val: return LEFT_MOUSE_UP;
|
||||
case EventType.RIGHT_MOUSE_DOWN.val: return RIGHT_MOUSE_DOWN;
|
||||
case EventType.RIGHT_MOUSE_UP.val: return RIGHT_MOUSE_UP;
|
||||
case EventType.MOUSE_MOVED.val: return MOUSE_MOVED;
|
||||
case EventType.LEFT_MOUSE_DRAGGED.val: return LEFT_MOUSE_DRAGGED;
|
||||
case EventType.RIGHT_MOUSE_DRAGGED.val: return RIGHT_MOUSE_DRAGGED;
|
||||
case EventType.MOUSE_ENTERED.val: return MOUSE_ENTERED;
|
||||
case EventType.MOUSE_EXITED.val: return MOUSE_EXITED;
|
||||
case EventType.KEY_DOWN.val: return KEY_DOWN;
|
||||
case EventType.KEY_UP.val: return KEY_UP;
|
||||
case EventType.FLAGS_CHANGED.val: return FLAGS_CHANGED;
|
||||
case EventType.APPKIT_DEFINED.val: return APPKIT_DEFINED;
|
||||
case EventType.SYSTEM_DEFINED.val: return SYSTEM_DEFINED;
|
||||
case EventType.APPLICATION_DEFINED.val: return APPLICATION_DEFINED;
|
||||
case EventType.PERIODIC.val: return PERIODIC;
|
||||
case EventType.CURSOR_UPDATE.val: return CURSOR_UPDATE;
|
||||
case EventType.SCROLL_WHEEL.val: return SCROLL_WHEEL;
|
||||
case EventType.TABLET_POINT.val: return TABLET_POINT;
|
||||
case EventType.TABLET_PROXIMITY.val: return TABLET_PROXIMITY;
|
||||
case EventType.OTHER_MOUSE_DOWN.val: return OTHER_MOUSE_DOWN;
|
||||
case EventType.OTHER_MOUSE_UP.val: return OTHER_MOUSE_UP;
|
||||
case EventType.OTHER_MOUSE_DRAGGED.val: return OTHER_MOUSE_DRAGGED;
|
||||
case EventType.GESTURE.val: return GESTURE;
|
||||
case EventType.MAGNIFY.val: return MAGNIFY;
|
||||
case EventType.SWIPE.val: return SWIPE;
|
||||
case EventType.ROTATE.val: return ROTATE;
|
||||
case EventType.BEGIN_GESTURE.val: return BEGIN_GESTURE;
|
||||
case EventType.END_GESTURE.val: return END_GESTURE;
|
||||
case EventType.SMART_MAGNIFY.val: return SMART_MAGNIFY;
|
||||
case EventType.QUICK_LOOK.val: return QUICK_LOOK;
|
||||
case EventType.PRESSURE.val: return PRESSURE;
|
||||
case EventType.DIRECT_TOUCH.val: return DIRECT_TOUCH;
|
||||
case EventType.CHANGE_MODE.val: return CHANGE_MODE;
|
||||
default: return ObjcFailure.UNKNOWN_EVENT?;
|
||||
}
|
||||
}
|
||||
|
||||
enum EventMask : (long val)
|
||||
{
|
||||
LEFT_MOUSE_DOWN = 1 << EventType.LEFT_MOUSE_DOWN.val,
|
||||
LEFT_MOUSE_UP = 1 << EventType.LEFT_MOUSE_UP.val,
|
||||
RIGHT_MOUSE_DOWN = 1 << EventType.RIGHT_MOUSE_DOWN.val,
|
||||
RIGHT_MOUSE_UP = 1 << EventType.RIGHT_MOUSE_UP.val,
|
||||
MOUSE_MOVED = 1 << EventType.MOUSE_MOVED.val,
|
||||
LEFT_MOUSE_DRAGGED = 1 << EventType.LEFT_MOUSE_DRAGGED.val,
|
||||
RIGHT_MOUSE_DRAGGED = 1 << EventType.RIGHT_MOUSE_DRAGGED.val,
|
||||
MOUSE_ENTERED = 1 << EventType.MOUSE_ENTERED.val,
|
||||
MOUSE_EXITED = 1 << EventType.MOUSE_EXITED.val,
|
||||
KEY_DOWN = 1 << EventType.KEY_DOWN.val,
|
||||
KEY_UP = 1 << EventType.KEY_UP.val,
|
||||
FLAGS_CHANGED = 1 << EventType.FLAGS_CHANGED.val,
|
||||
APPKIT_DEFINED = 1 << EventType.APPKIT_DEFINED.val,
|
||||
SYSTEM_DEFINED = 1 << EventType.SYSTEM_DEFINED.val,
|
||||
APPLICATION_DEFINED = 1 << EventType.APPLICATION_DEFINED.val,
|
||||
PERIODIC = 1 << EventType.PERIODIC.val,
|
||||
CURSOR_UPDATE = 1 << EventType.CURSOR_UPDATE.val,
|
||||
SCROLL_WHEEL = 1 << EventType.SCROLL_WHEEL.val,
|
||||
TABLET_POINT = 1 << EventType.TABLET_POINT.val,
|
||||
TABLET_PROXIMITY = 1 << EventType.TABLET_PROXIMITY.val,
|
||||
OTHER_MOUSE_DOWN = 1 << EventType.OTHER_MOUSE_DOWN.val,
|
||||
OTHER_MOUSE_UP = 1 << EventType.OTHER_MOUSE_UP.val,
|
||||
OTHER_MOUSE_DRAGGED = 1 << EventType.OTHER_MOUSE_DRAGGED.val,
|
||||
GESTURE = 1 << EventType.GESTURE.val,
|
||||
MAGNIFY = 1 << EventType.MAGNIFY.val,
|
||||
SWIPE = 1 << EventType.SWIPE.val,
|
||||
ROTATE = 1 << EventType.ROTATE.val,
|
||||
BEGIN_GESTURE = 1 << EventType.BEGIN_GESTURE.val,
|
||||
END_GESTURE = 1 << EventType.END_GESTURE.val,
|
||||
SMART_MAGNIFY = 1L << EventType.SMART_MAGNIFY.val,
|
||||
DIRECT_TOUCH = 1L << EventType.DIRECT_TOUCH.val,
|
||||
ANY = long.max,
|
||||
}
|
||||
|
||||
enum EventModifierFlag : (int val)
|
||||
{
|
||||
CAPS_LOCK = 1 << 16,
|
||||
SHIFT = 1 << 17,
|
||||
CONTROL = 1 << 18,
|
||||
OPTION = 1 << 19,
|
||||
COMMAND = 1 << 20,
|
||||
NUMERIC_PAD = 1 << 21,
|
||||
FUNCTION = 1 << 23,
|
||||
HELP = 1 << 22,
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ in [0, array.len) where x would be inserted or cmp(i) is true and cmp(j) is true
|
||||
macro usz binarysearch(list, x, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin
|
||||
{
|
||||
usz i;
|
||||
usz len = @len_from_list(list);
|
||||
usz len = len_from_list(list);
|
||||
var $no_cmp = @is_empty_macro_slot(cmp);
|
||||
var $has_context = @is_valid_macro_slot(context);
|
||||
for (usz j = len; i < j;)
|
||||
|
||||
@@ -10,7 +10,7 @@ Sort list using the counting sort algorithm.
|
||||
*>
|
||||
macro countingsort(list, key_fn = EMPTY_MACRO_SLOT) @builtin
|
||||
{
|
||||
usz len = sort::@len_from_list(list);
|
||||
usz len = sort::len_from_list(list);
|
||||
cs::csort(<$typeof(list), $typeof(key_fn)>)(list, 0, len, key_fn, ~((uint)0));
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,15 @@ Sort list using the quick sort algorithm.
|
||||
@require @is_sortable(list) "The list must be indexable and support .len or .len()"
|
||||
@require @is_valid_cmp_fn(cmp, list, context) "Expected a comparison function which compares values"
|
||||
*>
|
||||
macro insertionsort(list, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin
|
||||
macro insertionsort(list, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin @safemacro
|
||||
{
|
||||
usz len = sort::@len_from_list(list);
|
||||
is::isort(<$typeof(list), $typeof(cmp), $typeof(context)>)(list, 0, (isz)len, cmp, context);
|
||||
$if @typekind(list) == POINTER &&& (@typekind(*list) == ARRAY || @typekind(*list) == VECTOR):
|
||||
$typeof((*list)[0])[] list2 = list;
|
||||
is::isort(<$typeof(list2), $typeof(cmp), $typeof(context)>)(list2, 0, list.len, cmp, context);
|
||||
$else
|
||||
usz len = sort::len_from_list(list);
|
||||
is::isort(<$typeof(list), $typeof(cmp), $typeof(context)>)(list, 0, (isz)len, cmp, context);
|
||||
$endif
|
||||
}
|
||||
|
||||
module std::sort::is(<Type, CmpFn, Context>);
|
||||
|
||||
@@ -9,8 +9,28 @@ Sort list using the quick sort algorithm.
|
||||
*>
|
||||
macro quicksort(list, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin
|
||||
{
|
||||
usz len = sort::@len_from_list(list);
|
||||
qs::qsort(<$typeof(list), $typeof(cmp), $typeof(context)>)(list, 0, (isz)len - 1, cmp, context);
|
||||
$if @typekind(list) == POINTER &&& (@typekind(*list) == ARRAY || @typekind(*list) == VECTOR):
|
||||
$typeof((*list)[0])[] list2 = list;
|
||||
qs::qsort(<$typeof(list2), $typeof(cmp), $typeof(context)>)(list2, 0, (isz)list.len - 1, cmp, context);
|
||||
$else
|
||||
usz len = sort::len_from_list(list);
|
||||
qs::qsort(<$typeof(list), $typeof(cmp), $typeof(context)>)(list, 0, (isz)len - 1, cmp, context);
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
Select the (k+1)th smallest element in an unordered list using Hoare's
|
||||
selection algorithm (Quickselect). k should be between 0 and len-1. The data
|
||||
list will be partially sorted.
|
||||
|
||||
@require @is_sortable(list) "The list must be indexable and support .len or .len()"
|
||||
@require @is_valid_cmp_fn(cmp, list, context) "expected a comparison function which compares values"
|
||||
@require @is_valid_context(cmp, context) "Expected a valid context"
|
||||
*>
|
||||
macro quickselect(list, isz k, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin
|
||||
{
|
||||
usz len = sort::len_from_list(list);
|
||||
return qs::qselect(<$typeof(list), $typeof(cmp), $typeof(context)>)(list, 0, (isz)len - 1, k, cmp, context);
|
||||
}
|
||||
|
||||
module std::sort::qs(<Type, CmpFn, Context>);
|
||||
@@ -29,10 +49,6 @@ def Stack = StackElementItem[64] @private;
|
||||
|
||||
fn void qsort(Type list, isz low, isz high, CmpFn cmp, Context context)
|
||||
{
|
||||
var $has_cmp = @is_valid_macro_slot(cmp);
|
||||
var $has_context = @is_valid_macro_slot(context);
|
||||
var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom(CmpFn.paramsof[0].type));
|
||||
|
||||
if (low >= 0 && high >= 0 && low < high)
|
||||
{
|
||||
Stack stack;
|
||||
@@ -48,34 +64,7 @@ fn void qsort(Type list, isz low, isz high, CmpFn cmp, Context context)
|
||||
|
||||
if (l < h)
|
||||
{
|
||||
ElementType pivot = list[l];
|
||||
while (l < h)
|
||||
{
|
||||
$switch
|
||||
$case $cmp_by_value && $has_context:
|
||||
while (cmp(list[h], pivot, context) >= 0 && l < h) h--;
|
||||
if (l < h) list[l++] = list[h];
|
||||
while (cmp(list[l], pivot, context) <= 0 && l < h) l++;
|
||||
$case $cmp_by_value:
|
||||
while (cmp(list[h], pivot) >= 0 && l < h) h--;
|
||||
if (l < h) list[l++] = list[h];
|
||||
while (cmp(list[l], pivot) <= 0 && l < h) l++;
|
||||
$case $has_cmp && $has_context:
|
||||
while (cmp(&list[h], &pivot, context) >= 0 && l < h) h--;
|
||||
if (l < h) list[l++] = list[h];
|
||||
while (cmp(&list[l], &pivot, context) <= 0 && l < h) l++;
|
||||
$case $has_cmp:
|
||||
while (cmp(&list[h], &pivot) >= 0 && l < h) h--;
|
||||
if (l < h) list[l++] = list[h];
|
||||
while (cmp(&list[l], &pivot) <= 0 && l < h) l++;
|
||||
$default:
|
||||
while (greater_eq(list[h], pivot) && l < h) h--;
|
||||
if (l < h) list[l++] = list[h];
|
||||
while (less_eq(list[l], pivot) && l < h) l++;
|
||||
$endswitch
|
||||
if (l < h) list[h--] = list[l];
|
||||
}
|
||||
list[l] = pivot;
|
||||
l = @partition(list, l, h, cmp, context);
|
||||
stack[i + 1].low = l + 1;
|
||||
stack[i + 1].high = stack[i].high;
|
||||
stack[i++].high = l;
|
||||
@@ -91,3 +80,71 @@ fn void qsort(Type list, isz low, isz high, CmpFn cmp, Context context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@require low <= k "kth smalles element is smaller than lower bounds"
|
||||
@require k <= high "kth smalles element is larger than upper bounds"
|
||||
*>
|
||||
fn ElementType! qselect(Type list, isz low, isz high, isz k, CmpFn cmp, Context context)
|
||||
{
|
||||
if (low >= 0 && high >= 0 && low < high)
|
||||
{
|
||||
isz l = low;
|
||||
isz h = high;
|
||||
isz pivot;
|
||||
|
||||
usz max_retries = 64;
|
||||
while (l <= h && max_retries--)
|
||||
{
|
||||
pivot = @partition(list, l, h, cmp, context);
|
||||
if (k == pivot) return list[k];
|
||||
if (k < pivot)
|
||||
{
|
||||
h = pivot - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
l = pivot + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
macro @partition(Type list, isz l, isz h, CmpFn cmp, Context context)
|
||||
{
|
||||
var $has_cmp = @is_valid_macro_slot(cmp);
|
||||
var $has_context = @is_valid_macro_slot(context);
|
||||
var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom(CmpFn.paramsof[0].type));
|
||||
|
||||
ElementType pivot = list[l];
|
||||
while (l < h)
|
||||
{
|
||||
$switch
|
||||
$case $cmp_by_value && $has_context:
|
||||
while (cmp(list[h], pivot, context) >= 0 && l < h) h--;
|
||||
if (l < h) list[l++] = list[h];
|
||||
while (cmp(list[l], pivot, context) <= 0 && l < h) l++;
|
||||
$case $cmp_by_value:
|
||||
while (cmp(list[h], pivot) >= 0 && l < h) h--;
|
||||
if (l < h) list[l++] = list[h];
|
||||
while (cmp(list[l], pivot) <= 0 && l < h) l++;
|
||||
$case $has_cmp && $has_context:
|
||||
while (cmp(&list[h], &pivot, context) >= 0 && l < h) h--;
|
||||
if (l < h) list[l++] = list[h];
|
||||
while (cmp(&list[l], &pivot, context) <= 0 && l < h) l++;
|
||||
$case $has_cmp:
|
||||
while (cmp(&list[h], &pivot) >= 0 && l < h) h--;
|
||||
if (l < h) list[l++] = list[h];
|
||||
while (cmp(&list[l], &pivot) <= 0 && l < h) l++;
|
||||
$default:
|
||||
while (greater_eq(list[h], pivot) && l < h) h--;
|
||||
if (l < h) list[l++] = list[h];
|
||||
while (less_eq(list[l], pivot) && l < h) l++;
|
||||
$endswitch
|
||||
if (l < h) list[h--] = list[l];
|
||||
}
|
||||
list[l] = pivot;
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
module std::sort;
|
||||
|
||||
macro usz @len_from_list(&list)
|
||||
macro usz @len_from_list(&list) @deprecated
|
||||
{
|
||||
return len_from_list(*list);
|
||||
}
|
||||
|
||||
macro usz len_from_list(list)
|
||||
{
|
||||
$if $defined(list.len()):
|
||||
return list.len();
|
||||
@@ -16,6 +21,8 @@ macro bool @is_sortable(#list)
|
||||
return false;
|
||||
$case !$defined(#list.len):
|
||||
return false;
|
||||
$case @typekind(#list) == VECTOR || @typekind(#list) == ARRAY:
|
||||
return false;
|
||||
$case $defined(&#list[0]) &&& !types::is_same($typeof(&#list[0]), $typeof(#list[0])*):
|
||||
return false;
|
||||
$default:
|
||||
|
||||
59
lib/std/sort/sorted.c3
Normal file
59
lib/std/sort/sorted.c3
Normal file
@@ -0,0 +1,59 @@
|
||||
module std::sort;
|
||||
|
||||
<*
|
||||
Returns true if list is sorted in either ascending or descending order.
|
||||
@require @is_sortable(list) "The list must be indexable and support .len or .len()"
|
||||
@require @is_valid_cmp_fn(cmp, list, ctx) "Expected a comparison function which compares values"
|
||||
@require @is_valid_context(cmp, ctx) "Expected a valid context"
|
||||
*>
|
||||
macro bool is_sorted(list, cmp = EMPTY_MACRO_SLOT, ctx = EMPTY_MACRO_SLOT) @builtin
|
||||
{
|
||||
var $Type = $typeof(list);
|
||||
usz len = sort::len_from_list(list);
|
||||
if (len <= 1) return true;
|
||||
var check_sort = fn bool($Type list, usz start, usz end, $typeof(cmp) cmp, $typeof(ctx) ctx)
|
||||
{
|
||||
usz i;
|
||||
int sort_order;
|
||||
|
||||
// determine sort order (ascending or descending)
|
||||
for (i = start; i < end && sort_order == 0; i++)
|
||||
{
|
||||
sort_order = @sort_cmp(list, i, cmp, ctx);
|
||||
}
|
||||
|
||||
// no sort order found, all elements are the same, consider list sorted
|
||||
if (sort_order == 0) return true;
|
||||
|
||||
// compare adjacent elements to the sort order
|
||||
for (; i < end; i++)
|
||||
{
|
||||
if (sort_order * @sort_cmp(list, i, cmp, ctx) < 0) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
return check_sort(list, 0, len - 1, cmp, ctx);
|
||||
}
|
||||
|
||||
macro int @sort_cmp(list, pos, cmp, ctx) @local
|
||||
{
|
||||
var $has_cmp = @is_valid_macro_slot(cmp);
|
||||
var $has_context = @is_valid_macro_slot(ctx);
|
||||
var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom($typeof(cmp).paramsof[0].type));
|
||||
|
||||
var a = list[pos];
|
||||
var b = list[pos+1];
|
||||
|
||||
$switch
|
||||
$case $cmp_by_value && $has_context:
|
||||
return cmp(a, b);
|
||||
$case $cmp_by_value:
|
||||
return cmp(a, b);
|
||||
$case $has_cmp && $has_context:
|
||||
return cmp(&a, &b, ctx);
|
||||
$case $has_cmp:
|
||||
return cmp(&a, &b);
|
||||
$default:
|
||||
return compare_to(a,b);
|
||||
$endswitch
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user