mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Compare commits
310 Commits
0.5.5
...
ci_testing
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b46463563e | ||
|
|
33ce8e8a75 | ||
|
|
05ab0707fc | ||
|
|
d32861193b | ||
|
|
fb4a231703 | ||
|
|
0963ab4cc0 | ||
|
|
a248511d7b | ||
|
|
79a1639f8a | ||
|
|
476a6424ee | ||
|
|
6de17b9ae9 | ||
|
|
cb7116f08b | ||
|
|
15a4e23b22 | ||
|
|
2b0857baf9 | ||
|
|
20b0bf43ad | ||
|
|
17d6f03bae | ||
|
|
4edaf603c9 | ||
|
|
74b8da1e15 | ||
|
|
16cb756d3f | ||
|
|
f1efdf3d98 | ||
|
|
edfea639cf | ||
|
|
9fd9280132 | ||
|
|
d0bb69516a | ||
|
|
dc44254ba1 | ||
|
|
85c682f7e6 | ||
|
|
2a69f93605 | ||
|
|
ad4950130c | ||
|
|
3ccb4b9ec3 | ||
|
|
6bc486400c | ||
|
|
9228dbb8b8 | ||
|
|
e68b453218 | ||
|
|
1dd2b0ec19 | ||
|
|
e7e9d3b8c7 | ||
|
|
800ad9e898 | ||
|
|
ddecf2d5f0 | ||
|
|
09da17dab7 | ||
|
|
1678e2a939 | ||
|
|
9aab962ebc | ||
|
|
baf6e71a80 | ||
|
|
412fa4b12f | ||
|
|
3cae557b88 | ||
|
|
f7c39ae4a9 | ||
|
|
6d93ce9d33 | ||
|
|
031cbae0d6 | ||
|
|
5fbee47c2b | ||
|
|
2cd25a489a | ||
|
|
e67586b8b0 | ||
|
|
7d643942b4 | ||
|
|
2257a7f4ec | ||
|
|
f8ca173fd8 | ||
|
|
b08e6743be | ||
|
|
a97e4fe42d | ||
|
|
2706495668 | ||
|
|
224c3f4123 | ||
|
|
f2911be116 | ||
|
|
05c5eaed48 | ||
|
|
8541e9535e | ||
|
|
811cb2b95c | ||
|
|
05421223be | ||
|
|
808a6b82f3 | ||
|
|
30af7f1ca6 | ||
|
|
274e5280cb | ||
|
|
f85c4cd79f | ||
|
|
696d39b922 | ||
|
|
d997445284 | ||
|
|
f3e5268083 | ||
|
|
44db4a21fc | ||
|
|
c8a113384c | ||
|
|
07e7bc0a94 | ||
|
|
7c8acbe485 | ||
|
|
65c2126202 | ||
|
|
0ef0f62b69 | ||
|
|
921422a189 | ||
|
|
56b771a7ad | ||
|
|
d2988e6a88 | ||
|
|
16510d2400 | ||
|
|
6f790598ef | ||
|
|
63f0c7b2fe | ||
|
|
800f7970a7 | ||
|
|
b7381fc075 | ||
|
|
b1785606cc | ||
|
|
387d7d5508 | ||
|
|
6adacf8892 | ||
|
|
f7d6f93f1b | ||
|
|
9daa173ab7 | ||
|
|
e748f72447 | ||
|
|
14358417c8 | ||
|
|
8aa3461bf6 | ||
|
|
04c37a98b5 | ||
|
|
4850f1e94b | ||
|
|
60945ffe58 | ||
|
|
746016996c | ||
|
|
67a2734777 | ||
|
|
b208fc7cf5 | ||
|
|
2748cf99b3 | ||
|
|
b49b60ab5f | ||
|
|
620c67b04e | ||
|
|
a5b5f315d1 | ||
|
|
43ea05aad2 | ||
|
|
0ec1c80221 | ||
|
|
d91c289bf6 | ||
|
|
74b9971494 | ||
|
|
f8f116109a | ||
|
|
8498cb6258 | ||
|
|
7a72f44f64 | ||
|
|
a90e3c440b | ||
|
|
1aab8b87ec | ||
|
|
ebf071ac51 | ||
|
|
3d0fc33441 | ||
|
|
c50df85976 | ||
|
|
db9fc20acf | ||
|
|
10058cf271 | ||
|
|
310dadef45 | ||
|
|
3159f036a2 | ||
|
|
c3e426c82a | ||
|
|
b83d388523 | ||
|
|
d8820259d2 | ||
|
|
354d78e893 | ||
|
|
7f00f35f4b | ||
|
|
8c33b073c2 | ||
|
|
d6490c9bab | ||
|
|
b0e104bfd0 | ||
|
|
563e677b08 | ||
|
|
7664d0568e | ||
|
|
e1a13e433f | ||
|
|
d212f7d946 | ||
|
|
8d6dabf65c | ||
|
|
a4c5b85db8 | ||
|
|
e66001c182 | ||
|
|
08c7b35731 | ||
|
|
35cb36fcea | ||
|
|
bf8ca989d6 | ||
|
|
4976ebcef4 | ||
|
|
51661f5c55 | ||
|
|
3cbb10392c | ||
|
|
168ce752d1 | ||
|
|
8fcf9bc6bf | ||
|
|
56f43f55f3 | ||
|
|
9386ac026d | ||
|
|
e1565ccdc5 | ||
|
|
34993a20fd | ||
|
|
ea0124433a | ||
|
|
73b15c691d | ||
|
|
623dd9f3b3 | ||
|
|
379637f214 | ||
|
|
26ca8f7777 | ||
|
|
237f7e7f1a | ||
|
|
34fc9851bf | ||
|
|
abdaca08fe | ||
|
|
bdc9f339c9 | ||
|
|
3188d4d858 | ||
|
|
1bb76b1a49 | ||
|
|
9584efd84c | ||
|
|
c84bc8a8f3 | ||
|
|
edc55a2afd | ||
|
|
480325177c | ||
|
|
03cfa42eb6 | ||
|
|
b25c573ae3 | ||
|
|
7f5757d66b | ||
|
|
a3a275c3d5 | ||
|
|
557f007b12 | ||
|
|
542406c16f | ||
|
|
1fa870411f | ||
|
|
c096487eea | ||
|
|
97a8e0cdd4 | ||
|
|
eb20a5c051 | ||
|
|
5c6acf89da | ||
|
|
9dfe7ddbde | ||
|
|
8285720180 | ||
|
|
a4a1a42842 | ||
|
|
3c3217ab2b | ||
|
|
17ee3887dd | ||
|
|
db75da65db | ||
|
|
cf95257c81 | ||
|
|
b40036c203 | ||
|
|
b18661a8b0 | ||
|
|
bc0d52142a | ||
|
|
24041ed80d | ||
|
|
1a03e6b22e | ||
|
|
68fb916195 | ||
|
|
dfb8a1b8cb | ||
|
|
6c38409c57 | ||
|
|
27fd7a9088 | ||
|
|
0e62423e06 | ||
|
|
3f45ed14b9 | ||
|
|
ca4b782912 | ||
|
|
1976a11154 | ||
|
|
e7d8f64a49 | ||
|
|
5cf1f13328 | ||
|
|
fba706f10b | ||
|
|
c50630989e | ||
|
|
3832be94d0 | ||
|
|
900c1152d3 | ||
|
|
4ea50a8a85 | ||
|
|
0132fd4101 | ||
|
|
0e90ce3b8a | ||
|
|
9368ebfbd3 | ||
|
|
8381dbbd8f | ||
|
|
343ccaa2ef | ||
|
|
3f62775f4b | ||
|
|
c3ecad96b7 | ||
|
|
2ffb0cf5f7 | ||
|
|
ef716f3a69 | ||
|
|
cc935862b7 | ||
|
|
85a535dd0c | ||
|
|
ab626fe3eb | ||
|
|
05011df13a | ||
|
|
fcdb25c426 | ||
|
|
cc9ca35e04 | ||
|
|
4a50de8318 | ||
|
|
12051e7544 | ||
|
|
210508fe4f | ||
|
|
ba5b045351 | ||
|
|
9a19eeacb3 | ||
|
|
10ed03d6bf | ||
|
|
3be1bf4384 | ||
|
|
3396b20661 | ||
|
|
c9e1140189 | ||
|
|
416cd30b42 | ||
|
|
d66a07cc55 | ||
|
|
ce17dbe240 | ||
|
|
326fc501e2 | ||
|
|
91ad3ee0a2 | ||
|
|
2993c422c1 | ||
|
|
6f8cdde7e4 | ||
|
|
f521a0dd77 | ||
|
|
12fdb58da6 | ||
|
|
09876cefde | ||
|
|
d1e2ea7635 | ||
|
|
7b131f2a45 | ||
|
|
f3d5e3d4c2 | ||
|
|
492f83f5e2 | ||
|
|
7dcd1618d8 | ||
|
|
e2a39aa12e | ||
|
|
043833be7b | ||
|
|
ad394c19d5 | ||
|
|
05592183b1 | ||
|
|
079cbb8f68 | ||
|
|
3bddde20ab | ||
|
|
0a8a63bc15 | ||
|
|
fd2491446a | ||
|
|
26f3fe37f4 | ||
|
|
4cff80ecea | ||
|
|
83fe94d497 | ||
|
|
616bde2c4d | ||
|
|
0b971c2bd0 | ||
|
|
201b1b7fbc | ||
|
|
b0b976ee52 | ||
|
|
7020569f45 | ||
|
|
e153c76719 | ||
|
|
e7f9c11a14 | ||
|
|
f2e5c5e9b9 | ||
|
|
e02f73417c | ||
|
|
41db9c43e5 | ||
|
|
0dc2f0e923 | ||
|
|
5940d5ddad | ||
|
|
684850dda1 | ||
|
|
e8e615f4db | ||
|
|
559b060b6b | ||
|
|
581262d736 | ||
|
|
8878a49a1d | ||
|
|
3a7bc4d253 | ||
|
|
316982fb8f | ||
|
|
cfaea34053 | ||
|
|
8fd1d895d6 | ||
|
|
b592ecf6f5 | ||
|
|
65a8826158 | ||
|
|
c9fab898cc | ||
|
|
819049d596 | ||
|
|
147dee6ec7 | ||
|
|
b0b885d506 | ||
|
|
c94610f8a9 | ||
|
|
21fa006850 | ||
|
|
e293c435af | ||
|
|
321c5ec756 | ||
|
|
f04d93f9aa | ||
|
|
9436efe554 | ||
|
|
3acbf708d3 | ||
|
|
92979984ea | ||
|
|
a16d41a1e1 | ||
|
|
ff8b78fc99 | ||
|
|
97c9bd7ce0 | ||
|
|
c40c93340d | ||
|
|
094c105464 | ||
|
|
e36c696624 | ||
|
|
555a4ab4c5 | ||
|
|
7d8cc8776d | ||
|
|
960646ac8a | ||
|
|
ed9f15becf | ||
|
|
b09aa74f2f | ||
|
|
60805fd11d | ||
|
|
89ecd4b33d | ||
|
|
237f142a87 | ||
|
|
a21647a1aa | ||
|
|
e9afe4ee25 | ||
|
|
acd067582a | ||
|
|
82227e8901 | ||
|
|
8b6735a6aa | ||
|
|
9ed8831500 | ||
|
|
e7d726cc2c | ||
|
|
11a3dd26c8 | ||
|
|
04738586b9 | ||
|
|
18b4fce1ca | ||
|
|
3b9babe745 | ||
|
|
a4a85b7bbf | ||
|
|
e8f0275d8e | ||
|
|
3251f58d46 | ||
|
|
204fb211ac | ||
|
|
6cade814e1 | ||
|
|
eb2fbabbb1 | ||
|
|
ee9c5db719 |
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@@ -3,7 +3,7 @@
|
|||||||
github: [c3lang]
|
github: [c3lang]
|
||||||
patreon: # Replace with a single Patreon username
|
patreon: # Replace with a single Patreon username
|
||||||
open_collective: # Replace with a single Open Collective username
|
open_collective: # Replace with a single Open Collective username
|
||||||
ko_fi: # Replace with a single Ko-fi username
|
ko_fi: c3lang
|
||||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
liberapay: # Replace with a single Liberapay username
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
|||||||
290
.github/workflows/main.yml
vendored
290
.github/workflows/main.yml
vendored
@@ -2,13 +2,16 @@ name: CI
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master, dev, ci_testing ]
|
branches: [ master, dev, ci_testing, experiments ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
branches: [ master, dev ]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
LLVM_RELEASE_VERSION: 16
|
LLVM_RELEASE_VERSION_WINDOWS: 18
|
||||||
|
LLVM_RELEASE_VERSION_MAC: 18
|
||||||
|
LLVM_RELEASE_VERSION_LINUX: 17
|
||||||
|
LLVM_RELEASE_VERSION_UBUNTU20: 17
|
||||||
|
LLVM_DEV_VERSION: 20
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
build-msvc:
|
build-msvc:
|
||||||
@@ -33,28 +36,31 @@ jobs:
|
|||||||
- name: Compile and run some examples
|
- name: Compile and run some examples
|
||||||
run: |
|
run: |
|
||||||
cd resources
|
cd resources
|
||||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\hello_world_many.c3
|
..\build\${{ matrix.build_type }}\c3c.exe compile-run -L C:\ --print-linking examples\hello_world_many.c3
|
||||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\time.c3
|
..\build\${{ matrix.build_type }}\c3c.exe compile-run --print-linking examples\time.c3
|
||||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\fannkuch-redux.c3
|
..\build\${{ matrix.build_type }}\c3c.exe compile-run --print-linking examples\fannkuch-redux.c3
|
||||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\contextfree\boolerr.c3
|
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\contextfree\boolerr.c3
|
||||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\ls.c3
|
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\ls.c3
|
||||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\load_world.c3
|
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\load_world.c3
|
||||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\process.c3
|
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\process.c3
|
||||||
..\build\${{ matrix.build_type }}\c3c.exe compile --test -g -O0 --threads 1 --target macos-x64 examples\constants.c3
|
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\args.c3 -- foo -bar "baz baz"
|
||||||
|
..\build\${{ matrix.build_type }}\c3c.exe compile --no-entry --test -g -O0 --threads 1 --target macos-x64 examples\constants.c3
|
||||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run msvc_stack.c3
|
..\build\${{ matrix.build_type }}\c3c.exe compile-run msvc_stack.c3
|
||||||
|
|
||||||
- name: Build testproject
|
- name: Build testproject
|
||||||
run: |
|
run: |
|
||||||
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||||
cd resources/testproject
|
cd resources/testproject
|
||||||
..\..\build\${{ matrix.build_type }}\c3c.exe --debug-log --emit-llvm run hello_world_win32
|
..\..\build\${{ matrix.build_type }}\c3c.exe --debug-log --emit-llvm run hello_world_win32
|
||||||
dir build\llvm_ir
|
dir build\llvm_ir
|
||||||
..\..\build\${{ matrix.build_type }}\c3c.exe clean
|
..\..\build\${{ matrix.build_type }}\c3c.exe clean
|
||||||
dir build\llvm_ir
|
dir build\llvm_ir
|
||||||
|
|
||||||
|
|
||||||
- name: Build testproject lib
|
- name: Build testproject lib
|
||||||
run: |
|
run: |
|
||||||
cd resources/testproject
|
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 --debug-log build hello_world_win32_lib
|
||||||
|
|
||||||
- name: Vendor-fetch
|
- name: Vendor-fetch
|
||||||
@@ -64,7 +70,7 @@ jobs:
|
|||||||
- name: Try raylib
|
- name: Try raylib
|
||||||
run: |
|
run: |
|
||||||
cd resources
|
cd resources
|
||||||
..\build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib
|
..\build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib
|
||||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --wincrt=none examples\raylib\raylib_arkanoid.c3
|
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --wincrt=none examples\raylib\raylib_arkanoid.c3
|
||||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --wincrt=none examples\raylib\raylib_snake.c3
|
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --wincrt=none examples\raylib\raylib_snake.c3
|
||||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --wincrt=none examples\raylib\raylib_tetris.c3
|
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --wincrt=none examples\raylib\raylib_tetris.c3
|
||||||
@@ -112,8 +118,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
|
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}
|
- shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-llvm-18.1.1-3-any.pkg.tar.zst
|
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.1-3-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
|
||||||
- name: CMake
|
- name: CMake
|
||||||
run: |
|
run: |
|
||||||
cmake -B build -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
cmake -B build -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||||
@@ -122,12 +128,13 @@ jobs:
|
|||||||
- name: Compile and run some examples
|
- name: Compile and run some examples
|
||||||
run: |
|
run: |
|
||||||
cd resources
|
cd resources
|
||||||
../build/c3c compile-run examples/hello_world_many.c3
|
../build/c3c compile-run --print-linking examples/hello_world_many.c3
|
||||||
../build/c3c compile-run examples/time.c3
|
../build/c3c compile-run --print-linking examples/time.c3
|
||||||
../build/c3c compile-run examples/fannkuch-redux.c3
|
../build/c3c compile-run --print-linking examples/fannkuch-redux.c3
|
||||||
../build/c3c compile-run examples/contextfree/boolerr.c3
|
../build/c3c compile-run --print-linking examples/contextfree/boolerr.c3
|
||||||
../build/c3c compile-run examples/load_world.c3
|
../build/c3c compile-run --print-linking examples/load_world.c3
|
||||||
../build/c3c compile --test -g -O0 --threads 1 --target macos-x64 examples/constants.c3
|
../build/c3c compile-run --print-linking examples/args.c3 -- foo -bar "baz baz"
|
||||||
|
../build/c3c compile --no-entry --test -g -O0 --threads 1 --target macos-x64 examples/constants.c3
|
||||||
|
|
||||||
- name: Build testproject
|
- name: Build testproject
|
||||||
run: |
|
run: |
|
||||||
@@ -141,7 +148,7 @@ jobs:
|
|||||||
- name: Build testproject lib
|
- name: Build testproject lib
|
||||||
run: |
|
run: |
|
||||||
cd resources/testproject
|
cd resources/testproject
|
||||||
../../build/c3c build hello_world_lib --debug-log
|
../../build/c3c build hello_world_lib --cc cc --debug-log
|
||||||
|
|
||||||
- name: run compiler tests
|
- name: run compiler tests
|
||||||
run: |
|
run: |
|
||||||
@@ -183,7 +190,8 @@ jobs:
|
|||||||
../build/c3c compile-run examples/fannkuch-redux.c3
|
../build/c3c compile-run examples/fannkuch-redux.c3
|
||||||
../build/c3c compile-run examples/contextfree/boolerr.c3
|
../build/c3c compile-run examples/contextfree/boolerr.c3
|
||||||
../build/c3c compile-run examples/load_world.c3
|
../build/c3c compile-run examples/load_world.c3
|
||||||
../build/c3c compile --test -g -O0 --threads 1 --target macos-x64 examples/constants.c3
|
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
|
||||||
|
../build/c3c compile --no-entry --test -g -O0 --threads 1 --target macos-x64 examples/constants.c3
|
||||||
- name: Build testproject
|
- name: Build testproject
|
||||||
run: |
|
run: |
|
||||||
cd resources/testproject
|
cd resources/testproject
|
||||||
@@ -206,7 +214,7 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
build_type: [Release, Debug]
|
build_type: [Release, Debug]
|
||||||
llvm_version: [15, 16, 17, 18, 19]
|
llvm_version: [17, 18, 19, 20]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@@ -217,25 +225,29 @@ jobs:
|
|||||||
- name: Install Clang ${{matrix.llvm_version}}
|
- name: Install Clang ${{matrix.llvm_version}}
|
||||||
run: |
|
run: |
|
||||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||||
if [[ "${{matrix.llvm_version}}" < 16 ]]; then
|
if [[ "${{matrix.llvm_version}}" < 18 ]]; then
|
||||||
sudo apt remove libllvm15
|
|
||||||
fi
|
|
||||||
if [[ "${{matrix.llvm_version}}" < 19 ]]; then
|
|
||||||
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${{matrix.llvm_version}} main"
|
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${{matrix.llvm_version}} main"
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y -t llvm-toolchain-focal-${{matrix.llvm_version}} libpolly-${{matrix.llvm_version}}-dev \
|
sudo apt-get install -y -t llvm-toolchain-focal-${{matrix.llvm_version}} libpolly-${{matrix.llvm_version}}-dev \
|
||||||
clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev \
|
clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev \
|
||||||
lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev libmlir-${{matrix.llvm_version}} \
|
lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev libmlir-${{matrix.llvm_version}} \
|
||||||
libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
|
libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
|
||||||
else
|
else
|
||||||
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main"
|
if [[ "${{matrix.llvm_version}}" < "${{env.LLVM_DEV_VERSION}}" ]]; then
|
||||||
sudo apt-get install -y -t llvm-toolchain-focal libpolly-${{matrix.llvm_version}}-dev \
|
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${{matrix.llvm_version}} main"
|
||||||
clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev \
|
sudo apt-get update
|
||||||
lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev libmlir-${{matrix.llvm_version}} \
|
sudo apt-get install -y -t llvm-toolchain-focal-${{matrix.llvm_version}} libpolly-${{matrix.llvm_version}}-dev \
|
||||||
libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
|
clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev \
|
||||||
|
lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev
|
||||||
|
else
|
||||||
|
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main"
|
||||||
|
sudo apt-get install -y -t llvm-toolchain-focal libpolly-${{matrix.llvm_version}}-dev \
|
||||||
|
clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev \
|
||||||
|
lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
- name: CMake
|
- name: CMake
|
||||||
if: matrix.llvm_version != 18
|
if: matrix.llvm_version < 18 || matrix.llvm_version == env.LLVM_DEV_VERSION
|
||||||
run: |
|
run: |
|
||||||
cmake -B build \
|
cmake -B build \
|
||||||
-G Ninja \
|
-G Ninja \
|
||||||
@@ -249,7 +261,7 @@ jobs:
|
|||||||
-DC3_LLVM_VERSION=${{matrix.llvm_version}}
|
-DC3_LLVM_VERSION=${{matrix.llvm_version}}
|
||||||
cmake --build build
|
cmake --build build
|
||||||
- name: CMake18
|
- name: CMake18
|
||||||
if: matrix.llvm_version == 18
|
if: matrix.llvm_version >= 18 && matrix.llvm_version != env.LLVM_DEV_VERSION
|
||||||
run: |
|
run: |
|
||||||
cmake -B build \
|
cmake -B build \
|
||||||
-G Ninja \
|
-G Ninja \
|
||||||
@@ -260,12 +272,12 @@ jobs:
|
|||||||
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
|
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
|
||||||
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
|
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
|
||||||
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
|
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
|
||||||
-DC3_LLVM_VERSION=18.1
|
-DC3_LLVM_VERSION=${{matrix.llvm_version}}.1
|
||||||
cmake --build build
|
cmake --build build
|
||||||
|
|
||||||
- name: Compile and run some examples
|
- name: Compile and run some examples
|
||||||
run: |
|
run: |
|
||||||
cd resources
|
cd resources
|
||||||
../build/c3c compile examples/base64.c3
|
../build/c3c compile examples/base64.c3
|
||||||
../build/c3c compile examples/binarydigits.c3
|
../build/c3c compile examples/binarydigits.c3
|
||||||
../build/c3c compile examples/brainfk.c3
|
../build/c3c compile examples/brainfk.c3
|
||||||
@@ -273,9 +285,9 @@ jobs:
|
|||||||
../build/c3c compile examples/fasta.c3
|
../build/c3c compile examples/fasta.c3
|
||||||
../build/c3c compile examples/gameoflife.c3
|
../build/c3c compile examples/gameoflife.c3
|
||||||
../build/c3c compile examples/hash.c3
|
../build/c3c compile examples/hash.c3
|
||||||
../build/c3c compile examples/levenshtein.c3
|
../build/c3c compile-only examples/levenshtein.c3
|
||||||
../build/c3c compile examples/load_world.c3
|
../build/c3c compile examples/load_world.c3
|
||||||
../build/c3c compile examples/map.c3
|
../build/c3c compile-only examples/map.c3
|
||||||
../build/c3c compile examples/mandelbrot.c3
|
../build/c3c compile examples/mandelbrot.c3
|
||||||
../build/c3c compile examples/plus_minus.c3
|
../build/c3c compile examples/plus_minus.c3
|
||||||
../build/c3c compile examples/nbodies.c3
|
../build/c3c compile examples/nbodies.c3
|
||||||
@@ -284,8 +296,8 @@ jobs:
|
|||||||
../build/c3c compile examples/contextfree/boolerr.c3
|
../build/c3c compile examples/contextfree/boolerr.c3
|
||||||
../build/c3c compile examples/contextfree/dynscope.c3
|
../build/c3c compile examples/contextfree/dynscope.c3
|
||||||
../build/c3c compile examples/contextfree/guess_number.c3
|
../build/c3c compile examples/contextfree/guess_number.c3
|
||||||
../build/c3c compile examples/contextfree/multi.c3
|
../build/c3c compile examples/contextfree/multi.c3
|
||||||
../build/c3c compile examples/contextfree/cleanup.c3
|
../build/c3c compile examples/contextfree/cleanup.c3
|
||||||
../build/c3c compile-run examples/hello_world_many.c3
|
../build/c3c compile-run examples/hello_world_many.c3
|
||||||
../build/c3c compile-run examples/time.c3
|
../build/c3c compile-run examples/time.c3
|
||||||
../build/c3c compile-run examples/fannkuch-redux.c3
|
../build/c3c compile-run examples/fannkuch-redux.c3
|
||||||
@@ -293,8 +305,9 @@ jobs:
|
|||||||
../build/c3c compile-run examples/load_world.c3
|
../build/c3c compile-run examples/load_world.c3
|
||||||
../build/c3c compile-run examples/process.c3
|
../build/c3c compile-run examples/process.c3
|
||||||
../build/c3c compile-run examples/ls.c3
|
../build/c3c compile-run examples/ls.c3
|
||||||
../build/c3c compile-run --system-linker=no linux_stack.c3
|
../build/c3c compile-run --linker=builtin linux_stack.c3
|
||||||
../build/c3c compile-run linux_stack.c3
|
../build/c3c compile-run linux_stack.c3
|
||||||
|
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
|
||||||
|
|
||||||
- name: Compile run unit tests
|
- name: Compile run unit tests
|
||||||
run: |
|
run: |
|
||||||
@@ -306,10 +319,31 @@ jobs:
|
|||||||
cd resources/testproject
|
cd resources/testproject
|
||||||
../../build/c3c run --debug-log
|
../../build/c3c run --debug-log
|
||||||
|
|
||||||
|
- name: Test WASM
|
||||||
|
run: |
|
||||||
|
cd resources/testfragments
|
||||||
|
../../build/c3c compile --reloc=none --target wasm32 -g0 --link-libc=no --no-entry -Os wasm4.c3
|
||||||
|
|
||||||
|
- name: Install QEMU and Risc-V toolchain
|
||||||
|
run: |
|
||||||
|
sudo apt-get install opensbi qemu-system-misc u-boot-qemu gcc-riscv64-unknown-elf
|
||||||
|
|
||||||
|
- name: Compile and run Baremetal Risc-V Example
|
||||||
|
run: |
|
||||||
|
cd resources/examples/embedded/riscv-qemu
|
||||||
|
make C3C_PATH=../../../../build/ run
|
||||||
|
|
||||||
- name: Build testproject direct linker
|
- name: Build testproject direct linker
|
||||||
run: |
|
run: |
|
||||||
cd resources/testproject
|
cd resources/testproject
|
||||||
../../build/c3c run --debug-log --system-linker=no
|
../../build/c3c run --debug-log --linker=builtin
|
||||||
|
|
||||||
|
- name: Init a library & a project
|
||||||
|
run: |
|
||||||
|
./build/c3c init-lib mylib
|
||||||
|
ls mylib.c3l
|
||||||
|
./build/c3c init myproject
|
||||||
|
ls myproject
|
||||||
|
|
||||||
- name: run compiler tests
|
- name: run compiler tests
|
||||||
run: |
|
run: |
|
||||||
@@ -317,16 +351,16 @@ jobs:
|
|||||||
python3 src/tester.py ../build/c3c test_suite/
|
python3 src/tester.py ../build/c3c test_suite/
|
||||||
|
|
||||||
- name: bundle_output
|
- name: bundle_output
|
||||||
if: matrix.llvm_version == 16
|
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_LINUX
|
||||||
run: |
|
run: |
|
||||||
mkdir linux
|
mkdir c3
|
||||||
cp -r lib linux
|
cp -r lib c3
|
||||||
cp msvc_build_libraries.py linux
|
cp msvc_build_libraries.py c3
|
||||||
cp build/c3c linux
|
cp build/c3c c3
|
||||||
tar czf c3-linux-${{matrix.build_type}}.tar.gz linux
|
tar czf c3-linux-${{matrix.build_type}}.tar.gz c3
|
||||||
|
|
||||||
- name: upload artifacts
|
- name: upload artifacts
|
||||||
if: matrix.llvm_version == 16
|
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_LINUX
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: c3-linux-${{matrix.build_type}}
|
name: c3-linux-${{matrix.build_type}}
|
||||||
@@ -339,8 +373,7 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
build_type: [Release, Debug]
|
build_type: [Release, Debug]
|
||||||
llvm_version: [16]
|
llvm_version: [17, 18, 19]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Install common deps
|
- name: Install common deps
|
||||||
@@ -350,16 +383,17 @@ jobs:
|
|||||||
- name: Install Clang ${{matrix.llvm_version}}
|
- name: Install Clang ${{matrix.llvm_version}}
|
||||||
run: |
|
run: |
|
||||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||||
if [[ "${{matrix.llvm_version}}" < 17 ]]; then
|
if [[ "${{matrix.llvm_version}}" < "${{env.LLVM_DEV_VERSION}}" ]]; then
|
||||||
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${{matrix.llvm_version}} main"
|
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${{matrix.llvm_version}} main"
|
||||||
else
|
else
|
||||||
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main"
|
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main"
|
||||||
fi
|
fi
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev
|
sudo apt-get install -y clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev
|
||||||
sudo apt-get install -y libmlir-${{matrix.llvm_version}} libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
|
sudo apt-get install -y libmlir-${{matrix.llvm_version}} libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
|
||||||
sudo apt-get install -y libpolly-${{matrix.llvm_version}}-dev
|
sudo apt-get install -y libpolly-${{matrix.llvm_version}}-dev
|
||||||
- name: CMake
|
- name: CMake Old
|
||||||
|
if: matrix.llvm_version < 18 || matrix.llvm_version == env.LLVM_DEV_VERSION
|
||||||
run: |
|
run: |
|
||||||
cmake -B build \
|
cmake -B build \
|
||||||
-G Ninja \
|
-G Ninja \
|
||||||
@@ -372,13 +406,26 @@ jobs:
|
|||||||
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
|
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
|
||||||
-DC3_LLVM_VERSION=${{matrix.llvm_version}}
|
-DC3_LLVM_VERSION=${{matrix.llvm_version}}
|
||||||
cmake --build build
|
cmake --build build
|
||||||
|
- name: CMake
|
||||||
|
if: matrix.llvm_version >= 18 && matrix.llvm_version != env.LLVM_DEV_VERSION
|
||||||
|
run: |
|
||||||
|
cmake -B build \
|
||||||
|
-G Ninja \
|
||||||
|
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
|
||||||
|
-DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} \
|
||||||
|
-DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} \
|
||||||
|
-DCMAKE_LINKER=lld-link-${{matrix.llvm_version}} \
|
||||||
|
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
|
||||||
|
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
|
||||||
|
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
|
||||||
|
-DC3_LLVM_VERSION=${{matrix.llvm_version}}.1
|
||||||
|
cmake --build build
|
||||||
- name: Compile and run some examples
|
- name: Compile and run some examples
|
||||||
run: |
|
run: |
|
||||||
cd resources
|
cd resources
|
||||||
../build/c3c compile examples/gameoflife.c3
|
../build/c3c compile examples/gameoflife.c3
|
||||||
../build/c3c compile examples/levenshtein.c3
|
../build/c3c compile-only examples/levenshtein.c3
|
||||||
../build/c3c compile examples/map.c3
|
../build/c3c compile-only examples/map.c3
|
||||||
../build/c3c compile examples/mandelbrot.c3
|
../build/c3c compile examples/mandelbrot.c3
|
||||||
../build/c3c compile examples/plus_minus.c3
|
../build/c3c compile examples/plus_minus.c3
|
||||||
../build/c3c compile examples/spectralnorm.c3
|
../build/c3c compile examples/spectralnorm.c3
|
||||||
@@ -388,8 +435,8 @@ jobs:
|
|||||||
../build/c3c compile-run examples/nbodies.c3
|
../build/c3c compile-run examples/nbodies.c3
|
||||||
../build/c3c compile-run examples/contextfree/boolerr.c3
|
../build/c3c compile-run examples/contextfree/boolerr.c3
|
||||||
../build/c3c compile-run examples/contextfree/dynscope.c3
|
../build/c3c compile-run examples/contextfree/dynscope.c3
|
||||||
../build/c3c compile-run examples/contextfree/multi.c3
|
../build/c3c compile-run examples/contextfree/multi.c3
|
||||||
../build/c3c compile-run examples/contextfree/cleanup.c3
|
../build/c3c compile-run examples/contextfree/cleanup.c3
|
||||||
../build/c3c compile-run examples/hello_world_many.c3
|
../build/c3c compile-run examples/hello_world_many.c3
|
||||||
../build/c3c compile-run examples/time.c3
|
../build/c3c compile-run examples/time.c3
|
||||||
../build/c3c compile-run examples/fannkuch-redux.c3
|
../build/c3c compile-run examples/fannkuch-redux.c3
|
||||||
@@ -400,8 +447,9 @@ jobs:
|
|||||||
../build/c3c compile-run examples/factorial_macro.c3
|
../build/c3c compile-run examples/factorial_macro.c3
|
||||||
../build/c3c compile-run examples/fasta.c3
|
../build/c3c compile-run examples/fasta.c3
|
||||||
../build/c3c compile-run examples/process.c3
|
../build/c3c compile-run examples/process.c3
|
||||||
../build/c3c compile-run --system-linker=no linux_stack.c3
|
../build/c3c compile-run --linker=builtin linux_stack.c3
|
||||||
../build/c3c compile-run linux_stack.c3
|
../build/c3c compile-run linux_stack.c3
|
||||||
|
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
|
||||||
|
|
||||||
- name: Compile run unit tests
|
- name: Compile run unit tests
|
||||||
run: |
|
run: |
|
||||||
@@ -416,7 +464,7 @@ jobs:
|
|||||||
- name: Build testproject direct linker
|
- name: Build testproject direct linker
|
||||||
run: |
|
run: |
|
||||||
cd resources/testproject
|
cd resources/testproject
|
||||||
../../build/c3c run --debug-log --system-linker=no
|
../../build/c3c run --debug-log --linker=builtin
|
||||||
|
|
||||||
- name: run compiler tests
|
- name: run compiler tests
|
||||||
run: |
|
run: |
|
||||||
@@ -424,20 +472,108 @@ jobs:
|
|||||||
python3 src/tester.py ../build/c3c test_suite/
|
python3 src/tester.py ../build/c3c test_suite/
|
||||||
|
|
||||||
- name: bundle_output
|
- name: bundle_output
|
||||||
if: matrix.llvm_version == 16
|
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU20
|
||||||
run: |
|
run: |
|
||||||
mkdir linux
|
mkdir c3
|
||||||
cp -r lib linux
|
cp -r lib c3
|
||||||
cp msvc_build_libraries.py linux
|
cp msvc_build_libraries.py c3
|
||||||
cp build/c3c linux
|
cp build/c3c c3
|
||||||
tar czf c3-ubuntu-20-${{matrix.build_type}}.tar.gz linux
|
tar czf c3-ubuntu-20-${{matrix.build_type}}.tar.gz c3
|
||||||
|
|
||||||
- name: upload artifacts
|
- name: upload artifacts
|
||||||
if: matrix.llvm_version == 16
|
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU20
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: c3-ubuntu-20-${{matrix.build_type}}
|
name: c3-ubuntu-20-${{matrix.build_type}}
|
||||||
path: c3-ubuntu-20-${{matrix.build_type}}.tar.gz
|
path: c3-ubuntu-20-${{matrix.build_type}}.tar.gz
|
||||||
|
|
||||||
|
build-with-docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
ubuntu_version: [20.04, 22.04]
|
||||||
|
build_type: [Release, Debug]
|
||||||
|
llvm_version: [17, 18, 19, 20]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Make script executable
|
||||||
|
run: chmod +x ./build-with-docker.sh
|
||||||
|
|
||||||
|
- name: Run build
|
||||||
|
run: |
|
||||||
|
LLVM_VERSION=${{ matrix.llvm_version }} UBUNTU_VERSION=${{ matrix.ubuntu_version }} CMAKE_BUILD_TYPE=${{ matrix.build_type }} ./build-with-docker.sh
|
||||||
|
|
||||||
|
- name: Compile and run some examples
|
||||||
|
run: |
|
||||||
|
cd resources
|
||||||
|
../build/c3c compile examples/base64.c3
|
||||||
|
../build/c3c compile examples/binarydigits.c3
|
||||||
|
../build/c3c compile examples/brainfk.c3
|
||||||
|
../build/c3c compile examples/factorial_macro.c3
|
||||||
|
../build/c3c compile examples/fasta.c3
|
||||||
|
../build/c3c compile examples/gameoflife.c3
|
||||||
|
../build/c3c compile examples/hash.c3
|
||||||
|
../build/c3c compile-only examples/levenshtein.c3
|
||||||
|
../build/c3c compile examples/load_world.c3
|
||||||
|
../build/c3c compile-only examples/map.c3
|
||||||
|
../build/c3c compile examples/mandelbrot.c3
|
||||||
|
../build/c3c compile examples/plus_minus.c3
|
||||||
|
../build/c3c compile examples/nbodies.c3
|
||||||
|
../build/c3c compile examples/spectralnorm.c3
|
||||||
|
../build/c3c compile examples/swap.c3
|
||||||
|
../build/c3c compile examples/contextfree/boolerr.c3
|
||||||
|
../build/c3c compile examples/contextfree/dynscope.c3
|
||||||
|
../build/c3c compile examples/contextfree/guess_number.c3
|
||||||
|
../build/c3c compile examples/contextfree/multi.c3
|
||||||
|
../build/c3c compile examples/contextfree/cleanup.c3
|
||||||
|
../build/c3c compile-run examples/hello_world_many.c3
|
||||||
|
../build/c3c compile-run examples/time.c3
|
||||||
|
../build/c3c compile-run examples/fannkuch-redux.c3
|
||||||
|
../build/c3c compile-run examples/contextfree/boolerr.c3
|
||||||
|
../build/c3c compile-run examples/load_world.c3
|
||||||
|
../build/c3c compile-run examples/process.c3
|
||||||
|
../build/c3c compile-run examples/ls.c3
|
||||||
|
../build/c3c compile-run --linker=builtin linux_stack.c3
|
||||||
|
../build/c3c compile-run linux_stack.c3
|
||||||
|
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
|
||||||
|
|
||||||
|
- name: Compile run unit tests
|
||||||
|
run: |
|
||||||
|
cd test
|
||||||
|
../build/c3c compile-test unit
|
||||||
|
|
||||||
|
- name: Build testproject
|
||||||
|
run: |
|
||||||
|
cd resources/testproject
|
||||||
|
../../build/c3c run --debug-log
|
||||||
|
|
||||||
|
- name: Test WASM
|
||||||
|
run: |
|
||||||
|
cd resources/testfragments
|
||||||
|
../../build/c3c compile --reloc=none --target wasm32 -g0 --link-libc=no --no-entry -Os wasm4.c3
|
||||||
|
|
||||||
|
- name: Build testproject direct linker
|
||||||
|
run: |
|
||||||
|
cd resources/testproject
|
||||||
|
../../build/c3c run --debug-log --linker=builtin
|
||||||
|
|
||||||
|
- name: Init a library & a project
|
||||||
|
run: |
|
||||||
|
./build/c3c init-lib mylib
|
||||||
|
ls mylib.c3l
|
||||||
|
./build/c3c init myproject
|
||||||
|
ls myproject
|
||||||
|
|
||||||
|
- name: run compiler tests
|
||||||
|
run: |
|
||||||
|
cd test
|
||||||
|
python3 src/tester.py ../build/c3c test_suite/
|
||||||
|
|
||||||
build-mac:
|
build-mac:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
@@ -446,20 +582,26 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
build_type: [Release, Debug]
|
build_type: [Release, Debug]
|
||||||
llvm_version: [15, 16, 17]
|
llvm_version: [17, 18]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Download LLVM
|
- name: Download LLVM
|
||||||
run: |
|
run: |
|
||||||
brew install llvm@${{ matrix.llvm_version }} ninja curl
|
brew install llvm@${{ matrix.llvm_version }} ninja curl
|
||||||
echo "/usr/local/opt/llvm@${{ matrix.llvm_version }}/bin" >> $GITHUB_PATH
|
echo "/opt/homebrew/opt/llvm@${{ matrix.llvm_version }}/bin" >> $GITHUB_PATH
|
||||||
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
||||||
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: CMake
|
- name: CMake
|
||||||
|
if: matrix.llvm_version < 18
|
||||||
run: |
|
run: |
|
||||||
cmake -B build -G Ninja -DC3_LLVM_VERSION=${{matrix.llvm_version}} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
cmake -B build -G Ninja -DC3_LLVM_VERSION=${{matrix.llvm_version}} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||||
cmake --build build
|
cmake --build build
|
||||||
|
- name: CMake18
|
||||||
|
if: matrix.llvm_version >= 18
|
||||||
|
run: |
|
||||||
|
cmake -B build -G Ninja -DC3_LLVM_VERSION=${{matrix.llvm_version}}.1 -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||||
|
cmake --build build
|
||||||
|
|
||||||
- name: Vendor-fetch
|
- name: Vendor-fetch
|
||||||
run: |
|
run: |
|
||||||
@@ -474,6 +616,8 @@ jobs:
|
|||||||
../build/c3c compile-run examples/contextfree/boolerr.c3
|
../build/c3c compile-run examples/contextfree/boolerr.c3
|
||||||
../build/c3c compile-run examples/process.c3
|
../build/c3c compile-run examples/process.c3
|
||||||
../build/c3c compile-run examples/load_world.c3
|
../build/c3c compile-run examples/load_world.c3
|
||||||
|
../build/c3c compile-run -O5 examples/load_world.c3
|
||||||
|
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
|
||||||
|
|
||||||
- name: Compile run unit tests
|
- name: Compile run unit tests
|
||||||
run: |
|
run: |
|
||||||
@@ -488,7 +632,7 @@ jobs:
|
|||||||
- name: Build testproject direct linker
|
- name: Build testproject direct linker
|
||||||
run: |
|
run: |
|
||||||
cd resources/testproject
|
cd resources/testproject
|
||||||
../../build/c3c run --debug-log --system-linker=no
|
../../build/c3c run --debug-log --linker=builtin
|
||||||
|
|
||||||
- name: Build testproject lib
|
- name: Build testproject lib
|
||||||
run: |
|
run: |
|
||||||
@@ -501,7 +645,7 @@ jobs:
|
|||||||
python3 src/tester.py ../build/c3c test_suite/
|
python3 src/tester.py ../build/c3c test_suite/
|
||||||
|
|
||||||
- name: bundle_output
|
- name: bundle_output
|
||||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION
|
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_MAC
|
||||||
run: |
|
run: |
|
||||||
mkdir macos
|
mkdir macos
|
||||||
cp -r lib macos
|
cp -r lib macos
|
||||||
@@ -510,7 +654,7 @@ jobs:
|
|||||||
zip -r c3-macos-${{matrix.build_type}}.zip macos
|
zip -r c3-macos-${{matrix.build_type}}.zip macos
|
||||||
|
|
||||||
- name: upload artifacts
|
- name: upload artifacts
|
||||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION
|
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_MAC
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: c3-macos-${{matrix.build_type}}
|
name: c3-macos-${{matrix.build_type}}
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -67,3 +67,6 @@ out/
|
|||||||
|
|
||||||
/cmake-build-debug/
|
/cmake-build-debug/
|
||||||
/cmake-build-release/
|
/cmake-build-release/
|
||||||
|
|
||||||
|
# Emacs files
|
||||||
|
TAGS
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.15)
|
cmake_minimum_required(VERSION 3.20)
|
||||||
|
|
||||||
# Grab the version
|
# Grab the version
|
||||||
file(READ "src/version.h" ver)
|
file(READ "src/version.h" ver)
|
||||||
@@ -10,6 +10,11 @@ endif()
|
|||||||
project(c3c VERSION ${CMAKE_MATCH_1})
|
project(c3c VERSION ${CMAKE_MATCH_1})
|
||||||
message("C3C version: ${CMAKE_PROJECT_VERSION}")
|
message("C3C version: ${CMAKE_PROJECT_VERSION}")
|
||||||
|
|
||||||
|
# Avoid warning for FetchContent
|
||||||
|
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
|
||||||
|
cmake_policy(SET CMP0135 NEW)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (NOT DEFINED CMAKE_INSTALL_LIBDIR)
|
if (NOT DEFINED CMAKE_INSTALL_LIBDIR)
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
set(CMAKE_INSTALL_LIBDIR "c:\\c3c\\lib")
|
set(CMAKE_INSTALL_LIBDIR "c:\\c3c\\lib")
|
||||||
@@ -32,6 +37,7 @@ set(CMAKE_C_STANDARD 11)
|
|||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
|
message(STATUS "MSVC version ${MSVC_VERSION}")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2 /EHsc")
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2 /EHsc")
|
||||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_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_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi /EHa")
|
||||||
@@ -75,7 +81,7 @@ if (NOT WIN32)
|
|||||||
find_package(CURL)
|
find_package(CURL)
|
||||||
endif()
|
endif()
|
||||||
if (NOT C3_LLVM_VERSION STREQUAL "auto")
|
if (NOT C3_LLVM_VERSION STREQUAL "auto")
|
||||||
if (${C3_LLVM_VERSION} VERSION_LESS 15 OR ${C3_LLVM_VERSION} VERSION_GREATER 19)
|
if (${C3_LLVM_VERSION} VERSION_LESS 17 OR ${C3_LLVM_VERSION} VERSION_GREATER 20)
|
||||||
message(FATAL_ERROR "LLVM ${C3_LLVM_VERSION} is not supported!")
|
message(FATAL_ERROR "LLVM ${C3_LLVM_VERSION} is not supported!")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
@@ -97,15 +103,15 @@ endif()
|
|||||||
|
|
||||||
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||||
if (C3_LLVM_VERSION STREQUAL "auto")
|
if (C3_LLVM_VERSION STREQUAL "auto")
|
||||||
set(C3_LLVM_VERSION "16")
|
set(C3_LLVM_VERSION "18")
|
||||||
endif()
|
endif()
|
||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
LLVM_Windows
|
LLVM_Windows
|
||||||
URL https://github.com/c3lang/win-llvm/releases/download/llvm_16_0_2/llvm-16.0.2-windows-amd64-msvc17-libcmt.7z
|
URL https://github.com/c3lang/win-llvm/releases/download/llvm_18_1_8_with_rt/llvm-18.1.8-windows-amd64-msvc17-libcmt.7z
|
||||||
)
|
)
|
||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
LLVM_Windows_debug
|
LLVM_Windows_debug
|
||||||
URL https://github.com/c3lang/win-llvm/releases/download/llvm_16_0_2/llvm-16.0.2-windows-amd64-msvc17-libcmt-dbg.7z
|
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
|
||||||
)
|
)
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
message("Loading Windows LLVM debug libraries, this may take a while...")
|
message("Loading Windows LLVM debug libraries, this may take a while...")
|
||||||
@@ -130,6 +136,10 @@ message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
|
|||||||
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
|
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
|
||||||
message(STATUS "Libraries located in: ${LLVM_LIBRARY_DIRS}")
|
message(STATUS "Libraries located in: ${LLVM_LIBRARY_DIRS}")
|
||||||
|
|
||||||
|
if (NOT LLVM_PACKAGE_VERSION VERSION_GREATER_EQUAL 15.0)
|
||||||
|
message(FATAL_ERROR "LLVM version 15.0 or later is required.")
|
||||||
|
endif()
|
||||||
|
|
||||||
if(LLVM_ENABLE_RTTI)
|
if(LLVM_ENABLE_RTTI)
|
||||||
message(STATUS "LLVM was built with RTTI")
|
message(STATUS "LLVM was built with RTTI")
|
||||||
else()
|
else()
|
||||||
@@ -195,8 +205,10 @@ else()
|
|||||||
find_library(LLD_WASM NAMES liblldWasm.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
find_library(LLD_WASM NAMES liblldWasm.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/lib)
|
if (NOT(${CMAKE_BINARY_DIR} EQUAL ${CMAKE_SOURCE_DIR}))
|
||||||
file(COPY ${CMAKE_SOURCE_DIR}/lib DESTINATION ${CMAKE_BINARY_DIR})
|
file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/lib)
|
||||||
|
file(COPY ${CMAKE_SOURCE_DIR}/lib DESTINATION ${CMAKE_BINARY_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
if (${LLVM_PACKAGE_VERSION} VERSION_GREATER_EQUAL 16)
|
if (${LLVM_PACKAGE_VERSION} VERSION_GREATER_EQUAL 16)
|
||||||
find_library(LLD_LOONG NAMES libLLVMLoongArchCodeGen.lib libLLVMLoongArchAsmParser.lib libLLVMLoongArchCodeGen.a libLLVMLoongArchAsmParser.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
find_library(LLD_LOONG NAMES libLLVMLoongArchCodeGen.lib libLLVMLoongArchAsmParser.lib libLLVMLoongArchCodeGen.a libLLVMLoongArchAsmParser.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||||
@@ -234,11 +246,15 @@ add_executable(c3c
|
|||||||
src/build/builder.c
|
src/build/builder.c
|
||||||
src/build/build_options.c
|
src/build/build_options.c
|
||||||
src/build/project_creation.c
|
src/build/project_creation.c
|
||||||
|
src/build/project_manipulation.c
|
||||||
|
src/build/libraries.c
|
||||||
src/compiler/ast.c
|
src/compiler/ast.c
|
||||||
src/compiler/bigint.c
|
src/compiler/bigint.c
|
||||||
src/compiler/codegen_general.c
|
src/compiler/codegen_general.c
|
||||||
src/compiler/compiler.c
|
src/compiler/compiler.c
|
||||||
src/compiler/compiler.h
|
src/compiler/compiler.h
|
||||||
|
src/compiler/subprocess.c
|
||||||
|
src/compiler/subprocess.h
|
||||||
src/compiler/context.c
|
src/compiler/context.c
|
||||||
src/compiler/copying.c
|
src/compiler/copying.c
|
||||||
src/compiler/diagnostics.c
|
src/compiler/diagnostics.c
|
||||||
@@ -246,7 +262,6 @@ add_executable(c3c
|
|||||||
src/compiler/headers.c
|
src/compiler/headers.c
|
||||||
src/compiler/json_output.c
|
src/compiler/json_output.c
|
||||||
src/compiler/lexer.c
|
src/compiler/lexer.c
|
||||||
src/compiler/libraries.c
|
|
||||||
src/compiler/linker.c
|
src/compiler/linker.c
|
||||||
src/compiler/llvm_codegen.c
|
src/compiler/llvm_codegen.c
|
||||||
src/compiler/abi/c_abi_aarch64.c
|
src/compiler/abi/c_abi_aarch64.c
|
||||||
@@ -315,7 +330,8 @@ add_executable(c3c
|
|||||||
src/compiler/expr.c
|
src/compiler/expr.c
|
||||||
src/utils/time.c
|
src/utils/time.c
|
||||||
src/utils/http.c
|
src/utils/http.c
|
||||||
src/compiler/sema_liveness.c)
|
src/compiler/sema_liveness.c
|
||||||
|
src/build/common_build.c)
|
||||||
|
|
||||||
|
|
||||||
if (C3_USE_TB)
|
if (C3_USE_TB)
|
||||||
@@ -356,11 +372,12 @@ endif()
|
|||||||
|
|
||||||
|
|
||||||
target_include_directories(c3c PRIVATE
|
target_include_directories(c3c PRIVATE
|
||||||
"${CMAKE_SOURCE_DIR}/src/")
|
"${CMAKE_SOURCE_DIR}/src/"
|
||||||
|
"${CMAKE_SOURCE_DIR}/wrapper/include/")
|
||||||
|
|
||||||
|
|
||||||
target_include_directories(c3c_wrappers PRIVATE
|
target_include_directories(c3c_wrappers PRIVATE
|
||||||
"${CMAKE_SOURCE_DIR}/wrapper/src/")
|
"${CMAKE_SOURCE_DIR}/wrapper/include/")
|
||||||
|
|
||||||
target_include_directories(miniz PUBLIC
|
target_include_directories(miniz PUBLIC
|
||||||
"${CMAKE_SOURCE_DIR}/dependencies/miniz/")
|
"${CMAKE_SOURCE_DIR}/dependencies/miniz/")
|
||||||
@@ -388,6 +405,9 @@ if(MSVC)
|
|||||||
message("Adding MSVC options")
|
message("Adding MSVC options")
|
||||||
target_compile_options(c3c PRIVATE /wd4068 /wd4090 /WX /Wv:18)
|
target_compile_options(c3c PRIVATE /wd4068 /wd4090 /WX /Wv:18)
|
||||||
target_compile_options(c3c_wrappers PUBLIC /wd4624 /wd4267 /wd4244 /WX /Wv:18)
|
target_compile_options(c3c_wrappers PUBLIC /wd4624 /wd4267 /wd4244 /WX /Wv:18)
|
||||||
|
if (NOT LLVM_ENABLE_RTTI)
|
||||||
|
target_compile_options(c3c_wrappers PUBLIC /GR-)
|
||||||
|
endif()
|
||||||
target_link_options(c3c_wrappers PUBLIC /ignore:4099)
|
target_link_options(c3c_wrappers PUBLIC /ignore:4099)
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
target_compile_options(c3c PUBLIC /MTd)
|
target_compile_options(c3c PUBLIC /MTd)
|
||||||
@@ -404,10 +424,12 @@ if(MSVC)
|
|||||||
target_compile_options(tilde-backend PUBLIC /MT)
|
target_compile_options(tilde-backend PUBLIC /MT)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
else()
|
else()
|
||||||
message(STATUS "using gcc/clang warning switches")
|
message(STATUS "using gcc/clang warning switches")
|
||||||
target_link_options(c3c PRIVATE -pthread)
|
target_link_options(c3c PRIVATE -pthread)
|
||||||
|
if (NOT LLVM_ENABLE_RTTI)
|
||||||
|
target_compile_options(c3c_wrappers PRIVATE -fno-rtti)
|
||||||
|
endif()
|
||||||
target_compile_options(c3c PRIVATE -pthread -Wall -Werror -Wno-unknown-pragmas -Wno-unused-result
|
target_compile_options(c3c PRIVATE -pthread -Wall -Werror -Wno-unknown-pragmas -Wno-unused-result
|
||||||
-Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
|
-Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
18
README.md
18
README.md
@@ -32,7 +32,7 @@ whole new language.
|
|||||||
|
|
||||||
### Example code
|
### Example code
|
||||||
|
|
||||||
The following code shows [generic modules](http://www.c3-lang.org/generics/) (more examples can be found at http://www.c3-lang.org/examples/).
|
The following code shows [generic modules](https://c3-lang.org/references/docs/generics/) (more examples can be found at https://c3-lang.org/references/docs/examples/).
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
module stack (<Type>);
|
module stack (<Type>);
|
||||||
@@ -122,7 +122,7 @@ fn void main()
|
|||||||
- No mandatory header files
|
- No mandatory header files
|
||||||
- New semantic macro system
|
- New semantic macro system
|
||||||
- Module based name spacing
|
- Module based name spacing
|
||||||
- Subarrays (slices)
|
- Slices
|
||||||
- Compile time reflection
|
- Compile time reflection
|
||||||
- Enhanced compile time execution
|
- Enhanced compile time execution
|
||||||
- Generics based on generic modules
|
- Generics based on generic modules
|
||||||
@@ -137,9 +137,9 @@ fn void main()
|
|||||||
|
|
||||||
### Current status
|
### Current status
|
||||||
|
|
||||||
The current stable version of the compiler is **version 0.5**.
|
The current stable version of the compiler is **version 0.6.1**.
|
||||||
|
|
||||||
The upcoming 0.6 release will focus on expanding the standard library.
|
The upcoming 0.6.x releases will focus on expanding the standard library.
|
||||||
Follow the issues [here](https://github.com/c3lang/c3c/issues).
|
Follow the issues [here](https://github.com/c3lang/c3c/issues).
|
||||||
|
|
||||||
If you have suggestions on how to improve the language, either [file an issue](https://github.com/c3lang/c3c/issues)
|
If you have suggestions on how to improve the language, either [file an issue](https://github.com/c3lang/c3c/issues)
|
||||||
@@ -212,6 +212,8 @@ More platforms will be supported in the future.
|
|||||||
3. Unzip executable and standard lib.
|
3. Unzip executable and standard lib.
|
||||||
4. Run `./c3c`.
|
4. Run `./c3c`.
|
||||||
|
|
||||||
|
(*Note that there is a known issue with debug symbol generation on MacOS 13, see [issue #1086](https://github.com/c3lang/c3c/issues/1086))
|
||||||
|
|
||||||
#### Installing on Arch Linux
|
#### Installing on Arch Linux
|
||||||
There is an AUR package for the c3c compiler : [c3c-git](https://aur.archlinux.org/packages/c3c-git).
|
There is an AUR package for the c3c compiler : [c3c-git](https://aur.archlinux.org/packages/c3c-git).
|
||||||
|
|
||||||
@@ -251,7 +253,7 @@ A `c3c` executable will be found under `bin/`.
|
|||||||
#### Installing on OS X using Homebrew
|
#### Installing on OS X using Homebrew
|
||||||
|
|
||||||
2. Install CMake: `brew install cmake`
|
2. Install CMake: `brew install cmake`
|
||||||
3. Install LLVM 15: `brew install llvm`
|
3. Install LLVM 17+: `brew install llvm`
|
||||||
4. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
|
4. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
|
||||||
5. Enter the C3C directory `cd c3c`.
|
5. Enter the C3C directory `cd c3c`.
|
||||||
6. Create a build directory `mkdir build`
|
6. Create a build directory `mkdir build`
|
||||||
@@ -306,7 +308,7 @@ You can try it out by running some sample code: `c3c.exe compile ../resources/ex
|
|||||||
|
|
||||||
1. Make sure you have a C compiler that handles C11 and a C++ compiler, such as GCC or Clang. Git also needs to be installed.
|
1. Make sure you have a C compiler that handles C11 and a C++ compiler, such as GCC or Clang. Git also needs to be installed.
|
||||||
2. Install CMake: `sudo apt install cmake`
|
2. Install CMake: `sudo apt install cmake`
|
||||||
3. Install LLVM 15 (or greater: C3C supports LLVM 15-17): `sudo apt-get install clang-15 zlib1g zlib1g-dev libllvm15 llvm-15 llvm-15-dev llvm-15-runtime liblld-15-dev liblld-15`
|
3. Install LLVM 17+ (or greater: C3C supports LLVM 17+): `sudo apt-get install clang-17 zlib1g zlib1g-dev libllvm17 llvm-17 llvm-17-dev llvm-17-runtime liblld-17-dev liblld-17`
|
||||||
4. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
|
4. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
|
||||||
5. Enter the C3C directory `cd c3c`.
|
5. Enter the C3C directory `cd c3c`.
|
||||||
6. Create a build directory `mkdir build`
|
6. Create a build directory `mkdir build`
|
||||||
@@ -321,7 +323,7 @@ You can try it out by running some sample code: `./c3c compile ../resources/exam
|
|||||||
|
|
||||||
#### Compiling on Void Linux
|
#### Compiling on Void Linux
|
||||||
|
|
||||||
1. As root, ensure that all project dependencies are installed: `xbps-install git cmake llvm15 lld-devel libcurl-devel ncurses-devel zlib-devel libzstd-devel libxml2-devel`
|
1. As root, ensure that all project dependencies are installed: `xbps-install git cmake llvm17 lld17-devel libcurl-devel ncurses-devel zlib-devel libzstd-devel libxml2-devel`
|
||||||
2. Clone the C3C repository: `git clone https://github.com/c3lang/c3c.git`
|
2. Clone the C3C repository: `git clone https://github.com/c3lang/c3c.git`
|
||||||
- If you only need the latest commit, you may want to make a shallow clone instead: `git clone https://github.com/c3lang/c3c.git --depth=1`
|
- If you only need the latest commit, you may want to make a shallow clone instead: `git clone https://github.com/c3lang/c3c.git --depth=1`
|
||||||
3. Enter the directory: `cd c3c`
|
3. Enter the directory: `cd c3c`
|
||||||
@@ -337,7 +339,7 @@ For a sytem-wide installation, run the following as root: `cmake --install .`
|
|||||||
#### Compiling on other Linux / Unix variants
|
#### Compiling on other Linux / Unix variants
|
||||||
|
|
||||||
1. Install CMake.
|
1. Install CMake.
|
||||||
2. Install or compile LLVM and LLD *libraries* (version 15+ or higher)
|
2. Install or compile LLVM and LLD *libraries* (version 17+ or higher)
|
||||||
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
|
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
|
||||||
4. Enter the C3C directory `cd c3c`.
|
4. Enter the C3C directory `cd c3c`.
|
||||||
5. Create a build directory `mkdir build`
|
5. Create a build directory `mkdir build`
|
||||||
|
|||||||
@@ -1,43 +1,44 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
## build-with-docker.sh
|
|
||||||
## @author gdm85
|
|
||||||
## @modified by Kenta
|
|
||||||
##
|
|
||||||
## Script to build c3c for Ubuntu 22
|
|
||||||
##
|
|
||||||
#
|
|
||||||
|
|
||||||
read -p "Select Build Type: Debug/Release: " config
|
: ${DOCKER:=docker}
|
||||||
|
: ${IMAGE:="c3c-builder"}
|
||||||
|
: ${CMAKE_BUILD_TYPE:=Release}
|
||||||
|
: ${LLVM_VERSION:=18}
|
||||||
|
: ${UBUNTU_VERSION:="22.04"}
|
||||||
|
: ${CMAKE_VERSION:="3.20.0"}
|
||||||
|
|
||||||
set -e
|
cd docker || exit 1 # Exit if the 'docker' directory doesn't exist
|
||||||
|
|
||||||
DOCKER=docker
|
$DOCKER build \
|
||||||
DOCKER_RUN=""
|
--build-arg LLVM_VERSION=$LLVM_VERSION \
|
||||||
IMAGE="c3c-builder"
|
--build-arg CMAKE_VERSION=$CMAKE_VERSION \
|
||||||
if type podman 2>/dev/null >/dev/null; then
|
--build-arg UBUNTU_VERSION=$UBUNTU_VERSION \
|
||||||
DOCKER=podman
|
-t $IMAGE .
|
||||||
DOCKER_RUN="--userns=keep-id"
|
|
||||||
IMAGE="localhost/$IMAGE"
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Docker image build failed. Exiting."
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $config == "Debug" ]; then
|
|
||||||
CMAKE_BUILD_TYPE=Debug
|
|
||||||
else
|
|
||||||
CMAKE_BUILD_TYPE="$config"
|
|
||||||
fi
|
|
||||||
|
|
||||||
UBUNTU_VERSION="22.10"
|
|
||||||
LLVM_VERSION="15"
|
|
||||||
|
|
||||||
IMAGE="$IMAGE:22"
|
|
||||||
|
|
||||||
cd docker && $DOCKER build -t $IMAGE\
|
|
||||||
--build-arg DEPS="llvm-$LLVM_VERSION-dev liblld-$LLVM_VERSION-dev clang-$LLVM_VERSION libllvm$LLVM_VERSION llvm-$LLVM_VERSION-runtime" \
|
|
||||||
--build-arg UBUNTU_VERSION="$UBUNTU_VERSION" .
|
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
rm -rf build bin
|
rm -rf build bin
|
||||||
mkdir -p build bin
|
mkdir -p build bin
|
||||||
|
|
||||||
exec $DOCKER run -ti --rm --tmpfs=/tmp $DOCKER_RUN -v "$PWD":/home/c3c/source -w /home/c3c/source $IMAGE bash -c \
|
chmod -R 777 build bin
|
||||||
"cd build && cmake -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE -DC3_LLVM_VERSION=$LLVM_VERSION .. && cmake --build . && mv c3c lib ../bin/"
|
|
||||||
|
exec $DOCKER run -i --rm \
|
||||||
|
-v "$PWD":/home/c3c/source \
|
||||||
|
-w /home/c3c/source $IMAGE bash -c \
|
||||||
|
"cmake -S . -B build \
|
||||||
|
-G Ninja \
|
||||||
|
-DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE \
|
||||||
|
-DCMAKE_C_COMPILER=clang-$LLVM_VERSION \
|
||||||
|
-DCMAKE_CXX_COMPILER=clang++-$LLVM_VERSION \
|
||||||
|
-DCMAKE_LINKER=lld-$LLVM_VERSION \
|
||||||
|
-DCMAKE_OBJCOPY=llvm-objcopy-$LLVM_VERSION \
|
||||||
|
-DCMAKE_STRIP=llvm-strip-$LLVM_VERSION \
|
||||||
|
-DCMAKE_DLLTOOL=llvm-dlltool-$LLVM_VERSION \
|
||||||
|
-DC3_LLVM_VERSION=auto && \
|
||||||
|
cmake --build build && \
|
||||||
|
cp -r build/c3c build/lib bin"
|
||||||
@@ -1,16 +1,49 @@
|
|||||||
|
ARG UBUNTU_VERSION=22.04
|
||||||
|
FROM ubuntu:${UBUNTU_VERSION}
|
||||||
|
|
||||||
ARG UBUNTU_VERSION
|
ARG LLVM_VERSION=18
|
||||||
FROM ubuntu:$UBUNTU_VERSION
|
ENV LLVM_DEV_VERSION=20
|
||||||
|
|
||||||
ARG DEPS
|
ARG CMAKE_VERSION=3.20
|
||||||
|
|
||||||
RUN export DEBIAN_FRONTEND=noninteractive && export TERM=xterm && apt-get update && apt-get install -y build-essential cmake zlib1g zlib1g-dev \
|
RUN apt-get update && apt-get install -y wget gnupg software-properties-common zlib1g zlib1g-dev python3 ninja-build curl g++ && \
|
||||||
$DEPS && \
|
wget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-$CMAKE_VERSION-linux-x86_64.sh && \
|
||||||
rm -rf /var/lib/apt/lists/*
|
mkdir -p /opt/cmake && \
|
||||||
|
sh cmake-${CMAKE_VERSION}-linux-x86_64.sh --prefix=/opt/cmake --skip-license && \
|
||||||
|
rm cmake-${CMAKE_VERSION}-linux-x86_64.sh && \
|
||||||
|
ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake
|
||||||
|
|
||||||
ARG GID=1000
|
RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - && \
|
||||||
ARG UID=1000
|
if [ "${LLVM_VERSION}" -lt 18 ]; then \
|
||||||
|
add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${LLVM_VERSION} main" && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y -t llvm-toolchain-focal-${LLVM_VERSION} \
|
||||||
|
libpolly-${LLVM_VERSION}-dev \
|
||||||
|
clang-${LLVM_VERSION} llvm-${LLVM_VERSION} llvm-${LLVM_VERSION}-dev \
|
||||||
|
lld-${LLVM_VERSION} liblld-${LLVM_VERSION}-dev libmlir-${LLVM_VERSION} \
|
||||||
|
libmlir-${LLVM_VERSION}-dev mlir-${LLVM_VERSION}-tools; \
|
||||||
|
elif [ "${LLVM_VERSION}" -lt "${LLVM_DEV_VERSION}" ]; then \
|
||||||
|
add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${LLVM_VERSION} main" && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y -t llvm-toolchain-focal-${LLVM_VERSION} \
|
||||||
|
libpolly-${LLVM_VERSION}-dev \
|
||||||
|
clang-${LLVM_VERSION} clang++-${LLVM_VERSION} llvm-${LLVM_VERSION} llvm-${LLVM_VERSION}-dev \
|
||||||
|
lld-${LLVM_VERSION} liblld-${LLVM_VERSION}-dev; \
|
||||||
|
else \
|
||||||
|
add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main" && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y -t llvm-toolchain-focal \
|
||||||
|
libpolly-${LLVM_VERSION}-dev \
|
||||||
|
clang-${LLVM_VERSION} llvm-${LLVM_VERSION} llvm-${LLVM_VERSION}-dev \
|
||||||
|
lld-${LLVM_VERSION} liblld-${LLVM_VERSION}-dev; \
|
||||||
|
fi && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
RUN groupadd -o --gid=$GID c3c && useradd --gid=$GID --uid=$GID --create-home --shell /bin/bash c3c
|
RUN groupadd -g 1337 c3c && \
|
||||||
|
useradd -m -u 1337 -g c3c c3c
|
||||||
|
|
||||||
|
# Add cmake to PATH for user c3c
|
||||||
USER c3c
|
USER c3c
|
||||||
|
ENV PATH="/opt/cmake/bin:${PATH}"
|
||||||
|
|
||||||
|
WORKDIR /home/c3c
|
||||||
@@ -124,7 +124,7 @@ macro @atomic_exec(#func, data, value, ordering) @local
|
|||||||
case RELEASE: return #func(data, value, RELEASE);
|
case RELEASE: return #func(data, value, RELEASE);
|
||||||
case ACQUIRE_RELEASE: return #func(data, value, ACQUIRE_RELEASE);
|
case ACQUIRE_RELEASE: return #func(data, value, ACQUIRE_RELEASE);
|
||||||
case SEQ_CONSISTENT: return #func(data, value, SEQ_CONSISTENT);
|
case SEQ_CONSISTENT: return #func(data, value, SEQ_CONSISTENT);
|
||||||
default: assert(false, "Ordering may not be non-atomic or unordered.");
|
default: unreachable("Ordering may not be non-atomic or unordered.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ macro @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, $succe
|
|||||||
case AtomicOrdering.RELAXED.ordinal: return $$compare_exchange(ptr, expected, desired, false, false, $success, AtomicOrdering.RELAXED.ordinal, $alignment);
|
case AtomicOrdering.RELAXED.ordinal: return $$compare_exchange(ptr, expected, desired, false, false, $success, AtomicOrdering.RELAXED.ordinal, $alignment);
|
||||||
case AtomicOrdering.ACQUIRE.ordinal: return $$compare_exchange(ptr, expected, desired, false, false, $success, AtomicOrdering.ACQUIRE.ordinal, $alignment);
|
case AtomicOrdering.ACQUIRE.ordinal: return $$compare_exchange(ptr, expected, desired, false, false, $success, AtomicOrdering.ACQUIRE.ordinal, $alignment);
|
||||||
case AtomicOrdering.SEQ_CONSISTENT.ordinal: return $$compare_exchange(ptr, expected, desired, false, false, $success, AtomicOrdering.SEQ_CONSISTENT.ordinal, $alignment);
|
case AtomicOrdering.SEQ_CONSISTENT.ordinal: return $$compare_exchange(ptr, expected, desired, false, false, $success, AtomicOrdering.SEQ_CONSISTENT.ordinal, $alignment);
|
||||||
default: assert(false, "Unrecognized failure ordering");
|
default: unreachable("Unrecognized failure ordering");
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -23,12 +23,12 @@ macro @__atomic_compare_exchange_ordering_success(ptr, expected, desired, succes
|
|||||||
case AtomicOrdering.RELEASE.ordinal: return @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, AtomicOrdering.RELEASE.ordinal, failure, $alignment);
|
case AtomicOrdering.RELEASE.ordinal: return @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, AtomicOrdering.RELEASE.ordinal, failure, $alignment);
|
||||||
case AtomicOrdering.ACQUIRE_RELEASE.ordinal: return @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, AtomicOrdering.ACQUIRE_RELEASE.ordinal, failure, $alignment);
|
case AtomicOrdering.ACQUIRE_RELEASE.ordinal: return @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, AtomicOrdering.ACQUIRE_RELEASE.ordinal, failure, $alignment);
|
||||||
case AtomicOrdering.SEQ_CONSISTENT.ordinal: return @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, AtomicOrdering.SEQ_CONSISTENT.ordinal, failure, $alignment);
|
case AtomicOrdering.SEQ_CONSISTENT.ordinal: return @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, AtomicOrdering.SEQ_CONSISTENT.ordinal, failure, $alignment);
|
||||||
default: assert(false, "Unrecognized success ordering");
|
default: unreachable("Unrecognized success ordering");
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn CInt __atomic_compare_exchange(CInt size, any* ptr, any* expected, any* desired, CInt success, CInt failure) @extern("__atomic_compare_exchange") @export
|
fn CInt __atomic_compare_exchange(CInt size, any ptr, any expected, any desired, CInt success, CInt failure) @extern("__atomic_compare_exchange") @export
|
||||||
{
|
{
|
||||||
switch (size)
|
switch (size)
|
||||||
{
|
{
|
||||||
@@ -57,7 +57,7 @@ fn CInt __atomic_compare_exchange(CInt size, any* ptr, any* expected, any* desir
|
|||||||
nextcase;
|
nextcase;
|
||||||
$endif
|
$endif
|
||||||
default:
|
default:
|
||||||
assert(false, "Unsuported size (%d) for atomic_compare_exchange", size);
|
unreachable("Unsuported size (%d) for atomic_compare_exchange", size);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright (c) 2024 Christoffer Lerno. All rights reserved.
|
// Copyright (c) 2024 Christoffer Lerno. All rights reserved.
|
||||||
// Use of self source code is governed by the MIT license
|
// Use of self source code is governed by the MIT license
|
||||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||||
module std::collections::generic_list;
|
module std::collections::anylist;
|
||||||
import std::io,std::math;
|
import std::io,std::math;
|
||||||
|
|
||||||
def GenericPredicate = fn bool(any* value);
|
def AnyPredicate = fn bool(any value);
|
||||||
def GenericTest = fn bool(any* type, any* context);
|
def AnyTest = fn bool(any type, any context);
|
||||||
|
|
||||||
struct GenericList (Printable)
|
struct AnyList (Printable)
|
||||||
{
|
{
|
||||||
usz size;
|
usz size;
|
||||||
usz capacity;
|
usz capacity;
|
||||||
Allocator* allocator;
|
Allocator allocator;
|
||||||
any** entries;
|
any* entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -20,14 +20,14 @@ struct GenericList (Printable)
|
|||||||
* @param initial_capacity "The initial capacity to reserve"
|
* @param initial_capacity "The initial capacity to reserve"
|
||||||
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
||||||
**/
|
**/
|
||||||
fn GenericList* GenericList.new_init(&self, usz initial_capacity = 16, Allocator* allocator = allocator::heap())
|
fn AnyList* AnyList.new_init(&self, usz initial_capacity = 16, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
self.allocator = allocator;
|
self.allocator = allocator;
|
||||||
self.size = 0;
|
self.size = 0;
|
||||||
if (initial_capacity > 0)
|
if (initial_capacity > 0)
|
||||||
{
|
{
|
||||||
initial_capacity = math::next_power_of_2(initial_capacity);
|
initial_capacity = math::next_power_of_2(initial_capacity);
|
||||||
self.entries = allocator::alloc_array(allocator, any*, initial_capacity);
|
self.entries = allocator::alloc_array(allocator, any, initial_capacity);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -37,26 +37,17 @@ fn GenericList* GenericList.new_init(&self, usz initial_capacity = 16, Allocator
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param initial_capacity "The initial capacity to reserve"
|
|
||||||
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
|
||||||
**/
|
|
||||||
fn GenericList* GenericList.init_new(&self, usz initial_capacity = 16, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
|
|
||||||
{
|
|
||||||
return self.new_init(initial_capacity, allocator) @inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the list using the temp allocator.
|
* Initialize the list using the temp allocator.
|
||||||
*
|
*
|
||||||
* @param initial_capacity "The initial capacity to reserve"
|
* @param initial_capacity "The initial capacity to reserve"
|
||||||
**/
|
**/
|
||||||
fn GenericList* GenericList.temp_init(&self, usz initial_capacity = 16)
|
fn AnyList* AnyList.temp_init(&self, usz initial_capacity = 16)
|
||||||
{
|
{
|
||||||
return self.new_init(initial_capacity, allocator::temp()) @inline;
|
return self.new_init(initial_capacity, allocator::temp()) @inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usz! GenericList.to_format(&self, Formatter* formatter) @dynamic
|
fn usz! AnyList.to_format(&self, Formatter* formatter) @dynamic
|
||||||
{
|
{
|
||||||
switch (self.size)
|
switch (self.size)
|
||||||
{
|
{
|
||||||
@@ -76,12 +67,12 @@ fn usz! GenericList.to_format(&self, Formatter* formatter) @dynamic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String GenericList.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
|
fn String AnyList.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
|
||||||
{
|
{
|
||||||
return string::new_format("%s", *self, .allocator = allocator);
|
return string::new_format("%s", *self, .allocator = allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String GenericList.to_tstring(&self)
|
fn String AnyList.to_tstring(&self)
|
||||||
{
|
{
|
||||||
return string::tformat("%s", *self);
|
return string::tformat("%s", *self);
|
||||||
}
|
}
|
||||||
@@ -89,13 +80,13 @@ fn String GenericList.to_tstring(&self)
|
|||||||
/**
|
/**
|
||||||
* Push an element on the list by cloning it.
|
* Push an element on the list by cloning it.
|
||||||
**/
|
**/
|
||||||
macro void GenericList.push(&self, element)
|
macro void AnyList.push(&self, element)
|
||||||
{
|
{
|
||||||
if (!self.allocator) self.allocator = allocator::heap();
|
if (!self.allocator) self.allocator = allocator::heap();
|
||||||
self.append_internal(allocator::clone(self.allocator, element));
|
self.append_internal(allocator::clone(self.allocator, element));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void GenericList.append_internal(&self, any* element) @local
|
fn void AnyList.append_internal(&self, any element) @local
|
||||||
{
|
{
|
||||||
self.ensure_capacity();
|
self.ensure_capacity();
|
||||||
self.entries[self.size++] = element;
|
self.entries[self.size++] = element;
|
||||||
@@ -104,7 +95,7 @@ fn void GenericList.append_internal(&self, any* element) @local
|
|||||||
/**
|
/**
|
||||||
* Free a retained element removed using *_retained.
|
* Free a retained element removed using *_retained.
|
||||||
**/
|
**/
|
||||||
fn void GenericList.free_element(&self, any* element) @inline
|
fn void AnyList.free_element(&self, any element) @inline
|
||||||
{
|
{
|
||||||
allocator::free(self.allocator, element.ptr);
|
allocator::free(self.allocator, element.ptr);
|
||||||
}
|
}
|
||||||
@@ -115,7 +106,7 @@ fn void GenericList.free_element(&self, any* element) @inline
|
|||||||
*
|
*
|
||||||
* @return! CastResult.TYPE_MISMATCH, IteratorResult.NO_MORE_ELEMENT
|
* @return! CastResult.TYPE_MISMATCH, IteratorResult.NO_MORE_ELEMENT
|
||||||
**/
|
**/
|
||||||
macro GenericList.pop(&self, $Type)
|
macro AnyList.pop(&self, $Type)
|
||||||
{
|
{
|
||||||
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
defer self.free_element(self.entries[self.size]);
|
defer self.free_element(self.entries[self.size]);
|
||||||
@@ -126,7 +117,7 @@ macro GenericList.pop(&self, $Type)
|
|||||||
* Pop the last value and allocate the copy using the given allocator.
|
* Pop the last value and allocate the copy using the given allocator.
|
||||||
* @return! IteratorResult.NO_MORE_ELEMENT
|
* @return! IteratorResult.NO_MORE_ELEMENT
|
||||||
**/
|
**/
|
||||||
fn any*! GenericList.new_pop(&self, Allocator* allocator = allocator::heap())
|
fn any! AnyList.new_pop(&self, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
defer self.free_element(self.entries[self.size]);
|
defer self.free_element(self.entries[self.size]);
|
||||||
@@ -137,19 +128,19 @@ fn any*! GenericList.new_pop(&self, Allocator* allocator = allocator::heap())
|
|||||||
* Pop the last value and allocate the copy using the temp allocator
|
* Pop the last value and allocate the copy using the temp allocator
|
||||||
* @return! IteratorResult.NO_MORE_ELEMENT
|
* @return! IteratorResult.NO_MORE_ELEMENT
|
||||||
**/
|
**/
|
||||||
fn any*! GenericList.temp_pop(&self) => self.new_pop(allocator::temp());
|
fn any! AnyList.temp_pop(&self) => self.new_pop(allocator::temp());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pop the last value. It must later be released using list.free_element()
|
* Pop the last value. It must later be released using list.free_element()
|
||||||
* @return! IteratorResult.NO_MORE_ELEMENT
|
* @return! IteratorResult.NO_MORE_ELEMENT
|
||||||
**/
|
**/
|
||||||
fn any*! GenericList.pop_retained(&self)
|
fn any! AnyList.pop_retained(&self)
|
||||||
{
|
{
|
||||||
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
return self.entries[--self.size];
|
return self.entries[--self.size];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void GenericList.clear(&self)
|
fn void AnyList.clear(&self)
|
||||||
{
|
{
|
||||||
for (usz i = 0; i < self.size; i++)
|
for (usz i = 0; i < self.size; i++)
|
||||||
{
|
{
|
||||||
@@ -161,7 +152,7 @@ fn void GenericList.clear(&self)
|
|||||||
/**
|
/**
|
||||||
* Same as pop() but pops the first value instead.
|
* Same as pop() but pops the first value instead.
|
||||||
**/
|
**/
|
||||||
macro GenericList.pop_first(&self, $Type)
|
macro AnyList.pop_first(&self, $Type)
|
||||||
{
|
{
|
||||||
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
defer self.remove_at(0);
|
defer self.remove_at(0);
|
||||||
@@ -171,7 +162,7 @@ macro GenericList.pop_first(&self, $Type)
|
|||||||
/**
|
/**
|
||||||
* Same as pop_retained() but pops the first value instead.
|
* Same as pop_retained() but pops the first value instead.
|
||||||
**/
|
**/
|
||||||
fn any*! GenericList.pop_first_retained(&self)
|
fn any! AnyList.pop_first_retained(&self)
|
||||||
{
|
{
|
||||||
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
defer self.remove_at(0);
|
defer self.remove_at(0);
|
||||||
@@ -181,7 +172,7 @@ fn any*! GenericList.pop_first_retained(&self)
|
|||||||
/**
|
/**
|
||||||
* Same as new_pop() but pops the first value instead.
|
* Same as new_pop() but pops the first value instead.
|
||||||
**/
|
**/
|
||||||
fn any*! GenericList.new_pop_first(&self, Allocator* allocator = allocator::heap())
|
fn any! AnyList.new_pop_first(&self, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
defer self.free_element(self.entries[self.size]);
|
defer self.free_element(self.entries[self.size]);
|
||||||
@@ -192,19 +183,19 @@ fn any*! GenericList.new_pop_first(&self, Allocator* allocator = allocator::heap
|
|||||||
/**
|
/**
|
||||||
* Same as temp_pop() but pops the first value instead.
|
* Same as temp_pop() but pops the first value instead.
|
||||||
**/
|
**/
|
||||||
fn any*! GenericList.temp_pop_first(&self) => self.new_pop_first(allocator::temp());
|
fn any! AnyList.temp_pop_first(&self) => self.new_pop_first(allocator::temp());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require index < self.size
|
* @require index < self.size
|
||||||
**/
|
**/
|
||||||
fn void GenericList.remove_at(&self, usz index)
|
fn void AnyList.remove_at(&self, usz index)
|
||||||
{
|
{
|
||||||
if (!--self.size || index == self.size) return;
|
if (!--self.size || index == self.size) return;
|
||||||
self.free_element(self.entries[index]);
|
self.free_element(self.entries[index]);
|
||||||
self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
|
self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void GenericList.add_all(&self, GenericList* other_list)
|
fn void AnyList.add_all(&self, AnyList* other_list)
|
||||||
{
|
{
|
||||||
if (!other_list.size) return;
|
if (!other_list.size) return;
|
||||||
self.reserve(other_list.size);
|
self.reserve(other_list.size);
|
||||||
@@ -217,7 +208,7 @@ fn void GenericList.add_all(&self, GenericList* other_list)
|
|||||||
/**
|
/**
|
||||||
* Reverse the elements in a list.
|
* Reverse the elements in a list.
|
||||||
**/
|
**/
|
||||||
fn void GenericList.reverse(&self)
|
fn void AnyList.reverse(&self)
|
||||||
{
|
{
|
||||||
if (self.size < 2) return;
|
if (self.size < 2) return;
|
||||||
usz half = self.size / 2U;
|
usz half = self.size / 2U;
|
||||||
@@ -228,7 +219,7 @@ fn void GenericList.reverse(&self)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn any*[] GenericList.array_view(&self)
|
fn any[] AnyList.array_view(&self)
|
||||||
{
|
{
|
||||||
return self.entries[:self.size];
|
return self.entries[:self.size];
|
||||||
}
|
}
|
||||||
@@ -236,7 +227,7 @@ fn any*[] GenericList.array_view(&self)
|
|||||||
/**
|
/**
|
||||||
* Push an element to the front of the list.
|
* Push an element to the front of the list.
|
||||||
**/
|
**/
|
||||||
macro void GenericList.push_front(&self, type)
|
macro void AnyList.push_front(&self, type)
|
||||||
{
|
{
|
||||||
self.insert_at(0, type);
|
self.insert_at(0, type);
|
||||||
}
|
}
|
||||||
@@ -244,16 +235,16 @@ macro void GenericList.push_front(&self, type)
|
|||||||
/**
|
/**
|
||||||
* @require index < self.size
|
* @require index < self.size
|
||||||
**/
|
**/
|
||||||
macro void GenericList.insert_at(&self, usz index, type) @local
|
macro void AnyList.insert_at(&self, usz index, type) @local
|
||||||
{
|
{
|
||||||
any* value = allocator::copy(self.allocator, type);
|
any value = allocator::copy(self.allocator, type);
|
||||||
self.insert_at_internal(self, index, value);
|
self.insert_at_internal(self, index, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require index < self.size
|
* @require index < self.size
|
||||||
**/
|
**/
|
||||||
fn void GenericList.insert_at_internal(&self, usz index, any* value) @local
|
fn void AnyList.insert_at_internal(&self, usz index, any value) @local
|
||||||
{
|
{
|
||||||
self.ensure_capacity();
|
self.ensure_capacity();
|
||||||
for (usz i = self.size; i > index; i--)
|
for (usz i = self.size; i > index; i--)
|
||||||
@@ -268,7 +259,7 @@ fn void GenericList.insert_at_internal(&self, usz index, any* value) @local
|
|||||||
/**
|
/**
|
||||||
* @require self.size > 0
|
* @require self.size > 0
|
||||||
**/
|
**/
|
||||||
fn void GenericList.remove_last(&self)
|
fn void AnyList.remove_last(&self)
|
||||||
{
|
{
|
||||||
self.free_element(self.entries[--self.size]);
|
self.free_element(self.entries[--self.size]);
|
||||||
}
|
}
|
||||||
@@ -276,37 +267,37 @@ fn void GenericList.remove_last(&self)
|
|||||||
/**
|
/**
|
||||||
* @require self.size > 0
|
* @require self.size > 0
|
||||||
**/
|
**/
|
||||||
fn void GenericList.remove_first(&self)
|
fn void AnyList.remove_first(&self)
|
||||||
{
|
{
|
||||||
self.remove_at(0);
|
self.remove_at(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro GenericList.first(&self, $Type)
|
macro AnyList.first(&self, $Type)
|
||||||
{
|
{
|
||||||
return *anycast(self.first_any(), $Type);
|
return *anycast(self.first_any(), $Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn any*! GenericList.first_any(&self) @inline
|
fn any! AnyList.first_any(&self) @inline
|
||||||
{
|
{
|
||||||
return self.size ? self.entries[0] : IteratorResult.NO_MORE_ELEMENT?;
|
return self.size ? self.entries[0] : IteratorResult.NO_MORE_ELEMENT?;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro GenericList.last(&self, $Type)
|
macro AnyList.last(&self, $Type)
|
||||||
{
|
{
|
||||||
return *anycast(self.last_any(), $Type);
|
return *anycast(self.last_any(), $Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn any*! GenericList.last_any(&self) @inline
|
fn any! AnyList.last_any(&self) @inline
|
||||||
{
|
{
|
||||||
return self.size ? self.entries[self.size - 1] : IteratorResult.NO_MORE_ELEMENT?;
|
return self.size ? self.entries[self.size - 1] : IteratorResult.NO_MORE_ELEMENT?;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bool GenericList.is_empty(&self) @inline
|
fn bool AnyList.is_empty(&self) @inline
|
||||||
{
|
{
|
||||||
return !self.size;
|
return !self.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usz GenericList.len(&self) @operator(len) @inline
|
fn usz AnyList.len(&self) @operator(len) @inline
|
||||||
{
|
{
|
||||||
return self.size;
|
return self.size;
|
||||||
}
|
}
|
||||||
@@ -314,7 +305,7 @@ fn usz GenericList.len(&self) @operator(len) @inline
|
|||||||
/**
|
/**
|
||||||
* @require index < self.size "Index out of range"
|
* @require index < self.size "Index out of range"
|
||||||
**/
|
**/
|
||||||
macro GenericList.get(&self, usz index, $Type)
|
macro AnyList.get(&self, usz index, $Type)
|
||||||
{
|
{
|
||||||
return *anycast(self.entries[index], $Type);
|
return *anycast(self.entries[index], $Type);
|
||||||
}
|
}
|
||||||
@@ -322,12 +313,12 @@ macro GenericList.get(&self, usz index, $Type)
|
|||||||
/**
|
/**
|
||||||
* @require index < self.size "Index out of range"
|
* @require index < self.size "Index out of range"
|
||||||
**/
|
**/
|
||||||
fn any* GenericList.get_any(&self, usz index) @inline
|
fn any AnyList.get_any(&self, usz index) @inline
|
||||||
{
|
{
|
||||||
return self.entries[index];
|
return self.entries[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void GenericList.free(&self)
|
fn void AnyList.free(&self)
|
||||||
{
|
{
|
||||||
if (!self.allocator) return;
|
if (!self.allocator) return;
|
||||||
self.clear();
|
self.clear();
|
||||||
@@ -336,9 +327,9 @@ fn void GenericList.free(&self)
|
|||||||
self.entries = null;
|
self.entries = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void GenericList.swap(&self, usz i, usz j)
|
fn void AnyList.swap(&self, usz i, usz j)
|
||||||
{
|
{
|
||||||
any* temp = self.entries[i];
|
any temp = self.entries[i];
|
||||||
self.entries[i] = self.entries[j];
|
self.entries[i] = self.entries[j];
|
||||||
self.entries[j] = temp;
|
self.entries[j] = temp;
|
||||||
}
|
}
|
||||||
@@ -347,7 +338,7 @@ fn void GenericList.swap(&self, usz i, usz j)
|
|||||||
* @param filter "The function to determine if it should be removed or not"
|
* @param filter "The function to determine if it should be removed or not"
|
||||||
* @return "the number of deleted elements"
|
* @return "the number of deleted elements"
|
||||||
**/
|
**/
|
||||||
fn usz GenericList.remove_if(&self, GenericPredicate filter)
|
fn usz AnyList.remove_if(&self, AnyPredicate filter)
|
||||||
{
|
{
|
||||||
return self._remove_if(filter, false);
|
return self._remove_if(filter, false);
|
||||||
}
|
}
|
||||||
@@ -356,12 +347,12 @@ fn usz GenericList.remove_if(&self, GenericPredicate filter)
|
|||||||
* @param selection "The function to determine if it should be kept or not"
|
* @param selection "The function to determine if it should be kept or not"
|
||||||
* @return "the number of deleted elements"
|
* @return "the number of deleted elements"
|
||||||
**/
|
**/
|
||||||
fn usz GenericList.retain_if(&self, GenericPredicate selection)
|
fn usz AnyList.retain_if(&self, AnyPredicate selection)
|
||||||
{
|
{
|
||||||
return self._remove_if(selection, true);
|
return self._remove_if(selection, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro usz GenericList._remove_if(&self, GenericPredicate filter, bool $invert) @local
|
macro usz AnyList._remove_if(&self, AnyPredicate filter, bool $invert) @local
|
||||||
{
|
{
|
||||||
usz size = self.size;
|
usz size = self.size;
|
||||||
for (usz i = size, usz k = size; k > 0; k = i)
|
for (usz i = size, usz k = size; k > 0; k = i)
|
||||||
@@ -387,17 +378,17 @@ macro usz GenericList._remove_if(&self, GenericPredicate filter, bool $invert) @
|
|||||||
return size - self.size;
|
return size - self.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usz GenericList.remove_using_test(&self, GenericTest filter, any* context)
|
fn usz AnyList.remove_using_test(&self, AnyTest filter, any context)
|
||||||
{
|
{
|
||||||
return self._remove_using_test(filter, false, context);
|
return self._remove_using_test(filter, false, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usz GenericList.retain_using_test(&self, GenericTest filter, any* context)
|
fn usz AnyList.retain_using_test(&self, AnyTest filter, any context)
|
||||||
{
|
{
|
||||||
return self._remove_using_test(filter, true, context);
|
return self._remove_using_test(filter, true, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro usz GenericList._remove_using_test(&self, GenericTest filter, bool $invert, ctx) @local
|
macro usz AnyList._remove_using_test(&self, AnyTest filter, bool $invert, ctx) @local
|
||||||
{
|
{
|
||||||
usz size = self.size;
|
usz size = self.size;
|
||||||
for (usz i = size, usz k = size; k > 0; k = i)
|
for (usz i = size, usz k = size; k > 0; k = i)
|
||||||
@@ -426,17 +417,17 @@ macro usz GenericList._remove_using_test(&self, GenericTest filter, bool $invert
|
|||||||
/**
|
/**
|
||||||
* Reserve at least min_capacity
|
* Reserve at least min_capacity
|
||||||
**/
|
**/
|
||||||
fn void GenericList.reserve(&self, usz min_capacity)
|
fn void AnyList.reserve(&self, usz min_capacity)
|
||||||
{
|
{
|
||||||
if (!min_capacity) return;
|
if (!min_capacity) return;
|
||||||
if (self.capacity >= min_capacity) return;
|
if (self.capacity >= min_capacity) return;
|
||||||
if (!self.allocator) self.allocator = allocator::heap();
|
if (!self.allocator) self.allocator = allocator::heap();
|
||||||
min_capacity = math::next_power_of_2(min_capacity);
|
min_capacity = math::next_power_of_2(min_capacity);
|
||||||
self.entries = allocator::realloc(self.allocator, self.entries, any*.sizeof * min_capacity);
|
self.entries = allocator::realloc(self.allocator, self.entries, any.sizeof * min_capacity);
|
||||||
self.capacity = min_capacity;
|
self.capacity = min_capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro any* GenericList.@item_at(&self, usz index) @operator([])
|
macro any AnyList.@item_at(&self, usz index) @operator([])
|
||||||
{
|
{
|
||||||
return self.entries[index];
|
return self.entries[index];
|
||||||
}
|
}
|
||||||
@@ -444,7 +435,7 @@ macro any* GenericList.@item_at(&self, usz index) @operator([])
|
|||||||
/**
|
/**
|
||||||
* @require index <= self.size "Index out of range"
|
* @require index <= self.size "Index out of range"
|
||||||
**/
|
**/
|
||||||
macro void GenericList.set(&self, usz index, value)
|
macro void AnyList.set(&self, usz index, value)
|
||||||
{
|
{
|
||||||
if (index == self.size)
|
if (index == self.size)
|
||||||
{
|
{
|
||||||
@@ -455,7 +446,7 @@ macro void GenericList.set(&self, usz index, value)
|
|||||||
self.entries[index] = allocator::copy(self.allocator, value);
|
self.entries[index] = allocator::copy(self.allocator, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void GenericList.ensure_capacity(&self, usz added = 1) @inline @private
|
fn void AnyList.ensure_capacity(&self, usz added = 1) @inline @private
|
||||||
{
|
{
|
||||||
usz new_size = self.size + added;
|
usz new_size = self.size + added;
|
||||||
if (self.capacity >= new_size) return;
|
if (self.capacity >= new_size) return;
|
||||||
@@ -86,31 +86,17 @@ struct GrowableBitSet
|
|||||||
* @param initial_capacity
|
* @param initial_capacity
|
||||||
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
||||||
**/
|
**/
|
||||||
fn GrowableBitSet* GrowableBitSet.new_init(&self, usz initial_capacity = 1, Allocator* allocator = allocator::heap())
|
fn GrowableBitSet* GrowableBitSet.new_init(&self, usz initial_capacity = 1, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
self.data.new_init(initial_capacity, allocator);
|
self.data.new_init(initial_capacity, allocator);
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param initial_capacity
|
|
||||||
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
|
||||||
**/
|
|
||||||
fn GrowableBitSet* GrowableBitSet.init_new(&self, usz initial_capacity = 1, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
|
|
||||||
{
|
|
||||||
return self.new_init(initial_capacity, allocator) @inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn GrowableBitSet* GrowableBitSet.temp_init(&self, usz initial_capacity = 1)
|
fn GrowableBitSet* GrowableBitSet.temp_init(&self, usz initial_capacity = 1)
|
||||||
{
|
{
|
||||||
return self.new_init(initial_capacity, allocator::temp()) @inline;
|
return self.new_init(initial_capacity, allocator::temp()) @inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GrowableBitSet* GrowableBitSet.init_temp(&self, usz initial_capacity = 1) @deprecated("Replaced by temp_init")
|
|
||||||
{
|
|
||||||
return self.temp_init(initial_capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn void GrowableBitSet.free(&self)
|
fn void GrowableBitSet.free(&self)
|
||||||
{
|
{
|
||||||
self.data.free();
|
self.data.free();
|
||||||
|
|||||||
434
lib/std/collections/elastic_array.c3
Normal file
434
lib/std/collections/elastic_array.c3
Normal file
@@ -0,0 +1,434 @@
|
|||||||
|
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
|
||||||
|
// Use of self source code is governed by the MIT license
|
||||||
|
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||||
|
/**
|
||||||
|
* @require MAX_SIZE >= 1 `The size must be at least 1 element big.`
|
||||||
|
**/
|
||||||
|
module std::collections::elastic_array(<Type, MAX_SIZE>);
|
||||||
|
import std::io, std::math, std::collections::list_common;
|
||||||
|
|
||||||
|
def ElementPredicate = fn bool(Type *type);
|
||||||
|
def ElementTest = fn bool(Type *type, any context);
|
||||||
|
const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type);
|
||||||
|
const ELEMENT_IS_POINTER = Type.kindof == POINTER;
|
||||||
|
macro type_is_overaligned() => Type.alignof > mem::DEFAULT_MEM_ALIGNMENT;
|
||||||
|
|
||||||
|
struct ElasticArray (Printable)
|
||||||
|
{
|
||||||
|
usz size;
|
||||||
|
Type[MAX_SIZE] entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usz! ElasticArray.to_format(&self, Formatter* formatter) @dynamic
|
||||||
|
{
|
||||||
|
switch (self.size)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return formatter.print("[]")!;
|
||||||
|
case 1:
|
||||||
|
return formatter.printf("[%s]", self.entries[0])!;
|
||||||
|
default:
|
||||||
|
usz n = formatter.print("[")!;
|
||||||
|
foreach (i, element : self.entries[:self.size])
|
||||||
|
{
|
||||||
|
if (i != 0) formatter.print(", ")!;
|
||||||
|
n += formatter.printf("%s", element)!;
|
||||||
|
}
|
||||||
|
n += formatter.print("]")!;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn String ElasticArray.to_string(&self, Allocator allocator) @dynamic
|
||||||
|
{
|
||||||
|
return string::new_format("%s", *self, .allocator = allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn String ElasticArray.to_tstring(&self)
|
||||||
|
{
|
||||||
|
return string::tformat("%s", *self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void! ElasticArray.push_try(&self, Type element) @inline
|
||||||
|
{
|
||||||
|
if (self.size == MAX_SIZE) return AllocationFailure.OUT_OF_MEMORY?;
|
||||||
|
self.entries[self.size++] = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require self.size < MAX_SIZE `Tried to exceed the max size`
|
||||||
|
**/
|
||||||
|
fn void ElasticArray.push(&self, Type element) @inline
|
||||||
|
{
|
||||||
|
self.entries[self.size++] = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Type! ElasticArray.pop(&self)
|
||||||
|
{
|
||||||
|
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
|
return self.entries[--self.size];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void ElasticArray.clear(&self)
|
||||||
|
{
|
||||||
|
self.size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require self.size > 0
|
||||||
|
**/
|
||||||
|
fn Type! ElasticArray.pop_first(&self)
|
||||||
|
{
|
||||||
|
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
|
defer self.remove_at(0);
|
||||||
|
return self.entries[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require index < self.size
|
||||||
|
**/
|
||||||
|
fn void ElasticArray.remove_at(&self, usz index)
|
||||||
|
{
|
||||||
|
if (!--self.size || index == self.size) return;
|
||||||
|
self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require other_list.size + self.size <= MAX_SIZE
|
||||||
|
**/
|
||||||
|
fn void ElasticArray.add_all(&self, ElasticArray* other_list)
|
||||||
|
{
|
||||||
|
if (!other_list.size) return;
|
||||||
|
foreach (&value : other_list)
|
||||||
|
{
|
||||||
|
self.entries[self.size++] = *value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add as many elements as possible to the new array,
|
||||||
|
* returning the number of elements that didn't fit.
|
||||||
|
**/
|
||||||
|
fn usz ElasticArray.add_all_to_limit(&self, ElasticArray* other_list)
|
||||||
|
{
|
||||||
|
if (!other_list.size) return 0;
|
||||||
|
foreach (i, &value : other_list)
|
||||||
|
{
|
||||||
|
if (self.size == MAX_SIZE) return other_list.size - i;
|
||||||
|
self.entries[self.size++] = *value;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add as many values from this array as possible, returning the
|
||||||
|
* number of elements that didn't fit.
|
||||||
|
*
|
||||||
|
* @param [in] array
|
||||||
|
**/
|
||||||
|
fn usz ElasticArray.add_array_to_limit(&self, Type[] array)
|
||||||
|
{
|
||||||
|
if (!array.len) return 0;
|
||||||
|
foreach (i, &value : array)
|
||||||
|
{
|
||||||
|
if (self.size == MAX_SIZE) return array.len - i;
|
||||||
|
self.entries[self.size++] = *value;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the values of an array to this list.
|
||||||
|
*
|
||||||
|
* @param [in] array
|
||||||
|
* @require array.len + self.size <= MAX_SIZE `Size would exceed max.`
|
||||||
|
* @ensure self.size >= array.len
|
||||||
|
**/
|
||||||
|
fn void ElasticArray.add_array(&self, Type[] array)
|
||||||
|
{
|
||||||
|
if (!array.len) return;
|
||||||
|
foreach (&value : array)
|
||||||
|
{
|
||||||
|
self.entries[self.size++] = *value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IMPORTANT The returned array must be freed using free_aligned.
|
||||||
|
**/
|
||||||
|
fn Type[] ElasticArray.to_new_aligned_array(&self, Allocator allocator = allocator::heap())
|
||||||
|
{
|
||||||
|
return list_common::list_to_new_aligned_array(Type, self, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require !type_is_overaligned() : "This function is not available on overaligned types"
|
||||||
|
**/
|
||||||
|
macro Type[] ElasticArray.to_new_array(&self, Allocator allocator = allocator::heap())
|
||||||
|
{
|
||||||
|
return list_common::list_to_new_array(Type, self, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Type[] ElasticArray.to_tarray(&self)
|
||||||
|
{
|
||||||
|
$if type_is_overaligned():
|
||||||
|
return self.to_new_aligned_array(allocator::temp());
|
||||||
|
$else
|
||||||
|
return self.to_new_array(allocator::temp());
|
||||||
|
$endif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the elements in a list.
|
||||||
|
**/
|
||||||
|
fn void ElasticArray.reverse(&self)
|
||||||
|
{
|
||||||
|
list_common::list_reverse(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Type[] ElasticArray.array_view(&self)
|
||||||
|
{
|
||||||
|
return self.entries[:self.size];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require self.size < MAX_SIZE `List would exceed max size`
|
||||||
|
**/
|
||||||
|
fn void ElasticArray.push_front(&self, Type type) @inline
|
||||||
|
{
|
||||||
|
self.insert_at(0, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require self.size < MAX_SIZE `List would exceed max size`
|
||||||
|
**/
|
||||||
|
fn void! ElasticArray.push_front_try(&self, Type type) @inline
|
||||||
|
{
|
||||||
|
return self.insert_at_try(0, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require index <= self.size
|
||||||
|
**/
|
||||||
|
fn void! ElasticArray.insert_at_try(&self, usz index, Type value)
|
||||||
|
{
|
||||||
|
if (self.size == MAX_SIZE) return AllocationFailure.OUT_OF_MEMORY?;
|
||||||
|
self.insert_at(index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require self.size < MAX_SIZE `List would exceed max size`
|
||||||
|
* @require index <= self.size
|
||||||
|
**/
|
||||||
|
fn void ElasticArray.insert_at(&self, usz index, Type type)
|
||||||
|
{
|
||||||
|
for (usz i = self.size; i > index; i--)
|
||||||
|
{
|
||||||
|
self.entries[i] = self.entries[i - 1];
|
||||||
|
}
|
||||||
|
self.size++;
|
||||||
|
self.entries[index] = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require index < self.size
|
||||||
|
**/
|
||||||
|
fn void ElasticArray.set_at(&self, usz index, Type type)
|
||||||
|
{
|
||||||
|
self.entries[index] = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void! ElasticArray.remove_last(&self) @maydiscard
|
||||||
|
{
|
||||||
|
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
|
self.size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void! ElasticArray.remove_first(&self) @maydiscard
|
||||||
|
{
|
||||||
|
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
|
self.remove_at(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Type! ElasticArray.first(&self)
|
||||||
|
{
|
||||||
|
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
|
return self.entries[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Type! ElasticArray.last(&self)
|
||||||
|
{
|
||||||
|
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
|
return self.entries[self.size - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bool ElasticArray.is_empty(&self) @inline
|
||||||
|
{
|
||||||
|
return !self.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usz ElasticArray.byte_size(&self) @inline
|
||||||
|
{
|
||||||
|
return Type.sizeof * self.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usz ElasticArray.len(&self) @operator(len) @inline
|
||||||
|
{
|
||||||
|
return self.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Type ElasticArray.get(&self, usz index) @inline
|
||||||
|
{
|
||||||
|
return self.entries[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void ElasticArray.swap(&self, usz i, usz j)
|
||||||
|
{
|
||||||
|
@swap(self.entries[i], self.entries[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param filter "The function to determine if it should be removed or not"
|
||||||
|
* @return "the number of deleted elements"
|
||||||
|
**/
|
||||||
|
fn usz ElasticArray.remove_if(&self, ElementPredicate filter)
|
||||||
|
{
|
||||||
|
return list_common::list_remove_if(self, filter, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param selection "The function to determine if it should be kept or not"
|
||||||
|
* @return "the number of deleted elements"
|
||||||
|
**/
|
||||||
|
fn usz ElasticArray.retain_if(&self, ElementPredicate selection)
|
||||||
|
{
|
||||||
|
return list_common::list_remove_if(self, selection, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usz ElasticArray.remove_using_test(&self, ElementTest filter, any context)
|
||||||
|
{
|
||||||
|
return list_common::list_remove_using_test(self, filter, false, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usz ElasticArray.retain_using_test(&self, ElementTest filter, any context)
|
||||||
|
{
|
||||||
|
return list_common::list_remove_using_test(self, filter, true, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
macro Type ElasticArray.@item_at(&self, usz index) @operator([])
|
||||||
|
{
|
||||||
|
return self.entries[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Type* ElasticArray.get_ref(&self, usz index) @operator(&[]) @inline
|
||||||
|
{
|
||||||
|
return &self.entries[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void ElasticArray.set(&self, usz index, Type value) @operator([]=)
|
||||||
|
{
|
||||||
|
self.entries[index] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Functions for equatable types
|
||||||
|
fn usz! ElasticArray.index_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
|
||||||
|
{
|
||||||
|
foreach (i, v : self)
|
||||||
|
{
|
||||||
|
if (equals(v, type)) return i;
|
||||||
|
}
|
||||||
|
return SearchResult.MISSING?;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usz! ElasticArray.rindex_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
|
||||||
|
{
|
||||||
|
foreach_r (i, v : self)
|
||||||
|
{
|
||||||
|
if (equals(v, type)) return i;
|
||||||
|
}
|
||||||
|
return SearchResult.MISSING?;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bool ElasticArray.equals(&self, ElasticArray other_list) @if(ELEMENT_IS_EQUATABLE)
|
||||||
|
{
|
||||||
|
if (self.size != other_list.size) return false;
|
||||||
|
foreach (i, v : self)
|
||||||
|
{
|
||||||
|
if (!equals(v, other_list.entries[i])) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for presence of a value in a list.
|
||||||
|
*
|
||||||
|
* @param [&in] self "the list to find elements in"
|
||||||
|
* @param value "The value to search for"
|
||||||
|
* @return "True if the value is found, false otherwise"
|
||||||
|
**/
|
||||||
|
fn bool ElasticArray.contains(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
|
||||||
|
{
|
||||||
|
foreach (i, v : self)
|
||||||
|
{
|
||||||
|
if (equals(v, value)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param [&inout] self "The list to remove elements from"
|
||||||
|
* @param value "The value to remove"
|
||||||
|
* @return "true if the value was found"
|
||||||
|
**/
|
||||||
|
fn bool ElasticArray.remove_last_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
|
||||||
|
{
|
||||||
|
return @ok(self.remove_at(self.rindex_of(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param [&inout] self "The list to remove elements from"
|
||||||
|
* @param value "The value to remove"
|
||||||
|
* @return "true if the value was found"
|
||||||
|
**/
|
||||||
|
fn bool ElasticArray.remove_first_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
|
||||||
|
{
|
||||||
|
return @ok(self.remove_at(self.index_of(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param [&inout] self "The list to remove elements from"
|
||||||
|
* @param value "The value to remove"
|
||||||
|
* @return "the number of deleted elements."
|
||||||
|
**/
|
||||||
|
fn usz ElasticArray.remove_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
|
||||||
|
{
|
||||||
|
return list_common::list_remove_item(self, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn void ElasticArray.remove_all_from(&self, ElasticArray* other_list) @if(ELEMENT_IS_EQUATABLE)
|
||||||
|
{
|
||||||
|
if (!other_list.size) return;
|
||||||
|
foreach (v : other_list) self.remove_item(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param [&in] self
|
||||||
|
* @return "The number non-null values in the list"
|
||||||
|
**/
|
||||||
|
fn usz ElasticArray.compact_count(&self) @if(ELEMENT_IS_POINTER)
|
||||||
|
{
|
||||||
|
usz vals = 0;
|
||||||
|
foreach (v : self) if (v) vals++;
|
||||||
|
return vals;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usz ElasticArray.compact(&self) @if(ELEMENT_IS_POINTER)
|
||||||
|
{
|
||||||
|
return list_common::list_compact(self);
|
||||||
|
}
|
||||||
@@ -25,7 +25,7 @@ fn usz! EnumMap.to_format(&self, Formatter* formatter) @dynamic
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String EnumMap.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
|
fn String EnumMap.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
|
||||||
{
|
{
|
||||||
return string::new_format("%s", *self, .allocator = allocator);
|
return string::new_format("%s", *self, .allocator = allocator);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ fn usz! EnumSet.to_format(&set, Formatter* formatter) @dynamic
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String EnumSet.to_new_string(&set, Allocator* allocator = allocator::heap()) @dynamic
|
fn String EnumSet.to_new_string(&set, Allocator allocator = allocator::heap()) @dynamic
|
||||||
{
|
{
|
||||||
return string::new_format("%s", *set, .allocator = allocator);
|
return string::new_format("%s", *set, .allocator = allocator);
|
||||||
}
|
}
|
||||||
|
|||||||
400
lib/std/collections/hashmap.c3
Normal file
400
lib/std/collections/hashmap.c3
Normal file
@@ -0,0 +1,400 @@
|
|||||||
|
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
|
||||||
|
// Use of this source code is governed by the MIT license
|
||||||
|
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||||
|
module std::collections::map(<Key, Value>);
|
||||||
|
import std::math;
|
||||||
|
|
||||||
|
struct HashMap
|
||||||
|
{
|
||||||
|
Entry*[] table;
|
||||||
|
Allocator allocator;
|
||||||
|
uint count; // Number of elements
|
||||||
|
uint threshold; // Resize limit
|
||||||
|
float load_factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param [&inout] allocator "The allocator to use"
|
||||||
|
* @require capacity > 0 "The capacity must be 1 or higher"
|
||||||
|
* @require load_factor > 0.0 "The load factor must be higher than 0"
|
||||||
|
* @require !self.allocator "Map was already initialized"
|
||||||
|
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||||
|
**/
|
||||||
|
fn HashMap* HashMap.new_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
|
||||||
|
{
|
||||||
|
capacity = math::next_power_of_2(capacity);
|
||||||
|
self.allocator = allocator;
|
||||||
|
self.load_factor = load_factor;
|
||||||
|
self.threshold = (uint)(capacity * load_factor);
|
||||||
|
self.table = allocator::new_array(allocator, Entry*, capacity);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require capacity > 0 "The capacity must be 1 or higher"
|
||||||
|
* @require load_factor > 0.0 "The load factor must be higher than 0"
|
||||||
|
* @require !self.allocator "Map was already initialized"
|
||||||
|
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||||
|
**/
|
||||||
|
fn HashMap* HashMap.temp_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||||
|
{
|
||||||
|
return self.new_init(capacity, load_factor, allocator::temp()) @inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Has this hash map been initialized yet?
|
||||||
|
*
|
||||||
|
* @param [&in] map "The hash map we are testing"
|
||||||
|
* @return "Returns true if it has been initialized, false otherwise"
|
||||||
|
**/
|
||||||
|
fn bool HashMap.is_initialized(&map)
|
||||||
|
{
|
||||||
|
return (bool)map.allocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param [&inout] allocator "The allocator to use"
|
||||||
|
* @param [&in] other_map "The map to copy from."
|
||||||
|
**/
|
||||||
|
fn HashMap* HashMap.new_init_from_map(&self, HashMap* other_map, Allocator allocator = allocator::heap())
|
||||||
|
{
|
||||||
|
self.new_init(other_map.table.len, other_map.load_factor, allocator);
|
||||||
|
self.put_all_for_create(other_map);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param [&in] other_map "The map to copy from."
|
||||||
|
**/
|
||||||
|
fn HashMap* HashMap.temp_init_from_map(&map, HashMap* other_map)
|
||||||
|
{
|
||||||
|
return map.new_init_from_map(other_map, allocator::temp()) @inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bool HashMap.is_empty(&map) @inline
|
||||||
|
{
|
||||||
|
return !map.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usz HashMap.len(&map) @inline
|
||||||
|
{
|
||||||
|
return map.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Value*! HashMap.get_ref(&map, Key key)
|
||||||
|
{
|
||||||
|
if (!map.count) return SearchResult.MISSING?;
|
||||||
|
uint hash = rehash(key.hash());
|
||||||
|
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
|
||||||
|
{
|
||||||
|
if (e.hash == hash && equals(key, e.key)) return &e.value;
|
||||||
|
}
|
||||||
|
return SearchResult.MISSING?;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Entry*! HashMap.get_entry(&map, Key key)
|
||||||
|
{
|
||||||
|
if (!map.count) return SearchResult.MISSING?;
|
||||||
|
uint hash = rehash(key.hash());
|
||||||
|
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
|
||||||
|
{
|
||||||
|
if (e.hash == hash && equals(key, e.key)) return e;
|
||||||
|
}
|
||||||
|
return SearchResult.MISSING?;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value or update and
|
||||||
|
* @require $assignable(#expr, Value)
|
||||||
|
**/
|
||||||
|
macro Value HashMap.@get_or_set(&map, Key key, Value #expr)
|
||||||
|
{
|
||||||
|
if (!map.count)
|
||||||
|
{
|
||||||
|
Value val = #expr;
|
||||||
|
map.set(key, val);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
uint hash = rehash(key.hash());
|
||||||
|
uint index = index_for(hash, map.table.len);
|
||||||
|
for (Entry *e = map.table[index]; e != null; e = e.next)
|
||||||
|
{
|
||||||
|
if (e.hash == hash && equals(key, e.key)) return e.value;
|
||||||
|
}
|
||||||
|
Value val = #expr;
|
||||||
|
map.add_entry(hash, key, val, index);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Value! HashMap.get(&map, Key key) @operator([])
|
||||||
|
{
|
||||||
|
return *map.get_ref(key) @inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bool HashMap.has_key(&map, Key key)
|
||||||
|
{
|
||||||
|
return @ok(map.get_ref(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bool HashMap.set(&map, Key key, Value value) @operator([]=)
|
||||||
|
{
|
||||||
|
// If the map isn't initialized, use the defaults to initialize it.
|
||||||
|
if (!map.allocator)
|
||||||
|
{
|
||||||
|
map.new_init();
|
||||||
|
}
|
||||||
|
uint hash = rehash(key.hash());
|
||||||
|
uint index = index_for(hash, map.table.len);
|
||||||
|
for (Entry *e = map.table[index]; e != null; e = e.next)
|
||||||
|
{
|
||||||
|
if (e.hash == hash && equals(key, e.key))
|
||||||
|
{
|
||||||
|
e.value = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map.add_entry(hash, key, value, index);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void! HashMap.remove(&map, Key key) @maydiscard
|
||||||
|
{
|
||||||
|
if (!map.remove_entry_for_key(key)) return SearchResult.MISSING?;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void HashMap.clear(&map)
|
||||||
|
{
|
||||||
|
if (!map.count) return;
|
||||||
|
foreach (Entry** &entry_ref : map.table)
|
||||||
|
{
|
||||||
|
Entry* entry = *entry_ref;
|
||||||
|
if (!entry) continue;
|
||||||
|
Entry *next = entry.next;
|
||||||
|
while (next)
|
||||||
|
{
|
||||||
|
Entry *to_delete = next;
|
||||||
|
next = next.next;
|
||||||
|
map.free_entry(to_delete);
|
||||||
|
}
|
||||||
|
map.free_entry(entry);
|
||||||
|
*entry_ref = null;
|
||||||
|
}
|
||||||
|
map.count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void HashMap.free(&map)
|
||||||
|
{
|
||||||
|
if (!map.allocator) return;
|
||||||
|
map.clear();
|
||||||
|
map.free_internal(map.table.ptr);
|
||||||
|
map.table = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Key[] HashMap.key_tlist(&map)
|
||||||
|
{
|
||||||
|
return map.key_new_list(allocator::temp()) @inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Key[] HashMap.key_new_list(&map, Allocator allocator = allocator::heap())
|
||||||
|
{
|
||||||
|
if (!map.count) return {};
|
||||||
|
|
||||||
|
Key[] list = allocator::alloc_array(allocator, Key, map.count);
|
||||||
|
usz index = 0;
|
||||||
|
foreach (Entry* entry : map.table)
|
||||||
|
{
|
||||||
|
while (entry)
|
||||||
|
{
|
||||||
|
list[index++] = entry.key;
|
||||||
|
entry = entry.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro HashMap.@each(map; @body(key, value))
|
||||||
|
{
|
||||||
|
map.@each_entry(; Entry* entry) {
|
||||||
|
@body(entry.key, entry.value);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro HashMap.@each_entry(map; @body(entry))
|
||||||
|
{
|
||||||
|
if (map.count)
|
||||||
|
{
|
||||||
|
foreach (Entry* entry : map.table)
|
||||||
|
{
|
||||||
|
while (entry)
|
||||||
|
{
|
||||||
|
@body(entry);
|
||||||
|
entry = entry.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Value[] HashMap.value_tlist(&map)
|
||||||
|
{
|
||||||
|
return map.value_new_list(allocator::temp()) @inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Value[] HashMap.value_new_list(&map, Allocator allocator = allocator::heap())
|
||||||
|
{
|
||||||
|
if (!map.count) return {};
|
||||||
|
Value[] list = allocator::alloc_array(allocator, Value, map.count);
|
||||||
|
usz index = 0;
|
||||||
|
foreach (Entry* entry : map.table)
|
||||||
|
{
|
||||||
|
while (entry)
|
||||||
|
{
|
||||||
|
list[index++] = entry.value;
|
||||||
|
entry = entry.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bool HashMap.has_value(&map, Value v) @if(VALUE_IS_EQUATABLE)
|
||||||
|
{
|
||||||
|
if (!map.count) return false;
|
||||||
|
foreach (Entry* entry : map.table)
|
||||||
|
{
|
||||||
|
while (entry)
|
||||||
|
{
|
||||||
|
if (equals(v, entry.value)) return true;
|
||||||
|
entry = entry.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- private methods
|
||||||
|
|
||||||
|
fn void HashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
|
||||||
|
{
|
||||||
|
$if COPY_KEYS:
|
||||||
|
key = key.copy(map.allocator);
|
||||||
|
$endif
|
||||||
|
Entry* entry = allocator::new(map.allocator, Entry, { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] });
|
||||||
|
map.table[bucket_index] = entry;
|
||||||
|
if (map.count++ >= map.threshold)
|
||||||
|
{
|
||||||
|
map.resize(map.table.len * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void HashMap.resize(&map, uint new_capacity) @private
|
||||||
|
{
|
||||||
|
Entry*[] old_table = map.table;
|
||||||
|
uint old_capacity = old_table.len;
|
||||||
|
if (old_capacity == MAXIMUM_CAPACITY)
|
||||||
|
{
|
||||||
|
map.threshold = uint.max;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Entry*[] new_table = allocator::new_array(map.allocator, Entry*, new_capacity);
|
||||||
|
map.transfer(new_table);
|
||||||
|
map.table = new_table;
|
||||||
|
map.free_internal(old_table.ptr);
|
||||||
|
map.threshold = (uint)(new_capacity * map.load_factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void HashMap.transfer(&map, Entry*[] new_table) @private
|
||||||
|
{
|
||||||
|
Entry*[] src = map.table;
|
||||||
|
uint new_capacity = new_table.len;
|
||||||
|
foreach (uint j, Entry *e : src)
|
||||||
|
{
|
||||||
|
if (!e) continue;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Entry* next = e.next;
|
||||||
|
uint i = index_for(e.hash, new_capacity);
|
||||||
|
e.next = new_table[i];
|
||||||
|
new_table[i] = e;
|
||||||
|
e = next;
|
||||||
|
}
|
||||||
|
while (e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void HashMap.put_for_create(&map, Key key, Value value) @private
|
||||||
|
{
|
||||||
|
uint hash = rehash(key.hash());
|
||||||
|
uint i = index_for(hash, map.table.len);
|
||||||
|
for (Entry *e = map.table[i]; e != null; e = e.next)
|
||||||
|
{
|
||||||
|
if (e.hash == hash && equals(key, e.key))
|
||||||
|
{
|
||||||
|
e.value = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map.create_entry(hash, key, value, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void HashMap.free_internal(&map, void* ptr) @inline @private
|
||||||
|
{
|
||||||
|
allocator::free(map.allocator, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bool HashMap.remove_entry_for_key(&map, Key key) @private
|
||||||
|
{
|
||||||
|
uint hash = rehash(key.hash());
|
||||||
|
uint i = index_for(hash, map.table.len);
|
||||||
|
Entry* prev = map.table[i];
|
||||||
|
Entry* e = prev;
|
||||||
|
while (e)
|
||||||
|
{
|
||||||
|
Entry *next = e.next;
|
||||||
|
if (e.hash == hash && equals(key, e.key))
|
||||||
|
{
|
||||||
|
map.count--;
|
||||||
|
if (prev == e)
|
||||||
|
{
|
||||||
|
map.table[i] = next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prev.next = next;
|
||||||
|
}
|
||||||
|
map.free_entry(e);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
prev = e;
|
||||||
|
e = next;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void HashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private
|
||||||
|
{
|
||||||
|
Entry *e = map.table[bucket_index];
|
||||||
|
$if COPY_KEYS:
|
||||||
|
key = key.copy(map.allocator);
|
||||||
|
$endif
|
||||||
|
Entry* entry = allocator::new(map.allocator, Entry, { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] });
|
||||||
|
map.table[bucket_index] = entry;
|
||||||
|
map.count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void HashMap.free_entry(&self, Entry *entry) @local
|
||||||
|
{
|
||||||
|
$if COPY_KEYS:
|
||||||
|
allocator::free(self.allocator, entry.key);
|
||||||
|
$endif
|
||||||
|
self.free_internal(entry);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -3,6 +3,8 @@
|
|||||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||||
module std::collections::linkedlist(<Type>);
|
module std::collections::linkedlist(<Type>);
|
||||||
|
|
||||||
|
const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type);
|
||||||
|
|
||||||
struct Node @private
|
struct Node @private
|
||||||
{
|
{
|
||||||
Node *next;
|
Node *next;
|
||||||
@@ -12,51 +14,28 @@ struct Node @private
|
|||||||
|
|
||||||
struct LinkedList
|
struct LinkedList
|
||||||
{
|
{
|
||||||
Allocator *allocator;
|
Allocator allocator;
|
||||||
usz size;
|
usz size;
|
||||||
Node *_first;
|
Node *_first;
|
||||||
Node *_last;
|
Node *_last;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void LinkedList.push(&self, Type value)
|
|
||||||
{
|
|
||||||
self.link_first(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn void LinkedList.push_last(&self, Type value)
|
|
||||||
{
|
|
||||||
self.link_last(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
||||||
* @return "the initialized list"
|
* @return "the initialized list"
|
||||||
**/
|
**/
|
||||||
fn LinkedList* LinkedList.new_init(&self, Allocator* allocator = allocator::heap())
|
fn LinkedList* LinkedList.new_init(&self, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
*self = { .allocator = allocator };
|
*self = { .allocator = allocator };
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
|
||||||
* @return "the initialized list"
|
|
||||||
**/
|
|
||||||
fn LinkedList* LinkedList.init_new(&self, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
|
|
||||||
{
|
|
||||||
return self.new_init(allocator);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn LinkedList* LinkedList.temp_init(&self)
|
fn LinkedList* LinkedList.temp_init(&self)
|
||||||
{
|
{
|
||||||
return self.new_init(allocator::temp()) @inline;
|
return self.new_init(allocator::temp()) @inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn LinkedList* LinkedList.init_temp(&self) @deprecated("Replaced by temp_init")
|
|
||||||
{
|
|
||||||
return self.temp_init() @inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require self.allocator
|
* @require self.allocator
|
||||||
**/
|
**/
|
||||||
@@ -71,7 +50,7 @@ macro Node* LinkedList.alloc_node(&self) @private
|
|||||||
return allocator::alloc(self.allocator, Node);
|
return allocator::alloc(self.allocator, Node);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void LinkedList.link_first(&self, Type value) @private
|
fn void LinkedList.push_front(&self, Type value)
|
||||||
{
|
{
|
||||||
Node *first = self._first;
|
Node *first = self._first;
|
||||||
Node *new_node = self.alloc_node();
|
Node *new_node = self.alloc_node();
|
||||||
@@ -88,7 +67,7 @@ fn void LinkedList.link_first(&self, Type value) @private
|
|||||||
self.size++;
|
self.size++;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void LinkedList.link_last(&self, Type value) @private
|
fn void LinkedList.push(&self, Type value)
|
||||||
{
|
{
|
||||||
Node *last = self._last;
|
Node *last = self._last;
|
||||||
Node *new_node = self.alloc_node();
|
Node *new_node = self.alloc_node();
|
||||||
@@ -172,7 +151,7 @@ fn void LinkedList.set(&self, usz index, Type element)
|
|||||||
/**
|
/**
|
||||||
* @require index < self.size
|
* @require index < self.size
|
||||||
**/
|
**/
|
||||||
fn void LinkedList.remove(&self, usz index)
|
fn void LinkedList.remove_at(&self, usz index)
|
||||||
{
|
{
|
||||||
self.unlink(self.node_at_index(index));
|
self.unlink(self.node_at_index(index));
|
||||||
}
|
}
|
||||||
@@ -180,14 +159,14 @@ fn void LinkedList.remove(&self, usz index)
|
|||||||
/**
|
/**
|
||||||
* @require index <= self.size
|
* @require index <= self.size
|
||||||
**/
|
**/
|
||||||
fn void LinkedList.insert(&self, usz index, Type element)
|
fn void LinkedList.insert_at(&self, usz index, Type element)
|
||||||
{
|
{
|
||||||
switch (index)
|
switch (index)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
self.push(element);
|
self.push_front(element);
|
||||||
case self.size:
|
case self.size:
|
||||||
self.push_last(element);
|
self.push(element);
|
||||||
default:
|
default:
|
||||||
self.link_before(self.node_at_index(index), element);
|
self.link_before(self.node_at_index(index), element);
|
||||||
}
|
}
|
||||||
@@ -232,7 +211,53 @@ fn void LinkedList.unlink_first(&self) @private
|
|||||||
self.size--;
|
self.size--;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bool LinkedList.remove_value(&self, Type t)
|
fn usz LinkedList.remove(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||||
|
{
|
||||||
|
usz start = self.size;
|
||||||
|
Node* node = self._first;
|
||||||
|
while (node)
|
||||||
|
{
|
||||||
|
switch
|
||||||
|
{
|
||||||
|
case equals(node.value, t):
|
||||||
|
Node* next = node.next;
|
||||||
|
self.unlink(node);
|
||||||
|
node = next;
|
||||||
|
default:
|
||||||
|
node = node.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return start - self.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Type! LinkedList.pop(&self)
|
||||||
|
{
|
||||||
|
if (!self._last) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
|
defer self.unlink_last();
|
||||||
|
return self._last.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Type! LinkedList.pop_front(&self)
|
||||||
|
{
|
||||||
|
if (!self._first) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
|
defer self.unlink_first();
|
||||||
|
return self._first.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void! LinkedList.remove_last(&self) @maydiscard
|
||||||
|
{
|
||||||
|
if (!self._first) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
|
self.unlink_last();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void! LinkedList.remove_first(&self) @maydiscard
|
||||||
|
{
|
||||||
|
if (!self._first) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
|
self.unlink_first();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn bool LinkedList.remove_first_match(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||||
{
|
{
|
||||||
for (Node* node = self._first; node != null; node = node.next)
|
for (Node* node = self._first; node != null; node = node.next)
|
||||||
{
|
{
|
||||||
@@ -245,7 +270,7 @@ fn bool LinkedList.remove_value(&self, Type t)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bool LinkedList.remove_last_value(&self, Type t)
|
fn bool LinkedList.remove_last_match(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||||
{
|
{
|
||||||
for (Node* node = self._last; node != null; node = node.prev)
|
for (Node* node = self._last; node != null; node = node.prev)
|
||||||
{
|
{
|
||||||
@@ -257,26 +282,6 @@ fn bool LinkedList.remove_last_value(&self, Type t)
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Type! LinkedList.pop(&self)
|
|
||||||
{
|
|
||||||
if (!self._first) return IteratorResult.NO_MORE_ELEMENT?;
|
|
||||||
defer self.unlink_first();
|
|
||||||
return self._first.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn void! LinkedList.remove_last(&self)
|
|
||||||
{
|
|
||||||
if (!self._first) return IteratorResult.NO_MORE_ELEMENT?;
|
|
||||||
self.unlink_last();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn void! LinkedList.remove_first(&self)
|
|
||||||
{
|
|
||||||
if (!self._first) return IteratorResult.NO_MORE_ELEMENT?;
|
|
||||||
self.unlink_first();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require self._last
|
* @require self._last
|
||||||
**/
|
**/
|
||||||
|
|||||||
@@ -2,34 +2,39 @@
|
|||||||
// Use of self source code is governed by the MIT license
|
// Use of self source code is governed by the MIT license
|
||||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||||
module std::collections::list(<Type>);
|
module std::collections::list(<Type>);
|
||||||
import std::io,std::math;
|
import std::io, std::math, std::collections::list_common;
|
||||||
|
|
||||||
def ElementPredicate = fn bool(Type *type);
|
def ElementPredicate = fn bool(Type *type);
|
||||||
def ElementTest = fn bool(Type *type, any* context);
|
def ElementTest = fn bool(Type *type, any context);
|
||||||
const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type);
|
const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type);
|
||||||
const ELEMENT_IS_POINTER = Type.kindof == POINTER;
|
const ELEMENT_IS_POINTER = Type.kindof == POINTER;
|
||||||
|
|
||||||
|
macro type_is_overaligned() => Type.alignof > mem::DEFAULT_MEM_ALIGNMENT;
|
||||||
|
|
||||||
struct List (Printable)
|
struct List (Printable)
|
||||||
{
|
{
|
||||||
usz size;
|
usz size;
|
||||||
usz capacity;
|
usz capacity;
|
||||||
Allocator *allocator;
|
Allocator allocator;
|
||||||
Type *entries;
|
Type *entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param initial_capacity "The initial capacity to reserve"
|
* @param initial_capacity "The initial capacity to reserve"
|
||||||
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
||||||
**/
|
**/
|
||||||
fn List* List.new_init(&self, usz initial_capacity = 16, Allocator* allocator = allocator::heap())
|
fn List* List.new_init(&self, usz initial_capacity = 16, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
self.allocator = allocator;
|
self.allocator = allocator;
|
||||||
self.size = 0;
|
self.size = 0;
|
||||||
if (initial_capacity > 0)
|
if (initial_capacity > 0)
|
||||||
{
|
{
|
||||||
initial_capacity = math::next_power_of_2(initial_capacity);
|
initial_capacity = math::next_power_of_2(initial_capacity);
|
||||||
|
$if type_is_overaligned():
|
||||||
self.entries = allocator::malloc_aligned(allocator, Type.sizeof * initial_capacity, .alignment = Type[1].alignof)!!;
|
self.entries = allocator::malloc_aligned(allocator, Type.sizeof * initial_capacity, .alignment = Type[1].alignof)!!;
|
||||||
|
$else
|
||||||
|
self.entries = allocator::malloc(allocator, Type.sizeof * initial_capacity);
|
||||||
|
$endif
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -39,15 +44,6 @@ fn List* List.new_init(&self, usz initial_capacity = 16, Allocator* allocator =
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param initial_capacity "The initial capacity to reserve"
|
|
||||||
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
|
||||||
**/
|
|
||||||
fn List* List.init_new(&self, usz initial_capacity = 16, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
|
|
||||||
{
|
|
||||||
return self.new_init(initial_capacity, allocator) @inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the list using the temp allocator.
|
* Initialize the list using the temp allocator.
|
||||||
*
|
*
|
||||||
@@ -59,19 +55,35 @@ fn List* List.temp_init(&self, usz initial_capacity = 16)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the list using the temp allocator.
|
* Initialize a new list with an array.
|
||||||
*
|
*
|
||||||
* @param initial_capacity "The initial capacity to reserve"
|
* @param [in] values `The values to initialize the list with.`
|
||||||
|
* @require self.size == 0 "The List must be empty"
|
||||||
**/
|
**/
|
||||||
fn List* List.init_temp(&self, usz initial_capacity = 16) @deprecated("Replaced by temp_init")
|
fn List* List.new_init_with_array(&self, Type[] values, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
return self.temp_init(initial_capacity) @inline;
|
self.new_init(values.len, allocator) @inline;
|
||||||
|
self.add_array(values) @inline;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a temporary list with an array.
|
||||||
|
*
|
||||||
|
* @param [in] values `The values to initialize the list with.`
|
||||||
|
* @require self.size == 0 "The List must be empty"
|
||||||
|
**/
|
||||||
|
fn List* List.temp_init_with_array(&self, Type[] values)
|
||||||
|
{
|
||||||
|
self.temp_init(values.len) @inline;
|
||||||
|
self.add_array(values) @inline;
|
||||||
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require self.size == 0 "The List must be empty"
|
* @require self.size == 0 "The List must be empty"
|
||||||
**/
|
**/
|
||||||
fn void List.init_wrapping_array(&self, Type[] types, Allocator* allocator = allocator::heap())
|
fn void List.init_wrapping_array(&self, Type[] types, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
self.allocator = allocator;
|
self.allocator = allocator;
|
||||||
self.size = types.len;
|
self.size = types.len;
|
||||||
@@ -99,7 +111,7 @@ fn usz! List.to_format(&self, Formatter* formatter) @dynamic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String List.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
|
fn String List.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
|
||||||
{
|
{
|
||||||
return string::new_format("%s", *self, .allocator = allocator);
|
return string::new_format("%s", *self, .allocator = allocator);
|
||||||
}
|
}
|
||||||
@@ -111,20 +123,13 @@ fn String List.to_tstring(&self)
|
|||||||
|
|
||||||
fn void List.push(&self, Type element) @inline
|
fn void List.push(&self, Type element) @inline
|
||||||
{
|
{
|
||||||
self.append(element);
|
self.reserve(1);
|
||||||
}
|
|
||||||
|
|
||||||
fn void List.append(&self, Type element)
|
|
||||||
{
|
|
||||||
self.ensure_capacity();
|
|
||||||
self.entries[self.size++] = element;
|
self.entries[self.size++] = element;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
fn Type! List.pop(&self)
|
||||||
* @require self.size > 0
|
|
||||||
**/
|
|
||||||
fn Type List.pop(&self)
|
|
||||||
{
|
{
|
||||||
|
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
return self.entries[--self.size];
|
return self.entries[--self.size];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,11 +141,11 @@ fn void List.clear(&self)
|
|||||||
/**
|
/**
|
||||||
* @require self.size > 0
|
* @require self.size > 0
|
||||||
**/
|
**/
|
||||||
fn Type List.pop_first(&self)
|
fn Type! List.pop_first(&self)
|
||||||
{
|
{
|
||||||
Type value = self.entries[0];
|
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
self.remove_at(0);
|
defer self.remove_at(0);
|
||||||
return value;
|
return self.entries[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -163,17 +168,29 @@ fn void List.add_all(&self, List* other_list)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn Type[] List.to_new_array(&self, Allocator* allocator = allocator::heap())
|
/**
|
||||||
|
* IMPORTANT The returned array must be freed using free_aligned.
|
||||||
|
**/
|
||||||
|
fn Type[] List.to_new_aligned_array(&self, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
if (!self.size) return Type[] {};
|
return list_common::list_to_new_aligned_array(Type, self, allocator);
|
||||||
Type[] result = allocator::alloc_array(allocator, Type, self.size);
|
}
|
||||||
result[..] = self.entries[:self.size];
|
|
||||||
return result;
|
/**
|
||||||
|
* @require !type_is_overaligned() : "This function is not available on overaligned types"
|
||||||
|
**/
|
||||||
|
macro Type[] List.to_new_array(&self, Allocator allocator = allocator::heap())
|
||||||
|
{
|
||||||
|
return list_common::list_to_new_array(Type, self, allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Type[] List.to_tarray(&self)
|
fn Type[] List.to_tarray(&self)
|
||||||
{
|
{
|
||||||
|
$if type_is_overaligned():
|
||||||
|
return self.to_new_aligned_array(allocator::temp());
|
||||||
|
$else
|
||||||
return self.to_new_array(allocator::temp());
|
return self.to_new_array(allocator::temp());
|
||||||
|
$endif;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -181,13 +198,7 @@ fn Type[] List.to_tarray(&self)
|
|||||||
**/
|
**/
|
||||||
fn void List.reverse(&self)
|
fn void List.reverse(&self)
|
||||||
{
|
{
|
||||||
if (self.size < 2) return;
|
list_common::list_reverse(self);
|
||||||
usz half = self.size / 2U;
|
|
||||||
usz end = self.size - 1;
|
|
||||||
for (usz i = 0; i < half; i++)
|
|
||||||
{
|
|
||||||
@swap(self.entries[i], self.entries[end - i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Type[] List.array_view(&self)
|
fn Type[] List.array_view(&self)
|
||||||
@@ -195,14 +206,18 @@ fn Type[] List.array_view(&self)
|
|||||||
return self.entries[:self.size];
|
return self.entries[:self.size];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the values of an array to this list.
|
||||||
|
*
|
||||||
|
* @param [in] array
|
||||||
|
* @ensure self.size >= array.len
|
||||||
|
**/
|
||||||
fn void List.add_array(&self, Type[] array)
|
fn void List.add_array(&self, Type[] array)
|
||||||
{
|
{
|
||||||
if (!array.len) return;
|
if (!array.len) return;
|
||||||
self.reserve(array.len);
|
self.reserve(array.len);
|
||||||
foreach (&value : array)
|
self.entries[self.size : array.len] = array[..];
|
||||||
{
|
self.size += array.len;
|
||||||
self.entries[self.size++] = *value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void List.push_front(&self, Type type) @inline
|
fn void List.push_front(&self, Type type) @inline
|
||||||
@@ -211,11 +226,11 @@ fn void List.push_front(&self, Type type) @inline
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require index < self.size
|
* @require index <= self.size
|
||||||
**/
|
**/
|
||||||
fn void List.insert_at(&self, usz index, Type type)
|
fn void List.insert_at(&self, usz index, Type type)
|
||||||
{
|
{
|
||||||
self.ensure_capacity();
|
self.reserve(1);
|
||||||
for (usz i = self.size; i > index; i--)
|
for (usz i = self.size; i > index; i--)
|
||||||
{
|
{
|
||||||
self.entries[i] = self.entries[i - 1];
|
self.entries[i] = self.entries[i - 1];
|
||||||
@@ -232,30 +247,28 @@ fn void List.set_at(&self, usz index, Type type)
|
|||||||
self.entries[index] = type;
|
self.entries[index] = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
fn void! List.remove_last(&self) @maydiscard
|
||||||
* @require self.size > 0
|
|
||||||
**/
|
|
||||||
fn void List.remove_last(&self)
|
|
||||||
{
|
{
|
||||||
|
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
self.size--;
|
self.size--;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
fn void! List.remove_first(&self) @maydiscard
|
||||||
* @require self.size > 0
|
|
||||||
**/
|
|
||||||
fn void List.remove_first(&self)
|
|
||||||
{
|
{
|
||||||
|
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
self.remove_at(0);
|
self.remove_at(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Type* List.first(&self)
|
fn Type! List.first(&self)
|
||||||
{
|
{
|
||||||
return self.size ? &self.entries[0] : null;
|
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
|
return self.entries[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Type* List.last(&self)
|
fn Type! List.last(&self)
|
||||||
{
|
{
|
||||||
return self.size ? &self.entries[self.size - 1] : null;
|
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
|
return self.entries[self.size - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bool List.is_empty(&self) @inline
|
fn bool List.is_empty(&self) @inline
|
||||||
@@ -281,7 +294,11 @@ fn Type List.get(&self, usz index) @inline
|
|||||||
fn void List.free(&self)
|
fn void List.free(&self)
|
||||||
{
|
{
|
||||||
if (!self.allocator) return;
|
if (!self.allocator) return;
|
||||||
|
$if type_is_overaligned():
|
||||||
allocator::free_aligned(self.allocator, self.entries);
|
allocator::free_aligned(self.allocator, self.entries);
|
||||||
|
$else
|
||||||
|
allocator::free(self.allocator, self.entries);
|
||||||
|
$endif;
|
||||||
self.capacity = 0;
|
self.capacity = 0;
|
||||||
self.size = 0;
|
self.size = 0;
|
||||||
self.entries = null;
|
self.entries = null;
|
||||||
@@ -298,7 +315,7 @@ fn void List.swap(&self, usz i, usz j)
|
|||||||
**/
|
**/
|
||||||
fn usz List.remove_if(&self, ElementPredicate filter)
|
fn usz List.remove_if(&self, ElementPredicate filter)
|
||||||
{
|
{
|
||||||
return self._remove_if(filter, false);
|
return list_common::list_remove_if(self, filter, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -307,79 +324,30 @@ fn usz List.remove_if(&self, ElementPredicate filter)
|
|||||||
**/
|
**/
|
||||||
fn usz List.retain_if(&self, ElementPredicate selection)
|
fn usz List.retain_if(&self, ElementPredicate selection)
|
||||||
{
|
{
|
||||||
return self._remove_if(selection, true);
|
return list_common::list_remove_if(self, selection, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro usz List._remove_if(&self, ElementPredicate filter, bool $invert) @local
|
fn usz List.remove_using_test(&self, ElementTest filter, any context)
|
||||||
{
|
{
|
||||||
usz size = self.size;
|
return list_common::list_remove_using_test(self, filter, false, context);
|
||||||
for (usz i = size, usz k = size; k > 0; k = i)
|
|
||||||
{
|
|
||||||
// Find last index of item to be deleted.
|
|
||||||
$if $invert:
|
|
||||||
while (i > 0 && !filter(&self.entries[i - 1])) i--;
|
|
||||||
$else
|
|
||||||
while (i > 0 && filter(&self.entries[i - 1])) i--;
|
|
||||||
$endif
|
|
||||||
// Remove the items from this index up to the one not to be deleted.
|
|
||||||
usz n = self.size - k;
|
|
||||||
self.entries[i:n] = self.entries[k:n];
|
|
||||||
self.size -= k - i;
|
|
||||||
// Find last index of item not to be deleted.
|
|
||||||
$if $invert:
|
|
||||||
while (i > 0 && filter(&self.entries[i - 1])) i--;
|
|
||||||
$else
|
|
||||||
while (i > 0 && !filter(&self.entries[i - 1])) i--;
|
|
||||||
$endif
|
|
||||||
}
|
|
||||||
return size - self.size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usz List.remove_using_test(&self, ElementTest filter, any* context)
|
fn usz List.retain_using_test(&self, ElementTest filter, any context)
|
||||||
{
|
{
|
||||||
return self._remove_using_test(filter, false, context);
|
return list_common::list_remove_using_test(self, filter, true, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usz List.retain_using_test(&self, ElementTest filter, any* context)
|
fn void List.ensure_capacity(&self, usz min_capacity) @local
|
||||||
{
|
|
||||||
return self._remove_using_test(filter, true, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
macro usz List._remove_using_test(&self, ElementTest filter, bool $invert, ctx) @local
|
|
||||||
{
|
|
||||||
usz size = self.size;
|
|
||||||
for (usz i = size, usz k = size; k > 0; k = i)
|
|
||||||
{
|
|
||||||
// Find last index of item to be deleted.
|
|
||||||
$if $invert:
|
|
||||||
while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--;
|
|
||||||
$else
|
|
||||||
while (i > 0 && filter(&self.entries[i - 1], ctx)) i--;
|
|
||||||
$endif
|
|
||||||
// Remove the items from this index up to the one not to be deleted.
|
|
||||||
usz n = self.size - k;
|
|
||||||
self.entries[i:n] = self.entries[k:n];
|
|
||||||
self.size -= k - i;
|
|
||||||
// Find last index of item not to be deleted.
|
|
||||||
$if $invert:
|
|
||||||
while (i > 0 && filter(&self.entries[i - 1], ctx)) i--;
|
|
||||||
$else
|
|
||||||
while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--;
|
|
||||||
$endif
|
|
||||||
}
|
|
||||||
return size - self.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reserve at least min_capacity
|
|
||||||
**/
|
|
||||||
fn void List.reserve(&self, usz min_capacity)
|
|
||||||
{
|
{
|
||||||
if (!min_capacity) return;
|
if (!min_capacity) return;
|
||||||
if (self.capacity >= min_capacity) return;
|
if (self.capacity >= min_capacity) return;
|
||||||
if (!self.allocator) self.allocator = allocator::heap();
|
if (!self.allocator) self.allocator = allocator::heap();
|
||||||
min_capacity = math::next_power_of_2(min_capacity);
|
min_capacity = math::next_power_of_2(min_capacity);
|
||||||
self.entries = allocator::realloc_aligned(self.allocator, self.entries, Type.sizeof * min_capacity, .alignment = Type[1].alignof) ?? null;
|
$if type_is_overaligned():
|
||||||
|
self.entries = allocator::realloc_aligned(self.allocator, self.entries, Type.sizeof * min_capacity, .alignment = Type[1].alignof)!!;
|
||||||
|
$else
|
||||||
|
self.entries = allocator::realloc(self.allocator, self.entries, Type.sizeof * min_capacity);
|
||||||
|
$endif;
|
||||||
self.capacity = min_capacity;
|
self.capacity = min_capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,7 +366,7 @@ fn void List.set(&self, usz index, Type value) @operator([]=)
|
|||||||
self.entries[index] = value;
|
self.entries[index] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void List.ensure_capacity(&self, usz added = 1) @inline @private
|
fn void List.reserve(&self, usz added)
|
||||||
{
|
{
|
||||||
usz new_size = self.size + added;
|
usz new_size = self.size + added;
|
||||||
if (self.capacity >= new_size) return;
|
if (self.capacity >= new_size) return;
|
||||||
@@ -406,7 +374,7 @@ fn void List.ensure_capacity(&self, usz added = 1) @inline @private
|
|||||||
assert(new_size < usz.max / 2U);
|
assert(new_size < usz.max / 2U);
|
||||||
usz new_capacity = self.capacity ? 2U * self.capacity : 16U;
|
usz new_capacity = self.capacity ? 2U * self.capacity : 16U;
|
||||||
while (new_capacity < new_size) new_capacity *= 2U;
|
while (new_capacity < new_size) new_capacity *= 2U;
|
||||||
self.reserve(new_capacity);
|
self.ensure_capacity(new_capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Functions for equatable types
|
// Functions for equatable types
|
||||||
@@ -456,31 +424,42 @@ fn bool List.contains(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param [&inout] self "The list to remove elements from"
|
||||||
|
* @param value "The value to remove"
|
||||||
|
* @return "true if the value was found"
|
||||||
|
**/
|
||||||
|
fn bool List.remove_last_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
|
||||||
|
{
|
||||||
|
return @ok(self.remove_at(self.rindex_of(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param [&inout] self "The list to remove elements from"
|
||||||
|
* @param value "The value to remove"
|
||||||
|
* @return "true if the value was found"
|
||||||
|
**/
|
||||||
|
fn bool List.remove_first_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
|
||||||
|
{
|
||||||
|
return @ok(self.remove_at(self.index_of(value)));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param [&inout] self "The list to remove elements from"
|
* @param [&inout] self "The list to remove elements from"
|
||||||
* @param value "The value to remove"
|
* @param value "The value to remove"
|
||||||
* @return "the number of deleted elements."
|
* @return "the number of deleted elements."
|
||||||
**/
|
**/
|
||||||
fn usz List.remove(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
|
fn usz List.remove_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
|
||||||
{
|
{
|
||||||
usz size = self.size;
|
return list_common::list_remove_item(self, value);
|
||||||
for (usz i = size; i > 0; i--)
|
|
||||||
{
|
|
||||||
if (!equals(self.entries[i - 1], value)) continue;
|
|
||||||
for (usz j = i; j < size; j++)
|
|
||||||
{
|
|
||||||
self.entries[j - 1] = self.entries[j];
|
|
||||||
}
|
|
||||||
self.size--;
|
|
||||||
}
|
|
||||||
return size - self.size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void List.remove_all(&self, List* other_list) @if(ELEMENT_IS_EQUATABLE)
|
|
||||||
|
|
||||||
|
fn void List.remove_all_from(&self, List* other_list) @if(ELEMENT_IS_EQUATABLE)
|
||||||
{
|
{
|
||||||
if (!other_list.size) return;
|
if (!other_list.size) return;
|
||||||
foreach (v : other_list) self.remove(v);
|
foreach (v : other_list) self.remove_item(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -496,15 +475,38 @@ fn usz List.compact_count(&self) @if(ELEMENT_IS_POINTER)
|
|||||||
|
|
||||||
fn usz List.compact(&self) @if(ELEMENT_IS_POINTER)
|
fn usz List.compact(&self) @if(ELEMENT_IS_POINTER)
|
||||||
{
|
{
|
||||||
usz size = self.size;
|
return list_common::list_compact(self);
|
||||||
for (usz i = size; i > 0; i--)
|
}
|
||||||
{
|
|
||||||
if (self.entries[i - 1]) continue;
|
// --> Deprecated
|
||||||
for (usz j = i; j < size; j++)
|
|
||||||
{
|
/**
|
||||||
self.entries[j - 1] = self.entries[j];
|
* @param [&inout] self "The list to remove elements from"
|
||||||
}
|
* @param value "The value to remove"
|
||||||
self.size--;
|
* @return "true if the value was found"
|
||||||
}
|
**/
|
||||||
return size - self.size;
|
fn bool List.remove_last_match(&self, Type value) @if(ELEMENT_IS_EQUATABLE) @deprecated
|
||||||
|
{
|
||||||
|
return self.remove_last_item(value) @inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param [&inout] self "The list to remove elements from"
|
||||||
|
* @param value "The value to remove"
|
||||||
|
* @return "true if the value was found"
|
||||||
|
**/
|
||||||
|
fn bool List.remove_first_match(&self, Type value) @if(ELEMENT_IS_EQUATABLE) @deprecated
|
||||||
|
{
|
||||||
|
return self.remove_first_item(value) @inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param [&inout] self "The list to remove elements from"
|
||||||
|
* @param value "The value to remove"
|
||||||
|
* @return "the number of deleted elements."
|
||||||
|
**/
|
||||||
|
fn usz List.remove_all_matches(&self, Type value) @if(ELEMENT_IS_EQUATABLE) @deprecated
|
||||||
|
{
|
||||||
|
return self.remove_item(value) @inline;
|
||||||
}
|
}
|
||||||
|
|||||||
112
lib/std/collections/list_common.c3
Normal file
112
lib/std/collections/list_common.c3
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
module std::collections::list_common;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IMPORTANT The returned array must be freed using free_aligned.
|
||||||
|
**/
|
||||||
|
macro list_to_new_aligned_array($Type, self, Allocator allocator)
|
||||||
|
{
|
||||||
|
if (!self.size) return $Type[] {};
|
||||||
|
$Type[] result = allocator::alloc_array_aligned(allocator, $Type, self.size);
|
||||||
|
result[..] = self.entries[:self.size];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro list_to_new_array($Type, self, Allocator allocator)
|
||||||
|
{
|
||||||
|
if (!self.size) return $Type[] {};
|
||||||
|
$Type[] result = allocator::alloc_array(allocator, $Type, self.size);
|
||||||
|
result[..] = self.entries[:self.size];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro void list_reverse(self)
|
||||||
|
{
|
||||||
|
if (self.size < 2) return;
|
||||||
|
usz half = self.size / 2U;
|
||||||
|
usz end = self.size - 1;
|
||||||
|
for (usz i = 0; i < half; i++)
|
||||||
|
{
|
||||||
|
@swap(self.entries[i], self.entries[end - i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro usz list_remove_using_test(self, filter, bool $invert, ctx)
|
||||||
|
{
|
||||||
|
usz size = self.size;
|
||||||
|
for (usz i = size, usz k = size; k > 0; k = i)
|
||||||
|
{
|
||||||
|
// Find last index of item to be deleted.
|
||||||
|
$if $invert:
|
||||||
|
while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--;
|
||||||
|
$else
|
||||||
|
while (i > 0 && filter(&self.entries[i - 1], ctx)) i--;
|
||||||
|
$endif
|
||||||
|
// Remove the items from this index up to the one not to be deleted.
|
||||||
|
usz n = self.size - k;
|
||||||
|
self.entries[i:n] = self.entries[k:n];
|
||||||
|
self.size -= k - i;
|
||||||
|
// Find last index of item not to be deleted.
|
||||||
|
$if $invert:
|
||||||
|
while (i > 0 && filter(&self.entries[i - 1], ctx)) i--;
|
||||||
|
$else
|
||||||
|
while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--;
|
||||||
|
$endif
|
||||||
|
}
|
||||||
|
return size - self.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro usz list_compact(self)
|
||||||
|
{
|
||||||
|
usz size = self.size;
|
||||||
|
for (usz i = size; i > 0; i--)
|
||||||
|
{
|
||||||
|
if (self.entries[i - 1]) continue;
|
||||||
|
for (usz j = i; j < size; j++)
|
||||||
|
{
|
||||||
|
self.entries[j - 1] = self.entries[j];
|
||||||
|
}
|
||||||
|
self.size--;
|
||||||
|
}
|
||||||
|
return size - self.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro usz list_remove_item(self, value)
|
||||||
|
{
|
||||||
|
usz size = self.size;
|
||||||
|
for (usz i = size; i > 0; i--)
|
||||||
|
{
|
||||||
|
if (!equals(self.entries[i - 1], value)) continue;
|
||||||
|
for (usz j = i; j < self.size; j++)
|
||||||
|
{
|
||||||
|
self.entries[j - 1] = self.entries[j];
|
||||||
|
}
|
||||||
|
self.size--;
|
||||||
|
}
|
||||||
|
return size - self.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
macro usz list_remove_if(self, filter, bool $invert)
|
||||||
|
{
|
||||||
|
usz size = self.size;
|
||||||
|
for (usz i = size, usz k = size; k > 0; k = i)
|
||||||
|
{
|
||||||
|
// Find last index of item to be deleted.
|
||||||
|
$if $invert:
|
||||||
|
while (i > 0 && !filter(&self.entries[i - 1])) i--;
|
||||||
|
$else
|
||||||
|
while (i > 0 && filter(&self.entries[i - 1])) i--;
|
||||||
|
$endif
|
||||||
|
// Remove the items from this index up to the one not to be deleted.
|
||||||
|
usz n = self.size - k;
|
||||||
|
self.entries[i:n] = self.entries[k:n];
|
||||||
|
self.size -= k - i;
|
||||||
|
// Find last index of item not to be deleted.
|
||||||
|
$if $invert:
|
||||||
|
while (i > 0 && filter(&self.entries[i - 1])) i--;
|
||||||
|
$else
|
||||||
|
while (i > 0 && !filter(&self.entries[i - 1])) i--;
|
||||||
|
$endif
|
||||||
|
}
|
||||||
|
return size - self.size;
|
||||||
|
}
|
||||||
@@ -10,128 +10,84 @@ const float DEFAULT_LOAD_FACTOR = 0.75;
|
|||||||
const VALUE_IS_EQUATABLE = Value.is_eq;
|
const VALUE_IS_EQUATABLE = Value.is_eq;
|
||||||
const bool COPY_KEYS = types::implements_copy(Key);
|
const bool COPY_KEYS = types::implements_copy(Key);
|
||||||
|
|
||||||
struct HashMap
|
distinct Map = void*;
|
||||||
|
|
||||||
|
struct MapImpl
|
||||||
{
|
{
|
||||||
Entry*[] table;
|
Entry*[] table;
|
||||||
Allocator* allocator;
|
Allocator allocator;
|
||||||
uint count; // Number of elements
|
uint count; // Number of elements
|
||||||
uint threshold; // Resize limit
|
uint threshold; // Resize limit
|
||||||
float load_factor;
|
float load_factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param [&inout] allocator "The allocator to use"
|
|
||||||
* @require capacity > 0 "The capacity must be 1 or higher"
|
* @require capacity > 0 "The capacity must be 1 or higher"
|
||||||
* @require load_factor > 0.0 "The load factor must be higher than 0"
|
* @require load_factor > 0.0 "The load factor must be higher than 0"
|
||||||
* @require !self.allocator "Map was already initialized"
|
|
||||||
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||||
**/
|
**/
|
||||||
fn HashMap* HashMap.new_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator* allocator = allocator::heap())
|
fn Map new(uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
capacity = math::next_power_of_2(capacity);
|
MapImpl* map = allocator::alloc(allocator, MapImpl);
|
||||||
self.allocator = allocator;
|
_init(map, capacity, load_factor, allocator);
|
||||||
self.load_factor = load_factor;
|
return (Map)map;
|
||||||
self.threshold = (uint)(capacity * load_factor);
|
|
||||||
self.table = allocator::new_array(allocator, Entry*, capacity);
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param [&inout] allocator "The allocator to use"
|
|
||||||
* @require capacity > 0 "The capacity must be 1 or higher"
|
|
||||||
* @require load_factor > 0.0 "The load factor must be higher than 0"
|
|
||||||
* @require !map.allocator "Map was already initialized"
|
|
||||||
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
|
||||||
**/
|
|
||||||
fn HashMap* HashMap.init_new(&map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
|
|
||||||
{
|
|
||||||
return map.new_init(capacity, load_factor, allocator);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require capacity > 0 "The capacity must be 1 or higher"
|
* @require capacity > 0 "The capacity must be 1 or higher"
|
||||||
* @require load_factor > 0.0 "The load factor must be higher than 0"
|
* @require load_factor > 0.0 "The load factor must be higher than 0"
|
||||||
* @require !self.allocator "Map was already initialized"
|
|
||||||
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||||
**/
|
**/
|
||||||
fn HashMap* HashMap.temp_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
fn Map temp(uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||||
{
|
{
|
||||||
return self.new_init(capacity, load_factor, allocator::temp()) @inline;
|
MapImpl* map = mem::temp_alloc(MapImpl);
|
||||||
}
|
_init(map, capacity, load_factor, allocator::temp());
|
||||||
|
return (Map)map;
|
||||||
/**
|
|
||||||
* @require capacity > 0 "The capacity must be 1 or higher"
|
|
||||||
* @require load_factor > 0.0 "The load factor must be higher than 0"
|
|
||||||
* @require !map.allocator "Map was already initialized"
|
|
||||||
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
|
||||||
**/
|
|
||||||
fn HashMap* HashMap.init_temp(&map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) @deprecated("Replaced by temp_init")
|
|
||||||
{
|
|
||||||
return map.temp_init(capacity, load_factor) @inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Has this hash map been initialized yet?
|
|
||||||
*
|
|
||||||
* @param [&in] map "The hash map we are testing"
|
|
||||||
* @return "Returns true if it has been initialized, false otherwise"
|
|
||||||
**/
|
|
||||||
fn bool HashMap.is_initialized(&map)
|
|
||||||
{
|
|
||||||
return (bool)map.allocator;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param [&inout] allocator "The allocator to use"
|
|
||||||
* @param [&in] other_map "The map to copy from."
|
|
||||||
**/
|
|
||||||
fn HashMap* HashMap.new_init_from_map(&self, HashMap* other_map, Allocator* allocator = allocator::heap())
|
|
||||||
{
|
|
||||||
self.new_init(other_map.table.len, other_map.load_factor, allocator);
|
|
||||||
self.put_all_for_create(other_map);
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param [&inout] allocator "The allocator to use"
|
|
||||||
* @param [&in] other_map "The map to copy from."
|
|
||||||
**/
|
|
||||||
fn HashMap* HashMap.init_new_from_map(&self, HashMap* other_map, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init_from_map")
|
|
||||||
|
|
||||||
{
|
|
||||||
return self.new_init_from_map(other_map, allocator) @inline;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param [&in] other_map "The map to copy from."
|
* @param [&in] other_map "The map to copy from."
|
||||||
**/
|
**/
|
||||||
fn HashMap* HashMap.temp_init_from_map(&map, HashMap* other_map)
|
fn Map new_from_map(Map other_map, Allocator allocator = null)
|
||||||
{
|
{
|
||||||
return map.new_init_from_map(other_map, allocator::temp()) @inline;
|
MapImpl* other_map_impl = (MapImpl*)other_map;
|
||||||
|
if (!other_map_impl)
|
||||||
|
{
|
||||||
|
if (allocator) return new(.allocator = allocator);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
MapImpl* map = (MapImpl*)new(other_map_impl.table.len, other_map_impl.load_factor, allocator ?: allocator::heap());
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
return (Map)map;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param [&in] other_map "The map to copy from."
|
* @param [&in] other_map "The map to copy from."
|
||||||
**/
|
**/
|
||||||
fn HashMap* HashMap.init_temp_from_map(&map, HashMap* other_map) @deprecated("Replaced by temp_init_from_map")
|
fn Map temp_from_map(Map other_map)
|
||||||
{
|
{
|
||||||
return map.temp_init_from_map(other_map) @inline;
|
return new_from_map(other_map, allocator::temp());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bool Map.is_empty(map) @inline
|
||||||
fn bool HashMap.is_empty(&map) @inline
|
|
||||||
{
|
{
|
||||||
return !map.count;
|
return !map || !((MapImpl*)map).count;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usz HashMap.len(&map) @inline
|
fn usz Map.len(map) @inline
|
||||||
{
|
{
|
||||||
return map.count;
|
return map ? ((MapImpl*)map).count : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Value*! HashMap.get_ref(&map, Key key)
|
fn Value*! Map.get_ref(self, Key key)
|
||||||
{
|
{
|
||||||
if (!map.count) return SearchResult.MISSING?;
|
MapImpl *map = (MapImpl*)self;
|
||||||
|
if (!map || !map.count) return SearchResult.MISSING?;
|
||||||
uint hash = rehash(key.hash());
|
uint hash = rehash(key.hash());
|
||||||
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
|
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
|
||||||
{
|
{
|
||||||
@@ -140,11 +96,12 @@ fn Value*! HashMap.get_ref(&map, Key key)
|
|||||||
return SearchResult.MISSING?;
|
return SearchResult.MISSING?;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Entry*! HashMap.get_entry(&map, Key key)
|
fn Entry*! Map.get_entry(map, Key key)
|
||||||
{
|
{
|
||||||
if (!map.count) return SearchResult.MISSING?;
|
MapImpl *map_impl = (MapImpl*)map;
|
||||||
|
if (!map_impl || !map_impl.count) return SearchResult.MISSING?;
|
||||||
uint hash = rehash(key.hash());
|
uint hash = rehash(key.hash());
|
||||||
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
|
for (Entry *e = map_impl.table[index_for(hash, map_impl.table.len)]; e != null; e = e.next)
|
||||||
{
|
{
|
||||||
if (e.hash == hash && equals(key, e.key)) return e;
|
if (e.hash == hash && equals(key, e.key)) return e;
|
||||||
}
|
}
|
||||||
@@ -155,9 +112,10 @@ fn Entry*! HashMap.get_entry(&map, Key key)
|
|||||||
* Get the value or update and
|
* Get the value or update and
|
||||||
* @require $assignable(#expr, Value)
|
* @require $assignable(#expr, Value)
|
||||||
**/
|
**/
|
||||||
macro Value HashMap.@get_or_set(&map, Key key, Value #expr)
|
macro Value Map.@get_or_set(&self, Key key, Value #expr)
|
||||||
{
|
{
|
||||||
if (!map.count)
|
MapImpl *map = (MapImpl*)*self;
|
||||||
|
if (!map || !map.count)
|
||||||
{
|
{
|
||||||
Value val = #expr;
|
Value val = #expr;
|
||||||
map.set(key, val);
|
map.set(key, val);
|
||||||
@@ -174,23 +132,27 @@ macro Value HashMap.@get_or_set(&map, Key key, Value #expr)
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Value! HashMap.get(&map, Key key) @operator([])
|
fn Value! Map.get(map, Key key) @operator([])
|
||||||
{
|
{
|
||||||
return *map.get_ref(key) @inline;
|
return *map.get_ref(key) @inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bool HashMap.has_key(&map, Key key)
|
fn bool Map.has_key(map, Key key)
|
||||||
{
|
{
|
||||||
return @ok(map.get_ref(key));
|
return @ok(map.get_ref(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bool HashMap.set(&map, Key key, Value value) @operator([]=)
|
macro Value Map.set_value_return(&map, Key key, Value value) @operator([]=)
|
||||||
|
{
|
||||||
|
map.set(key, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bool Map.set(&self, Key key, Value value)
|
||||||
{
|
{
|
||||||
// If the map isn't initialized, use the defaults to initialize it.
|
// If the map isn't initialized, use the defaults to initialize it.
|
||||||
if (!map.allocator)
|
if (!*self) *self = new();
|
||||||
{
|
MapImpl* map = (MapImpl*)*self;
|
||||||
map.new_init();
|
|
||||||
}
|
|
||||||
uint hash = rehash(key.hash());
|
uint hash = rehash(key.hash());
|
||||||
uint index = index_for(hash, map.table.len);
|
uint index = index_for(hash, map.table.len);
|
||||||
for (Entry *e = map.table[index]; e != null; e = e.next)
|
for (Entry *e = map.table[index]; e != null; e = e.next)
|
||||||
@@ -201,18 +163,19 @@ fn bool HashMap.set(&map, Key key, Value value) @operator([]=)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
map.add_entry(hash, key, value, index);
|
map._add_entry(hash, key, value, index);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! HashMap.remove(&map, Key key) @maydiscard
|
fn void! Map.remove(map, Key key) @maydiscard
|
||||||
{
|
{
|
||||||
if (!map.remove_entry_for_key(key)) return SearchResult.MISSING?;
|
if (!map || !((MapImpl*)map)._remove_entry_for_key(key)) return SearchResult.MISSING?;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void HashMap.clear(&map)
|
fn void Map.clear(self)
|
||||||
{
|
{
|
||||||
if (!map.count) return;
|
MapImpl* map = (MapImpl*)self;
|
||||||
|
if (!map || !map.count) return;
|
||||||
foreach (Entry** &entry_ref : map.table)
|
foreach (Entry** &entry_ref : map.table)
|
||||||
{
|
{
|
||||||
Entry* entry = *entry_ref;
|
Entry* entry = *entry_ref;
|
||||||
@@ -222,30 +185,33 @@ fn void HashMap.clear(&map)
|
|||||||
{
|
{
|
||||||
Entry *to_delete = next;
|
Entry *to_delete = next;
|
||||||
next = next.next;
|
next = next.next;
|
||||||
map.free_entry(to_delete);
|
map._free_entry(to_delete);
|
||||||
}
|
}
|
||||||
map.free_entry(entry);
|
map._free_entry(entry);
|
||||||
*entry_ref = null;
|
*entry_ref = null;
|
||||||
}
|
}
|
||||||
map.count = 0;
|
map.count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void HashMap.free(&map)
|
fn void Map.free(self)
|
||||||
{
|
{
|
||||||
if (!map.allocator) return;
|
if (!self) return;
|
||||||
map.clear();
|
MapImpl* map = (MapImpl*)self;
|
||||||
map.free_internal(map.table.ptr);
|
self.clear();
|
||||||
|
map._free_internal(map.table.ptr);
|
||||||
map.table = {};
|
map.table = {};
|
||||||
|
allocator::free(map.allocator, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Key[] HashMap.key_tlist(&map)
|
fn Key[] Map.temp_keys_list(map)
|
||||||
{
|
{
|
||||||
return map.key_new_list(allocator::temp()) @inline;
|
return map.new_keys_list(allocator::temp()) @inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Key[] HashMap.key_new_list(&map, Allocator* allocator = allocator::heap())
|
fn Key[] Map.new_keys_list(self, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
if (!map.count) return {};
|
MapImpl* map = (MapImpl*)self;
|
||||||
|
if (!map || !map.count) return {};
|
||||||
|
|
||||||
Key[] list = allocator::alloc_array(allocator, Key, map.count);
|
Key[] list = allocator::alloc_array(allocator, Key, map.count);
|
||||||
usz index = 0;
|
usz index = 0;
|
||||||
@@ -260,36 +226,36 @@ fn Key[] HashMap.key_new_list(&map, Allocator* allocator = allocator::heap())
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro HashMap.@each(map; @body(key, value))
|
macro Map.@each(map; @body(key, value))
|
||||||
{
|
{
|
||||||
map.@each_entry(; Entry* entry) {
|
map.@each_entry(; Entry* entry) {
|
||||||
@body(entry.key, entry.value);
|
@body(entry.key, entry.value);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro HashMap.@each_entry(map; @body(entry))
|
macro Map.@each_entry(self; @body(entry))
|
||||||
{
|
{
|
||||||
if (map.count)
|
MapImpl *map = (MapImpl*)self;
|
||||||
|
if (!map || !map.count) return;
|
||||||
|
foreach (Entry* entry : map.table)
|
||||||
{
|
{
|
||||||
foreach (Entry* entry : map.table)
|
while (entry)
|
||||||
{
|
{
|
||||||
while (entry)
|
@body(entry);
|
||||||
{
|
entry = entry.next;
|
||||||
@body(entry);
|
|
||||||
entry = entry.next;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Value[] HashMap.value_tlist(&map)
|
fn Value[] Map.temp_values_list(map)
|
||||||
{
|
{
|
||||||
return map.value_new_list(allocator::temp()) @inline;
|
return map.new_values_list(allocator::temp()) @inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Value[] HashMap.value_new_list(&map, Allocator* allocator = allocator::heap())
|
fn Value[] Map.new_values_list(self, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
if (!map.count) return {};
|
MapImpl* map = (MapImpl*)self;
|
||||||
|
if (!map || !map.count) return {};
|
||||||
Value[] list = allocator::alloc_array(allocator, Value, map.count);
|
Value[] list = allocator::alloc_array(allocator, Value, map.count);
|
||||||
usz index = 0;
|
usz index = 0;
|
||||||
foreach (Entry* entry : map.table)
|
foreach (Entry* entry : map.table)
|
||||||
@@ -303,9 +269,10 @@ fn Value[] HashMap.value_new_list(&map, Allocator* allocator = allocator::heap()
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bool HashMap.has_value(&map, Value v) @if(VALUE_IS_EQUATABLE)
|
fn bool Map.has_value(self, Value v) @if(VALUE_IS_EQUATABLE)
|
||||||
{
|
{
|
||||||
if (!map.count) return false;
|
MapImpl* map = (MapImpl*)self;
|
||||||
|
if (!map || !map.count) return false;
|
||||||
foreach (Entry* entry : map.table)
|
foreach (Entry* entry : map.table)
|
||||||
{
|
{
|
||||||
while (entry)
|
while (entry)
|
||||||
@@ -319,7 +286,7 @@ fn bool HashMap.has_value(&map, Value v) @if(VALUE_IS_EQUATABLE)
|
|||||||
|
|
||||||
// --- private methods
|
// --- private methods
|
||||||
|
|
||||||
fn void HashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
|
fn void MapImpl._add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
|
||||||
{
|
{
|
||||||
$if COPY_KEYS:
|
$if COPY_KEYS:
|
||||||
key = key.copy(map.allocator);
|
key = key.copy(map.allocator);
|
||||||
@@ -328,11 +295,11 @@ fn void HashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_ind
|
|||||||
map.table[bucket_index] = entry;
|
map.table[bucket_index] = entry;
|
||||||
if (map.count++ >= map.threshold)
|
if (map.count++ >= map.threshold)
|
||||||
{
|
{
|
||||||
map.resize(map.table.len * 2);
|
map._resize(map.table.len * 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void HashMap.resize(&map, uint new_capacity) @private
|
fn void MapImpl._resize(&map, uint new_capacity) @private
|
||||||
{
|
{
|
||||||
Entry*[] old_table = map.table;
|
Entry*[] old_table = map.table;
|
||||||
uint old_capacity = old_table.len;
|
uint old_capacity = old_table.len;
|
||||||
@@ -342,9 +309,9 @@ fn void HashMap.resize(&map, uint new_capacity) @private
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Entry*[] new_table = allocator::new_array(map.allocator, Entry*, new_capacity);
|
Entry*[] new_table = allocator::new_array(map.allocator, Entry*, new_capacity);
|
||||||
map.transfer(new_table);
|
map._transfer(new_table);
|
||||||
map.table = new_table;
|
map.table = new_table;
|
||||||
map.free_internal(old_table.ptr);
|
map._free_internal(old_table.ptr);
|
||||||
map.threshold = (uint)(new_capacity * map.load_factor);
|
map.threshold = (uint)(new_capacity * map.load_factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,7 +326,7 @@ macro uint index_for(uint hash, uint capacity) @private
|
|||||||
return hash & (capacity - 1);
|
return hash & (capacity - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void HashMap.transfer(&map, Entry*[] new_table) @private
|
fn void MapImpl._transfer(&map, Entry*[] new_table) @private
|
||||||
{
|
{
|
||||||
Entry*[] src = map.table;
|
Entry*[] src = map.table;
|
||||||
uint new_capacity = new_table.len;
|
uint new_capacity = new_table.len;
|
||||||
@@ -378,17 +345,18 @@ fn void HashMap.transfer(&map, Entry*[] new_table) @private
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void HashMap.put_all_for_create(&map, HashMap* other_map) @private
|
fn void _init(MapImpl* impl, uint capacity, float load_factor, Allocator allocator) @private
|
||||||
{
|
{
|
||||||
if (!other_map.count) return;
|
capacity = math::next_power_of_2(capacity);
|
||||||
foreach (Entry *e : other_map.table)
|
*impl = {
|
||||||
{
|
.allocator = allocator,
|
||||||
if (!e) continue;
|
.load_factor = load_factor,
|
||||||
map.put_for_create(e.key, e.value);
|
.threshold = (uint)(capacity * load_factor),
|
||||||
}
|
.table = allocator::new_array(allocator, Entry*, capacity)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void HashMap.put_for_create(&map, Key key, Value value) @private
|
fn void MapImpl._put_for_create(&map, Key key, Value value) @private
|
||||||
{
|
{
|
||||||
uint hash = rehash(key.hash());
|
uint hash = rehash(key.hash());
|
||||||
uint i = index_for(hash, map.table.len);
|
uint i = index_for(hash, map.table.len);
|
||||||
@@ -400,16 +368,17 @@ fn void HashMap.put_for_create(&map, Key key, Value value) @private
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
map.create_entry(hash, key, value, i);
|
map._create_entry(hash, key, value, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void HashMap.free_internal(&map, void* ptr) @inline @private
|
fn void MapImpl._free_internal(&map, void* ptr) @inline @private
|
||||||
{
|
{
|
||||||
allocator::free(map.allocator, ptr);
|
allocator::free(map.allocator, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bool HashMap.remove_entry_for_key(&map, Key key) @private
|
fn bool MapImpl._remove_entry_for_key(&map, Key key) @private
|
||||||
{
|
{
|
||||||
|
if (!map.count) return false;
|
||||||
uint hash = rehash(key.hash());
|
uint hash = rehash(key.hash());
|
||||||
uint i = index_for(hash, map.table.len);
|
uint i = index_for(hash, map.table.len);
|
||||||
Entry* prev = map.table[i];
|
Entry* prev = map.table[i];
|
||||||
@@ -428,7 +397,7 @@ fn bool HashMap.remove_entry_for_key(&map, Key key) @private
|
|||||||
{
|
{
|
||||||
prev.next = next;
|
prev.next = next;
|
||||||
}
|
}
|
||||||
map.free_entry(e);
|
map._free_entry(e);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
prev = e;
|
prev = e;
|
||||||
@@ -437,7 +406,7 @@ fn bool HashMap.remove_entry_for_key(&map, Key key) @private
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void HashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private
|
fn void MapImpl._create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private
|
||||||
{
|
{
|
||||||
Entry *e = map.table[bucket_index];
|
Entry *e = map.table[bucket_index];
|
||||||
$if COPY_KEYS:
|
$if COPY_KEYS:
|
||||||
@@ -448,12 +417,12 @@ fn void HashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_i
|
|||||||
map.count++;
|
map.count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void HashMap.free_entry(&self, Entry *entry) @local
|
fn void MapImpl._free_entry(&self, Entry *entry) @local
|
||||||
{
|
{
|
||||||
$if COPY_KEYS:
|
$if COPY_KEYS:
|
||||||
allocator::free(self.allocator, entry.key);
|
allocator::free(self.allocator, entry.key);
|
||||||
$endif
|
$endif
|
||||||
self.free_internal(entry);
|
self._free_internal(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Entry
|
struct Entry
|
||||||
@@ -462,4 +431,4 @@ struct Entry
|
|||||||
Key key;
|
Key key;
|
||||||
Value value;
|
Value value;
|
||||||
Entry* next;
|
Entry* next;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const Object NULL_OBJECT = { .type = void*.typeid };
|
|||||||
struct Object (Printable)
|
struct Object (Printable)
|
||||||
{
|
{
|
||||||
typeid type;
|
typeid type;
|
||||||
Allocator* allocator;
|
Allocator allocator;
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
uint128 i;
|
uint128 i;
|
||||||
@@ -48,9 +48,9 @@ fn usz! Object.to_format(&self, Formatter* formatter) @dynamic
|
|||||||
return n;
|
return n;
|
||||||
case ObjectInternalMap:
|
case ObjectInternalMap:
|
||||||
usz n = formatter.printf("{")!;
|
usz n = formatter.printf("{")!;
|
||||||
@pool()
|
@stack_mem(1024; Allocator mem)
|
||||||
{
|
{
|
||||||
foreach (i, key : self.map.key_tlist())
|
foreach (i, key : self.map.key_new_list(mem))
|
||||||
{
|
{
|
||||||
if (i > 0) n += formatter.printf(",")!;
|
if (i > 0) n += formatter.printf(",")!;
|
||||||
n += formatter.printf(`"%s":`, key)!;
|
n += formatter.printf(`"%s":`, key)!;
|
||||||
@@ -76,7 +76,7 @@ fn usz! Object.to_format(&self, Formatter* formatter) @dynamic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Object* new_obj(Allocator* allocator)
|
fn Object* new_obj(Allocator allocator)
|
||||||
{
|
{
|
||||||
return allocator::new(allocator, Object, { .allocator = allocator, .type = void.typeid });
|
return allocator::new(allocator, Object, { .allocator = allocator, .type = void.typeid });
|
||||||
}
|
}
|
||||||
@@ -86,22 +86,22 @@ fn Object* new_null()
|
|||||||
return &NULL_OBJECT;
|
return &NULL_OBJECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Object* new_int(int128 i, Allocator* allocator)
|
fn Object* new_int(int128 i, Allocator allocator)
|
||||||
{
|
{
|
||||||
return allocator::new(allocator, Object, { .i = i, .allocator = allocator, .type = int128.typeid });
|
return allocator::new(allocator, Object, { .i = i, .allocator = allocator, .type = int128.typeid });
|
||||||
}
|
}
|
||||||
|
|
||||||
macro Object* new_enum(e, Allocator* allocator)
|
macro Object* new_enum(e, Allocator allocator)
|
||||||
{
|
{
|
||||||
return allocator::new(allocator, Object, { .i = (int128)e, .allocator = allocator, .type = @typeid(e) });
|
return allocator::new(allocator, Object, { .i = (int128)e, .allocator = allocator, .type = @typeid(e) });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Object* new_float(double f, Allocator* allocator)
|
fn Object* new_float(double f, Allocator allocator)
|
||||||
{
|
{
|
||||||
return allocator::new(allocator, Object, { .f = f, .allocator = allocator, .type = double.typeid });
|
return allocator::new(allocator, Object, { .f = f, .allocator = allocator, .type = double.typeid });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Object* new_string(String s, Allocator* allocator)
|
fn Object* new_string(String s, Allocator allocator)
|
||||||
{
|
{
|
||||||
return allocator::new(allocator, Object, { .s = s.copy(allocator), .allocator = allocator, .type = String.typeid });
|
return allocator::new(allocator, Object, { .s = s.copy(allocator), .allocator = allocator, .type = String.typeid });
|
||||||
}
|
}
|
||||||
@@ -190,7 +190,6 @@ fn void Object.set_object(&self, String key, Object* new_object) @private
|
|||||||
macro Object* Object.object_from_value(&self, value) @private
|
macro Object* Object.object_from_value(&self, value) @private
|
||||||
{
|
{
|
||||||
var $Type = $typeof(value);
|
var $Type = $typeof(value);
|
||||||
|
|
||||||
$switch
|
$switch
|
||||||
$case types::is_int($Type):
|
$case types::is_int($Type):
|
||||||
return new_int(value, self.allocator);
|
return new_int(value, self.allocator);
|
||||||
@@ -234,10 +233,10 @@ macro Object* Object.set_at(&self, usz index, String key, value)
|
|||||||
* @require self.is_indexable()
|
* @require self.is_indexable()
|
||||||
* @ensure return != null
|
* @ensure return != null
|
||||||
**/
|
**/
|
||||||
macro Object* Object.append(&self, value)
|
macro Object* Object.push(&self, value)
|
||||||
{
|
{
|
||||||
Object* val = self.object_from_value(value);
|
Object* val = self.object_from_value(value);
|
||||||
self.append_object(val);
|
self.push_object(val);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,10 +267,10 @@ fn usz Object.get_len(&self)
|
|||||||
/**
|
/**
|
||||||
* @require self.is_indexable()
|
* @require self.is_indexable()
|
||||||
**/
|
**/
|
||||||
fn void Object.append_object(&self, Object* to_append)
|
fn void Object.push_object(&self, Object* to_append)
|
||||||
{
|
{
|
||||||
self.init_array_if_needed();
|
self.init_array_if_needed();
|
||||||
self.array.append(to_append);
|
self.array.push(to_append);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -282,11 +281,11 @@ fn void Object.set_object_at(&self, usz index, Object* to_set)
|
|||||||
self.init_array_if_needed();
|
self.init_array_if_needed();
|
||||||
while (self.array.len() < index)
|
while (self.array.len() < index)
|
||||||
{
|
{
|
||||||
self.array.append(&NULL_OBJECT);
|
self.array.push(&NULL_OBJECT);
|
||||||
}
|
}
|
||||||
if (self.array.len() == index)
|
if (self.array.len() == index)
|
||||||
{
|
{
|
||||||
self.array.append(to_set);
|
self.array.push(to_set);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.array.get(index).free();
|
self.array.get(index).free();
|
||||||
|
|||||||
@@ -36,12 +36,7 @@ struct PrivatePriorityQueue (Printable)
|
|||||||
Heap heap;
|
Heap heap;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void PrivatePriorityQueue.init_new(&self, usz initial_capacity = 16, Allocator* allocator = allocator::heap()) @inline @deprecated("Replaced by new_init")
|
fn void PrivatePriorityQueue.new_init(&self, usz initial_capacity = 16, Allocator allocator = allocator::heap()) @inline
|
||||||
{
|
|
||||||
return self.new_init(initial_capacity, allocator);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn void PrivatePriorityQueue.new_init(&self, usz initial_capacity = 16, Allocator* allocator = allocator::heap()) @inline
|
|
||||||
{
|
{
|
||||||
self.heap.new_init(initial_capacity, allocator);
|
self.heap.new_init(initial_capacity, allocator);
|
||||||
}
|
}
|
||||||
@@ -51,11 +46,6 @@ fn void PrivatePriorityQueue.temp_init(&self, usz initial_capacity = 16) @inline
|
|||||||
self.heap.new_init(initial_capacity, allocator::temp()) @inline;
|
self.heap.new_init(initial_capacity, allocator::temp()) @inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void PrivatePriorityQueue.init_temp(&self, usz initial_capacity = 16) @inline @deprecated("Replaced by temp_init")
|
|
||||||
{
|
|
||||||
return self.temp_init(initial_capacity) @inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn void PrivatePriorityQueue.push(&self, Type element)
|
fn void PrivatePriorityQueue.push(&self, Type element)
|
||||||
{
|
{
|
||||||
self.heap.push(element);
|
self.heap.push(element);
|
||||||
@@ -117,7 +107,7 @@ fn Type! PrivatePriorityQueue.pop(&self)
|
|||||||
return self.heap.pop();
|
return self.heap.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Type! PrivatePriorityQueue.peek(&self)
|
fn Type! PrivatePriorityQueue.first(&self)
|
||||||
{
|
{
|
||||||
if (!self.len()) return IteratorResult.NO_MORE_ELEMENT?;
|
if (!self.len()) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
return self.heap.get(0);
|
return self.heap.get(0);
|
||||||
@@ -141,7 +131,7 @@ fn bool PrivatePriorityQueue.is_empty(&self)
|
|||||||
/**
|
/**
|
||||||
* @require index < self.len()
|
* @require index < self.len()
|
||||||
*/
|
*/
|
||||||
fn Type PrivatePriorityQueue.peek_at(&self, usz index) @operator([])
|
fn Type PrivatePriorityQueue.get(&self, usz index) @operator([])
|
||||||
{
|
{
|
||||||
return self.heap[index];
|
return self.heap[index];
|
||||||
}
|
}
|
||||||
@@ -151,7 +141,7 @@ fn usz! PrivatePriorityQueue.to_format(&self, Formatter* formatter) @dynamic
|
|||||||
return self.heap.to_format(formatter);
|
return self.heap.to_format(formatter);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String PrivatePriorityQueue.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
|
fn String PrivatePriorityQueue.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
|
||||||
{
|
{
|
||||||
return self.heap.to_new_string(allocator);
|
return self.heap.to_new_string(allocator);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ fn Type Range.get(&self, usz index) @operator([])
|
|||||||
return (Type)(self.start + (usz)index);
|
return (Type)(self.start + (usz)index);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String Range.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
|
fn String Range.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
|
||||||
{
|
{
|
||||||
return string::new_format("[%s..%s]", self.start, self.end, .allocator = allocator);
|
return string::new_format("[%s..%s]", self.start, self.end, .allocator = allocator);
|
||||||
}
|
}
|
||||||
@@ -66,7 +66,7 @@ fn usz! ExclusiveRange.to_format(&self, Formatter* formatter) @dynamic
|
|||||||
return formatter.printf("[%s..<%s]", self.start, self.end)!;
|
return formatter.printf("[%s..<%s]", self.start, self.end)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String ExclusiveRange.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
|
fn String ExclusiveRange.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
|
||||||
{
|
{
|
||||||
return string::new_format("[%s..<%s]", self.start, self.end, .allocator = allocator);
|
return string::new_format("[%s..<%s]", self.start, self.end, .allocator = allocator);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ fn void RingBuffer.init(&self) @inline
|
|||||||
*self = {};
|
*self = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void RingBuffer.putc(&self, Type c)
|
fn void RingBuffer.push(&self, Type c)
|
||||||
{
|
{
|
||||||
if (self.written < SIZE)
|
if (self.written < SIZE)
|
||||||
{
|
{
|
||||||
@@ -26,7 +26,7 @@ fn void RingBuffer.putc(&self, Type c)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Type RingBuffer.getc(&self, usz index)
|
fn Type RingBuffer.get(&self, usz index) @operator([])
|
||||||
{
|
{
|
||||||
index %= SIZE;
|
index %= SIZE;
|
||||||
usz avail = SIZE - self.head;
|
usz avail = SIZE - self.head;
|
||||||
@@ -37,7 +37,7 @@ fn Type RingBuffer.getc(&self, usz index)
|
|||||||
return self.buf[index - avail];
|
return self.buf[index - avail];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Type! RingBuffer.popc(&self)
|
fn Type! RingBuffer.pop(&self)
|
||||||
{
|
{
|
||||||
switch
|
switch
|
||||||
{
|
{
|
||||||
@@ -52,7 +52,7 @@ fn Type! RingBuffer.popc(&self)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usz RingBuffer.get(&self, usz index, Type[] buffer)
|
fn usz RingBuffer.read(&self, usz index, Type[] buffer)
|
||||||
{
|
{
|
||||||
index %= SIZE;
|
index %= SIZE;
|
||||||
if (self.written < SIZE)
|
if (self.written < SIZE)
|
||||||
@@ -87,7 +87,7 @@ fn usz RingBuffer.get(&self, usz index, Type[] buffer)
|
|||||||
return n1 + n2;
|
return n1 + n2;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void RingBuffer.push(&self, Type[] buffer)
|
fn void RingBuffer.write(&self, Type[] buffer)
|
||||||
{
|
{
|
||||||
usz i;
|
usz i;
|
||||||
while (self.written < SIZE && i < buffer.len)
|
while (self.written < SIZE && i < buffer.len)
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ fn void ArenaAllocator.reset(&self, usz mark) @dynamic => self.used = mark;
|
|||||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||||
* @require size > 0
|
* @require size > 0
|
||||||
**/
|
**/
|
||||||
fn void*! ArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
|
fn void*! ArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
|
||||||
{
|
{
|
||||||
alignment = alignment_for_allocation(alignment);
|
alignment = alignment_for_allocation(alignment);
|
||||||
usz total_len = self.data.len;
|
usz total_len = self.data.len;
|
||||||
@@ -65,7 +65,7 @@ fn void*! ArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz
|
|||||||
self.used = end;
|
self.used = end;
|
||||||
ArenaAllocatorHeader* header = mem - ArenaAllocatorHeader.sizeof;
|
ArenaAllocatorHeader* header = mem - ArenaAllocatorHeader.sizeof;
|
||||||
header.size = size;
|
header.size = size;
|
||||||
if (clear) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
|
if (init_type == ZERO) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||||
return mem;
|
return mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ fn void*! ArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz
|
|||||||
* @require old_pointer != null
|
* @require old_pointer != null
|
||||||
* @require size > 0
|
* @require size > 0
|
||||||
**/
|
**/
|
||||||
fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignment, usz offset) @dynamic
|
fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignment) @dynamic
|
||||||
{
|
{
|
||||||
alignment = alignment_for_allocation(alignment);
|
alignment = alignment_for_allocation(alignment);
|
||||||
assert(old_pointer >= self.data.ptr, "Pointer originates from a different allocator.");
|
assert(old_pointer >= self.data.ptr, "Pointer originates from a different allocator.");
|
||||||
@@ -100,7 +100,7 @@ fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignmen
|
|||||||
return old_pointer;
|
return old_pointer;
|
||||||
}
|
}
|
||||||
// Otherwise just allocate new memory.
|
// Otherwise just allocate new memory.
|
||||||
void* mem = self.acquire(size, false, alignment, 0)!;
|
void* mem = self.acquire(size, NO_ZERO, alignment)!;
|
||||||
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||||
return mem;
|
return mem;
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,7 @@ import std::math;
|
|||||||
|
|
||||||
struct DynamicArenaAllocator (Allocator)
|
struct DynamicArenaAllocator (Allocator)
|
||||||
{
|
{
|
||||||
Allocator* backing_allocator;
|
Allocator backing_allocator;
|
||||||
DynamicArenaPage* page;
|
DynamicArenaPage* page;
|
||||||
DynamicArenaPage* unused_page;
|
DynamicArenaPage* unused_page;
|
||||||
usz page_size;
|
usz page_size;
|
||||||
@@ -16,7 +16,7 @@ struct DynamicArenaAllocator (Allocator)
|
|||||||
* @param [&inout] allocator
|
* @param [&inout] allocator
|
||||||
* @require page_size >= 128
|
* @require page_size >= 128
|
||||||
**/
|
**/
|
||||||
fn void DynamicArenaAllocator.init(&self, usz page_size, Allocator* allocator)
|
fn void DynamicArenaAllocator.init(&self, usz page_size, Allocator allocator)
|
||||||
{
|
{
|
||||||
self.page = null;
|
self.page = null;
|
||||||
self.unused_page = null;
|
self.unused_page = null;
|
||||||
@@ -24,12 +24,14 @@ fn void DynamicArenaAllocator.init(&self, usz page_size, Allocator* allocator)
|
|||||||
self.backing_allocator = allocator;
|
self.backing_allocator = allocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import std::io;
|
||||||
fn void DynamicArenaAllocator.free(&self)
|
fn void DynamicArenaAllocator.free(&self)
|
||||||
{
|
{
|
||||||
DynamicArenaPage* page = self.page;
|
DynamicArenaPage* page = self.page;
|
||||||
while (page)
|
while (page)
|
||||||
{
|
{
|
||||||
DynamicArenaPage* next_page = page.prev_arena;
|
DynamicArenaPage* next_page = page.prev_arena;
|
||||||
|
allocator::free(self.backing_allocator, page.memory);
|
||||||
allocator::free(self.backing_allocator, page);
|
allocator::free(self.backing_allocator, page);
|
||||||
page = next_page;
|
page = next_page;
|
||||||
}
|
}
|
||||||
@@ -37,6 +39,7 @@ fn void DynamicArenaAllocator.free(&self)
|
|||||||
while (page)
|
while (page)
|
||||||
{
|
{
|
||||||
DynamicArenaPage* next_page = page.prev_arena;
|
DynamicArenaPage* next_page = page.prev_arena;
|
||||||
|
allocator::free(self.backing_allocator, page.memory);
|
||||||
allocator::free(self.backing_allocator, page);
|
allocator::free(self.backing_allocator, page);
|
||||||
page = next_page;
|
page = next_page;
|
||||||
}
|
}
|
||||||
@@ -77,7 +80,7 @@ fn void DynamicArenaAllocator.release(&self, void* ptr, bool) @dynamic
|
|||||||
* @require old_pointer != null `Resize doesn't handle null pointers`
|
* @require old_pointer != null `Resize doesn't handle null pointers`
|
||||||
* @require self.page `tried to realloc pointer on invalid allocator`
|
* @require self.page `tried to realloc pointer on invalid allocator`
|
||||||
*/
|
*/
|
||||||
fn void*! DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
|
fn void*! DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
|
||||||
{
|
{
|
||||||
DynamicArenaPage* current_page = self.page;
|
DynamicArenaPage* current_page = self.page;
|
||||||
alignment = alignment_for_allocation(alignment);
|
alignment = alignment_for_allocation(alignment);
|
||||||
@@ -102,7 +105,7 @@ fn void*! DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz a
|
|||||||
current_page.used += add_size;
|
current_page.used += add_size;
|
||||||
return old_pointer;
|
return old_pointer;
|
||||||
}
|
}
|
||||||
void* new_mem = self.acquire(size, false, alignment, 0)!;
|
void* new_mem = self.acquire(size, NO_ZERO, alignment)!;
|
||||||
mem::copy(new_mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT);
|
mem::copy(new_mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||||
return new_mem;
|
return new_mem;
|
||||||
}
|
}
|
||||||
@@ -131,8 +134,8 @@ fn void DynamicArenaAllocator.reset(&self, usz mark = 0) @dynamic
|
|||||||
fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @local
|
fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @local
|
||||||
{
|
{
|
||||||
// First, make sure that we can align it, extending the page size if needed.
|
// First, make sure that we can align it, extending the page size if needed.
|
||||||
usz page_size = max(self.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof, alignment));
|
usz page_size = max(self.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + alignment, alignment));
|
||||||
|
assert(page_size > size + DynamicArenaChunk.sizeof);
|
||||||
// Grab the page without alignment (we do it ourselves)
|
// Grab the page without alignment (we do it ourselves)
|
||||||
void* mem = allocator::malloc_try(self.backing_allocator, page_size)!;
|
void* mem = allocator::malloc_try(self.backing_allocator, page_size)!;
|
||||||
DynamicArenaPage*! page = allocator::new_try(self.backing_allocator, DynamicArenaPage);
|
DynamicArenaPage*! page = allocator::new_try(self.backing_allocator, DynamicArenaPage);
|
||||||
@@ -158,7 +161,7 @@ fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @loca
|
|||||||
* @require size > 0 `acquire expects size > 0`
|
* @require size > 0 `acquire expects size > 0`
|
||||||
* @require !alignment || math::is_power_of_2(alignment)
|
* @require !alignment || math::is_power_of_2(alignment)
|
||||||
*/
|
*/
|
||||||
fn void*! DynamicArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
|
fn void*! DynamicArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
|
||||||
{
|
{
|
||||||
alignment = alignment_for_allocation(alignment);
|
alignment = alignment_for_allocation(alignment);
|
||||||
DynamicArenaPage* page = self.page;
|
DynamicArenaPage* page = self.page;
|
||||||
@@ -195,6 +198,6 @@ fn void*! DynamicArenaAllocator.acquire(&self, usz size, bool clear, usz alignme
|
|||||||
chunk.size = size;
|
chunk.size = size;
|
||||||
return mem;
|
return mem;
|
||||||
|}!;
|
|}!;
|
||||||
if (clear) mem::clear(ptr, size, mem::DEFAULT_MEM_ALIGNMENT);
|
if (init_type == ZERO) mem::clear(ptr, size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,16 +21,16 @@ fn void SimpleHeapAllocator.init(&self, MemoryAllocFn allocator)
|
|||||||
self.free_list = null;
|
self.free_list = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void*! SimpleHeapAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
|
fn void*! SimpleHeapAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
|
||||||
{
|
{
|
||||||
if (clear)
|
if (init_type == ZERO)
|
||||||
{
|
{
|
||||||
return alignment > 0 ? @aligned_alloc(self._calloc, size, alignment) : self._calloc(size);
|
return alignment > 0 ? @aligned_alloc(self._calloc, size, alignment) : self._calloc(size);
|
||||||
}
|
}
|
||||||
return alignment > 0 ? @aligned_alloc(self._alloc, size, alignment) : self._alloc(size);
|
return alignment > 0 ? @aligned_alloc(self._alloc, size, alignment) : self._alloc(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void*! SimpleHeapAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
|
fn void*! SimpleHeapAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
|
||||||
{
|
{
|
||||||
return alignment > 0
|
return alignment > 0
|
||||||
? @aligned_realloc(self._calloc, self._free, old_pointer, size, alignment)
|
? @aligned_realloc(self._calloc, self._free, old_pointer, size, alignment)
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ module std::core::mem::allocator @if(env::POSIX);
|
|||||||
import std::os;
|
import std::os;
|
||||||
import libc;
|
import libc;
|
||||||
|
|
||||||
fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz offset) @dynamic
|
fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
|
||||||
{
|
{
|
||||||
if (clear)
|
if (init_type == ZERO)
|
||||||
{
|
{
|
||||||
void* data @noinit;
|
void* data @noinit;
|
||||||
if (alignment > mem::DEFAULT_MEM_ALIGNMENT)
|
if (alignment > mem::DEFAULT_MEM_ALIGNMENT)
|
||||||
@@ -43,19 +43,9 @@ fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment, usz offset) @dynamic
|
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
|
||||||
{
|
{
|
||||||
if (!new_bytes)
|
|
||||||
{
|
|
||||||
self.release(old_ptr, alignment > 0);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (!old_ptr)
|
|
||||||
{
|
|
||||||
return self.acquire(new_bytes, false, alignment, 0);
|
|
||||||
}
|
|
||||||
if (alignment <= mem::DEFAULT_MEM_ALIGNMENT) return libc::realloc(old_ptr, new_bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
|
if (alignment <= mem::DEFAULT_MEM_ALIGNMENT) return libc::realloc(old_ptr, new_bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
|
||||||
|
|
||||||
void* new_ptr;
|
void* new_ptr;
|
||||||
if (posix::posix_memalign(&new_ptr, alignment, new_bytes)) return AllocationFailure.OUT_OF_MEMORY?;
|
if (posix::posix_memalign(&new_ptr, alignment, new_bytes)) return AllocationFailure.OUT_OF_MEMORY?;
|
||||||
|
|
||||||
@@ -83,13 +73,13 @@ module std::core::mem::allocator @if(env::WIN32);
|
|||||||
import std::os::win32;
|
import std::os::win32;
|
||||||
import libc;
|
import libc;
|
||||||
|
|
||||||
fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz offset) @dynamic
|
fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
|
||||||
{
|
{
|
||||||
if (clear)
|
if (init_type == ZERO)
|
||||||
{
|
{
|
||||||
if (alignment > 0)
|
if (alignment > 0)
|
||||||
{
|
{
|
||||||
return win32::_aligned_recalloc(null, bytes, alignment) ?: AllocationFailure.OUT_OF_MEMORY?;
|
return win32::_aligned_recalloc(null, 1, bytes, alignment) ?: AllocationFailure.OUT_OF_MEMORY?;
|
||||||
}
|
}
|
||||||
return libc::calloc(1, bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
|
return libc::calloc(1, bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
|
||||||
}
|
}
|
||||||
@@ -101,7 +91,7 @@ fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment, usz offset) @dynamic
|
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
|
||||||
{
|
{
|
||||||
if (alignment)
|
if (alignment)
|
||||||
{
|
{
|
||||||
@@ -123,9 +113,9 @@ fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
|
|||||||
module std::core::mem::allocator @if(!env::WIN32 && !env::POSIX);
|
module std::core::mem::allocator @if(!env::WIN32 && !env::POSIX);
|
||||||
import libc;
|
import libc;
|
||||||
|
|
||||||
fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz offset) @dynamic
|
fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
|
||||||
{
|
{
|
||||||
if (clear)
|
if (init_type == ZERO)
|
||||||
{
|
{
|
||||||
void* data = alignment ? @aligned_alloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment)!! : libc::calloc(bytes, 1);
|
void* data = alignment ? @aligned_alloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment)!! : libc::calloc(bytes, 1);
|
||||||
return data ?: AllocationFailure.OUT_OF_MEMORY?;
|
return data ?: AllocationFailure.OUT_OF_MEMORY?;
|
||||||
@@ -142,7 +132,7 @@ fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment, usz offset) @dynamic
|
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
|
||||||
{
|
{
|
||||||
if (alignment)
|
if (alignment)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module std::core::mem::allocator;
|
|||||||
|
|
||||||
struct OnStackAllocator (Allocator)
|
struct OnStackAllocator (Allocator)
|
||||||
{
|
{
|
||||||
Allocator* backing_allocator;
|
Allocator backing_allocator;
|
||||||
char[] data;
|
char[] data;
|
||||||
usz used;
|
usz used;
|
||||||
OnStackAllocatorExtraChunk* chunk;
|
OnStackAllocatorExtraChunk* chunk;
|
||||||
@@ -20,7 +20,7 @@ struct OnStackAllocatorExtraChunk @local
|
|||||||
* @param [&inout] allocator
|
* @param [&inout] allocator
|
||||||
* Initialize a memory arena for use using the provided bytes.
|
* Initialize a memory arena for use using the provided bytes.
|
||||||
**/
|
**/
|
||||||
fn void OnStackAllocator.init(&self, char[] data, Allocator* allocator)
|
fn void OnStackAllocator.init(&self, char[] data, Allocator allocator)
|
||||||
{
|
{
|
||||||
self.data = data;
|
self.data = data;
|
||||||
self.backing_allocator = allocator;
|
self.backing_allocator = allocator;
|
||||||
@@ -103,18 +103,18 @@ fn OnStackAllocatorExtraChunk* on_stack_allocator_find_chunk(OnStackAllocator* a
|
|||||||
* @require old_pointer != null
|
* @require old_pointer != null
|
||||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||||
**/
|
**/
|
||||||
fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz deprecated) @dynamic
|
fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
|
||||||
{
|
{
|
||||||
if (!allocation_in_stack_mem(self, old_pointer))
|
if (!allocation_in_stack_mem(self, old_pointer))
|
||||||
{
|
{
|
||||||
OnStackAllocatorExtraChunk* chunk = on_stack_allocator_find_chunk(self, old_pointer);
|
OnStackAllocatorExtraChunk* chunk = on_stack_allocator_find_chunk(self, old_pointer);
|
||||||
assert(chunk, "Tried to realloc pointer not belonging to the allocator");
|
assert(chunk, "Tried to realloc pointer not belonging to the allocator");
|
||||||
return chunk.data = self.backing_allocator.resize(old_pointer, size, alignment, 0)!;
|
return chunk.data = self.backing_allocator.resize(old_pointer, size, alignment)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof;
|
OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof;
|
||||||
usz old_size = header.size;
|
usz old_size = header.size;
|
||||||
void* mem = self.acquire(size, false, alignment, 0)!;
|
void* mem = self.acquire(size, NO_ZERO, alignment)!;
|
||||||
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||||
return mem;
|
return mem;
|
||||||
}
|
}
|
||||||
@@ -123,7 +123,7 @@ fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignm
|
|||||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||||
* @require size > 0
|
* @require size > 0
|
||||||
**/
|
**/
|
||||||
fn void*! OnStackAllocator.acquire(&self, usz size, bool clear, usz alignment, usz deprecated) @dynamic
|
fn void*! OnStackAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
|
||||||
{
|
{
|
||||||
bool aligned = alignment > 0;
|
bool aligned = alignment > 0;
|
||||||
alignment = alignment_for_allocation(alignment);
|
alignment = alignment_for_allocation(alignment);
|
||||||
@@ -132,7 +132,7 @@ fn void*! OnStackAllocator.acquire(&self, usz size, bool clear, usz alignment, u
|
|||||||
void* unaligned_pointer_to_offset = start_mem + self.used + OnStackAllocatorHeader.sizeof ;
|
void* unaligned_pointer_to_offset = start_mem + self.used + OnStackAllocatorHeader.sizeof ;
|
||||||
void* mem = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
|
void* mem = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
|
||||||
usz end = (usz)(mem - self.data.ptr) + size;
|
usz end = (usz)(mem - self.data.ptr) + size;
|
||||||
Allocator* backing_allocator = self.backing_allocator;
|
Allocator backing_allocator = self.backing_allocator;
|
||||||
|
|
||||||
if (end > total_len)
|
if (end > total_len)
|
||||||
{
|
{
|
||||||
@@ -140,7 +140,7 @@ fn void*! OnStackAllocator.acquire(&self, usz size, bool clear, usz alignment, u
|
|||||||
defer catch allocator::free(backing_allocator, chunk);
|
defer catch allocator::free(backing_allocator, chunk);
|
||||||
defer try self.chunk = chunk;
|
defer try self.chunk = chunk;
|
||||||
*chunk = { .prev = self.chunk, .is_aligned = aligned };
|
*chunk = { .prev = self.chunk, .is_aligned = aligned };
|
||||||
return chunk.data = backing_allocator.acquire(size, clear, aligned ? alignment : 0, 0)!;
|
return chunk.data = backing_allocator.acquire(size, init_type, aligned ? alignment : 0)!;
|
||||||
}
|
}
|
||||||
self.used = end;
|
self.used = end;
|
||||||
OnStackAllocatorHeader* header = mem - OnStackAllocatorHeader.sizeof;
|
OnStackAllocatorHeader* header = mem - OnStackAllocatorHeader.sizeof;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ struct TempAllocatorChunk @local
|
|||||||
|
|
||||||
struct TempAllocator (Allocator)
|
struct TempAllocator (Allocator)
|
||||||
{
|
{
|
||||||
Allocator* backing_allocator;
|
Allocator backing_allocator;
|
||||||
TempAllocatorPage* last_page;
|
TempAllocatorPage* last_page;
|
||||||
usz used;
|
usz used;
|
||||||
usz capacity;
|
usz capacity;
|
||||||
@@ -35,7 +35,7 @@ macro bool TempAllocatorPage.is_aligned(&self) => self.size & PAGE_IS_ALIGNED ==
|
|||||||
/**
|
/**
|
||||||
* @require size >= 16
|
* @require size >= 16
|
||||||
**/
|
**/
|
||||||
fn TempAllocator*! new_temp_allocator(usz size, Allocator* allocator)
|
fn TempAllocator*! new_temp_allocator(usz size, Allocator allocator)
|
||||||
{
|
{
|
||||||
TempAllocator* temp = allocator::alloc_with_padding(allocator, TempAllocator, size)!;
|
TempAllocator* temp = allocator::alloc_with_padding(allocator, TempAllocator, size)!;
|
||||||
temp.last_page = null;
|
temp.last_page = null;
|
||||||
@@ -45,9 +45,11 @@ fn TempAllocator*! new_temp_allocator(usz size, Allocator* allocator)
|
|||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn TempAllocator*! new_temp(usz size, Allocator* allocator) @deprecated("Use new_temp_allocator")
|
fn void TempAllocator.destroy(&self)
|
||||||
{
|
{
|
||||||
return new_temp_allocator(size, allocator);
|
self.reset(0);
|
||||||
|
if (self.last_page) (void)self._free_page(self.last_page);
|
||||||
|
allocator::free(self.backing_allocator, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usz TempAllocator.mark(&self) @dynamic => self.used;
|
fn usz TempAllocator.mark(&self) @dynamic => self.used;
|
||||||
@@ -63,6 +65,16 @@ fn void TempAllocator.release(&self, void* old_pointer, bool) @dynamic
|
|||||||
fn void TempAllocator.reset(&self, usz mark) @dynamic
|
fn void TempAllocator.reset(&self, usz mark) @dynamic
|
||||||
{
|
{
|
||||||
TempAllocatorPage *last_page = self.last_page;
|
TempAllocatorPage *last_page = self.last_page;
|
||||||
|
$if env::COMPILER_SAFE_MODE:
|
||||||
|
if (!last_page)
|
||||||
|
{
|
||||||
|
usz cleaned = self.used - mark;
|
||||||
|
if (cleaned > 0)
|
||||||
|
{
|
||||||
|
self.data[mark : cleaned] = 0xAA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$endif
|
||||||
while (last_page && last_page.mark > mark)
|
while (last_page && last_page.mark > mark)
|
||||||
{
|
{
|
||||||
TempAllocatorPage *to_free = last_page;
|
TempAllocatorPage *to_free = last_page;
|
||||||
@@ -94,13 +106,13 @@ fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size,
|
|||||||
*pointer_to_prev = page.prev_page;
|
*pointer_to_prev = page.prev_page;
|
||||||
usz page_size = page.pagesize();
|
usz page_size = page.pagesize();
|
||||||
// Clear on size > original size.
|
// Clear on size > original size.
|
||||||
void* data = self.acquire(size, size > page_size, alignment, 0)!;
|
void* data = self.acquire(size, NO_ZERO, alignment)!;
|
||||||
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||||
self.backing_allocator.release(real_pointer, page.is_aligned());
|
self.backing_allocator.release(real_pointer, page.is_aligned());
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment, usz deprecated) @dynamic
|
fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @dynamic
|
||||||
{
|
{
|
||||||
TempAllocatorChunk *chunk = pointer - TempAllocatorChunk.sizeof;
|
TempAllocatorChunk *chunk = pointer - TempAllocatorChunk.sizeof;
|
||||||
if (chunk.size == (usz)-1)
|
if (chunk.size == (usz)-1)
|
||||||
@@ -111,8 +123,7 @@ fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment, us
|
|||||||
return self._realloc_page(page, size, alignment);
|
return self._realloc_page(page, size, alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO optimize last allocation
|
TempAllocatorChunk* data = self.acquire(size, NO_ZERO, alignment)!;
|
||||||
TempAllocatorChunk* data = self.acquire(size, size > chunk.size, alignment, 0)!;
|
|
||||||
mem::copy(data, pointer, chunk.size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
mem::copy(data, pointer, chunk.size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
@@ -123,7 +134,7 @@ fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment, us
|
|||||||
* @require !alignment || math::is_power_of_2(alignment)
|
* @require !alignment || math::is_power_of_2(alignment)
|
||||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||||
**/
|
**/
|
||||||
fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz deprecated) @dynamic
|
fn void*! TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
|
||||||
{
|
{
|
||||||
alignment = alignment_for_allocation(alignment);
|
alignment = alignment_for_allocation(alignment);
|
||||||
void* start_mem = &self.data;
|
void* start_mem = &self.data;
|
||||||
@@ -142,7 +153,7 @@ fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz
|
|||||||
TempAllocatorChunk* chunk_start = mem - TempAllocatorChunk.sizeof;
|
TempAllocatorChunk* chunk_start = mem - TempAllocatorChunk.sizeof;
|
||||||
chunk_start.size = size;
|
chunk_start.size = size;
|
||||||
self.used = new_usage;
|
self.used = new_usage;
|
||||||
if (clear) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
|
if (init_type == ZERO) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||||
return mem;
|
return mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +165,7 @@ fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz
|
|||||||
{
|
{
|
||||||
// This is actually simpler, since it will create the offset for us.
|
// This is actually simpler, since it will create the offset for us.
|
||||||
usz total_alloc_size = mem::aligned_offset(TempAllocatorPage.sizeof + size, alignment);
|
usz total_alloc_size = mem::aligned_offset(TempAllocatorPage.sizeof + size, alignment);
|
||||||
if (clear)
|
if (init_type == ZERO)
|
||||||
{
|
{
|
||||||
mem = allocator::calloc_aligned(self.backing_allocator, total_alloc_size, alignment)!;
|
mem = allocator::calloc_aligned(self.backing_allocator, total_alloc_size, alignment)!;
|
||||||
}
|
}
|
||||||
@@ -171,7 +182,7 @@ fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz
|
|||||||
// Here we might need to pad
|
// Here we might need to pad
|
||||||
usz padded_header_size = mem::aligned_offset(TempAllocatorPage.sizeof, mem::DEFAULT_MEM_ALIGNMENT);
|
usz padded_header_size = mem::aligned_offset(TempAllocatorPage.sizeof, mem::DEFAULT_MEM_ALIGNMENT);
|
||||||
usz total_alloc_size = padded_header_size + size;
|
usz total_alloc_size = padded_header_size + size;
|
||||||
void* alloc = self.backing_allocator.acquire(total_alloc_size, clear, 0, 0)!;
|
void* alloc = self.backing_allocator.acquire(total_alloc_size, init_type, 0)!;
|
||||||
|
|
||||||
// Find the page.
|
// Find the page.
|
||||||
page = alloc + padded_header_size - TempAllocatorPage.sizeof;
|
page = alloc + padded_header_size - TempAllocatorPage.sizeof;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ def AllocMap = HashMap(<uptr, Allocation>);
|
|||||||
// is not compatible with allocators that uses mark()
|
// is not compatible with allocators that uses mark()
|
||||||
struct TrackingAllocator (Allocator)
|
struct TrackingAllocator (Allocator)
|
||||||
{
|
{
|
||||||
Allocator* inner_allocator;
|
Allocator inner_allocator;
|
||||||
AllocMap map;
|
AllocMap map;
|
||||||
usz mem_total;
|
usz mem_total;
|
||||||
usz allocs_total;
|
usz allocs_total;
|
||||||
@@ -31,7 +31,7 @@ struct TrackingAllocator (Allocator)
|
|||||||
*
|
*
|
||||||
* @param [&inout] allocator "The allocator to track"
|
* @param [&inout] allocator "The allocator to track"
|
||||||
**/
|
**/
|
||||||
fn void TrackingAllocator.init(&self, Allocator* allocator)
|
fn void TrackingAllocator.init(&self, Allocator allocator)
|
||||||
{
|
{
|
||||||
*self = { .inner_allocator = allocator };
|
*self = { .inner_allocator = allocator };
|
||||||
self.map.new_init(.allocator = allocator);
|
self.map.new_init(.allocator = allocator);
|
||||||
@@ -69,7 +69,7 @@ fn usz TrackingAllocator.total_allocated(&self) => self.mem_total;
|
|||||||
**/
|
**/
|
||||||
fn usz TrackingAllocator.total_allocation_count(&self) => self.allocs_total;
|
fn usz TrackingAllocator.total_allocation_count(&self) => self.allocs_total;
|
||||||
|
|
||||||
fn Allocation[] TrackingAllocator.allocations_tlist(&self, Allocator* allocator)
|
fn Allocation[] TrackingAllocator.allocations_tlist(&self, Allocator allocator)
|
||||||
{
|
{
|
||||||
return self.map.value_tlist();
|
return self.map.value_tlist();
|
||||||
}
|
}
|
||||||
@@ -79,9 +79,9 @@ fn Allocation[] TrackingAllocator.allocations_tlist(&self, Allocator* allocator)
|
|||||||
**/
|
**/
|
||||||
fn usz TrackingAllocator.allocation_count(&self) => self.map.count;
|
fn usz TrackingAllocator.allocation_count(&self) => self.map.count;
|
||||||
|
|
||||||
fn void*! TrackingAllocator.acquire(&self, usz size, bool clear, usz alignment, usz deprecated) @dynamic
|
fn void*! TrackingAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
|
||||||
{
|
{
|
||||||
void* data = self.inner_allocator.acquire(size, clear, alignment, 0)!;
|
void* data = self.inner_allocator.acquire(size, init_type, alignment)!;
|
||||||
self.allocs_total++;
|
self.allocs_total++;
|
||||||
void*[MAX_BACKTRACE] bt;
|
void*[MAX_BACKTRACE] bt;
|
||||||
backtrace::capture_current(&bt);
|
backtrace::capture_current(&bt);
|
||||||
@@ -91,9 +91,9 @@ fn void*! TrackingAllocator.acquire(&self, usz size, bool clear, usz alignment,
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void*! TrackingAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz deprecated) @dynamic
|
fn void*! TrackingAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
|
||||||
{
|
{
|
||||||
void* data = self.inner_allocator.resize(old_pointer, size, alignment, 0)!;
|
void* data = self.inner_allocator.resize(old_pointer, size, alignment)!;
|
||||||
self.map.remove((uptr)old_pointer);
|
self.map.remove((uptr)old_pointer);
|
||||||
void*[MAX_BACKTRACE] bt;
|
void*[MAX_BACKTRACE] bt;
|
||||||
backtrace::capture_current(&bt);
|
backtrace::capture_current(&bt);
|
||||||
@@ -107,7 +107,7 @@ fn void TrackingAllocator.release(&self, void* old_pointer, bool is_aligned) @dy
|
|||||||
{
|
{
|
||||||
if (catch self.map.remove((uptr)old_pointer))
|
if (catch self.map.remove((uptr)old_pointer))
|
||||||
{
|
{
|
||||||
assert(false, "Attempt to release untracked pointer %p, this is likely a bug.", old_pointer);
|
unreachable("Attempt to release untracked pointer %p, this is likely a bug.", old_pointer);
|
||||||
}
|
}
|
||||||
self.inner_allocator.release(old_pointer, is_aligned);
|
self.inner_allocator.release(old_pointer, is_aligned);
|
||||||
}
|
}
|
||||||
@@ -119,7 +119,7 @@ fn void TrackingAllocator.clear(&self)
|
|||||||
|
|
||||||
fn void TrackingAllocator.print_report(&self) => self.fprint_report(io::stdout())!!;
|
fn void TrackingAllocator.print_report(&self) => self.fprint_report(io::stdout())!!;
|
||||||
|
|
||||||
fn void! TrackingAllocator.fprint_report(&self, OutStream* out)
|
fn void! TrackingAllocator.fprint_report(&self, OutStream out)
|
||||||
{
|
{
|
||||||
|
|
||||||
usz total = 0;
|
usz total = 0;
|
||||||
|
|||||||
@@ -45,17 +45,17 @@ macro rindex_of(array, element)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Concatenate two arrays or subarrays, returning a subarray containing the concatenation of them.
|
* Concatenate two arrays or slices, returning a slice containing the concatenation of them.
|
||||||
*
|
*
|
||||||
* @param [in] arr1
|
* @param [in] arr1
|
||||||
* @param [in] arr2
|
* @param [in] arr2
|
||||||
* @param [&inout] allocator "The allocator to use, default is the heap allocator"
|
* @param [&inout] allocator "The allocator to use, default is the heap allocator"
|
||||||
* @require @typekind(arr1) == SUBARRAY || @typekind(arr1) == ARRAY
|
* @require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY
|
||||||
* @require @typekind(arr2) == SUBARRAY || @typekind(arr2) == ARRAY
|
* @require @typekind(arr2) == SLICE || @typekind(arr2) == ARRAY
|
||||||
* @require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
|
* @require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
|
||||||
* @ensure result.len == arr1.len + arr2.len
|
* @ensure result.len == arr1.len + arr2.len
|
||||||
**/
|
**/
|
||||||
macro concat_new(arr1, arr2, Allocator* allocator = allocator::heap())
|
macro concat_new(arr1, arr2, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
var $Type = $typeof(arr1[0]);
|
var $Type = $typeof(arr1[0]);
|
||||||
$Type[] result = allocator::alloc_array(allocator, $Type, arr1.len + arr2.len);
|
$Type[] result = allocator::alloc_array(allocator, $Type, arr1.len + arr2.len);
|
||||||
@@ -71,13 +71,13 @@ macro concat_new(arr1, arr2, Allocator* allocator = allocator::heap())
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Concatenate two arrays or subarrays, returning a subarray containing the concatenation of them,
|
* Concatenate two arrays or slices, returning a slice containing the concatenation of them,
|
||||||
* allocated using the temp allocator.
|
* allocated using the temp allocator.
|
||||||
*
|
*
|
||||||
* @param [in] arr1
|
* @param [in] arr1
|
||||||
* @param [in] arr2
|
* @param [in] arr2
|
||||||
* @require @typekind(arr1) == SUBARRAY || @typekind(arr1) == ARRAY
|
* @require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY
|
||||||
* @require @typekind(arr2) == SUBARRAY || @typekind(arr2) == ARRAY
|
* @require @typekind(arr2) == SLICE || @typekind(arr2) == ARRAY
|
||||||
* @require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
|
* @require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
|
||||||
* @ensure result.len == arr1.len + arr2.len
|
* @ensure result.len == arr1.len + arr2.len
|
||||||
**/
|
**/
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ bitstruct UInt128LE : uint128 @littleendian
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require is_array_or_sub_of_char(bytes) "argument must be an array, a pointer to an array or a subarray of char"
|
* @require is_array_or_slice_of_char(bytes) "argument must be an array, a pointer to an array or a slice of char"
|
||||||
* @require is_bitorder($Type) "type must be a bitorder integer"
|
* @require is_bitorder($Type) "type must be a bitorder integer"
|
||||||
**/
|
**/
|
||||||
macro read(bytes, $Type)
|
macro read(bytes, $Type)
|
||||||
@@ -104,7 +104,7 @@ macro read(bytes, $Type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require is_arrayptr_or_sub_of_char(bytes) "argument must be a pointer to an array or a subarray of char"
|
* @require is_arrayptr_or_slice_of_char(bytes) "argument must be a pointer to an array or a slice of char"
|
||||||
* @require is_bitorder($Type) "type must be a bitorder integer"
|
* @require is_bitorder($Type) "type must be a bitorder integer"
|
||||||
**/
|
**/
|
||||||
macro write(x, bytes, $Type)
|
macro write(x, bytes, $Type)
|
||||||
@@ -144,7 +144,7 @@ macro is_bitorder($Type)
|
|||||||
$endswitch
|
$endswitch
|
||||||
}
|
}
|
||||||
|
|
||||||
macro bool is_array_or_sub_of_char(bytes)
|
macro bool is_array_or_slice_of_char(bytes)
|
||||||
{
|
{
|
||||||
$switch (@typekind(bytes))
|
$switch (@typekind(bytes))
|
||||||
$case POINTER:
|
$case POINTER:
|
||||||
@@ -154,7 +154,7 @@ macro bool is_array_or_sub_of_char(bytes)
|
|||||||
return $Inner2.typeid == char.typeid;
|
return $Inner2.typeid == char.typeid;
|
||||||
$endif
|
$endif
|
||||||
$case ARRAY:
|
$case ARRAY:
|
||||||
$case SUBARRAY:
|
$case SLICE:
|
||||||
var $Inner = $typefrom($typeof(bytes).inner);
|
var $Inner = $typefrom($typeof(bytes).inner);
|
||||||
return $Inner.typeid == char.typeid;
|
return $Inner.typeid == char.typeid;
|
||||||
$default:
|
$default:
|
||||||
@@ -162,7 +162,7 @@ macro bool is_array_or_sub_of_char(bytes)
|
|||||||
$endswitch
|
$endswitch
|
||||||
}
|
}
|
||||||
|
|
||||||
macro bool is_arrayptr_or_sub_of_char(bytes)
|
macro bool is_arrayptr_or_slice_of_char(bytes)
|
||||||
{
|
{
|
||||||
$switch (@typekind(bytes))
|
$switch (@typekind(bytes))
|
||||||
$case POINTER:
|
$case POINTER:
|
||||||
@@ -171,7 +171,7 @@ macro bool is_arrayptr_or_sub_of_char(bytes)
|
|||||||
var $Inner2 = $typefrom($Inner.inner);
|
var $Inner2 = $typefrom($Inner.inner);
|
||||||
return $Inner2.typeid == char.typeid;
|
return $Inner2.typeid == char.typeid;
|
||||||
$endif
|
$endif
|
||||||
$case SUBARRAY:
|
$case SLICE:
|
||||||
var $Inner = $typefrom($typeof(bytes).inner);
|
var $Inner = $typefrom($typeof(bytes).inner);
|
||||||
return $Inner.typeid == char.typeid;
|
return $Inner.typeid == char.typeid;
|
||||||
$default:
|
$default:
|
||||||
|
|||||||
@@ -7,26 +7,17 @@ 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.
|
* Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds.
|
||||||
**/
|
**/
|
||||||
fault IteratorResult
|
fault IteratorResult { NO_MORE_ELEMENT }
|
||||||
{
|
|
||||||
NO_MORE_ELEMENT
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use `SearchResult` when trying to return a value from some collection but the element is missing.
|
* Use `SearchResult` when trying to return a value from some collection but the element is missing.
|
||||||
**/
|
**/
|
||||||
fault SearchResult
|
fault SearchResult { MISSING }
|
||||||
{
|
|
||||||
MISSING
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use `CastResult` when an attempt at conversion fails.
|
* Use `CastResult` when an attempt at conversion fails.
|
||||||
**/
|
**/
|
||||||
fault CastResult
|
fault CastResult { TYPE_MISMATCH }
|
||||||
{
|
|
||||||
TYPE_MISMATCH
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores a variable on the stack, then restores it at the end of the
|
* Stores a variable on the stack, then restores it at the end of the
|
||||||
@@ -61,7 +52,7 @@ macro void @swap(&a, &b) @builtin
|
|||||||
* @ensure @typeis(return, $Type*)
|
* @ensure @typeis(return, $Type*)
|
||||||
* @return! CastResult.TYPE_MISMATCH
|
* @return! CastResult.TYPE_MISMATCH
|
||||||
**/
|
**/
|
||||||
macro anycast(any* v, $Type) @builtin
|
macro anycast(any v, $Type) @builtin
|
||||||
{
|
{
|
||||||
if (v.type != $Type.typeid) return CastResult.TYPE_MISMATCH?;
|
if (v.type != $Type.typeid) return CastResult.TYPE_MISMATCH?;
|
||||||
return ($Type*)v.ptr;
|
return ($Type*)v.ptr;
|
||||||
@@ -83,17 +74,18 @@ fn bool print_backtrace(String message, int backtraces_to_ignore) @if(env::NATIV
|
|||||||
foreach (i, &trace : backtrace)
|
foreach (i, &trace : backtrace)
|
||||||
{
|
{
|
||||||
if (i < backtraces_to_ignore) continue;
|
if (i < backtraces_to_ignore) continue;
|
||||||
|
String inline_suffix = trace.is_inline ? " [inline]" : "";
|
||||||
if (trace.is_unknown())
|
if (trace.is_unknown())
|
||||||
{
|
{
|
||||||
io::eprintn(" in ???");
|
io::eprintfn(" in ???%s", inline_suffix);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (trace.has_file())
|
if (trace.has_file())
|
||||||
{
|
{
|
||||||
io::eprintfn(" in %s (%s:%d) [%s]", trace.function, trace.file, trace.line, trace.object_file);
|
io::eprintfn(" in %s (%s:%d) [%s]%s", trace.function, trace.file, trace.line, trace.object_file, inline_suffix);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
io::eprintfn(" in %s (source unavailable) [%s]", trace.function, trace.object_file);
|
io::eprintfn(" in %s (source unavailable) [%s]%s", trace.function, trace.object_file, inline_suffix);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
@@ -126,7 +118,7 @@ PanicFn panic = &default_panic;
|
|||||||
|
|
||||||
fn void panicf(String fmt, String file, String function, uint line, args...)
|
fn void panicf(String fmt, String file, String function, uint line, args...)
|
||||||
{
|
{
|
||||||
@stack_mem(512; Allocator* allocator)
|
@stack_mem(512; Allocator allocator)
|
||||||
{
|
{
|
||||||
DString s;
|
DString s;
|
||||||
s.new_init(.allocator = allocator);
|
s.new_init(.allocator = allocator);
|
||||||
@@ -142,7 +134,9 @@ fn void panicf(String fmt, String file, String function, uint line, args...)
|
|||||||
**/
|
**/
|
||||||
macro void unreachable(String string = "Unreachable statement reached.", ...) @builtin @noreturn
|
macro void unreachable(String string = "Unreachable statement reached.", ...) @builtin @noreturn
|
||||||
{
|
{
|
||||||
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat());
|
$if env::COMPILER_SAFE_MODE:
|
||||||
|
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat);
|
||||||
|
$endif;
|
||||||
$$unreachable();
|
$$unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,10 +146,18 @@ macro void unreachable(String string = "Unreachable statement reached.", ...) @b
|
|||||||
**/
|
**/
|
||||||
macro void unsupported(String string = "Unsupported function invoked") @builtin @noreturn
|
macro void unsupported(String string = "Unsupported function invoked") @builtin @noreturn
|
||||||
{
|
{
|
||||||
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat());
|
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat);
|
||||||
$$unreachable();
|
$$unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unconditionally break into an attached debugger when reached.
|
||||||
|
**/
|
||||||
|
macro void breakpoint() @builtin
|
||||||
|
{
|
||||||
|
$$breakpoint();
|
||||||
|
}
|
||||||
|
|
||||||
macro any_make(void* ptr, typeid type) @builtin
|
macro any_make(void* ptr, typeid type) @builtin
|
||||||
{
|
{
|
||||||
return $$any_make(ptr, type);
|
return $$any_make(ptr, type);
|
||||||
@@ -289,12 +291,12 @@ macro @prefetch(void* ptr, PrefetchLocality $locality = VERY_NEAR, bool $write =
|
|||||||
|
|
||||||
macro swizzle(v, ...) @builtin
|
macro swizzle(v, ...) @builtin
|
||||||
{
|
{
|
||||||
return $$swizzle(v, $vasplat());
|
return $$swizzle(v, $vasplat);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro swizzle2(v, v2, ...) @builtin
|
macro swizzle2(v, v2, ...) @builtin
|
||||||
{
|
{
|
||||||
return $$swizzle2(v, v2, $vasplat());
|
return $$swizzle2(v, v2, $vasplat);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro anyfault @catch(#expr) @builtin
|
macro anyfault @catch(#expr) @builtin
|
||||||
@@ -314,6 +316,11 @@ 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);
|
||||||
|
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 int.hash(int i) => i;
|
||||||
macro uint uint.hash(uint i) => i;
|
macro uint uint.hash(uint i) => i;
|
||||||
macro uint short.hash(short s) => s;
|
macro uint short.hash(short s) => s;
|
||||||
@@ -330,6 +337,11 @@ macro uint String.hash(String c) => (uint)fnv32a::encode(c);
|
|||||||
macro uint char[].hash(char[] 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) => ((ulong)(uptr)ptr).hash();
|
||||||
|
|
||||||
|
distinct EmptySlot = void*;
|
||||||
|
const EmptySlot EMPTY_MACRO_SLOT @builtin = null;
|
||||||
|
macro @is_empty_macro_slot(#arg) @builtin => @typeis(#arg, EmptySlot);
|
||||||
|
macro @is_valid_macro_slot(#arg) @builtin => !@typeis(#arg, EmptySlot);
|
||||||
|
|
||||||
const MAX_FRAMEADDRESS = 128;
|
const MAX_FRAMEADDRESS = 128;
|
||||||
/**
|
/**
|
||||||
* @require n >= 0
|
* @require n >= 0
|
||||||
@@ -662,7 +674,7 @@ fn void install_signal_handler(CInt signal, SignalFunction func) @local
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clean this up
|
// Clean this up
|
||||||
fn void install_signal_handlers() @init(101) @local
|
fn void install_signal_handlers() @init(101) @local @if(env::BACKTRACE)
|
||||||
{
|
{
|
||||||
install_signal_handler(libc::SIGBUS, &sig_bus_error);
|
install_signal_handler(libc::SIGBUS, &sig_bus_error);
|
||||||
install_signal_handler(libc::SIGSEGV, &sig_segmentation_fault);
|
install_signal_handler(libc::SIGSEGV, &sig_segmentation_fault);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
module std::core::builtin;
|
module std::core::builtin;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
|
* @require types::@comparable_value(a) && types::@comparable_value(b)
|
||||||
**/
|
**/
|
||||||
macro less(a, b) @builtin
|
macro less(a, b) @builtin
|
||||||
{
|
{
|
||||||
@@ -19,7 +19,7 @@ macro less(a, b) @builtin
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
|
* @require types::@comparable_value(a) && types::@comparable_value(b)
|
||||||
**/
|
**/
|
||||||
macro less_eq(a, b) @builtin
|
macro less_eq(a, b) @builtin
|
||||||
{
|
{
|
||||||
@@ -34,7 +34,7 @@ macro less_eq(a, b) @builtin
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
|
* @require types::@comparable_value(a) && types::@comparable_value(b)
|
||||||
**/
|
**/
|
||||||
macro greater(a, b) @builtin
|
macro greater(a, b) @builtin
|
||||||
{
|
{
|
||||||
@@ -49,7 +49,7 @@ macro greater(a, b) @builtin
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
|
* @require types::@comparable_value(a) && types::@comparable_value(b)
|
||||||
**/
|
**/
|
||||||
macro int compare_to(a, b) @builtin
|
macro int compare_to(a, b) @builtin
|
||||||
{
|
{
|
||||||
@@ -63,7 +63,7 @@ macro int compare_to(a, b) @builtin
|
|||||||
$endswitch
|
$endswitch
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
|
* @require types::@comparable_value(a) && types::@comparable_value(b)
|
||||||
**/
|
**/
|
||||||
macro greater_eq(a, b) @builtin
|
macro greater_eq(a, b) @builtin
|
||||||
{
|
{
|
||||||
@@ -78,7 +78,7 @@ macro greater_eq(a, b) @builtin
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require types::is_equatable_value(a) && types::is_equatable_value(b) `values must be equatable`
|
* @require types::@equatable_value(a) && types::@equatable_value(b) `values must be equatable`
|
||||||
**/
|
**/
|
||||||
macro bool equals(a, b) @builtin
|
macro bool equals(a, b) @builtin
|
||||||
{
|
{
|
||||||
@@ -97,13 +97,13 @@ macro bool equals(a, b) @builtin
|
|||||||
macro min(x, ...) @builtin
|
macro min(x, ...) @builtin
|
||||||
{
|
{
|
||||||
$if $vacount == 1:
|
$if $vacount == 1:
|
||||||
return less(x, $vaarg(0)) ? x : $vaarg(0);
|
return less(x, $vaarg[0]) ? x : $vaarg[0];
|
||||||
$else
|
$else
|
||||||
var result = x;
|
var result = x;
|
||||||
$for (var $i = 0; $i < $vacount; $i++)
|
$for (var $i = 0; $i < $vacount; $i++)
|
||||||
if (less($vaarg($i), result))
|
if (less($vaarg[$i], result))
|
||||||
{
|
{
|
||||||
result = $vaarg($i);
|
result = $vaarg[$i];
|
||||||
}
|
}
|
||||||
$endfor
|
$endfor
|
||||||
return result;
|
return result;
|
||||||
@@ -113,13 +113,13 @@ macro min(x, ...) @builtin
|
|||||||
macro max(x, ...) @builtin
|
macro max(x, ...) @builtin
|
||||||
{
|
{
|
||||||
$if $vacount == 1:
|
$if $vacount == 1:
|
||||||
return greater(x, $vaarg(0)) ? x : $vaarg(0);
|
return greater(x, $vaarg[0]) ? x : $vaarg[0];
|
||||||
$else
|
$else
|
||||||
var result = x;
|
var result = x;
|
||||||
$for (var $i = 0; $i < $vacount; $i++)
|
$for (var $i = 0; $i < $vacount; $i++)
|
||||||
if (greater($vaarg($i), result))
|
if (greater($vaarg[$i], result))
|
||||||
{
|
{
|
||||||
result = $vaarg($i);
|
result = $vaarg[$i];
|
||||||
}
|
}
|
||||||
$endfor
|
$endfor
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -108,14 +108,14 @@ fn usz char32_to_utf8_unsafe(Char32 c, char** output)
|
|||||||
{
|
{
|
||||||
switch
|
switch
|
||||||
{
|
{
|
||||||
case c < 0x7f:
|
case c <= 0x7f:
|
||||||
(*output)++[0] = (char)c;
|
(*output)++[0] = (char)c;
|
||||||
return 1;
|
return 1;
|
||||||
case c < 0x7ff:
|
case c <= 0x7ff:
|
||||||
(*output)++[0] = (char)(0xC0 | c >> 6);
|
(*output)++[0] = (char)(0xC0 | c >> 6);
|
||||||
(*output)++[0] = (char)(0x80 | (c & 0x3F));
|
(*output)++[0] = (char)(0x80 | (c & 0x3F));
|
||||||
return 2;
|
return 2;
|
||||||
case c < 0xffff:
|
case c <= 0xffff:
|
||||||
(*output)++[0] = (char)(0xE0 | c >> 12);
|
(*output)++[0] = (char)(0xE0 | c >> 12);
|
||||||
(*output)++[0] = (char)(0x80 | (c >> 6 & 0x3F));
|
(*output)++[0] = (char)(0x80 | (c >> 6 & 0x3F));
|
||||||
(*output)++[0] = (char)(0x80 | (c & 0x3F));
|
(*output)++[0] = (char)(0x80 | (c & 0x3F));
|
||||||
@@ -210,11 +210,11 @@ fn usz utf8len_for_utf32(Char32[] utf32)
|
|||||||
{
|
{
|
||||||
switch (true)
|
switch (true)
|
||||||
{
|
{
|
||||||
case uc < 0x7f:
|
case uc <= 0x7f:
|
||||||
len++;
|
len++;
|
||||||
case uc < 0x7ff:
|
case uc <= 0x7ff:
|
||||||
len += 2;
|
len += 2;
|
||||||
case uc < 0xffff:
|
case uc <= 0xffff:
|
||||||
len += 3;
|
len += 3;
|
||||||
default:
|
default:
|
||||||
len += 4;
|
len += 4;
|
||||||
@@ -237,12 +237,12 @@ fn usz utf8len_for_utf16(Char16[] utf16)
|
|||||||
Char16 c = utf16[i];
|
Char16 c = utf16[i];
|
||||||
if (c & UTF16_SURROGATE_GENERIC_MASK != UTF16_SURROGATE_GENERIC_VALUE)
|
if (c & UTF16_SURROGATE_GENERIC_MASK != UTF16_SURROGATE_GENERIC_VALUE)
|
||||||
{
|
{
|
||||||
if (c < 0x7f)
|
if (c <= 0x7f)
|
||||||
{
|
{
|
||||||
len++;
|
len++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (c < 0x7ff)
|
if (c <= 0x7ff)
|
||||||
{
|
{
|
||||||
len += 2;
|
len += 2;
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const usz MIN_CAPACITY @private = 16;
|
|||||||
/**
|
/**
|
||||||
* @require !self.data() "String already initialized"
|
* @require !self.data() "String already initialized"
|
||||||
**/
|
**/
|
||||||
fn DString DString.new_init(&self, usz capacity = MIN_CAPACITY, Allocator* allocator = allocator::heap())
|
fn DString DString.new_init(&self, usz capacity = MIN_CAPACITY, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
|
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
|
||||||
StringData* data = allocator::alloc_with_padding(allocator, StringData, capacity)!!;
|
StringData* data = allocator::alloc_with_padding(allocator, StringData, capacity)!!;
|
||||||
@@ -18,14 +18,6 @@ fn DString DString.new_init(&self, usz capacity = MIN_CAPACITY, Allocator* alloc
|
|||||||
return *self = (DString)data;
|
return *self = (DString)data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @require !self.data() "String already initialized"
|
|
||||||
**/
|
|
||||||
fn DString DString.init_new(&self, usz capacity = MIN_CAPACITY, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
|
|
||||||
{
|
|
||||||
return self.new_init(capacity, allocator) @inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require !self.data() "String already initialized"
|
* @require !self.data() "String already initialized"
|
||||||
**/
|
**/
|
||||||
@@ -35,22 +27,14 @@ fn DString DString.temp_init(&self, usz capacity = MIN_CAPACITY)
|
|||||||
return *self;
|
return *self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
fn DString new_with_capacity(usz capacity, Allocator allocator = allocator::heap())
|
||||||
* @require !self.data() "String already initialized"
|
|
||||||
**/
|
|
||||||
fn DString DString.init_temp(&self, usz capacity = MIN_CAPACITY) @deprecated("Replaced by temp_init")
|
|
||||||
{
|
|
||||||
return self.temp_init(capacity) @inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn DString new_with_capacity(usz capacity, Allocator* allocator = allocator::heap())
|
|
||||||
{
|
{
|
||||||
return DString{}.new_init(capacity, allocator);
|
return DString{}.new_init(capacity, allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn DString temp_with_capacity(usz capacity) => new_with_capacity(capacity, allocator::temp()) @inline;
|
fn DString temp_with_capacity(usz capacity) => new_with_capacity(capacity, allocator::temp()) @inline;
|
||||||
|
|
||||||
fn DString new(String c = "", Allocator* allocator = allocator::heap())
|
fn DString new(String c = "", Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
usz len = c.len;
|
usz len = c.len;
|
||||||
StringData* data = (StringData*)new_with_capacity(len, allocator);
|
StringData* data = (StringData*)new_with_capacity(len, allocator);
|
||||||
@@ -64,7 +48,57 @@ fn DString new(String c = "", Allocator* allocator = allocator::heap())
|
|||||||
|
|
||||||
fn DString temp_new(String s = "") => new(s, allocator::temp()) @inline;
|
fn DString temp_new(String s = "") => new(s, allocator::temp()) @inline;
|
||||||
|
|
||||||
fn DString DString.new_concat(self, DString b, Allocator* allocator = allocator::heap())
|
|
||||||
|
fn void DString.replace_char(self, char ch, char replacement)
|
||||||
|
{
|
||||||
|
StringData* data = self.data();
|
||||||
|
foreach (&c : data.chars[:data.len])
|
||||||
|
{
|
||||||
|
if (*c == ch) *c = replacement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void DString.replace(&self, String needle, String replacement)
|
||||||
|
{
|
||||||
|
StringData* data = self.data();
|
||||||
|
usz needle_len = needle.len;
|
||||||
|
if (!data || data.len < needle_len) return;
|
||||||
|
usz replace_len = replacement.len;
|
||||||
|
if (needle_len == 1 && replace_len == 1)
|
||||||
|
{
|
||||||
|
self.replace_char(needle[0], replacement[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
@pool(data.allocator) {
|
||||||
|
String str = self.tcopy_str();
|
||||||
|
self.clear();
|
||||||
|
usz len = str.len;
|
||||||
|
usz match = 0;
|
||||||
|
foreach (i, c : str)
|
||||||
|
{
|
||||||
|
if (c == needle[match])
|
||||||
|
{
|
||||||
|
match++;
|
||||||
|
if (match == needle_len)
|
||||||
|
{
|
||||||
|
self.append_chars(replacement);
|
||||||
|
match = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (match > 0)
|
||||||
|
{
|
||||||
|
self.append_chars(str[i - match:match]);
|
||||||
|
match = 0;
|
||||||
|
}
|
||||||
|
self.append_char(c);
|
||||||
|
}
|
||||||
|
if (match > 0) self.append_chars(str[^match:match]);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn DString DString.new_concat(self, DString b, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
DString string;
|
DString string;
|
||||||
string.new_init(self.len() + b.len(), allocator);
|
string.new_init(self.len() + b.len(), allocator);
|
||||||
@@ -75,8 +109,6 @@ fn DString DString.new_concat(self, DString b, Allocator* allocator = allocator:
|
|||||||
|
|
||||||
fn DString DString.temp_concat(self, DString b) => self.new_concat(b, allocator::temp());
|
fn DString DString.temp_concat(self, DString b) => self.new_concat(b, allocator::temp());
|
||||||
|
|
||||||
fn DString DString.new_tconcat(self, DString b) @deprecated("Replaced by temp_concat") => self.new_concat(b, allocator::temp());
|
|
||||||
|
|
||||||
fn ZString DString.zstr_view(&self)
|
fn ZString DString.zstr_view(&self)
|
||||||
{
|
{
|
||||||
StringData* data = self.data();
|
StringData* data = self.data();
|
||||||
@@ -166,7 +198,7 @@ fn void DString.append_char32(&self, Char32 c)
|
|||||||
|
|
||||||
fn DString DString.tcopy(&self) => self.copy(allocator::temp());
|
fn DString DString.tcopy(&self) => self.copy(allocator::temp());
|
||||||
|
|
||||||
fn DString DString.copy(self, Allocator* allocator = null)
|
fn DString DString.copy(self, Allocator allocator = null)
|
||||||
{
|
{
|
||||||
if (!self)
|
if (!self)
|
||||||
{
|
{
|
||||||
@@ -180,7 +212,7 @@ fn DString DString.copy(self, Allocator* allocator = null)
|
|||||||
return new_string;
|
return new_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ZString DString.copy_zstr(self, Allocator* allocator = allocator::heap())
|
fn ZString DString.copy_zstr(self, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
usz str_len = self.len();
|
usz str_len = self.len();
|
||||||
if (!str_len)
|
if (!str_len)
|
||||||
@@ -194,7 +226,7 @@ fn ZString DString.copy_zstr(self, Allocator* allocator = allocator::heap())
|
|||||||
return (ZString)zstr;
|
return (ZString)zstr;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String DString.copy_str(self, Allocator* allocator = allocator::heap())
|
fn String DString.copy_str(self, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
return (String)self.copy_zstr(allocator)[:self.len()];
|
return (String)self.copy_zstr(allocator)[:self.len()];
|
||||||
}
|
}
|
||||||
@@ -258,7 +290,7 @@ fn void DString.append_chars(&self, String str)
|
|||||||
data.len += other_len;
|
data.len += other_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Char32[] DString.copy_utf32(&self, Allocator* allocator = allocator::heap())
|
fn Char32[] DString.copy_utf32(&self, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
return self.str_view().to_new_utf32(allocator) @inline!!;
|
return self.str_view().to_new_utf32(allocator) @inline!!;
|
||||||
}
|
}
|
||||||
@@ -389,21 +421,29 @@ fn void DString.insert_at(&self, usz index, String s)
|
|||||||
|
|
||||||
fn usz! DString.appendf(&self, String format, args...) @maydiscard
|
fn usz! DString.appendf(&self, String format, args...) @maydiscard
|
||||||
{
|
{
|
||||||
Formatter formatter;
|
if (!self.data()) self.new_init(format.len + 20);
|
||||||
formatter.init(&out_string_append_fn, self);
|
@pool(self.data().allocator)
|
||||||
return formatter.vprintf(format, args);
|
{
|
||||||
|
Formatter formatter;
|
||||||
|
formatter.init(&out_string_append_fn, self);
|
||||||
|
return formatter.vprintf(format, args);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usz! DString.appendfn(&self, String format, args...) @maydiscard
|
fn usz! DString.appendfn(&self, String format, args...) @maydiscard
|
||||||
{
|
{
|
||||||
Formatter formatter;
|
if (!self.data()) self.new_init(format.len + 20);
|
||||||
formatter.init(&out_string_append_fn, self);
|
@pool(self.data().allocator)
|
||||||
usz len = formatter.vprintf(format, args)!;
|
{
|
||||||
self.append('\n');
|
Formatter formatter;
|
||||||
return len + 1;
|
formatter.init(&out_string_append_fn, self);
|
||||||
|
usz len = formatter.vprintf(format, args)!;
|
||||||
|
self.append('\n');
|
||||||
|
return len + 1;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn DString new_join(String[] s, String joiner, Allocator* allocator = allocator::heap())
|
fn DString new_join(String[] s, String joiner, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
if (!s.len) return (DString)null;
|
if (!s.len) return (DString)null;
|
||||||
usz total_size = joiner.len * s.len;
|
usz total_size = joiner.len * s.len;
|
||||||
@@ -450,7 +490,7 @@ fn void DString.reserve(&self, usz addition)
|
|||||||
*self = (DString)allocator::realloc(data.allocator, data, StringData.sizeof + new_capacity);
|
*self = (DString)allocator::realloc(data.allocator, data, StringData.sizeof + new_capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usz! DString.read_from_stream(&self, InStream* reader)
|
fn usz! DString.read_from_stream(&self, InStream reader)
|
||||||
{
|
{
|
||||||
if (&reader.available)
|
if (&reader.available)
|
||||||
{
|
{
|
||||||
@@ -482,7 +522,7 @@ fn usz! DString.read_from_stream(&self, InStream* reader)
|
|||||||
|
|
||||||
struct StringData @private
|
struct StringData @private
|
||||||
{
|
{
|
||||||
Allocator* allocator;
|
Allocator allocator;
|
||||||
usz len;
|
usz len;
|
||||||
usz capacity;
|
usz capacity;
|
||||||
char[*] chars;
|
char[*] chars;
|
||||||
|
|||||||
@@ -112,10 +112,13 @@ enum ArchType
|
|||||||
WASM64, // WebAssembly with 64-bit pointers
|
WASM64, // WebAssembly with 64-bit pointers
|
||||||
RSCRIPT32, // 32-bit RenderScript
|
RSCRIPT32, // 32-bit RenderScript
|
||||||
RSCRIPT64, // 64-bit RenderScript
|
RSCRIPT64, // 64-bit RenderScript
|
||||||
|
XTENSA, // Xtensa
|
||||||
}
|
}
|
||||||
|
|
||||||
const OsType OS_TYPE = (OsType)$$OS_TYPE;
|
const OsType OS_TYPE = (OsType)$$OS_TYPE;
|
||||||
const ArchType ARCH_TYPE = (ArchType)$$ARCH_TYPE;
|
const ArchType ARCH_TYPE = (ArchType)$$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 LIBC = $$COMPILER_LIBC_AVAILABLE;
|
||||||
const bool NO_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)$$COMPILER_OPT_LEVEL;
|
||||||
@@ -123,8 +126,10 @@ const bool BIG_ENDIAN = $$PLATFORM_BIG_ENDIAN;
|
|||||||
const bool I128_NATIVE_SUPPORT = $$PLATFORM_I128_SUPPORTED;
|
const bool I128_NATIVE_SUPPORT = $$PLATFORM_I128_SUPPORTED;
|
||||||
const bool F16_SUPPORT = $$PLATFORM_F16_SUPPORTED;
|
const bool F16_SUPPORT = $$PLATFORM_F16_SUPPORTED;
|
||||||
const bool F128_SUPPORT = $$PLATFORM_F128_SUPPORTED;
|
const bool F128_SUPPORT = $$PLATFORM_F128_SUPPORTED;
|
||||||
|
const REGISTER_SIZE = $$REGISTER_SIZE;
|
||||||
const bool COMPILER_SAFE_MODE = $$COMPILER_SAFE_MODE;
|
const bool COMPILER_SAFE_MODE = $$COMPILER_SAFE_MODE;
|
||||||
const bool DEBUG_SYMBOLS = $$DEBUG_SYMBOLS;
|
const bool DEBUG_SYMBOLS = $$DEBUG_SYMBOLS;
|
||||||
|
const bool BACKTRACE = $$BACKTRACE;
|
||||||
const usz LLVM_VERSION = $$LLVM_VERSION;
|
const usz LLVM_VERSION = $$LLVM_VERSION;
|
||||||
const bool BENCHMARKING = $$BENCHMARKING;
|
const bool BENCHMARKING = $$BENCHMARKING;
|
||||||
const bool TESTING = $$TESTING;
|
const bool TESTING = $$TESTING;
|
||||||
@@ -144,7 +149,7 @@ const bool NETBSD = LIBC && OS_TYPE == NETBSD;
|
|||||||
const bool WASI = LIBC && OS_TYPE == WASI;
|
const bool WASI = LIBC && OS_TYPE == WASI;
|
||||||
const bool WASM_NOLIBC @builtin = !LIBC && ARCH_TYPE == ArchType.WASM32 || ARCH_TYPE == ArchType.WASM64;
|
const bool WASM_NOLIBC @builtin = !LIBC && ARCH_TYPE == ArchType.WASM32 || ARCH_TYPE == ArchType.WASM64;
|
||||||
|
|
||||||
macro bool os_is_darwin()
|
macro bool os_is_darwin() @const
|
||||||
{
|
{
|
||||||
$switch (OS_TYPE)
|
$switch (OS_TYPE)
|
||||||
$case IOS:
|
$case IOS:
|
||||||
@@ -157,7 +162,7 @@ macro bool os_is_darwin()
|
|||||||
$endswitch
|
$endswitch
|
||||||
}
|
}
|
||||||
|
|
||||||
macro bool os_is_posix()
|
macro bool os_is_posix() @const
|
||||||
{
|
{
|
||||||
$switch (OS_TYPE)
|
$switch (OS_TYPE)
|
||||||
$case IOS:
|
$case IOS:
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ import std::math;
|
|||||||
const MAX_MEMORY_ALIGNMENT = 0x1000_0000;
|
const MAX_MEMORY_ALIGNMENT = 0x1000_0000;
|
||||||
const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2;
|
const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2;
|
||||||
|
|
||||||
|
macro bool @constant_is_power_of_2($x) @const @private
|
||||||
|
{
|
||||||
|
return $x != 0 && ($x & ($x - 1)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a vector from memory according to a mask assuming default alignment.
|
* Load a vector from memory according to a mask assuming default alignment.
|
||||||
@@ -37,7 +41,7 @@ macro masked_load(ptr, bool[<*>] mask, passthru)
|
|||||||
* @require $assignable(&&passthru, $typeof(ptr)) : "Pointer and passthru must match"
|
* @require $assignable(&&passthru, $typeof(ptr)) : "Pointer and passthru must match"
|
||||||
* @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
|
* @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
|
||||||
* @require passthru.len == mask.len : "Mask and passthru must have the same length"
|
* @require passthru.len == mask.len : "Mask and passthru must have the same length"
|
||||||
* @require math::is_power_of_2($alignment) : "The alignment must be a power of two"
|
* @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||||
*
|
*
|
||||||
* @return "A vector with the loaded values where the mask is true, passthru where the mask is false"
|
* @return "A vector with the loaded values where the mask is true, passthru where the mask is false"
|
||||||
**/
|
**/
|
||||||
@@ -80,7 +84,7 @@ macro gather(ptrvec, bool[<*>] mask, passthru)
|
|||||||
* @require $assignable(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match"
|
* @require $assignable(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match"
|
||||||
* @require passthru.len == mask.len : "Mask and passthru must have the same length"
|
* @require passthru.len == mask.len : "Mask and passthru must have the same length"
|
||||||
* @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
|
* @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
|
||||||
* @require math::is_power_of_2($alignment) : "The alignment must be a power of two"
|
* @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||||
*
|
*
|
||||||
* @return "A vector with the loaded values where the mask is true, passthru where the mask is false"
|
* @return "A vector with the loaded values where the mask is true, passthru where the mask is false"
|
||||||
**/
|
**/
|
||||||
@@ -115,7 +119,7 @@ macro masked_store(ptr, value, bool[<*>] mask)
|
|||||||
* @require $assignable(&&value, $typeof(ptr)) : "Pointer and value must match"
|
* @require $assignable(&&value, $typeof(ptr)) : "Pointer and value must match"
|
||||||
* @require @typekind(value) == VECTOR : "Expected value to be a vector"
|
* @require @typekind(value) == VECTOR : "Expected value to be a vector"
|
||||||
* @require value.len == mask.len : "Mask and value must have the same length"
|
* @require value.len == mask.len : "Mask and value must have the same length"
|
||||||
* @require math::is_power_of_2($alignment) : "The alignment must be a power of two"
|
* @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||||
*
|
*
|
||||||
**/
|
**/
|
||||||
macro @masked_store_aligned(ptr, value, bool[<*>] mask, usz $alignment)
|
macro @masked_store_aligned(ptr, value, bool[<*>] mask, usz $alignment)
|
||||||
@@ -150,13 +154,39 @@ macro scatter(ptrvec, value, bool[<*>] mask)
|
|||||||
* @require $assignable(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match"
|
* @require $assignable(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match"
|
||||||
* @require value.len == mask.len : "Mask and value must have the same length"
|
* @require value.len == mask.len : "Mask and value must have the same length"
|
||||||
* @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
|
* @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
|
||||||
* @require math::is_power_of_2($alignment) : "The alignment must be a power of two"
|
* @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||||
**/
|
**/
|
||||||
macro @scatter_aligned(ptrvec, value, bool[<*>] mask, usz $alignment)
|
macro @scatter_aligned(ptrvec, value, bool[<*>] mask, usz $alignment)
|
||||||
{
|
{
|
||||||
return $$scatter(ptrvec, value, mask, $alignment);
|
return $$scatter(ptrvec, value, mask, $alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param [in] x "The variable or dereferenced pointer to load."
|
||||||
|
* @param $alignment "The alignment to assume for the load"
|
||||||
|
* @return "The value of x"
|
||||||
|
*
|
||||||
|
* @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||||
|
**/
|
||||||
|
macro @unaligned_load(&x, usz $alignment) @builtin
|
||||||
|
{
|
||||||
|
return $$unaligned_load(x, $alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param [out] 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"
|
||||||
|
*
|
||||||
|
* @require $assignable(value, $typeof(*x)) : "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
|
||||||
|
{
|
||||||
|
return $$unaligned_store(x, ($typeof(*x))value, $alignment);
|
||||||
|
}
|
||||||
|
|
||||||
macro @volatile_load(&x) @builtin
|
macro @volatile_load(&x) @builtin
|
||||||
{
|
{
|
||||||
return $$volatile_load(x);
|
return $$volatile_load(x);
|
||||||
@@ -343,9 +373,9 @@ macro void set_inline(void* dst, char val, usz $len, usz $dst_align = 0, bool $i
|
|||||||
$$memset_inline(dst, val, $len, $is_volatile, $dst_align);
|
$$memset_inline(dst, val, $len, $is_volatile, $dst_align);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @require values::@inner_kind(a) == TypeKind.SUBARRAY || values::@inner_kind(a) == TypeKind.POINTER
|
* @require values::@inner_kind(a) == TypeKind.SLICE || values::@inner_kind(a) == TypeKind.POINTER
|
||||||
* @require values::@inner_kind(b) == TypeKind.SUBARRAY || values::@inner_kind(b) == TypeKind.POINTER
|
* @require values::@inner_kind(b) == TypeKind.SLICE || values::@inner_kind(b) == TypeKind.POINTER
|
||||||
* @require values::@inner_kind(a) != TypeKind.SUBARRAY || len == -1
|
* @require values::@inner_kind(a) != TypeKind.SLICE || len == -1
|
||||||
* @require values::@inner_kind(a) != TypeKind.POINTER || len > -1
|
* @require values::@inner_kind(a) != TypeKind.POINTER || len > -1
|
||||||
* @require values::@assign_to(a, b) && values::@assign_to(b, a)
|
* @require values::@assign_to(a, b) && values::@assign_to(b, a)
|
||||||
**/
|
**/
|
||||||
@@ -356,7 +386,7 @@ macro bool equals(a, b, isz len = -1, usz $align = 0)
|
|||||||
$endif
|
$endif
|
||||||
void* x @noinit;
|
void* x @noinit;
|
||||||
void* y @noinit;
|
void* y @noinit;
|
||||||
$if values::@inner_kind(a) == TypeKind.SUBARRAY:
|
$if values::@inner_kind(a) == TypeKind.SLICE:
|
||||||
len = a.len;
|
len = a.len;
|
||||||
if (len != b.len) return false;
|
if (len != b.len) return false;
|
||||||
x = a.ptr;
|
x = a.ptr;
|
||||||
@@ -403,9 +433,9 @@ macro type_alloc_must_be_aligned($Type)
|
|||||||
/**
|
/**
|
||||||
* Run with a specific allocator inside of the macro body.
|
* Run with a specific allocator inside of the macro body.
|
||||||
**/
|
**/
|
||||||
macro void @scoped(Allocator* allocator; @body())
|
macro void @scoped(Allocator allocator; @body())
|
||||||
{
|
{
|
||||||
Allocator* old_allocator = allocator::thread_allocator;
|
Allocator old_allocator = allocator::thread_allocator;
|
||||||
allocator::thread_allocator = allocator;
|
allocator::thread_allocator = allocator;
|
||||||
defer allocator::thread_allocator = old_allocator;
|
defer allocator::thread_allocator = old_allocator;
|
||||||
@body();
|
@body();
|
||||||
@@ -415,7 +445,7 @@ macro void @report_heap_allocs_in_scope(;@body())
|
|||||||
{
|
{
|
||||||
TrackingAllocator tracker;
|
TrackingAllocator tracker;
|
||||||
tracker.init(allocator::thread_allocator);
|
tracker.init(allocator::thread_allocator);
|
||||||
Allocator* old_allocator = allocator::thread_allocator;
|
Allocator old_allocator = allocator::thread_allocator;
|
||||||
allocator::thread_allocator = &tracker;
|
allocator::thread_allocator = &tracker;
|
||||||
defer
|
defer
|
||||||
{
|
{
|
||||||
@@ -426,7 +456,7 @@ macro void @report_heap_allocs_in_scope(;@body())
|
|||||||
@body();
|
@body();
|
||||||
}
|
}
|
||||||
|
|
||||||
macro void @stack_mem(usz $size; @body(Allocator* mem)) @builtin
|
macro void @stack_mem(usz $size; @body(Allocator mem)) @builtin
|
||||||
{
|
{
|
||||||
char[$size] buffer;
|
char[$size] buffer;
|
||||||
OnStackAllocator allocator;
|
OnStackAllocator allocator;
|
||||||
@@ -498,19 +528,11 @@ macro void @pool(TempAllocator* #other_temp = null; @body) @builtin
|
|||||||
@body();
|
@body();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import libc;
|
import libc;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
macro TempAllocator* temp() @deprecated("Use allocator::temp()") => allocator::temp();
|
|
||||||
macro Allocator* current_allocator() @deprecated("Use allocator::heap()") => allocator::heap();
|
|
||||||
macro Allocator* heap() @deprecated("Use allocator::heap()") => allocator::heap();
|
|
||||||
|
|
||||||
module std::core::mem @if(WASM_NOLIBC);
|
module std::core::mem @if(WASM_NOLIBC);
|
||||||
|
import std::core::mem::allocator @public;
|
||||||
SimpleHeapAllocator wasm_allocator @private;
|
SimpleHeapAllocator wasm_allocator @private;
|
||||||
extern int __heap_base;
|
extern int __heap_base;
|
||||||
|
|
||||||
@@ -521,8 +543,8 @@ fn void initialize_wasm_mem() @init(1) @private
|
|||||||
uptr start = (uptr)&__heap_base;
|
uptr start = (uptr)&__heap_base;
|
||||||
if (start > mem::DEFAULT_MEM_ALIGNMENT) allocator::wasm_memory.use = start;
|
if (start > mem::DEFAULT_MEM_ALIGNMENT) allocator::wasm_memory.use = start;
|
||||||
wasm_allocator.init(fn (x) => allocator::wasm_memory.allocate_block(x));
|
wasm_allocator.init(fn (x) => allocator::wasm_memory.allocate_block(x));
|
||||||
temp_base_allocator = &wasm_allocator;
|
|
||||||
allocator::thread_allocator = &wasm_allocator;
|
allocator::thread_allocator = &wasm_allocator;
|
||||||
|
allocator::init_default_temp_allocators();
|
||||||
}
|
}
|
||||||
|
|
||||||
module std::core::mem;
|
module std::core::mem;
|
||||||
@@ -552,15 +574,24 @@ fn void* malloc(usz size) @builtin @inline @nodiscard
|
|||||||
return allocator::malloc(allocator::heap(), size);
|
return allocator::malloc(allocator::heap(), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void* tmalloc(usz size, usz alignment = 0, usz offset = 0) @builtin @inline @nodiscard
|
/**
|
||||||
|
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
|
||||||
|
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
|
||||||
|
**/
|
||||||
|
fn void* malloc_aligned(usz size, usz alignment) @builtin @inline @nodiscard
|
||||||
|
{
|
||||||
|
return allocator::malloc_aligned(allocator::heap(), size, alignment)!!;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void* tmalloc(usz size, usz alignment = 0) @builtin @inline @nodiscard
|
||||||
{
|
{
|
||||||
if (!size) return null;
|
if (!size) return null;
|
||||||
return allocator::temp().acquire(size, false, alignment, 0)!!;
|
return allocator::temp().acquire(size, NO_ZERO, alignment)!!;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require $vacount < 2 : "Too many arguments."
|
* @require $vacount < 2 : "Too many arguments."
|
||||||
* @require $or($vacount == 0, $assignable($vaexpr(0), $Type)) : "The second argument must be an initializer for the type"
|
* @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||||
* @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
|
* @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
|
||||||
**/
|
**/
|
||||||
macro new($Type, ...) @nodiscard
|
macro new($Type, ...) @nodiscard
|
||||||
@@ -569,7 +600,7 @@ macro new($Type, ...) @nodiscard
|
|||||||
return ($Type*)calloc($Type.sizeof);
|
return ($Type*)calloc($Type.sizeof);
|
||||||
$else
|
$else
|
||||||
$Type* val = malloc($Type.sizeof);
|
$Type* val = malloc($Type.sizeof);
|
||||||
*val = $vaexpr(0);
|
*val = $vaexpr[0];
|
||||||
return val;
|
return val;
|
||||||
$endif
|
$endif
|
||||||
}
|
}
|
||||||
@@ -578,7 +609,7 @@ macro new($Type, ...) @nodiscard
|
|||||||
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
|
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
|
||||||
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
|
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
|
||||||
* @require $vacount < 2 : "Too many arguments."
|
* @require $vacount < 2 : "Too many arguments."
|
||||||
* @require $or($vacount == 0, $assignable($vaexpr(0), $Type)) : "The second argument must be an initializer for the type"
|
* @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||||
**/
|
**/
|
||||||
macro new_aligned($Type, ...) @nodiscard
|
macro new_aligned($Type, ...) @nodiscard
|
||||||
{
|
{
|
||||||
@@ -586,7 +617,7 @@ macro new_aligned($Type, ...) @nodiscard
|
|||||||
return ($Type*)calloc_aligned($Type.sizeof, $Type.alignof);
|
return ($Type*)calloc_aligned($Type.sizeof, $Type.alignof);
|
||||||
$else
|
$else
|
||||||
$Type* val = malloc_aligned($Type.sizeof, $Type.alignof);
|
$Type* val = malloc_aligned($Type.sizeof, $Type.alignof);
|
||||||
*val = $vaexpr(0);
|
*val = $vaexpr[0];
|
||||||
return val;
|
return val;
|
||||||
$endif
|
$endif
|
||||||
}
|
}
|
||||||
@@ -608,19 +639,9 @@ macro alloc_aligned($Type) @nodiscard
|
|||||||
return ($Type*)malloc_aligned($Type.sizeof, $Type.alignof);
|
return ($Type*)malloc_aligned($Type.sizeof, $Type.alignof);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro new_clear($Type) @deprecated("Use mem::new")
|
|
||||||
{
|
|
||||||
return new($Type);
|
|
||||||
}
|
|
||||||
|
|
||||||
macro new_temp($Type) @deprecated("Use mem::temp_alloc or mem::temp_new")
|
|
||||||
{
|
|
||||||
return tmalloc($Type.sizeof);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require $vacount < 2 : "Too many arguments."
|
* @require $vacount < 2 : "Too many arguments."
|
||||||
* @require $or($vacount == 0, $assignable($vaexpr(0), $Type)) : "The second argument must be an initializer for the type"
|
* @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||||
**/
|
**/
|
||||||
macro temp_new($Type, ...) @nodiscard
|
macro temp_new($Type, ...) @nodiscard
|
||||||
{
|
{
|
||||||
@@ -628,7 +649,7 @@ macro temp_new($Type, ...) @nodiscard
|
|||||||
return ($Type*)tcalloc($Type.sizeof) @inline;
|
return ($Type*)tcalloc($Type.sizeof) @inline;
|
||||||
$else
|
$else
|
||||||
$Type* val = tmalloc($Type.sizeof) @inline;
|
$Type* val = tmalloc($Type.sizeof) @inline;
|
||||||
*val = $vaexpr(0);
|
*val = $vaexpr[0];
|
||||||
return val;
|
return val;
|
||||||
$endif
|
$endif
|
||||||
}
|
}
|
||||||
@@ -638,12 +659,6 @@ macro temp_alloc($Type) @nodiscard
|
|||||||
return tmalloc($Type.sizeof);
|
return tmalloc($Type.sizeof);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro new_temp_clear($Type) @deprecated("use mem::temp_new")
|
|
||||||
{
|
|
||||||
return tcalloc($Type.sizeof);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_array_aligned' instead"
|
* @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_array_aligned' instead"
|
||||||
@@ -676,12 +691,7 @@ macro alloc_array($Type, usz elements) @nodiscard
|
|||||||
**/
|
**/
|
||||||
macro alloc_array_aligned($Type, usz elements) @nodiscard
|
macro alloc_array_aligned($Type, usz elements) @nodiscard
|
||||||
{
|
{
|
||||||
return allocator::alloc_array(allocator::heap(), $Type, elements);
|
return allocator::alloc_array_aligned(allocator::heap(), $Type, elements);
|
||||||
}
|
|
||||||
|
|
||||||
macro talloc_array($Type, usz elements) @nodiscard @deprecated("use mem::temp_alloc_array")
|
|
||||||
{
|
|
||||||
return temp_alloc_array($Type, elements);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro temp_alloc_array($Type, usz elements) @nodiscard
|
macro temp_alloc_array($Type, usz elements) @nodiscard
|
||||||
@@ -689,40 +699,29 @@ macro temp_alloc_array($Type, usz elements) @nodiscard
|
|||||||
return (($Type*)tmalloc($Type.sizeof * elements, $Type.alignof))[:elements];
|
return (($Type*)tmalloc($Type.sizeof * elements, $Type.alignof))[:elements];
|
||||||
}
|
}
|
||||||
|
|
||||||
macro temp_array($Type, usz elements) @nodiscard @deprecated("use mem::temp_alloc_array")
|
|
||||||
{
|
|
||||||
return temp_alloc_array($Type, elements);
|
|
||||||
}
|
|
||||||
|
|
||||||
macro temp_new_array($Type, usz elements) @nodiscard
|
macro temp_new_array($Type, usz elements) @nodiscard
|
||||||
{
|
{
|
||||||
return (($Type*)tcalloc($Type.sizeof * elements, $Type.alignof))[:elements];
|
return (($Type*)tcalloc($Type.sizeof * elements, $Type.alignof))[:elements];
|
||||||
}
|
}
|
||||||
|
|
||||||
macro new_zero_array($Type, usz elements) @deprecated("Use new_array")
|
|
||||||
{
|
|
||||||
return new_array($Type, elements);
|
|
||||||
}
|
|
||||||
|
|
||||||
macro temp_zero_array($Type, usz elements) @deprecated("Use temp_new_array")
|
|
||||||
{
|
|
||||||
return temp_new_array($Type, elements);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn void* calloc(usz size) @builtin @inline @nodiscard
|
fn void* calloc(usz size) @builtin @inline @nodiscard
|
||||||
{
|
{
|
||||||
return allocator::calloc(allocator::heap(), size);
|
return allocator::calloc(allocator::heap(), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
|
||||||
|
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
|
||||||
|
**/
|
||||||
fn void* calloc_aligned(usz size, usz alignment) @builtin @inline @nodiscard
|
fn void* calloc_aligned(usz size, usz alignment) @builtin @inline @nodiscard
|
||||||
{
|
{
|
||||||
return allocator::calloc_aligned(allocator::heap(), size, alignment)!!;
|
return allocator::calloc_aligned(allocator::heap(), size, alignment)!!;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void* tcalloc(usz size, usz alignment = 0, usz offset = 0) @builtin @inline @nodiscard
|
fn void* tcalloc(usz size, usz alignment = 0) @builtin @inline @nodiscard
|
||||||
{
|
{
|
||||||
if (!size) return null;
|
if (!size) return null;
|
||||||
return allocator::temp().acquire(size, false, alignment, 0)!!;
|
return allocator::temp().acquire(size, ZERO, alignment)!!;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void* realloc(void *ptr, usz new_size) @builtin @inline @nodiscard
|
fn void* realloc(void *ptr, usz new_size) @builtin @inline @nodiscard
|
||||||
@@ -749,6 +748,6 @@ fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMEN
|
|||||||
{
|
{
|
||||||
if (!size) return null;
|
if (!size) return null;
|
||||||
if (!ptr) return tmalloc(size, alignment);
|
if (!ptr) return tmalloc(size, alignment);
|
||||||
return allocator::temp().resize(ptr, size, alignment, 0)!!;
|
return allocator::temp().resize(ptr, size, alignment)!!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ struct TrackingEnv
|
|||||||
uint line;
|
uint line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum AllocInitType
|
||||||
|
{
|
||||||
|
NO_ZERO,
|
||||||
|
ZERO
|
||||||
|
}
|
||||||
|
|
||||||
interface Allocator
|
interface Allocator
|
||||||
{
|
{
|
||||||
@@ -18,18 +23,16 @@ interface Allocator
|
|||||||
/**
|
/**
|
||||||
* @require !alignment || math::is_power_of_2(alignment)
|
* @require !alignment || math::is_power_of_2(alignment)
|
||||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||||
* @require offset == 0 `offset no longer supported`
|
|
||||||
* @require size > 0
|
* @require size > 0
|
||||||
**/
|
**/
|
||||||
fn void*! acquire(usz size, bool clear, usz alignment, usz offset);
|
fn void*! acquire(usz size, AllocInitType init_type, usz alignment = 0);
|
||||||
/**
|
/**
|
||||||
* @require !alignment || math::is_power_of_2(alignment)
|
* @require !alignment || math::is_power_of_2(alignment)
|
||||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||||
* @require offset == 0 `offset no longer supported`
|
|
||||||
* @require ptr != null
|
* @require ptr != null
|
||||||
* @require new_size > 0
|
* @require new_size > 0
|
||||||
**/
|
**/
|
||||||
fn void*! resize(void* ptr, usz new_size, usz alignment, usz offset);
|
fn void*! resize(void* ptr, usz new_size, usz alignment = 0);
|
||||||
/**
|
/**
|
||||||
* @require ptr != null
|
* @require ptr != null
|
||||||
**/
|
**/
|
||||||
@@ -46,54 +49,54 @@ fault AllocationFailure
|
|||||||
|
|
||||||
fn usz alignment_for_allocation(usz alignment) @inline @private
|
fn usz alignment_for_allocation(usz alignment) @inline @private
|
||||||
{
|
{
|
||||||
return alignment < mem::DEFAULT_MEM_ALIGNMENT ? alignment = mem::DEFAULT_MEM_ALIGNMENT : alignment;
|
return alignment < mem::DEFAULT_MEM_ALIGNMENT ? mem::DEFAULT_MEM_ALIGNMENT : alignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro void* malloc(Allocator* allocator, usz size) @nodiscard
|
macro void* malloc(Allocator allocator, usz size) @nodiscard
|
||||||
{
|
{
|
||||||
return malloc_try(allocator, size)!!;
|
return malloc_try(allocator, size)!!;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro void*! malloc_try(Allocator* allocator, usz size) @nodiscard
|
macro void*! malloc_try(Allocator allocator, usz size) @nodiscard
|
||||||
{
|
{
|
||||||
if (!size) return null;
|
if (!size) return null;
|
||||||
$if env::TESTING:
|
$if env::TESTING:
|
||||||
char* data = allocator.acquire(size, false, 0, 0)!;
|
char* data = allocator.acquire(size, NO_ZERO)!;
|
||||||
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
|
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||||
return data;
|
return data;
|
||||||
$else
|
$else
|
||||||
return allocator.acquire(size, false, 0, 0);
|
return allocator.acquire(size, NO_ZERO);
|
||||||
$endif
|
$endif
|
||||||
}
|
}
|
||||||
|
|
||||||
macro void* calloc(Allocator* allocator, usz size) @nodiscard
|
macro void* calloc(Allocator allocator, usz size) @nodiscard
|
||||||
{
|
{
|
||||||
return calloc_try(allocator, size)!!;
|
return calloc_try(allocator, size)!!;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro void*! calloc_try(Allocator* allocator, usz size) @nodiscard
|
macro void*! calloc_try(Allocator allocator, usz size) @nodiscard
|
||||||
{
|
{
|
||||||
if (!size) return null;
|
if (!size) return null;
|
||||||
return allocator.acquire(size, true, 0, 0);
|
return allocator.acquire(size, ZERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro void* realloc(Allocator* allocator, void* ptr, usz new_size) @nodiscard
|
macro void* realloc(Allocator allocator, void* ptr, usz new_size) @nodiscard
|
||||||
{
|
{
|
||||||
return realloc_try(allocator, ptr, new_size)!!;
|
return realloc_try(allocator, ptr, new_size)!!;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro void*! realloc_try(Allocator* allocator, void* ptr, usz new_size) @nodiscard
|
macro void*! realloc_try(Allocator allocator, void* ptr, usz new_size) @nodiscard
|
||||||
{
|
{
|
||||||
if (!new_size)
|
if (!new_size)
|
||||||
{
|
{
|
||||||
free(allocator, ptr);
|
free(allocator, ptr);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!ptr) return allocator.acquire(new_size, false, 0, 0);
|
if (!ptr) return allocator.acquire(new_size, NO_ZERO);
|
||||||
return allocator.resize(ptr, new_size, 0, 0);
|
return allocator.resize(ptr, new_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro void free(Allocator* allocator, void* ptr)
|
macro void free(Allocator allocator, void* ptr)
|
||||||
{
|
{
|
||||||
if (!ptr) return;
|
if (!ptr) return;
|
||||||
$if env::TESTING:
|
$if env::TESTING:
|
||||||
@@ -102,25 +105,25 @@ macro void free(Allocator* allocator, void* ptr)
|
|||||||
allocator.release(ptr, false);
|
allocator.release(ptr, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro void*! malloc_aligned(Allocator* allocator, usz size, usz alignment, usz offset = 0) @nodiscard
|
macro void*! malloc_aligned(Allocator allocator, usz size, usz alignment) @nodiscard
|
||||||
{
|
{
|
||||||
if (!size) return null;
|
if (!size) return null;
|
||||||
$if env::TESTING:
|
$if env::TESTING:
|
||||||
char* data = allocator.acquire(size, false, alignment, offset)!;
|
char* data = allocator.acquire(size, NO_ZERO, alignment)!;
|
||||||
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
|
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||||
return data;
|
return data;
|
||||||
$else
|
$else
|
||||||
return allocator.acquire(size, false, alignment, offset);
|
return allocator.acquire(size, NO_ZERO, alignment);
|
||||||
$endif
|
$endif
|
||||||
}
|
}
|
||||||
|
|
||||||
macro void*! calloc_aligned(Allocator* allocator, usz size, usz alignment, usz offset = 0) @nodiscard
|
macro void*! calloc_aligned(Allocator allocator, usz size, usz alignment) @nodiscard
|
||||||
{
|
{
|
||||||
if (!size) return null;
|
if (!size) return null;
|
||||||
return allocator.acquire(size, true, alignment, offset);
|
return allocator.acquire(size, ZERO, alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro void*! realloc_aligned(Allocator* allocator, void* ptr, usz new_size, usz alignment, usz offset = 0) @nodiscard
|
macro void*! realloc_aligned(Allocator allocator, void* ptr, usz new_size, usz alignment) @nodiscard
|
||||||
{
|
{
|
||||||
if (!new_size)
|
if (!new_size)
|
||||||
{
|
{
|
||||||
@@ -131,104 +134,164 @@ macro void*! realloc_aligned(Allocator* allocator, void* ptr, usz new_size, usz
|
|||||||
{
|
{
|
||||||
return malloc_aligned(allocator, new_size, alignment);
|
return malloc_aligned(allocator, new_size, alignment);
|
||||||
}
|
}
|
||||||
return allocator.resize(ptr, new_size, alignment, offset);
|
return allocator.resize(ptr, new_size, alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro void free_aligned(Allocator* allocator, void* ptr)
|
macro void free_aligned(Allocator allocator, void* ptr)
|
||||||
{
|
{
|
||||||
if (!ptr) return;
|
if (!ptr) return;
|
||||||
$if env::TESTING:
|
$if env::TESTING:
|
||||||
((char*)ptr)[0] = 0xBA;
|
((char*)ptr)[0] = 0xBA;
|
||||||
$endif
|
$endif
|
||||||
allocator.release(ptr, true);
|
allocator.release(ptr, .aligned = true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_aligned' instead"
|
||||||
* @require $vacount < 2 : "Too many arguments."
|
* @require $vacount < 2 : "Too many arguments."
|
||||||
* @require $or($vacount == 0, $assignable($vaexpr(0), $Type)) : "The second argument must be an initializer for the type"
|
* @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||||
**/
|
**/
|
||||||
macro new(Allocator* allocator, $Type, ...) @nodiscard
|
macro new(Allocator allocator, $Type, ...) @nodiscard
|
||||||
{
|
{
|
||||||
$if $vacount == 0:
|
$if $vacount == 0:
|
||||||
return ($Type*)calloc(allocator, $Type.sizeof);
|
return ($Type*)calloc(allocator, $Type.sizeof);
|
||||||
$else
|
$else
|
||||||
$Type* val = malloc(allocator, $Type.sizeof);
|
$Type* val = malloc(allocator, $Type.sizeof);
|
||||||
*val = $vaexpr(0);
|
*val = $vaexpr[0];
|
||||||
return val;
|
return val;
|
||||||
$endif
|
$endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_aligned' instead"
|
||||||
* @require $vacount < 2 : "Too many arguments."
|
* @require $vacount < 2 : "Too many arguments."
|
||||||
* @require $or($vacount == 0, $assignable($vaexpr(0), $Type)) : "The second argument must be an initializer for the type"
|
* @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||||
**/
|
**/
|
||||||
macro new_try(Allocator* allocator, $Type, ...) @nodiscard
|
macro new_try(Allocator allocator, $Type, ...) @nodiscard
|
||||||
{
|
{
|
||||||
$if $vacount == 0:
|
$if $vacount == 0:
|
||||||
return ($Type*)calloc_try(allocator, $Type.sizeof);
|
return ($Type*)calloc_try(allocator, $Type.sizeof);
|
||||||
$else
|
$else
|
||||||
$Type* val = malloc_try(allocator, $Type.sizeof)!;
|
$Type* val = malloc_try(allocator, $Type.sizeof)!;
|
||||||
*val = $vaexpr(0);
|
*val = $vaexpr[0];
|
||||||
return val;
|
return val;
|
||||||
$endif
|
$endif
|
||||||
}
|
}
|
||||||
|
|
||||||
macro new_with_padding(Allocator* allocator, $Type, usz padding) @nodiscard
|
/**
|
||||||
|
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
|
||||||
|
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
|
||||||
|
* @require $vacount < 2 : "Too many arguments."
|
||||||
|
* @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||||
|
**/
|
||||||
|
macro new_aligned($Type, ...) @nodiscard
|
||||||
|
{
|
||||||
|
$if $vacount == 0:
|
||||||
|
return ($Type*)calloc_aligned(allocator, $Type.sizeof, $Type.alignof);
|
||||||
|
$else
|
||||||
|
$Type* val = malloc_aligned(allocator, $Type.sizeof, $Type.alignof);
|
||||||
|
*val = $vaexpr[0];
|
||||||
|
return val;
|
||||||
|
$endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT
|
||||||
|
**/
|
||||||
|
macro new_with_padding(Allocator allocator, $Type, usz padding) @nodiscard
|
||||||
{
|
{
|
||||||
return ($Type*)calloc_try(allocator, $Type.sizeof + padding);
|
return ($Type*)calloc_try(allocator, $Type.sizeof + padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro alloc(Allocator* allocator, $Type) @nodiscard
|
/**
|
||||||
|
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
|
||||||
|
**/
|
||||||
|
macro alloc(Allocator allocator, $Type) @nodiscard
|
||||||
{
|
{
|
||||||
return ($Type*)malloc(allocator, $Type.sizeof);
|
return ($Type*)malloc(allocator, $Type.sizeof);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro alloc_try(Allocator* allocator, $Type) @nodiscard
|
/**
|
||||||
|
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
|
||||||
|
**/
|
||||||
|
macro alloc_try(Allocator allocator, $Type) @nodiscard
|
||||||
{
|
{
|
||||||
return ($Type*)malloc_try(allocator, $Type.sizeof);
|
return ($Type*)malloc_try(allocator, $Type.sizeof);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro alloc_with_padding(Allocator* allocator, $Type, usz padding) @nodiscard
|
/**
|
||||||
|
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
|
||||||
|
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
|
||||||
|
**/
|
||||||
|
macro alloc_aligned(Allocator allocator, $Type) @nodiscard
|
||||||
|
{
|
||||||
|
return ($Type*)malloc_aligned(allocator, $Type.sizeof, $Type.alignof);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT
|
||||||
|
**/
|
||||||
|
macro alloc_with_padding(Allocator allocator, $Type, usz padding) @nodiscard
|
||||||
{
|
{
|
||||||
return ($Type*)malloc_try(allocator, $Type.sizeof + padding);
|
return ($Type*)malloc_try(allocator, $Type.sizeof + padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro new_array(Allocator* allocator, $Type, usz elements) @nodiscard
|
/**
|
||||||
|
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_array_aligned' instead"
|
||||||
|
**/
|
||||||
|
macro new_array(Allocator allocator, $Type, usz elements) @nodiscard
|
||||||
{
|
{
|
||||||
return new_array_try(allocator, $Type, elements)!!;
|
return new_array_try(allocator, $Type, elements)!!;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro new_array_try(Allocator* allocator, $Type, usz elements) @nodiscard
|
/**
|
||||||
|
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_array_aligned' instead"
|
||||||
|
**/
|
||||||
|
macro new_array_try(Allocator allocator, $Type, usz elements) @nodiscard
|
||||||
{
|
{
|
||||||
return (($Type*)calloc_try(allocator, $Type.sizeof * elements))[:elements];
|
return (($Type*)calloc_try(allocator, $Type.sizeof * elements))[:elements];
|
||||||
}
|
}
|
||||||
|
|
||||||
macro new_array_aligned(Allocator* allocator, $Type, usz elements) @nodiscard
|
/**
|
||||||
|
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
|
||||||
|
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
|
||||||
|
**/
|
||||||
|
macro new_array_aligned(Allocator allocator, $Type, usz elements) @nodiscard
|
||||||
{
|
{
|
||||||
return ((Type*)calloc_aligned(allocator, $Type.sizeof * elements, $Type.alignof))[:elements]!!;
|
return (($Type*)calloc_aligned(allocator, $Type.sizeof * elements, $Type.alignof))[:elements]!!;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro alloc_array(Allocator* allocator, $Type, usz elements) @nodiscard
|
/**
|
||||||
|
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_array_aligned' instead"
|
||||||
|
**/
|
||||||
|
macro alloc_array(Allocator allocator, $Type, usz elements) @nodiscard
|
||||||
{
|
{
|
||||||
return alloc_array_try(allocator, $Type, elements)!!;
|
return alloc_array_try(allocator, $Type, elements)!!;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro alloc_array_aligned(Allocator* allocator, $Type, usz elements) @nodiscard
|
/**
|
||||||
|
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
|
||||||
|
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
|
||||||
|
**/
|
||||||
|
macro alloc_array_aligned(Allocator allocator, $Type, usz elements) @nodiscard
|
||||||
{
|
{
|
||||||
return ((Type*)malloc_aligned(allocator, $Type.sizeof * elements, $Type.alignof))[:elements]!!;
|
return (($Type*)malloc_aligned(allocator, $Type.sizeof * elements, $Type.alignof))[:elements]!!;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro alloc_array_try(Allocator* allocator, $Type, usz elements) @nodiscard
|
/**
|
||||||
|
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_array_aligned' instead"
|
||||||
|
**/
|
||||||
|
macro alloc_array_try(Allocator allocator, $Type, usz elements) @nodiscard
|
||||||
{
|
{
|
||||||
return (($Type*)malloc_try(allocator, $Type.sizeof * elements))[:elements];
|
return (($Type*)malloc_try(allocator, $Type.sizeof * elements))[:elements];
|
||||||
}
|
}
|
||||||
|
|
||||||
macro clone(Allocator* allocator, value) @nodiscard
|
macro clone(Allocator allocator, value) @nodiscard
|
||||||
{
|
{
|
||||||
return new(allocator, $typeof(value), value);
|
return new(allocator, $typeof(value), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn any* clone_any(Allocator* allocator, any* value) @nodiscard
|
fn any clone_any(Allocator allocator, any value) @nodiscard
|
||||||
{
|
{
|
||||||
usz size = value.type.sizeof;
|
usz size = value.type.sizeof;
|
||||||
void* data = malloc(allocator, size);
|
void* data = malloc(allocator, size);
|
||||||
@@ -236,106 +299,6 @@ fn any* clone_any(Allocator* allocator, any* value) @nodiscard
|
|||||||
return any_make(data, value.type);
|
return any_make(data, value.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocator "functions"
|
|
||||||
|
|
||||||
macro void*! Allocator.alloc_checked(&self, usz size) @deprecated("Use allocator::malloc_try")
|
|
||||||
{
|
|
||||||
return malloc_try(self, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
macro void*! Allocator.calloc_checked(&self, usz size) @deprecated("Use allocator::calloc_try")
|
|
||||||
{
|
|
||||||
return calloc_try(self, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
macro void*! Allocator.realloc_checked(&self, void* ptr, usz new_size) @deprecated("Use allocator::realloc_try")
|
|
||||||
{
|
|
||||||
return realloc_try(ptr, new_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
macro Allocator.new_array(&self, $Type, usz size, usz end_padding = 0) @deprecated("Use allocator::alloc_array")
|
|
||||||
{
|
|
||||||
return (($Type*)self.alloc_checked($Type.sizeof * size + end_padding))[:size]!!;
|
|
||||||
}
|
|
||||||
|
|
||||||
macro Allocator.new_array_checked(&self, $Type, usz size, usz end_padding = 0) @deprecated("Use allocator::alloc_array_try")
|
|
||||||
{
|
|
||||||
return (($Type*)self.alloc_checked($Type.sizeof * size + end_padding))[:size];
|
|
||||||
}
|
|
||||||
|
|
||||||
macro Allocator.new_zero_array(&self, $Type, usz size, usz end_padding = 0) @deprecated("Use allocator::new_array")
|
|
||||||
{
|
|
||||||
return (($Type*)self.calloc_checked($Type.sizeof * size + end_padding))[:size]!!;
|
|
||||||
}
|
|
||||||
|
|
||||||
macro Allocator.new_zero_array_checked(&self, $Type, usz size, usz end_padding = 0) @deprecated("Use allocator::new_array_try")
|
|
||||||
{
|
|
||||||
return (($Type*)self.calloc_checked($Type.sizeof * size + end_padding))[:size];
|
|
||||||
}
|
|
||||||
|
|
||||||
macro Allocator.new(&self, $Type, usz end_padding = 0) @nodiscard @deprecated("Use allocator::alloc")
|
|
||||||
{
|
|
||||||
return ($Type*)self.alloc_checked($Type.sizeof + end_padding)!!;
|
|
||||||
}
|
|
||||||
|
|
||||||
macro Allocator.new_checked(&self, $Type, usz end_padding = 0) @nodiscard @deprecated("Use allocator::alloc_try")
|
|
||||||
{
|
|
||||||
return ($Type*)self.alloc_checked($Type.sizeof + end_padding);
|
|
||||||
}
|
|
||||||
|
|
||||||
macro Allocator.new_clear(&self, $Type, usz end_padding = 0) @nodiscard @deprecated("Use allocator::new")
|
|
||||||
{
|
|
||||||
return ($Type*)self.calloc_checked($Type.sizeof + end_padding)!!;
|
|
||||||
}
|
|
||||||
|
|
||||||
macro Allocator.new_clear_checked(&self, $Type, usz end_padding = 0) @nodiscard @deprecated("Use allocator::new_try")
|
|
||||||
{
|
|
||||||
return ($Type*)self.calloc_checked($Type.sizeof + end_padding);
|
|
||||||
}
|
|
||||||
macro Allocator.clone(&self, value) @deprecated("Use allocator::clone")
|
|
||||||
{
|
|
||||||
var x = self.alloc($typeof(value));
|
|
||||||
*x = value;
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn void* Allocator.alloc(&self, usz size) @nodiscard @deprecated("Use allocator::malloc")
|
|
||||||
{
|
|
||||||
return malloc(self, size);
|
|
||||||
}
|
|
||||||
fn void* Allocator.calloc(&self, usz size) @nodiscard @deprecated("Use allocator::calloc")
|
|
||||||
{
|
|
||||||
return calloc(self, size);
|
|
||||||
}
|
|
||||||
fn void* Allocator.realloc(&self, void* ptr, usz new_size) @nodiscard @deprecated("Use allocator::realloc")
|
|
||||||
{
|
|
||||||
return realloc(self, ptr, new_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn void*! Allocator.alloc_aligned(&self, usz size, usz alignment, usz offset = 0) @deprecated("Use allocator::malloc_aligned")
|
|
||||||
{
|
|
||||||
return malloc_aligned(self, size, alignment, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn void*! Allocator.calloc_aligned(&self, usz size, usz alignment, usz offset = 0) @deprecated("Use allocator::calloc_aligned")
|
|
||||||
{
|
|
||||||
return calloc_aligned(self, size, alignment, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn void*! Allocator.realloc_aligned(&self, void* ptr, usz new_size, usz alignment = 0, usz offset = 0) @deprecated("Use allocator::realloc_aligned")
|
|
||||||
{
|
|
||||||
return realloc_aligned(self, ptr, new_size, alignment, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn void Allocator.free(&self, void* ptr) @deprecated("Use allocator::free")
|
|
||||||
{
|
|
||||||
free(self, ptr);
|
|
||||||
}
|
|
||||||
fn void Allocator.free_aligned(&self, void* ptr) @deprecated("Use allocator::free_aligned")
|
|
||||||
{
|
|
||||||
free_aligned(self, ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require bytes > 0
|
* @require bytes > 0
|
||||||
@@ -395,12 +358,12 @@ macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes
|
|||||||
|
|
||||||
|
|
||||||
// All allocators
|
// All allocators
|
||||||
tlocal Allocator* thread_allocator @private = &allocator::LIBC_ALLOCATOR;
|
tlocal Allocator thread_allocator @private = &allocator::LIBC_ALLOCATOR;
|
||||||
tlocal TempAllocator* thread_temp_allocator @private = null;
|
tlocal TempAllocator* thread_temp_allocator @private = null;
|
||||||
tlocal TempAllocator*[2] temp_allocator_pair @private;
|
tlocal TempAllocator*[2] temp_allocator_pair @private;
|
||||||
Allocator* temp_base_allocator @private = &allocator::LIBC_ALLOCATOR;
|
Allocator temp_base_allocator @private = &allocator::LIBC_ALLOCATOR;
|
||||||
|
|
||||||
macro TempAllocator* create_default_sized_temp_allocator(Allocator* allocator) @local
|
macro TempAllocator* create_default_sized_temp_allocator(Allocator allocator) @local
|
||||||
{
|
{
|
||||||
$switch (env::MEMORY_ENV)
|
$switch (env::MEMORY_ENV)
|
||||||
$case NORMAL:
|
$case NORMAL:
|
||||||
@@ -414,7 +377,7 @@ macro TempAllocator* create_default_sized_temp_allocator(Allocator* allocator) @
|
|||||||
$endswitch
|
$endswitch
|
||||||
}
|
}
|
||||||
|
|
||||||
macro Allocator* heap() => thread_allocator;
|
macro Allocator heap() => thread_allocator;
|
||||||
|
|
||||||
macro TempAllocator* temp()
|
macro TempAllocator* temp()
|
||||||
{
|
{
|
||||||
@@ -432,7 +395,24 @@ fn void init_default_temp_allocators() @private
|
|||||||
thread_temp_allocator = temp_allocator_pair[0];
|
thread_temp_allocator = temp_allocator_pair[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn TempAllocator *temp_allocator_next() @private
|
fn void destroy_temp_allocators_after_exit() @finalizer(65535) @local @if(env::LIBC)
|
||||||
|
{
|
||||||
|
destroy_temp_allocators();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this to destroy any memory used by the temp allocators. This will invalidate all temp memory.
|
||||||
|
**/
|
||||||
|
fn void destroy_temp_allocators()
|
||||||
|
{
|
||||||
|
if (!thread_temp_allocator) return;
|
||||||
|
temp_allocator_pair[0].destroy();
|
||||||
|
temp_allocator_pair[1].destroy();
|
||||||
|
temp_allocator_pair[..] = null;
|
||||||
|
thread_temp_allocator = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn TempAllocator* temp_allocator_next() @private
|
||||||
{
|
{
|
||||||
if (!thread_temp_allocator)
|
if (!thread_temp_allocator)
|
||||||
{
|
{
|
||||||
@@ -441,4 +421,4 @@ fn TempAllocator *temp_allocator_next() @private
|
|||||||
}
|
}
|
||||||
usz index = thread_temp_allocator == temp_allocator_pair[0] ? 1 : 0;
|
usz index = thread_temp_allocator == temp_allocator_pair[0] ? 1 : 0;
|
||||||
return thread_temp_allocator = temp_allocator_pair[index];
|
return thread_temp_allocator = temp_allocator_pair[index];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ fn void runtime_startup() @public @export("__c3_runtime_startup")
|
|||||||
}
|
}
|
||||||
assert(runtime_state == RUN_CTORS);
|
assert(runtime_state == RUN_CTORS);
|
||||||
runtime_state = READ_DYLIB;
|
runtime_state = READ_DYLIB;
|
||||||
ctor = null;
|
ctor_first = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void runtime_finalize() @public @export("__c3_runtime_finalize")
|
fn void runtime_finalize() @public @export("__c3_runtime_finalize")
|
||||||
|
|||||||
@@ -84,19 +84,19 @@ macro void release_wargs(String[] list) @private
|
|||||||
free(list.ptr);
|
free(list.ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro int @win_to_err_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd)
|
macro int @win_to_err_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||||
{
|
{
|
||||||
if (catch #m()) return 1;
|
if (catch #m()) return 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
macro int @win_to_int_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd) => #m();
|
macro int @win_to_int_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd) => #m();
|
||||||
macro int @win_to_void_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd)
|
macro int @win_to_void_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||||
{
|
{
|
||||||
#m();
|
#m();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro int @win_to_err_main_args(#m, void* handle, Char16* cmd_line, int show_cmd)
|
macro int @win_to_err_main_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||||
{
|
{
|
||||||
String[] args = win_command_line_to_strings(cmd_line);
|
String[] args = win_command_line_to_strings(cmd_line);
|
||||||
defer release_wargs(args);
|
defer release_wargs(args);
|
||||||
@@ -104,14 +104,14 @@ macro int @win_to_err_main_args(#m, void* handle, Char16* cmd_line, int show_cmd
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro int @win_to_int_main_args(#m, void* handle, Char16* cmd_line, int show_cmd)
|
macro int @win_to_int_main_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||||
{
|
{
|
||||||
String[] args = win_command_line_to_strings(cmd_line);
|
String[] args = win_command_line_to_strings(cmd_line);
|
||||||
defer release_wargs(args);
|
defer release_wargs(args);
|
||||||
return #m(args);
|
return #m(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro int @win_to_void_main_args(#m, void* handle, Char16* cmd_line, int show_cmd)
|
macro int @win_to_void_main_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||||
{
|
{
|
||||||
String[] args = win_command_line_to_strings(cmd_line);
|
String[] args = win_command_line_to_strings(cmd_line);
|
||||||
defer release_wargs(args);
|
defer release_wargs(args);
|
||||||
@@ -119,26 +119,26 @@ macro int @win_to_void_main_args(#m, void* handle, Char16* cmd_line, int show_cm
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro int @win_to_err_main(#m, void* handle, Char16* cmd_line, int show_cmd)
|
macro int @win_to_err_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||||
{
|
{
|
||||||
String[] args = win_command_line_to_strings(cmd_line);
|
String[] args = win_command_line_to_strings(cmd_line);
|
||||||
defer release_wargs(args);
|
defer release_wargs(args);
|
||||||
if (catch #m(handle, args, show_cmd)) return 1;
|
if (catch #m(handle, prev_handle, args, show_cmd)) return 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro int @win_to_int_main(#m, void* handle, Char16* cmd_line, int show_cmd)
|
macro int @win_to_int_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||||
{
|
{
|
||||||
String[] args = win_command_line_to_strings(cmd_line);
|
String[] args = win_command_line_to_strings(cmd_line);
|
||||||
defer release_wargs(args);
|
defer release_wargs(args);
|
||||||
return #m(handle, args, show_cmd);
|
return #m(handle, prev_handle, args, show_cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro int @win_to_void_main(#m, void* handle, Char16* cmd_line, int show_cmd)
|
macro int @win_to_void_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||||
{
|
{
|
||||||
String[] args = win_command_line_to_strings(cmd_line);
|
String[] args = win_command_line_to_strings(cmd_line);
|
||||||
defer release_wargs(args);
|
defer release_wargs(args);
|
||||||
#m(handle, args, show_cmd);
|
#m(handle, prev_handle, args, show_cmd);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,13 +4,13 @@
|
|||||||
module std::core::runtime;
|
module std::core::runtime;
|
||||||
import libc, std::time, std::io, std::sort;
|
import libc, std::time, std::io, std::sort;
|
||||||
|
|
||||||
struct AnyStruct
|
struct AnyRaw
|
||||||
{
|
{
|
||||||
void* ptr;
|
void* ptr;
|
||||||
typeid type;
|
typeid type;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SubArrayStruct
|
struct SliceRaw
|
||||||
{
|
{
|
||||||
void* ptr;
|
void* ptr;
|
||||||
usz len;
|
usz len;
|
||||||
@@ -24,7 +24,7 @@ struct BenchmarkUnit
|
|||||||
BenchmarkFn func;
|
BenchmarkFn func;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn BenchmarkUnit[] benchmark_collection_create(Allocator* allocator = allocator::heap())
|
fn BenchmarkUnit[] benchmark_collection_create(Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
BenchmarkFn[] fns = $$BENCHMARK_FNS;
|
BenchmarkFn[] fns = $$BENCHMARK_FNS;
|
||||||
String[] names = $$BENCHMARK_NAMES;
|
String[] names = $$BENCHMARK_NAMES;
|
||||||
@@ -142,7 +142,7 @@ struct TestUnit
|
|||||||
TestFn func;
|
TestFn func;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn TestUnit[] test_collection_create(Allocator* allocator = allocator::heap())
|
fn TestUnit[] test_collection_create(Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
TestFn[] fns = $$TEST_FNS;
|
TestFn[] fns = $$TEST_FNS;
|
||||||
String[] names = $$TEST_NAMES;
|
String[] names = $$TEST_NAMES;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
module std::core::string;
|
module std::core::string;
|
||||||
import std::ascii;
|
import std::ascii;
|
||||||
|
|
||||||
|
distinct String @if(!$defined(String)) = inline char[];
|
||||||
distinct ZString = inline char*;
|
distinct ZString = inline char*;
|
||||||
distinct WString = inline Char16*;
|
distinct WString = inline Char16*;
|
||||||
def Char32 = uint;
|
def Char32 = uint;
|
||||||
@@ -31,31 +32,77 @@ fault NumberConversion
|
|||||||
FLOAT_OUT_OF_RANGE,
|
FLOAT_OUT_OF_RANGE,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a temporary String created using the formatting function.
|
||||||
|
*
|
||||||
|
* @param [in] fmt `The formatting string`
|
||||||
|
**/
|
||||||
macro String tformat(String fmt, ...)
|
macro String tformat(String fmt, ...)
|
||||||
{
|
{
|
||||||
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
|
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
|
||||||
str.appendf(fmt, $vasplat());
|
str.appendf(fmt, $vasplat);
|
||||||
return str.str_view();
|
return str.str_view();
|
||||||
}
|
}
|
||||||
|
|
||||||
macro String new_format(String fmt, ..., Allocator* allocator = allocator::heap())
|
/**
|
||||||
|
* Return a temporary ZString created using the formatting function.
|
||||||
|
*
|
||||||
|
* @param [in] fmt `The formatting string`
|
||||||
|
**/
|
||||||
|
macro ZString tformat_zstr(String fmt, ...)
|
||||||
|
{
|
||||||
|
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
|
||||||
|
str.appendf(fmt, $vasplat);
|
||||||
|
return str.zstr_view();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a new String created using the formatting function.
|
||||||
|
*
|
||||||
|
* @param [in] fmt `The formatting string`
|
||||||
|
* @param [inout] allocator `The allocator to use`
|
||||||
|
**/
|
||||||
|
macro String new_format(String fmt, ..., Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
@pool(allocator)
|
@pool(allocator)
|
||||||
{
|
{
|
||||||
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
|
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
|
||||||
str.appendf(fmt, $vasplat());
|
str.appendf(fmt, $vasplat);
|
||||||
return str.copy_str(allocator);
|
return str.copy_str(allocator);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a new ZString created using the formatting function.
|
||||||
|
*
|
||||||
|
* @param [in] fmt `The formatting string`
|
||||||
|
* @param [inout] allocator `The allocator to use`
|
||||||
|
**/
|
||||||
|
macro ZString new_format_zstr(String fmt, ..., Allocator allocator = allocator::heap())
|
||||||
|
{
|
||||||
|
@pool(allocator)
|
||||||
|
{
|
||||||
|
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
|
||||||
|
str.appendf(fmt, $vasplat);
|
||||||
|
return str.copy_zstr(allocator);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a character is in a set.
|
||||||
|
*
|
||||||
|
* @param c `the character to check`
|
||||||
|
* @param [in] set `The formatting string`
|
||||||
|
* @pure
|
||||||
|
* @return `True if a character is in the set`
|
||||||
|
**/
|
||||||
macro bool char_in_set(char c, String set)
|
macro bool char_in_set(char c, String set)
|
||||||
{
|
{
|
||||||
foreach (ch : set) if (ch == c) return true;
|
foreach (ch : set) if (ch == c) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String join_new(String[] s, String joiner, Allocator* allocator = allocator::heap())
|
fn String join_new(String[] s, String joiner, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
if (!s)
|
if (!s)
|
||||||
{
|
{
|
||||||
@@ -81,8 +128,12 @@ fn String join_new(String[] s, String joiner, Allocator* allocator = allocator::
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param [in] string
|
* Remove characters from the front and end of a string.
|
||||||
* @param [in] to_trim
|
*
|
||||||
|
* @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(string, String to_trim = "\t\n\r ")
|
fn String String.trim(string, String to_trim = "\t\n\r ")
|
||||||
{
|
{
|
||||||
@@ -96,8 +147,12 @@ fn String String.trim(string, String to_trim = "\t\n\r ")
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Check if the String starts with the needle.
|
||||||
|
*
|
||||||
* @param [in] string
|
* @param [in] string
|
||||||
* @param [in] needle
|
* @param [in] needle
|
||||||
|
* @pure
|
||||||
|
* @return `'true' if the string starts with the needle`
|
||||||
**/
|
**/
|
||||||
fn bool String.starts_with(string, String needle)
|
fn bool String.starts_with(string, String needle)
|
||||||
{
|
{
|
||||||
@@ -107,8 +162,12 @@ fn bool String.starts_with(string, String needle)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Check if the String ends with the needle.
|
||||||
|
*
|
||||||
* @param [in] string
|
* @param [in] string
|
||||||
* @param [in] needle
|
* @param [in] needle
|
||||||
|
* @pure
|
||||||
|
* @return `'true' if the string ends with the needle`
|
||||||
**/
|
**/
|
||||||
fn bool String.ends_with(string, String needle)
|
fn bool String.ends_with(string, String needle)
|
||||||
{
|
{
|
||||||
@@ -122,6 +181,8 @@ fn bool String.ends_with(string, String needle)
|
|||||||
*
|
*
|
||||||
* @param [in] string
|
* @param [in] string
|
||||||
* @param [in] needle
|
* @param [in] needle
|
||||||
|
* @pure
|
||||||
|
* @return `the substring with the prefix removed`
|
||||||
**/
|
**/
|
||||||
fn String String.strip(string, String needle)
|
fn String String.strip(string, String needle)
|
||||||
{
|
{
|
||||||
@@ -134,6 +195,8 @@ fn String String.strip(string, String needle)
|
|||||||
*
|
*
|
||||||
* @param [in] string
|
* @param [in] string
|
||||||
* @param [in] needle
|
* @param [in] needle
|
||||||
|
* @pure
|
||||||
|
* @return `the substring with the suffix removed`
|
||||||
**/
|
**/
|
||||||
fn String String.strip_end(string, String needle)
|
fn String String.strip_end(string, String needle)
|
||||||
{
|
{
|
||||||
@@ -153,7 +216,7 @@ fn String String.strip_end(string, String needle)
|
|||||||
* @require needle.len > 0 "The needle must be at least 1 character long"
|
* @require needle.len > 0 "The needle must be at least 1 character long"
|
||||||
* @ensure return.len > 0
|
* @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())
|
||||||
{
|
{
|
||||||
usz capacity = 16;
|
usz capacity = 16;
|
||||||
usz i = 0;
|
usz i = 0;
|
||||||
@@ -196,6 +259,14 @@ fn String[] String.tsplit(s, String needle, usz max = 0)
|
|||||||
return s.split(needle, max, allocator::temp()) @inline;
|
return s.split(needle, max, allocator::temp()) @inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a substring is found in the string.
|
||||||
|
|
||||||
|
* @param [in] s
|
||||||
|
* @param [in] needle "The string to look for."
|
||||||
|
* @pure
|
||||||
|
* @return "true if the string contains the substring, false otherwise"
|
||||||
|
**/
|
||||||
fn bool String.contains(s, String needle)
|
fn bool String.contains(s, String needle)
|
||||||
{
|
{
|
||||||
return @ok(s.index_of(needle));
|
return @ok(s.index_of(needle));
|
||||||
@@ -205,6 +276,7 @@ fn bool String.contains(s, String needle)
|
|||||||
* Find the index of the first incidence of a string.
|
* Find the index of the first incidence of a string.
|
||||||
*
|
*
|
||||||
* @param [in] s
|
* @param [in] s
|
||||||
|
* @param needle "The character to look for"
|
||||||
* @pure
|
* @pure
|
||||||
* @ensure return < s.len
|
* @ensure return < s.len
|
||||||
* @return "the index of the needle"
|
* @return "the index of the needle"
|
||||||
@@ -220,9 +292,32 @@ fn usz! String.index_of_char(s, char needle)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the index of the first incidence of a string.
|
* Find the index of the first incidence of a character.
|
||||||
*
|
*
|
||||||
* @param [in] s
|
* @param [in] s
|
||||||
|
* @param needle "The character to look for"
|
||||||
|
* @param start_index "The index to start with, may exceed max index."
|
||||||
|
* @pure
|
||||||
|
* @ensure return < s.len
|
||||||
|
* @return "the index of the needle"
|
||||||
|
* @return! SearchResult.MISSING "if the needle cannot be found starting from the start_index"
|
||||||
|
**/
|
||||||
|
fn usz! String.index_of_char_from(s, char needle, usz start_index)
|
||||||
|
{
|
||||||
|
usz len = s.len;
|
||||||
|
if (len <= start_index) return SearchResult.MISSING?;
|
||||||
|
for (usz i = start_index; i < len; i++)
|
||||||
|
{
|
||||||
|
if (s[i] == needle) return i;
|
||||||
|
}
|
||||||
|
return SearchResult.MISSING?;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the index of the first incidence of a character starting from the end.
|
||||||
|
*
|
||||||
|
* @param [in] s
|
||||||
|
* @param needle "the character to find"
|
||||||
* @pure
|
* @pure
|
||||||
* @ensure return < s.len
|
* @ensure return < s.len
|
||||||
* @return "the index of the needle"
|
* @return "the index of the needle"
|
||||||
@@ -312,7 +407,7 @@ fn usz ZString.len(str)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn ZString String.zstr_copy(s, Allocator* allocator = allocator::heap())
|
fn ZString String.zstr_copy(s, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
usz len = s.len;
|
usz len = s.len;
|
||||||
char* str = allocator::malloc(allocator, len + 1);
|
char* str = allocator::malloc(allocator, len + 1);
|
||||||
@@ -321,7 +416,7 @@ fn ZString String.zstr_copy(s, Allocator* allocator = allocator::heap())
|
|||||||
return (ZString)str;
|
return (ZString)str;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String String.concat(s1, String s2, Allocator* allocator = allocator::heap())
|
fn String String.concat(s1, String s2, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
usz full_len = s1.len + s2.len;
|
usz full_len = s1.len + s2.len;
|
||||||
char* str = allocator::malloc(allocator, full_len + 1);
|
char* str = allocator::malloc(allocator, full_len + 1);
|
||||||
@@ -337,7 +432,7 @@ fn String String.tconcat(s1, String s2) => s1.concat(s2, allocator::temp());
|
|||||||
|
|
||||||
fn ZString String.zstr_tcopy(s) => s.zstr_copy(allocator::temp()) @inline;
|
fn ZString String.zstr_tcopy(s) => s.zstr_copy(allocator::temp()) @inline;
|
||||||
|
|
||||||
fn String String.copy(s, Allocator* allocator = allocator::heap())
|
fn String String.copy(s, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
usz len = s.len;
|
usz len = s.len;
|
||||||
char* str = allocator::malloc(allocator, len + 1);
|
char* str = allocator::malloc(allocator, len + 1);
|
||||||
@@ -346,7 +441,7 @@ fn String String.copy(s, Allocator* allocator = allocator::heap())
|
|||||||
return (String)str[:len];
|
return (String)str[:len];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void String.free(&s, Allocator* allocator = allocator::heap())
|
fn void String.free(&s, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
if (!s.len) return;
|
if (!s.len) return;
|
||||||
allocator::free(allocator, s.ptr);
|
allocator::free(allocator, s.ptr);
|
||||||
@@ -355,7 +450,7 @@ fn void String.free(&s, Allocator* allocator = allocator::heap())
|
|||||||
|
|
||||||
fn String String.tcopy(s) => s.copy(allocator::temp()) @inline;
|
fn String String.tcopy(s) => s.copy(allocator::temp()) @inline;
|
||||||
|
|
||||||
fn String ZString.copy(z, Allocator* allocator = allocator::temp())
|
fn String ZString.copy(z, Allocator allocator = allocator::temp())
|
||||||
{
|
{
|
||||||
return z.str_view().copy(allocator) @inline;
|
return z.str_view().copy(allocator) @inline;
|
||||||
}
|
}
|
||||||
@@ -371,7 +466,7 @@ fn String ZString.tcopy(z)
|
|||||||
* @return! UnicodeResult.INVALID_UTF8 "If the string contained an invalid UTF-8 sequence"
|
* @return! UnicodeResult.INVALID_UTF8 "If the string contained an invalid UTF-8 sequence"
|
||||||
* @return! AllocationFailure "If allocation of the string fails"
|
* @return! AllocationFailure "If allocation of the string fails"
|
||||||
**/
|
**/
|
||||||
fn Char16[]! String.to_new_utf16(s, Allocator* allocator = allocator::heap())
|
fn Char16[]! String.to_new_utf16(s, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
usz len16 = conv::utf16len_for_utf8(s);
|
usz len16 = conv::utf16len_for_utf8(s);
|
||||||
Char16* data = allocator::alloc_array_try(allocator, Char16, len16 + 1)!;
|
Char16* data = allocator::alloc_array_try(allocator, Char16, len16 + 1)!;
|
||||||
@@ -391,7 +486,7 @@ fn Char16[]! String.to_temp_utf16(s)
|
|||||||
return s.to_new_utf16(allocator::temp());
|
return s.to_new_utf16(allocator::temp());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn WString! String.to_new_wstring(s, Allocator* allocator = allocator::heap())
|
fn WString! String.to_new_wstring(s, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
return (WString)s.to_new_utf16(allocator).ptr;
|
return (WString)s.to_new_utf16(allocator).ptr;
|
||||||
}
|
}
|
||||||
@@ -401,7 +496,7 @@ fn WString! String.to_temp_wstring(s)
|
|||||||
return (WString)s.to_temp_utf16().ptr;
|
return (WString)s.to_temp_utf16().ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Char32[]! String.to_new_utf32(s, Allocator* allocator = allocator::heap())
|
fn Char32[]! String.to_new_utf32(s, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
usz codepoints = conv::utf8_codepoints(s);
|
usz codepoints = conv::utf8_codepoints(s);
|
||||||
Char32* data = allocator::alloc_array_try(allocator, Char32, codepoints + 1)!;
|
Char32* data = allocator::alloc_array_try(allocator, Char32, codepoints + 1)!;
|
||||||
@@ -415,29 +510,49 @@ fn Char32[]! String.to_temp_utf32(s)
|
|||||||
return s.to_new_utf32(allocator::temp());
|
return s.to_new_utf32(allocator::temp());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a string to ASCII lower case.
|
||||||
|
*
|
||||||
|
* @param [inout] s
|
||||||
|
* @pure
|
||||||
|
**/
|
||||||
fn void String.convert_ascii_to_lower(s)
|
fn void String.convert_ascii_to_lower(s)
|
||||||
{
|
{
|
||||||
foreach (&c : s) if (c.is_upper()) *c += 'a' - 'A';
|
foreach (&c : s) if (c.is_upper() @pure) *c += 'a' - 'A';
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String String.new_ascii_to_lower(s, Allocator* allocator = allocator::heap())
|
fn String String.new_ascii_to_lower(s, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
String copy = s.copy(allocator);
|
String copy = s.copy(allocator);
|
||||||
copy.convert_ascii_to_lower();
|
copy.convert_ascii_to_lower();
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String String.temp_ascii_to_lower(s, Allocator* allocator = allocator::heap())
|
fn String String.temp_ascii_to_lower(s, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
return s.new_ascii_to_lower(allocator::temp());
|
return s.new_ascii_to_lower(allocator::temp());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a string to ASCII upper case.
|
||||||
|
*
|
||||||
|
* @param [inout] s
|
||||||
|
* @pure
|
||||||
|
**/
|
||||||
fn void String.convert_ascii_to_upper(s)
|
fn void String.convert_ascii_to_upper(s)
|
||||||
{
|
{
|
||||||
foreach (&c : s) if (c.is_lower()) *c -= 'a' - 'A';
|
foreach (&c : s) if (c.is_lower() @pure) *c -= 'a' - 'A';
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String String.new_ascii_to_upper(s, Allocator* allocator = allocator::heap())
|
/**
|
||||||
|
* Returns a string converted to ASCII upper case.
|
||||||
|
*
|
||||||
|
* @param [in] s
|
||||||
|
* @param [inout] allocator
|
||||||
|
*
|
||||||
|
* @return `a new String converted to ASCII upper case.`
|
||||||
|
**/
|
||||||
|
fn String String.new_ascii_to_upper(s, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
String copy = s.copy(allocator);
|
String copy = s.copy(allocator);
|
||||||
copy.convert_ascii_to_upper();
|
copy.convert_ascii_to_upper();
|
||||||
@@ -449,12 +564,16 @@ fn StringIterator String.iterator(s)
|
|||||||
return { s, 0 };
|
return { s, 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param [in] s
|
||||||
|
* @return `a temporary String converted to ASCII upper case.`
|
||||||
|
**/
|
||||||
fn String String.temp_ascii_to_upper(s)
|
fn String String.temp_ascii_to_upper(s)
|
||||||
{
|
{
|
||||||
return s.new_ascii_to_upper(allocator::temp());
|
return s.new_ascii_to_upper(allocator::temp());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String! new_from_utf32(Char32[] utf32, Allocator* allocator = allocator::heap())
|
fn String! new_from_utf32(Char32[] utf32, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
usz len = conv::utf8len_for_utf32(utf32);
|
usz len = conv::utf8len_for_utf32(utf32);
|
||||||
char* data = allocator::malloc_try(allocator, len + 1)!;
|
char* data = allocator::malloc_try(allocator, len + 1)!;
|
||||||
@@ -464,7 +583,7 @@ fn String! new_from_utf32(Char32[] utf32, Allocator* allocator = allocator::heap
|
|||||||
return (String)data[:len];
|
return (String)data[:len];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String! new_from_utf16(Char16[] utf16, Allocator* allocator = allocator::heap())
|
fn String! new_from_utf16(Char16[] utf16, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
usz len = conv::utf8len_for_utf16(utf16);
|
usz len = conv::utf8len_for_utf16(utf16);
|
||||||
char* data = allocator::malloc_try(allocator, len + 1)!;
|
char* data = allocator::malloc_try(allocator, len + 1)!;
|
||||||
@@ -474,7 +593,7 @@ fn String! new_from_utf16(Char16[] utf16, Allocator* allocator = allocator::heap
|
|||||||
return (String)data[:len];
|
return (String)data[:len];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String! new_from_wstring(WString wstring, Allocator* allocator = allocator::heap())
|
fn String! new_from_wstring(WString wstring, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
usz utf16_len;
|
usz utf16_len;
|
||||||
while (wstring[utf16_len] != 0) utf16_len++;
|
while (wstring[utf16_len] != 0) utf16_len++;
|
||||||
|
|||||||
@@ -13,11 +13,37 @@ fn void StringIterator.reset(&self)
|
|||||||
|
|
||||||
fn Char32! StringIterator.next(&self)
|
fn Char32! StringIterator.next(&self)
|
||||||
{
|
{
|
||||||
usz len = self.utf8.len;
|
usz len = self.utf8.len;
|
||||||
usz current = self.current;
|
usz current = self.current;
|
||||||
if (current >= len) return IteratorResult.NO_MORE_ELEMENT?;
|
if (current >= len) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
usz read = (len - current < 4 ? len - current : 4);
|
usz read = (len - current < 4 ? len - current : 4);
|
||||||
Char32 res = conv::utf8_to_char32(&self.utf8[current], &read)!;
|
Char32 res = conv::utf8_to_char32(&self.utf8[current], &read)!;
|
||||||
self.current += read;
|
self.current += read;
|
||||||
return res;
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Char32! StringIterator.peek(&self)
|
||||||
|
{
|
||||||
|
usz len = self.utf8.len;
|
||||||
|
usz current = self.current;
|
||||||
|
if (current >= len) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
|
usz read = (len - current < 4 ? len - current : 4);
|
||||||
|
Char32 res = conv::utf8_to_char32(&self.utf8[current], &read)!;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bool StringIterator.has_next(&self)
|
||||||
|
{
|
||||||
|
return self.current < self.utf8.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Char32! StringIterator.get(&self)
|
||||||
|
{
|
||||||
|
usz len = self.utf8.len;
|
||||||
|
usz current = self.current;
|
||||||
|
usz read = (len - current < 4 ? len - current : 4);
|
||||||
|
usz index = current > read ? current - read : 0;
|
||||||
|
if (index >= len) return IteratorResult.NO_MORE_ELEMENT?;
|
||||||
|
Char32 res = conv::utf8_to_char32(&self.utf8[index], &read)!;
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ fault ConversionResult
|
|||||||
/**
|
/**
|
||||||
* @require $Type.kindof.is_int() || $Type.kindof == TypeKind.ENUM "Argument was not an integer"
|
* @require $Type.kindof.is_int() || $Type.kindof == TypeKind.ENUM "Argument was not an integer"
|
||||||
**/
|
**/
|
||||||
macro any_to_int(any* v, $Type)
|
macro any_to_int(any v, $Type)
|
||||||
{
|
{
|
||||||
typeid any_type = v.type;
|
typeid any_type = v.type;
|
||||||
TypeKind kind = any_type.kindof;
|
TypeKind kind = any_type.kindof;
|
||||||
@@ -108,10 +108,10 @@ fn bool TypeKind.is_int(kind) @inline
|
|||||||
return kind == TypeKind.SIGNED_INT || kind == TypeKind.UNSIGNED_INT;
|
return kind == TypeKind.SIGNED_INT || kind == TypeKind.UNSIGNED_INT;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro bool is_subarray_convertable($Type)
|
macro bool is_slice_convertable($Type)
|
||||||
{
|
{
|
||||||
$switch ($Type.kindof)
|
$switch ($Type.kindof)
|
||||||
$case SUBARRAY:
|
$case SLICE:
|
||||||
return true;
|
return true;
|
||||||
$case POINTER:
|
$case POINTER:
|
||||||
return $Type.inner.kindof == TypeKind.ARRAY;
|
return $Type.inner.kindof == TypeKind.ARRAY;
|
||||||
@@ -120,10 +120,20 @@ macro bool is_subarray_convertable($Type)
|
|||||||
$endswitch
|
$endswitch
|
||||||
}
|
}
|
||||||
|
|
||||||
macro bool is_bool($Type) => $Type.kindof == TypeKind.BOOL;
|
macro bool is_bool($Type) @const => $Type.kindof == TypeKind.BOOL;
|
||||||
macro bool is_int($Type) => $Type.kindof == TypeKind.SIGNED_INT || $Type.kindof == TypeKind.UNSIGNED_INT;
|
macro bool is_int($Type) @const => $Type.kindof == TypeKind.SIGNED_INT || $Type.kindof == TypeKind.UNSIGNED_INT;
|
||||||
|
|
||||||
macro bool is_intlike($Type)
|
macro bool is_indexable($Type) @const
|
||||||
|
{
|
||||||
|
return $defined($Type{}[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro bool is_ref_indexable($Type) @const
|
||||||
|
{
|
||||||
|
return $defined(&$Type{}[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro bool is_intlike($Type) @const
|
||||||
{
|
{
|
||||||
$switch ($Type.kindof)
|
$switch ($Type.kindof)
|
||||||
$case SIGNED_INT:
|
$case SIGNED_INT:
|
||||||
@@ -136,7 +146,7 @@ macro bool is_intlike($Type)
|
|||||||
$endswitch
|
$endswitch
|
||||||
}
|
}
|
||||||
|
|
||||||
macro bool is_underlying_int($Type)
|
macro bool is_underlying_int($Type) @const
|
||||||
{
|
{
|
||||||
$switch ($Type.kindof)
|
$switch ($Type.kindof)
|
||||||
$case SIGNED_INT:
|
$case SIGNED_INT:
|
||||||
@@ -149,9 +159,9 @@ macro bool is_underlying_int($Type)
|
|||||||
$endswitch
|
$endswitch
|
||||||
}
|
}
|
||||||
|
|
||||||
macro bool is_float($Type) => $Type.kindof == TypeKind.FLOAT;
|
macro bool is_float($Type) @const => $Type.kindof == TypeKind.FLOAT;
|
||||||
|
|
||||||
macro bool is_floatlike($Type)
|
macro bool is_floatlike($Type) @const
|
||||||
{
|
{
|
||||||
$switch ($Type.kindof)
|
$switch ($Type.kindof)
|
||||||
$case FLOAT:
|
$case FLOAT:
|
||||||
@@ -163,12 +173,12 @@ macro bool is_floatlike($Type)
|
|||||||
$endswitch
|
$endswitch
|
||||||
}
|
}
|
||||||
|
|
||||||
macro bool is_vector($Type)
|
macro bool is_vector($Type) @const
|
||||||
{
|
{
|
||||||
return $Type.kindof == TypeKind.VECTOR;
|
return $Type.kindof == TypeKind.VECTOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro TypeKind inner_kind($Type)
|
macro TypeKind inner_kind($Type) @const
|
||||||
{
|
{
|
||||||
$if $Type.kindof == TypeKind.DISTINCT:
|
$if $Type.kindof == TypeKind.DISTINCT:
|
||||||
return inner_kind($typefrom($Type.inner));
|
return inner_kind($typefrom($Type.inner));
|
||||||
@@ -177,26 +187,26 @@ macro TypeKind inner_kind($Type)
|
|||||||
$endif
|
$endif
|
||||||
}
|
}
|
||||||
|
|
||||||
macro bool is_same($TypeA, $TypeB)
|
macro bool is_same($TypeA, $TypeB) @const
|
||||||
{
|
{
|
||||||
return $TypeA.typeid == $TypeB.typeid;
|
return $TypeA.typeid == $TypeB.typeid;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro bool @has_same(#a, #b, ...)
|
macro bool @has_same(#a, #b, ...) @const
|
||||||
{
|
{
|
||||||
var $type_a = @typeid(#a);
|
var $type_a = @typeid(#a);
|
||||||
$if $type_a != @typeid(#b):
|
$if $type_a != @typeid(#b):
|
||||||
return false;
|
return false;
|
||||||
$endif
|
$endif
|
||||||
$for (var $i = 0; $i < $vacount; $i++)
|
$for (var $i = 0; $i < $vacount; $i++)
|
||||||
$if @typeid($vaexpr($i)) != $type_a:
|
$if @typeid($vaexpr[$i]) != $type_a:
|
||||||
return false;
|
return false;
|
||||||
$endif
|
$endif
|
||||||
$endfor
|
$endfor
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro bool may_load_atomic($Type)
|
macro bool may_load_atomic($Type) @const
|
||||||
{
|
{
|
||||||
$switch ($Type.kindof)
|
$switch ($Type.kindof)
|
||||||
$case SIGNED_INT:
|
$case SIGNED_INT:
|
||||||
@@ -211,7 +221,7 @@ macro bool may_load_atomic($Type)
|
|||||||
$endswitch
|
$endswitch
|
||||||
}
|
}
|
||||||
|
|
||||||
macro lower_to_atomic_compatible_type($Type)
|
macro lower_to_atomic_compatible_type($Type) @const
|
||||||
{
|
{
|
||||||
$switch ($Type.kindof)
|
$switch ($Type.kindof)
|
||||||
$case SIGNED_INT:
|
$case SIGNED_INT:
|
||||||
@@ -237,10 +247,10 @@ macro lower_to_atomic_compatible_type($Type)
|
|||||||
$endswitch
|
$endswitch
|
||||||
}
|
}
|
||||||
|
|
||||||
macro bool is_promotable_to_floatlike($Type) => types::is_floatlike($Type) || types::is_int($Type);
|
macro bool is_promotable_to_floatlike($Type) @const => types::is_floatlike($Type) || types::is_int($Type);
|
||||||
macro bool is_promotable_to_float($Type) => types::is_float($Type) || types::is_int($Type);
|
macro bool is_promotable_to_float($Type) @const => types::is_float($Type) || types::is_int($Type);
|
||||||
|
|
||||||
macro bool is_same_vector_type($Type1, $Type2)
|
macro bool is_same_vector_type($Type1, $Type2) @const
|
||||||
{
|
{
|
||||||
$if $Type1.kindof != TypeKind.VECTOR:
|
$if $Type1.kindof != TypeKind.VECTOR:
|
||||||
return $Type2.kindof != TypeKind.VECTOR;
|
return $Type2.kindof != TypeKind.VECTOR;
|
||||||
@@ -249,7 +259,7 @@ macro bool is_same_vector_type($Type1, $Type2)
|
|||||||
$endif
|
$endif
|
||||||
}
|
}
|
||||||
|
|
||||||
macro bool is_equatable_type($Type)
|
macro bool is_equatable_type($Type) @const
|
||||||
{
|
{
|
||||||
$if $defined($Type.less) || $defined($Type.compare_to) || $defined($Type.equals):
|
$if $defined($Type.less) || $defined($Type.compare_to) || $defined($Type.equals):
|
||||||
return true;
|
return true;
|
||||||
@@ -261,17 +271,31 @@ macro bool is_equatable_type($Type)
|
|||||||
/**
|
/**
|
||||||
* Checks if a type implements the copy protocol.
|
* Checks if a type implements the copy protocol.
|
||||||
**/
|
**/
|
||||||
macro bool implements_copy($Type)
|
macro bool implements_copy($Type) @const
|
||||||
{
|
{
|
||||||
return $defined($Type.copy) && $defined($Type.free);
|
return $defined($Type.copy) && $defined($Type.free);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro bool is_equatable_value(value)
|
macro bool is_equatable_value(value) @deprecated
|
||||||
{
|
{
|
||||||
return is_equatable_type($typeof(value));
|
return is_equatable_type($typeof(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
macro bool is_comparable_value(value)
|
macro bool @equatable_value(#value) @const
|
||||||
|
{
|
||||||
|
return is_equatable_type($typeof(#value));
|
||||||
|
}
|
||||||
|
|
||||||
|
macro bool @comparable_value(#value) @const
|
||||||
|
{
|
||||||
|
$if $defined(#value.less) || $defined(#value.compare_to):
|
||||||
|
return true;
|
||||||
|
$else
|
||||||
|
return $typeof(#value).is_ordered;
|
||||||
|
$endif
|
||||||
|
}
|
||||||
|
|
||||||
|
macro bool is_comparable_value(value) @deprecated
|
||||||
{
|
{
|
||||||
$if $defined(value.less) || $defined(value.compare_to):
|
$if $defined(value.less) || $defined(value.compare_to):
|
||||||
return true;
|
return true;
|
||||||
@@ -298,10 +322,11 @@ enum TypeKind : char
|
|||||||
FUNC,
|
FUNC,
|
||||||
OPTIONAL,
|
OPTIONAL,
|
||||||
ARRAY,
|
ARRAY,
|
||||||
SUBARRAY,
|
SLICE,
|
||||||
VECTOR,
|
VECTOR,
|
||||||
DISTINCT,
|
DISTINCT,
|
||||||
POINTER,
|
POINTER,
|
||||||
|
INTERFACE,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TypeEnum
|
struct TypeEnum
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
module std::core::values;
|
module std::core::values;
|
||||||
|
|
||||||
macro typeid @typeid(#value) @builtin => $typeof(#value).typeid;
|
macro typeid @typeid(#value) @const @builtin => $typeof(#value).typeid;
|
||||||
macro TypeKind @typekind(#value) @builtin => $typeof(#value).kindof;
|
macro TypeKind @typekind(#value) @const @builtin => $typeof(#value).kindof;
|
||||||
macro bool @typeis(#value, $Type) @builtin => $typeof(#value).typeid == $Type.typeid;
|
macro bool @typeis(#value, $Type) @const @builtin => $typeof(#value).typeid == $Type.typeid;
|
||||||
/**
|
/**
|
||||||
* Return true if two values have the same type before any conversions.
|
* Return true if two values have the same type before any conversions.
|
||||||
**/
|
**/
|
||||||
macro bool @is_same_type(#value1, #value2) => $typeof(#value1).typeid == $typeof(#value2).typeid;
|
macro bool @is_same_type(#value1, #value2) @const => $typeof(#value1).typeid == $typeof(#value2).typeid;
|
||||||
macro bool @is_bool(#value) => types::is_bool($typeof(#value));
|
macro bool @is_bool(#value) @const => types::is_bool($typeof(#value));
|
||||||
macro bool @is_int(#value) => types::is_int($typeof(#value));
|
macro bool @is_int(#value) @const => types::is_int($typeof(#value));
|
||||||
macro bool @is_floatlike(#value) => types::is_floatlike($typeof(#value));
|
macro bool @is_floatlike(#value) @const => types::is_floatlike($typeof(#value));
|
||||||
macro bool @is_float(#value) => types::is_float($typeof(#value));
|
macro bool @is_float(#value) @const => types::is_float($typeof(#value));
|
||||||
macro bool @is_promotable_to_floatlike(#value) => types::is_promotable_to_floatlike($typeof(#value));
|
macro bool @is_promotable_to_floatlike(#value) @const => types::is_promotable_to_floatlike($typeof(#value));
|
||||||
macro bool @is_promotable_to_float(#value) => types::is_promotable_to_float($typeof(#value));
|
macro bool @is_promotable_to_float(#value) @const => types::is_promotable_to_float($typeof(#value));
|
||||||
macro bool @is_vector(#value) => types::is_vector($typeof(#value));
|
macro bool @is_vector(#value) @const => types::is_vector($typeof(#value));
|
||||||
macro bool @is_same_vector_type(#value1, #value2) => types::is_same_vector_type($typeof(#value1), $typeof(#value2));
|
macro bool @is_same_vector_type(#value1, #value2) @const => types::is_same_vector_type($typeof(#value1), $typeof(#value2));
|
||||||
macro bool @assign_to(#value1, #value2) => $assignable(#value1, $typeof(#value2));
|
macro bool @assign_to(#value1, #value2) @const => $assignable(#value1, $typeof(#value2));
|
||||||
|
|
||||||
macro promote_int(x)
|
macro promote_int(x)
|
||||||
{
|
{
|
||||||
@@ -26,5 +26,21 @@ macro promote_int(x)
|
|||||||
$endif
|
$endif
|
||||||
}
|
}
|
||||||
|
|
||||||
macro TypeKind @inner_kind(#value) => types::inner_kind($typeof(#value));
|
macro promote_int_same(x, y)
|
||||||
|
{
|
||||||
|
$if @is_int(x):
|
||||||
|
$switch
|
||||||
|
$case @is_vector(y) &&& $typeof(y).inner == float.typeid:
|
||||||
|
return (float)x;
|
||||||
|
$case $typeof(y).typeid == float.typeid:
|
||||||
|
return (float)x;
|
||||||
|
$default:
|
||||||
|
return (double)x;
|
||||||
|
$endswitch
|
||||||
|
$else
|
||||||
|
return x;
|
||||||
|
$endif
|
||||||
|
}
|
||||||
|
|
||||||
|
macro TypeKind @inner_kind(#value) @const => types::inner_kind($typeof(#value));
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,18 @@ fn void Rc4.init(&self, char[] key)
|
|||||||
self.j = 0;
|
self.j = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a single pass of en/decryption using a particular key.
|
||||||
|
* @param [in] key
|
||||||
|
* @param [inout] data
|
||||||
|
**/
|
||||||
|
fn void crypt(char[] key, char[] data)
|
||||||
|
{
|
||||||
|
Rc4 rc4;
|
||||||
|
rc4.init(key);
|
||||||
|
rc4.crypt(data, data);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encrypt or decrypt a sequence of bytes.
|
* Encrypt or decrypt a sequence of bytes.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -3,22 +3,22 @@ import std::io;
|
|||||||
|
|
||||||
struct CsvReader
|
struct CsvReader
|
||||||
{
|
{
|
||||||
InStream* stream;
|
InStream stream;
|
||||||
String separator;
|
String separator;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void CsvReader.init(&self, InStream* stream, String separator = ",")
|
fn void CsvReader.init(&self, InStream stream, String separator = ",")
|
||||||
{
|
{
|
||||||
self.stream = stream;
|
self.stream = stream;
|
||||||
self.separator = separator;
|
self.separator = separator;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String[]! CsvReader.read_new_row(self, Allocator* allocator = allocator::heap())
|
fn String[]! CsvReader.read_new_row(self, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
return self.read_new_row_with_allocator(allocator::temp()) @inline;
|
return self.read_new_row_with_allocator(allocator::temp()) @inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String[]! CsvReader.read_new_row_with_allocator(self, Allocator* allocator = allocator::heap())
|
fn String[]! CsvReader.read_new_row_with_allocator(self, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
@pool(allocator)
|
@pool(allocator)
|
||||||
{
|
{
|
||||||
@@ -45,7 +45,7 @@ macro CsvReader.@each_row(self, int rows = int.max; @body(String[] row))
|
|||||||
String sep = self.separator;
|
String sep = self.separator;
|
||||||
while (rows--)
|
while (rows--)
|
||||||
{
|
{
|
||||||
@stack_mem(512; Allocator* mem)
|
@stack_mem(512; Allocator mem)
|
||||||
{
|
{
|
||||||
String[] parts;
|
String[] parts;
|
||||||
@pool()
|
@pool()
|
||||||
|
|||||||
@@ -15,11 +15,29 @@ fault JsonParsingError
|
|||||||
INVALID_NUMBER,
|
INVALID_NUMBER,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Object*! parse(InStream* s, Allocator* allocator = allocator::heap())
|
fn Object*! parse_string(String s, Allocator allocator = allocator::heap())
|
||||||
|
{
|
||||||
|
return parse(ByteReader{}.init(s), allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Object*! temp_parse_string(String s)
|
||||||
|
{
|
||||||
|
return parse(ByteReader{}.init(s), allocator::temp());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Object*! parse(InStream s, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
JsonContext context = { .last_string = dstring::new_with_capacity(64, allocator), .stream = s, .allocator = allocator };
|
JsonContext context = { .last_string = dstring::new_with_capacity(64, allocator), .stream = s, .allocator = allocator };
|
||||||
defer context.last_string.free();
|
defer context.last_string.free();
|
||||||
return parse_any(&context);
|
@pool(allocator)
|
||||||
|
{
|
||||||
|
return parse_any(&context);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Object*! temp_parse(InStream s)
|
||||||
|
{
|
||||||
|
return parse(s, allocator::temp());
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Implementation follows --
|
// -- Implementation follows --
|
||||||
@@ -44,8 +62,8 @@ enum JsonTokenType @local
|
|||||||
struct JsonContext @local
|
struct JsonContext @local
|
||||||
{
|
{
|
||||||
uint line;
|
uint line;
|
||||||
InStream* stream;
|
InStream stream;
|
||||||
Allocator* allocator;
|
Allocator allocator;
|
||||||
JsonTokenType token;
|
JsonTokenType token;
|
||||||
DString last_string;
|
DString last_string;
|
||||||
double last_number;
|
double last_number;
|
||||||
@@ -170,7 +188,7 @@ fn Object*! parse_array(JsonContext* context) @local
|
|||||||
while (token != JsonTokenType.RBRACKET)
|
while (token != JsonTokenType.RBRACKET)
|
||||||
{
|
{
|
||||||
Object* element = parse_from_token(context, token)!;
|
Object* element = parse_from_token(context, token)!;
|
||||||
list.append(element);
|
list.push(element);
|
||||||
token = advance(context)!;
|
token = advance(context)!;
|
||||||
if (token == JsonTokenType.COMMA)
|
if (token == JsonTokenType.COMMA)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ module std::io;
|
|||||||
|
|
||||||
struct BitReader
|
struct BitReader
|
||||||
{
|
{
|
||||||
InStream* reader;
|
InStream reader;
|
||||||
uint bits;
|
uint bits;
|
||||||
uint len;
|
uint len;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void BitReader.init(&self, InStream* byte_reader)
|
fn void BitReader.init(&self, InStream byte_reader)
|
||||||
{
|
{
|
||||||
*self = { .reader = byte_reader };
|
*self = { .reader = byte_reader };
|
||||||
}
|
}
|
||||||
@@ -40,12 +40,12 @@ fn char! BitReader.read_bits(&self, uint nbits)
|
|||||||
|
|
||||||
struct BitWriter
|
struct BitWriter
|
||||||
{
|
{
|
||||||
OutStream* writer;
|
OutStream writer;
|
||||||
uint bits;
|
uint bits;
|
||||||
uint len;
|
uint len;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void BitWriter.init(&self, OutStream* byte_writer)
|
fn void BitWriter.init(&self, OutStream byte_writer)
|
||||||
{
|
{
|
||||||
*self = { .writer = byte_writer };
|
*self = { .writer = byte_writer };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ fn char[]! load_buffer(String filename, char[] buffer)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn char[]! load_new(String filename, Allocator* allocator = allocator::heap())
|
fn char[]! load_new(String filename, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
File file = open(filename, "rb")!;
|
File file = open(filename, "rb")!;
|
||||||
defer (void)file.close();
|
defer (void)file.close();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const int PRINTF_NTOA_BUFFER_SIZE = 256;
|
|||||||
|
|
||||||
interface Printable
|
interface Printable
|
||||||
{
|
{
|
||||||
fn String to_new_string(Allocator *allocator) @optional;
|
fn String to_new_string(Allocator allocator) @optional;
|
||||||
fn usz! to_format(Formatter* formatter) @optional;
|
fn usz! to_format(Formatter* formatter) @optional;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ fn usz! Formatter.out(&self, char c) @private
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usz! Formatter.print_with_function(&self, Printable* arg)
|
fn usz! Formatter.print_with_function(&self, Printable arg)
|
||||||
{
|
{
|
||||||
if (&arg.to_format)
|
if (&arg.to_format)
|
||||||
{
|
{
|
||||||
@@ -85,6 +85,7 @@ fn usz! Formatter.print_with_function(&self, Printable* arg)
|
|||||||
self.width = old_width;
|
self.width = old_width;
|
||||||
self.prec = old_prec;
|
self.prec = old_prec;
|
||||||
}
|
}
|
||||||
|
if (!arg) return self.out_substr("(null)");
|
||||||
return arg.to_format(self);
|
return arg.to_format(self);
|
||||||
}
|
}
|
||||||
if (&arg.to_new_string)
|
if (&arg.to_new_string)
|
||||||
@@ -98,7 +99,8 @@ fn usz! Formatter.print_with_function(&self, Printable* arg)
|
|||||||
self.width = old_width;
|
self.width = old_width;
|
||||||
self.prec = old_prec;
|
self.prec = old_prec;
|
||||||
}
|
}
|
||||||
@stack_mem(1024; Allocator* mem)
|
if (!arg) return self.out_substr("(null)");
|
||||||
|
@stack_mem(1024; Allocator mem)
|
||||||
{
|
{
|
||||||
return self.out_substr(arg.to_new_string(mem));
|
return self.out_substr(arg.to_new_string(mem));
|
||||||
};
|
};
|
||||||
@@ -107,7 +109,7 @@ fn usz! Formatter.print_with_function(&self, Printable* arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn usz! Formatter.out_str(&self, any* arg) @private
|
fn usz! Formatter.out_str(&self, any arg) @private
|
||||||
{
|
{
|
||||||
switch (arg.type.kindof)
|
switch (arg.type.kindof)
|
||||||
{
|
{
|
||||||
@@ -119,7 +121,7 @@ fn usz! Formatter.out_str(&self, any* arg) @private
|
|||||||
case FAULT:
|
case FAULT:
|
||||||
return self.out_substr((*(anyfault*)arg.ptr).nameof);
|
return self.out_substr((*(anyfault*)arg.ptr).nameof);
|
||||||
case ANY:
|
case ANY:
|
||||||
return self.out_str(*(any**)arg);
|
return self.out_str(*(any*)arg);
|
||||||
case OPTIONAL:
|
case OPTIONAL:
|
||||||
unreachable();
|
unreachable();
|
||||||
case SIGNED_INT:
|
case SIGNED_INT:
|
||||||
@@ -149,18 +151,9 @@ fn usz! Formatter.out_str(&self, any* arg) @private
|
|||||||
return self.out_substr(*(bool*)arg.ptr ? "true" : "false");
|
return self.out_substr(*(bool*)arg.ptr ? "true" : "false");
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
usz! n = self.print_with_function((Printable*)arg);
|
usz! n = self.print_with_function((Printable)arg);
|
||||||
if (catch err = n)
|
if (try n) return n;
|
||||||
{
|
if (@catch(n) != SearchResult.MISSING) n!;
|
||||||
case SearchResult.MISSING:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return err?;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
switch (arg.type.kindof)
|
switch (arg.type.kindof)
|
||||||
{
|
{
|
||||||
case ENUM:
|
case ENUM:
|
||||||
@@ -178,14 +171,23 @@ fn usz! Formatter.out_str(&self, any* arg) @private
|
|||||||
case DISTINCT:
|
case DISTINCT:
|
||||||
if (arg.type == ZString.typeid)
|
if (arg.type == ZString.typeid)
|
||||||
{
|
{
|
||||||
return self.out_substr(((ZString*)arg).str_view());
|
return self.out_substr(*(ZString*)arg ? ((ZString*)arg).str_view() : "(null)");
|
||||||
}
|
}
|
||||||
if (arg.type == DString.typeid)
|
if (arg.type == DString.typeid)
|
||||||
{
|
{
|
||||||
return self.out_substr(((DString*)arg).str_view());
|
return self.out_substr(*(DString*)arg ? ((DString*)arg).str_view() : "(null)");
|
||||||
}
|
}
|
||||||
return self.out_str(arg.as_inner());
|
return self.out_str(arg.as_inner());
|
||||||
case POINTER:
|
case POINTER:
|
||||||
|
typeid inner = arg.type.inner;
|
||||||
|
void** pointer = arg.ptr;
|
||||||
|
if (arg.type.inner != void.typeid)
|
||||||
|
{
|
||||||
|
any deref = any_make(*pointer, inner);
|
||||||
|
n = self.print_with_function((Printable)deref);
|
||||||
|
if (try n) return n;
|
||||||
|
if (@catch(n) != SearchResult.MISSING) n!;
|
||||||
|
}
|
||||||
PrintFlags flags = self.flags;
|
PrintFlags flags = self.flags;
|
||||||
uint width = self.width;
|
uint width = self.width;
|
||||||
defer
|
defer
|
||||||
@@ -193,8 +195,8 @@ fn usz! Formatter.out_str(&self, any* arg) @private
|
|||||||
self.flags = flags;
|
self.flags = flags;
|
||||||
self.width = width;
|
self.width = width;
|
||||||
}
|
}
|
||||||
self.flags = {};
|
|
||||||
self.width = 0;
|
self.width = 0;
|
||||||
|
self.out_substr("0x")!;
|
||||||
return self.ntoa_any(arg, 16);
|
return self.ntoa_any(arg, 16);
|
||||||
case ARRAY:
|
case ARRAY:
|
||||||
// this is SomeType[*] so grab the "SomeType"
|
// this is SomeType[*] so grab the "SomeType"
|
||||||
@@ -246,7 +248,7 @@ fn usz! Formatter.out_str(&self, any* arg) @private
|
|||||||
}
|
}
|
||||||
len += self.out_substr(">]")!;
|
len += self.out_substr(">]")!;
|
||||||
return len;
|
return len;
|
||||||
case SUBARRAY:
|
case SLICE:
|
||||||
// this is SomeType[] so grab the "SomeType"
|
// this is SomeType[] so grab the "SomeType"
|
||||||
typeid inner = arg.type.inner;
|
typeid inner = arg.type.inner;
|
||||||
if (inner == char.typeid)
|
if (inner == char.typeid)
|
||||||
@@ -291,7 +293,7 @@ fn void! out_null_fn(void* data @unused, char c @unused) @private
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
fn usz! Formatter.vprintf(&self, String format, any*[] anys)
|
fn usz! Formatter.vprintf(&self, String format, any[] anys)
|
||||||
{
|
{
|
||||||
if (!self.out_fn)
|
if (!self.out_fn)
|
||||||
{
|
{
|
||||||
@@ -358,7 +360,7 @@ fn usz! Formatter.vprintf(&self, String format, any*[] anys)
|
|||||||
// evaluate specifier
|
// evaluate specifier
|
||||||
uint base = 0;
|
uint base = 0;
|
||||||
if (variant_index >= anys.len) return PrintFault.MISSING_ARG?;
|
if (variant_index >= anys.len) return PrintFault.MISSING_ARG?;
|
||||||
any* current = anys[variant_index++];
|
any current = anys[variant_index++];
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
case 'd':
|
case 'd':
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ fn usz! Formatter.adjust(&self, usz len) @local
|
|||||||
return self.pad(' ', self.width, len);
|
return self.pad(' ', self.width, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uint128! int_from_any(any* arg, bool *is_neg) @private
|
fn uint128! int_from_any(any arg, bool *is_neg) @private
|
||||||
{
|
{
|
||||||
switch (arg.type.kindof)
|
switch (arg.type.kindof)
|
||||||
{
|
{
|
||||||
@@ -63,14 +63,11 @@ fn uint128! int_from_any(any* arg, bool *is_neg) @private
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn FloatType! float_from_any(any* arg) @private
|
fn FloatType! float_from_any(any arg) @private
|
||||||
{
|
{
|
||||||
$if env::F128_SUPPORT:
|
$if env::F128_SUPPORT:
|
||||||
if (arg.type == float128.typeid) return (FloatType)*((float128*)arg.ptr);
|
if (arg.type == float128.typeid) return (FloatType)*((float128*)arg.ptr);
|
||||||
$endif
|
$endif
|
||||||
$if env::F16_SUPPORT:
|
|
||||||
if (arg.type == float16.typeid) return *((float16*)arg.ptr);
|
|
||||||
$endif
|
|
||||||
if (arg.type.kindof == TypeKind.DISTINCT)
|
if (arg.type.kindof == TypeKind.DISTINCT)
|
||||||
{
|
{
|
||||||
return float_from_any(arg.as_inner());
|
return float_from_any(arg.as_inner());
|
||||||
@@ -588,14 +585,14 @@ fn usz! Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint ba
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn usz! Formatter.ntoa_any(&self, any* arg, uint base) @private
|
fn usz! Formatter.ntoa_any(&self, any arg, uint base) @private
|
||||||
{
|
{
|
||||||
bool is_neg;
|
bool is_neg;
|
||||||
uint128 val = int_from_any(arg, &is_neg)!!;
|
uint128 val = int_from_any(arg, &is_neg)!!;
|
||||||
return self.ntoa(val, is_neg, base) @inline;
|
return self.ntoa(val, is_neg, base) @inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usz! Formatter.out_char(&self, any* arg) @private
|
fn usz! Formatter.out_char(&self, any arg) @private
|
||||||
{
|
{
|
||||||
usz len = 1;
|
usz len = 1;
|
||||||
uint l = 1;
|
uint l = 1;
|
||||||
@@ -631,15 +628,15 @@ fn usz! Formatter.out_reverse(&self, char[] buf) @private
|
|||||||
usz buffer_start_idx = self.idx;
|
usz buffer_start_idx = self.idx;
|
||||||
usz len = buf.len;
|
usz len = buf.len;
|
||||||
// pad spaces up to given width
|
// pad spaces up to given width
|
||||||
if (!self.flags.zeropad)
|
if (!self.flags.zeropad && !self.flags.left)
|
||||||
{
|
{
|
||||||
n += self.adjust(len)!;
|
n += self.pad(' ', self.width, len)!;
|
||||||
}
|
}
|
||||||
// reverse string
|
// reverse string
|
||||||
while (len) n += self.out(buf[--len])!;
|
while (len) n += self.out(buf[--len])!;
|
||||||
|
|
||||||
// append pad spaces up to given width
|
// append pad spaces up to given width
|
||||||
n += self.adjust(self.idx - buffer_start_idx)!;
|
n += self.adjust(n)!;
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -649,21 +646,21 @@ fn void! printf_advance_format(usz format_len, usz *index_ptr) @inline @private
|
|||||||
if (val >= format_len) return FormattingFault.UNTERMINATED_FORMAT?;
|
if (val >= format_len) return FormattingFault.UNTERMINATED_FORMAT?;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn any*! next_any(any** args_ptr, usz args_len, usz* arg_index_ptr) @inline @private
|
fn any! next_any(any* args_ptr, usz args_len, usz* arg_index_ptr) @inline @private
|
||||||
{
|
{
|
||||||
if (*arg_index_ptr >= args_len) return FormattingFault.MISSING_ARG?;
|
if (*arg_index_ptr >= args_len) return FormattingFault.MISSING_ARG?;
|
||||||
return args_ptr[(*arg_index_ptr)++];
|
return args_ptr[(*arg_index_ptr)++];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn int! printf_parse_format_field(
|
fn int! printf_parse_format_field(
|
||||||
any** args_ptr, usz args_len, usz* args_index_ptr,
|
any* args_ptr, usz args_len, usz* args_index_ptr,
|
||||||
char* format_ptr, usz format_len, usz* index_ptr) @inline @private
|
char* format_ptr, usz format_len, usz* index_ptr) @inline @private
|
||||||
{
|
{
|
||||||
char c = format_ptr[*index_ptr];
|
char c = format_ptr[*index_ptr];
|
||||||
if (c.is_digit()) return simple_atoi(format_ptr, format_len, index_ptr);
|
if (c.is_digit()) return simple_atoi(format_ptr, format_len, index_ptr);
|
||||||
if (c != '*') return 0;
|
if (c != '*') return 0;
|
||||||
printf_advance_format(format_len, index_ptr)!;
|
printf_advance_format(format_len, index_ptr)!;
|
||||||
any* val = next_any(args_ptr, args_len, args_index_ptr)!;
|
any val = next_any(args_ptr, args_len, args_index_ptr)!;
|
||||||
if (!val.type.kindof.is_int()) return FormattingFault.INVALID_WIDTH_ARG?;
|
if (!val.type.kindof.is_int()) return FormattingFault.INVALID_WIDTH_ARG?;
|
||||||
uint! intval = types::any_to_int(val, int);
|
uint! intval = types::any_to_int(val, int);
|
||||||
return intval ?? FormattingFault.INVALID_WIDTH_ARG?;
|
return intval ?? FormattingFault.INVALID_WIDTH_ARG?;
|
||||||
|
|||||||
167
lib/std/io/io.c3
167
lib/std/io/io.c3
@@ -46,12 +46,18 @@ fault IoError
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param stream
|
* Read from a stream (default is stdin) to the next "\n"
|
||||||
* @require @is_instream(stream)
|
* or to the end of the stream, whatever comes first.
|
||||||
|
* "\r" will be filtered from the String.
|
||||||
|
*
|
||||||
|
* @param stream `The stream to read from.`
|
||||||
|
* @require @is_instream(stream) `The stream must implement InStream.`
|
||||||
|
* @param [inout] allocator `the allocator to use.`
|
||||||
|
* @return `The string containing the data read.`
|
||||||
**/
|
**/
|
||||||
macro String! readline(stream = io::stdin(), Allocator* allocator = allocator::heap())
|
macro String! readline(stream = io::stdin(), Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
bool $is_stream = @typeid(stream) == InStream*.typeid;
|
bool $is_stream = @typeis(stream, InStream);
|
||||||
$if $is_stream:
|
$if $is_stream:
|
||||||
$typeof(&stream.read_byte) func = &stream.read_byte;
|
$typeof(&stream.read_byte) func = &stream.read_byte;
|
||||||
char val = func((void*)stream)!;
|
char val = func((void*)stream)!;
|
||||||
@@ -84,38 +90,67 @@ macro String! readline(stream = io::stdin(), Allocator* allocator = allocator::h
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro String! treadline(stream = io::stdin()) => readline(stream, allocator::temp()) @inline;
|
/**
|
||||||
|
* Reads a string, see `readline`, except the it is allocated
|
||||||
|
* on the temporary allocator and does not need to be freed.
|
||||||
|
*
|
||||||
|
* @param stream `The stream to read from.`
|
||||||
|
* @require @is_instream(stream) `The stream must implement InStream.`
|
||||||
|
* @return `The temporary string containing the data read.`
|
||||||
|
**/
|
||||||
|
macro String! treadline(stream = io::stdin())
|
||||||
|
{
|
||||||
|
return readline(stream, allocator::temp()) @inline;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require @is_outstream(out) "The output must implement OutStream"
|
* Print a value to a stream.
|
||||||
|
*
|
||||||
|
* @param out `the stream to print to`
|
||||||
|
* @param x `the value to print`
|
||||||
|
* @require @is_outstream(out) `The output must implement OutStream.`
|
||||||
|
* @return `the number of bytes printed.`
|
||||||
*/
|
*/
|
||||||
macro usz! fprint(out, x)
|
macro usz! fprint(out, x)
|
||||||
{
|
{
|
||||||
var $Type = $typeof(x);
|
var $Type = $typeof(x);
|
||||||
$switch ($Type)
|
$switch ($Type)
|
||||||
$case String:
|
$case String: return out.write(x);
|
||||||
return out.write(x);
|
$case ZString: return out.write(x.str_view());
|
||||||
$case ZString:
|
$case DString: return out.write(x.str_view());
|
||||||
return out.write(x.str_view());
|
$default:
|
||||||
$case DString:
|
$if $assignable(x, String):
|
||||||
return out.write(x.str_view());
|
return out.write((String)x);
|
||||||
$default:
|
$else
|
||||||
$if $assignable(x, String):
|
return fprintf(out, "%s", x);
|
||||||
return out.write((String)x);
|
$endif
|
||||||
$else
|
|
||||||
return fprintf(out, "%s", x);
|
|
||||||
$endif
|
|
||||||
$endswitch
|
$endswitch
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usz! fprintf(OutStream* out, String format, args...)
|
/**
|
||||||
|
* Prints using a 'printf'-style formatting string.
|
||||||
|
* See `printf` for details on formatting.
|
||||||
|
*
|
||||||
|
* @param [inout] out `The OutStream to print to`
|
||||||
|
* @param [in] format `The printf-style format string`
|
||||||
|
* @return `the number of characters printed`
|
||||||
|
**/
|
||||||
|
fn usz! fprintf(OutStream out, String format, args...)
|
||||||
{
|
{
|
||||||
Formatter formatter;
|
Formatter formatter;
|
||||||
formatter.init(&out_putstream_fn, &out);
|
formatter.init(&out_putstream_fn, &out);
|
||||||
return formatter.vprintf(format, args);
|
return formatter.vprintf(format, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usz! fprintfn(OutStream* out, String format, args...) @maydiscard
|
/**
|
||||||
|
* Prints using a 'printf'-style formatting string,
|
||||||
|
* appending '\n' at the end. See `printf`.
|
||||||
|
*
|
||||||
|
* @param [inout] out `The OutStream to print to`
|
||||||
|
* @param [in] format `The printf-style format string`
|
||||||
|
* @return `the number of characters printed`
|
||||||
|
**/
|
||||||
|
fn usz! fprintfn(OutStream out, String format, args...) @maydiscard
|
||||||
{
|
{
|
||||||
Formatter formatter;
|
Formatter formatter;
|
||||||
formatter.init(&out_putstream_fn, &out);
|
formatter.init(&out_putstream_fn, &out);
|
||||||
@@ -133,7 +168,7 @@ macro usz! fprintn(out, x = "")
|
|||||||
usz len = fprint(out, x)!;
|
usz len = fprint(out, x)!;
|
||||||
out.write_byte('\n')!;
|
out.write_byte('\n')!;
|
||||||
$switch
|
$switch
|
||||||
$case @typeid(out) == OutStream*.typeid:
|
$case @typeid(out) == OutStream.typeid:
|
||||||
if (&out.flush) out.flush()!;
|
if (&out.flush) out.flush()!;
|
||||||
$case $defined(out.flush):
|
$case $defined(out.flush):
|
||||||
out.flush()!;
|
out.flush()!;
|
||||||
@@ -141,21 +176,37 @@ macro usz! fprintn(out, x = "")
|
|||||||
return len + 1;
|
return len + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print any value to stdout.
|
||||||
|
**/
|
||||||
macro void print(x)
|
macro void print(x)
|
||||||
{
|
{
|
||||||
(void)fprint(io::stdout(), x);
|
(void)fprint(io::stdout(), x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print any value to stdout, appending an '\n’ after.
|
||||||
|
*
|
||||||
|
* @param x "The value to print"
|
||||||
|
**/
|
||||||
macro void printn(x = "")
|
macro void printn(x = "")
|
||||||
{
|
{
|
||||||
(void)fprintn(io::stdout(), x);
|
(void)fprintn(io::stdout(), x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print any value to stderr.
|
||||||
|
**/
|
||||||
macro void eprint(x)
|
macro void eprint(x)
|
||||||
{
|
{
|
||||||
(void)fprint(io::stderr(), x);
|
(void)fprint(io::stderr(), x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print any value to stderr, appending an '\n’ after.
|
||||||
|
*
|
||||||
|
* @param x "The value to print"
|
||||||
|
**/
|
||||||
macro void eprintn(x)
|
macro void eprintn(x)
|
||||||
{
|
{
|
||||||
(void)fprintn(io::stderr(), x);
|
(void)fprintn(io::stderr(), x);
|
||||||
@@ -164,7 +215,7 @@ macro void eprintn(x)
|
|||||||
|
|
||||||
fn void! out_putstream_fn(void* data, char c) @private
|
fn void! out_putstream_fn(void* data, char c) @private
|
||||||
{
|
{
|
||||||
OutStream** stream = data;
|
OutStream* stream = data;
|
||||||
return (*stream).write_byte(c);
|
return (*stream).write_byte(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,6 +224,20 @@ fn void! out_putchar_fn(void* data @unused, char c) @private
|
|||||||
libc::putchar(c);
|
libc::putchar(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints using a 'printf'-style formatting string.
|
||||||
|
* To print integer numbers, use "%d" or "%x"/"%X,
|
||||||
|
* the latter gives the hexadecimal representation.
|
||||||
|
*
|
||||||
|
* All types can be printed using "%s" which gives
|
||||||
|
* the default representation of the value.
|
||||||
|
*
|
||||||
|
* To create a custom output for a type, implement
|
||||||
|
* the Printable interface.
|
||||||
|
*
|
||||||
|
* @param [in] format `The printf-style format string`
|
||||||
|
* @return `the number of characters printed`
|
||||||
|
**/
|
||||||
fn usz! printf(String format, args...) @maydiscard
|
fn usz! printf(String format, args...) @maydiscard
|
||||||
{
|
{
|
||||||
Formatter formatter;
|
Formatter formatter;
|
||||||
@@ -180,6 +245,13 @@ fn usz! printf(String format, args...) @maydiscard
|
|||||||
return formatter.vprintf(format, args);
|
return formatter.vprintf(format, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints using a 'printf'-style formatting string,
|
||||||
|
* appending '\n' at the end. See `printf`.
|
||||||
|
*
|
||||||
|
* @param [in] format `The printf-style format string`
|
||||||
|
* @return `the number of characters printed`
|
||||||
|
**/
|
||||||
fn usz! printfn(String format, args...) @maydiscard
|
fn usz! printfn(String format, args...) @maydiscard
|
||||||
{
|
{
|
||||||
Formatter formatter;
|
Formatter formatter;
|
||||||
@@ -190,19 +262,33 @@ fn usz! printfn(String format, args...) @maydiscard
|
|||||||
return len + 1;
|
return len + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints using a 'printf'-style formatting string
|
||||||
|
* to stderr.
|
||||||
|
*
|
||||||
|
* @param [in] format `The printf-style format string`
|
||||||
|
* @return `the number of characters printed`
|
||||||
|
**/
|
||||||
fn usz! eprintf(String format, args...) @maydiscard
|
fn usz! eprintf(String format, args...) @maydiscard
|
||||||
{
|
{
|
||||||
Formatter formatter;
|
Formatter formatter;
|
||||||
OutStream* stream = stderr();
|
OutStream stream = stderr();
|
||||||
formatter.init(&out_putstream_fn, &stream);
|
formatter.init(&out_putstream_fn, &stream);
|
||||||
return formatter.vprintf(format, args);
|
return formatter.vprintf(format, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints using a 'printf'-style formatting string,
|
||||||
|
* to stderr appending '\n' at the end. See `printf`.
|
||||||
|
*
|
||||||
|
* @param [in] format `The printf-style format string`
|
||||||
|
* @return `the number of characters printed`
|
||||||
|
**/
|
||||||
fn usz! eprintfn(String format, args...) @maydiscard
|
fn usz! eprintfn(String format, args...) @maydiscard
|
||||||
{
|
{
|
||||||
Formatter formatter;
|
Formatter formatter;
|
||||||
OutStream* stream = stderr();
|
OutStream stream = stderr();
|
||||||
formatter.init(&out_putstream_fn, &stream);
|
formatter.init(&out_putstream_fn, &stream);
|
||||||
usz len = formatter.vprintf(format, args)! + 1;
|
usz len = formatter.vprintf(format, args)! + 1;
|
||||||
stderr().write_byte('\n')!;
|
stderr().write_byte('\n')!;
|
||||||
@@ -210,6 +296,14 @@ fn usz! eprintfn(String format, args...) @maydiscard
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints using a 'printf'-style formatting string,
|
||||||
|
* to a string buffer. See `printf`.
|
||||||
|
*
|
||||||
|
* @param [inout] buffer `The buffer to print to`
|
||||||
|
* @param [in] format `The printf-style format string`
|
||||||
|
* @return `a slice formed from the "buffer" with the resulting length.`
|
||||||
|
**/
|
||||||
fn char[]! bprintf(char[] buffer, String format, args...) @maydiscard
|
fn char[]! bprintf(char[] buffer, String format, args...) @maydiscard
|
||||||
{
|
{
|
||||||
Formatter formatter;
|
Formatter formatter;
|
||||||
@@ -219,6 +313,7 @@ fn char[]! bprintf(char[] buffer, String format, args...) @maydiscard
|
|||||||
return buffer[:data.written];
|
return buffer[:data.written];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used to print to a buffer.
|
||||||
fn void! out_buffer_fn(void *data, char c) @private
|
fn void! out_buffer_fn(void *data, char c) @private
|
||||||
{
|
{
|
||||||
BufferData *buffer_data = data;
|
BufferData *buffer_data = data;
|
||||||
@@ -226,22 +321,30 @@ fn void! out_buffer_fn(void *data, char c) @private
|
|||||||
buffer_data.buffer[buffer_data.written++] = c;
|
buffer_data.buffer[buffer_data.written++] = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used for buffer printing
|
||||||
struct BufferData @private
|
struct BufferData @private
|
||||||
{
|
{
|
||||||
char[] buffer;
|
char[] buffer;
|
||||||
usz written;
|
usz written;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only available with LIBC
|
||||||
module std::io @if (env::LIBC);
|
module std::io @if (env::LIBC);
|
||||||
import libc;
|
import libc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Libc `putchar`, prints a single character to stdout.
|
||||||
|
**/
|
||||||
fn void putchar(char c) @inline
|
fn void putchar(char c) @inline
|
||||||
{
|
{
|
||||||
libc::putchar(c);
|
libc::putchar(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get standard out.
|
||||||
|
*
|
||||||
|
* @return `stdout as a File`
|
||||||
|
**/
|
||||||
fn File* stdout()
|
fn File* stdout()
|
||||||
{
|
{
|
||||||
static File file;
|
static File file;
|
||||||
@@ -249,6 +352,11 @@ fn File* stdout()
|
|||||||
return &file;
|
return &file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get standard err.
|
||||||
|
*
|
||||||
|
* @return `stderr as a File`
|
||||||
|
**/
|
||||||
fn File* stderr()
|
fn File* stderr()
|
||||||
{
|
{
|
||||||
static File file;
|
static File file;
|
||||||
@@ -256,6 +364,11 @@ fn File* stderr()
|
|||||||
return &file;
|
return &file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get standard in.
|
||||||
|
*
|
||||||
|
* @return `stdin as a File`
|
||||||
|
**/
|
||||||
fn File* stdin()
|
fn File* stdin()
|
||||||
{
|
{
|
||||||
static File file;
|
static File file;
|
||||||
@@ -271,7 +384,7 @@ File stderr_file;
|
|||||||
|
|
||||||
fn void putchar(char c) @inline
|
fn void putchar(char c) @inline
|
||||||
{
|
{
|
||||||
(void)stdout_file.putc(c);
|
(void)stdout_file.write_byte(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn File* stdout()
|
fn File* stdout()
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ fn bool native_is_file(String path)
|
|||||||
$case env::DARWIN:
|
$case env::DARWIN:
|
||||||
$case env::LINUX:
|
$case env::LINUX:
|
||||||
Stat stat;
|
Stat stat;
|
||||||
return @ok(native_stat(&stat, path)) && stat.st_mode & libc::S_IFREG;
|
return @ok(native_stat(&stat, path)) && libc_S_ISTYPE(stat.st_mode, libc::S_IFREG);
|
||||||
$default:
|
$default:
|
||||||
File! f = file::open(path, "r");
|
File! f = file::open(path, "r");
|
||||||
defer (void)f.close();
|
defer (void)f.close();
|
||||||
@@ -107,7 +107,7 @@ fn bool native_is_dir(String path)
|
|||||||
{
|
{
|
||||||
$if env::DARWIN || env::LINUX:
|
$if env::DARWIN || env::LINUX:
|
||||||
Stat stat;
|
Stat stat;
|
||||||
return @ok(native_stat(&stat, path)) && stat.st_mode & libc::S_IFDIR;
|
return @ok(native_stat(&stat, path)) && libc_S_ISTYPE(stat.st_mode, libc::S_IFDIR);
|
||||||
$else
|
$else
|
||||||
return native_file_or_dir_exists(path) && !native_is_file(path);
|
return native_file_or_dir_exists(path) && !native_is_file(path);
|
||||||
$endif
|
$endif
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
module std::io::os;
|
module std::io::os;
|
||||||
import libc, std::os;
|
import libc, std::os;
|
||||||
|
|
||||||
macro String! getcwd(Allocator* allocator = allocator::heap())
|
macro String! getcwd(Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
$switch
|
$switch
|
||||||
$case env::WIN32:
|
$case env::WIN32:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
module std::io::os @if(env::POSIX);
|
module std::io::os @if(env::POSIX);
|
||||||
import std::io, std::os;
|
import std::io, std::os;
|
||||||
|
|
||||||
fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator* allocator)
|
fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator allocator)
|
||||||
{
|
{
|
||||||
PathList list;
|
PathList list;
|
||||||
list.new_init(.allocator = allocator);
|
list.new_init(.allocator = allocator);
|
||||||
@@ -16,7 +16,7 @@ fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Al
|
|||||||
if (entry.d_type == posix::DT_LNK && no_symlinks) continue;
|
if (entry.d_type == posix::DT_LNK && no_symlinks) continue;
|
||||||
if (entry.d_type == posix::DT_DIR && no_dirs) continue;
|
if (entry.d_type == posix::DT_DIR && no_dirs) continue;
|
||||||
Path path = path::new(name, allocator)!!;
|
Path path = path::new(name, allocator)!!;
|
||||||
list.append(path);
|
list.push(path);
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,7 @@ fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Al
|
|||||||
module std::io::os @if(env::WIN32);
|
module std::io::os @if(env::WIN32);
|
||||||
import std::time, std::os, std::io;
|
import std::time, std::os, std::io;
|
||||||
|
|
||||||
fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator* allocator)
|
fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator allocator)
|
||||||
{
|
{
|
||||||
PathList list;
|
PathList list;
|
||||||
list.new_init(.allocator = allocator);
|
list.new_init(.allocator = allocator);
|
||||||
@@ -43,7 +43,7 @@ fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Al
|
|||||||
{
|
{
|
||||||
String filename = string::temp_from_wstring((WString)&find_data.cFileName)!;
|
String filename = string::temp_from_wstring((WString)&find_data.cFileName)!;
|
||||||
if (filename == ".." || filename == ".") continue;
|
if (filename == ".." || filename == ".") continue;
|
||||||
list.append(path::new(filename, allocator)!);
|
list.push(path::new(filename, allocator)!);
|
||||||
};
|
};
|
||||||
} while (win32::findNextFileW(find, &find_data));
|
} while (win32::findNextFileW(find, &find_data));
|
||||||
return list;
|
return list;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ fn void! native_rmtree(Path dir)
|
|||||||
{
|
{
|
||||||
String name = ((ZString)&entry.name).str_view();
|
String name = ((ZString)&entry.name).str_view();
|
||||||
if (!name || name == "." || name == "..") continue;
|
if (!name || name == "." || name == "..") continue;
|
||||||
Path new_path = dir.tappend(name)!;
|
Path new_path = dir.temp_append(name)!;
|
||||||
if (entry.d_type == posix::DT_DIR)
|
if (entry.d_type == posix::DT_DIR)
|
||||||
{
|
{
|
||||||
native_rmtree(new_path)!;
|
native_rmtree(new_path)!;
|
||||||
@@ -49,7 +49,7 @@ fn void! native_rmtree(Path path)
|
|||||||
{
|
{
|
||||||
String filename = string::new_from_wstring((WString)&find_data.cFileName, allocator::temp())!;
|
String filename = string::new_from_wstring((WString)&find_data.cFileName, allocator::temp())!;
|
||||||
if (filename == "." || filename == "..") continue;
|
if (filename == "." || filename == "..") continue;
|
||||||
Path file_path = path.tappend(filename)!;
|
Path file_path = path.temp_append(filename)!;
|
||||||
if (find_data.dwFileAttributes & win32::FILE_ATTRIBUTE_DIRECTORY)
|
if (find_data.dwFileAttributes & win32::FILE_ATTRIBUTE_DIRECTORY)
|
||||||
{
|
{
|
||||||
native_rmtree(file_path)!;
|
native_rmtree(file_path)!;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
module std::io::os @if(env::LIBC);
|
module std::io::os @if(env::LIBC);
|
||||||
import std::io::path, std::os;
|
import std::io::path, std::os;
|
||||||
|
|
||||||
fn Path! native_temp_directory(Allocator* allocator = allocator::heap()) @if(!env::WIN32)
|
fn Path! native_temp_directory(Allocator allocator = allocator::heap()) @if(!env::WIN32)
|
||||||
{
|
{
|
||||||
foreach (String env : { "TMPDIR", "TMP", "TEMP", "TEMPDIR" })
|
foreach (String env : { "TMPDIR", "TMP", "TEMP", "TEMPDIR" })
|
||||||
{
|
{
|
||||||
@@ -11,7 +11,7 @@ fn Path! native_temp_directory(Allocator* allocator = allocator::heap()) @if(!en
|
|||||||
return path::new("/tmp", allocator);
|
return path::new("/tmp", allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Path! native_temp_directory(Allocator* allocator = allocator::heap()) @if(env::WIN32)
|
fn Path! native_temp_directory(Allocator allocator = allocator::heap()) @if(env::WIN32)
|
||||||
{
|
{
|
||||||
@pool(allocator)
|
@pool(allocator)
|
||||||
{
|
{
|
||||||
@@ -24,8 +24,9 @@ fn Path! native_temp_directory(Allocator* allocator = allocator::heap()) @if(env
|
|||||||
}
|
}
|
||||||
|
|
||||||
module std::io::os @if(env::NO_LIBC);
|
module std::io::os @if(env::NO_LIBC);
|
||||||
|
import std::io::path;
|
||||||
|
|
||||||
macro Path! native_temp_directory(Allocator* allocator = allocator::heap())
|
macro Path! native_temp_directory(Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
return IoError.UNSUPPORTED_OPERATION?;
|
return IoError.UNSUPPORTED_OPERATION?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
module std::io::path;
|
module std::io::path;
|
||||||
import std::collections::list, std::io::os;
|
import std::collections::list, std::io::os;
|
||||||
|
import std::os::win32;
|
||||||
|
|
||||||
const PathEnv DEFAULT_PATH_ENV = env::WIN32 ? PathEnv.WIN32 : PathEnv.POSIX;
|
const PathEnv DEFAULT_PATH_ENV = env::WIN32 ? PathEnv.WIN32 : PathEnv.POSIX;
|
||||||
const char PREFERRED_SEPARATOR_WIN32 = '\\';
|
const char PREFERRED_SEPARATOR_WIN32 = '\\';
|
||||||
@@ -14,7 +15,9 @@ fault PathResult
|
|||||||
NO_PARENT,
|
NO_PARENT,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Path (Printable)
|
def Path = PathImp;
|
||||||
|
|
||||||
|
struct PathImp (Printable)
|
||||||
{
|
{
|
||||||
String path_string;
|
String path_string;
|
||||||
PathEnv env;
|
PathEnv env;
|
||||||
@@ -26,7 +29,15 @@ enum PathEnv
|
|||||||
POSIX
|
POSIX
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Path! getcwd(Allocator* allocator = allocator::heap())
|
fn Path! new_cwd(Allocator allocator = allocator::heap())
|
||||||
|
{
|
||||||
|
@pool(allocator)
|
||||||
|
{
|
||||||
|
return new(os::getcwd(allocator::temp()), allocator);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Path! getcwd(Allocator allocator = allocator::heap()) @deprecated("Use new_cwd()")
|
||||||
{
|
{
|
||||||
@pool(allocator)
|
@pool(allocator)
|
||||||
{
|
{
|
||||||
@@ -38,9 +49,10 @@ fn bool is_dir(Path path) => os::native_is_dir(path.str_view());
|
|||||||
fn bool is_file(Path path) => os::native_is_file(path.str_view());
|
fn bool is_file(Path path) => os::native_is_file(path.str_view());
|
||||||
fn usz! file_size(Path path) => os::native_file_size(path.str_view());
|
fn usz! file_size(Path path) => os::native_file_size(path.str_view());
|
||||||
fn bool exists(Path path) => os::native_file_or_dir_exists(path.str_view());
|
fn bool exists(Path path) => os::native_file_or_dir_exists(path.str_view());
|
||||||
fn Path! tgetcwd() => getcwd(allocator::temp()) @inline;
|
fn Path! temp_cwd() => new_cwd(allocator::temp()) @inline;
|
||||||
|
fn Path! tgetcwd() @deprecated("Use temp_cwd()") => new_cwd(allocator::temp()) @inline;
|
||||||
fn void! chdir(Path path) => os::native_chdir(path) @inline;
|
fn void! chdir(Path path) => os::native_chdir(path) @inline;
|
||||||
fn Path! temp_directory(Allocator* allocator = allocator::heap()) => os::native_temp_directory(allocator);
|
fn Path! temp_directory(Allocator allocator = allocator::heap()) => os::native_temp_directory(allocator);
|
||||||
fn void! delete(Path path) => os::native_remove(path.str_view()) @inline;
|
fn void! delete(Path path) => os::native_remove(path.str_view()) @inline;
|
||||||
|
|
||||||
macro bool is_separator(char c, PathEnv path_env = DEFAULT_PATH_ENV)
|
macro bool is_separator(char c, PathEnv path_env = DEFAULT_PATH_ENV)
|
||||||
@@ -58,7 +70,17 @@ macro bool is_win32_separator(char c)
|
|||||||
return c == '/' || c == '\\';
|
return c == '/' || c == '\\';
|
||||||
}
|
}
|
||||||
|
|
||||||
fn PathList! ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "", Allocator* allocator = allocator::heap())
|
fn PathList! ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "", Allocator allocator = allocator::heap()) @deprecated("use new_ls")
|
||||||
|
{
|
||||||
|
return new_ls(dir, no_dirs, no_symlinks, mask, allocator);
|
||||||
|
|
||||||
|
}
|
||||||
|
fn PathList! temp_ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "")
|
||||||
|
{
|
||||||
|
return new_ls(dir, no_dirs, no_symlinks, mask, allocator::temp()) @inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn PathList! new_ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "", Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
$if $defined(os::native_ls):
|
$if $defined(os::native_ls):
|
||||||
return os::native_ls(dir, no_dirs, no_symlinks, mask, allocator);
|
return os::native_ls(dir, no_dirs, no_symlinks, mask, allocator);
|
||||||
@@ -105,7 +127,7 @@ fn void! rmtree(Path path)
|
|||||||
$endif
|
$endif
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Path! new(String path, Allocator* allocator = allocator::heap(), PathEnv path_env = DEFAULT_PATH_ENV)
|
fn Path! new(String path, Allocator allocator = allocator::heap(), PathEnv path_env = DEFAULT_PATH_ENV)
|
||||||
{
|
{
|
||||||
return { normalize(path.copy(allocator), path_env), path_env };
|
return { normalize(path.copy(allocator), path_env), path_env };
|
||||||
}
|
}
|
||||||
@@ -115,7 +137,7 @@ fn Path! temp_new(String path, PathEnv path_env = DEFAULT_PATH_ENV)
|
|||||||
return new(path, allocator::temp(), path_env);
|
return new(path, allocator::temp(), path_env);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Path! new_win32_wstring(WString path, Allocator* allocator = allocator::heap())
|
fn Path! new_win32_wstring(WString path, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
@pool(allocator)
|
@pool(allocator)
|
||||||
{
|
{
|
||||||
@@ -123,12 +145,12 @@ fn Path! new_win32_wstring(WString path, Allocator* allocator = allocator::heap(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Path! new_windows(String path, Allocator* allocator = allocator::heap())
|
fn Path! new_windows(String path, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
return new(path, allocator, WIN32);
|
return new(path, allocator, WIN32);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Path! new_posix(String path, Allocator* allocator = allocator::heap())
|
fn Path! new_posix(String path, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
return new(path, allocator, POSIX);
|
return new(path, allocator, POSIX);
|
||||||
}
|
}
|
||||||
@@ -138,12 +160,17 @@ fn bool Path.equals(self, Path p2)
|
|||||||
return self.env == p2.env && self.path_string == p2.path_string;
|
return self.env == p2.env && self.path_string == p2.path_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn Path! Path.append(self, String filename, Allocator allocator = allocator::heap()) @deprecated("Use path.new_append(...)")
|
||||||
|
{
|
||||||
|
return self.new_append(filename, allocator) @inline;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append the string to the current path.
|
* Append the string to the current path.
|
||||||
*
|
*
|
||||||
* @param [in] filename
|
* @param [in] filename
|
||||||
**/
|
**/
|
||||||
fn Path! Path.append(self, String filename, Allocator* allocator = allocator::heap())
|
fn Path! Path.new_append(self, String filename, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
if (!self.path_string.len) return new(filename, allocator, self.env)!;
|
if (!self.path_string.len) return new(filename, allocator, self.env)!;
|
||||||
assert(!is_separator(self.path_string[^1], self.env));
|
assert(!is_separator(self.path_string[^1], self.env));
|
||||||
@@ -158,7 +185,9 @@ fn Path! Path.append(self, String filename, Allocator* allocator = allocator::he
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Path! Path.tappend(self, String filename) => self.append(filename, allocator::temp());
|
fn Path! Path.temp_append(self, String filename) => self.new_append(filename, allocator::temp());
|
||||||
|
|
||||||
|
fn Path! Path.tappend(self, String filename) @deprecated("Use path.temp_append(...)") => self.new_append(filename, allocator::temp());
|
||||||
|
|
||||||
fn usz Path.start_of_base_name(self) @local
|
fn usz Path.start_of_base_name(self) @local
|
||||||
{
|
{
|
||||||
@@ -166,7 +195,19 @@ fn usz Path.start_of_base_name(self) @local
|
|||||||
if (!path_str.len) return 0;
|
if (!path_str.len) return 0;
|
||||||
if (self.env == PathEnv.WIN32)
|
if (self.env == PathEnv.WIN32)
|
||||||
{
|
{
|
||||||
return path_str.rindex_of_char('\\') + 1 ?? volume_name_len(path_str, self.env)!!;
|
if (try index = path_str.rindex_of_char('\\'))
|
||||||
|
{
|
||||||
|
// c:\ style path, we're done!
|
||||||
|
if (path_str[0] != '\\') return index + 1;
|
||||||
|
// Handle \\server\foo
|
||||||
|
// Find the \ before "foo"
|
||||||
|
usz last_index = 2 + path_str[2..].index_of_char('\\')!!;
|
||||||
|
// If they don't match, we're done
|
||||||
|
assert(last_index <= index, "Invalid normalized, path %d vs %s in %s", last_index, index, path_str);
|
||||||
|
if (last_index != index) return index + 1;
|
||||||
|
// Otherwise just default to the volume length.
|
||||||
|
}
|
||||||
|
return volume_name_len(path_str, self.env)!!;
|
||||||
}
|
}
|
||||||
return path_str.rindex_of_char('/') + 1 ?? 0;
|
return path_str.rindex_of_char('/') + 1 ?? 0;
|
||||||
}
|
}
|
||||||
@@ -176,28 +217,44 @@ fn bool! Path.is_absolute(self)
|
|||||||
String path_str = self.str_view();
|
String path_str = self.str_view();
|
||||||
if (!path_str.len) return false;
|
if (!path_str.len) return false;
|
||||||
usz path_start = volume_name_len(path_str, self.env)!;
|
usz path_start = volume_name_len(path_str, self.env)!;
|
||||||
|
if (path_start > 0 && path_str[0] == '\\') return true;
|
||||||
return path_start < path_str.len && is_separator(path_str[path_start], self.env);
|
return path_start < path_str.len && is_separator(path_str[path_start], self.env);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Path! Path.absolute(self, Allocator* allocator = allocator::heap())
|
fn Path! Path.absolute(self, Allocator allocator = allocator::heap()) @deprecated("Use path.new_absolute()")
|
||||||
|
{
|
||||||
|
return self.new_absolute(allocator) @inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require self.env == DEFAULT_PATH_ENV : "This method is only available on native paths"
|
||||||
|
**/
|
||||||
|
fn Path! Path.new_absolute(self, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
String path_str = self.str_view();
|
String path_str = self.str_view();
|
||||||
if (!path_str.len) path_str = ".";
|
if (!path_str.len) return PathResult.INVALID_PATH?;
|
||||||
|
if (self.is_absolute()!) return new(path_str, allocator, self.env);
|
||||||
if (path_str == ".")
|
if (path_str == ".")
|
||||||
{
|
{
|
||||||
|
@pool(allocator)
|
||||||
|
{
|
||||||
|
String cwd = os::getcwd(allocator::temp())!;
|
||||||
|
return new(cwd, allocator, self.env);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
$if DEFAULT_PATH_ENV == WIN32:
|
||||||
|
@pool(allocator)
|
||||||
|
{
|
||||||
|
const usz BUFFER_LEN = 4096;
|
||||||
|
WString buffer = (WString)mem::temp_alloc_array(Char16, BUFFER_LEN);
|
||||||
|
buffer = win32::_wfullpath(buffer, path_str.to_temp_wstring()!, BUFFER_LEN);
|
||||||
|
if (!buffer) return PathResult.INVALID_PATH?;
|
||||||
|
return { string::new_from_wstring(buffer, allocator), WIN32 };
|
||||||
|
};
|
||||||
|
$else
|
||||||
String cwd = os::getcwd(allocator::temp())!;
|
String cwd = os::getcwd(allocator::temp())!;
|
||||||
return new(cwd, allocator, self.env);
|
return Path { cwd, self.env }.new_append(path_str, allocator)!;
|
||||||
}
|
$endif
|
||||||
switch (self.env)
|
|
||||||
{
|
|
||||||
case WIN32:
|
|
||||||
usz path_start = volume_name_len(path_str, self.env)!;
|
|
||||||
if (path_start > 0) return self;
|
|
||||||
case POSIX:
|
|
||||||
if (path_str[0] == PREFERRED_SEPARATOR_POSIX) return self;
|
|
||||||
}
|
|
||||||
String cwd = os::getcwd(allocator::temp())!;
|
|
||||||
return Path{ cwd, self.env }.append(path_str, allocator)!;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String Path.basename(self)
|
fn String Path.basename(self)
|
||||||
@@ -208,16 +265,40 @@ fn String Path.basename(self)
|
|||||||
return path_str[basename_start..];
|
return path_str[basename_start..];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn String Path.dirname(self)
|
fn String Path.dirname(self)
|
||||||
{
|
{
|
||||||
usz basename_start = self.start_of_base_name();
|
usz basename_start = self.start_of_base_name();
|
||||||
String path_str = self.path_string;
|
String path_str = self.path_string;
|
||||||
if (basename_start == 0) return "";
|
if (basename_start == 0) return ".";
|
||||||
usz start = volume_name_len(path_str, self.env)!!;
|
usz start = volume_name_len(path_str, self.env)!!;
|
||||||
if (basename_start <= start + 1) return path_str[:basename_start];
|
if (basename_start <= start + 1)
|
||||||
|
{
|
||||||
|
if (self.env == WIN32 && basename_start > start && path_str[0..1] == `\\`)
|
||||||
|
{
|
||||||
|
return path_str[:basename_start - 1];
|
||||||
|
}
|
||||||
|
return path_str[:basename_start];
|
||||||
|
}
|
||||||
return path_str[:basename_start - 1];
|
return path_str[:basename_start - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test if the path has the given extension, so given the path /foo/bar.c3
|
||||||
|
* this would be true matching the extension "c3"
|
||||||
|
*
|
||||||
|
* @param [in] extension `The extension name (not including the leading '.')`
|
||||||
|
* @require extension.len > 0 : `The extension cannot be empty`
|
||||||
|
* @return `true if the extension matches`
|
||||||
|
**/
|
||||||
|
fn bool Path.has_extension(self, String extension)
|
||||||
|
{
|
||||||
|
String basename = self.basename();
|
||||||
|
if (basename.len <= extension.len) return false;
|
||||||
|
if (basename[^extension.len + 1] != '.') return false;
|
||||||
|
return basename[^extension.len..] == extension;
|
||||||
|
}
|
||||||
|
|
||||||
fn String! Path.extension(self)
|
fn String! Path.extension(self)
|
||||||
{
|
{
|
||||||
String basename = self.basename();
|
String basename = self.basename();
|
||||||
@@ -248,13 +329,20 @@ fn usz! volume_name_len(String path, PathEnv path_env) @local
|
|||||||
while (count < len && path[count] == '\\') count++;
|
while (count < len && path[count] == '\\') count++;
|
||||||
// Not 2 => folded paths
|
// Not 2 => folded paths
|
||||||
if (count != 2) return 0;
|
if (count != 2) return 0;
|
||||||
// Check that we have a name followed by '/'
|
// Check that we have a name followed by '\'
|
||||||
|
isz base_found = 0;
|
||||||
for (usz i = 2; i < len; i++)
|
for (usz i = 2; i < len; i++)
|
||||||
{
|
{
|
||||||
char c = path[i];
|
char c = path[i];
|
||||||
if (is_win32_separator(c)) return i;
|
if (is_win32_separator(c))
|
||||||
|
{
|
||||||
|
if (base_found) return i;
|
||||||
|
base_found = i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (is_reserved_win32_path_char(c)) return PathResult.INVALID_PATH?;
|
if (is_reserved_win32_path_char(c)) return PathResult.INVALID_PATH?;
|
||||||
}
|
}
|
||||||
|
if (base_found > 0 && base_found + 1 < len) return len;
|
||||||
return PathResult.INVALID_PATH?;
|
return PathResult.INVALID_PATH?;
|
||||||
case 'A'..'Z':
|
case 'A'..'Z':
|
||||||
case 'a'..'z':
|
case 'a'..'z':
|
||||||
@@ -281,6 +369,10 @@ fn String! normalize(String path_str, PathEnv path_env = DEFAULT_PATH_ENV)
|
|||||||
{
|
{
|
||||||
if (!path_str.len) return "";
|
if (!path_str.len) return "";
|
||||||
usz path_start = volume_name_len(path_str, path_env)!;
|
usz path_start = volume_name_len(path_str, path_env)!;
|
||||||
|
if (path_start > 0 && path_env == PathEnv.WIN32)
|
||||||
|
{
|
||||||
|
for (usz i = 0; i < path_start; i++) if (path_str[i] == '/') path_str[i] = '\\';
|
||||||
|
}
|
||||||
usz path_len = path_str.len;
|
usz path_len = path_str.len;
|
||||||
if (path_start == path_len) return path_str;
|
if (path_start == path_len) return path_str;
|
||||||
char path_separator = path_env == PathEnv.WIN32 ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX;
|
char path_separator = path_env == PathEnv.WIN32 ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX;
|
||||||
@@ -384,7 +476,9 @@ fn String! normalize(String path_str, PathEnv path_env = DEFAULT_PATH_ENV)
|
|||||||
len++;
|
len++;
|
||||||
}
|
}
|
||||||
if (len > path_start + 1 && is_separator(path_str[len - 1], path_env)) len--;
|
if (len > path_start + 1 && is_separator(path_str[len - 1], path_env)) len--;
|
||||||
path_str.ptr[len] = 0;
|
if (path_str.len > len) path_str.ptr[len] = 0;
|
||||||
|
// Empty path after normalization -> "."
|
||||||
|
if (!len) return ".";
|
||||||
return path_str[:len];
|
return path_str[:len];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,6 +489,7 @@ fn String Path.root_directory(self)
|
|||||||
String path_str = self.str_view();
|
String path_str = self.str_view();
|
||||||
usz len = path_str.len;
|
usz len = path_str.len;
|
||||||
if (!len) return "";
|
if (!len) return "";
|
||||||
|
if (path_str == ".") return ".";
|
||||||
if (self.env == PathEnv.WIN32)
|
if (self.env == PathEnv.WIN32)
|
||||||
{
|
{
|
||||||
usz root_len = volume_name_len(path_str, self.env)!!;
|
usz root_len = volume_name_len(path_str, self.env)!!;
|
||||||
@@ -417,18 +512,19 @@ def PathWalker = fn bool! (Path, bool is_dir, void*);
|
|||||||
/*
|
/*
|
||||||
* Walk the path recursively. PathWalker is run on every file and
|
* Walk the path recursively. PathWalker is run on every file and
|
||||||
* directory found. Return true to abort the walk.
|
* directory found. Return true to abort the walk.
|
||||||
|
* @require self.env == DEFAULT_PATH_ENV : "This method is only available on native paths"
|
||||||
*/
|
*/
|
||||||
fn bool! Path.walk(self, PathWalker w, void* data)
|
fn bool! Path.walk(self, PathWalker w, void* data)
|
||||||
{
|
{
|
||||||
const PATH_MAX = 512;
|
const PATH_MAX = 512;
|
||||||
@stack_mem(PATH_MAX; Allocator* allocator)
|
@stack_mem(PATH_MAX; Allocator allocator)
|
||||||
{
|
{
|
||||||
Path abs = self.absolute(allocator)!;
|
Path abs = self.new_absolute(allocator)!;
|
||||||
PathList files = ls(abs, .allocator = allocator)!;
|
PathList files = new_ls(abs, .allocator = allocator)!;
|
||||||
foreach (f : files)
|
foreach (f : files)
|
||||||
{
|
{
|
||||||
if (f.str_view() == "." || f.str_view() == "..") continue;
|
if (f.str_view() == "." || f.str_view() == "..") continue;
|
||||||
f = abs.append(f.str_view(), allocator)!;
|
f = abs.new_append(f.str_view(), allocator)!;
|
||||||
bool is_directory = is_dir(f);
|
bool is_directory = is_dir(f);
|
||||||
if (w(f, is_directory, data)!) return true;
|
if (w(f, is_directory, data)!) return true;
|
||||||
if (is_directory && f.walk(w, data)!) return true;
|
if (is_directory && f.walk(w, data)!) return true;
|
||||||
@@ -448,6 +544,11 @@ fn bool Path.has_suffix(self, String str)
|
|||||||
return self.str_view().ends_with(str);
|
return self.str_view().ends_with(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn void Path.free_with_allocator(self, Allocator allocator)
|
||||||
|
{
|
||||||
|
allocator::free(allocator, self.path_string.ptr);
|
||||||
|
}
|
||||||
|
|
||||||
fn void Path.free(self)
|
fn void Path.free(self)
|
||||||
{
|
{
|
||||||
free(self.path_string.ptr);
|
free(self.path_string.ptr);
|
||||||
@@ -459,7 +560,7 @@ fn usz! Path.to_format(&self, Formatter* formatter) @dynamic
|
|||||||
return formatter.print(self.str_view());
|
return formatter.print(self.str_view());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String Path.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
|
fn String Path.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
|
||||||
{
|
{
|
||||||
return self.str_view().copy(allocator);
|
return self.str_view().copy(allocator);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ interface InStream
|
|||||||
fn usz! available() @optional;
|
fn usz! available() @optional;
|
||||||
fn usz! read(char[] buffer);
|
fn usz! read(char[] buffer);
|
||||||
fn char! read_byte();
|
fn char! read_byte();
|
||||||
fn usz! write_to(OutStream* out) @optional;
|
fn usz! write_to(OutStream out) @optional;
|
||||||
fn void! pushback_byte() @optional;
|
fn void! pushback_byte() @optional;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,10 +21,10 @@ interface OutStream
|
|||||||
fn void! flush() @optional;
|
fn void! flush() @optional;
|
||||||
fn usz! write(char[] bytes);
|
fn usz! write(char[] bytes);
|
||||||
fn void! write_byte(char c);
|
fn void! write_byte(char c);
|
||||||
fn usz! read_to(InStream* in) @optional;
|
fn usz! read_to(InStream in) @optional;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usz! available(InStream* s)
|
fn usz! available(InStream s)
|
||||||
{
|
{
|
||||||
if (&s.available) return s.available();
|
if (&s.available) return s.available();
|
||||||
if (&s.seek)
|
if (&s.seek)
|
||||||
@@ -39,19 +39,19 @@ fn usz! available(InStream* s)
|
|||||||
|
|
||||||
macro bool @is_instream(#expr)
|
macro bool @is_instream(#expr)
|
||||||
{
|
{
|
||||||
return $assignable(#expr, InStream*);
|
return $assignable(#expr, InStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro bool @is_outstream(#expr)
|
macro bool @is_outstream(#expr)
|
||||||
{
|
{
|
||||||
return $assignable(#expr, OutStream*);
|
return $assignable(#expr, OutStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param [&out] ref
|
* @param [&out] ref
|
||||||
* @require @is_instream(stream)
|
* @require @is_instream(stream)
|
||||||
**/
|
**/
|
||||||
macro usz! read_any(stream, any* ref)
|
macro usz! read_any(stream, any ref)
|
||||||
{
|
{
|
||||||
return read_all(stream, ((char*)ref)[:ref.type.sizeof]);
|
return read_all(stream, ((char*)ref)[:ref.type.sizeof]);
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,7 @@ macro usz! read_any(stream, any* ref)
|
|||||||
* @require @is_outstream(stream)
|
* @require @is_outstream(stream)
|
||||||
* @ensure return == ref.type.sizeof
|
* @ensure return == ref.type.sizeof
|
||||||
*/
|
*/
|
||||||
macro usz! write_any(stream, any* ref)
|
macro usz! write_any(stream, any ref)
|
||||||
{
|
{
|
||||||
return write_all(stream, ((char*)ref)[:ref.type.sizeof]);
|
return write_all(stream, ((char*)ref)[:ref.type.sizeof]);
|
||||||
}
|
}
|
||||||
@@ -134,29 +134,23 @@ macro void! @pushback_using_seek(&s)
|
|||||||
s.seek(-1, CURSOR)!;
|
s.seek(-1, CURSOR)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usz! copy_to(InStream* in, OutStream* dst, char[] buffer = {})
|
fn usz! copy_to(InStream in, OutStream dst, char[] buffer = {})
|
||||||
{
|
{
|
||||||
if (buffer.len) return copy_through_buffer(in, dst, buffer);
|
if (buffer.len) return copy_through_buffer(in, dst, buffer);
|
||||||
if (&in.write_to) return in.write_to(dst);
|
if (&in.write_to) return in.write_to(dst);
|
||||||
if (&dst.read_to) return dst.read_to(in);
|
if (&dst.read_to) return dst.read_to(in);
|
||||||
$switch (env::MEMORY_ENV)
|
$switch (env::MEMORY_ENV)
|
||||||
$case NORMAL:
|
$case NORMAL:
|
||||||
@pool()
|
return copy_through_buffer(in, dst, &&char[4096]{});
|
||||||
{
|
|
||||||
return copy_through_buffer(in, dst, mem::temp_alloc_array(char, 4096));
|
|
||||||
};
|
|
||||||
$case SMALL:
|
$case SMALL:
|
||||||
@pool()
|
return copy_through_buffer(in, dst, &&char[1024]{});
|
||||||
{
|
|
||||||
return copy_through_buffer(in, dst, mem::temp_alloc_array(char, 1024));
|
|
||||||
};
|
|
||||||
$case TINY:
|
$case TINY:
|
||||||
$case NONE:
|
$case NONE:
|
||||||
return copy_through_buffer(in, dst, &&(char[256]{}));
|
return copy_through_buffer(in, dst, &&(char[256]{}));
|
||||||
$endswitch
|
$endswitch
|
||||||
}
|
}
|
||||||
|
|
||||||
macro usz! copy_through_buffer(InStream *in, OutStream* dst, char[] buffer) @local
|
macro usz! copy_through_buffer(InStream in, OutStream dst, char[] buffer) @local
|
||||||
{
|
{
|
||||||
usz total_copied;
|
usz total_copied;
|
||||||
while (true)
|
while (true)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module std::io;
|
|||||||
|
|
||||||
struct ReadBuffer (InStream)
|
struct ReadBuffer (InStream)
|
||||||
{
|
{
|
||||||
InStream* wrapped_stream;
|
InStream wrapped_stream;
|
||||||
char[] bytes;
|
char[] bytes;
|
||||||
usz read_idx;
|
usz read_idx;
|
||||||
usz write_idx;
|
usz write_idx;
|
||||||
@@ -14,7 +14,7 @@ struct ReadBuffer (InStream)
|
|||||||
* @require bytes.len > 0
|
* @require bytes.len > 0
|
||||||
* @require self.bytes.len == 0 "Init may not run on already initialized data"
|
* @require self.bytes.len == 0 "Init may not run on already initialized data"
|
||||||
**/
|
**/
|
||||||
fn ReadBuffer* ReadBuffer.init(&self, InStream* wrapped_stream, char[] bytes)
|
fn ReadBuffer* ReadBuffer.init(&self, InStream wrapped_stream, char[] bytes)
|
||||||
{
|
{
|
||||||
*self = { .wrapped_stream = wrapped_stream, .bytes = bytes };
|
*self = { .wrapped_stream = wrapped_stream, .bytes = bytes };
|
||||||
return self;
|
return self;
|
||||||
@@ -63,7 +63,7 @@ fn void! ReadBuffer.refill(&self) @local @inline
|
|||||||
|
|
||||||
struct WriteBuffer (OutStream)
|
struct WriteBuffer (OutStream)
|
||||||
{
|
{
|
||||||
OutStream* wrapped_stream;
|
OutStream wrapped_stream;
|
||||||
char[] bytes;
|
char[] bytes;
|
||||||
usz index;
|
usz index;
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ struct WriteBuffer (OutStream)
|
|||||||
* @require bytes.len > 0 "Non-empty buffer required"
|
* @require bytes.len > 0 "Non-empty buffer required"
|
||||||
* @require self.bytes.len == 0 "Init may not run on already initialized data"
|
* @require self.bytes.len == 0 "Init may not run on already initialized data"
|
||||||
**/
|
**/
|
||||||
fn WriteBuffer* WriteBuffer.init(&self, OutStream* wrapped_stream, char[] bytes)
|
fn WriteBuffer* WriteBuffer.init(&self, OutStream wrapped_stream, char[] bytes)
|
||||||
{
|
{
|
||||||
*self = { .wrapped_stream = wrapped_stream, .bytes = bytes };
|
*self = { .wrapped_stream = wrapped_stream, .bytes = bytes };
|
||||||
return self;
|
return self;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import std::math;
|
|||||||
|
|
||||||
struct ByteBuffer (InStream, OutStream)
|
struct ByteBuffer (InStream, OutStream)
|
||||||
{
|
{
|
||||||
Allocator* allocator;
|
Allocator allocator;
|
||||||
usz max_read;
|
usz max_read;
|
||||||
char[] bytes;
|
char[] bytes;
|
||||||
usz read_idx;
|
usz read_idx;
|
||||||
@@ -16,17 +16,7 @@ struct ByteBuffer (InStream, OutStream)
|
|||||||
* max_read defines how many bytes might be kept before its internal buffer is shrinked.
|
* max_read defines how many bytes might be kept before its internal buffer is shrinked.
|
||||||
* @require self.bytes.len == 0 "Buffer already initialized."
|
* @require self.bytes.len == 0 "Buffer already initialized."
|
||||||
**/
|
**/
|
||||||
fn ByteBuffer*! ByteBuffer.init_new(&self, usz max_read, usz initial_capacity = 16, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
|
fn ByteBuffer*! ByteBuffer.new_init(&self, usz max_read, usz initial_capacity = 16, Allocator allocator = allocator::heap())
|
||||||
{
|
|
||||||
return self.new_init(max_read, initial_capacity, allocator) @inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ByteBuffer provides a streamable read/write buffer.
|
|
||||||
* max_read defines how many bytes might be kept before its internal buffer is shrinked.
|
|
||||||
* @require self.bytes.len == 0 "Buffer already initialized."
|
|
||||||
**/
|
|
||||||
fn ByteBuffer*! ByteBuffer.new_init(&self, usz max_read, usz initial_capacity = 16, Allocator* allocator = allocator::heap())
|
|
||||||
{
|
{
|
||||||
*self = { .allocator = allocator, .max_read = max_read };
|
*self = { .allocator = allocator, .max_read = max_read };
|
||||||
initial_capacity = max(initial_capacity, 16);
|
initial_capacity = max(initial_capacity, 16);
|
||||||
@@ -34,11 +24,6 @@ fn ByteBuffer*! ByteBuffer.new_init(&self, usz max_read, usz initial_capacity =
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ByteBuffer*! ByteBuffer.init_temp(&self, usz max_read, usz initial_capacity = 16) @deprecated("Replaced by temp_init")
|
|
||||||
{
|
|
||||||
return self.temp_init(max_read, initial_capacity) @inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ByteBuffer*! ByteBuffer.temp_init(&self, usz max_read, usz initial_capacity = 16)
|
fn ByteBuffer*! ByteBuffer.temp_init(&self, usz max_read, usz initial_capacity = 16)
|
||||||
{
|
{
|
||||||
return self.new_init(max_read, initial_capacity, allocator::temp());
|
return self.new_init(max_read, initial_capacity, allocator::temp());
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ fn usz! ByteReader.seek(&self, isz offset, Seek seek) @dynamic
|
|||||||
return new_index;
|
return new_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usz! ByteReader.write_to(&self, OutStream* writer) @dynamic
|
fn usz! ByteReader.write_to(&self, OutStream writer) @dynamic
|
||||||
{
|
{
|
||||||
if (self.index >= self.bytes.len) return 0;
|
if (self.index >= self.bytes.len) return 0;
|
||||||
usz written = writer.write(self.bytes[self.index..])!;
|
usz written = writer.write(self.bytes[self.index..])!;
|
||||||
|
|||||||
@@ -5,16 +5,16 @@ struct ByteWriter (OutStream)
|
|||||||
{
|
{
|
||||||
char[] bytes;
|
char[] bytes;
|
||||||
usz index;
|
usz index;
|
||||||
Allocator* allocator;
|
Allocator allocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param [&inout] self
|
* @param [&inout] self
|
||||||
* @param [&inout] allocator
|
* @param [&inout] allocator
|
||||||
* @require self.bytes.len == 0 "Init may not run on on already initialized data"
|
* @require self.bytes.len == 0 "Init may not run on already initialized data"
|
||||||
* @ensure (bool)allocator, self.index == 0
|
* @ensure (bool)allocator, self.index == 0
|
||||||
**/
|
**/
|
||||||
fn ByteWriter* ByteWriter.new_init(&self, Allocator* allocator = allocator::heap())
|
fn ByteWriter* ByteWriter.new_init(&self, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
*self = { .bytes = {}, .allocator = allocator };
|
*self = { .bytes = {}, .allocator = allocator };
|
||||||
return self;
|
return self;
|
||||||
@@ -22,18 +22,7 @@ fn ByteWriter* ByteWriter.new_init(&self, Allocator* allocator = allocator::heap
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param [&inout] self
|
* @param [&inout] self
|
||||||
* @param [&inout] allocator
|
* @require self.bytes.len == 0 "Init may not run on already initialized data"
|
||||||
* @require self.bytes.len == 0 "Init may not run on on already initialized data"
|
|
||||||
* @ensure (bool)allocator, self.index == 0
|
|
||||||
**/
|
|
||||||
fn ByteWriter* ByteWriter.init_new(&self, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
|
|
||||||
{
|
|
||||||
return self.new_init(allocator) @inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param [&inout] self
|
|
||||||
* @require self.bytes.len == 0 "Init may not run on on already initialized data"
|
|
||||||
* @ensure self.index == 0
|
* @ensure self.index == 0
|
||||||
**/
|
**/
|
||||||
fn ByteWriter* ByteWriter.temp_init(&self)
|
fn ByteWriter* ByteWriter.temp_init(&self)
|
||||||
@@ -41,16 +30,6 @@ fn ByteWriter* ByteWriter.temp_init(&self)
|
|||||||
return self.new_init(allocator::temp()) @inline;
|
return self.new_init(allocator::temp()) @inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param [&inout] self
|
|
||||||
* @require self.bytes.len == 0 "Init may not run on on already initialized data"
|
|
||||||
* @ensure self.index == 0
|
|
||||||
**/
|
|
||||||
fn ByteWriter* ByteWriter.init_temp(&self) @deprecated("Replaced by temp_init")
|
|
||||||
{
|
|
||||||
return self.temp_init() @inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ByteWriter* ByteWriter.init_with_buffer(&self, char[] data)
|
fn ByteWriter* ByteWriter.init_with_buffer(&self, char[] data)
|
||||||
{
|
{
|
||||||
*self = { .bytes = data, .allocator = null };
|
*self = { .bytes = data, .allocator = null };
|
||||||
@@ -97,7 +76,7 @@ fn void! ByteWriter.write_byte(&self, char c) @dynamic
|
|||||||
* @param [&inout] self
|
* @param [&inout] self
|
||||||
* @param reader
|
* @param reader
|
||||||
**/
|
**/
|
||||||
fn usz! ByteWriter.read_from(&self, InStream* reader) @dynamic
|
fn usz! ByteWriter.read_from(&self, InStream reader) @dynamic
|
||||||
{
|
{
|
||||||
usz start_index = self.index;
|
usz start_index = self.index;
|
||||||
if (&reader.available)
|
if (&reader.available)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module std::io;
|
|||||||
|
|
||||||
struct LimitReader (InStream)
|
struct LimitReader (InStream)
|
||||||
{
|
{
|
||||||
InStream* wrapped_stream;
|
InStream wrapped_stream;
|
||||||
usz limit;
|
usz limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ struct LimitReader (InStream)
|
|||||||
* @param [&inout] wrapped_stream "The stream to read from"
|
* @param [&inout] wrapped_stream "The stream to read from"
|
||||||
* @param limit "The max limit to read"
|
* @param limit "The max limit to read"
|
||||||
**/
|
**/
|
||||||
fn LimitReader* LimitReader.init(&self, InStream* wrapped_stream, usz limit)
|
fn LimitReader* LimitReader.init(&self, InStream wrapped_stream, usz limit)
|
||||||
{
|
{
|
||||||
*self = { .wrapped_stream = wrapped_stream, .limit = limit };
|
*self = { .wrapped_stream = wrapped_stream, .limit = limit };
|
||||||
return self;
|
return self;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module std::io;
|
|||||||
|
|
||||||
struct Scanner (InStream)
|
struct Scanner (InStream)
|
||||||
{
|
{
|
||||||
InStream* wrapped_stream;
|
InStream wrapped_stream;
|
||||||
char[] buf;
|
char[] buf;
|
||||||
usz pattern_idx;
|
usz pattern_idx;
|
||||||
usz read_idx;
|
usz read_idx;
|
||||||
@@ -16,7 +16,7 @@ struct Scanner (InStream)
|
|||||||
* @param [&in] stream "The stream to read data from."
|
* @param [&in] stream "The stream to read data from."
|
||||||
* @require buffer.len > 0 "Non-empty buffer required."
|
* @require buffer.len > 0 "Non-empty buffer required."
|
||||||
**/
|
**/
|
||||||
fn void Scanner.init(&self, InStream* stream, char[] buffer)
|
fn void Scanner.init(&self, InStream stream, char[] buffer)
|
||||||
{
|
{
|
||||||
*self = { .wrapped_stream = stream, .buf = buffer };
|
*self = { .wrapped_stream = stream, .buf = buffer };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,8 +183,8 @@ extern fn isz write(Fd fd, void* buffer, usz count) @if(!env::WIN32);
|
|||||||
|
|
||||||
extern fn CFile fmemopen(void* ptr, usz size, ZString mode);
|
extern fn CFile fmemopen(void* ptr, usz size, ZString mode);
|
||||||
extern fn isz getline(char** linep, usz* linecapp, CFile stream);
|
extern fn isz getline(char** linep, usz* linecapp, CFile stream);
|
||||||
extern fn int timespec_get(TimeSpec* ts, int base);
|
extern fn CInt timespec_get(TimeSpec* ts, CInt base);
|
||||||
extern fn int nanosleep(TimeSpec* req, TimeSpec* remaining);
|
extern fn CInt nanosleep(TimeSpec* req, TimeSpec* remaining);
|
||||||
extern fn ZString ctime(Time_t *timer);
|
extern fn ZString ctime(Time_t *timer);
|
||||||
extern fn Time_t time(Time_t *timer);
|
extern fn Time_t time(Time_t *timer);
|
||||||
|
|
||||||
@@ -360,6 +360,7 @@ const int EOF = -1;
|
|||||||
const int FOPEN_MAX = 20;
|
const int FOPEN_MAX = 20;
|
||||||
const int FILENAME_MAX = 1024;
|
const int FILENAME_MAX = 1024;
|
||||||
|
|
||||||
|
macro bool libc_S_ISTYPE(value, mask) @builtin => (value & S_IFMT) == mask;
|
||||||
const S_IFMT = 0o170000; // type of file mask
|
const S_IFMT = 0o170000; // type of file mask
|
||||||
const S_IFIFO = 0o010000; // named pipe (fifo)
|
const S_IFIFO = 0o010000; // named pipe (fifo)
|
||||||
const S_IFCHR = 0o020000; // character special
|
const S_IFCHR = 0o020000; // character special
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
module libc @if(env::POSIX);
|
module libc @if(env::POSIX);
|
||||||
|
|
||||||
|
extern fn isz recv(Fd socket, void *buffer, usz length, CInt flags);
|
||||||
|
extern fn isz send(Fd socket, void *buffer, usz length, CInt flags);
|
||||||
|
|
||||||
extern fn void* dlopen(ZString path, int flags);
|
extern fn void* dlopen(ZString path, int flags);
|
||||||
extern fn CInt dlclose(void*);
|
extern fn CInt dlclose(void*);
|
||||||
@@ -53,3 +55,139 @@ struct Stack_t
|
|||||||
|
|
||||||
extern fn CInt sigaltstack(Stack_t* ss, Stack_t* old_ss);
|
extern fn CInt sigaltstack(Stack_t* ss, Stack_t* old_ss);
|
||||||
extern fn CInt sigaction(CInt signum, Sigaction *action, Sigaction *oldaction);
|
extern fn CInt sigaction(CInt signum, Sigaction *action, Sigaction *oldaction);
|
||||||
|
|
||||||
|
module libc::termios @if(env::LIBC &&& env::POSIX);
|
||||||
|
|
||||||
|
distinct Cc = char;
|
||||||
|
distinct Speed = CUInt;
|
||||||
|
distinct Tcflags = CUInt;
|
||||||
|
|
||||||
|
const Tcflags TCOOFF = 0;
|
||||||
|
const Tcflags TCOON = 1;
|
||||||
|
const Tcflags TCIOFF = 2;
|
||||||
|
const Tcflags TCION = 3;
|
||||||
|
const Tcflags TCIFLUSH = 0;
|
||||||
|
const Tcflags TCOFLUSH = 1;
|
||||||
|
const Tcflags TCIOFLUSH = 2;
|
||||||
|
const Tcflags TCSANOW = 0;
|
||||||
|
const Tcflags TCSADRAIN = 1;
|
||||||
|
const Tcflags TCSAFLUSH = 2;
|
||||||
|
const Speed B0 = 0000000;
|
||||||
|
const Speed B50 = 0000001;
|
||||||
|
const Speed B75 = 0000002;
|
||||||
|
const Speed B110 = 0000003;
|
||||||
|
const Speed B134 = 0000004;
|
||||||
|
const Speed B150 = 0000005;
|
||||||
|
const Speed B200 = 0000006;
|
||||||
|
const Speed B300 = 0000007;
|
||||||
|
const Speed B600 = 0000010;
|
||||||
|
const Speed B1200 = 0000011;
|
||||||
|
const Speed B1800 = 0000012;
|
||||||
|
const Speed B2400 = 0000013;
|
||||||
|
const Speed B4800 = 0000014;
|
||||||
|
const Speed B9600 = 0000015;
|
||||||
|
const Speed B19200 = 0000016;
|
||||||
|
const Speed B38400 = 0000017;
|
||||||
|
const Speed B57600 = 0010001;
|
||||||
|
const Speed B115200 = 0010002;
|
||||||
|
const Speed B230400 = 0010003;
|
||||||
|
const Speed B460800 = 0010004;
|
||||||
|
const Speed B500000 = 0010005;
|
||||||
|
const Speed B576000 = 0010006;
|
||||||
|
const Speed B921600 = 0010007;
|
||||||
|
const Speed B1000000 = 0010010;
|
||||||
|
const Speed B1152000 = 0010011;
|
||||||
|
const Speed B1500000 = 0010012;
|
||||||
|
const Speed B2000000 = 0010013;
|
||||||
|
const Speed B2500000 = 0010014;
|
||||||
|
const Speed B3000000 = 0010015;
|
||||||
|
const Speed B3500000 = 0010016;
|
||||||
|
const Speed B4000000 = 0010017;
|
||||||
|
const Speed MAX_BAUD = B4000000;
|
||||||
|
const CInt VINTR = 0;
|
||||||
|
const CInt VQUIT = 1;
|
||||||
|
const CInt VERASE = 2;
|
||||||
|
const CInt VKILL = 3;
|
||||||
|
const CInt VEOF = 4;
|
||||||
|
const CInt VTIME = 5;
|
||||||
|
const CInt VMIN = 6;
|
||||||
|
const CInt VSWTC = 7;
|
||||||
|
const CInt VSTART = 8;
|
||||||
|
const CInt VSTOP = 9;
|
||||||
|
const CInt VSUSP = 10;
|
||||||
|
const CInt VEOL = 11;
|
||||||
|
const CInt VREPRINT = 12;
|
||||||
|
const CInt VDISCARD = 13;
|
||||||
|
const CInt VWERASE = 14;
|
||||||
|
const CInt VLNEXT = 15;
|
||||||
|
const CInt VEOL2 = 16;
|
||||||
|
const CInt ISIG = 0000001;
|
||||||
|
const CInt ICANON = 0000002;
|
||||||
|
const CInt ECHO = 0000010;
|
||||||
|
const CInt ECHOE = 0000020;
|
||||||
|
const CInt ECHOK = 0000040;
|
||||||
|
const CInt ECHONL = 0000100;
|
||||||
|
const CInt NOFLSH = 0000200;
|
||||||
|
const CInt TOSTOP = 0000400;
|
||||||
|
const CInt IEXTEN = 0100000;
|
||||||
|
const CInt CSIZE = 0000060;
|
||||||
|
const CInt CS5 = 0000000;
|
||||||
|
const CInt CS6 = 0000020;
|
||||||
|
const CInt CS7 = 0000040;
|
||||||
|
const CInt CS8 = 0000060;
|
||||||
|
const CInt CSTOPB = 0000100;
|
||||||
|
const CInt CREAD = 0000200;
|
||||||
|
const CInt PARENB = 0000400;
|
||||||
|
const CInt PARODD = 0001000;
|
||||||
|
const CInt HUPCL = 0002000;
|
||||||
|
const CInt CLOCAL = 0004000;
|
||||||
|
const CInt OPOST = 0000001;
|
||||||
|
const CInt OLCUC = 0000002;
|
||||||
|
const CInt ONLCR = 0000004;
|
||||||
|
const CInt OCRNL = 0000010;
|
||||||
|
const CInt ONOCR = 0000020;
|
||||||
|
const CInt ONLRET = 0000040;
|
||||||
|
const CInt OFILL = 0000100;
|
||||||
|
const CInt OFDEL = 0000200;
|
||||||
|
const CInt VTDLY = 0040000;
|
||||||
|
const CInt VT0 = 0000000;
|
||||||
|
const CInt VT1 = 0040000;
|
||||||
|
const CInt IGNBRK = 0000001;
|
||||||
|
const CInt BRKINT = 0000002;
|
||||||
|
const CInt IGNPAR = 0000004;
|
||||||
|
const CInt PARMRK = 0000010;
|
||||||
|
const CInt INPCK = 0000020;
|
||||||
|
const CInt ISTRIP = 0000040;
|
||||||
|
const CInt INLCR = 0000100;
|
||||||
|
const CInt IGNCR = 0000200;
|
||||||
|
const CInt ICRNL = 0000400;
|
||||||
|
const CInt IUCLC = 0001000;
|
||||||
|
const CInt IXON = 0002000;
|
||||||
|
const CInt IXANY = 0004000;
|
||||||
|
const CInt IXOFF = 0010000;
|
||||||
|
const CInt IMAXBEL = 0020000;
|
||||||
|
const CInt IUTF8 = 0040000;
|
||||||
|
|
||||||
|
extern fn CInt tcgetattr(Fd fd, Termios* self);
|
||||||
|
extern fn CInt tcsetattr(Fd fd, CInt optional_actions, Termios* self);
|
||||||
|
extern fn CInt tcsendbreak(Fd fd, CInt duration);
|
||||||
|
extern fn CInt tcdrain(Fd fd);
|
||||||
|
extern fn CInt tcflush(Fd fd, CInt queue_selector);
|
||||||
|
extern fn CInt tcflow(Fd fd, CInt action);
|
||||||
|
extern fn Speed cfgetospeed(Termios* self);
|
||||||
|
extern fn Speed cfgetispeed(Termios* self);
|
||||||
|
extern fn CInt cfsetospeed(Termios* self, Speed speed);
|
||||||
|
extern fn CInt cfsetispeed(Termios* self, Speed speed);
|
||||||
|
|
||||||
|
const CInt NCCS = 32;
|
||||||
|
struct Termios {
|
||||||
|
Tcflags c_iflag;
|
||||||
|
Tcflags c_oflag;
|
||||||
|
Tcflags c_cflag;
|
||||||
|
Tcflags c_lflag;
|
||||||
|
Cc c_line;
|
||||||
|
Cc[NCCS] c_cc;
|
||||||
|
Speed c_ispeed;
|
||||||
|
Speed c_ospeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
module libc @if(env::WIN32);
|
module libc @if(env::WIN32);
|
||||||
|
import std::os::win32;
|
||||||
|
|
||||||
extern fn CFile __acrt_iob_func(CInt c);
|
extern fn CFile __acrt_iob_func(CInt c);
|
||||||
extern fn CInt _close(Fd fd); def close = _close;
|
extern fn CInt _close(Fd fd); def close = _close;
|
||||||
@@ -15,11 +15,13 @@ extern fn Time_t _mkgmtime64(Tm* timeptr); def timegm = _mkgmtime64;
|
|||||||
extern fn Time_t _mktime64(Tm *timeptr); def mktime = _mktime64;
|
extern fn Time_t _mktime64(Tm *timeptr); def mktime = _mktime64;
|
||||||
extern fn usz _msize(void* ptr);
|
extern fn usz _msize(void* ptr);
|
||||||
extern fn CInt _read(Fd fd, void* buffer, CUInt buffer_size);
|
extern fn CInt _read(Fd fd, void* buffer, CUInt buffer_size);
|
||||||
extern fn CInt _setjmp(void* frameptr, JmpBuf* buffer) @if(env::WIN32);
|
extern fn CInt _setjmp(JmpBuf* buffer, void* frameptr);
|
||||||
extern fn CFile _wfopen(WString, WString);
|
extern fn CFile _wfopen(WString, WString);
|
||||||
extern fn CFile _wfreopen(WString, WString, CFile);
|
extern fn CFile _wfreopen(WString, WString, CFile);
|
||||||
extern fn CInt _write(Fd fd, void* buffer, CUInt count);
|
extern fn CInt _write(Fd fd, void* buffer, CUInt count);
|
||||||
extern fn CInt _wremove(WString);
|
extern fn CInt _wremove(WString);
|
||||||
|
extern fn int recv(Win32_SOCKET s, void* buf, int len, int flags);
|
||||||
|
extern fn int send(Win32_SOCKET s, void* buf, int len, int flags);
|
||||||
|
|
||||||
struct SystemInfo
|
struct SystemInfo
|
||||||
{
|
{
|
||||||
@@ -45,7 +47,7 @@ extern fn CInt get_system_info(SystemInfo*) @extern("GetSystemInfo");
|
|||||||
|
|
||||||
// Aliases to simplify libc use
|
// Aliases to simplify libc use
|
||||||
macro Tm* localtime_r(Time_t* timer, Tm* buf) => _localtime64_s(buf, timer);
|
macro Tm* localtime_r(Time_t* timer, Tm* buf) => _localtime64_s(buf, timer);
|
||||||
macro CInt setjmp(JmpBuf* buffer) => _setjmp($$frameaddress(0), buffer);
|
macro CInt setjmp(JmpBuf* buffer) => _setjmp(buffer, null);
|
||||||
macro Tm* gmtime_r(Time_t* timer, Tm* buf) => _gmtime64_s(buf, timer);
|
macro Tm* gmtime_r(Time_t* timer, Tm* buf) => _gmtime64_s(buf, timer);
|
||||||
macro isz read(Fd fd, void* buffer, usz buffer_size) => _read(fd, buffer, (CUInt)buffer_size);
|
macro isz read(Fd fd, void* buffer, usz buffer_size) => _read(fd, buffer, (CUInt)buffer_size);
|
||||||
macro isz write(Fd fd, void* buffer, usz count) => _write(fd, buffer, (CUInt)count);
|
macro isz write(Fd fd, void* buffer, usz count) => _write(fd, buffer, (CUInt)count);
|
||||||
122
lib/std/libc/termios.c3
Normal file
122
lib/std/libc/termios.c3
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
module libc::termios @if(env::LIBC &&& env::POSIX);
|
||||||
|
|
||||||
|
fn int sendBreak(Fd fd, int duration) => tcsendbreak(fd, duration);
|
||||||
|
fn int drain(Fd fd) => tcdrain(fd);
|
||||||
|
fn int flush(Fd fd, int queue_selector) => tcflush(fd, queue_selector);
|
||||||
|
fn int flow(Fd fd, int action) => tcflow(fd, action);
|
||||||
|
fn Speed Termios.getOSpeed(Termios* self) => cfgetospeed(self);
|
||||||
|
fn Speed Termios.getISpeed(Termios* self) => cfgetispeed(self);
|
||||||
|
fn int Termios.setOSpeed(Termios* self, Speed speed) => cfsetospeed(self, speed);
|
||||||
|
fn int Termios.setISpeed(Termios* self, Speed speed) => cfsetispeed(self, speed);
|
||||||
|
fn int Termios.getAttr(Termios* self, Fd fd) => tcgetattr(fd, self);
|
||||||
|
fn int Termios.setAttr(Termios* self, Fd fd, int optional_actions) => tcsetattr(fd, optional_actions, self);
|
||||||
|
|
||||||
|
module libc::termios @if(!env::LIBC ||| !env::POSIX);
|
||||||
|
|
||||||
|
distinct Cc = char;
|
||||||
|
distinct Speed = CUInt;
|
||||||
|
distinct Tcflags = CUInt;
|
||||||
|
struct Termios {
|
||||||
|
void* dummy;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn CInt tcgetattr(Fd fd, Termios* self)
|
||||||
|
{
|
||||||
|
unreachable("tcgetattr unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn CInt tcsetattr(Fd fd, CInt optional_actions, Termios* self)
|
||||||
|
{
|
||||||
|
unreachable("tcsetattr unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn CInt tcsendbreak(Fd fd, CInt duration)
|
||||||
|
{
|
||||||
|
unreachable("tcsendbreak unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn CInt tcdrain(Fd fd)
|
||||||
|
{
|
||||||
|
unreachable("tcdrain unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn CInt tcflush(Fd fd, CInt queue_selector)
|
||||||
|
{
|
||||||
|
unreachable("tcflush unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn CInt tcflow(Fd fd, CInt action)
|
||||||
|
{
|
||||||
|
unreachable("tcflow unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Speed cfgetospeed(Termios* self)
|
||||||
|
{
|
||||||
|
unreachable("cfgetospeed unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Speed cfgetispeed(Termios* self)
|
||||||
|
{
|
||||||
|
unreachable("cfgetispeed unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn CInt cfsetospeed(Termios* self, Speed speed)
|
||||||
|
{
|
||||||
|
unreachable("cfsetospeed unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn CInt cfsetispeed(Termios* self, Speed speed)
|
||||||
|
{
|
||||||
|
unreachable("cfsetispeed unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int sendBreak(Fd fd, int duration)
|
||||||
|
{
|
||||||
|
unreachable("sendBreak unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int drain(Fd fd)
|
||||||
|
{
|
||||||
|
unreachable("drain unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int flush(Fd fd, int queue_selector)
|
||||||
|
{
|
||||||
|
unreachable("flush unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int flow(Fd fd, int action)
|
||||||
|
{
|
||||||
|
unreachable("flow unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Speed Termios.getOSpeed(Termios* self)
|
||||||
|
{
|
||||||
|
unreachable("Termios.getOSpeed unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Speed Termios.getISpeed(Termios* self)
|
||||||
|
{
|
||||||
|
unreachable("Termios.getISpeed unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int Termios.setOSpeed(Termios* self, Speed speed)
|
||||||
|
{
|
||||||
|
unreachable("Termios.setOSpeed unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int Termios.setISpeed(Termios* self, Speed speed)
|
||||||
|
{
|
||||||
|
unreachable("Termios.setISpeed unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int Termios.getAttr(Termios* self, Fd fd)
|
||||||
|
{
|
||||||
|
unreachable("Termios.getAttr unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int Termios.setAttr(Termios* self, Fd fd, int optional_actions)
|
||||||
|
{
|
||||||
|
unreachable("Termios.setAttr unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
@@ -92,15 +92,13 @@ fault MatrixError
|
|||||||
|
|
||||||
def Complexf = Complex(<float>);
|
def Complexf = Complex(<float>);
|
||||||
def Complex = Complex(<double>);
|
def Complex = Complex(<double>);
|
||||||
def complexf_identity = complex::identity(<float>);
|
def COMPLEX_IDENTITY = complex::IDENTITY(<double>);
|
||||||
def complex_identity = complex::identity(<double>);
|
def COMPLEXF_IDENTITY = complex::IDENTITY(<float>);
|
||||||
|
|
||||||
def Quaternionf = Quaternion(<float>);
|
def Quaternionf = Quaternion(<float>);
|
||||||
def Quaternion = Quaternion(<double>);
|
def Quaternion = Quaternion(<double>);
|
||||||
def QUATERNION_IDENTITY = quaternion::IDENTITY(<double>);
|
def QUATERNION_IDENTITY = quaternion::IDENTITY(<double>);
|
||||||
def QUATERNIONF_IDENTITY = quaternion::IDENTITY(<float>);
|
def QUATERNIONF_IDENTITY = quaternion::IDENTITY(<float>);
|
||||||
def quaternion_identity = quaternion::identity(<double>);
|
|
||||||
def quaternionf_identity = quaternion::identity(<float>);
|
|
||||||
|
|
||||||
def Matrix2f = Matrix2x2(<float>);
|
def Matrix2f = Matrix2x2(<float>);
|
||||||
def Matrix2 = Matrix2x2(<double>);
|
def Matrix2 = Matrix2x2(<double>);
|
||||||
@@ -161,7 +159,7 @@ macro atan2(x, y)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
|
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
|
||||||
* @require $and(@typekind(y) == ARRAY || @typekind(y) == VECTOR, y.len == 2)
|
* @require (@typekind(y) == ARRAY || @typekind(y) == VECTOR) &&& y.len == 2
|
||||||
* @require $assignable(x, $typeof(y[0]))
|
* @require $assignable(x, $typeof(y[0]))
|
||||||
**/
|
**/
|
||||||
macro sincos(x, y)
|
macro sincos(x, y)
|
||||||
@@ -268,7 +266,7 @@ macro clamp(x, lower, upper) => $$max(($typeof(x))lower, $$min(x, ($typeof(x))up
|
|||||||
* @require values::@is_promotable_to_floatlike(mag) `The input must be a number value or float vector`
|
* @require values::@is_promotable_to_floatlike(mag) `The input must be a number value or float vector`
|
||||||
* @require values::@assign_to(sgn, values::promote_int(mag))
|
* @require values::@assign_to(sgn, values::promote_int(mag))
|
||||||
**/
|
**/
|
||||||
macro copysign(mag, sgn) => $$copysign(values::promote_int(mag), ($typeof(values::promote_int(mag)))sgn);
|
macro copysign(mag, sgn) => $$copysign(values::promote_int_same(mag, sgn), ($typeof(values::promote_int_same(mag, sgn)))sgn);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
|
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
|
||||||
@@ -341,7 +339,10 @@ macro ln(x) => $$log(values::promote_int(x));
|
|||||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
|
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
|
||||||
* @require values::@is_promotable_to_floatlike(base) `The base must be a number or a float vector`
|
* @require values::@is_promotable_to_floatlike(base) `The base must be a number or a float vector`
|
||||||
**/
|
**/
|
||||||
macro log(x, base) => $$log(values::promote_int(x)) / $$log(values::promote_int(base));
|
macro log(x, base)
|
||||||
|
{
|
||||||
|
return $$log(values::promote_int_same(x, base)) / $$log(values::promote_int_same(base, x));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
|
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
|
||||||
@@ -364,7 +365,7 @@ macro max(x, y, ...)
|
|||||||
$else
|
$else
|
||||||
var m = $$max(x, y);
|
var m = $$max(x, y);
|
||||||
$for (var $i = 0; $i < $vacount; $i++)
|
$for (var $i = 0; $i < $vacount; $i++)
|
||||||
m = $$max(m, $vaarg($i));
|
m = $$max(m, $vaarg[$i]);
|
||||||
$endfor
|
$endfor
|
||||||
return m;
|
return m;
|
||||||
$endif
|
$endif
|
||||||
@@ -381,7 +382,7 @@ macro min(x, y, ...)
|
|||||||
$else
|
$else
|
||||||
var m = $$min(x, y);
|
var m = $$min(x, y);
|
||||||
$for (var $i = 0; $i < $vacount; $i++)
|
$for (var $i = 0; $i < $vacount; $i++)
|
||||||
m = $$min(m, $vaarg($i));
|
m = $$min(m, $vaarg[$i]);
|
||||||
$endfor
|
$endfor
|
||||||
return m;
|
return m;
|
||||||
$endif
|
$endif
|
||||||
@@ -405,7 +406,7 @@ macro nearbyint(x) => $$nearbyint(x);
|
|||||||
macro pow(x, exp)
|
macro pow(x, exp)
|
||||||
{
|
{
|
||||||
$if types::is_floatlike($typeof(exp)):
|
$if types::is_floatlike($typeof(exp)):
|
||||||
return $$pow(values::promote_int(x), ($typeof(values::promote_int(x)))exp);
|
return $$pow(values::promote_int_same(x, exp), ($typeof(values::promote_int_same(x, exp)))exp);
|
||||||
$else
|
$else
|
||||||
return $$pow_int(values::promote_int(x), exp);
|
return $$pow_int(values::promote_int(x), exp);
|
||||||
$endif
|
$endif
|
||||||
@@ -692,6 +693,7 @@ macro ichar ichar[<*>].or(ichar[<*>] x) => $$reduce_or(x);
|
|||||||
macro ichar ichar[<*>].xor(ichar[<*>] x) => $$reduce_xor(x);
|
macro ichar ichar[<*>].xor(ichar[<*>] x) => $$reduce_xor(x);
|
||||||
macro ichar ichar[<*>].max(ichar[<*>] x) => $$reduce_max(x);
|
macro ichar ichar[<*>].max(ichar[<*>] x) => $$reduce_max(x);
|
||||||
macro ichar ichar[<*>].min(ichar[<*>] x) => $$reduce_min(x);
|
macro ichar ichar[<*>].min(ichar[<*>] x) => $$reduce_min(x);
|
||||||
|
macro ichar ichar[<*>].dot(ichar[<*>] x, ichar[<*>] y) => (x * y).sum();
|
||||||
|
|
||||||
macro bool[<*>] short[<*>].comp_lt(short[<*>] x, short[<*>] y) => $$veccomplt(x, y);
|
macro bool[<*>] short[<*>].comp_lt(short[<*>] x, short[<*>] y) => $$veccomplt(x, y);
|
||||||
macro bool[<*>] short[<*>].comp_le(short[<*>] x, short[<*>] y) => $$veccomple(x, y);
|
macro bool[<*>] short[<*>].comp_le(short[<*>] x, short[<*>] y) => $$veccomple(x, y);
|
||||||
@@ -707,6 +709,7 @@ macro short short[<*>].or(short[<*>] x) => $$reduce_or(x);
|
|||||||
macro short short[<*>].xor(short[<*>] x) => $$reduce_xor(x);
|
macro short short[<*>].xor(short[<*>] x) => $$reduce_xor(x);
|
||||||
macro short short[<*>].max(short[<*>] x) => $$reduce_max(x);
|
macro short short[<*>].max(short[<*>] x) => $$reduce_max(x);
|
||||||
macro short short[<*>].min(short[<*>] x) => $$reduce_min(x);
|
macro short short[<*>].min(short[<*>] x) => $$reduce_min(x);
|
||||||
|
macro short short[<*>].dot(short[<*>] x, short[<*>] y) => (x * y).sum();
|
||||||
|
|
||||||
macro bool[<*>] int[<*>].comp_lt(int[<*>] x, int[<*>] y) => $$veccomplt(x, y);
|
macro bool[<*>] int[<*>].comp_lt(int[<*>] x, int[<*>] y) => $$veccomplt(x, y);
|
||||||
macro bool[<*>] int[<*>].comp_le(int[<*>] x, int[<*>] y) => $$veccomple(x, y);
|
macro bool[<*>] int[<*>].comp_le(int[<*>] x, int[<*>] y) => $$veccomple(x, y);
|
||||||
@@ -722,6 +725,7 @@ macro int int[<*>].or(int[<*>] x) => $$reduce_or(x);
|
|||||||
macro int int[<*>].xor(int[<*>] x) => $$reduce_xor(x);
|
macro int int[<*>].xor(int[<*>] x) => $$reduce_xor(x);
|
||||||
macro int int[<*>].max(int[<*>] x) => $$reduce_max(x);
|
macro int int[<*>].max(int[<*>] x) => $$reduce_max(x);
|
||||||
macro int int[<*>].min(int[<*>] x) => $$reduce_min(x);
|
macro int int[<*>].min(int[<*>] x) => $$reduce_min(x);
|
||||||
|
macro int int[<*>].dot(int[<*>] x, int[<*>] y) => (x * y).sum();
|
||||||
|
|
||||||
macro bool[<*>] long[<*>].comp_lt(long[<*>] x, long[<*>] y) => $$veccomplt(x, y);
|
macro bool[<*>] long[<*>].comp_lt(long[<*>] x, long[<*>] y) => $$veccomplt(x, y);
|
||||||
macro bool[<*>] long[<*>].comp_le(long[<*>] x, long[<*>] y) => $$veccomple(x, y);
|
macro bool[<*>] long[<*>].comp_le(long[<*>] x, long[<*>] y) => $$veccomple(x, y);
|
||||||
@@ -736,6 +740,7 @@ macro long long[<*>].or(long[<*>] x) => $$reduce_or(x);
|
|||||||
macro long long[<*>].xor(long[<*>] x) => $$reduce_xor(x);
|
macro long long[<*>].xor(long[<*>] x) => $$reduce_xor(x);
|
||||||
macro long long[<*>].max(long[<*>] x) => $$reduce_max(x);
|
macro long long[<*>].max(long[<*>] x) => $$reduce_max(x);
|
||||||
macro long long[<*>].min(long[<*>] x) => $$reduce_min(x);
|
macro long long[<*>].min(long[<*>] x) => $$reduce_min(x);
|
||||||
|
macro long long[<*>].dot(long[<*>] x, long[<*>] y) => (x * y).sum();
|
||||||
|
|
||||||
macro bool[<*>] int128[<*>].comp_lt(int128[<*>] x, int128[<*>] y) => $$veccomplt(x, y);
|
macro bool[<*>] int128[<*>].comp_lt(int128[<*>] x, int128[<*>] y) => $$veccomplt(x, y);
|
||||||
macro bool[<*>] int128[<*>].comp_le(int128[<*>] x, int128[<*>] y) => $$veccomple(x, y);
|
macro bool[<*>] int128[<*>].comp_le(int128[<*>] x, int128[<*>] y) => $$veccomple(x, y);
|
||||||
@@ -750,6 +755,7 @@ macro int128 int128[<*>].or(int128[<*>] x) => $$reduce_or(x);
|
|||||||
macro int128 int128[<*>].xor(int128[<*>] x) => $$reduce_xor(x);
|
macro int128 int128[<*>].xor(int128[<*>] x) => $$reduce_xor(x);
|
||||||
macro int128 int128[<*>].max(int128[<*>] x) => $$reduce_max(x);
|
macro int128 int128[<*>].max(int128[<*>] x) => $$reduce_max(x);
|
||||||
macro int128 int128[<*>].min(int128[<*>] x) => $$reduce_min(x);
|
macro int128 int128[<*>].min(int128[<*>] x) => $$reduce_min(x);
|
||||||
|
macro int128 int128[<*>].dot(int128[<*>] x, int128[<*>] y) => (x * y).sum();
|
||||||
|
|
||||||
macro bool[<*>] bool[<*>].comp_lt(bool[<*>] x, bool[<*>] y) => $$veccomplt(x, y);
|
macro bool[<*>] bool[<*>].comp_lt(bool[<*>] x, bool[<*>] y) => $$veccomplt(x, y);
|
||||||
macro bool[<*>] bool[<*>].comp_le(bool[<*>] x, bool[<*>] y) => $$veccomple(x, y);
|
macro bool[<*>] bool[<*>].comp_le(bool[<*>] x, bool[<*>] y) => $$veccomple(x, y);
|
||||||
@@ -780,6 +786,7 @@ macro char char[<*>].or(char[<*>] x) => $$reduce_or(x);
|
|||||||
macro char char[<*>].xor(char[<*>] x) => $$reduce_xor(x);
|
macro char char[<*>].xor(char[<*>] x) => $$reduce_xor(x);
|
||||||
macro char char[<*>].max(char[<*>] x) => $$reduce_max(x);
|
macro char char[<*>].max(char[<*>] x) => $$reduce_max(x);
|
||||||
macro char char[<*>].min(char[<*>] x) => $$reduce_min(x);
|
macro char char[<*>].min(char[<*>] x) => $$reduce_min(x);
|
||||||
|
macro char char[<*>].dot(char[<*>] x, char[<*>] y) => (x * y).sum();
|
||||||
|
|
||||||
macro bool[<*>] ushort[<*>].comp_lt(ushort[<*>] x, ushort[<*>] y) => $$veccomplt(x, y);
|
macro bool[<*>] ushort[<*>].comp_lt(ushort[<*>] x, ushort[<*>] y) => $$veccomplt(x, y);
|
||||||
macro bool[<*>] ushort[<*>].comp_le(ushort[<*>] x, ushort[<*>] y) => $$veccomple(x, y);
|
macro bool[<*>] ushort[<*>].comp_le(ushort[<*>] x, ushort[<*>] y) => $$veccomple(x, y);
|
||||||
@@ -795,6 +802,7 @@ macro ushort ushort[<*>].or(ushort[<*>] x) => $$reduce_or(x);
|
|||||||
macro ushort ushort[<*>].xor(ushort[<*>] x) => $$reduce_xor(x);
|
macro ushort ushort[<*>].xor(ushort[<*>] x) => $$reduce_xor(x);
|
||||||
macro ushort ushort[<*>].max(ushort[<*>] x) => $$reduce_max(x);
|
macro ushort ushort[<*>].max(ushort[<*>] x) => $$reduce_max(x);
|
||||||
macro ushort ushort[<*>].min(ushort[<*>] x) => $$reduce_min(x);
|
macro ushort ushort[<*>].min(ushort[<*>] x) => $$reduce_min(x);
|
||||||
|
macro ushort ushort[<*>].dot(ushort[<*>] x, ushort[<*>] y) => (x * y).sum();
|
||||||
|
|
||||||
macro bool[<*>] uint[<*>].comp_lt(uint[<*>] x, uint[<*>] y) => $$veccomplt(x, y);
|
macro bool[<*>] uint[<*>].comp_lt(uint[<*>] x, uint[<*>] y) => $$veccomplt(x, y);
|
||||||
macro bool[<*>] uint[<*>].comp_le(uint[<*>] x, uint[<*>] y) => $$veccomple(x, y);
|
macro bool[<*>] uint[<*>].comp_le(uint[<*>] x, uint[<*>] y) => $$veccomple(x, y);
|
||||||
@@ -810,6 +818,7 @@ macro uint uint[<*>].or(uint[<*>] x) => $$reduce_or(x);
|
|||||||
macro uint uint[<*>].xor(uint[<*>] x) => $$reduce_xor(x);
|
macro uint uint[<*>].xor(uint[<*>] x) => $$reduce_xor(x);
|
||||||
macro uint uint[<*>].max(uint[<*>] x) => $$reduce_max(x);
|
macro uint uint[<*>].max(uint[<*>] x) => $$reduce_max(x);
|
||||||
macro uint uint[<*>].min(uint[<*>] x) => $$reduce_min(x);
|
macro uint uint[<*>].min(uint[<*>] x) => $$reduce_min(x);
|
||||||
|
macro uint uint[<*>].dot(uint[<*>] x, uint[<*>] y) => (x * y).sum();
|
||||||
|
|
||||||
macro bool[<*>] ulong[<*>].comp_lt(ulong[<*>] x, ulong[<*>] y) => $$veccomplt(x, y);
|
macro bool[<*>] ulong[<*>].comp_lt(ulong[<*>] x, ulong[<*>] y) => $$veccomplt(x, y);
|
||||||
macro bool[<*>] ulong[<*>].comp_le(ulong[<*>] x, ulong[<*>] y) => $$veccomple(x, y);
|
macro bool[<*>] ulong[<*>].comp_le(ulong[<*>] x, ulong[<*>] y) => $$veccomple(x, y);
|
||||||
@@ -825,6 +834,7 @@ macro ulong ulong[<*>].or(ulong[<*>] x) => $$reduce_or(x);
|
|||||||
macro ulong ulong[<*>].xor(ulong[<*>] x) => $$reduce_xor(x);
|
macro ulong ulong[<*>].xor(ulong[<*>] x) => $$reduce_xor(x);
|
||||||
macro ulong ulong[<*>].max(ulong[<*>] x) => $$reduce_max(x);
|
macro ulong ulong[<*>].max(ulong[<*>] x) => $$reduce_max(x);
|
||||||
macro ulong ulong[<*>].min(ulong[<*>] x) => $$reduce_min(x);
|
macro ulong ulong[<*>].min(ulong[<*>] x) => $$reduce_min(x);
|
||||||
|
macro ulong ulong[<*>].dot(ulong[<*>] x, ulong[<*>] y) => (x * y).sum();
|
||||||
|
|
||||||
macro bool[<*>] uint128[<*>].comp_lt(uint128[<*>] x, uint128[<*>] y) => $$veccomplt(x, y);
|
macro bool[<*>] uint128[<*>].comp_lt(uint128[<*>] x, uint128[<*>] y) => $$veccomplt(x, y);
|
||||||
macro bool[<*>] uint128[<*>].comp_le(uint128[<*>] x, uint128[<*>] y) => $$veccomple(x, y);
|
macro bool[<*>] uint128[<*>].comp_le(uint128[<*>] x, uint128[<*>] y) => $$veccomple(x, y);
|
||||||
@@ -840,6 +850,7 @@ macro uint128 uint128[<*>].or(uint128[<*>] x) => $$reduce_or(x);
|
|||||||
macro uint128 uint128[<*>].xor(uint128[<*>] x) => $$reduce_xor(x);
|
macro uint128 uint128[<*>].xor(uint128[<*>] x) => $$reduce_xor(x);
|
||||||
macro uint128 uint128[<*>].max(uint128[<*>] x) => $$reduce_max(x);
|
macro uint128 uint128[<*>].max(uint128[<*>] x) => $$reduce_max(x);
|
||||||
macro uint128 uint128[<*>].min(uint128[<*>] x) => $$reduce_min(x);
|
macro uint128 uint128[<*>].min(uint128[<*>] x) => $$reduce_min(x);
|
||||||
|
macro uint128 uint128[<*>].dot(uint128[<*>] x, uint128[<*>] y) => (x * y).sum();
|
||||||
|
|
||||||
macro char char.sat_add(char x, char y) => $$sat_add(x, y);
|
macro char char.sat_add(char x, char y) => $$sat_add(x, y);
|
||||||
macro char char.sat_sub(char x, char y) => $$sat_sub(x, y);
|
macro char char.sat_sub(char x, char y) => $$sat_sub(x, y);
|
||||||
@@ -1085,3 +1096,70 @@ macro overflow_mul_helper(x, y) @local
|
|||||||
if ($$overflow_mul(x, y, &res)) return MathError.OVERFLOW?;
|
if ($$overflow_mul(x, y, &res)) return MathError.OVERFLOW?;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro mul_div_helper(val, mul, div) @private
|
||||||
|
{
|
||||||
|
var $Type = $typeof(val);
|
||||||
|
return ($Type)(($Type)mul * (val / ($Type)div) + ($Type)mul * (val % ($Type)div) / ($Type)div);
|
||||||
|
}
|
||||||
|
macro char char.muldiv(self, char mul, char div) => mul_div_helper(self, mul, div);
|
||||||
|
macro ichar ichar.muldiv(self, ichar mul, ichar div) => mul_div_helper(self, mul, div);
|
||||||
|
macro short short.muldiv(self, short mul, short div) => mul_div_helper(self, mul, div);
|
||||||
|
macro ushort ushort.muldiv(self, ushort mul, ushort div) => mul_div_helper(self, mul, div);
|
||||||
|
macro int int.muldiv(self, int mul, int div) => mul_div_helper(self, mul, div);
|
||||||
|
macro uint uint.muldiv(self, uint mul, uint div) => mul_div_helper(self, mul, div);
|
||||||
|
macro long long.muldiv(self, long mul, long div) => mul_div_helper(self, mul, div);
|
||||||
|
macro ulong ulong.muldiv(self, ulong mul, ulong div) => mul_div_helper(self, mul, div);
|
||||||
|
|
||||||
|
macro bool @is_same_vector_or_scalar(#vector_value, #vector_or_scalar) @private
|
||||||
|
{
|
||||||
|
return (values::@is_vector(#vector_or_scalar) &&& values::@is_same_vector_type(#vector_value, #vector_or_scalar)) ||| values::@is_int(#vector_or_scalar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
|
||||||
|
* @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
|
||||||
|
*/
|
||||||
|
macro char[<*>] char[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
|
||||||
|
* @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
|
||||||
|
*/
|
||||||
|
macro ichar[<*>] ichar[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
|
||||||
|
* @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
|
||||||
|
*/
|
||||||
|
macro short[<*>] short[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
|
||||||
|
* @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
|
||||||
|
*/
|
||||||
|
macro ushort[<*>] ushort[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
|
||||||
|
* @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
|
||||||
|
*/
|
||||||
|
macro int[<*>] int[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
|
||||||
|
* @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
|
||||||
|
*/
|
||||||
|
macro uint[<*>] uint[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
|
||||||
|
* @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
|
||||||
|
*/
|
||||||
|
macro long[<*>] long[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
@@ -10,7 +10,7 @@ union Complex
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
macro Complex identity() => { 1, 0 };
|
const Complex IDENTITY = { 1, 0 };
|
||||||
macro Complex Complex.add(self, Complex b) => Complex { .v = self.v + b.v };
|
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.add_each(self, Real b) => Complex { .v = self.v + b };
|
||||||
macro Complex Complex.sub(self, Complex b) => Complex { .v = self.v - b.v };
|
macro Complex Complex.sub(self, Complex b) => Complex { .v = self.v - b.v };
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ fn double __tan(double x, double y, int odd) @extern("__tan") @weak @nostrip
|
|||||||
w = x + r;
|
w = x + r;
|
||||||
if (big)
|
if (big)
|
||||||
{
|
{
|
||||||
s = 1 - 2 * odd;
|
s = (double)(1 - 2 * odd);
|
||||||
v = s - 2.0 * (x + (r - w*w/(w + s)));
|
v = s - 2.0 * (x + (r - w*w/(w + s)));
|
||||||
return sign ? -v : v;
|
return sign ? -v : v;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ fn int __rem_pio2f(float x, double *y)
|
|||||||
if (ix >= 0x7f800000)
|
if (ix >= 0x7f800000)
|
||||||
{
|
{
|
||||||
// x is inf or NaN */
|
// x is inf or NaN */
|
||||||
*y = x-x;
|
*y = x - (double)x;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/* scale x into [2^23, 2^24-1] */
|
/* scale x into [2^23, 2^24-1] */
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ union Quaternion
|
|||||||
|
|
||||||
const Quaternion IDENTITY = { 0, 0, 0, 1 };
|
const Quaternion IDENTITY = { 0, 0, 0, 1 };
|
||||||
|
|
||||||
macro Quaternion identity() @deprecated("Replaced with QUATERNION_IDENTITY constant") => { 0, 0, 0, 1 };
|
|
||||||
macro Quaternion Quaternion.add(Quaternion a, Quaternion b) => Quaternion { .v = a.v + b.v };
|
macro Quaternion Quaternion.add(Quaternion a, Quaternion b) => Quaternion { .v = a.v + b.v };
|
||||||
macro Quaternion Quaternion.add_each(Quaternion a, Real b) => Quaternion { .v = a.v + b };
|
macro Quaternion Quaternion.add_each(Quaternion a, Real b) => Quaternion { .v = a.v + b };
|
||||||
macro Quaternion Quaternion.sub(Quaternion a, Quaternion b) => Quaternion { .v = a.v - b.v };
|
macro Quaternion Quaternion.sub(Quaternion a, Quaternion b) => Quaternion { .v = a.v - b.v };
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* Randoms:
|
||||||
|
* General usage -
|
||||||
|
* 1. Create a Random (see std/math/random for various alternatives), or pick DefaultRandom
|
||||||
|
* 2. Define it `DefaultRandom my_random;`
|
||||||
|
* 3. Seed it: `random::seed(&my_random, some_seed);` or `rand::seed_entropy(&my_random);`
|
||||||
|
* 4. Use it with the functions in random: `float random_float = random::next_float(&my_random);`
|
||||||
|
*
|
||||||
|
* For just a simple integer between 0 and n (not including n), you can use `rand(n)`.
|
||||||
|
**/
|
||||||
module std::math::random;
|
module std::math::random;
|
||||||
|
|
||||||
interface Random
|
|
||||||
{
|
|
||||||
fn void set_seed(char[] input);
|
|
||||||
fn char next_byte();
|
|
||||||
fn ushort next_short();
|
|
||||||
fn uint next_int();
|
|
||||||
fn ulong next_long();
|
|
||||||
fn uint128 next_int128();
|
|
||||||
fn void next_bytes(char[] buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
macro bool is_random(random) => $assignable(random, Random*);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @require is_random(random)
|
* @require is_random(random)
|
||||||
**/
|
**/
|
||||||
@@ -21,12 +18,19 @@ macro void seed(random, seed)
|
|||||||
random.set_seed(@as_char_view(seed));
|
random.set_seed(@as_char_view(seed));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seed the random with some best effort entropy.
|
||||||
|
*
|
||||||
|
* @require is_random(random)
|
||||||
|
**/
|
||||||
macro void seed_entropy(random)
|
macro void seed_entropy(random)
|
||||||
{
|
{
|
||||||
random.set_seed(&&entropy());
|
random.set_seed(&&entropy());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Get the next value between 0 and max (not including max).
|
||||||
|
*
|
||||||
* @require is_random(random)
|
* @require is_random(random)
|
||||||
**/
|
**/
|
||||||
macro int next(random, int max)
|
macro int next(random, int max)
|
||||||
@@ -34,6 +38,11 @@ macro int next(random, int max)
|
|||||||
return (int)(next_double(random) * max);
|
return (int)(next_double(random) * max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def DefaultRandom = Sfc64Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a default random value between 0 and max (not including max)
|
||||||
|
**/
|
||||||
fn int rand(int max) @builtin
|
fn int rand(int max) @builtin
|
||||||
{
|
{
|
||||||
tlocal Sfc64Random default_random;
|
tlocal Sfc64Random default_random;
|
||||||
@@ -45,15 +54,20 @@ fn int rand(int max) @builtin
|
|||||||
}
|
}
|
||||||
return next(&default_random, max);
|
return next(&default_random, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Get 'true' or 'false'
|
||||||
|
*
|
||||||
* @require is_random(random)
|
* @require is_random(random)
|
||||||
**/
|
**/
|
||||||
macro void next_bool(random)
|
macro bool next_bool(random)
|
||||||
{
|
{
|
||||||
return random.next_byte() & 1;
|
return (bool)(random.next_byte() & 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Get a float between 0 and 1.0, not including 1.0.
|
||||||
|
*
|
||||||
* @require is_random(random)
|
* @require is_random(random)
|
||||||
**/
|
**/
|
||||||
macro float next_float(random)
|
macro float next_float(random)
|
||||||
@@ -63,6 +77,8 @@ macro float next_float(random)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Get a double between 0 and 1.0, not including 1.0.
|
||||||
|
*
|
||||||
* @require is_random(random)
|
* @require is_random(random)
|
||||||
**/
|
**/
|
||||||
macro double next_double(random)
|
macro double next_double(random)
|
||||||
@@ -71,6 +87,9 @@ macro double next_double(random)
|
|||||||
return val * 0x1.0p-53;
|
return val * 0x1.0p-53;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// True if the value is a Random.
|
||||||
|
macro bool is_random(random) => $assignable(random, Random);
|
||||||
|
|
||||||
macro uint128 @long_to_int128(#function) => (uint128)#function << 64 + #function;
|
macro uint128 @long_to_int128(#function) => (uint128)#function << 64 + #function;
|
||||||
macro ulong @int_to_long(#function) => (ulong)#function << 32 + #function;
|
macro ulong @int_to_long(#function) => (ulong)#function << 32 + #function;
|
||||||
macro uint @short_to_int(#function) => (uint)#function << 16 + #function;
|
macro uint @short_to_int(#function) => (uint)#function << 16 + #function;
|
||||||
@@ -94,4 +113,17 @@ macro @random_value_to_bytes(#function, char[] bytes)
|
|||||||
bytes = bytes[$byte_size..];
|
bytes = bytes[$byte_size..];
|
||||||
}
|
}
|
||||||
unreachable();
|
unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is the interface to implement for Random implementations, usually
|
||||||
|
// it is not invoked directly.
|
||||||
|
interface Random
|
||||||
|
{
|
||||||
|
fn void set_seed(char[] input);
|
||||||
|
fn char next_byte();
|
||||||
|
fn ushort next_short();
|
||||||
|
fn uint next_int();
|
||||||
|
fn ulong next_long();
|
||||||
|
fn uint128 next_int128();
|
||||||
|
fn void next_bytes(char[] buffer);
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ macro Vec4.distance_sq(self, Vec4 v2) => (self - v2).length_sq();
|
|||||||
|
|
||||||
macro Vec2f.transform(self, Matrix4f mat) => transform2(self, mat);
|
macro Vec2f.transform(self, Matrix4f mat) => transform2(self, mat);
|
||||||
macro Vec2f.rotate(self, float angle) => rotate(self, angle);
|
macro Vec2f.rotate(self, float angle) => rotate(self, angle);
|
||||||
macro Vec2f.angle(self, Vec2f v2) => math::atan2(v2[1], v2[0]) - math::atan2(self[1], v2[0]);
|
macro Vec2f.angle(self, Vec2f v2) => math::atan2(v2.y, v2.x) - math::atan2(self.y, self.x);
|
||||||
|
|
||||||
macro Vec2.transform(self, Matrix4 mat) => transform2(self, mat);
|
macro Vec2.transform(self, Matrix4 mat) => transform2(self, mat);
|
||||||
macro Vec2.rotate(self, double angle) => rotate(self, angle);
|
macro Vec2.rotate(self, double angle) => rotate(self, angle);
|
||||||
macro Vec2.angle(self, Vec2 v2) => math::atan2(v2[1], v2[0]) - math::atan2(self[1], v2[0]);
|
macro Vec2.angle(self, Vec2 v2) => math::atan2(v2.y, v2.x) - math::atan2(self.y, self.x);
|
||||||
|
|
||||||
macro Vec2f.clamp_mag(self, float min, float max) => clamp_magnitude(self, min, max);
|
macro Vec2f.clamp_mag(self, float min, float max) => clamp_magnitude(self, min, max);
|
||||||
macro Vec3f.clamp_mag(self, float min, float max) => clamp_magnitude(self, min, max);
|
macro Vec3f.clamp_mag(self, float min, float max) => clamp_magnitude(self, min, max);
|
||||||
@@ -190,7 +190,7 @@ macro rotate_axis_angle(v, axis, angle) @private
|
|||||||
var w = axis * math::sin(angle);
|
var w = axis * math::sin(angle);
|
||||||
var wv = w.cross(v);
|
var wv = w.cross(v);
|
||||||
var wwv = w.cross(wv);
|
var wwv = w.cross(wv);
|
||||||
wv *= math::cos(angle) * 2;
|
wv *= math::cos(($typeof(v))angle) * 2;
|
||||||
wwv *= 2;
|
wwv *= 2;
|
||||||
|
|
||||||
return v + wv + wwv;
|
return v + wv + wwv;
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import std::ascii;
|
|||||||
|
|
||||||
enum IpProtocol : char (AIFamily ai_family)
|
enum IpProtocol : char (AIFamily ai_family)
|
||||||
{
|
{
|
||||||
UNSPECIFIED (os::AF_UNSPEC),
|
UNSPECIFIED = os::AF_UNSPEC,
|
||||||
IPV4 (os::AF_INET),
|
IPV4 = os::AF_INET,
|
||||||
IPV6 (os::AF_INET6),
|
IPV6 = os::AF_INET6,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InetAddress (Printable)
|
struct InetAddress (Printable)
|
||||||
@@ -56,7 +56,7 @@ fn usz! InetAddress.to_format(InetAddress* addr, Formatter* formatter) @dynamic
|
|||||||
return formatter.printf("%d.%d.%d.%d", addr.ipv4.a, addr.ipv4.b, addr.ipv4.c, addr.ipv4.d)!;
|
return formatter.printf("%d.%d.%d.%d", addr.ipv4.a, addr.ipv4.b, addr.ipv4.c, addr.ipv4.d)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String InetAddress.to_new_string(InetAddress* addr, Allocator* allocator = allocator::heap()) @dynamic
|
fn String InetAddress.to_new_string(InetAddress* addr, Allocator allocator = allocator::heap()) @dynamic
|
||||||
{
|
{
|
||||||
if (addr.is_ipv6)
|
if (addr.is_ipv6)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ fault NetError
|
|||||||
ALREADY_CONNECTED,
|
ALREADY_CONNECTED,
|
||||||
NETWORK_UNREACHABLE,
|
NETWORK_UNREACHABLE,
|
||||||
OPERATION_NOT_SUPPORTED_ON_SOCKET,
|
OPERATION_NOT_SUPPORTED_ON_SOCKET,
|
||||||
|
CONNECTION_RESET,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uint! ipv4toint(String s)
|
fn uint! ipv4toint(String s)
|
||||||
@@ -57,7 +58,7 @@ fn uint! ipv4toint(String s)
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String! int_to_new_ipv4(uint val, Allocator* allocator = allocator::heap())
|
fn String! int_to_new_ipv4(uint val, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
char[3 * 4 + 3 + 1] buffer;
|
char[3 * 4 + 3 + 1] buffer;
|
||||||
String res = (String)io::bprintf(&buffer, "%d.%d.%d.%d", val >> 24, (val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF)!;
|
String res = (String)io::bprintf(&buffer, "%d.%d.%d.%d", val >> 24, (val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF)!;
|
||||||
|
|||||||
@@ -16,11 +16,11 @@ struct Posix_pollfd
|
|||||||
def Posix_nfds_t = CUInt;
|
def Posix_nfds_t = CUInt;
|
||||||
|
|
||||||
extern fn CInt connect(NativeSocket socket, SockAddrPtr address, Socklen_t address_len);
|
extern fn CInt connect(NativeSocket socket, SockAddrPtr address, Socklen_t address_len);
|
||||||
extern fn NativeSocket socket(AIFamily af, AISockType type, AIProtocol ip_protocol) @extern("socket");
|
extern fn NativeSocket socket(AIFamily af, AISockType type, AIProtocol ip_protocol);
|
||||||
extern fn int fcntl(NativeSocket socket, int cmd, ...) @extern("fcntl");
|
extern fn int fcntl(NativeSocket socket, int cmd, ...);
|
||||||
extern fn CInt bind(NativeSocket socket, SockAddrPtr address, Socklen_t address_len) @extern("bind");
|
extern fn CInt bind(NativeSocket socket, SockAddrPtr address, Socklen_t address_len);
|
||||||
extern fn CInt listen(NativeSocket socket, CInt backlog) @extern("listen");
|
extern fn CInt listen(NativeSocket socket, CInt backlog);
|
||||||
extern fn NativeSocket accept(NativeSocket socket, SockAddrPtr address, Socklen_t* address_len) @extern("accept");
|
extern fn NativeSocket accept(NativeSocket socket, SockAddrPtr address, Socklen_t* address_len);
|
||||||
extern fn CInt poll(Posix_pollfd* fds, Posix_nfds_t nfds, CInt timeout);
|
extern fn CInt poll(Posix_pollfd* fds, Posix_nfds_t nfds, CInt timeout);
|
||||||
|
|
||||||
const CUShort POLLIN = 0x0001;
|
const CUShort POLLIN = 0x0001;
|
||||||
@@ -39,6 +39,7 @@ fn anyfault convert_error(Errno error)
|
|||||||
case errno::EALREADY: return NetError.CONNECTION_ALREADY_IN_PROGRESS;
|
case errno::EALREADY: return NetError.CONNECTION_ALREADY_IN_PROGRESS;
|
||||||
case errno::EBADF: return NetError.BAD_SOCKET_DESCRIPTOR;
|
case errno::EBADF: return NetError.BAD_SOCKET_DESCRIPTOR;
|
||||||
case errno::ECONNREFUSED: return NetError.CONNECTION_REFUSED;
|
case errno::ECONNREFUSED: return NetError.CONNECTION_REFUSED;
|
||||||
|
case errno::ECONNRESET: return NetError.CONNECTION_RESET;
|
||||||
case errno::EISCONN: return NetError.ALREADY_CONNECTED;
|
case errno::EISCONN: return NetError.ALREADY_CONNECTED;
|
||||||
case errno::ENETUNREACH: return NetError.NETWORK_UNREACHABLE;
|
case errno::ENETUNREACH: return NetError.NETWORK_UNREACHABLE;
|
||||||
case errno::ENOTSOCK: return NetError.NOT_A_SOCKET;
|
case errno::ENOTSOCK: return NetError.NOT_A_SOCKET;
|
||||||
@@ -55,6 +56,11 @@ fn anyfault socket_error()
|
|||||||
return convert_error(libc::errno());
|
return convert_error(libc::errno());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro bool NativeSocket.is_valid(self)
|
||||||
|
{
|
||||||
|
return (iptr)self >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
macro void! NativeSocket.close(self)
|
macro void! NativeSocket.close(self)
|
||||||
{
|
{
|
||||||
if (libc::close(self))
|
if (libc::close(self))
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const int FIONREAD = 1074030207;
|
|||||||
const int FIONBIO = -2147195266;
|
const int FIONBIO = -2147195266;
|
||||||
const int FIOASYNC = -2147195267;
|
const int FIOASYNC = -2147195267;
|
||||||
|
|
||||||
distinct NativeSocket = uptr;
|
distinct NativeSocket = inline Win32_SOCKET;
|
||||||
|
|
||||||
extern fn CInt ioctlsocket(NativeSocket, CLong cmd, CULong *argp);
|
extern fn CInt ioctlsocket(NativeSocket, CLong cmd, CULong *argp);
|
||||||
extern fn WSAError closesocket(NativeSocket);
|
extern fn WSAError closesocket(NativeSocket);
|
||||||
@@ -22,6 +22,11 @@ extern fn int bind(NativeSocket, SockAddrPtr address, Socklen_t address_len);
|
|||||||
extern fn int listen(NativeSocket, int backlog);
|
extern fn int listen(NativeSocket, int backlog);
|
||||||
extern fn NativeSocket accept(NativeSocket, SockAddrPtr address, Socklen_t* address_len);
|
extern fn NativeSocket accept(NativeSocket, SockAddrPtr address, Socklen_t* address_len);
|
||||||
|
|
||||||
|
macro bool NativeSocket.is_valid(self)
|
||||||
|
{
|
||||||
|
return self != (NativeSocket)(uptr)-1;
|
||||||
|
}
|
||||||
|
|
||||||
fn void! NativeSocket.set_non_blocking(self, bool non_blocking)
|
fn void! NativeSocket.set_non_blocking(self, bool non_blocking)
|
||||||
{
|
{
|
||||||
if (ioctlsocket(self, win32::FIONBIO, &&(CULong)non_blocking))
|
if (ioctlsocket(self, win32::FIONBIO, &&(CULong)non_blocking))
|
||||||
@@ -76,6 +81,7 @@ fn anyfault convert_error(WSAError error)
|
|||||||
case wsa::ENOTSOCK: return NetError.NOT_A_SOCKET;
|
case wsa::ENOTSOCK: return NetError.NOT_A_SOCKET;
|
||||||
case wsa::EOPNOTSUPP: return NetError.OPERATION_NOT_SUPPORTED_ON_SOCKET;
|
case wsa::EOPNOTSUPP: return NetError.OPERATION_NOT_SUPPORTED_ON_SOCKET;
|
||||||
case wsa::ETIMEDOUT: return NetError.CONNECTION_TIMED_OUT;
|
case wsa::ETIMEDOUT: return NetError.CONNECTION_TIMED_OUT;
|
||||||
|
case wsa::ECONNRESET: return NetError.CONNECTION_RESET;
|
||||||
default: return IoError.GENERAL_ERROR;
|
default: return IoError.GENERAL_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ macro void @loop_over_ai(AddrInfo* ai; @body(NativeSocket fd, AddrInfo* ai))
|
|||||||
while (ai)
|
while (ai)
|
||||||
{
|
{
|
||||||
NativeSocket sockfd = os::socket(ai.ai_family, ai.ai_socktype, ai.ai_protocol);
|
NativeSocket sockfd = os::socket(ai.ai_family, ai.ai_socktype, ai.ai_protocol);
|
||||||
if (sockfd > 0)
|
if (sockfd.is_valid())
|
||||||
{
|
{
|
||||||
@body(sockfd, ai);
|
@body(sockfd, ai);
|
||||||
}
|
}
|
||||||
@@ -83,12 +83,12 @@ macro Socket new_socket(fd, ai)
|
|||||||
|
|
||||||
enum SocketOption : char (CInt value)
|
enum SocketOption : char (CInt value)
|
||||||
{
|
{
|
||||||
REUSEADDR (os::SO_REUSEADDR),
|
REUSEADDR = os::SO_REUSEADDR,
|
||||||
REUSEPORT (os::SO_REUSEPORT) @if(!env::WIN32),
|
REUSEPORT @if(!env::WIN32) = os::SO_REUSEPORT,
|
||||||
KEEPALIVE (os::SO_KEEPALIVE),
|
KEEPALIVE = os::SO_KEEPALIVE,
|
||||||
BROADCAST (os::SO_BROADCAST),
|
BROADCAST = os::SO_BROADCAST,
|
||||||
OOBINLINE (os::SO_OOBINLINE),
|
OOBINLINE = os::SO_OOBINLINE,
|
||||||
DONTROUTE (os::SO_DONTROUTE),
|
DONTROUTE = os::SO_DONTROUTE,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bool! Socket.get_broadcast(&self) => self.get_option(BROADCAST);
|
fn bool! Socket.get_broadcast(&self) => self.get_option(BROADCAST);
|
||||||
@@ -120,8 +120,12 @@ fn bool! Socket.get_option(&self, SocketOption option)
|
|||||||
|
|
||||||
fn usz! Socket.read(&self, char[] bytes) @dynamic
|
fn usz! Socket.read(&self, char[] bytes) @dynamic
|
||||||
{
|
{
|
||||||
isz n = libc::read((Fd)self.sock, bytes.ptr, bytes.len);
|
$if env::WIN32:
|
||||||
if (n < 0) return NetError.READ_FAILED?;
|
isz n = libc::recv(self.sock, bytes.ptr, (int)bytes.len, 0);
|
||||||
|
$else
|
||||||
|
isz n = libc::recv(self.sock, bytes.ptr, bytes.len, 0);
|
||||||
|
$endif
|
||||||
|
if (n < 0) return os::socket_error()?;
|
||||||
return (usz)n;
|
return (usz)n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,8 +133,12 @@ fn char! Socket.read_byte(&self) @dynamic => io::@read_byte_using_read(self);
|
|||||||
|
|
||||||
fn usz! Socket.write(&self, char[] bytes) @dynamic
|
fn usz! Socket.write(&self, char[] bytes) @dynamic
|
||||||
{
|
{
|
||||||
isz n = libc::write((Fd)self.sock, bytes.ptr, bytes.len);
|
$if env::WIN32:
|
||||||
if (n < 0) return NetError.WRITE_FAILED?;
|
isz n = libc::send(self.sock, bytes.ptr, (int)bytes.len, 0);
|
||||||
|
$else
|
||||||
|
isz n = libc::send(self.sock, bytes.ptr, bytes.len, 0);
|
||||||
|
$endif
|
||||||
|
if (n < 0) return os::socket_error()?;
|
||||||
return (usz)n;
|
return (usz)n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,8 +43,9 @@ fn TcpServerSocket! listen(String host, uint port, uint backlog, SocketOption...
|
|||||||
fn TcpSocket! accept(TcpServerSocket* server_socket)
|
fn TcpSocket! accept(TcpServerSocket* server_socket)
|
||||||
{
|
{
|
||||||
TcpSocket socket;
|
TcpSocket socket;
|
||||||
|
socket.ai_addrlen = socket.ai_addr_storage.len;
|
||||||
socket.sock = os::accept(server_socket.sock, (SockAddrPtr)&socket.ai_addr_storage, &socket.ai_addrlen);
|
socket.sock = os::accept(server_socket.sock, (SockAddrPtr)&socket.ai_addr_storage, &socket.ai_addrlen);
|
||||||
if (socket.sock < 0) return NetError.ACCEPT_FAILED?;
|
if (!socket.sock.is_valid()) return NetError.ACCEPT_FAILED?;
|
||||||
return socket;
|
return socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ fault BacktraceFault
|
|||||||
RESOLUTION_FAILED,
|
RESOLUTION_FAILED,
|
||||||
}
|
}
|
||||||
|
|
||||||
const Backtrace BACKTRACE_UNKNOWN = { 0, "", "", "", 0, null };
|
const Backtrace BACKTRACE_UNKNOWN = { 0, "", "", "", 0, null, false };
|
||||||
|
|
||||||
struct Backtrace (Printable)
|
struct Backtrace (Printable)
|
||||||
{
|
{
|
||||||
@@ -19,7 +19,8 @@ struct Backtrace (Printable)
|
|||||||
String object_file;
|
String object_file;
|
||||||
String file;
|
String file;
|
||||||
uint line;
|
uint line;
|
||||||
Allocator* allocator;
|
Allocator allocator;
|
||||||
|
bool is_inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -35,15 +36,16 @@ fn bool Backtrace.is_unknown(&self)
|
|||||||
|
|
||||||
fn usz! Backtrace.to_format(&self, Formatter* formatter) @dynamic
|
fn usz! Backtrace.to_format(&self, Formatter* formatter) @dynamic
|
||||||
{
|
{
|
||||||
|
String inline_suffix = self.is_inline ? " [inline]" : "";
|
||||||
if (self.has_file())
|
if (self.has_file())
|
||||||
{
|
{
|
||||||
return formatter.printf("%s (in %s) (%s:%d)", self.function, self.object_file, self.file, self.line);
|
return formatter.printf("%s (in %s) (%s:%d)%s", self.function, self.object_file, self.file, self.line, inline_suffix);
|
||||||
}
|
}
|
||||||
if (self.is_unknown())
|
if (self.is_unknown())
|
||||||
{
|
{
|
||||||
return formatter.printf("??? (in unknown)");
|
return formatter.printf("??? (in unknown)%s", inline_suffix);
|
||||||
}
|
}
|
||||||
return formatter.printf("%s (in %s) (source unavailable)", self.function, self.object_file);
|
return formatter.printf("%s (in %s) (source unavailable)%s", self.function, self.object_file, inline_suffix);
|
||||||
}
|
}
|
||||||
fn void Backtrace.free(&self)
|
fn void Backtrace.free(&self)
|
||||||
{
|
{
|
||||||
@@ -53,7 +55,7 @@ fn void Backtrace.free(&self)
|
|||||||
allocator::free(self.allocator, self.file);
|
allocator::free(self.allocator, self.file);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Backtrace* Backtrace.init(&self, uptr offset, String function, String object_file, String file = "", uint line = 0, Allocator* allocator)
|
fn Backtrace* Backtrace.init(&self, uptr offset, String function, String object_file, String file = "", uint line = 0, Allocator allocator)
|
||||||
{
|
{
|
||||||
if (!allocator)
|
if (!allocator)
|
||||||
{
|
{
|
||||||
@@ -95,7 +97,7 @@ def symbolize_backtrace = linux::symbolize_backtrace @if(env::LINUX);
|
|||||||
def symbolize_backtrace = win32::symbolize_backtrace @if(env::WIN32);
|
def symbolize_backtrace = win32::symbolize_backtrace @if(env::WIN32);
|
||||||
def symbolize_backtrace = darwin::symbolize_backtrace @if(env::DARWIN);
|
def symbolize_backtrace = darwin::symbolize_backtrace @if(env::DARWIN);
|
||||||
|
|
||||||
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator) @if(!env::NATIVE_STACKTRACE)
|
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator) @if(!env::NATIVE_STACKTRACE)
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ import std::io::path, libc, std::os;
|
|||||||
* @require name.len > 0
|
* @require name.len > 0
|
||||||
* @return! SearchResult.MISSING
|
* @return! SearchResult.MISSING
|
||||||
**/
|
**/
|
||||||
fn String! get_var(String name, Allocator* allocator = allocator::heap())
|
fn String! get_var(String name, Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
@pool(allocator)
|
@pool(allocator)
|
||||||
{
|
{
|
||||||
@@ -72,7 +72,7 @@ fn bool set_var(String name, String value, bool overwrite = true)
|
|||||||
/**
|
/**
|
||||||
* Returns the current user's home directory.
|
* Returns the current user's home directory.
|
||||||
**/
|
**/
|
||||||
fn String! get_home_dir(Allocator* using = allocator::heap())
|
fn String! get_home_dir(Allocator using = allocator::heap())
|
||||||
{
|
{
|
||||||
String home;
|
String home;
|
||||||
$if !env::WIN32:
|
$if !env::WIN32:
|
||||||
@@ -83,10 +83,15 @@ fn String! get_home_dir(Allocator* using = allocator::heap())
|
|||||||
return get_var(home, using);
|
return get_var(home, using);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn Path! get_config_dir(Allocator allocator = allocator::heap()) @deprecated("use new_get_config_dir()")
|
||||||
|
{
|
||||||
|
return new_get_config_dir(allocator) @inline;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current user's config directory.
|
* Returns the current user's config directory.
|
||||||
**/
|
**/
|
||||||
fn Path! get_config_dir(Allocator* allocator = allocator::heap())
|
fn Path! new_get_config_dir(Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
@pool(allocator)
|
@pool(allocator)
|
||||||
{
|
{
|
||||||
@@ -100,7 +105,7 @@ fn Path! get_config_dir(Allocator* allocator = allocator::heap())
|
|||||||
String s = get_var_temp("XDG_CONFIG_HOME") ?? get_var_temp("HOME")!;
|
String s = get_var_temp("XDG_CONFIG_HOME") ?? get_var_temp("HOME")!;
|
||||||
const DIR = ".config";
|
const DIR = ".config";
|
||||||
$endif
|
$endif
|
||||||
return path::temp_new(s).append(DIR, .allocator = allocator);
|
return path::temp_new(s).new_append(DIR, .allocator = allocator);
|
||||||
$endif
|
$endif
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -126,11 +131,16 @@ fn bool clear_var(String name)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String! executable_path(Allocator *allocator = allocator::heap())
|
fn String! executable_path(Allocator allocator = allocator::heap()) @deprecated("use new_executable_path()")
|
||||||
|
{
|
||||||
|
return new_executable_path(allocator) @inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn String! new_executable_path(Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
$if env::DARWIN:
|
$if env::DARWIN:
|
||||||
return darwin::executable_path(allocator);
|
return darwin::executable_path(allocator);
|
||||||
$else
|
$else
|
||||||
return "<Unsupported>";
|
return SearchResult.MISSING?;
|
||||||
$endif
|
$endif
|
||||||
}
|
}
|
||||||
@@ -128,17 +128,17 @@ fn ulong! elf_module_image_base(String path) @local
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Backtrace! backtrace_load_from_exec(void* addr, Allocator* allocator) @local
|
fn void! backtrace_add_from_exec(BacktraceList* list, void* addr, Allocator allocator) @local
|
||||||
{
|
{
|
||||||
char[] buf = mem::temp_alloc_array(char, 1024);
|
char[] buf = mem::temp_alloc_array(char, 1024);
|
||||||
|
|
||||||
String exec_path = process::execute_stdout_to_buffer(buf, {"realpath", "-e", string::tformat("/proc/%d/exe", posix::getpid())})!;
|
String exec_path = process::execute_stdout_to_buffer(buf, {"realpath", "-e", string::tformat("/proc/%d/exe", posix::getpid())})!;
|
||||||
String obj_name = exec_path.copy(allocator);
|
String obj_name = exec_path.copy(allocator);
|
||||||
String addr2line = process::execute_stdout_to_buffer(buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", exec_path, string::tformat("0x%x", addr)})!;
|
String addr2line = process::execute_stdout_to_buffer(buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", exec_path, string::tformat("0x%x", addr)})!;
|
||||||
return backtrace_from_addr2line(addr, addr2line, obj_name, "???", allocator);
|
return backtrace_add_addr2line(list, addr, addr2line, obj_name, "???", allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Backtrace! backtrace_load_from_dlinfo(void* addr, Linux_Dl_info* info, Allocator* allocator) @local
|
fn void! backtrace_add_from_dlinfo(BacktraceList* list, void* addr, Linux_Dl_info* info, Allocator allocator) @local
|
||||||
{
|
{
|
||||||
char[] buf = mem::temp_alloc_array(char, 1024);
|
char[] buf = mem::temp_alloc_array(char, 1024);
|
||||||
|
|
||||||
@@ -146,30 +146,19 @@ fn Backtrace! backtrace_load_from_dlinfo(void* addr, Linux_Dl_info* info, Alloca
|
|||||||
ZString obj_path = info.dli_fname;
|
ZString obj_path = info.dli_fname;
|
||||||
String sname = info.dli_sname ? info.dli_sname.str_view() : "???";
|
String sname = info.dli_sname ? info.dli_sname.str_view() : "???";
|
||||||
String addr2line = process::execute_stdout_to_buffer(buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", obj_path.str_view(), string::tformat("0x%x", obj_addr - 1)})!;
|
String addr2line = process::execute_stdout_to_buffer(buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", obj_path.str_view(), string::tformat("0x%x", obj_addr - 1)})!;
|
||||||
return backtrace_from_addr2line(addr, addr2line, info.dli_fname.str_view(), sname, allocator);
|
return backtrace_add_addr2line(list, addr, addr2line, info.dli_fname.str_view(), sname, allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Backtrace! backtrace_from_addr2line(void* addr, String addr2line, String obj_name, String func_name, Allocator* allocator) @local
|
fn Backtrace! backtrace_line_parse(String string, String obj_name, String func_name, bool is_inlined, Allocator allocator)
|
||||||
{
|
{
|
||||||
String[] parts = addr2line.tsplit(" at ");
|
String[] parts = string.trim().tsplit(" at ");
|
||||||
if (parts.len != 2)
|
if (parts.len != 2) return SearchResult.MISSING?;
|
||||||
{
|
|
||||||
return {
|
|
||||||
.function = func_name.copy(allocator),
|
|
||||||
.object_file = obj_name.copy(allocator),
|
|
||||||
.offset = (uptr)addr,
|
|
||||||
.file = "".copy(allocator),
|
|
||||||
.line = 0,
|
|
||||||
.allocator = allocator
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
uint line = 0;
|
uint line = 0;
|
||||||
String source = "";
|
String source = "";
|
||||||
if (!parts[1].contains("?") && parts[1].contains(":"))
|
if (!parts[1].contains("?") && parts[1].contains(":"))
|
||||||
{
|
{
|
||||||
usz index = parts[1].rindex_of_char(':')!;
|
usz index = parts[1].rindex_of_char(':')!;
|
||||||
|
|
||||||
source = parts[1][:index];
|
source = parts[1][:index];
|
||||||
line = parts[1][index + 1..].to_uint()!;
|
line = parts[1][index + 1..].to_uint()!;
|
||||||
}
|
}
|
||||||
@@ -179,25 +168,53 @@ fn Backtrace! backtrace_from_addr2line(void* addr, String addr2line, String obj_
|
|||||||
.file = source.copy(allocator),
|
.file = source.copy(allocator),
|
||||||
.line = line,
|
.line = line,
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
|
.is_inline = is_inlined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
fn void! backtrace_add_addr2line(BacktraceList* list, void* addr, String addr2line, String obj_name, String func_name, Allocator allocator) @local
|
||||||
fn Backtrace! backtrace_load_element(void* addr, Allocator* allocator = allocator::heap()) @local
|
|
||||||
{
|
{
|
||||||
if (!addr) return backtrace::BACKTRACE_UNKNOWN;
|
String[] inline_parts = addr2line.tsplit("(inlined by)");
|
||||||
|
usz last = inline_parts.len - 1;
|
||||||
|
foreach (i, part : inline_parts)
|
||||||
|
{
|
||||||
|
bool is_inline = i != last;
|
||||||
|
Backtrace! trace = backtrace_line_parse(part, obj_name, func_name, is_inline, allocator);
|
||||||
|
if (catch trace)
|
||||||
|
{
|
||||||
|
list.push({
|
||||||
|
.function = func_name.copy(allocator),
|
||||||
|
.object_file = obj_name.copy(allocator),
|
||||||
|
.offset = (uptr)addr,
|
||||||
|
.file = "".copy(allocator),
|
||||||
|
.line = 0,
|
||||||
|
.allocator = allocator,
|
||||||
|
.is_inline = is_inline
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
list.push(trace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void! backtrace_add_element(BacktraceList *list, void* addr, Allocator allocator = allocator::heap()) @local
|
||||||
|
{
|
||||||
|
if (!addr)
|
||||||
|
{
|
||||||
|
list.push(backtrace::BACKTRACE_UNKNOWN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
@pool(allocator) {
|
@pool(allocator) {
|
||||||
Linux_Dl_info info;
|
Linux_Dl_info info;
|
||||||
|
|
||||||
if (dladdr(addr, &info) == 0)
|
if (dladdr(addr, &info) == 0)
|
||||||
{
|
{
|
||||||
return backtrace_load_from_exec(addr, allocator);
|
return backtrace_add_from_exec(list, addr, allocator);
|
||||||
}
|
}
|
||||||
return backtrace_load_from_dlinfo(addr, &info, allocator);
|
return backtrace_add_from_dlinfo(list, addr, &info, allocator);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator)
|
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator)
|
||||||
{
|
{
|
||||||
BacktraceList list;
|
BacktraceList list;
|
||||||
list.new_init(backtrace.len, allocator);
|
list.new_init(backtrace.len, allocator);
|
||||||
@@ -213,8 +230,7 @@ fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator)
|
|||||||
{
|
{
|
||||||
foreach (addr : backtrace)
|
foreach (addr : backtrace)
|
||||||
{
|
{
|
||||||
Backtrace trace = backtrace_load_element(addr, allocator)!;
|
backtrace_add_element(&list, addr, allocator)!;
|
||||||
list.append(trace);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return list;
|
return list;
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ struct Darwin_segment_command_64
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn String! executable_path(Allocator *allocator)
|
fn String! executable_path(Allocator allocator)
|
||||||
{
|
{
|
||||||
char[4096] path;
|
char[4096] path;
|
||||||
uint len = path.len;
|
uint len = path.len;
|
||||||
@@ -80,7 +80,7 @@ fn uptr! load_address() @local
|
|||||||
{
|
{
|
||||||
Darwin_segment_command_64* cmd = darwin::getsegbyname("__TEXT");
|
Darwin_segment_command_64* cmd = darwin::getsegbyname("__TEXT");
|
||||||
if (!cmd) return BacktraceFault.SEGMENT_NOT_FOUND?;
|
if (!cmd) return BacktraceFault.SEGMENT_NOT_FOUND?;
|
||||||
String path = env::executable_path(allocator::temp()) ?? BacktraceFault.EXECUTABLE_PATH_NOT_FOUND?!;
|
String path = env::new_executable_path(allocator::temp()) ?? BacktraceFault.EXECUTABLE_PATH_NOT_FOUND?!;
|
||||||
uint dyld_count = darwin::_dyld_image_count();
|
uint dyld_count = darwin::_dyld_image_count();
|
||||||
for (uint i = 0; i < dyld_count; i++)
|
for (uint i = 0; i < dyld_count; i++)
|
||||||
{
|
{
|
||||||
@@ -93,7 +93,7 @@ fn uptr! load_address() @local
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn Backtrace! backtrace_load_element(String execpath, void* buffer, void* load_address, Allocator* allocator = allocator::heap()) @local
|
fn Backtrace! backtrace_load_element(String execpath, void* buffer, void* load_address, Allocator allocator = allocator::heap()) @local
|
||||||
{
|
{
|
||||||
@pool(allocator)
|
@pool(allocator)
|
||||||
{
|
{
|
||||||
@@ -132,7 +132,7 @@ fn Backtrace! backtrace_load_element(String execpath, void* buffer, void* load_a
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator)
|
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator)
|
||||||
{
|
{
|
||||||
void *load_addr = (void *)load_address()!;
|
void *load_addr = (void *)load_address()!;
|
||||||
BacktraceList list;
|
BacktraceList list;
|
||||||
@@ -150,7 +150,7 @@ fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator)
|
|||||||
String execpath = executable_path(allocator::temp())!;
|
String execpath = executable_path(allocator::temp())!;
|
||||||
foreach (addr : backtrace)
|
foreach (addr : backtrace)
|
||||||
{
|
{
|
||||||
list.append(backtrace_load_element(execpath, addr, load_addr, allocator) ?? backtrace::BACKTRACE_UNKNOWN);
|
list.push(backtrace_load_element(execpath, addr, load_addr, allocator) ?? backtrace::BACKTRACE_UNKNOWN);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return list;
|
return list;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ macro Class! class_by_name(ZString c)
|
|||||||
return cls ?: ObjcFailure.CLASS_NOT_FOUND?;
|
return cls ?: ObjcFailure.CLASS_NOT_FOUND?;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro Class[] class_get_list(Allocator *allocator = allocator::heap())
|
macro Class[] class_get_list(Allocator allocator = allocator::heap())
|
||||||
{
|
{
|
||||||
int num_classes = macos_objc_getClassList(null, 0);
|
int num_classes = macos_objc_getClassList(null, 0);
|
||||||
if (!num_classes) return {};
|
if (!num_classes) return {};
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ enum Win32_GET_FILEEX_INFO_LEVELS
|
|||||||
MAX,
|
MAX,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct Win32_FILE_ATTRIBUTE_DATA
|
struct Win32_FILE_ATTRIBUTE_DATA
|
||||||
{
|
{
|
||||||
Win32_DWORD dwFileAttributes;
|
Win32_DWORD dwFileAttributes;
|
||||||
@@ -17,7 +16,6 @@ struct Win32_FILE_ATTRIBUTE_DATA
|
|||||||
Win32_DWORD nFileSizeLow;
|
Win32_DWORD nFileSizeLow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const MAX_PATH = 260;
|
const MAX_PATH = 260;
|
||||||
|
|
||||||
struct Win32_WIN32_FIND_DATAW
|
struct Win32_WIN32_FIND_DATAW
|
||||||
@@ -99,6 +97,8 @@ extern fn CFile _fdopen(int fd, ZString mode);
|
|||||||
extern fn CInt _access(ZString path, CInt mode);
|
extern fn CInt _access(ZString path, CInt mode);
|
||||||
extern fn CInt _waccess(WString path, CInt mode);
|
extern fn CInt _waccess(WString path, CInt mode);
|
||||||
|
|
||||||
|
extern fn WString _wfullpath(WString absPath, WString relPath, usz maxLength);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
extern ulong _win32_GetCurrentDirectoryW(ulong, Char16* buffer) @extern("GetCurrentDirectoryW");
|
extern ulong _win32_GetCurrentDirectoryW(ulong, Char16* buffer) @extern("GetCurrentDirectoryW");
|
||||||
extern bool _win32_CreateSymbolicLinkW(WString symlink_file, WString target_file, ulong flags) @extern("CreateSymbolicLinkW");
|
extern bool _win32_CreateSymbolicLinkW(WString symlink_file, WString target_file, ulong flags) @extern("CreateSymbolicLinkW");
|
||||||
|
|||||||
6
lib/std/os/win32/gdi.c3
Normal file
6
lib/std/os/win32/gdi.c3
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
module std::os::win32 @if(env::WIN32);
|
||||||
|
|
||||||
|
extern fn Win32_HBRUSH createSolidBrush(Win32_COLORREF) @extern("CreateSolidBrush");
|
||||||
|
extern fn Win32_COLORREF setTextColor(Win32_HDC, Win32_COLORREF) @extern("SetTextColor");
|
||||||
|
extern fn CInt setBkMode(Win32_HDC, CInt) @extern("SetBkMode");
|
||||||
|
extern fn Win32_BOOL textOut(Win32_HDC, CInt, CInt, Win32_LPCWSTR, CInt) @extern("TextOutW");
|
||||||
@@ -2,10 +2,10 @@ module std::os::win32 @if(env::WIN32);
|
|||||||
|
|
||||||
extern fn void* _aligned_malloc(usz size, usz alignment);
|
extern fn void* _aligned_malloc(usz size, usz alignment);
|
||||||
extern fn void* _aligned_realloc(void* memblock, usz size, usz alignment);
|
extern fn void* _aligned_realloc(void* memblock, usz size, usz alignment);
|
||||||
extern fn void* _aligned_recalloc(void* memblock, usz size, usz alignment);
|
extern fn void* _aligned_recalloc(void* memblock, usz num, usz size, usz alignment);
|
||||||
extern fn void _aligned_free(void* memblock);
|
extern fn void _aligned_free(void* memblock);
|
||||||
extern fn void _aligned_msize(void* memblock, usz alignment, usz offset);
|
extern fn void _aligned_msize(void* memblock, usz alignment, usz offset);
|
||||||
extern fn void* _aligned_offset_malloc(usz size, usz alignment, usz offset);
|
extern fn void* _aligned_offset_malloc(usz size, usz alignment, usz offset);
|
||||||
extern fn void* _aligned_offset_realloc(void* memblock, usz size, usz alignment, usz offset);
|
extern fn void* _aligned_offset_realloc(void* memblock, usz size, usz alignment, usz offset);
|
||||||
extern fn void* _aligned_offset_recalloc(void* memblock, usz size, usz alignment, usz offset);
|
extern fn void* _aligned_offset_recalloc(void* memblock, usz num, usz size, usz alignment, usz offset);
|
||||||
extern fn usz _msize(void* memblock);
|
extern fn usz _msize(void* memblock);
|
||||||
@@ -48,20 +48,22 @@ const UNDNAME_COMPLETE = 0x0000;
|
|||||||
|
|
||||||
extern fn void initializeCriticalSection(Win32_CRITICAL_SECTION* section) @extern("InitializeCriticalSection");
|
extern fn void initializeCriticalSection(Win32_CRITICAL_SECTION* section) @extern("InitializeCriticalSection");
|
||||||
extern fn void deleteCriticalSection(Win32_CRITICAL_SECTION* section) @extern("DeleteCriticalSection");
|
extern fn void deleteCriticalSection(Win32_CRITICAL_SECTION* section) @extern("DeleteCriticalSection");
|
||||||
extern fn Win32_HANDLE createMutex(void*, bool, void*) @extern("CreateMutexA");
|
extern fn Win32_HANDLE createMutex(void*, Win32_BOOL, void*) @extern("CreateMutexA");
|
||||||
extern fn Win32_BOOL releaseMutex(Win32_HANDLE) @extern("ReleaseMutex");
|
extern fn Win32_BOOL releaseMutex(Win32_HANDLE) @extern("ReleaseMutex");
|
||||||
extern fn void enterCriticalSection(Win32_CRITICAL_SECTION* section) @extern("EnterCriticalSection");
|
extern fn void enterCriticalSection(Win32_CRITICAL_SECTION* section) @extern("EnterCriticalSection");
|
||||||
extern fn void leaveCriticalSection(Win32_CRITICAL_SECTION* section) @extern("LeaveCriticalSection");
|
extern fn void leaveCriticalSection(Win32_CRITICAL_SECTION* section) @extern("LeaveCriticalSection");
|
||||||
extern fn Win32_BOOL tryEnterCriticalSection(Win32_CRITICAL_SECTION* section) @extern("TryEnterCriticalSection");
|
extern fn Win32_BOOL tryEnterCriticalSection(Win32_CRITICAL_SECTION* section) @extern("TryEnterCriticalSection");
|
||||||
extern fn uint waitForSingleObject(Win32_HANDLE, uint milliseconds) @extern("WaitForSingleObject");
|
extern fn Win32_DWORD waitForSingleObject(Win32_HANDLE hHandle, Win32_DWORD dwMilliseconds) @extern("WaitForSingleObject");
|
||||||
|
extern fn Win32_DWORD waitForSingleObjectEx(Win32_HANDLE hHandle, Win32_DWORD dwMilliseconds, Win32_BOOL bAlertable) @extern("WaitForSingleObjectEx");
|
||||||
|
extern fn Win32_DWORD waitForMultipleObjects(Win32_DWORD nCount, Win32_HANDLE* lpHandles, Win32_BOOL bWaitAll, Win32_DWORD dwMilliseconds) @extern("WaitForMultipleObjects");
|
||||||
|
extern fn Win32_DWORD waitForMultipleObjectsEx(Win32_DWORD nCount, Win32_HANDLE* lpHandles, Win32_BOOL bWaitAll, Win32_DWORD dwMilliseconds, Win32_BOOL bAlertable) @extern("WaitForMultipleObjectsEx");
|
||||||
extern fn void sleep(uint ms) @extern("Sleep");
|
extern fn void sleep(uint ms) @extern("Sleep");
|
||||||
extern fn uint waitForMultipleObjects(uint count, Win32_HANDLE* handles, bool wait_all, uint ms) @extern("WaitForMultipleObjects");
|
|
||||||
extern fn Win32_BOOL resetEvent(Win32_HANDLE event) @extern("ResetEvent");
|
extern fn Win32_BOOL resetEvent(Win32_HANDLE event) @extern("ResetEvent");
|
||||||
extern fn Win32_BOOL setEvent(Win32_HANDLE handle) @extern("SetEvent");
|
extern fn Win32_BOOL setEvent(Win32_HANDLE handle) @extern("SetEvent");
|
||||||
extern fn long interlockedCompareExchange(int* dest, int exchange, int comperand) @extern("InterlockedCompareExchange");
|
extern fn long interlockedCompareExchange(int* dest, int exchange, int comperand) @extern("InterlockedCompareExchange");
|
||||||
extern fn Win32_DWORD sleepEx(Win32_DWORD ms, Win32_BOOL alertable) @extern("SleepEx");
|
extern fn Win32_DWORD sleepEx(Win32_DWORD ms, Win32_BOOL alertable) @extern("SleepEx");
|
||||||
extern fn Win32_HANDLE createThread(void* attributes, usz stack, ThreadFn func, void* arg, uint flags, uint* thread_id) @extern("CreateThread");
|
extern fn Win32_HANDLE createThread(void* attributes, usz stack, ThreadFn func, Win32_LPVOID arg, Win32_DWORD flags, Win32_LPDWORD thread_id) @extern("CreateThread");
|
||||||
extern fn Win32_BOOL getExitCodeThread(Win32_HANDLE handle, uint* exit_code) @extern("GetExitCodeThread");
|
extern fn Win32_BOOL getExitCodeThread(Win32_HANDLE handle, Win32_LPDWORD exit_code) @extern("GetExitCodeThread");
|
||||||
extern fn Win32_BOOL getExitCodeProcess(Win32_HANDLE hProcess, Win32_LPDWORD lpExitCode) @extern("GetExitCodeProcess");
|
extern fn Win32_BOOL getExitCodeProcess(Win32_HANDLE hProcess, Win32_LPDWORD lpExitCode) @extern("GetExitCodeProcess");
|
||||||
extern fn Win32_DWORD getThreadId(Win32_HANDLE) @extern("GetThreadId");
|
extern fn Win32_DWORD getThreadId(Win32_HANDLE) @extern("GetThreadId");
|
||||||
extern fn void exitThread(Win32_DWORD dwExitCode) @noreturn @extern("ExitThread");
|
extern fn void exitThread(Win32_DWORD dwExitCode) @noreturn @extern("ExitThread");
|
||||||
@@ -152,7 +154,7 @@ struct Symbol
|
|||||||
|
|
||||||
Win32_DWORD64 displacement;
|
Win32_DWORD64 displacement;
|
||||||
|
|
||||||
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator)
|
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator)
|
||||||
{
|
{
|
||||||
BacktraceList list;
|
BacktraceList list;
|
||||||
list.new_init(backtrace.len, allocator);
|
list.new_init(backtrace.len, allocator);
|
||||||
@@ -161,12 +163,12 @@ fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator)
|
|||||||
defer symCleanup(process);
|
defer symCleanup(process);
|
||||||
foreach (addr : backtrace)
|
foreach (addr : backtrace)
|
||||||
{
|
{
|
||||||
list.append(resolve_backtrace(addr, process, allocator) ?? backtrace::BACKTRACE_UNKNOWN);
|
list.push(resolve_backtrace(addr, process, allocator) ?? backtrace::BACKTRACE_UNKNOWN);
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Backtrace! resolve_backtrace(void* addr, Win32_HANDLE process, Allocator* allocator)
|
fn Backtrace! resolve_backtrace(void* addr, Win32_HANDLE process, Allocator allocator)
|
||||||
{
|
{
|
||||||
Symbol symbol;
|
Symbol symbol;
|
||||||
//Win32_DWORD image_type = load_modules()!;
|
//Win32_DWORD image_type = load_modules()!;
|
||||||
|
|||||||
33
lib/std/os/win32/windef.c3
Normal file
33
lib/std/os/win32/windef.c3
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
module std::os::win32 @if(env::WIN32);
|
||||||
|
|
||||||
|
struct Win32_RECT
|
||||||
|
{
|
||||||
|
Win32_LONG left;
|
||||||
|
Win32_LONG top;
|
||||||
|
Win32_LONG right;
|
||||||
|
Win32_LONG bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Win32_POINT
|
||||||
|
{
|
||||||
|
Win32_LONG x;
|
||||||
|
Win32_LONG y;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Win32_SIZE
|
||||||
|
{
|
||||||
|
Win32_LONG cx;
|
||||||
|
Win32_LONG cy;
|
||||||
|
}
|
||||||
|
|
||||||
|
def Win32_PSIZE = Win32_SIZE*;
|
||||||
|
def Win32_NPSIZE = Win32_SIZE*;
|
||||||
|
def Win32_LPSIZE = Win32_SIZE*;
|
||||||
|
|
||||||
|
def Win32_PPOINT = Win32_POINT*;
|
||||||
|
def Win32_NPOINT = Win32_POINT*;
|
||||||
|
def Win32_LPOINT = Win32_POINT*;
|
||||||
|
|
||||||
|
def Win32_PRECT = Win32_RECT*;
|
||||||
|
def Win32_NPRECT = Win32_RECT*;
|
||||||
|
def Win32_LPRECT = Win32_RECT*;
|
||||||
150
lib/std/os/win32/winuser.c3
Normal file
150
lib/std/os/win32/winuser.c3
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
module std::os::win32 @if(env::WIN32);
|
||||||
|
|
||||||
|
def Win32_WNDPROC = fn Win32_LRESULT(Win32_HWND, Win32_UINT, Win32_WPARAM, Win32_LPARAM);
|
||||||
|
|
||||||
|
struct Win32_WNDCLASSEXW
|
||||||
|
{
|
||||||
|
Win32_UINT cbSize;
|
||||||
|
Win32_UINT style;
|
||||||
|
Win32_WNDPROC lpfnWndProc;
|
||||||
|
CInt cbClsExtra;
|
||||||
|
CInt cbWndExtra;
|
||||||
|
Win32_HINSTANCE hInstance;
|
||||||
|
Win32_HICON hIcon;
|
||||||
|
Win32_HCURSOR hCursor;
|
||||||
|
Win32_HBRUSH hbrBackground;
|
||||||
|
Win32_LPCWSTR lpszMenuName;
|
||||||
|
Win32_LPCWSTR lpszClassName;
|
||||||
|
Win32_HICON hIconSm;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Win32_MSG
|
||||||
|
{
|
||||||
|
Win32_HWND hwnd;
|
||||||
|
Win32_UINT message;
|
||||||
|
Win32_WPARAM wParam;
|
||||||
|
Win32_LPARAM lParam;
|
||||||
|
Win32_DWORD time;
|
||||||
|
Win32_POINT pt;
|
||||||
|
Win32_DWORD lPrivate;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Win32_PAINTSTRUCT
|
||||||
|
{
|
||||||
|
Win32_HDC hdc;
|
||||||
|
Win32_BOOL fErase;
|
||||||
|
Win32_RECT rcPaint;
|
||||||
|
Win32_BOOL fRestore;
|
||||||
|
Win32_BOOL fIncUpdate;
|
||||||
|
Win32_BYTE[32] rgbReserved;
|
||||||
|
}
|
||||||
|
def Win32_PWNDCLASSEXW = Win32_WNDCLASSEXW*;
|
||||||
|
def Win32_LPWNDCLASSEXW = Win32_WNDCLASSEXW*;
|
||||||
|
def Win32_NPWNDCLASSEXW = Win32_WNDCLASSEXW*;
|
||||||
|
|
||||||
|
def Win32_PPAINTSTRUCT = Win32_PAINTSTRUCT*;
|
||||||
|
def Win32_LPPAINTSTRUCT = Win32_PAINTSTRUCT*;
|
||||||
|
def Win32_NPPAINTSTRUCT = Win32_PAINTSTRUCT*;
|
||||||
|
|
||||||
|
def Win32_PMSG = Win32_MSG*;
|
||||||
|
def Win32_LPMSG = Win32_MSG*;
|
||||||
|
def Win32_NPMSG = Win32_MSG*;
|
||||||
|
|
||||||
|
def Win32_ATOM = ushort;
|
||||||
|
|
||||||
|
const Win32_DWORD WS_BORDER = 0x00800000L;
|
||||||
|
const Win32_DWORD WS_CAPTION = 0x00C00000L;
|
||||||
|
const Win32_DWORD WS_CHILD = 0x40000000L;
|
||||||
|
const Win32_DWORD WS_CHILDWINDOW = 0x40000000L;
|
||||||
|
const Win32_DWORD WS_CLIPCHILDREN = 0x02000000L;
|
||||||
|
const Win32_DWORD WS_CLIPSIBLINGS = 0x04000000L;
|
||||||
|
const Win32_DWORD WS_DISABLED = 0x08000000L;
|
||||||
|
const Win32_DWORD WS_DLGFRAME = 0x00400000L;
|
||||||
|
const Win32_DWORD WS_GROUP = 0x00020000L;
|
||||||
|
const Win32_DWORD WS_HSCROLL = 0x00100000L;
|
||||||
|
const Win32_DWORD WS_ICONIC = 0x20000000L;
|
||||||
|
const Win32_DWORD WS_MAXIMIZE = 0x01000000L;
|
||||||
|
const Win32_DWORD WS_MAXIMIZEBOX = 0x00010000L;
|
||||||
|
const Win32_DWORD WS_MINIMIZE = 0x20000000L;
|
||||||
|
const Win32_DWORD WS_MINIMIZEBOX = 0x00020000L;
|
||||||
|
const Win32_DWORD WS_OVERLAPPED = 0x00000000L;
|
||||||
|
const Win32_DWORD WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
|
||||||
|
const Win32_DWORD WS_POPUP = 0x80000000L;
|
||||||
|
const Win32_DWORD WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU;
|
||||||
|
const Win32_DWORD WS_SIZEBOX = 0x00040000L;
|
||||||
|
const Win32_DWORD WS_SYSMENU = 0x00080000L;
|
||||||
|
const Win32_DWORD WS_TABSTOP = 0x00010000L;
|
||||||
|
const Win32_DWORD WS_THICKFRAME = 0x00040000L;
|
||||||
|
const Win32_DWORD WS_TILED = 0x00000000L;
|
||||||
|
const Win32_DWORD WS_TILEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
|
||||||
|
const Win32_DWORD WS_VISIBLE = 0x10000000L;
|
||||||
|
const Win32_DWORD WS_VSCROLL = 0x00200000L;
|
||||||
|
|
||||||
|
const Win32_UINT MB_OK = 0x00000000;
|
||||||
|
const Win32_UINT MB_OKCANCEL = 0x00000001;
|
||||||
|
const Win32_UINT MB_ABORTRETRYIGNORE = 0x00000002;
|
||||||
|
const Win32_UINT MB_YESNOCANCEL = 0x00000003;
|
||||||
|
const Win32_UINT MB_YESNO = 0x00000004;
|
||||||
|
const Win32_UINT MB_RETRYCANCEL = 0x00000005;
|
||||||
|
const Win32_UINT MB_CANCELTRYCONTINUE = 0x00000006;
|
||||||
|
const Win32_UINT MB_ICONHAND = 0x00000010;
|
||||||
|
const Win32_UINT MB_ICONQUESTION = 0x00000020;
|
||||||
|
const Win32_UINT MB_ICONEXCLAMATION = 0x00000030;
|
||||||
|
const Win32_UINT MB_ICONASTERISK = 0x00000040;
|
||||||
|
const Win32_UINT MB_USERICON = 0x00000080;
|
||||||
|
const Win32_UINT MB_ICONWARNING = MB_ICONEXCLAMATION;
|
||||||
|
const Win32_UINT MB_ICONERROR = MB_ICONHAND;
|
||||||
|
const Win32_UINT MB_ICONINFORMATION = MB_ICONASTERISK;
|
||||||
|
const Win32_UINT MB_ICONSTOP = MB_ICONHAND;
|
||||||
|
|
||||||
|
const GWL_WNDPROC @if(env::ARCH_32_BIT) = -4;
|
||||||
|
const GWL_HINSTANCE @if(env::ARCH_32_BIT) = -6;
|
||||||
|
const GWL_HWNDPARENT @if(env::ARCH_32_BIT) = -8;
|
||||||
|
const GWL_STYLE = -16;
|
||||||
|
const GWL_EXSTYLE = -20;
|
||||||
|
const GWL_USERDATA @if(env::ARCH_32_BIT) = -21;
|
||||||
|
const GWL_ID = -12;
|
||||||
|
const GWLP_WNDPROC = -4;
|
||||||
|
const GWLP_HINSTANCE = -6;
|
||||||
|
const GWLP_HWNDPARENT = -8;
|
||||||
|
const GWLP_USERDATA = -21;
|
||||||
|
const GWLP_ID = -12;
|
||||||
|
|
||||||
|
extern fn Win32_HDC beginPaint(Win32_HWND, Win32_LPPAINTSTRUCT) @extern("BeginPaint");
|
||||||
|
extern fn Win32_LRESULT callWindowProcW(Win32_WNDPROC lpPrevWndFunc, Win32_HWND hWnd, Win32_UINT msg, Win32_WPARAM wParam, Win32_LPARAM lParam) @extern("CallWindowProcW");
|
||||||
|
extern fn Win32_HWND createWindowExW(Win32_DWORD, Win32_LPCWSTR, Win32_LPCWSTR, Win32_DWORD, CInt, CInt, CInt, CInt, Win32_HWND, Win32_HMENU, Win32_HINSTANCE, Win32_LPVOID) @extern("CreateWindowExW");
|
||||||
|
extern fn Win32_LRESULT defWindowProcW(Win32_HWND, Win32_UINT, Win32_WPARAM, Win32_LPARAM) @extern("DefWindowProcW");
|
||||||
|
extern fn Win32_BOOL dispatchMessage(Win32_MSG* lpMsg) @extern("DispatchMessageW");
|
||||||
|
extern fn Win32_BOOL endPaint(Win32_HWND, Win32_LPPAINTSTRUCT) @extern("EndPaint");
|
||||||
|
extern fn Win32_BOOL getMessageW(Win32_LPMSG, Win32_HWND, Win32_UINT, Win32_UINT) @extern("GetMessageW");
|
||||||
|
extern fn Win32_BOOL getUpdateRect(Win32_HWND hWnd, Win32_LPRECT lpRect, Win32_BOOL bErase) @extern("GetUpdateRect");
|
||||||
|
extern fn Win32_LONG_PTR getWindowLongPtrW(Win32_HWND hWnd, CInt nIndex) @extern("GetWindowLongPtrW");
|
||||||
|
extern fn Win32_LONG getWindowLongW(Win32_HWND hWnd, CInt nIndex) @extern("GetWindowLongW");
|
||||||
|
extern fn Win32_HCURSOR loadCursorW(Win32_HINSTANCE instance, Win32_LPCWSTR cursorName) @extern("LoadCursorW");
|
||||||
|
extern fn Win32_HICON loadIconW(Win32_HINSTANCE instance, Win32_LPCWSTR iconName) @extern("LoadIconW");
|
||||||
|
extern fn int messageBoxW(Win32_HWND hWnd, Win32_LPCWSTR lpText, Win32_LPCWSTR lpCaption, Win32_UINT uType) @extern("MessageBoxW");
|
||||||
|
extern fn void postQuitMessage(CInt) @extern("PostQuitMessage");
|
||||||
|
extern fn Win32_ATOM registerClassExW(Win32_WNDCLASSEXW*) @extern("RegisterClassExW");
|
||||||
|
extern fn Win32_LONG_PTR setWindowLongPtrW(Win32_HWND hWnd, CInt nIndex, Win32_LONG_PTR dwNewLong) @extern("SetWindowLongPtrW");
|
||||||
|
extern fn Win32_LONG setWindowLongW(Win32_HWND hWnd, CInt nIndex, Win32_LONG dwNewLong) @extern("SetWindowLongW");
|
||||||
|
extern fn Win32_BOOL showWindow(Win32_HWND, CInt) @extern("ShowWindow");
|
||||||
|
extern fn Win32_BOOL translateMessage(Win32_MSG* lpMsg) @extern("TranslateMessage");
|
||||||
|
extern fn Win32_BOOL updateWindow(Win32_HWND) @extern("UpdateWindow");
|
||||||
|
|
||||||
|
macro getWindowLongPtr(Win32_HWND hWnd, CInt nIndex)
|
||||||
|
{
|
||||||
|
$if env::ARCH_64_BIT:
|
||||||
|
return getWindowLongPtrW(hWnd, nIndex);
|
||||||
|
$else
|
||||||
|
return getWindowLongW(hWnd, nIndex);
|
||||||
|
$endif
|
||||||
|
}
|
||||||
|
|
||||||
|
macro setWindowLongPtr(Win32_HWND hWnd, CInt nIndex, dwNewLong)
|
||||||
|
{
|
||||||
|
$if env::ARCH_64_BIT:
|
||||||
|
return setWindowLongPtrW(hWnd, nIndex, dwNewLong);
|
||||||
|
$else
|
||||||
|
return setWindowLongW(hWnd, nIndex, dwNewLong);
|
||||||
|
$endif
|
||||||
|
}
|
||||||
@@ -32,6 +32,8 @@ extern fn CInt win32_WSAPoll(Win32_LPWSAPOLLFD fdArray, Win32_ULONG fds, Win32_I
|
|||||||
extern fn WSAError win32_WSAGetLastError() @extern("WSAGetLastError") @builtin;
|
extern fn WSAError win32_WSAGetLastError() @extern("WSAGetLastError") @builtin;
|
||||||
extern fn void win32_WSASetLastError(WSAError error) @extern("WSASetLastError") @builtin;
|
extern fn void win32_WSASetLastError(WSAError error) @extern("WSASetLastError") @builtin;
|
||||||
extern fn CInt win32_WSAStartup(Win32_WORD, void*) @extern("WSAStartup") @builtin;
|
extern fn CInt win32_WSAStartup(Win32_WORD, void*) @extern("WSAStartup") @builtin;
|
||||||
|
extern fn CInt win32_WSACleanup() @extern("WSACleanup") @builtin;
|
||||||
|
|
||||||
const int FIONBIO = -2147195266;
|
const int FIONBIO = -2147195266;
|
||||||
const int FIONREAD = 1074030207;
|
const int FIONREAD = 1074030207;
|
||||||
const int SIOCATMARK = 1074033415;
|
const int SIOCATMARK = 1074033415;
|
||||||
|
|||||||
@@ -3,17 +3,20 @@ module std::sort;
|
|||||||
/**
|
/**
|
||||||
* Perform a binary search over the sorted array and return the index
|
* Perform a binary search over the sorted array and return the index
|
||||||
* in [0, array.len) where x would be inserted or cmp(i) is true and cmp(j) is true for j in [i, array.len).
|
* in [0, array.len) where x would be inserted or cmp(i) is true and cmp(j) is true for j in [i, array.len).
|
||||||
* @require $defined(list[0]) && $defined(list.len) "The list must be indexable"
|
* @require @is_sortable(list) "The list must be sortable"
|
||||||
* @require $or(@typeid(cmp) == void*.typeid, @is_comparer(cmp, list)) "Expected a comparison function which compares values"
|
* @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 usz binarysearch(list, x, cmp = null) @builtin
|
macro usz binarysearch(list, x, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin
|
||||||
{
|
{
|
||||||
usz i;
|
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;)
|
for (usz j = len; i < j;)
|
||||||
{
|
{
|
||||||
usz half = i + (j - i) / 2;
|
usz half = i + (j - i) / 2;
|
||||||
$if @typeid(cmp) == void*.typeid:
|
$if $no_cmp:
|
||||||
switch
|
switch
|
||||||
{
|
{
|
||||||
case greater(list[half], x): j = half;
|
case greater(list[half], x): j = half;
|
||||||
@@ -21,11 +24,20 @@ macro usz binarysearch(list, x, cmp = null) @builtin
|
|||||||
default: return half;
|
default: return half;
|
||||||
}
|
}
|
||||||
$else
|
$else
|
||||||
|
|
||||||
$switch
|
$switch
|
||||||
$case $typeof(cmp).params[0] == @typeid(list[0]):
|
$case $defined(cmp(list[0], list[0], context)):
|
||||||
|
int res = cmp(list[half], x, context);
|
||||||
|
$case $defined(cmp(list[0], list[0])):
|
||||||
|
assert(!$has_context);
|
||||||
int res = cmp(list[half], x);
|
int res = cmp(list[half], x);
|
||||||
$default:
|
$case $defined(cmp(&list[0], &list[0], context)):
|
||||||
|
int res = cmp(&list[half], &x, context);
|
||||||
|
$case $defined(cmp(&list[0], &list[0])):
|
||||||
|
assert(!$has_context);
|
||||||
int res = cmp(&list[half], &x);
|
int res = cmp(&list[half], &x);
|
||||||
|
$default:
|
||||||
|
assert(false, "Invalid comparison function");
|
||||||
$endswitch
|
$endswitch
|
||||||
switch
|
switch
|
||||||
{
|
{
|
||||||
|
|||||||
181
lib/std/sort/countingsort.c3
Normal file
181
lib/std/sort/countingsort.c3
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
module std::sort;
|
||||||
|
import std::sort::is;
|
||||||
|
import std::sort::cs;
|
||||||
|
import std::sort::qs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort list using the counting sort algorithm.
|
||||||
|
* @require @is_sortable(list) "The list must be indexable and support .len or .len()"
|
||||||
|
* @require @is_cmp_key_fn(key_fn, list) "Expected a transformation function which returns an unsigned integer."
|
||||||
|
**/
|
||||||
|
macro countingsort(list, key_fn = EMPTY_MACRO_SLOT) @builtin
|
||||||
|
{
|
||||||
|
usz len = sort::@len_from_list(list);
|
||||||
|
cs::csort(<$typeof(list), $typeof(key_fn)>)(list, 0, len, key_fn, ~((uint)0));
|
||||||
|
}
|
||||||
|
|
||||||
|
macro insertionsort_indexed(list, start, end, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin
|
||||||
|
{
|
||||||
|
is::isort(<$typeof(list), $typeof(cmp), $typeof(context)>)(list, (usz)start, (usz)end, cmp, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro quicksort_indexed(list, start, end, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin
|
||||||
|
{
|
||||||
|
qs::qsort(<$typeof(list), $typeof(cmp), $typeof(context)>)(list, (isz)start, (isz)(end-1), cmp, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
module std::sort::cs(<Type, KeyFn>);
|
||||||
|
|
||||||
|
def Counts = usz[256] @private;
|
||||||
|
def Ranges = usz[257] @private;
|
||||||
|
def Indexs = char[256] @private;
|
||||||
|
def ElementType = $typeof(Type{}[0]);
|
||||||
|
|
||||||
|
const bool NO_KEY_FN @private = types::is_same(KeyFn, EmptySlot);
|
||||||
|
const bool KEY_BY_VALUE @private = NO_KEY_FN ||| $assignable(Type{}[0], $typefrom(KeyFn.params[0]));
|
||||||
|
const bool LIST_HAS_REF @private = $defined(&Type{}[0]);
|
||||||
|
|
||||||
|
def KeyFnReturnType = $typefrom(KeyFn.returns) @if(!NO_KEY_FN);
|
||||||
|
def KeyFnReturnType = ElementType @if(NO_KEY_FN);
|
||||||
|
def CmpCallback = fn int(ElementType, ElementType) @if(KEY_BY_VALUE && NO_KEY_FN);
|
||||||
|
def CmpCallback = fn int(ElementType*, ElementType*) @if(!KEY_BY_VALUE && NO_KEY_FN);
|
||||||
|
def CmpCallback = fn int(ElementType, ElementType, KeyFn) @if(KEY_BY_VALUE && !NO_KEY_FN);
|
||||||
|
def CmpCallback = fn int(ElementType*, ElementType*, KeyFn) @if(!KEY_BY_VALUE && !NO_KEY_FN);
|
||||||
|
|
||||||
|
fn void csort(Type list, usz low, usz high, KeyFn key_fn, uint byte_idx)
|
||||||
|
{
|
||||||
|
if (high <= low) return;
|
||||||
|
$if NO_KEY_FN:
|
||||||
|
CmpCallback compare_fn = fn (lhs, rhs) => compare_to(lhs, rhs);
|
||||||
|
$else
|
||||||
|
CmpCallback compare_fn = fn (lhs, rhs, key_fn) => compare_to(key_fn(lhs), key_fn(rhs));
|
||||||
|
$endif;
|
||||||
|
|
||||||
|
byte_idx = byte_idx >= KeyFnReturnType.sizeof ? KeyFnReturnType.sizeof - 1 : byte_idx;
|
||||||
|
|
||||||
|
Counts counts;
|
||||||
|
Ranges ranges;
|
||||||
|
Indexs indexs;
|
||||||
|
|
||||||
|
KeyFnReturnType mn = ~(KeyFnReturnType)0;
|
||||||
|
KeyFnReturnType mx = 0;
|
||||||
|
|
||||||
|
char last_key = 0;
|
||||||
|
char keys_ordered = 1;
|
||||||
|
|
||||||
|
for (usz i = low; i < high; i++)
|
||||||
|
{
|
||||||
|
$switch
|
||||||
|
$case NO_KEY_FN:
|
||||||
|
KeyFnReturnType k = list[i];
|
||||||
|
$case KEY_BY_VALUE:
|
||||||
|
KeyFnReturnType k = key_fn(list[i]);
|
||||||
|
$case LIST_HAS_REF:
|
||||||
|
KeyFnReturnType k = key_fn(&list[i]);
|
||||||
|
$default:
|
||||||
|
KeyFnReturnType k = key_fn(&&list[i]);
|
||||||
|
$endswitch;
|
||||||
|
|
||||||
|
char key_byte = (char)((k >> (byte_idx * 8)) & 0xff);
|
||||||
|
++counts[key_byte];
|
||||||
|
|
||||||
|
mn = k < mn ? k : mn;
|
||||||
|
mx = k > mx ? k : mx;
|
||||||
|
|
||||||
|
keys_ordered = keys_ordered & (char)(key_byte >= last_key);
|
||||||
|
last_key = key_byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyFnReturnType diff = mx - mn;
|
||||||
|
if (diff == 0) return;
|
||||||
|
|
||||||
|
ushort fallback0_count = 0;
|
||||||
|
ushort fallback1_count = 0;
|
||||||
|
ushort recursion_count = 0;
|
||||||
|
|
||||||
|
usz total = 0;
|
||||||
|
foreach (char i, count : counts)
|
||||||
|
{
|
||||||
|
indexs[fallback0_count] = i;
|
||||||
|
indexs[255 - recursion_count] = i;
|
||||||
|
|
||||||
|
fallback0_count += (ushort)(count > 1 && count <= 32);
|
||||||
|
recursion_count += (ushort)(count > 128);
|
||||||
|
|
||||||
|
counts[i] = total;
|
||||||
|
ranges[i] = total;
|
||||||
|
total += count;
|
||||||
|
}
|
||||||
|
ranges[256] = total;
|
||||||
|
|
||||||
|
ushort remaining_indexs = 256 - (fallback0_count + recursion_count);
|
||||||
|
for(ushort i = 0; (i < 256) && remaining_indexs; i++) {
|
||||||
|
indexs[fallback0_count + fallback1_count] = (char)i;
|
||||||
|
usz count = ranges[i + 1] - ranges[i];
|
||||||
|
ushort within_fallback1_range = (ushort)(count > 32 && count <= 128);
|
||||||
|
fallback1_count += within_fallback1_range;
|
||||||
|
remaining_indexs -= within_fallback1_range;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!keys_ordered)
|
||||||
|
{
|
||||||
|
usz sorted_count = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
foreach (x, s : counts)
|
||||||
|
{
|
||||||
|
usz e = ranges[x + 1];
|
||||||
|
sorted_count += (e - s);
|
||||||
|
for (; s < e; s++)
|
||||||
|
{
|
||||||
|
$switch
|
||||||
|
$case NO_KEY_FN:
|
||||||
|
KeyFnReturnType k = list[low + s];
|
||||||
|
$case KEY_BY_VALUE:
|
||||||
|
KeyFnReturnType k = key_fn(list[low + s]);
|
||||||
|
$case LIST_HAS_REF:
|
||||||
|
KeyFnReturnType k = key_fn(&list[low + s]);
|
||||||
|
$default:
|
||||||
|
KeyFnReturnType k = key_fn(&&list[low + s]);
|
||||||
|
$endswitch;
|
||||||
|
char k_idx = (char)(k >> (byte_idx * 8));
|
||||||
|
usz target_idx = counts[k_idx];
|
||||||
|
@swap(list[low + s], list[low + target_idx]);
|
||||||
|
counts[k_idx]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (sorted_count < ranges[256]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byte_idx)
|
||||||
|
{
|
||||||
|
for (usz p = 0; p < fallback0_count; p++) {
|
||||||
|
usz i = indexs[p];
|
||||||
|
|
||||||
|
usz start_offset = ranges[i];
|
||||||
|
usz end_offset = ranges[i + 1];
|
||||||
|
|
||||||
|
insertionsort_indexed(list, low + start_offset, low + end_offset, compare_fn, key_fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (usz p = 0; p < fallback1_count; p++) {
|
||||||
|
usz i = indexs[fallback0_count + p];
|
||||||
|
|
||||||
|
usz start_offset = ranges[i];
|
||||||
|
usz end_offset = ranges[i + 1];
|
||||||
|
|
||||||
|
quicksort_indexed(list, low + start_offset, low + end_offset, compare_fn, key_fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (usz p = 0; p < recursion_count; p++)
|
||||||
|
{
|
||||||
|
usz i = indexs[255 - p];
|
||||||
|
|
||||||
|
usz start_offset = ranges[i];
|
||||||
|
usz end_offset = ranges[i + 1];
|
||||||
|
|
||||||
|
csort(list, low + start_offset, low + end_offset, key_fn, byte_idx - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
65
lib/std/sort/insertionsort.c3
Normal file
65
lib/std/sort/insertionsort.c3
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
module std::sort;
|
||||||
|
import std::sort::is;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
{
|
||||||
|
usz len = sort::@len_from_list(list);
|
||||||
|
is::isort(<$typeof(list), $typeof(cmp), $typeof(context)>)(list, 0, (isz)len, cmp, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
module std::sort::is(<Type, CmpFn, Context>);
|
||||||
|
|
||||||
|
def ElementType = $typeof(Type{}[0]);
|
||||||
|
|
||||||
|
fn void isort(Type list, usz low, usz high, CmpFn comp, Context context)
|
||||||
|
{
|
||||||
|
var $has_cmp = @is_valid_macro_slot(comp);
|
||||||
|
var $has_context = @is_valid_macro_slot(context);
|
||||||
|
var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom(CmpFn.params[0]));
|
||||||
|
var $has_get_ref = $defined(&list[0]);
|
||||||
|
for (usz i = low; i < high; ++i)
|
||||||
|
{
|
||||||
|
usz j = i;
|
||||||
|
for (;j > low;)
|
||||||
|
{
|
||||||
|
$if $has_get_ref:
|
||||||
|
ElementType *rhs = &list[j];
|
||||||
|
ElementType *lhs = &list[--j];
|
||||||
|
$switch
|
||||||
|
$case $cmp_by_value && $has_context:
|
||||||
|
if (comp(*rhs, *lhs, context) >= 0) break;
|
||||||
|
$case $cmp_by_value:
|
||||||
|
if (comp(*rhs, *lhs) >= 0) break;
|
||||||
|
$case $has_cmp && $has_context:
|
||||||
|
if (comp(rhs, lhs, context) >= 0) break;
|
||||||
|
$case $has_cmp:
|
||||||
|
if (comp(rhs, lhs) >= 0) break;
|
||||||
|
$default:
|
||||||
|
if (!less(*rhs, *lhs)) break;
|
||||||
|
$endswitch
|
||||||
|
@swap(*rhs, *lhs);
|
||||||
|
$else
|
||||||
|
usz r = j;
|
||||||
|
--j;
|
||||||
|
$switch
|
||||||
|
$case $cmp_by_value && $has_context:
|
||||||
|
if (comp(list[r], list[j], context) >= 0) break;
|
||||||
|
$case $cmp_by_value:
|
||||||
|
if (comp(list[r], list[j]) >= 0) break;
|
||||||
|
$case $has_cmp && $has_context:
|
||||||
|
if (comp(&list[r], &list[j], context) >= 0) break;
|
||||||
|
$case $has_cmp:
|
||||||
|
if (comp(&list[r], &list[j]) >= 0) break;
|
||||||
|
$default:
|
||||||
|
if (!less(list[r], list[j])) break;
|
||||||
|
$endswitch
|
||||||
|
@swap(list[r], list[j]);
|
||||||
|
$endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,18 +3,17 @@ import std::sort::qs;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sort list using the quick sort algorithm.
|
* Sort list using the quick sort algorithm.
|
||||||
* @require $defined(list[0]) && $defined(list.len) "The list must be indexable and support .len or .len()"
|
* @require @is_sortable(list) "The list must be indexable and support .len or .len()"
|
||||||
* @require $or(@typeid(cmp) == void*.typeid, @is_comparer(cmp, list)) "Expected a comparison function which compares values"
|
* @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 quicksort(list, cmp = null) @builtin
|
macro quicksort(list, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin
|
||||||
{
|
{
|
||||||
var $Type = $typeof(list);
|
|
||||||
var $CmpType = $typeof(cmp);
|
|
||||||
usz len = sort::@len_from_list(list);
|
usz len = sort::@len_from_list(list);
|
||||||
qs::qsort(<$Type, $CmpType>)(list, 0, (isz)len - 1, cmp);
|
qs::qsort(<$typeof(list), $typeof(cmp), $typeof(context)>)(list, 0, (isz)len - 1, cmp, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
module std::sort::qs(<Type, Comparer>);
|
module std::sort::qs(<Type, CmpFn, Context>);
|
||||||
|
|
||||||
def ElementType = $typeof(Type{}[0]);
|
def ElementType = $typeof(Type{}[0]);
|
||||||
|
|
||||||
@@ -28,10 +27,12 @@ def Stack = StackElementItem[64] @private;
|
|||||||
|
|
||||||
// Based on https://alienryderflex.com/quicksort by Darel Rex Finley, Public Domain.
|
// Based on https://alienryderflex.com/quicksort by Darel Rex Finley, Public Domain.
|
||||||
|
|
||||||
fn void qsort(Type list, isz low, isz high, Comparer cmp)
|
fn void qsort(Type list, isz low, isz high, CmpFn cmp, Context context)
|
||||||
{
|
{
|
||||||
var $no_cmp = Comparer.typeid == void*.typeid;
|
var $has_cmp = @is_valid_macro_slot(cmp);
|
||||||
var $cmp_by_value = $and(!$no_cmp, Comparer.params[0] == @typeid(list[0]));
|
var $has_context = @is_valid_macro_slot(context);
|
||||||
|
var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom(CmpFn.params[0]));
|
||||||
|
|
||||||
if (low >= 0 && high >= 0 && low < high)
|
if (low >= 0 && high >= 0 && low < high)
|
||||||
{
|
{
|
||||||
Stack stack;
|
Stack stack;
|
||||||
@@ -51,20 +52,25 @@ fn void qsort(Type list, isz low, isz high, Comparer cmp)
|
|||||||
while (l < h)
|
while (l < h)
|
||||||
{
|
{
|
||||||
$switch
|
$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:
|
$case $cmp_by_value:
|
||||||
while (cmp(list[h], pivot) >= 0 && l < h) h--;
|
while (cmp(list[h], pivot) >= 0 && l < h) h--;
|
||||||
$case !$no_cmp:
|
if (l < h) list[l++] = list[h];
|
||||||
while (cmp(&list[h], &pivot) >= 0 && l < h) h--;
|
|
||||||
$default:
|
|
||||||
while (greater_eq(list[h], pivot) && l < h) h--;
|
|
||||||
$endswitch
|
|
||||||
if (l < h) list[l++] = list[h];
|
|
||||||
$switch
|
|
||||||
$case $cmp_by_value:
|
|
||||||
while (cmp(list[l], pivot) <= 0 && l < h) l++;
|
while (cmp(list[l], pivot) <= 0 && l < h) l++;
|
||||||
$case !$no_cmp:
|
$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++;
|
while (cmp(&list[l], &pivot) <= 0 && l < h) l++;
|
||||||
$default:
|
$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++;
|
while (less_eq(list[l], pivot) && l < h) l++;
|
||||||
$endswitch
|
$endswitch
|
||||||
if (l < h) list[h--] = list[l];
|
if (l < h) list[h--] = list[l];
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user