mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Compare commits
533 Commits
named_argu
...
v0.6.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15503a9054 | ||
|
|
660654f9e0 | ||
|
|
2f7d18bfb8 | ||
|
|
29a6a0db32 | ||
|
|
7b2fe92241 | ||
|
|
70da1f748a | ||
|
|
3033295884 | ||
|
|
8c12f92aff | ||
|
|
76da7936e5 | ||
|
|
2a924ae3b0 | ||
|
|
5ba9acad5d | ||
|
|
4cb984e56d | ||
|
|
70606a2bbe | ||
|
|
259112e178 | ||
|
|
a2cde1e072 | ||
|
|
de04c52379 | ||
|
|
27970085e5 | ||
|
|
e0afc0f9ea | ||
|
|
0e44e63fa8 | ||
|
|
2623d7d525 | ||
|
|
4e78e32ced | ||
|
|
f65ca07b62 | ||
|
|
7a805340c5 | ||
|
|
a863d7fe9e | ||
|
|
f60bfa8442 | ||
|
|
50fdf9900d | ||
|
|
8785c2c46f | ||
|
|
c8fa7b0cb3 | ||
|
|
f2e69f8fdc | ||
|
|
8a9edc02b6 | ||
|
|
d173ba0377 | ||
|
|
fbb4ae056a | ||
|
|
ae10ae6847 | ||
|
|
64ab67bb58 | ||
|
|
dd650bc334 | ||
|
|
48923a2237 | ||
|
|
1f29110271 | ||
|
|
e133f4406a | ||
|
|
2fa258a066 | ||
|
|
87d29a62e5 | ||
|
|
190dc246b3 | ||
|
|
6ae84aac78 | ||
|
|
c8c58f946c | ||
|
|
0d1fb2843e | ||
|
|
d67fcb3956 | ||
|
|
f1325f6539 | ||
|
|
3a1bba19af | ||
|
|
0857363470 | ||
|
|
713199d7be | ||
|
|
b941f93416 | ||
|
|
c22b7d45c1 | ||
|
|
c78bb45f2f | ||
|
|
11f9365eb0 | ||
|
|
cdc1656f3a | ||
|
|
8fb3ec73ff | ||
|
|
214e806a33 | ||
|
|
8e0d6d11b9 | ||
|
|
9412b58d80 | ||
|
|
dad97fc2d9 | ||
|
|
ff33cc4dad | ||
|
|
51e0e5e66d | ||
|
|
5fa6ecf9ae | ||
|
|
34c7f4e6b7 | ||
|
|
737559d3f8 | ||
|
|
f801372074 | ||
|
|
ea2dce0ab4 | ||
|
|
314c6f94f0 | ||
|
|
8fd119e546 | ||
|
|
8612476103 | ||
|
|
35812bd7ba | ||
|
|
f1ef2e8138 | ||
|
|
c47cb512ab | ||
|
|
fe7d4230d8 | ||
|
|
b6e166f44d | ||
|
|
ab2d223e71 | ||
|
|
c6c7baa3b4 | ||
|
|
218f293cd4 | ||
|
|
4d641d193c | ||
|
|
67ff78f1ca | ||
|
|
4f0716ab13 | ||
|
|
07c59e6a6c | ||
|
|
86a674b87e | ||
|
|
9957ab259c | ||
|
|
4c3944f626 | ||
|
|
6f9b466d7c | ||
|
|
61badb6af7 | ||
|
|
e31e57c7e7 | ||
|
|
469188044d | ||
|
|
38063e5602 | ||
|
|
ba5e2b7fa6 | ||
|
|
eed806962d | ||
|
|
a1ce5e15ce | ||
|
|
f0735c945a | ||
|
|
d84e131b73 | ||
|
|
ad1511e69c | ||
|
|
db4dc114f2 | ||
|
|
a7f363ea43 | ||
|
|
0ccbba61ce | ||
|
|
d921a4e168 | ||
|
|
819a85ee06 | ||
|
|
a3d15fe16c | ||
|
|
56d25cdeeb | ||
|
|
d027a15b4a | ||
|
|
a16316d7b4 | ||
|
|
14d8e93004 | ||
|
|
37c62bf9b7 | ||
|
|
72839d7654 | ||
|
|
1994cba73e | ||
|
|
55cdcbb39b | ||
|
|
c7ce6230db | ||
|
|
7c45ae24ae | ||
|
|
99c350fc43 | ||
|
|
faf1c5cb64 | ||
|
|
ece6efc75e | ||
|
|
0a809ab5f0 | ||
|
|
78ff1a4af5 | ||
|
|
c0dcae4f1d | ||
|
|
5e32c8a828 | ||
|
|
4d15a2f45e | ||
|
|
a913f21c45 | ||
|
|
322c70433b | ||
|
|
ad9cfcdcc7 | ||
|
|
4232c9d2b0 | ||
|
|
9edd59d280 | ||
|
|
df74cbf06f | ||
|
|
5af224ab16 | ||
|
|
7b734df09e | ||
|
|
1340a47bc2 | ||
|
|
4f4476ba75 | ||
|
|
5c7a183f8a | ||
|
|
53bada2a1e | ||
|
|
43efb7df2f | ||
|
|
f5cea221a6 | ||
|
|
b7082f34a1 | ||
|
|
d20d957881 | ||
|
|
008274cda5 | ||
|
|
e07ab7547f | ||
|
|
cf10837eb8 | ||
|
|
291b26f230 | ||
|
|
08e8c9bf57 | ||
|
|
625152440c | ||
|
|
fbd51821d1 | ||
|
|
c3ebf51295 | ||
|
|
9a9ff7f32c | ||
|
|
7b73eec82b | ||
|
|
75ba4a1cdb | ||
|
|
e5ca9065bd | ||
|
|
1042d0825f | ||
|
|
17942925f5 | ||
|
|
7424317d03 | ||
|
|
dbf1d91961 | ||
|
|
eb1644b302 | ||
|
|
cde5bc3263 | ||
|
|
e995e289db | ||
|
|
5020caa9c3 | ||
|
|
f34eb7d9f3 | ||
|
|
bf74ef0e5e | ||
|
|
0ff52311c3 | ||
|
|
e453e6f9ca | ||
|
|
6078598aff | ||
|
|
9fdb3b3b4a | ||
|
|
1362aa655f | ||
|
|
9c22ab8925 | ||
|
|
ca2dbb2f4b | ||
|
|
16bbc5a026 | ||
|
|
627f10cd18 | ||
|
|
13509b9231 | ||
|
|
ca88afbf5b | ||
|
|
42c9c9894b | ||
|
|
b4de62cfc2 | ||
|
|
226fbc191b | ||
|
|
4839d8861d | ||
|
|
789b47d565 | ||
|
|
c13cdcdd36 | ||
|
|
4ae3d0150f | ||
|
|
7d153a162a | ||
|
|
7bc3e94ff3 | ||
|
|
9c1fb26660 | ||
|
|
3dd725a0f0 | ||
|
|
a8aad53038 | ||
|
|
68c60f58c0 | ||
|
|
c9c3f33acc | ||
|
|
5ffc5187eb | ||
|
|
5d31cdfa16 | ||
|
|
e8ff4af5b9 | ||
|
|
723e1dd9a6 | ||
|
|
369a4558a3 | ||
|
|
5e6a3d9d8e | ||
|
|
62dca4f1c5 | ||
|
|
061c02306f | ||
|
|
f006b05010 | ||
|
|
c5a727aa9b | ||
|
|
e67e9d3bbf | ||
|
|
ea86c9d37a | ||
|
|
d1a2e6e5bd | ||
|
|
c96985f1db | ||
|
|
b7010c83e0 | ||
|
|
20a3d19ac7 | ||
|
|
0ded93ab9b | ||
|
|
ec82ec0426 | ||
|
|
6281f8ff89 | ||
|
|
2c9d2d4fd7 | ||
|
|
8569239bc1 | ||
|
|
5463c398cb | ||
|
|
7381734913 | ||
|
|
462322026f | ||
|
|
b5e5c719ed | ||
|
|
a0f4976b07 | ||
|
|
44c2486a74 | ||
|
|
5fc6672784 | ||
|
|
bcb1edba90 | ||
|
|
8099e7a75d | ||
|
|
cc9a501351 | ||
|
|
b536a23124 | ||
|
|
6ca5bcc6b8 | ||
|
|
ac966f118a | ||
|
|
f13472a8c3 | ||
|
|
0e213ae777 | ||
|
|
a0c82a6a47 | ||
|
|
a087ba608b | ||
|
|
9112d63655 | ||
|
|
3f7f7a0aa7 | ||
|
|
8d03aafe72 | ||
|
|
b0c0fd7dc8 | ||
|
|
c273f26cb3 | ||
|
|
60101830cc | ||
|
|
a58d782704 | ||
|
|
9b94c1dda9 | ||
|
|
201a6b350e | ||
|
|
b2724caeda | ||
|
|
9d99d556a1 | ||
|
|
a1a6511e26 | ||
|
|
652456646f | ||
|
|
ca0dc49f64 | ||
|
|
ae1b39eb60 | ||
|
|
22f7faf60e | ||
|
|
f3bf9eb14d | ||
|
|
347a1a48d4 | ||
|
|
c9793457f3 | ||
|
|
50d31ba398 | ||
|
|
2788c4cc00 | ||
|
|
ba54232b8d | ||
|
|
489bb70901 | ||
|
|
dd06dfa5ba | ||
|
|
f39e339726 | ||
|
|
295b374b48 | ||
|
|
8ed390c394 | ||
|
|
f9e9cac6e8 | ||
|
|
f3304acc93 | ||
|
|
a233771433 | ||
|
|
ea9a871d90 | ||
|
|
84d010bb2f | ||
|
|
e0ba468b7e | ||
|
|
f88c0dd645 | ||
|
|
758918c077 | ||
|
|
7b516e6113 | ||
|
|
61a76bb834 | ||
|
|
e6b6edefaf | ||
|
|
a228eb020d | ||
|
|
c46933a81a | ||
|
|
746046c8c0 | ||
|
|
acab95792f | ||
|
|
b882265e52 | ||
|
|
547f2ef189 | ||
|
|
69004943a7 | ||
|
|
658fb4b1f9 | ||
|
|
e675b0c00a | ||
|
|
95ac29559c | ||
|
|
b7a095b4b4 | ||
|
|
08d1b29301 | ||
|
|
741707273d | ||
|
|
bdd6ed0e83 | ||
|
|
8154e275fa | ||
|
|
6258cba79a | ||
|
|
213831289a | ||
|
|
ba34b45651 | ||
|
|
4be5c74798 | ||
|
|
b06a611e69 | ||
|
|
fd5b8d1374 | ||
|
|
4d84811629 | ||
|
|
cd4fd02ee3 | ||
|
|
475972aecd | ||
|
|
b7a23e558a | ||
|
|
827440686f | ||
|
|
7f70145f55 | ||
|
|
0639659270 | ||
|
|
4be08ee0bd | ||
|
|
cc6a24cf80 | ||
|
|
b187d5a3fc | ||
|
|
83f8d24892 | ||
|
|
fd1898b70a | ||
|
|
8ce63106b9 | ||
|
|
c0c571ffe0 | ||
|
|
d8f4da4d90 | ||
|
|
f02387073d | ||
|
|
d344cc6020 | ||
|
|
9100638400 | ||
|
|
d2085654a7 | ||
|
|
78d5939d9d | ||
|
|
c013006671 | ||
|
|
e09a9f0d80 | ||
|
|
705856d51a | ||
|
|
4445b6c054 | ||
|
|
cf03bc0a0a | ||
|
|
f37f1769ae | ||
|
|
31cd839063 | ||
|
|
9f6a4eb300 | ||
|
|
6a2957faf7 | ||
|
|
e6c9cfed42 | ||
|
|
0da7f1b4de | ||
|
|
8ce171877e | ||
|
|
a38c7f6e49 | ||
|
|
d19f628c73 | ||
|
|
526d15b804 | ||
|
|
c308397ed6 | ||
|
|
0cf93a8c32 | ||
|
|
cba25710fe | ||
|
|
1b471283c9 | ||
|
|
09fee2aa4b | ||
|
|
2739c86881 | ||
|
|
cdf67684cc | ||
|
|
4e5ba327fe | ||
|
|
efeb9e627e | ||
|
|
8e24f15d58 | ||
|
|
1adad860f4 | ||
|
|
cf07570871 | ||
|
|
bf30e52993 | ||
|
|
a91ddd40dd | ||
|
|
57ecadd23e | ||
|
|
967f14148f | ||
|
|
7a6544b17c | ||
|
|
bf59efd3f4 | ||
|
|
c1266e9d06 | ||
|
|
557adb6ed9 | ||
|
|
9f10996ac7 | ||
|
|
31f48829b0 | ||
|
|
39d4a97e24 | ||
|
|
a665978b64 | ||
|
|
0cc62058a9 | ||
|
|
7dfdb9d061 | ||
|
|
e3ea1d5049 | ||
|
|
19a96acac8 | ||
|
|
80d016e076 | ||
|
|
ac214b97df | ||
|
|
1a948e4341 | ||
|
|
6bbc77a69c | ||
|
|
217151be8d | ||
|
|
2ef1465244 | ||
|
|
cfc1d0d8f8 | ||
|
|
6fabecac1a | ||
|
|
77ac864995 | ||
|
|
f95769541d | ||
|
|
fa4ca7944f | ||
|
|
02e9bfaf31 | ||
|
|
7f66d5992f | ||
|
|
84e10cf635 | ||
|
|
1b8f8c5f5a | ||
|
|
131a783e89 | ||
|
|
2233f24c8f | ||
|
|
607a625641 | ||
|
|
9b49d19224 | ||
|
|
46ae4353e0 | ||
|
|
44fcba2e3a | ||
|
|
f434795ee5 | ||
|
|
0ea423d022 | ||
|
|
c9b9de2838 | ||
|
|
5918d5120f | ||
|
|
0d73f2fffa | ||
|
|
c8018c5543 | ||
|
|
071bd0ebf2 | ||
|
|
a00fce516e | ||
|
|
94abb3bd0c | ||
|
|
2e94ea1a0d | ||
|
|
3b009e0b50 | ||
|
|
cc130e04dd | ||
|
|
2eca868540 | ||
|
|
eeba5f020a | ||
|
|
ded8bce8e6 | ||
|
|
8e7efaae99 | ||
|
|
fe9e434020 | ||
|
|
8a0b0f5cf5 | ||
|
|
5df321816b | ||
|
|
7ff645c423 | ||
|
|
93f290d57c | ||
|
|
2146a76795 | ||
|
|
b071e24d7e | ||
|
|
a99e4b602a | ||
|
|
4cdea865f0 | ||
|
|
bf9ae2f0d3 | ||
|
|
da2f958614 | ||
|
|
b47201fe61 | ||
|
|
a8932910d9 | ||
|
|
413877b59d | ||
|
|
da47588502 | ||
|
|
6f7ffbeb3c | ||
|
|
a258f2084f | ||
|
|
d067a31ce6 | ||
|
|
c6e4eee789 | ||
|
|
07c49f832e | ||
|
|
01b087238a | ||
|
|
d4832812ef | ||
|
|
9c098fd79f | ||
|
|
029d5e9068 | ||
|
|
f30486adf9 | ||
|
|
e21c337d3d | ||
|
|
f66f324e0e | ||
|
|
885acdac24 | ||
|
|
ccb04c317c | ||
|
|
abbedeec4f | ||
|
|
cdae3ec936 | ||
|
|
d727696830 | ||
|
|
2a9078a3b4 | ||
|
|
ac479c7e40 | ||
|
|
cda6ffea1e | ||
|
|
0900f401c0 | ||
|
|
d5a96ed637 | ||
|
|
8d9eff5297 | ||
|
|
19c1511901 | ||
|
|
8e37e54645 | ||
|
|
c25645eab1 | ||
|
|
b9f7711f21 | ||
|
|
9447913de6 | ||
|
|
8a9834cac0 | ||
|
|
2fec1c83a4 | ||
|
|
ff36380ddf | ||
|
|
f2cfa61a39 | ||
|
|
41156cc45d | ||
|
|
9f51bfcc10 | ||
|
|
9426e813be | ||
|
|
20fd7aba9b | ||
|
|
3bada4560e | ||
|
|
9719abe99a | ||
|
|
5540519e52 | ||
|
|
0b94e73c0b | ||
|
|
5e2a06bfd6 | ||
|
|
ac95e411bc | ||
|
|
08219fc57e | ||
|
|
09643f3c8b | ||
|
|
08a575fa82 | ||
|
|
62887a6ce8 | ||
|
|
297a6c9348 | ||
|
|
1181edc48e | ||
|
|
81f1930349 | ||
|
|
54a1819d46 | ||
|
|
1b5472cc94 | ||
|
|
06a083bafc | ||
|
|
9bb45cb6a3 | ||
|
|
1bfe9c568e | ||
|
|
4f5e5fcdba | ||
|
|
bca44d1c14 | ||
|
|
e6d1d66c8f | ||
|
|
c3a5f5c0f0 | ||
|
|
c0875d7987 | ||
|
|
6b6ac2bcb3 | ||
|
|
f78466452a | ||
|
|
f51230e793 | ||
|
|
3ab201ce10 | ||
|
|
45a94cfe86 | ||
|
|
f16cc999bd | ||
|
|
d39f25efd3 | ||
|
|
6b2ce6de6f | ||
|
|
3f1738e0fe | ||
|
|
3ceaf2ab81 | ||
|
|
4c7d61ae82 | ||
|
|
d53dd57b84 | ||
|
|
6ff5ac5592 | ||
|
|
9ce1bbe3cd | ||
|
|
65c48419d0 | ||
|
|
d376ee6671 | ||
|
|
eaa419a48d | ||
|
|
1b6ec34c61 | ||
|
|
9f4da339c3 | ||
|
|
1b54a99f6a | ||
|
|
2b0d2892af | ||
|
|
27f2d201ed | ||
|
|
d6cf622e49 | ||
|
|
2092e2167e | ||
|
|
503032cbcf | ||
|
|
6f90e13502 | ||
|
|
b22bd459dd | ||
|
|
f67147a405 | ||
|
|
df4eb3d0f0 | ||
|
|
32cc4bcd03 | ||
|
|
1502c6d660 | ||
|
|
d4fb5b747b | ||
|
|
7581651011 | ||
|
|
4f54e273ab | ||
|
|
1cc1b83b6f | ||
|
|
8e9199f453 | ||
|
|
223501eeca | ||
|
|
7649738618 | ||
|
|
78c60ae695 | ||
|
|
f5f122d5a5 | ||
|
|
4b27a33a10 | ||
|
|
15aca2eb84 | ||
|
|
1cb91c0ac9 | ||
|
|
840b3b3161 | ||
|
|
a5cf3ce2f1 | ||
|
|
04c85eb9ce | ||
|
|
82364d2e3c | ||
|
|
3db7bf5dfd | ||
|
|
de13023981 | ||
|
|
35b825c78a | ||
|
|
28428fcf30 | ||
|
|
1e570bf506 | ||
|
|
ad0e97ab7b | ||
|
|
ed5d338a39 | ||
|
|
e795745e43 | ||
|
|
5e4d790fc3 | ||
|
|
7e47f4ed08 | ||
|
|
63fc77a861 | ||
|
|
59ff94c005 | ||
|
|
bbc199cda3 | ||
|
|
df91ee3d2a | ||
|
|
528fecef4d | ||
|
|
d39f1a6af0 | ||
|
|
4367ef11fa | ||
|
|
b8d77d2490 | ||
|
|
2600c3116c | ||
|
|
2506c2579b | ||
|
|
fe7392a656 | ||
|
|
8b7a3f1835 | ||
|
|
e6acc56c1f | ||
|
|
d635cfb90f | ||
|
|
6cb6113c57 | ||
|
|
6aea0f12cd | ||
|
|
99ace59b45 | ||
|
|
31f9ed3e6b | ||
|
|
573c0881e9 | ||
|
|
7134b3ba35 | ||
|
|
bc267e22bd | ||
|
|
3d83316b03 | ||
|
|
dfe80eb050 |
25
.editorconfig
Normal file
25
.editorconfig
Normal file
@@ -0,0 +1,25 @@
|
||||
# EditorConfig is awesome: https://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
|
||||
[CMakeLists.txt]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.{c,cc,h}]
|
||||
indent_style = tab
|
||||
|
||||
[*.{c3}]
|
||||
indent_style = tab
|
||||
|
||||
[*.{json,toml,yml,gyp}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.{py,pyi}]
|
||||
indent_style = tab
|
||||
|
||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -1,4 +1,2 @@
|
||||
$ cat .gitattributes
|
||||
* text=auto
|
||||
*.c3 linguist-language=C
|
||||
*.c3t linguist-language=C
|
||||
243
.github/workflows/main.yml
vendored
243
.github/workflows/main.yml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
|
||||
env:
|
||||
LLVM_RELEASE_VERSION_WINDOWS: 18
|
||||
LLVM_RELEASE_VERSION_MAC: 18
|
||||
LLVM_RELEASE_VERSION_MAC: 17
|
||||
LLVM_RELEASE_VERSION_LINUX: 17
|
||||
LLVM_RELEASE_VERSION_UBUNTU20: 17
|
||||
LLVM_DEV_VERSION: 20
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
cd resources/testproject
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe --debug-log --emit-llvm run hello_world_win32
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe -vvv --emit-llvm run hello_world_win32
|
||||
dir build\llvm_ir
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe clean
|
||||
dir build\llvm_ir
|
||||
@@ -61,19 +61,31 @@ jobs:
|
||||
run: |
|
||||
cd resources/testproject
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe --debug-log build hello_world_win32_lib
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe -vvv build hello_world_win32_lib
|
||||
|
||||
- name: Compile and run dynlib-test
|
||||
run: |
|
||||
cd resources/examples/dynlib-test
|
||||
..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv dynamic-lib add.c3
|
||||
..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv compile-run test.c3 -l ./add.lib
|
||||
|
||||
- name: Compile and run staticlib-test
|
||||
run: |
|
||||
cd resources/examples/staticlib-test
|
||||
..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv static-lib add.c3
|
||||
..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv compile-run test.c3 -l ./add.lib
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib
|
||||
build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib5
|
||||
|
||||
- name: Try raylib
|
||||
- name: Try raylib5
|
||||
run: |
|
||||
cd resources
|
||||
..\build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --print-linking --wincrt=none examples\raylib\raylib_arkanoid.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --print-linking --wincrt=none examples\raylib\raylib_snake.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --print-linking --wincrt=none examples\raylib\raylib_tetris.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib5
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib5 --print-linking examples\raylib\raylib_arkanoid.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib5 --print-linking examples\raylib\raylib_snake.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib5 --print-linking examples\raylib\raylib_tetris.c3
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
@@ -91,7 +103,7 @@ jobs:
|
||||
dir msvc_sdk
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: c3-windows-${{ matrix.build_type }}
|
||||
path: |
|
||||
@@ -120,8 +132,8 @@ jobs:
|
||||
install: git binutils mingw-w64-x86_64-clang mingw-w64-x86_64-ninja mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-python
|
||||
- shell: msys2 {0}
|
||||
run: |
|
||||
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-llvm-18.1.8-1-any.pkg.tar.zst
|
||||
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-18.1.8-1-any.pkg.tar.zst
|
||||
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-llvm-19.1.6-1-any.pkg.tar.zst
|
||||
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-19.1.6-1-any.pkg.tar.zst
|
||||
- name: CMake
|
||||
run: |
|
||||
cmake -B build -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
@@ -141,16 +153,16 @@ jobs:
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log
|
||||
../../build/c3c run -vvv
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
./build/c3c vendor-fetch raylib
|
||||
./build/c3c vendor-fetch raylib5
|
||||
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build hello_world_lib --cc cc --debug-log
|
||||
../../build/c3c build hello_world_lib --cc cc -vvv
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
@@ -197,12 +209,12 @@ jobs:
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log
|
||||
../../build/c3c run -vvv
|
||||
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build hello_world_lib --debug-log
|
||||
../../build/c3c build hello_world_lib -vvv
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
@@ -311,6 +323,24 @@ jobs:
|
||||
../build/c3c compile-run linux_stack.c3
|
||||
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
|
||||
|
||||
- name: Compile and run dynlib-test
|
||||
run: |
|
||||
cd resources/examples/dynlib-test
|
||||
../../../build/c3c -vv dynamic-lib add.c3
|
||||
mv add.so libadd.so
|
||||
cc test.c -L. -ladd -Wl,-rpath=.
|
||||
./a.out
|
||||
../../../build/c3c compile-run test.c3 -L . -l add -z -Wl,-rpath=.
|
||||
|
||||
- name: Compile and run staticlib-test
|
||||
run: |
|
||||
cd resources/examples/staticlib-test
|
||||
../../../build/c3c -vv static-lib add.c3
|
||||
mv add.a libadd.a
|
||||
cc test.c -L. -ladd
|
||||
./a.out
|
||||
../../../build/c3c compile-run test.c3 -L . -l add
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
@@ -319,12 +349,12 @@ jobs:
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log
|
||||
../../build/c3c run -vvv
|
||||
|
||||
- name: Test WASM
|
||||
run: |
|
||||
cd resources/testfragments
|
||||
../../build/c3c compile --reloc=none --target wasm32 -g0 --link-libc=no --no-entry -Os wasm4.c3
|
||||
../../build/c3c compile --target wasm32 -g0 --no-entry -Os wasm4.c3
|
||||
|
||||
- name: Install QEMU and Risc-V toolchain
|
||||
run: |
|
||||
@@ -338,7 +368,7 @@ jobs:
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log --linker=builtin
|
||||
../../build/c3c run -vvv --linker=builtin
|
||||
|
||||
- name: Init a library & a project
|
||||
run: |
|
||||
@@ -363,7 +393,7 @@ jobs:
|
||||
|
||||
- name: upload artifacts
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_LINUX
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: c3-linux-${{matrix.build_type}}
|
||||
path: c3-linux-${{matrix.build_type}}.tar.gz
|
||||
@@ -375,7 +405,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Release, Debug]
|
||||
llvm_version: [17, 18, 19]
|
||||
llvm_version: [17, 18, 19, 20]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install common deps
|
||||
@@ -461,12 +491,12 @@ jobs:
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log
|
||||
../../build/c3c run -vvv
|
||||
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log --linker=builtin
|
||||
../../build/c3c run -vvv --linker=builtin
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
@@ -484,7 +514,7 @@ jobs:
|
||||
|
||||
- name: upload artifacts
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU20
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: c3-ubuntu-20-${{matrix.build_type}}
|
||||
path: c3-ubuntu-20-${{matrix.build_type}}.tar.gz
|
||||
@@ -553,17 +583,17 @@ jobs:
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log
|
||||
../../build/c3c run -vvv
|
||||
|
||||
- name: Test WASM
|
||||
run: |
|
||||
cd resources/testfragments
|
||||
../../build/c3c compile --reloc=none --target wasm32 -g0 --link-libc=no --no-entry -Os wasm4.c3
|
||||
../../build/c3c compile --reloc=none --target wasm32 -g0 --no-entry -Os wasm4.c3
|
||||
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log --linker=builtin
|
||||
../../build/c3c run -vvv --linker=builtin
|
||||
|
||||
- name: Init a library & a project
|
||||
run: |
|
||||
@@ -607,7 +637,7 @@ jobs:
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
./build/c3c vendor-fetch raylib
|
||||
./build/c3c vendor-fetch raylib5
|
||||
|
||||
- name: Compile and run some examples
|
||||
run: |
|
||||
@@ -621,25 +651,36 @@ jobs:
|
||||
../build/c3c compile-run -O5 examples/load_world.c3
|
||||
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
|
||||
|
||||
- name: Compile and run dynlib-test
|
||||
run: |
|
||||
cd resources/examples/dynlib-test
|
||||
../../../build/c3c -vv dynamic-lib add.c3
|
||||
../../../build/c3c compile-run test.c3 -l ./add.dylib
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
../build/c3c compile-test unit
|
||||
|
||||
- name: Test WASM
|
||||
run: |
|
||||
cd resources/testfragments
|
||||
../../build/c3c compile --target wasm32 -g0 --no-entry -Os wasm4.c3
|
||||
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log
|
||||
../../build/c3c run -vvv
|
||||
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log --linker=builtin
|
||||
../../build/c3c run -vvv --linker=builtin
|
||||
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build hello_world_lib --debug-log
|
||||
../../build/c3c build hello_world_lib -vvv
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
@@ -657,15 +698,45 @@ jobs:
|
||||
|
||||
- name: upload artifacts
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_MAC
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: c3-macos-${{matrix.build_type}}
|
||||
path: c3-macos-${{matrix.build_type}}.zip
|
||||
|
||||
build-nix:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
# Don't abort runners if a single one fails
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [ Release, Debug ]
|
||||
nixpkgs: [ Lock, Latest ]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
github_access_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Update flake (if necessary)
|
||||
run: |
|
||||
if [[ matrix.nixpkgs == "Latest" ]]; then
|
||||
nix flake update
|
||||
fi
|
||||
nix flake info
|
||||
|
||||
- name: Build and check
|
||||
run: |
|
||||
if [[ ${{ matrix.build_type }} = "Debug" ]]; then
|
||||
nix build -L ".#c3c-debug-checks"
|
||||
else
|
||||
nix build -L ".#c3c-checks"
|
||||
fi
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-msvc, build-linux, build-mac]
|
||||
needs: [build-msvc, build-linux, build-mac, build-linux-ubuntu20]
|
||||
if: github.ref == 'refs/heads/master'
|
||||
|
||||
steps:
|
||||
@@ -692,16 +763,22 @@ jobs:
|
||||
sha: context.sha
|
||||
})
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
- run: cp -r lib c3-windows-Release
|
||||
- run: cp -r lib c3-windows-Debug
|
||||
- run: cp msvc_build_libraries.py c3-windows-Release
|
||||
- run: cp msvc_build_libraries.py c3-windows-Debug
|
||||
- run: zip -r c3-windows-Release.zip c3-windows-Release
|
||||
- run: zip -r c3-windows-Debug.zip c3-windows-Debug
|
||||
- run: zip -r c3-windows.zip c3-windows-Release
|
||||
- run: zip -r c3-windows-debug.zip c3-windows-Debug
|
||||
- run: mv c3-linux-Release/c3-linux-Release.tar.gz c3-linux-Release/c3-linux.tar.gz
|
||||
- run: mv c3-linux-Debug/c3-linux-Debug.tar.gz c3-linux-Debug/c3-linux-debug.tar.gz
|
||||
- run: mv c3-ubuntu-20-Release/c3-ubuntu-20-Release.tar.gz c3-ubuntu-20-Release/c3-ubuntu-20.tar.gz
|
||||
- run: mv c3-ubuntu-20-Debug/c3-ubuntu-20-Debug.tar.gz c3-ubuntu-20-Debug/c3-ubuntu-20-debug.tar.gz
|
||||
- run: mv c3-macos-Release/c3-macos-Release.zip c3-macos-Release/c3-macos.zip
|
||||
- run: mv c3-macos-Debug/c3-macos-Debug.zip c3-macos-Debug/c3-macos-debug.zip
|
||||
|
||||
- id: create_release
|
||||
uses: actions/create-release@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
@@ -709,84 +786,12 @@ jobs:
|
||||
release_name: latest
|
||||
draft: false
|
||||
prerelease: true
|
||||
|
||||
- name: upload windows
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: c3-windows-Release.zip
|
||||
asset_name: c3-windows.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: upload windows debug
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: c3-windows-Debug.zip
|
||||
asset_name: c3-windows-debug.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: upload linux
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: c3-linux-Release/c3-linux-Release.tar.gz
|
||||
asset_name: c3-linux.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
|
||||
- name: upload linux debug
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: c3-linux-Debug/c3-linux-Debug.tar.gz
|
||||
asset_name: c3-linux-debug.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
|
||||
- name: upload ubuntu 20
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
|
||||
asset_path: c3-ubuntu-20-Release/c3-ubuntu-20-Release.tar.gz
|
||||
asset_name: c3-ubuntu-20.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
|
||||
- name: upload ubuntu 20 debug
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: c3-ubuntu-20-Debug/c3-ubuntu-20-Debug.tar.gz
|
||||
asset_name: c3-ubuntu-20-debug.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
|
||||
- name: upload macos
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: c3-macos-Release/c3-macos-Release.zip
|
||||
asset_name: c3-macos.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: upload macos debug
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: c3-macos-Debug/c3-macos-Debug.zip
|
||||
asset_name: c3-macos-debug.zip
|
||||
asset_content_type: application/zip
|
||||
files: |
|
||||
c3-windows.zip
|
||||
c3-windows-debug.zip
|
||||
c3-linux-Release/c3-linux.tar.gz
|
||||
c3-linux-Debug/c3-linux-debug.tar.gz
|
||||
c3-ubuntu-20-Release/c3-ubuntu-20.tar.gz
|
||||
c3-ubuntu-20-Debug/c3-ubuntu-20-debug.tar.gz
|
||||
c3-macos-Release/c3-macos.zip
|
||||
c3-macos-Debug/c3-macos-debug.zip
|
||||
|
||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -19,6 +19,7 @@
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.tlb
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
@@ -70,3 +71,13 @@ out/
|
||||
|
||||
# Emacs files
|
||||
TAGS
|
||||
|
||||
# Clangd LSP files
|
||||
/.cache/
|
||||
/compile_commands.json
|
||||
|
||||
# 'nix build' resulting symlink
|
||||
result
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
447
CMakeLists.txt
447
CMakeLists.txt
@@ -38,10 +38,10 @@ set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
if(MSVC)
|
||||
message(STATUS "MSVC version ${MSVC_VERSION}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2 /EHsc")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /O2 /EHsc")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi /EHa")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Od /Zi /EHa")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2 /EHsc /utf-8")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /O2 /EHsc /utf-8")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi /EHa /utf-8")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Od /Zi /EHa /utf-8")
|
||||
else()
|
||||
if (true)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -fno-exceptions")
|
||||
@@ -63,6 +63,8 @@ set(C3_LLVM_VERSION "auto" CACHE STRING "Use LLVM version [default: auto]")
|
||||
option(C3_USE_MIMALLOC "Use built-in mimalloc" OFF)
|
||||
option(C3_USE_TB "Use TB" OFF)
|
||||
set(C3_MIMALLOC_TAG "v1.7.3" CACHE STRING "Used version of mimalloc")
|
||||
option(C3_WITH_LLVM "Build with LLVM" ON)
|
||||
option(C3_LLD_DIR "Use custom LLD directory" "")
|
||||
|
||||
set(C3_USE_MIMALLOC OFF)
|
||||
if(C3_USE_MIMALLOC)
|
||||
@@ -71,18 +73,20 @@ if(C3_USE_MIMALLOC)
|
||||
option(MI_PADDING OFF)
|
||||
option(MI_DEBUG_FULL OFF)
|
||||
FetchContent_Declare(
|
||||
mimalloc
|
||||
GIT_REPOSITORY https://github.com/microsoft/mimalloc.git
|
||||
GIT_TAG ${C3_MIMALLOC_TAG}
|
||||
mimalloc
|
||||
GIT_REPOSITORY https://github.com/microsoft/mimalloc.git
|
||||
GIT_TAG ${C3_MIMALLOC_TAG}
|
||||
)
|
||||
FetchContent_MakeAvailable(mimalloc)
|
||||
endif()
|
||||
if (NOT WIN32)
|
||||
find_package(CURL)
|
||||
endif()
|
||||
if (NOT C3_LLVM_VERSION STREQUAL "auto")
|
||||
if (${C3_LLVM_VERSION} VERSION_LESS 17 OR ${C3_LLVM_VERSION} VERSION_GREATER 20)
|
||||
message(FATAL_ERROR "LLVM ${C3_LLVM_VERSION} is not supported!")
|
||||
if(C3_WITH_LLVM)
|
||||
if (NOT C3_LLVM_VERSION STREQUAL "auto")
|
||||
if (${C3_LLVM_VERSION} VERSION_LESS 17 OR ${C3_LLVM_VERSION} VERSION_GREATER 20)
|
||||
message(FATAL_ERROR "LLVM ${C3_LLVM_VERSION} is not supported!")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -101,126 +105,142 @@ if(C3_USE_TB AND GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
# Get git hash
|
||||
execute_process(COMMAND git rev-parse HEAD
|
||||
OUTPUT_VARIABLE GIT_HASH
|
||||
RESULT_VARIABLE GIT_REVPARSE_RESULT
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(GIT_REVPARSE_RESULT EQUAL "0")
|
||||
add_compile_definitions(GIT_HASH="${GIT_HASH}")
|
||||
else()
|
||||
message(WARNING "Cannot to get Git Hash: git rev-parse failed with ${GIT_REVPARSE_RESULT}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||
if (C3_LLVM_VERSION STREQUAL "auto")
|
||||
set(C3_LLVM_VERSION "18")
|
||||
endif()
|
||||
FetchContent_Declare(
|
||||
LLVM_Windows
|
||||
URL https://github.com/c3lang/win-llvm/releases/download/llvm_18_1_8_with_rt/llvm-18.1.8-windows-amd64-msvc17-libcmt.7z
|
||||
# Clangd LSP support
|
||||
option(C3_ENABLE_CLANGD_LSP "Enable/Disable output of compile commands during generation." OFF)
|
||||
if(C3_ENABLE_CLANGD_LSP)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink
|
||||
${CMAKE_BINARY_DIR}/compile_commands.json
|
||||
${CMAKE_SOURCE_DIR}/compile_commands.json
|
||||
)
|
||||
FetchContent_Declare(
|
||||
LLVM_Windows_debug
|
||||
URL https://github.com/c3lang/win-llvm/releases/download/llvm_18_1_8_with_rt/llvm-18.1.8-windows-amd64-msvc17-libcmt-dbg.7z
|
||||
)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
message("Loading Windows LLVM debug libraries, this may take a while...")
|
||||
FetchContent_MakeAvailable(LLVM_Windows_debug)
|
||||
set(llvm_dir ${llvm_windows_debug_SOURCE_DIR})
|
||||
else()
|
||||
message("Loading Windows LLVM libraries, this may take a while...")
|
||||
FetchContent_MakeAvailable(LLVM_Windows)
|
||||
set(llvm_dir ${llvm_windows_SOURCE_DIR})
|
||||
endif()
|
||||
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_dir} ${CMAKE_SYSTEM_PREFIX_PATH})
|
||||
endif(C3_ENABLE_CLANGD_LSP)
|
||||
|
||||
if(C3_WITH_LLVM)
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||
if (C3_LLVM_VERSION STREQUAL "auto")
|
||||
set(C3_LLVM_VERSION "19")
|
||||
endif()
|
||||
FetchContent_Declare(
|
||||
LLVM_Windows
|
||||
URL https://github.com/c3lang/win-llvm/releases/download/llvm_19_1_5/llvm-19.1.5-windows-amd64-msvc17-libcmt.7z
|
||||
)
|
||||
FetchContent_Declare(
|
||||
LLVM_Windows_debug
|
||||
URL https://github.com/c3lang/win-llvm/releases/download/llvm_19_1_5/llvm-19.1.5-windows-amd64-msvc17-libcmt-dbg.7z
|
||||
)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
message("Loading Windows LLVM debug libraries, this may take a while...")
|
||||
FetchContent_MakeAvailable(LLVM_Windows_debug)
|
||||
set(llvm_dir ${llvm_windows_debug_SOURCE_DIR})
|
||||
else()
|
||||
message("Loading Windows LLVM libraries, this may take a while...")
|
||||
FetchContent_MakeAvailable(LLVM_Windows)
|
||||
set(llvm_dir ${llvm_windows_SOURCE_DIR})
|
||||
endif()
|
||||
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_dir} ${CMAKE_SYSTEM_PREFIX_PATH})
|
||||
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
find_package(LLD REQUIRED CONFIG)
|
||||
else()
|
||||
if (NOT C3_LLVM_VERSION STREQUAL "auto")
|
||||
find_package(LLVM ${C3_LLVM_VERSION} REQUIRED CONFIG)
|
||||
else()
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
find_package(LLD REQUIRED CONFIG)
|
||||
else()
|
||||
if (NOT C3_LLVM_VERSION STREQUAL "auto")
|
||||
find_package(LLVM ${C3_LLVM_VERSION} REQUIRED CONFIG)
|
||||
else()
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
|
||||
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
|
||||
message(STATUS "Libraries located in: ${LLVM_LIBRARY_DIRS}")
|
||||
if (EXISTS /usr/lib)
|
||||
# Some systems (such as Alpine Linux) seem to put some of the relevant
|
||||
# LLVM files in /usr/lib, but this doesn't seem to be included in the
|
||||
# value of LLVM_LIBRARY_DIRS.
|
||||
list(APPEND LLVM_LIBRARY_DIRS /usr/lib)
|
||||
endif()
|
||||
|
||||
if (NOT LLVM_PACKAGE_VERSION VERSION_GREATER_EQUAL 15.0)
|
||||
message(FATAL_ERROR "LLVM version 15.0 or later is required.")
|
||||
endif()
|
||||
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
|
||||
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
|
||||
message(STATUS "Libraries located in: ${LLVM_LIBRARY_DIRS}")
|
||||
|
||||
if(LLVM_ENABLE_RTTI)
|
||||
message(STATUS "LLVM was built with RTTI")
|
||||
else()
|
||||
message(STATUS "LLVM was not built with RTTI")
|
||||
endif()
|
||||
if (NOT LLVM_PACKAGE_VERSION VERSION_GREATER_EQUAL 15.0)
|
||||
message(FATAL_ERROR "LLVM version 15.0 or later is required.")
|
||||
endif()
|
||||
|
||||
string(REPLACE "." ";" VERSION_LIST ${LLVM_PACKAGE_VERSION})
|
||||
list(GET VERSION_LIST 0 LLVM_MAJOR_VERSION)
|
||||
if(LLVM_ENABLE_RTTI)
|
||||
message(STATUS "LLVM was built with RTTI")
|
||||
else()
|
||||
message(STATUS "LLVM was not built with RTTI")
|
||||
endif()
|
||||
|
||||
include_directories(${LLVM_INCLUDE_DIRS})
|
||||
link_directories(${LLVM_LIBRARY_DIRS})
|
||||
add_definitions(${LLVM_DEFINITIONS})
|
||||
string(REPLACE "." ";" VERSION_LIST ${LLVM_PACKAGE_VERSION})
|
||||
list(GET VERSION_LIST 0 LLVM_MAJOR_VERSION)
|
||||
|
||||
if(NOT C3_LINK_DYNAMIC)
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
AllTargetsAsmParsers
|
||||
AllTargetsCodeGens
|
||||
AllTargetsDescs
|
||||
AllTargetsDisassemblers
|
||||
AllTargetsInfos
|
||||
Analysis
|
||||
AsmPrinter
|
||||
BitReader
|
||||
Core
|
||||
DebugInfoPDB
|
||||
InstCombine
|
||||
IrReader
|
||||
LibDriver
|
||||
Linker
|
||||
LTO
|
||||
MC
|
||||
MCDisassembler
|
||||
native
|
||||
nativecodegen
|
||||
Object
|
||||
Option
|
||||
ScalarOpts
|
||||
Support
|
||||
Target
|
||||
TransformUtils
|
||||
WindowsManifest
|
||||
WindowsDriver
|
||||
)
|
||||
include_directories(${LLVM_INCLUDE_DIRS})
|
||||
link_directories(${LLVM_LIBRARY_DIRS})
|
||||
add_definitions(${LLVM_DEFINITIONS})
|
||||
|
||||
llvm_map_components_to_libnames(llvm_libs ${LLVM_LINK_COMPONENTS})
|
||||
if(NOT C3_LINK_DYNAMIC)
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
AllTargetsAsmParsers
|
||||
AllTargetsCodeGens
|
||||
AllTargetsDescs
|
||||
AllTargetsDisassemblers
|
||||
AllTargetsInfos
|
||||
Analysis
|
||||
AsmPrinter
|
||||
BitReader
|
||||
Core
|
||||
DebugInfoPDB
|
||||
InstCombine
|
||||
IrReader
|
||||
LibDriver
|
||||
Linker
|
||||
LTO
|
||||
MC
|
||||
MCDisassembler
|
||||
native
|
||||
nativecodegen
|
||||
Object
|
||||
Option
|
||||
ScalarOpts
|
||||
Support
|
||||
Target
|
||||
TransformUtils
|
||||
WindowsManifest
|
||||
WindowsDriver
|
||||
)
|
||||
|
||||
# These don't seem to be reliable on windows.
|
||||
message(STATUS "using find_library")
|
||||
find_library(LLD_COFF NAMES lldCOFF.lib lldCOFF.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_COMMON NAMES lldCommon.lib lldCommon.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_ELF NAMES lldELF.lib lldELF.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_MACHO NAMES lldMachO.lib lldMachO.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_MINGW NAMES lldMinGW.lib lldMinGW.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_WASM NAMES lldWasm.lib lldWasm.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
else()
|
||||
find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
set(llvm_libs ${LLVM})
|
||||
llvm_map_components_to_libnames(llvm_libs ${LLVM_LINK_COMPONENTS})
|
||||
|
||||
# These don't seem to be reliable on windows.
|
||||
message(STATUS "using find_library")
|
||||
find_library(LLD_COFF NAMES liblldCOFF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_COMMON NAMES liblldCommon.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_ELF NAMES liblldELF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_MACHO NAMES liblldMachO.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_MINGW NAMES liblldMinGW.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_WASM NAMES liblldWasm.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
if(NOT ${C3_LLD_DIR} EQUAL "" AND EXISTS ${C3_LLD_DIR})
|
||||
message("C3_LLD_DIR: " ${C3_LLD_DIR})
|
||||
set(LLVM_LIBRARY_DIRS
|
||||
"${LLVM_LIBRARY_DIRS}"
|
||||
"${C3_LLD_DIR}"
|
||||
)
|
||||
list(REMOVE_DUPLICATES LLVM_LIBRARY_DIRS)
|
||||
endif()
|
||||
|
||||
# These don't seem to be reliable on windows.
|
||||
message(STATUS "using find_library")
|
||||
find_library(LLD_COFF NAMES liblldCOFF.dylib lldCOFF.lib lldCOFF.a liblldCOFF.dll.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_COMMON NAMES liblldCommon.dylib lldCommon.lib lldCommon.a liblldCommon.dll.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_ELF NAMES liblldELF.dylib lldELF.lib lldELF.a liblldELF.dll.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_MACHO NAMES liblldMachO.dylib lldMachO.lib lldMachO.a liblldMachO.dll.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_MINGW NAMES liblldMinGW.dylib lldMinGW.lib lldMinGW.a liblldMinGW.dll.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_WASM NAMES liblldWasm.dylib lldWasm.lib lldWasm.a liblldWasm.dll.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
else()
|
||||
find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
set(llvm_libs ${LLVM})
|
||||
|
||||
# These don't seem to be reliable on windows.
|
||||
message(STATUS "using find_library")
|
||||
find_library(LLD_COFF NAMES liblldCOFF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_COMMON NAMES liblldCommon.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_ELF NAMES liblldELF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_MACHO NAMES liblldMachO.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_MINGW NAMES liblldMinGW.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_WASM NAMES liblldWasm.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT(${CMAKE_BINARY_DIR} EQUAL ${CMAKE_SOURCE_DIR}))
|
||||
@@ -228,36 +248,36 @@ if (NOT(${CMAKE_BINARY_DIR} EQUAL ${CMAKE_SOURCE_DIR}))
|
||||
file(COPY ${CMAKE_SOURCE_DIR}/lib DESTINATION ${CMAKE_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
find_library(LLD_LOONG NAMES libLLVMLoongArchCodeGen.lib libLLVMLoongArchAsmParser.lib libLLVMLoongArchCodeGen.a libLLVMLoongArchAsmParser.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
set(lld_libs
|
||||
${LLD_COFF}
|
||||
${LLD_COMMON}
|
||||
${LLD_WASM}
|
||||
${LLD_MINGW}
|
||||
${LLD_ELF}
|
||||
${LLD_MACHO}
|
||||
if(C3_WITH_LLVM)
|
||||
find_library(LLD_LOONG NAMES libLLVMLoongArchCodeGen.lib libLLVMLoongArchAsmParser.lib libLLVMLoongArchCodeGen.a libLLVMLoongArchAsmParser.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
set(lld_libs
|
||||
${LLD_COFF}
|
||||
${LLD_WASM}
|
||||
${LLD_MINGW}
|
||||
${LLD_ELF}
|
||||
${LLD_MACHO}
|
||||
${LLD_COMMON}
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
set(lld_libs ${lld_libs} xar)
|
||||
find_file(RT_ASAN_DYNAMIC NAMES libclang_rt.asan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIRS}/clang/${LLVM_MAJOR_VERSION}/lib/darwin")
|
||||
find_file(RT_TSAN_DYNAMIC NAMES libclang_rt.tsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIRS}/clang/${LLVM_MAJOR_VERSION}/lib/darwin")
|
||||
find_file(RT_UBSAN_DYNAMIC NAMES libclang_rt.ubsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIRS}/clang/${LLVM_MAJOR_VERSION}/lib/darwin")
|
||||
find_file(RT_LSAN_DYNAMIC NAMES libclang_rt.lsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIRS}/clang/${LLVM_MAJOR_VERSION}/lib/darwin")
|
||||
set(sanitizer_runtime_libraries
|
||||
${RT_ASAN_DYNAMIC}
|
||||
${RT_TSAN_DYNAMIC}
|
||||
# Unused
|
||||
# ${RT_UBSAN_DYNAMIC}
|
||||
# ${RT_LSAN_DYNAMIC}
|
||||
if (APPLE)
|
||||
set(lld_libs ${lld_libs} xar)
|
||||
find_file(RT_ASAN_DYNAMIC NAMES libclang_rt.asan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin")
|
||||
find_file(RT_TSAN_DYNAMIC NAMES libclang_rt.tsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin")
|
||||
find_file(RT_UBSAN_DYNAMIC NAMES libclang_rt.ubsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin")
|
||||
find_file(RT_LSAN_DYNAMIC NAMES libclang_rt.lsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin")
|
||||
set(sanitizer_runtime_libraries
|
||||
${RT_ASAN_DYNAMIC}
|
||||
${RT_TSAN_DYNAMIC}
|
||||
# Unused
|
||||
# ${RT_UBSAN_DYNAMIC}
|
||||
# ${RT_LSAN_DYNAMIC}
|
||||
)
|
||||
endif()
|
||||
|
||||
message(STATUS "linking to llvm libs ${lld_libs}")
|
||||
message(STATUS "Found lld libs ${lld_libs}")
|
||||
endif()
|
||||
|
||||
message(STATUS "linking to llvm libs ${lld_libs}")
|
||||
message(STATUS "Found lld libs ${lld_libs}")
|
||||
|
||||
|
||||
add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp)
|
||||
add_library(miniz STATIC dependencies/miniz/miniz.c)
|
||||
|
||||
add_executable(c3c
|
||||
@@ -281,7 +301,6 @@ add_executable(c3c
|
||||
src/compiler/json_output.c
|
||||
src/compiler/lexer.c
|
||||
src/compiler/linker.c
|
||||
src/compiler/llvm_codegen.c
|
||||
src/compiler/abi/c_abi_aarch64.c
|
||||
src/compiler/abi/c_abi.c
|
||||
src/compiler/abi/c_abi_riscv.c
|
||||
@@ -289,14 +308,6 @@ add_executable(c3c
|
||||
src/compiler/abi/c_abi_win64.c
|
||||
src/compiler/abi/c_abi_x64.c
|
||||
src/compiler/abi/c_abi_x86.c
|
||||
src/compiler/llvm_codegen_debug_info.c
|
||||
src/compiler/llvm_codegen_expr.c
|
||||
src/compiler/llvm_codegen_function.c
|
||||
src/compiler/llvm_codegen_instr.c
|
||||
src/compiler/llvm_codegen_module.c
|
||||
src/compiler/llvm_codegen_stmt.c
|
||||
src/compiler/llvm_codegen_type.c
|
||||
src/compiler/llvm_codegen_value.c
|
||||
src/compiler/module.c
|
||||
src/compiler/number.c
|
||||
src/compiler/parse_expr.c
|
||||
@@ -338,19 +349,61 @@ add_executable(c3c
|
||||
src/utils/whereami.c
|
||||
src/utils/cpus.c
|
||||
src/utils/unzipper.c
|
||||
src/compiler/c_codegen.c
|
||||
src/compiler/decltable.c
|
||||
src/compiler/mac_support.c
|
||||
src/compiler/llvm_codegen_storeload.c
|
||||
src/compiler/windows_support.c
|
||||
src/compiler/codegen_asm.c
|
||||
src/compiler/asm_target.c
|
||||
src/compiler/llvm_codegen_builtins.c
|
||||
src/compiler/expr.c
|
||||
src/utils/time.c
|
||||
src/utils/http.c
|
||||
src/compiler/sema_liveness.c
|
||||
src/build/common_build.c)
|
||||
src/build/common_build.c
|
||||
src/compiler/sema_const.c
|
||||
${CMAKE_BINARY_DIR}/git_hash.h
|
||||
)
|
||||
|
||||
if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
# We are inside of a git repository so rebuilding the hash every time something changes.
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_BINARY_DIR}/git_hash.h
|
||||
COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_LIST_DIR}/git_hash.cmake"
|
||||
DEPENDS "${CMAKE_CURRENT_LIST_DIR}/.git")
|
||||
else()
|
||||
# We are NOT inside of a git repository. Building the has only once.
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_BINARY_DIR}/git_hash.h
|
||||
COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_LIST_DIR}/git_hash.cmake")
|
||||
endif()
|
||||
|
||||
if(C3_WITH_LLVM)
|
||||
target_sources(c3c PRIVATE
|
||||
src/compiler/llvm_codegen.c
|
||||
src/compiler/llvm_codegen_debug_info.c
|
||||
src/compiler/llvm_codegen_expr.c
|
||||
src/compiler/llvm_codegen_function.c
|
||||
src/compiler/llvm_codegen_instr.c
|
||||
src/compiler/llvm_codegen_module.c
|
||||
src/compiler/llvm_codegen_stmt.c
|
||||
src/compiler/llvm_codegen_type.c
|
||||
src/compiler/llvm_codegen_value.c
|
||||
src/compiler/llvm_codegen_storeload.c
|
||||
src/compiler/llvm_codegen_builtins.c)
|
||||
|
||||
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=1)
|
||||
add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp)
|
||||
else()
|
||||
target_sources(c3c PRIVATE src/utils/hostinfo.c)
|
||||
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=0)
|
||||
endif()
|
||||
|
||||
target_include_directories(c3c PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/src/"
|
||||
"${CMAKE_BINARY_DIR}")
|
||||
|
||||
target_include_directories(miniz PUBLIC
|
||||
"${CMAKE_SOURCE_DIR}/dependencies/miniz/")
|
||||
|
||||
if (C3_USE_TB)
|
||||
file(GLOB tilde-sources
|
||||
@@ -364,7 +417,7 @@ if (C3_USE_TB)
|
||||
tilde-backend/src/tb/x64/*.c
|
||||
tilde-backend/src/tb/wasm/*.c
|
||||
tilde-backend/src/tb/aarch64/*.c
|
||||
)
|
||||
)
|
||||
target_sources(c3c PRIVATE
|
||||
src/compiler/tilde_codegen.c
|
||||
src/compiler/tilde_codegen_instr.c
|
||||
@@ -384,24 +437,29 @@ if (C3_USE_TB)
|
||||
|
||||
target_include_directories(c3c PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/tilde-backend/include/")
|
||||
|
||||
else()
|
||||
|
||||
target_compile_definitions(c3c PUBLIC TB_AVAILABLE=0)
|
||||
|
||||
endif()
|
||||
|
||||
if(C3_WITH_LLVM)
|
||||
target_link_libraries(c3c ${llvm_libs} miniz c3c_wrappers ${lld_libs})
|
||||
|
||||
target_include_directories(c3c PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/src/"
|
||||
"${CMAKE_SOURCE_DIR}/wrapper/include/")
|
||||
target_include_directories(c3c PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/wrapper/include/")
|
||||
|
||||
target_include_directories(c3c_wrappers PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/wrapper/include/")
|
||||
|
||||
target_include_directories(c3c_wrappers PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/wrapper/include/")
|
||||
target_link_libraries(c3c_wrappers ${llvm_libs} ${lld_libs})
|
||||
|
||||
target_include_directories(miniz PUBLIC
|
||||
"${CMAKE_SOURCE_DIR}/dependencies/miniz/")
|
||||
else()
|
||||
|
||||
target_link_libraries(c3c_wrappers ${llvm_libs} ${lld_libs})
|
||||
target_link_libraries(c3c ${llvm_libs} miniz c3c_wrappers ${lld_libs})
|
||||
target_link_libraries(c3c ${llvm_libs} miniz ${lld_libs})
|
||||
|
||||
endif()
|
||||
|
||||
if(C3_USE_MIMALLOC)
|
||||
target_link_libraries(c3c mimalloc-static)
|
||||
@@ -411,6 +469,11 @@ if (WIN32)
|
||||
target_link_libraries(c3c Winhttp.lib)
|
||||
endif()
|
||||
|
||||
if(MINGW)
|
||||
message("Increase stack for msys")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--stack,8388608")
|
||||
endif ()
|
||||
|
||||
if (CURL_FOUND)
|
||||
target_link_libraries(c3c ${CURL_LIBRARIES})
|
||||
target_include_directories(c3c PRIVATE ${CURL_INCLUDES})
|
||||
@@ -419,39 +482,48 @@ else()
|
||||
target_compile_definitions(c3c PUBLIC CURL_FOUND=0)
|
||||
endif()
|
||||
|
||||
|
||||
if(MSVC)
|
||||
message("Adding MSVC options")
|
||||
target_compile_options(c3c PRIVATE /wd4068 /wd4090 /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-)
|
||||
if(C3_WITH_LLVM)
|
||||
target_compile_options(c3c_wrappers PUBLIC /wd4624 /wd4267 /wd4244 /WX /Wv:18)
|
||||
if(NOT LLVM_ENABLE_RTTI)
|
||||
target_compile_options(c3c_wrappers PUBLIC /GR-)
|
||||
endif()
|
||||
target_link_options(c3c_wrappers PUBLIC /ignore:4099)
|
||||
endif()
|
||||
target_link_options(c3c_wrappers PUBLIC /ignore:4099)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
target_compile_options(c3c PUBLIC /MTd)
|
||||
target_compile_options(c3c_wrappers PUBLIC /MTd)
|
||||
if (C3_WITH_LLVM)
|
||||
target_compile_options(c3c_wrappers PUBLIC /MTd)
|
||||
endif()
|
||||
target_compile_options(miniz PUBLIC /MTd)
|
||||
if (C3_USE_TB)
|
||||
target_compile_options(tilde-backend PUBLIC /MTd)
|
||||
endif()
|
||||
else()
|
||||
target_compile_options(c3c PUBLIC /MT)
|
||||
target_compile_options(c3c_wrappers PUBLIC /MT)
|
||||
if (C3_WITH_LLVM)
|
||||
target_compile_options(c3c_wrappers PUBLIC /MT)
|
||||
endif()
|
||||
target_compile_options(miniz PUBLIC /MT)
|
||||
if (C3_USE_TB)
|
||||
target_compile_options(tilde-backend PUBLIC /MT)
|
||||
endif()
|
||||
endif()
|
||||
set(clang_lib_dir ${llvm_dir}/lib/clang/${C3_LLVM_VERSION}/lib/windows)
|
||||
set(sanitizer_runtime_libraries
|
||||
${clang_lib_dir}/clang_rt.asan-x86_64.lib
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.lib
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.dll
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib)
|
||||
if(C3_WITH_LLVM)
|
||||
set(clang_lib_dir ${llvm_dir}/lib/clang/${C3_LLVM_VERSION}/lib/windows)
|
||||
set(sanitizer_runtime_libraries
|
||||
${clang_lib_dir}/clang_rt.asan-x86_64.lib
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.lib
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.dll
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "using gcc/clang warning switches")
|
||||
target_link_options(c3c PRIVATE -pthread)
|
||||
if (NOT LLVM_ENABLE_RTTI)
|
||||
if (C3_WITH_LLVM AND NOT LLVM_ENABLE_RTTI)
|
||||
target_compile_options(c3c_wrappers PRIVATE -fno-rtti)
|
||||
endif()
|
||||
target_compile_options(c3c PRIVATE -pthread -Wall -Werror -Wno-unknown-pragmas -Wno-unused-result
|
||||
@@ -461,19 +533,24 @@ endif()
|
||||
install(TARGETS c3c DESTINATION bin)
|
||||
install(DIRECTORY lib/ DESTINATION lib/c3)
|
||||
|
||||
if (DEFINED sanitizer_runtime_libraries)
|
||||
# Man page install (OSX/Linux only)
|
||||
if (NOT WIN32)
|
||||
install(FILES c3c.1 DESTINATION "share/man/man1")
|
||||
endif()
|
||||
|
||||
if (C3_WITH_LLVM AND DEFINED sanitizer_runtime_libraries)
|
||||
add_custom_command(TARGET c3c POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E rm -rf -- $<TARGET_FILE_DIR:c3c>/c3c_rt
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory $<TARGET_FILE_DIR:c3c>/c3c_rt
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy ${sanitizer_runtime_libraries} $<TARGET_FILE_DIR:c3c>/c3c_rt
|
||||
VERBATIM
|
||||
COMMENT "Copying sanitizer runtime libraries to output directory")
|
||||
COMMAND "${CMAKE_COMMAND}" -E rm -rf -- $<TARGET_FILE_DIR:c3c>/c3c_rt
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory $<TARGET_FILE_DIR:c3c>/c3c_rt
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy ${sanitizer_runtime_libraries} $<TARGET_FILE_DIR:c3c>/c3c_rt
|
||||
VERBATIM
|
||||
COMMENT "Copying sanitizer runtime libraries to output directory")
|
||||
|
||||
if (APPLE)
|
||||
# Change LC_ID_DYLIB to be rpath-based instead of having an absolute path
|
||||
add_custom_command(TARGET c3c POST_BUILD
|
||||
COMMAND find $<TARGET_FILE_DIR:c3c>/c3c_rt -type f -name "*.dylib" -execdir ${LLVM_TOOLS_BINARY_DIR}/llvm-install-name-tool -id @rpath/{} {} $<SEMICOLON>
|
||||
VERBATIM)
|
||||
COMMAND find $<TARGET_FILE_DIR:c3c>/c3c_rt -type f -name "*.dylib" -execdir ${LLVM_TOOLS_BINARY_DIR}/llvm-install-name-tool -id @rpath/{} {} $<SEMICOLON>
|
||||
VERBATIM)
|
||||
endif()
|
||||
|
||||
install(DIRECTORY $<TARGET_FILE_DIR:c3c>/c3c_rt/ DESTINATION bin/c3c_rt)
|
||||
|
||||
67
README.md
67
README.md
@@ -10,7 +10,8 @@ Precompiled binaries for the following operating systems are available:
|
||||
|
||||
- Windows x64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-windows.zip), [install instructions](#installing-on-windows-with-precompiled-binaries).
|
||||
- Debian x64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-linux.tar.gz), [install instructions](#installing-on-debian-with-precompiled-binaries).
|
||||
- MacOS x64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip), [install instructions](#installing-on-mac-with-precompiled-binaries).
|
||||
- Ubuntu x86 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20.tar.gz), [install instructions](#installing-on-ubuntu-with-precompiled-binaries).
|
||||
- MacOS Arm64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip), [install instructions](#installing-on-mac-with-precompiled-binaries).
|
||||
|
||||
The manual for C3 can be found at [www.c3-lang.org](http://www.c3-lang.org).
|
||||
|
||||
@@ -34,7 +35,7 @@ whole new language.
|
||||
|
||||
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++
|
||||
```cpp
|
||||
module stack (<Type>);
|
||||
// Above: the parameterized type is applied to the entire module.
|
||||
|
||||
@@ -54,7 +55,7 @@ fn void Stack.push(Stack* this, Type element)
|
||||
if (this.capacity == this.size)
|
||||
{
|
||||
this.capacity *= 2;
|
||||
if (this.capacity < 16) this.capacity = 16;
|
||||
if (this.capacity < 16) this.capacity = 16;
|
||||
this.elems = realloc(this.elems, Type.sizeof * this.capacity);
|
||||
}
|
||||
this.elems[this.size++] = element;
|
||||
@@ -137,7 +138,7 @@ fn void main()
|
||||
|
||||
### Current status
|
||||
|
||||
The current stable version of the compiler is **version 0.6.1**.
|
||||
The current stable version of the compiler is **version 0.6.5**.
|
||||
|
||||
The upcoming 0.6.x releases will focus on expanding the standard library.
|
||||
Follow the issues [here](https://github.com/c3lang/c3c/issues).
|
||||
@@ -205,7 +206,13 @@ More platforms will be supported in the future.
|
||||
2. Unpack executable and standard lib.
|
||||
3. Run `./c3c`.
|
||||
|
||||
#### Installing on Mac with precompiled binaries
|
||||
#### Installing on Ubuntu with precompiled binaries
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20.tar.gz](https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20-debug.tar.gz))
|
||||
2. Unpack executable and standard lib.
|
||||
3. Run `./c3c`.
|
||||
|
||||
#### Installing on MacOS with precompiled binaries
|
||||
1. Make sure you have XCode with command line tools installed.
|
||||
2. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip](https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-macos-debug.zip))
|
||||
@@ -215,9 +222,16 @@ More platforms will be supported in the future.
|
||||
(*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
|
||||
There is an AUR package for the c3c compiler : [c3c-git](https://aur.archlinux.org/packages/c3c-git).
|
||||
Arch includes c3c in the official 'extra' repo. It can be easily installed the usual way:
|
||||
|
||||
Due to some issues with the LLVM packaged for Arch Linux, the AUR package will download and use LLVM 16 for Ubuntu-23.04 to compile the c3c compiler.
|
||||
```sh
|
||||
sudo pacman -S c3c
|
||||
# or paru -S c3c
|
||||
# or yay -S c3c
|
||||
# or aura -A c3c
|
||||
```
|
||||
|
||||
There is also an AUR package for the c3c compiler : [c3c-git](https://aur.archlinux.org/packages/c3c-git).
|
||||
|
||||
You can use your AUR package manager:
|
||||
```sh
|
||||
@@ -289,7 +303,7 @@ called `hello_world` or `hello_world.exe`depending on platform.
|
||||
|
||||
#### Compiling on Windows
|
||||
|
||||
1. Make sure you have Visual Studio 17 2022 installed or alternatively install the "Buildtools for Visual Studio" (https://aka.ms/vs/17/release/vs_BuildTools.exe) and then select "Desktop development with C++" (there is also `c3c/resources/install_win_reqs.bat` to automate this)
|
||||
1. Make sure you have Visual Studio 17 2022 installed or alternatively install the "Buildtools for Visual Studio" (https://aka.ms/vs/17/release/vs_BuildTools.exe) and then select "Desktop development with C++"
|
||||
2. Install CMake
|
||||
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
|
||||
4. Enter the C3C directory `cd c3c`.
|
||||
@@ -304,17 +318,16 @@ You can try it out by running some sample code: `c3c.exe compile ../resources/ex
|
||||
*Note that if you run into linking issues when building, make sure that you are using the latest version of VS17.*
|
||||
|
||||
|
||||
#### Compiling on Ubuntu 20.10
|
||||
#### Compiling on Ubuntu 24.04 LTS
|
||||
|
||||
1. Make sure you have a C compiler that handles C11 and a C++ compiler, such as GCC or Clang. Git also needs to be installed.
|
||||
2. Install CMake: `sudo apt install cmake`
|
||||
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`
|
||||
5. Enter the C3C directory `cd c3c`.
|
||||
6. Create a build directory `mkdir build`
|
||||
7. Change directory to the build directory `cd build`
|
||||
8. Set up CMake build: `cmake ..`
|
||||
9. Build: `cmake --build .`
|
||||
2. Install LLVM 18 `sudo apt-get install cmake git clang zlib1g zlib1g-dev libllvm18 llvm llvm-dev llvm-runtime liblld-dev liblld-18 libpolly-18-dev`
|
||||
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
|
||||
4. Enter the C3C directory `cd c3c`.
|
||||
5. Create a build directory `mkdir build`
|
||||
6. Change directory to the build directory `cd build`
|
||||
7. Set up CMake build: `cmake ..`
|
||||
8. Build: `cmake --build .`
|
||||
|
||||
You should now have a `c3c` executable.
|
||||
|
||||
@@ -336,6 +349,20 @@ Your c3c executable should have compiled properly. You may want to test it: `./c
|
||||
For a sytem-wide installation, run the following as root: `cmake --install .`
|
||||
|
||||
|
||||
#### Compiling on Fedora
|
||||
|
||||
1. Install required project dependencies: `dnf install cmake clang git llvm llvm-devel lld lld-devel ncurses-devel`
|
||||
2. Optionally, install additional dependencies: `dnf install libcurl-devel zlib-devel libzstd-devel libxml2-devel libffi-devel`
|
||||
3. Clone the C3C repository: `git clone https://github.com/c3lang/c3c.git`
|
||||
- If you only need the latest commit, you may want to make a shallow clone: `git clone https://github.com/c3lang/c3c.git --depth=1`
|
||||
4. Enter the C3C directory: `cd c3c`
|
||||
5. Create a build directory and navigate into it: `mkdir build && cd build`
|
||||
6. Create the CMake build cache. The Fedora repositories provide `.so` libraries for lld, so you need to set the C3_LINK_DYNAMIC flag: `cmake .. -DC3_LINK_DYNAMIC=1`
|
||||
7. Build the project: `cmake --build .`
|
||||
|
||||
The c3c binary should be created in the build directory. You can try it out by running some sample code: `./c3c compile ../resources/examples/hash.c3`
|
||||
|
||||
|
||||
#### Compiling on other Linux / Unix variants
|
||||
|
||||
1. Install CMake.
|
||||
@@ -369,3 +396,9 @@ Editor plugins can be found at https://github.com/c3lang/editor-plugins.
|
||||
3. Run tests and see that they pass. (Recommended settings: `c3c compile-test -O0 test/unit`.
|
||||
- in this example `test/unit/` is the relative path to the test directory, so adjust as required)
|
||||
4. Make a pull request for the new tests.
|
||||
|
||||
## Thank yous
|
||||
|
||||
A huge **THANK YOU** goes out to all contributors and sponsors.
|
||||
|
||||
A special thank you to sponsors [Caleb-o](https://github.com/Caleb-o) and [devdad](https://github.com/devdad) for going the extra mile.
|
||||
|
||||
69
benchmarks/stdlib/sort/quicksort.c3
Normal file
69
benchmarks/stdlib/sort/quicksort.c3
Normal file
@@ -0,0 +1,69 @@
|
||||
module sort_bench;
|
||||
|
||||
import std::sort;
|
||||
|
||||
fn void init() @init
|
||||
{
|
||||
set_benchmark_warmup_iterations(5);
|
||||
set_benchmark_max_iterations(10_000);
|
||||
}
|
||||
|
||||
fn void quicksort_bench() @benchmark
|
||||
{
|
||||
// test set: 500 numbers between 0 and 99;
|
||||
int[] data = {
|
||||
71, 28, 2, 13, 62, 10, 54, 78, 63, 86,
|
||||
33, 65, 89, 51, 58, 0, 51, 16, 87, 30,
|
||||
89, 14, 52, 41, 88, 25, 83, 91, 56, 86,
|
||||
14, 64, 76, 18, 39, 24, 79, 62, 34, 58,
|
||||
90, 24, 56, 73, 85, 82, 79, 63, 47, 69,
|
||||
78, 29, 49, 28, 43, 47, 56, 53, 79, 56,
|
||||
19, 63, 29, 52, 71, 93, 61, 46, 30, 11,
|
||||
21, 26, 37, 86, 93, 74, 62, 0, 41, 17,
|
||||
26, 27, 34, 11, 54, 69, 72, 44, 74, 3,
|
||||
61, 62, 80, 90, 3, 82, 16, 12, 28, 1,
|
||||
2, 49, 4, 44, 57, 86, 63, 74, 33, 41,
|
||||
76, 77, 56, 57, 56, 88, 74, 71, 6, 59,
|
||||
40, 42, 94, 55, 21, 17, 17, 63, 21, 83,
|
||||
73, 19, 39, 88, 93, 74, 21, 0, 63, 45,
|
||||
69, 66, 22, 68, 86, 86, 85, 67, 8, 50,
|
||||
23, 98, 64, 80, 64, 36, 40, 30, 73, 36,
|
||||
23, 14, 1, 77, 82, 8, 18, 73, 37, 86,
|
||||
29, 70, 27, 87, 64, 81, 13, 0, 4, 83,
|
||||
90, 17, 71, 66, 38, 39, 54, 22, 86, 18,
|
||||
84, 66, 77, 25, 64, 93, 80, 91, 2, 92,
|
||||
47, 32, 90, 16, 46, 29, 56, 87, 70, 73,
|
||||
89, 41, 5, 54, 93, 63, 16, 39, 71, 84,
|
||||
74, 91, 69, 59, 49, 87, 74, 37, 75, 83,
|
||||
77, 19, 51, 44, 79, 62, 94, 20, 24, 83,
|
||||
37, 70, 57, 32, 93, 8, 29, 11, 7, 92,
|
||||
8, 23, 20, 21, 7, 70, 28, 20, 96, 6,
|
||||
50, 58, 30, 61, 66, 42, 50, 54, 64, 7,
|
||||
10, 53, 63, 44, 16, 39, 83, 73, 3, 29,
|
||||
97, 32, 36, 68, 84, 64, 73, 5, 29, 13,
|
||||
48, 3, 84, 65, 75, 68, 66, 22, 39, 33,
|
||||
39, 24, 27, 85, 18, 34, 3, 63, 32, 9,
|
||||
29, 66, 24, 90, 75, 50, 11, 95, 47, 14,
|
||||
92, 1, 76, 45, 76, 41, 55, 54, 38, 67,
|
||||
43, 40, 5, 61, 97, 11, 61, 24, 92, 24,
|
||||
76, 53, 60, 34, 78, 80, 70, 75, 30, 90,
|
||||
65, 99, 80, 61, 94, 75, 63, 67, 10, 35,
|
||||
23, 42, 31, 48, 14, 68, 84, 14, 79, 1,
|
||||
25, 94, 23, 53, 49, 69, 44, 73, 63, 51,
|
||||
44, 96, 88, 51, 94, 24, 64, 72, 59, 81,
|
||||
73, 93, 14, 35, 9, 53, 25, 48, 50, 88,
|
||||
46, 97, 67, 40, 27, 17, 2, 42, 11, 82,
|
||||
0, 46, 44, 38, 31, 88, 63, 88, 10, 82,
|
||||
77, 61, 24, 39, 27, 33, 10, 91, 69, 22,
|
||||
42, 74, 71, 13, 32, 56, 12, 46, 81, 74,
|
||||
17, 26, 45, 50, 76, 84, 76, 36, 43, 65,
|
||||
81, 64, 0, 49, 70, 11, 76, 19, 60, 55,
|
||||
15, 98, 31, 91, 56, 8, 97, 9, 3, 94,
|
||||
3, 88, 7, 2, 3, 98, 10, 51, 21, 79,
|
||||
99, 3, 8, 76, 52, 13, 40, 90, 85, 15,
|
||||
70, 77, 43, 30, 4, 89, 18, 21, 59, 17,
|
||||
};
|
||||
sort::quicksort(data);
|
||||
}
|
||||
|
||||
|
||||
552
c3c.1
Normal file
552
c3c.1
Normal file
@@ -0,0 +1,552 @@
|
||||
.TH "c3c" "1" "2024-10-27" "C3 Compiler" "User Commands"
|
||||
.SH NAME
|
||||
c3c \- Compiler for the C3 programming language
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B c3c
|
||||
[\fIoptions\fR ...] \fIcommand\fR [\fIargs\fR ...]
|
||||
|
||||
.SH DESCRIPTION
|
||||
.B c3c
|
||||
is the compiler for the C3 language, providing commands for compilation, project
|
||||
management, testing, and distribution. The available commands allow users to
|
||||
compile files, initialize projects, build targets, run benchmarks, clean build
|
||||
files, and more.
|
||||
|
||||
.SH COMMANDS
|
||||
.PP
|
||||
.B c3c compile
|
||||
\fIfile1\fR [\fIfile2\fR ...]
|
||||
.RS
|
||||
Compile files without a project into an executable.
|
||||
.RE
|
||||
.PP
|
||||
.B c3c init
|
||||
\fIproject name\fR
|
||||
.RS
|
||||
Initialize a new project structure.
|
||||
.RE
|
||||
.PP
|
||||
.B c3c init-lib
|
||||
\fIlibrary name\fR
|
||||
.RS
|
||||
Initialize a new library structure.
|
||||
.RE
|
||||
.PP
|
||||
.B c3c build
|
||||
[\fItarget\fR]
|
||||
.RS
|
||||
Build the target in the current project.
|
||||
.RE
|
||||
.PP
|
||||
.B c3c benchmark
|
||||
.RS
|
||||
Run the benchmarks in the current project.
|
||||
.RE
|
||||
.PP
|
||||
.B c3c test
|
||||
.RS
|
||||
Run the unit tests in the current project.
|
||||
.RE
|
||||
.PP
|
||||
.B c3c clean
|
||||
.RS
|
||||
Clean all build files.
|
||||
.RE
|
||||
.PP
|
||||
.B c3c run
|
||||
[\fItarget\fR] [-- [\fIarg1\fR ...]]
|
||||
.RS
|
||||
Run (and build if needed) the target in the current project.
|
||||
.RE
|
||||
.PP
|
||||
.B c3c dist
|
||||
[\fItarget\fR]
|
||||
.RS
|
||||
Clean and build a target for distribution.
|
||||
.RE
|
||||
.PP
|
||||
.B c3c directives
|
||||
[\fItarget\fR]
|
||||
.RS
|
||||
Generate documentation for the target.
|
||||
.RE
|
||||
.PP
|
||||
.B c3c bench
|
||||
[\fItarget\fR]
|
||||
.RS
|
||||
Benchmark a target.
|
||||
.RE
|
||||
.PP
|
||||
.B c3c clean-run
|
||||
[\fItarget\fR] [-- [\fIarg1\fR ...]]
|
||||
.RS
|
||||
Clean, then run the target.
|
||||
.RE
|
||||
.PP
|
||||
.B c3c compile-run
|
||||
\fIfile1\fR [\fIfile2\fR ...] [-- [\fIarg1\fR ...]]
|
||||
.RS
|
||||
Compile files then immediately run the result.
|
||||
.RE
|
||||
.PP
|
||||
.B c3c compile-only
|
||||
\fIfile1\fR [\fIfile2\fR ...]
|
||||
.RS
|
||||
Compile files but do not perform linking.
|
||||
.RE
|
||||
.PP
|
||||
.B c3c compile-benchmark
|
||||
\fIfile1\fR [\fIfile2\fR ...]
|
||||
.RS
|
||||
Compile files into an executable and run benchmarks.
|
||||
.RE
|
||||
.PP
|
||||
.B c3c compile-test
|
||||
\fIfile1\fR [\fIfile2\fR ...]
|
||||
.RS
|
||||
Compile files into an executable and run unit tests.
|
||||
.RE
|
||||
.PP
|
||||
.B c3c static-lib
|
||||
\fIfile1\fR [\fIfile2\fR ...]
|
||||
.RS
|
||||
Compile files without a project into a static library.
|
||||
.RE
|
||||
.PP
|
||||
.B c3c dynamic-lib
|
||||
\fIfile1\fR [\fIfile2\fR ...]
|
||||
.RS
|
||||
Compile files without a project into a dynamic library.
|
||||
.RE
|
||||
.PP
|
||||
.B c3c headers
|
||||
\fIfile1\fR [\fIfile2\fR ...]
|
||||
.RS
|
||||
Analyze files and generate C headers for public methods.
|
||||
.RE
|
||||
.PP
|
||||
.B c3c vendor-fetch
|
||||
\fIlibrary\fR ...
|
||||
.RS
|
||||
Fetch one or more libraries from the vendor collection.
|
||||
.RE
|
||||
.PP
|
||||
.B c3c project
|
||||
\fIsubcommand\fR ...
|
||||
.RS
|
||||
Manipulate or view project files.
|
||||
.RE
|
||||
|
||||
.SH OPTIONS
|
||||
.PP
|
||||
.B --stdlib
|
||||
\fIdir\fR
|
||||
.RS
|
||||
Use this directory as the C3 standard library path.
|
||||
.RE
|
||||
.PP
|
||||
.B --no-entry
|
||||
.RS
|
||||
Do not generate (or require) a main function.
|
||||
.RE
|
||||
.PP
|
||||
.B --libdir
|
||||
\fIdir\fR
|
||||
.RS
|
||||
Add this directory to the C3 library search paths.
|
||||
.RE
|
||||
.PP
|
||||
.B --lib
|
||||
\fIname\fR
|
||||
.RS
|
||||
Add this library to the compilation.
|
||||
.RE
|
||||
.PP
|
||||
.B --path
|
||||
\fIdir\fR
|
||||
.RS
|
||||
Use this as the base directory for the current command.
|
||||
.RE
|
||||
.PP
|
||||
.B --template
|
||||
\fItemplate\fR
|
||||
.RS
|
||||
Select template for 'init': "exe", "static-lib", "dynamic-lib" or a path.
|
||||
.RE
|
||||
.PP
|
||||
.B --about
|
||||
Prints a short description of C3.
|
||||
.PP
|
||||
.B --symtab
|
||||
\fIvalue\fR
|
||||
.RS
|
||||
Sets the preferred symtab size.
|
||||
.RE
|
||||
.PP
|
||||
.B --max-mem
|
||||
\fIvalue\fR
|
||||
.RS
|
||||
Sets the preferred max memory size.
|
||||
.RE
|
||||
.PP
|
||||
.B --run-once
|
||||
.RS
|
||||
After running the output file, delete it immediately.
|
||||
.RE
|
||||
.PP
|
||||
.B -V, --version
|
||||
Print version information.
|
||||
.PP
|
||||
.B -E
|
||||
Lex only.
|
||||
.PP
|
||||
.B -P
|
||||
Only parse and output the AST as JSON.
|
||||
.PP
|
||||
.B -C
|
||||
Only lex, parse and check.
|
||||
.PP
|
||||
.B -
|
||||
\fIcode\fR...
|
||||
.RS
|
||||
Read code from standard in.
|
||||
.RE
|
||||
.PP
|
||||
.B -o
|
||||
\fIfile\fR
|
||||
.RS
|
||||
Write output to \fIfile\fR.
|
||||
.RE
|
||||
.PP
|
||||
.B -O0
|
||||
Safe, no optimizations, emit debug info.
|
||||
.PP
|
||||
.B -O1
|
||||
Safe, high optimization, emit debug info.
|
||||
.PP
|
||||
.B -O2
|
||||
Unsafe, high optimization, emit debug info.
|
||||
.PP
|
||||
.B -O3
|
||||
Unsafe, high optimization, single module, emit debug info.
|
||||
.PP
|
||||
.B -O4
|
||||
Unsafe, highest optimization, relaxed maths, single module, emit debug info, no panic messages.
|
||||
.PP
|
||||
.B -O5
|
||||
Unsafe, highest optimization, fast maths, single module, emit debug info, no panic messages, no backtrace.
|
||||
.PP
|
||||
.B -Os
|
||||
Unsafe, high optimization, small code, single module, no debug info, no panic messages.
|
||||
.PP
|
||||
.B -Oz
|
||||
Unsafe, high optimization, tiny code, single module, no debug info, no panic messages, no backtrace.
|
||||
.PP
|
||||
.B -D
|
||||
\fIname\fR
|
||||
.RS
|
||||
Add feature flag \fIname\fR.
|
||||
.RE
|
||||
.PP
|
||||
.B -U
|
||||
\fIname\fR
|
||||
.RS
|
||||
Remove feature flag \fIname\fR.
|
||||
.RE
|
||||
.PP
|
||||
.B --trust=
|
||||
\fIoption\fR
|
||||
.RS
|
||||
Trust level: none (default), include ($include allowed), full ($exec / exec allowed).
|
||||
.RE
|
||||
.PP
|
||||
.B --output-dir
|
||||
\fIdir\fR
|
||||
.RS
|
||||
Override general output directory.
|
||||
.RE
|
||||
.PP
|
||||
.B --threads
|
||||
\fInumber\fR
|
||||
.RS
|
||||
Set the number of threads to use for compilation.
|
||||
.RE
|
||||
.PP
|
||||
.B --show-backtrace=
|
||||
\fIyes|no\fR
|
||||
.RS
|
||||
Show detailed backtrace on segfaults.
|
||||
.RE
|
||||
|
||||
.PP
|
||||
.B -g
|
||||
Emit debug info.
|
||||
.PP
|
||||
.B -g0
|
||||
Emit no debug info.
|
||||
|
||||
|
||||
.PP
|
||||
.B -l
|
||||
\fIlibrary\fR
|
||||
.RS
|
||||
Link with the library provided.
|
||||
.RE
|
||||
.PP
|
||||
.B -L
|
||||
\fIlibrary\fR \fIdir\fR
|
||||
.RS
|
||||
Append the directory to the linker search paths.
|
||||
.RE
|
||||
.PP
|
||||
.B -z
|
||||
\fIargument\fR
|
||||
.RS
|
||||
Send the \fIargument\fR as a parameter to the linker.
|
||||
.RE
|
||||
.PP
|
||||
.B --cc
|
||||
\fIpath\fR
|
||||
.RS
|
||||
Set C compiler (for C files in projects and use as system linker).
|
||||
.RE
|
||||
.PP
|
||||
.B --linker=
|
||||
\fIoption\fR [\fIpath\fR]
|
||||
.RS
|
||||
Specify the linker: builtin, cc, custom (default is 'cc'). 'Custom' requires a path.
|
||||
.RE
|
||||
|
||||
.PP
|
||||
.B --use-stdlib=
|
||||
\fIyes|no\fR
|
||||
.RS
|
||||
Include the standard library (default: yes).
|
||||
.RE
|
||||
.PP
|
||||
.B --link-libc=
|
||||
\fIyes|no\fR
|
||||
.RS
|
||||
Link libc and other default libraries (default: yes).
|
||||
.RE
|
||||
.PP
|
||||
.B --emit-stdlib=
|
||||
\fIyes|no\fR
|
||||
.RS
|
||||
Output files for the standard library (default: yes).
|
||||
.RE
|
||||
.PP
|
||||
.B --panicfn
|
||||
\fIname\fR
|
||||
.RS
|
||||
Override the panic function name.
|
||||
.RE
|
||||
.PP
|
||||
.B --testfn
|
||||
\fIname\fR
|
||||
.RS
|
||||
Override the test runner function name.
|
||||
.RE
|
||||
.PP
|
||||
.B --benchfn
|
||||
\fIname\fR
|
||||
.RS
|
||||
Override the benchmark runner function name.
|
||||
.RE
|
||||
|
||||
.PP
|
||||
.B --reloc=
|
||||
\fIoption\fR
|
||||
.RS
|
||||
Specify the relocation model: none, pic, PIC, pie, PIE.
|
||||
.RE
|
||||
.PP
|
||||
.B --x86cpu=
|
||||
\fIoption\fR
|
||||
.RS
|
||||
Set general level of x64 CPU: baseline, ssse3, sse4, avx1, avx2-v1, avx2-v2 (Skylake/Zen1+), avx512 (Icelake/Zen4+), native.
|
||||
.RE
|
||||
.PP
|
||||
.B --x86vec=
|
||||
\fIoption\fR
|
||||
.RS
|
||||
Set maximum type of vector use: none, mmx, sse, avx, avx512, default.
|
||||
.RE
|
||||
.PP
|
||||
.B --riscvfloat=
|
||||
\fIoption\fR
|
||||
.RS
|
||||
Set type of RISC-V float support: none, float, double.
|
||||
.RE
|
||||
.PP
|
||||
.B --memory-env=
|
||||
\fIoption\fR
|
||||
.RS
|
||||
Set the memory environment: normal, small, tiny, none.
|
||||
.RE
|
||||
.PP
|
||||
.B --strip-unused=
|
||||
\fIyes|no\fR
|
||||
.RS
|
||||
Strip unused code and globals from the output (default: yes).
|
||||
.RE
|
||||
.PP
|
||||
.B --fp-math=
|
||||
\fIoption\fR
|
||||
.RS
|
||||
Specify floating-point math behavior: strict, relaxed, fast.
|
||||
.RE
|
||||
.PP
|
||||
.B --win64-simd=
|
||||
\fIoption\fR
|
||||
.RS
|
||||
Windows SIMD ABI: array, full.
|
||||
.RE
|
||||
|
||||
.PP
|
||||
.B --debug-stats
|
||||
Print debug statistics.
|
||||
.PP
|
||||
.B --print-linking
|
||||
Print linker arguments.
|
||||
.PP
|
||||
.B --debug-log
|
||||
Print debug logging to stdout.
|
||||
|
||||
|
||||
.PP
|
||||
.B --benchmarking
|
||||
Run built-in benchmarks.
|
||||
.PP
|
||||
.B --testing
|
||||
Run built-in tests.
|
||||
|
||||
|
||||
.PP
|
||||
.B --list-attributes
|
||||
List all attributes.
|
||||
.PP
|
||||
.B --list-builtins
|
||||
List all builtins.
|
||||
.PP
|
||||
.B --list-keywords
|
||||
List all keywords.
|
||||
.PP
|
||||
.B --list-operators
|
||||
List all operators.
|
||||
.PP
|
||||
.B --list-precedence
|
||||
List operator precedence order.
|
||||
.PP
|
||||
.B --list-project-properties
|
||||
List all available keys used in project.json files.
|
||||
.PP
|
||||
.B --list-manifest-properties
|
||||
List all available keys used in manifest.json files.
|
||||
.PP
|
||||
.B --list-targets
|
||||
List all architectures the compiler supports.
|
||||
.PP
|
||||
.B --list-type-properties
|
||||
List all type properties.
|
||||
|
||||
|
||||
.PP
|
||||
.B --print-output
|
||||
Print the object files created to stdout.
|
||||
.PP
|
||||
.B --print-input
|
||||
Print inputted C3 files to stdout.
|
||||
|
||||
|
||||
.PP
|
||||
.B --winsdk
|
||||
\fIdir\fR
|
||||
.RS
|
||||
Set the directory for Windows system library files for cross-compilation.
|
||||
.RE
|
||||
.PP
|
||||
.B --wincrt=
|
||||
\fIoption\fR
|
||||
.RS
|
||||
Windows CRT linking: none, static-debug, static, dynamic-debug (default if debug info enabled), dynamic (default).
|
||||
.RE
|
||||
.PP
|
||||
.B --windef
|
||||
\fIfile\fR
|
||||
.RS
|
||||
Use Windows 'def' file for function exports instead of 'dllexport'.
|
||||
.RE
|
||||
|
||||
.PP
|
||||
.B --macossdk
|
||||
\fIdir\fR
|
||||
.RS
|
||||
Set the directory for the MacOS SDK for cross-compilation.
|
||||
.RE
|
||||
.PP
|
||||
.B --macos-min-version
|
||||
\fIver\fR
|
||||
.RS
|
||||
Set the minimum MacOS version to compile for.
|
||||
.RE
|
||||
.PP
|
||||
.B --macos-sdk-version
|
||||
\fIver\fR
|
||||
.RS
|
||||
Set the MacOS SDK version to compile for.
|
||||
.RE
|
||||
|
||||
.PP
|
||||
.B --linux-crt
|
||||
\fIdir\fR
|
||||
.RS
|
||||
Set the directory to use for finding crt1.o and related files.
|
||||
.RE
|
||||
.PP
|
||||
.B --linux-crtbegin
|
||||
\fIdir\fR
|
||||
.RS
|
||||
Set the directory to use for finding crtbegin.o and related files.
|
||||
.RE
|
||||
|
||||
.PP
|
||||
.B --vector-conv=
|
||||
\fIoption\fR
|
||||
.RS
|
||||
Set vector conversion behavior: default, old.
|
||||
.RE
|
||||
.PP
|
||||
.B --sanitize=
|
||||
\fIoption\fR
|
||||
.RS
|
||||
Enable a sanitizer: address, memory, thread.
|
||||
.RE
|
||||
|
||||
.SH EXAMPLES
|
||||
.PP
|
||||
Create a project:
|
||||
.RS
|
||||
.B c3c init new_project
|
||||
.RE
|
||||
.PP
|
||||
Create a library project:
|
||||
.RS
|
||||
.B c3c init-lib new_library
|
||||
.RE
|
||||
.PP
|
||||
Compile a file:
|
||||
.RS
|
||||
.B c3c compile main.c3
|
||||
.RE
|
||||
.PP
|
||||
Build the current project:
|
||||
.RS
|
||||
.B c3c build
|
||||
.RE
|
||||
.PP
|
||||
Run tests for the current project:
|
||||
.RS
|
||||
.B c3c test
|
||||
.RE
|
||||
61
flake.lock
generated
Normal file
61
flake.lock
generated
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1726560853,
|
||||
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1730958623,
|
||||
"narHash": "sha256-JwQZIGSYnRNOgDDoIgqKITrPVil+RMWHsZH1eE1VGN0=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "85f7e662eda4fa3a995556527c87b2524b691933",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
44
flake.nix
Normal file
44
flake.nix
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
description = "C3 compiler flake";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs?ref=nixpkgs-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = { self, ... } @ inputs: inputs.flake-utils.lib.eachDefaultSystem
|
||||
(system:
|
||||
let pkgs = import inputs.nixpkgs { inherit system; };
|
||||
call = set: pkgs.callPackage ./nix/default.nix (
|
||||
set // {
|
||||
rev = self.rev or "unknown";
|
||||
}
|
||||
);
|
||||
in {
|
||||
packages = {
|
||||
default = self.packages.${system}.c3c;
|
||||
|
||||
c3c = call {};
|
||||
|
||||
c3c-checks = pkgs.callPackage ./nix/default.nix {
|
||||
checks = true;
|
||||
};
|
||||
|
||||
c3c-debug = pkgs.callPackage ./nix/default.nix {
|
||||
debug = true;
|
||||
};
|
||||
|
||||
c3c-debug-checks = pkgs.callPackage ./nix/default.nix {
|
||||
debug = true;
|
||||
checks = true;
|
||||
};
|
||||
};
|
||||
|
||||
devShells = {
|
||||
default = pkgs.callPackage ./nix/shell.nix {
|
||||
c3c = self.packages.${system}.c3c-debug;
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
15
git_hash.cmake
Normal file
15
git_hash.cmake
Normal file
@@ -0,0 +1,15 @@
|
||||
find_package(Git QUIET)
|
||||
|
||||
set(GIT_HASH "unknown")
|
||||
|
||||
if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/.git")
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
|
||||
OUTPUT_VARIABLE GIT_HASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
COMMAND_ERROR_IS_FATAL ANY)
|
||||
endif()
|
||||
|
||||
message("Git Hash: ${GIT_HASH}")
|
||||
|
||||
file(WRITE ${CMAKE_BINARY_DIR}/git_hash.h "#pragma once\n#define GIT_HASH \"${GIT_HASH}\"\n")
|
||||
@@ -53,9 +53,9 @@ fn bool char.is_blank(char c) => is_blank_m(c);
|
||||
fn bool char.is_cntrl(char c) => is_cntrl_m(c);
|
||||
fn char char.to_lower(char c) => (char)to_lower_m(c);
|
||||
fn char char.to_upper(char c) => (char)to_upper_m(c);
|
||||
/**
|
||||
* @require c.is_xdigit()
|
||||
**/
|
||||
<*
|
||||
@require c.is_xdigit()
|
||||
*>
|
||||
fn char char.from_hex(char c) => c.is_digit() ? c - '0' : 10 + (c | 0x20) - 'a';
|
||||
|
||||
fn bool uint.in_range(uint c, uint start, uint len) => in_range_m(c, start, len);
|
||||
|
||||
@@ -8,12 +8,12 @@ struct Atomic
|
||||
Type data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads data atomically, by default this uses SEQ_CONSISTENT ordering.
|
||||
*
|
||||
* @param ordering "The ordering, cannot be release or acquire-release."
|
||||
* @require ordering != RELEASE && ordering != ACQUIRE_RELEASE : "Release and acquire-release are not valid for load"
|
||||
**/
|
||||
<*
|
||||
Loads data atomically, by default this uses SEQ_CONSISTENT ordering.
|
||||
|
||||
@param ordering "The ordering, cannot be release or acquire-release."
|
||||
@require ordering != RELEASE && ordering != ACQUIRE_RELEASE : "Release and acquire-release are not valid for load"
|
||||
*>
|
||||
macro Type Atomic.load(&self, AtomicOrdering ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
@@ -28,12 +28,12 @@ macro Type Atomic.load(&self, AtomicOrdering ordering = SEQ_CONSISTENT)
|
||||
case RELEASE: unreachable("Invalid ordering.");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Stores data atomically, by default this uses SEQ_CONSISTENT ordering.
|
||||
*
|
||||
* @param ordering "The ordering, cannot be acquire or acquire-release."
|
||||
* @require ordering != ACQUIRE && ordering != ACQUIRE_RELEASE : "Acquire and acquire-release are not valid for store"
|
||||
**/
|
||||
<*
|
||||
Stores data atomically, by default this uses SEQ_CONSISTENT ordering.
|
||||
|
||||
@param ordering "The ordering, cannot be acquire or acquire-release."
|
||||
@require ordering != ACQUIRE && ordering != ACQUIRE_RELEASE : "Acquire and acquire-release are not valid for store"
|
||||
*>
|
||||
macro void Atomic.store(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
@@ -131,16 +131,16 @@ macro @atomic_exec(#func, data, value, ordering) @local
|
||||
module std::atomic;
|
||||
import std::math;
|
||||
|
||||
/**
|
||||
* @param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
* @param [in] y "the value to be added to ptr."
|
||||
* @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
* @return "returns the old value of ptr"
|
||||
*
|
||||
* @require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two."
|
||||
* @require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
|
||||
* @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
**/
|
||||
<*
|
||||
@param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
@param [in] y "the value to be added to ptr."
|
||||
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
|
||||
@require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two."
|
||||
@require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_add(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
|
||||
{
|
||||
$if $alignment == 0:
|
||||
@@ -149,16 +149,16 @@ macro fetch_add(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
|
||||
return $$atomic_fetch_add(ptr, y, $volatile, $ordering.ordinal, $alignment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
* @param [in] y "the value to be added to ptr."
|
||||
* @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
* @return "returns the old value of ptr"
|
||||
*
|
||||
* @require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two."
|
||||
* @require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
|
||||
* @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
**/
|
||||
<*
|
||||
@param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
@param [in] y "the value to be added to ptr."
|
||||
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
|
||||
@require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two."
|
||||
@require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_sub(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
|
||||
{
|
||||
$if $alignment == 0:
|
||||
@@ -167,15 +167,15 @@ macro fetch_sub(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
|
||||
return $$atomic_fetch_sub(ptr, y, $volatile, $ordering.ordinal, $alignment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
* @param [in] y "the value to be added to ptr."
|
||||
* @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
* @return "returns the old value of ptr"
|
||||
*
|
||||
* @require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
|
||||
* @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
**/
|
||||
<*
|
||||
@param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
@param [in] y "the value to be added to ptr."
|
||||
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
|
||||
@require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
var $load_ordering = $ordering;
|
||||
@@ -204,15 +204,15 @@ macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
return old_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
* @param [in] y "the value to be added to ptr."
|
||||
* @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
* @return "returns the old value of ptr"
|
||||
*
|
||||
* @require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
|
||||
* @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
**/
|
||||
<*
|
||||
@param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
@param [in] y "the value to be added to ptr."
|
||||
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
|
||||
@require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
var $load_ordering = $ordering;
|
||||
@@ -240,17 +240,17 @@ macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
return old_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
* @param [in] y "the value to be added to ptr."
|
||||
* @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
* @return "returns the old value of ptr"
|
||||
*
|
||||
* @require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two."
|
||||
* @require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
|
||||
* @require types::is_int($typeof(y)) "The value for or must be an int"
|
||||
* @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
**/
|
||||
<*
|
||||
@param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
@param [in] y "the value to be added to ptr."
|
||||
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
|
||||
@require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two."
|
||||
@require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
|
||||
@require types::is_int($typeof(y)) "The value for or must be an int"
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_or(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
|
||||
{
|
||||
$if types::is_int($typeof(*ptr)):
|
||||
@@ -283,17 +283,17 @@ macro fetch_or(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile
|
||||
return old_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
* @param [in] y "the value to be added to ptr."
|
||||
* @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
* @return "returns the old value of ptr"
|
||||
*
|
||||
* @require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two."
|
||||
* @require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
|
||||
* @require types::is_int($typeof(y)) "The value for or must be an int"
|
||||
* @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
**/
|
||||
<*
|
||||
@param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
@param [in] y "the value to be added to ptr."
|
||||
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
|
||||
@require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two."
|
||||
@require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
|
||||
@require types::is_int($typeof(y)) "The value for or must be an int"
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_xor(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
|
||||
{
|
||||
$if types::is_int($typeof(*ptr)):
|
||||
@@ -326,17 +326,17 @@ macro fetch_xor(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
|
||||
return old_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
* @param [in] y "the value to be added to ptr."
|
||||
* @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
* @return "returns the old value of ptr"
|
||||
*
|
||||
* @require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two."
|
||||
* @require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
|
||||
* @require types::is_int($typeof(y)) "The value for or must be an int"
|
||||
* @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
**/
|
||||
<*
|
||||
@param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
@param [in] y "the value to be added to ptr."
|
||||
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
|
||||
@require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two."
|
||||
@require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
|
||||
@require types::is_int($typeof(y)) "The value for or must be an int"
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_and(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
|
||||
{
|
||||
$if types::is_int($typeof(*ptr)):
|
||||
@@ -369,16 +369,16 @@ macro fetch_and(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
|
||||
return old_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
* @param [in] y "the value to be added to ptr."
|
||||
* @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
* @return "returns the old value of ptr"
|
||||
*
|
||||
* @require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
|
||||
* @require types::is_int($typeof(y)) "The value for or must be an int"
|
||||
* @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
**/
|
||||
<*
|
||||
@param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
@param [in] y "the value to be added to ptr."
|
||||
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
|
||||
@require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
|
||||
@require types::is_int($typeof(y)) "The value for or must be an int"
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_shift_right(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
var $load_ordering = $ordering;
|
||||
@@ -407,16 +407,16 @@ macro fetch_shift_right(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
return old_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
* @param [in] y "the value to be added to ptr."
|
||||
* @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
* @return "returns the old value of ptr"
|
||||
*
|
||||
* @require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
|
||||
* @require types::is_int($typeof(y)) "The value for or must be an int"
|
||||
* @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
**/
|
||||
<*
|
||||
@param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
@param [in] y "the value to be added to ptr."
|
||||
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
|
||||
@require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
|
||||
@require types::is_int($typeof(y)) "The value for or must be an int"
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_shift_left(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
var $load_ordering = $ordering;
|
||||
@@ -445,14 +445,14 @@ macro fetch_shift_left(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
return old_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
* @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
* @return "returns the old value of ptr"
|
||||
*
|
||||
* @require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
|
||||
* @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
**/
|
||||
<*
|
||||
@param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
|
||||
@require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
*>
|
||||
macro flag_set(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
$typeof(*ptr) old_value;
|
||||
@@ -465,14 +465,14 @@ macro flag_set(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
return old_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
* @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
* @return "returns the old value of ptr"
|
||||
*
|
||||
* @require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
|
||||
* @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
**/
|
||||
<*
|
||||
@param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
|
||||
@require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
*>
|
||||
macro flag_clear(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
$typeof(*ptr) old_value;
|
||||
@@ -485,15 +485,15 @@ macro flag_clear(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
return old_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
* @param [in] y "the value to be added to ptr."
|
||||
* @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
* @return "returns the old value of ptr"
|
||||
*
|
||||
* @require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
|
||||
* @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
**/
|
||||
<*
|
||||
@param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
@param [in] y "the value to be added to ptr."
|
||||
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
|
||||
@require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_max(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
|
||||
{
|
||||
$if $alignment == 0:
|
||||
@@ -502,15 +502,15 @@ macro fetch_max(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
|
||||
return $$atomic_fetch_max(ptr, y, $volatile, $ordering.ordinal, $alignment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
* @param [in] y "the value to be added to ptr."
|
||||
* @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
* @return "returns the old value of ptr"
|
||||
*
|
||||
* @require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
|
||||
* @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
**/
|
||||
<*
|
||||
@param [&in] ptr "the variable or dereferenced pointer to the data."
|
||||
@param [in] y "the value to be added to ptr."
|
||||
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
|
||||
@require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
|
||||
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_min(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
|
||||
{
|
||||
$if $alignment == 0:
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
module std::bits;
|
||||
|
||||
/**
|
||||
* @require types::is_intlike($typeof(i)) `The input must be an integer or integer vector`
|
||||
**/
|
||||
<*
|
||||
@require types::is_intlike($typeof(i)) `The input must be an integer or integer vector`
|
||||
*>
|
||||
macro reverse(i) => $$bitreverse(i);
|
||||
|
||||
/**
|
||||
* @require types::is_intlike($typeof(i)) `The input must be an integer or integer vector`
|
||||
**/
|
||||
<*
|
||||
@require types::is_intlike($typeof(i)) `The input must be an integer or integer vector`
|
||||
*>
|
||||
macro bswap(i) @builtin => $$bswap(i);
|
||||
|
||||
macro uint[<*>].popcount(self) => $$popcount(self);
|
||||
|
||||
@@ -16,19 +16,20 @@ struct AnyList (Printable)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param initial_capacity "The initial capacity to reserve"
|
||||
* Use `init` for to use a custom allocator.
|
||||
**/
|
||||
<*
|
||||
Use `init` for to use a custom allocator.
|
||||
|
||||
@param initial_capacity "The initial capacity to reserve"
|
||||
*>
|
||||
fn AnyList* AnyList.new_init(&self, usz initial_capacity = 16, Allocator allocator = null)
|
||||
{
|
||||
return self.init(allocator ?: allocator::heap(), initial_capacity) @inline;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] allocator "The allocator to use"
|
||||
* @param initial_capacity "The initial capacity to reserve"
|
||||
**/
|
||||
<*
|
||||
@param [&inout] allocator "The allocator to use"
|
||||
@param initial_capacity "The initial capacity to reserve"
|
||||
*>
|
||||
fn AnyList* AnyList.init(&self, Allocator allocator, usz initial_capacity = 16)
|
||||
{
|
||||
self.allocator = allocator;
|
||||
@@ -46,11 +47,11 @@ fn AnyList* AnyList.init(&self, Allocator allocator, usz initial_capacity = 16)
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the list using the temp allocator.
|
||||
*
|
||||
* @param initial_capacity "The initial capacity to reserve"
|
||||
**/
|
||||
<*
|
||||
Initialize the list using the temp allocator.
|
||||
|
||||
@param initial_capacity "The initial capacity to reserve"
|
||||
*>
|
||||
fn AnyList* AnyList.temp_init(&self, usz initial_capacity = 16)
|
||||
{
|
||||
return self.init(allocator::temp(), initial_capacity) @inline;
|
||||
@@ -78,20 +79,20 @@ fn usz! AnyList.to_format(&self, Formatter* formatter) @dynamic
|
||||
|
||||
fn String AnyList.to_new_string(&self, Allocator allocator = null) @dynamic
|
||||
{
|
||||
return string::format("%s", *self, .allocator = allocator ?: allocator::heap());
|
||||
return string::format("%s", *self, allocator: allocator ?: allocator::heap());
|
||||
}
|
||||
|
||||
|
||||
fn String AnyList.to_string(&self, Allocator allocator) @dynamic
|
||||
{
|
||||
return string::format("%s", *self, .allocator = allocator);
|
||||
return string::format("%s", *self, allocator: allocator);
|
||||
}
|
||||
|
||||
fn String AnyList.to_tstring(&self) => string::tformat("%s", *self);
|
||||
|
||||
/**
|
||||
* Push an element on the list by cloning it.
|
||||
**/
|
||||
<*
|
||||
Push an element on the list by cloning it.
|
||||
*>
|
||||
macro void AnyList.push(&self, element)
|
||||
{
|
||||
if (!self.allocator) self.allocator = allocator::heap();
|
||||
@@ -104,20 +105,20 @@ fn void AnyList.append_internal(&self, any element) @local
|
||||
self.entries[self.size++] = element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a retained element removed using *_retained.
|
||||
**/
|
||||
<*
|
||||
Free a retained element removed using *_retained.
|
||||
*>
|
||||
fn void AnyList.free_element(&self, any element) @inline
|
||||
{
|
||||
allocator::free(self.allocator, element.ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop a value who's type is known. If the type is incorrect, this
|
||||
* will still pop the element.
|
||||
*
|
||||
* @return! CastResult.TYPE_MISMATCH, IteratorResult.NO_MORE_ELEMENT
|
||||
**/
|
||||
<*
|
||||
Pop a value who's type is known. If the type is incorrect, this
|
||||
will still pop the element.
|
||||
|
||||
@return! CastResult.TYPE_MISMATCH, IteratorResult.NO_MORE_ELEMENT
|
||||
*>
|
||||
macro AnyList.pop(&self, $Type)
|
||||
{
|
||||
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
@@ -125,10 +126,10 @@ macro AnyList.pop(&self, $Type)
|
||||
return *anycast(self.entries[--self.size], $Type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop the last value and allocate the copy using the given allocator.
|
||||
* @return! IteratorResult.NO_MORE_ELEMENT
|
||||
**/
|
||||
<*
|
||||
Pop the last value and allocate the copy using the given allocator.
|
||||
@return! IteratorResult.NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any! AnyList.copy_pop(&self, Allocator allocator = allocator::heap())
|
||||
{
|
||||
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
@@ -136,33 +137,33 @@ fn any! AnyList.copy_pop(&self, Allocator allocator = allocator::heap())
|
||||
return allocator::clone_any(allocator, self.entries[--self.size]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop the last value and allocate the copy using the given allocator.
|
||||
* @return! IteratorResult.NO_MORE_ELEMENT
|
||||
* @deprecated `use copy_pop`
|
||||
**/
|
||||
<*
|
||||
Pop the last value and allocate the copy using the given allocator.
|
||||
@return! IteratorResult.NO_MORE_ELEMENT
|
||||
@deprecated `use copy_pop`
|
||||
*>
|
||||
fn any! AnyList.new_pop(&self, Allocator allocator = allocator::heap())
|
||||
{
|
||||
return self.copy_pop(allocator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop the last value and allocate the copy using the temp allocator
|
||||
* @return! IteratorResult.NO_MORE_ELEMENT
|
||||
* @deprecated `use tcopy_pop`
|
||||
**/
|
||||
<*
|
||||
Pop the last value and allocate the copy using the temp allocator
|
||||
@return! IteratorResult.NO_MORE_ELEMENT
|
||||
@deprecated `use tcopy_pop`
|
||||
*>
|
||||
fn any! AnyList.temp_pop(&self) => self.copy_pop(allocator::temp());
|
||||
|
||||
/**
|
||||
* Pop the last value and allocate the copy using the temp allocator
|
||||
* @return! IteratorResult.NO_MORE_ELEMENT
|
||||
**/
|
||||
<*
|
||||
Pop the last value and allocate the copy using the temp allocator
|
||||
@return! IteratorResult.NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any! AnyList.tcopy_pop(&self) => self.copy_pop(allocator::temp());
|
||||
|
||||
/**
|
||||
* Pop the last value. It must later be released using list.free_element()
|
||||
* @return! IteratorResult.NO_MORE_ELEMENT
|
||||
**/
|
||||
<*
|
||||
Pop the last value. It must later be released using list.free_element()
|
||||
@return! IteratorResult.NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any! AnyList.pop_retained(&self)
|
||||
{
|
||||
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
@@ -178,9 +179,9 @@ fn void AnyList.clear(&self)
|
||||
self.size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as pop() but pops the first value instead.
|
||||
**/
|
||||
<*
|
||||
Same as pop() but pops the first value instead.
|
||||
*>
|
||||
macro AnyList.pop_first(&self, $Type)
|
||||
{
|
||||
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
@@ -188,9 +189,9 @@ macro AnyList.pop_first(&self, $Type)
|
||||
return *anycast(self.entries[0], $Type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as pop_retained() but pops the first value instead.
|
||||
**/
|
||||
<*
|
||||
Same as pop_retained() but pops the first value instead.
|
||||
*>
|
||||
fn any! AnyList.pop_first_retained(&self)
|
||||
{
|
||||
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
@@ -198,18 +199,18 @@ fn any! AnyList.pop_first_retained(&self)
|
||||
return self.entries[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as new_pop() but pops the first value instead.
|
||||
* @deprecated `use copy_pop_first`
|
||||
**/
|
||||
<*
|
||||
Same as new_pop() but pops the first value instead.
|
||||
@deprecated `use copy_pop_first`
|
||||
*>
|
||||
fn any! AnyList.new_pop_first(&self, Allocator allocator = allocator::heap())
|
||||
{
|
||||
return self.copy_pop_first(allocator) @inline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as new_pop() but pops the first value instead.
|
||||
**/
|
||||
<*
|
||||
Same as new_pop() but pops the first value instead.
|
||||
*>
|
||||
fn any! AnyList.copy_pop_first(&self, Allocator allocator = allocator::heap())
|
||||
{
|
||||
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
@@ -218,20 +219,20 @@ fn any! AnyList.copy_pop_first(&self, Allocator allocator = allocator::heap())
|
||||
return allocator::clone_any(allocator, self.entries[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as temp_pop() but pops the first value instead.
|
||||
**/
|
||||
<*
|
||||
Same as temp_pop() but pops the first value instead.
|
||||
*>
|
||||
fn any! AnyList.tcopy_pop_first(&self) => self.copy_pop_first(allocator::temp());
|
||||
|
||||
/**
|
||||
* Same as temp_pop() but pops the first value instead.
|
||||
* @deprecated `use tcopy_pop_first`
|
||||
**/
|
||||
<*
|
||||
Same as temp_pop() but pops the first value instead.
|
||||
@deprecated `use tcopy_pop_first`
|
||||
*>
|
||||
fn any! AnyList.temp_pop_first(&self) => self.new_pop_first(allocator::temp());
|
||||
|
||||
/**
|
||||
* @require index < self.size
|
||||
**/
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
fn void AnyList.remove_at(&self, usz index)
|
||||
{
|
||||
if (!--self.size || index == self.size) return;
|
||||
@@ -249,9 +250,9 @@ fn void AnyList.add_all(&self, AnyList* other_list)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the elements in a list.
|
||||
**/
|
||||
<*
|
||||
Reverse the elements in a list.
|
||||
*>
|
||||
fn void AnyList.reverse(&self)
|
||||
{
|
||||
if (self.size < 2) return;
|
||||
@@ -268,26 +269,26 @@ fn any[] AnyList.array_view(&self)
|
||||
return self.entries[:self.size];
|
||||
}
|
||||
|
||||
/**
|
||||
* Push an element to the front of the list.
|
||||
**/
|
||||
<*
|
||||
Push an element to the front of the list.
|
||||
*>
|
||||
macro void AnyList.push_front(&self, type)
|
||||
{
|
||||
self.insert_at(0, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index < self.size
|
||||
**/
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
macro void AnyList.insert_at(&self, usz index, type) @local
|
||||
{
|
||||
any value = allocator::copy(self.allocator, type);
|
||||
self.insert_at_internal(self, index, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index < self.size
|
||||
**/
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
fn void AnyList.insert_at_internal(&self, usz index, any value) @local
|
||||
{
|
||||
self.ensure_capacity();
|
||||
@@ -300,17 +301,17 @@ fn void AnyList.insert_at_internal(&self, usz index, any value) @local
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @require self.size > 0
|
||||
**/
|
||||
<*
|
||||
@require self.size > 0
|
||||
*>
|
||||
fn void AnyList.remove_last(&self)
|
||||
{
|
||||
self.free_element(self.entries[--self.size]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.size > 0
|
||||
**/
|
||||
<*
|
||||
@require self.size > 0
|
||||
*>
|
||||
fn void AnyList.remove_first(&self)
|
||||
{
|
||||
self.remove_at(0);
|
||||
@@ -346,17 +347,17 @@ fn usz AnyList.len(&self) @operator(len) @inline
|
||||
return self.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index < self.size "Index out of range"
|
||||
**/
|
||||
<*
|
||||
@require index < self.size "Index out of range"
|
||||
*>
|
||||
macro AnyList.get(&self, usz index, $Type)
|
||||
{
|
||||
return *anycast(self.entries[index], $Type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index < self.size "Index out of range"
|
||||
**/
|
||||
<*
|
||||
@require index < self.size "Index out of range"
|
||||
*>
|
||||
fn any AnyList.get_any(&self, usz index) @inline
|
||||
{
|
||||
return self.entries[index];
|
||||
@@ -378,19 +379,19 @@ fn void AnyList.swap(&self, usz i, usz j)
|
||||
self.entries[j] = temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param filter "The function to determine if it should be removed or not"
|
||||
* @return "the number of deleted elements"
|
||||
**/
|
||||
<*
|
||||
@param filter "The function to determine if it should be removed or not"
|
||||
@return "the number of deleted elements"
|
||||
*>
|
||||
fn usz AnyList.remove_if(&self, AnyPredicate filter)
|
||||
{
|
||||
return self._remove_if(filter, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selection "The function to determine if it should be kept or not"
|
||||
* @return "the number of deleted elements"
|
||||
**/
|
||||
<*
|
||||
@param selection "The function to determine if it should be kept or not"
|
||||
@return "the number of deleted elements"
|
||||
*>
|
||||
fn usz AnyList.retain_if(&self, AnyPredicate selection)
|
||||
{
|
||||
return self._remove_if(selection, true);
|
||||
@@ -458,9 +459,9 @@ macro usz AnyList._remove_using_test(&self, AnyTest filter, bool $invert, ctx) @
|
||||
return size - self.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reserve at least min_capacity
|
||||
**/
|
||||
<*
|
||||
Reserve at least min_capacity
|
||||
*>
|
||||
fn void AnyList.reserve(&self, usz min_capacity)
|
||||
{
|
||||
if (!min_capacity) return;
|
||||
@@ -476,9 +477,9 @@ macro any AnyList.@item_at(&self, usz index) @operator([])
|
||||
return self.entries[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index <= self.size "Index out of range"
|
||||
**/
|
||||
<*
|
||||
@require index <= self.size "Index out of range"
|
||||
*>
|
||||
macro void AnyList.set(&self, usz index, value)
|
||||
{
|
||||
if (index == self.size)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @require SIZE > 0
|
||||
**/
|
||||
<*
|
||||
@require SIZE > 0
|
||||
*>
|
||||
module std::collections::bitset(<SIZE>);
|
||||
|
||||
def Type = uint;
|
||||
@@ -23,9 +23,9 @@ fn usz BitSet.cardinality(&self)
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require i < SIZE
|
||||
**/
|
||||
<*
|
||||
@require i < SIZE
|
||||
*>
|
||||
fn void BitSet.set(&self, usz i)
|
||||
{
|
||||
usz q = i / BITS;
|
||||
@@ -33,9 +33,9 @@ fn void BitSet.set(&self, usz i)
|
||||
self.data[q] |= 1 << r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require i < SIZE
|
||||
**/
|
||||
<*
|
||||
@require i < SIZE
|
||||
*>
|
||||
fn void BitSet.unset(&self, usz i)
|
||||
{
|
||||
usz q = i / BITS;
|
||||
@@ -43,9 +43,9 @@ fn void BitSet.unset(&self, usz i)
|
||||
self.data[q] &= ~(1 << r);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require i < SIZE
|
||||
**/
|
||||
<*
|
||||
@require i < SIZE
|
||||
*>
|
||||
fn bool BitSet.get(&self, usz i) @operator([]) @inline
|
||||
{
|
||||
usz q = i / BITS;
|
||||
@@ -58,18 +58,18 @@ fn usz BitSet.len(&self) @operator(len) @inline
|
||||
return SZ * BITS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require i < SIZE
|
||||
**/
|
||||
<*
|
||||
@require i < SIZE
|
||||
*>
|
||||
fn void BitSet.set_bool(&self, usz i, bool value) @operator([]=) @inline
|
||||
{
|
||||
if (value) return self.set(i);
|
||||
self.unset(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require Type.kindof == UNSIGNED_INT
|
||||
**/
|
||||
<*
|
||||
@require Type.kindof == UNSIGNED_INT
|
||||
*>
|
||||
module std::collections::growablebitset(<Type>);
|
||||
import std::collections::list;
|
||||
|
||||
@@ -82,10 +82,10 @@ struct GrowableBitSet
|
||||
GrowableBitSetList data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param initial_capacity
|
||||
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
||||
**/
|
||||
<*
|
||||
@param initial_capacity
|
||||
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
||||
*>
|
||||
fn GrowableBitSet* GrowableBitSet.new_init(&self, usz initial_capacity = 1, Allocator allocator = allocator::heap())
|
||||
{
|
||||
self.data.new_init(initial_capacity, allocator);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// 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.`
|
||||
**/
|
||||
<*
|
||||
@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;
|
||||
|
||||
@@ -41,12 +41,12 @@ fn usz! ElasticArray.to_format(&self, Formatter* formatter) @dynamic
|
||||
|
||||
fn String ElasticArray.to_string(&self, Allocator allocator) @dynamic
|
||||
{
|
||||
return string::format("%s", *self, .allocator = allocator);
|
||||
return string::format("%s", *self, allocator: allocator);
|
||||
}
|
||||
|
||||
fn String ElasticArray.to_new_string(&self, Allocator allocator = nul) @dynamic
|
||||
{
|
||||
return string::format("%s", *self, .allocator = allocator ?: allocator::heap());
|
||||
return string::format("%s", *self, allocator: allocator ?: allocator::heap());
|
||||
}
|
||||
|
||||
fn String ElasticArray.to_tstring(&self)
|
||||
@@ -60,9 +60,9 @@ fn void! ElasticArray.push_try(&self, Type element) @inline
|
||||
self.entries[self.size++] = element;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.size < MAX_SIZE `Tried to exceed the max size`
|
||||
**/
|
||||
<*
|
||||
@require self.size < MAX_SIZE `Tried to exceed the max size`
|
||||
*>
|
||||
fn void ElasticArray.push(&self, Type element) @inline
|
||||
{
|
||||
self.entries[self.size++] = element;
|
||||
@@ -79,9 +79,9 @@ fn void ElasticArray.clear(&self)
|
||||
self.size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.size > 0
|
||||
**/
|
||||
<*
|
||||
@require self.size > 0
|
||||
*>
|
||||
fn Type! ElasticArray.pop_first(&self)
|
||||
{
|
||||
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
@@ -89,18 +89,18 @@ fn Type! ElasticArray.pop_first(&self)
|
||||
return self.entries[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index < self.size
|
||||
**/
|
||||
<*
|
||||
@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
|
||||
**/
|
||||
<*
|
||||
@require other_list.size + self.size <= MAX_SIZE
|
||||
*>
|
||||
fn void ElasticArray.add_all(&self, ElasticArray* other_list)
|
||||
{
|
||||
if (!other_list.size) return;
|
||||
@@ -110,10 +110,10 @@ fn void ElasticArray.add_all(&self, ElasticArray* other_list)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add as many elements as possible to the new array,
|
||||
* returning the number of elements that didn't fit.
|
||||
**/
|
||||
<*
|
||||
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;
|
||||
@@ -125,12 +125,12 @@ fn usz ElasticArray.add_all_to_limit(&self, ElasticArray* other_list)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add as many values from this array as possible, returning the
|
||||
* number of elements that didn't fit.
|
||||
*
|
||||
* @param [in] array
|
||||
**/
|
||||
<*
|
||||
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;
|
||||
@@ -142,13 +142,13 @@ fn usz ElasticArray.add_array_to_limit(&self, Type[] array)
|
||||
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
|
||||
**/
|
||||
<*
|
||||
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;
|
||||
@@ -160,33 +160,33 @@ fn void ElasticArray.add_array(&self, Type[] array)
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* IMPORTANT The returned array must be freed using free_aligned.
|
||||
**/
|
||||
<*
|
||||
IMPORTANT The returned array must be freed using free_aligned.
|
||||
*>
|
||||
fn Type[] ElasticArray.to_new_aligned_array(&self)
|
||||
{
|
||||
return list_common::list_to_new_aligned_array(Type, self, allocator::heap());
|
||||
}
|
||||
|
||||
/**
|
||||
* IMPORTANT The returned array must be freed using free_aligned.
|
||||
**/
|
||||
<*
|
||||
IMPORTANT The returned array must be freed using free_aligned.
|
||||
*>
|
||||
fn Type[] ElasticArray.to_aligned_array(&self, Allocator allocator)
|
||||
{
|
||||
return list_common::list_to_new_aligned_array(Type, self, allocator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require !type_is_overaligned() : "This function is not available on overaligned types"
|
||||
**/
|
||||
<*
|
||||
@require !type_is_overaligned() : "This function is not available on overaligned types"
|
||||
*>
|
||||
macro Type[] ElasticArray.to_new_array(&self)
|
||||
{
|
||||
return list_common::list_to_array(Type, self, allocator::heap());
|
||||
}
|
||||
|
||||
/**
|
||||
* @require !type_is_overaligned() : "This function is not available on overaligned types"
|
||||
**/
|
||||
<*
|
||||
@require !type_is_overaligned() : "This function is not available on overaligned types"
|
||||
*>
|
||||
macro Type[] ElasticArray.to_array(&self, Allocator allocator)
|
||||
{
|
||||
return list_common::list_to_new_array(Type, self, allocator);
|
||||
@@ -201,9 +201,9 @@ fn Type[] ElasticArray.to_tarray(&self)
|
||||
$endif;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the elements in a list.
|
||||
**/
|
||||
<*
|
||||
Reverse the elements in a list.
|
||||
*>
|
||||
fn void ElasticArray.reverse(&self)
|
||||
{
|
||||
list_common::list_reverse(self);
|
||||
@@ -214,35 +214,35 @@ fn Type[] ElasticArray.array_view(&self)
|
||||
return self.entries[:self.size];
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.size < MAX_SIZE `List would exceed max 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`
|
||||
**/
|
||||
<*
|
||||
@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
|
||||
**/
|
||||
<*
|
||||
@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
|
||||
**/
|
||||
<*
|
||||
@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--)
|
||||
@@ -253,9 +253,9 @@ fn void ElasticArray.insert_at(&self, usz index, Type type)
|
||||
self.entries[index] = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index < self.size
|
||||
**/
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
fn void ElasticArray.set_at(&self, usz index, Type type)
|
||||
{
|
||||
self.entries[index] = type;
|
||||
@@ -310,19 +310,19 @@ 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"
|
||||
**/
|
||||
<*
|
||||
@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"
|
||||
**/
|
||||
<*
|
||||
@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);
|
||||
@@ -384,13 +384,13 @@ fn bool ElasticArray.equals(&self, ElasticArray other_list) @if(ELEMENT_IS_EQUAT
|
||||
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"
|
||||
**/
|
||||
<*
|
||||
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)
|
||||
@@ -400,31 +400,31 @@ fn bool ElasticArray.contains(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] self "The list to remove elements from"
|
||||
* @param value "The value to remove"
|
||||
* @return "true if the value was found"
|
||||
**/
|
||||
<*
|
||||
@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"
|
||||
**/
|
||||
<*
|
||||
@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."
|
||||
**/
|
||||
<*
|
||||
@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);
|
||||
@@ -438,10 +438,10 @@ fn void ElasticArray.remove_all_from(&self, ElasticArray* other_list) @if(ELEMEN
|
||||
foreach (v : other_list) self.remove_item(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] self
|
||||
* @return "The number non-null values in the list"
|
||||
**/
|
||||
<*
|
||||
@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;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
<*
|
||||
@require Enum.kindof == TypeKind.ENUM : "Only enums may be used with an enummap"
|
||||
*>
|
||||
module std::collections::enummap(<Enum, ValueType>);
|
||||
import std::io;
|
||||
struct EnumMap (Printable)
|
||||
@@ -19,7 +22,7 @@ fn usz! EnumMap.to_format(&self, Formatter* formatter) @dynamic
|
||||
foreach (i, &value : self.values)
|
||||
{
|
||||
if (i != 0) formatter.print(", ")!;
|
||||
n += formatter.printf("%s: %s", (Enum)i, *value)!;
|
||||
n += formatter.printf("%s: %s", Enum.from_ordinal(i), *value)!;
|
||||
}
|
||||
n += formatter.print(" }")!;
|
||||
return n;
|
||||
@@ -27,12 +30,12 @@ fn usz! EnumMap.to_format(&self, Formatter* formatter) @dynamic
|
||||
|
||||
fn String EnumMap.to_string(&self, Allocator allocator) @dynamic
|
||||
{
|
||||
return string::format("%s", *self, .allocator = allocator);
|
||||
return string::format("%s", *self, allocator: allocator);
|
||||
}
|
||||
|
||||
fn String EnumMap.to_new_string(&self, Allocator allocator = null) @dynamic
|
||||
{
|
||||
return string::format("%s", *self, .allocator = allocator ?: allocator::heap());
|
||||
return string::format("%s", *self, allocator: allocator ?: allocator::heap());
|
||||
}
|
||||
|
||||
fn String EnumMap.to_tstring(&self) @dynamic
|
||||
@@ -40,18 +43,18 @@ fn String EnumMap.to_tstring(&self) @dynamic
|
||||
return string::tformat("%s", *self);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return "The total size of this map, which is the same as the number of enum values"
|
||||
* @pure
|
||||
**/
|
||||
<*
|
||||
@return "The total size of this map, which is the same as the number of enum values"
|
||||
@pure
|
||||
*>
|
||||
fn usz EnumMap.len(&self) @operator(len) @inline
|
||||
{
|
||||
return self.values.len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return "Retrieve a value given the underlying enum, if there is no entry, then the zero value for the value is returned."
|
||||
**/
|
||||
<*
|
||||
@return "Retrieve a value given the underlying enum, if there is no entry, then the zero value for the value is returned."
|
||||
*>
|
||||
fn ValueType EnumMap.get(&self, Enum key) @operator([]) @inline
|
||||
{
|
||||
return self.values[key.ordinal];
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
// Use of self source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
|
||||
/**
|
||||
* @require Enum.kindof == TypeKind.ENUM : "Only enums maybe be used with an enumset"
|
||||
**/
|
||||
<*
|
||||
@require Enum.kindof == TypeKind.ENUM : "Only enums may be used with an enumset"
|
||||
*>
|
||||
module std::collections::enumset(<Enum>);
|
||||
import std::io;
|
||||
|
||||
@@ -16,9 +16,9 @@ distinct EnumSet (Printable) = EnumSetType;
|
||||
fn void EnumSet.add(&self, Enum v)
|
||||
{
|
||||
$if IS_CHAR_ARRAY:
|
||||
(*self)[(usz)v / 8] |= (char)(1u << ((usz)v % 8));
|
||||
(*self)[(usz)v.ordinal / 8] |= (char)(1u << ((usz)v.ordinal % 8));
|
||||
$else
|
||||
*self = (EnumSet)((EnumSetType)*self | 1u << (EnumSetType)v);
|
||||
*self = (EnumSet)((EnumSetType)*self | 1u << (EnumSetType)v.ordinal);
|
||||
$endif
|
||||
}
|
||||
|
||||
@@ -35,11 +35,11 @@ fn bool EnumSet.remove(&self, Enum v)
|
||||
{
|
||||
$if IS_CHAR_ARRAY:
|
||||
if (!self.has(v) @inline) return false;
|
||||
(*self)[(usz)v / 8] &= (char)~(1u << ((usz)v % 8));
|
||||
(*self)[(usz)v.ordinal / 8] &= (char)~(1u << ((usz)v.ordinal % 8));
|
||||
return true;
|
||||
$else
|
||||
EnumSetType old = (EnumSetType)*self;
|
||||
EnumSetType new = old & ~(1u << (EnumSetType)v);
|
||||
EnumSetType new = old & ~(1u << (EnumSetType)v.ordinal);
|
||||
*self = (EnumSet)new;
|
||||
return old != new;
|
||||
$endif
|
||||
@@ -48,9 +48,9 @@ fn bool EnumSet.remove(&self, Enum v)
|
||||
fn bool EnumSet.has(&self, Enum v)
|
||||
{
|
||||
$if IS_CHAR_ARRAY:
|
||||
return (bool)(((*self)[(usz)v / 8] << ((usz)v % 8)) & 0x01);
|
||||
return (bool)(((*self)[(usz)v.ordinal / 8] << ((usz)v.ordinal % 8)) & 0x01);
|
||||
$else
|
||||
return ((EnumSetType)*self & (1u << (EnumSetType)v)) != 0;
|
||||
return ((EnumSetType)*self & (1u << (EnumSetType)v.ordinal)) != 0;
|
||||
$endif
|
||||
}
|
||||
|
||||
@@ -143,12 +143,12 @@ fn usz! EnumSet.to_format(&set, Formatter* formatter) @dynamic
|
||||
|
||||
fn String EnumSet.to_new_string(&set, Allocator allocator = allocator::heap()) @dynamic
|
||||
{
|
||||
return string::format("%s", *set, .allocator = allocator);
|
||||
return string::format("%s", *set, allocator: allocator);
|
||||
}
|
||||
|
||||
fn String EnumSet.to_string(&set, Allocator allocator) @dynamic
|
||||
{
|
||||
return string::format("%s", *set, .allocator = allocator);
|
||||
return string::format("%s", *set, allocator: allocator);
|
||||
}
|
||||
|
||||
fn String EnumSet.to_tstring(&set) @dynamic
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
<*
|
||||
@require $defined(Key{}.hash()) `No .hash function found on the key`
|
||||
*>
|
||||
module std::collections::map(<Key, Value>);
|
||||
import std::math;
|
||||
|
||||
@@ -13,25 +16,25 @@ struct HashMap
|
||||
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"
|
||||
**/
|
||||
<*
|
||||
@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 = null)
|
||||
{
|
||||
return self.init(allocator ?: allocator::heap(), capacity, 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"
|
||||
**/
|
||||
<*
|
||||
@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.init(&self, Allocator allocator, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
capacity = math::next_power_of_2(capacity);
|
||||
@@ -42,41 +45,115 @@ fn HashMap* HashMap.init(&self, Allocator allocator, uint capacity = DEFAULT_INI
|
||||
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"
|
||||
**/
|
||||
<*
|
||||
@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.init(allocator::temp(), capacity, load_factor) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] allocator "The allocator to use"
|
||||
@require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values"
|
||||
@require capacity > 0 "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 "The load factor must be higher than 0"
|
||||
@require !self.allocator "Map was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
*>
|
||||
macro HashMap* HashMap.new_init_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
|
||||
{
|
||||
self.new_init(capacity, load_factor, allocator);
|
||||
$for (var $i = 0; $i < $vacount; $i += 2)
|
||||
self.set($vaarg[$i], $vaarg[$i+1]);
|
||||
$endfor
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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"
|
||||
**/
|
||||
<*
|
||||
@param [in] keys "The keys for the HashMap entries"
|
||||
@param [in] values "The values for the HashMap entries"
|
||||
@param [&inout] allocator "The allocator to use"
|
||||
@require keys.len == values.len "Both keys and values arrays must be the same length"
|
||||
@require capacity > 0 "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 "The load factor must be higher than 0"
|
||||
@require !self.allocator "Map was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn HashMap* HashMap.new_init_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
|
||||
{
|
||||
assert(keys.len == values.len);
|
||||
self.new_init(capacity, load_factor, allocator);
|
||||
for (usz i = 0; i < keys.len; i++)
|
||||
{
|
||||
self.set(keys[i], values[i]);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values"
|
||||
@require capacity > 0 "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 "The load factor must be higher than 0"
|
||||
@require !self.allocator "Map was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
*>
|
||||
macro HashMap* HashMap.temp_init_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
self.temp_init(capacity, load_factor);
|
||||
$for (var $i = 0; $i < $vacount; $i += 2)
|
||||
self.set($vaarg[$i], $vaarg[$i+1]);
|
||||
$endfor
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] keys "The keys for the HashMap entries"
|
||||
@param [in] values "The values for the HashMap entries"
|
||||
@param [&inout] allocator "The allocator to use"
|
||||
@require keys.len == values.len "Both keys and values arrays must be the same length"
|
||||
@require capacity > 0 "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 "The load factor must be higher than 0"
|
||||
@require !self.allocator "Map was already initialized"
|
||||
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn HashMap* HashMap.temp_init_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
|
||||
{
|
||||
assert(keys.len == values.len);
|
||||
self.temp_init(capacity, load_factor);
|
||||
for (usz i = 0; i < keys.len; i++)
|
||||
{
|
||||
self.set(keys[i], values[i]);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
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 [&in] other_map "The map to copy from."
|
||||
**/
|
||||
<*
|
||||
@param [&in] other_map "The map to copy from."
|
||||
*>
|
||||
fn HashMap* HashMap.new_init_from_map(&self, HashMap* other_map)
|
||||
{
|
||||
return self.init_from_map(other_map, allocator::heap()) @inline;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] allocator "The allocator to use"
|
||||
* @param [&in] other_map "The map to copy from."
|
||||
**/
|
||||
<*
|
||||
@param [&inout] allocator "The allocator to use"
|
||||
@param [&in] other_map "The map to copy from."
|
||||
*>
|
||||
fn HashMap* HashMap.init_from_map(&self, HashMap* other_map, Allocator allocator)
|
||||
{
|
||||
self.new_init(other_map.table.len, other_map.load_factor, allocator);
|
||||
@@ -84,9 +161,9 @@ fn HashMap* HashMap.init_from_map(&self, HashMap* other_map, Allocator allocator
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
return map.init_from_map(other_map, allocator::temp()) @inline;
|
||||
@@ -124,10 +201,10 @@ fn Entry*! HashMap.get_entry(&map, Key key)
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value or update and
|
||||
* @require $assignable(#expr, Value)
|
||||
**/
|
||||
<*
|
||||
Get the value or update and
|
||||
@require $assignable(#expr, Value)
|
||||
*>
|
||||
macro Value HashMap.@get_or_set(&map, Key key, Value #expr)
|
||||
{
|
||||
if (!map.count)
|
||||
@@ -221,12 +298,12 @@ fn Key[] HashMap.key_tlist(&map) @deprecated("Use 'tcopy_keys'")
|
||||
return map.copy_keys(allocator::temp()) @inline;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated "use copy_keys"
|
||||
**/
|
||||
<*
|
||||
@deprecated "use copy_keys"
|
||||
*>
|
||||
fn Key[] HashMap.key_new_list(&map, Allocator allocator = allocator::heap())
|
||||
{
|
||||
return map.copy_keys() @inline;
|
||||
return map.copy_keys(allocator) @inline;
|
||||
}
|
||||
|
||||
fn Key[] HashMap.copy_keys(&map, Allocator allocator = allocator::heap())
|
||||
@@ -239,7 +316,11 @@ fn Key[] HashMap.copy_keys(&map, Allocator allocator = allocator::heap())
|
||||
{
|
||||
while (entry)
|
||||
{
|
||||
list[index++] = entry.key;
|
||||
$if COPY_KEYS:
|
||||
list[index++] = entry.key.copy(allocator);
|
||||
$else
|
||||
list[index++] = entry.key;
|
||||
$endif
|
||||
entry = entry.next;
|
||||
}
|
||||
}
|
||||
@@ -268,9 +349,9 @@ macro HashMap.@each_entry(map; @body(entry))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated `use tcopy_values`
|
||||
**/
|
||||
<*
|
||||
@deprecated `use tcopy_values`
|
||||
*>
|
||||
fn Value[] HashMap.value_tlist(&map)
|
||||
{
|
||||
return map.copy_values(allocator::temp()) @inline;
|
||||
@@ -281,9 +362,9 @@ fn Value[] HashMap.tcopy_values(&map)
|
||||
return map.copy_values(allocator::temp()) @inline;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated `use copy_values`
|
||||
**/
|
||||
<*
|
||||
@deprecated `use copy_values`
|
||||
*>
|
||||
fn Value[] HashMap.value_new_list(&map, Allocator allocator = allocator::heap())
|
||||
{
|
||||
return map.copy_values(allocator);
|
||||
@@ -319,6 +400,21 @@ fn bool HashMap.has_value(&map, Value v) @if(VALUE_IS_EQUATABLE)
|
||||
return false;
|
||||
}
|
||||
|
||||
fn HashMapIterator HashMap.iter(&self)
|
||||
{
|
||||
return { .map = self, .index = -1 };
|
||||
}
|
||||
|
||||
fn HashMapValueIterator HashMap.value_iter(&self)
|
||||
{
|
||||
return { .map = self, .index = -1 };
|
||||
}
|
||||
|
||||
fn HashMapKeyIterator HashMap.key_iter(&self)
|
||||
{
|
||||
return { .map = self, .index = -1 };
|
||||
}
|
||||
|
||||
// --- private methods
|
||||
|
||||
fn void HashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
|
||||
@@ -374,8 +470,11 @@ fn void HashMap.put_all_for_create(&map, HashMap* other_map) @private
|
||||
if (!other_map.count) return;
|
||||
foreach (Entry *e : other_map.table)
|
||||
{
|
||||
if (!e) continue;
|
||||
map.put_for_create(e.key, e.value);
|
||||
while (e)
|
||||
{
|
||||
map.put_for_create(e.key, e.value);
|
||||
e = e.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,6 +500,7 @@ fn void HashMap.free_internal(&map, void* ptr) @inline @private
|
||||
|
||||
fn bool HashMap.remove_entry_for_key(&map, Key key) @private
|
||||
{
|
||||
if (!map.count) return false;
|
||||
uint hash = rehash(key.hash());
|
||||
uint i = index_for(hash, map.table.len);
|
||||
Entry* prev = map.table[i];
|
||||
@@ -447,3 +547,54 @@ fn void HashMap.free_entry(&self, Entry *entry) @local
|
||||
self.free_internal(entry);
|
||||
}
|
||||
|
||||
|
||||
struct HashMapIterator
|
||||
{
|
||||
HashMap* map;
|
||||
int top_index;
|
||||
int index;
|
||||
Entry* current_entry;
|
||||
}
|
||||
|
||||
distinct HashMapValueIterator = HashMapIterator;
|
||||
distinct HashMapKeyIterator = HashMapIterator;
|
||||
|
||||
|
||||
<*
|
||||
@require idx < self.map.count
|
||||
*>
|
||||
fn Entry HashMapIterator.get(&self, usz idx) @operator([])
|
||||
{
|
||||
if (idx < self.index)
|
||||
{
|
||||
self.top_index = 0;
|
||||
self.current_entry = null;
|
||||
self.index = -1;
|
||||
}
|
||||
while (self.index != idx)
|
||||
{
|
||||
if (self.current_entry)
|
||||
{
|
||||
self.current_entry = self.current_entry.next;
|
||||
if (self.current_entry) self.index++;
|
||||
continue;
|
||||
}
|
||||
self.current_entry = self.map.table[self.top_index++];
|
||||
if (self.current_entry) self.index++;
|
||||
}
|
||||
return *self.current_entry;
|
||||
}
|
||||
|
||||
fn Value HashMapValueIterator.get(&self, usz idx) @operator([])
|
||||
{
|
||||
return ((HashMapIterator*)self).get(idx).value;
|
||||
}
|
||||
|
||||
fn Key HashMapKeyIterator.get(&self, usz idx) @operator([])
|
||||
{
|
||||
return ((HashMapIterator*)self).get(idx).key;
|
||||
}
|
||||
|
||||
fn usz HashMapValueIterator.len(self) @operator(len) => self.map.count;
|
||||
fn usz HashMapKeyIterator.len(self) @operator(len) => self.map.count;
|
||||
fn usz HashMapIterator.len(self) @operator(len) => self.map.count;
|
||||
|
||||
@@ -20,19 +20,19 @@ struct LinkedList
|
||||
Node *_last;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
||||
* @return "the initialized list"
|
||||
**/
|
||||
<*
|
||||
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
||||
@return "the initialized list"
|
||||
*>
|
||||
fn LinkedList* LinkedList.init(&self, Allocator allocator)
|
||||
{
|
||||
*self = { .allocator = allocator };
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return "the initialized list"
|
||||
**/
|
||||
<*
|
||||
@return "the initialized list"
|
||||
*>
|
||||
fn LinkedList* LinkedList.new_init(&self)
|
||||
{
|
||||
return self.init(allocator::heap()) @inline;
|
||||
@@ -44,9 +44,9 @@ fn LinkedList* LinkedList.temp_init(&self)
|
||||
return self.init(allocator::temp()) @inline;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.allocator
|
||||
**/
|
||||
<*
|
||||
@require self.allocator
|
||||
*>
|
||||
macro void LinkedList.free_node(&self, Node* node) @private
|
||||
{
|
||||
allocator::free(self.allocator, node);
|
||||
@@ -124,9 +124,9 @@ fn void LinkedList.clear(&self)
|
||||
|
||||
fn usz LinkedList.len(&self) @inline => self.size;
|
||||
|
||||
/**
|
||||
* @require index < self.size
|
||||
**/
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
macro Node* LinkedList.node_at_index(&self, usz index)
|
||||
{
|
||||
if (index * 2 >= self.size)
|
||||
@@ -140,33 +140,33 @@ macro Node* LinkedList.node_at_index(&self, usz index)
|
||||
while (index--) node = node.next;
|
||||
return node;
|
||||
}
|
||||
/**
|
||||
* @require index < self.size
|
||||
**/
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
fn Type LinkedList.get(&self, usz index)
|
||||
{
|
||||
return self.node_at_index(index).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index < self.size
|
||||
**/
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
fn void LinkedList.set(&self, usz index, Type element)
|
||||
{
|
||||
self.node_at_index(index).value = element;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index < self.size
|
||||
**/
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
fn void LinkedList.remove_at(&self, usz index)
|
||||
{
|
||||
self.unlink(self.node_at_index(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index <= self.size
|
||||
**/
|
||||
<*
|
||||
@require index <= self.size
|
||||
*>
|
||||
fn void LinkedList.insert_at(&self, usz index, Type element)
|
||||
{
|
||||
switch (index)
|
||||
@@ -179,9 +179,9 @@ fn void LinkedList.insert_at(&self, usz index, Type element)
|
||||
self.link_before(self.node_at_index(index), element);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @require succ != null
|
||||
**/
|
||||
<*
|
||||
@require succ != null
|
||||
*>
|
||||
fn void LinkedList.link_before(&self, Node *succ, Type value) @private
|
||||
{
|
||||
Node* pred = succ.prev;
|
||||
@@ -199,9 +199,9 @@ fn void LinkedList.link_before(&self, Node *succ, Type value) @private
|
||||
self.size++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self._first
|
||||
**/
|
||||
<*
|
||||
@require self._first
|
||||
*>
|
||||
fn void LinkedList.unlink_first(&self) @private
|
||||
{
|
||||
Node* f = self._first;
|
||||
@@ -245,6 +245,11 @@ fn Type! LinkedList.pop(&self)
|
||||
return self._last.value;
|
||||
}
|
||||
|
||||
fn bool LinkedList.is_empty(&self)
|
||||
{
|
||||
return !self._first;
|
||||
}
|
||||
|
||||
fn Type! LinkedList.pop_front(&self)
|
||||
{
|
||||
if (!self._first) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
@@ -290,9 +295,9 @@ fn bool LinkedList.remove_last_match(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* @require self._last
|
||||
**/
|
||||
<*
|
||||
@require self._last
|
||||
*>
|
||||
fn void LinkedList.unlink_last(&self) @inline @private
|
||||
{
|
||||
Node* l = self._last;
|
||||
@@ -310,9 +315,9 @@ fn void LinkedList.unlink_last(&self) @inline @private
|
||||
self.size--;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require x != null
|
||||
**/
|
||||
<*
|
||||
@require x != null
|
||||
*>
|
||||
fn void LinkedList.unlink(&self, Node* x) @private
|
||||
{
|
||||
Node* next = x.next;
|
||||
|
||||
@@ -19,10 +19,10 @@ struct List (Printable)
|
||||
Type *entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param initial_capacity "The initial capacity to reserve"
|
||||
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
||||
**/
|
||||
<*
|
||||
@param initial_capacity "The initial capacity to reserve"
|
||||
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
|
||||
*>
|
||||
fn List* List.new_init(&self, usz initial_capacity = 16, Allocator allocator = allocator::heap())
|
||||
{
|
||||
self.allocator = allocator;
|
||||
@@ -33,22 +33,22 @@ fn List* List.new_init(&self, usz initial_capacity = 16, Allocator allocator = a
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the list using the temp allocator.
|
||||
*
|
||||
* @param initial_capacity "The initial capacity to reserve"
|
||||
**/
|
||||
<*
|
||||
Initialize the list using the temp allocator.
|
||||
|
||||
@param initial_capacity "The initial capacity to reserve"
|
||||
*>
|
||||
fn List* List.temp_init(&self, usz initial_capacity = 16)
|
||||
{
|
||||
return self.new_init(initial_capacity, allocator::temp()) @inline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a new list with an array.
|
||||
*
|
||||
* @param [in] values `The values to initialize the list with.`
|
||||
* @require self.size == 0 "The List must be empty"
|
||||
**/
|
||||
<*
|
||||
Initialize a new list with an array.
|
||||
|
||||
@param [in] values `The values to initialize the list with.`
|
||||
@require self.size == 0 "The List must be empty"
|
||||
*>
|
||||
fn List* List.new_init_with_array(&self, Type[] values, Allocator allocator = allocator::heap())
|
||||
{
|
||||
self.new_init(values.len, allocator) @inline;
|
||||
@@ -56,12 +56,12 @@ fn List* List.new_init_with_array(&self, Type[] values, Allocator allocator = al
|
||||
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"
|
||||
**/
|
||||
<*
|
||||
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;
|
||||
@@ -69,9 +69,9 @@ fn List* List.temp_init_with_array(&self, Type[] values)
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.capacity == 0 "The List must not be allocated"
|
||||
**/
|
||||
<*
|
||||
@require self.capacity == 0 "The List must not be allocated"
|
||||
*>
|
||||
fn void List.init_wrapping_array(&self, Type[] types, Allocator allocator = allocator::heap())
|
||||
{
|
||||
self.allocator = allocator;
|
||||
@@ -102,7 +102,7 @@ fn usz! List.to_format(&self, Formatter* formatter) @dynamic
|
||||
|
||||
fn String List.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
|
||||
{
|
||||
return string::format("%s", *self, .allocator = allocator);
|
||||
return string::format("%s", *self, allocator: allocator);
|
||||
}
|
||||
|
||||
fn String List.to_tstring(&self)
|
||||
@@ -128,9 +128,6 @@ fn void List.clear(&self)
|
||||
self.set_size(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.size > 0
|
||||
**/
|
||||
fn Type! List.pop_first(&self)
|
||||
{
|
||||
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
@@ -138,9 +135,9 @@ fn Type! List.pop_first(&self)
|
||||
return self.entries[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index < self.size
|
||||
**/
|
||||
<*
|
||||
@require index < self.size `Removed element out of bounds`
|
||||
*>
|
||||
fn void List.remove_at(&self, usz index)
|
||||
{
|
||||
self.set_size(self.size - 1);
|
||||
@@ -160,17 +157,17 @@ fn void List.add_all(&self, List* other_list)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* IMPORTANT The returned array must be freed using free_aligned.
|
||||
**/
|
||||
<*
|
||||
IMPORTANT The returned array must be freed using free_aligned.
|
||||
*>
|
||||
fn Type[] List.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"
|
||||
**/
|
||||
<*
|
||||
@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);
|
||||
@@ -185,9 +182,9 @@ fn Type[] List.to_tarray(&self)
|
||||
$endif;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the elements in a list.
|
||||
**/
|
||||
<*
|
||||
Reverse the elements in a list.
|
||||
*>
|
||||
fn void List.reverse(&self)
|
||||
{
|
||||
list_common::list_reverse(self);
|
||||
@@ -198,12 +195,12 @@ fn Type[] List.array_view(&self)
|
||||
return self.entries[:self.size];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the values of an array to this list.
|
||||
*
|
||||
* @param [in] array
|
||||
* @ensure self.size >= array.len
|
||||
**/
|
||||
<*
|
||||
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)
|
||||
{
|
||||
if (!array.len) return;
|
||||
@@ -217,9 +214,9 @@ fn void List.push_front(&self, Type type) @inline
|
||||
self.insert_at(0, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index <= self.size
|
||||
**/
|
||||
<*
|
||||
@require index <= self.size `Insert was out of bounds`
|
||||
*>
|
||||
fn void List.insert_at(&self, usz index, Type type)
|
||||
{
|
||||
self.reserve(1);
|
||||
@@ -231,9 +228,9 @@ fn void List.insert_at(&self, usz index, Type type)
|
||||
self.entries[index] = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index < self.size
|
||||
**/
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
fn void List.set_at(&self, usz index, Type type)
|
||||
{
|
||||
self.entries[index] = type;
|
||||
@@ -278,6 +275,9 @@ fn usz List.len(&self) @operator(len) @inline
|
||||
return self.size;
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.size `Access out of bounds`
|
||||
*>
|
||||
fn Type List.get(&self, usz index) @inline
|
||||
{
|
||||
return self.entries[index];
|
||||
@@ -299,24 +299,27 @@ fn void List.free(&self)
|
||||
self.entries = null;
|
||||
}
|
||||
|
||||
<*
|
||||
@require i < self.size && j < self.size `Access out of bounds`
|
||||
*>
|
||||
fn void List.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"
|
||||
**/
|
||||
<*
|
||||
@param filter "The function to determine if it should be removed or not"
|
||||
@return "the number of deleted elements"
|
||||
*>
|
||||
fn usz List.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"
|
||||
**/
|
||||
<*
|
||||
@param selection "The function to determine if it should be kept or not"
|
||||
@return "the number of deleted elements"
|
||||
*>
|
||||
fn usz List.retain_if(&self, ElementPredicate selection)
|
||||
{
|
||||
return list_common::list_remove_if(self, selection, true);
|
||||
@@ -352,7 +355,7 @@ fn void List.ensure_capacity(&self, usz min_capacity) @local
|
||||
|
||||
min_capacity = math::next_power_of_2(min_capacity);
|
||||
$if type_is_overaligned():
|
||||
self.entries = allocator::realloc_aligned(self.allocator, self.entries, Type.sizeof * min_capacity, .alignment = Type[1].alignof)!!;
|
||||
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;
|
||||
@@ -361,16 +364,25 @@ fn void List.ensure_capacity(&self, usz min_capacity) @local
|
||||
self.post_alloc(); // Add sanitizer annotation
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.size `Access out of bounds`
|
||||
*>
|
||||
macro Type List.@item_at(&self, usz index) @operator([])
|
||||
{
|
||||
return self.entries[index];
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.size `Access out of bounds`
|
||||
*>
|
||||
fn Type* List.get_ref(&self, usz index) @operator(&[]) @inline
|
||||
{
|
||||
return &self.entries[index];
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.size `Access out of bounds`
|
||||
*>
|
||||
fn void List.set(&self, usz index, Type value) @operator([]=)
|
||||
{
|
||||
self.entries[index] = value;
|
||||
@@ -395,9 +407,9 @@ fn void List._update_size_change(&self,usz old_size, usz new_size)
|
||||
&self.entries[old_size],
|
||||
&self.entries[new_size]);
|
||||
}
|
||||
/**
|
||||
* @require new_size == 0 || self.capacity != 0
|
||||
**/
|
||||
<*
|
||||
@require new_size == 0 || self.capacity != 0
|
||||
*>
|
||||
fn usz List.set_size(&self, usz new_size) @inline @private
|
||||
{
|
||||
usz old_size = self.size;
|
||||
@@ -412,9 +424,9 @@ macro void List.pre_free(&self) @private
|
||||
self._update_size_change(self.size, self.capacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.capacity
|
||||
**/
|
||||
<*
|
||||
@require self.capacity
|
||||
*>
|
||||
macro void List.post_alloc(&self) @private
|
||||
{
|
||||
self._update_size_change(self.capacity, self.size);
|
||||
@@ -451,13 +463,13 @@ fn bool List.equals(&self, List other_list) @if(ELEMENT_IS_EQUATABLE)
|
||||
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"
|
||||
**/
|
||||
<*
|
||||
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 List.contains(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
|
||||
{
|
||||
foreach (i, v : self)
|
||||
@@ -467,30 +479,30 @@ fn bool List.contains(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] self "The list to remove elements from"
|
||||
* @param value "The value to remove"
|
||||
* @return "true if the value was found"
|
||||
**/
|
||||
<*
|
||||
@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"
|
||||
**/
|
||||
<*
|
||||
@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 value "The value to remove"
|
||||
* @return "the number of deleted elements."
|
||||
**/
|
||||
<*
|
||||
@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_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
|
||||
{
|
||||
usz old_size = self.size;
|
||||
@@ -512,10 +524,10 @@ fn void List.remove_all_from(&self, List* other_list) @if(ELEMENT_IS_EQUATABLE)
|
||||
foreach (v : other_list) self.remove_item(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] self
|
||||
* @return "The number non-null values in the list"
|
||||
**/
|
||||
<*
|
||||
@param [&in] self
|
||||
@return "The number non-null values in the list"
|
||||
*>
|
||||
fn usz List.compact_count(&self) @if(ELEMENT_IS_POINTER)
|
||||
{
|
||||
usz vals = 0;
|
||||
@@ -534,32 +546,32 @@ fn usz List.compact(&self) @if(ELEMENT_IS_POINTER)
|
||||
|
||||
// --> Deprecated
|
||||
|
||||
/**
|
||||
* @param [&inout] self "The list to remove elements from"
|
||||
* @param value "The value to remove"
|
||||
* @return "true if the value was found"
|
||||
**/
|
||||
<*
|
||||
@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_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"
|
||||
**/
|
||||
<*
|
||||
@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."
|
||||
**/
|
||||
<*
|
||||
@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;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
module std::collections::list_common;
|
||||
|
||||
/**
|
||||
* IMPORTANT The returned array must be freed using free_aligned.
|
||||
**/
|
||||
<*
|
||||
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[] {};
|
||||
|
||||
@@ -21,11 +21,11 @@ struct MapImpl
|
||||
float load_factor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require capacity > 0 "The capacity must be 1 or higher"
|
||||
* @require load_factor > 0.0 "The load factor must be higher than 0"
|
||||
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
**/
|
||||
<*
|
||||
@require capacity > 0 "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 "The load factor must be higher than 0"
|
||||
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn Map new(uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
|
||||
{
|
||||
MapImpl* map = allocator::alloc(allocator, MapImpl);
|
||||
@@ -33,11 +33,11 @@ fn Map new(uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT
|
||||
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 capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
**/
|
||||
<*
|
||||
@require capacity > 0 "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 "The load factor must be higher than 0"
|
||||
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn Map temp(uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
MapImpl* map = mem::temp_alloc(MapImpl);
|
||||
@@ -45,30 +45,104 @@ fn Map temp(uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAUL
|
||||
return (Map)map;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] other_map "The map to copy from."
|
||||
**/
|
||||
<*
|
||||
@param [&inout] allocator "The allocator to use"
|
||||
@require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values"
|
||||
@require capacity > 0 "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 "The load factor must be higher than 0"
|
||||
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
*>
|
||||
macro Map new_init_with_key_values(..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
|
||||
{
|
||||
Map map = new(capacity, load_factor, allocator);
|
||||
$for (var $i = 0; $i < $vacount; $i += 2)
|
||||
map.set($vaarg[$i], $vaarg[$i+1]);
|
||||
$endfor
|
||||
return map;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] keys "Array of keys for the Map entries"
|
||||
@param [in] values "Array of values for the Map entries"
|
||||
@param [&inout] allocator "The allocator to use"
|
||||
@require keys.len == values.len "Both keys and values arrays must be the same length"
|
||||
@require capacity > 0 "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 "The load factor must be higher than 0"
|
||||
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn Map new_init_from_keys_and_values(Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
|
||||
{
|
||||
assert(keys.len == values.len);
|
||||
Map map = new(capacity, load_factor, allocator);
|
||||
for (usz i = 0; i < keys.len; i++)
|
||||
{
|
||||
map.set(keys[i], values[i]);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
<*
|
||||
@require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values"
|
||||
@require capacity > 0 "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 "The load factor must be higher than 0"
|
||||
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
*>
|
||||
macro Map temp_new_with_key_values(..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
Map map = temp(capacity, load_factor);
|
||||
$for (var $i = 0; $i < $vacount; $i += 2)
|
||||
map.set($vaarg[$i], $vaarg[$i+1]);
|
||||
$endfor
|
||||
return map;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] keys "The keys for the HashMap entries"
|
||||
@param [in] values "The values for the HashMap entries"
|
||||
@param [&inout] allocator "The allocator to use"
|
||||
@require keys.len == values.len "Both keys and values arrays must be the same length"
|
||||
@require capacity > 0 "The capacity must be 1 or higher"
|
||||
@require load_factor > 0.0 "The load factor must be higher than 0"
|
||||
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
*>
|
||||
fn Map temp_init_from_keys_and_values(Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
|
||||
{
|
||||
assert(keys.len == values.len);
|
||||
Map map = temp(capacity, load_factor);
|
||||
for (usz i = 0; i < keys.len; i++)
|
||||
{
|
||||
map.set(keys[i], values[i]);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] other_map "The map to copy from."
|
||||
*>
|
||||
fn Map new_from_map(Map other_map, Allocator allocator = null)
|
||||
{
|
||||
MapImpl* other_map_impl = (MapImpl*)other_map;
|
||||
if (!other_map_impl)
|
||||
{
|
||||
if (allocator) return new(.allocator = allocator);
|
||||
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);
|
||||
}
|
||||
{
|
||||
while (e)
|
||||
{
|
||||
map._put_for_create(e.key, e.value);
|
||||
e = e.next;
|
||||
}
|
||||
}
|
||||
return (Map)map;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] other_map "The map to copy from."
|
||||
**/
|
||||
<*
|
||||
@param [&in] other_map "The map to copy from."
|
||||
*>
|
||||
fn Map temp_from_map(Map other_map)
|
||||
{
|
||||
return new_from_map(other_map, allocator::temp());
|
||||
@@ -108,10 +182,10 @@ fn Entry*! Map.get_entry(map, Key key)
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value or update and
|
||||
* @require $assignable(#expr, Value)
|
||||
**/
|
||||
<*
|
||||
Get the value or update and
|
||||
@require $assignable(#expr, Value)
|
||||
*>
|
||||
macro Value Map.@get_or_set(&self, Key key, Value #expr)
|
||||
{
|
||||
MapImpl *map = (MapImpl*)*self;
|
||||
@@ -350,9 +424,9 @@ fn void _init(MapImpl* impl, uint capacity, float load_factor, Allocator allocat
|
||||
capacity = math::next_power_of_2(capacity);
|
||||
*impl = {
|
||||
.allocator = allocator,
|
||||
.load_factor = load_factor,
|
||||
.threshold = (uint)(capacity * load_factor),
|
||||
.table = allocator::new_array(allocator, Entry*, capacity)
|
||||
.load_factor = load_factor,
|
||||
.threshold = (uint)(capacity * load_factor),
|
||||
.table = allocator::new_array(allocator, Entry*, capacity)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,43 @@
|
||||
module std::collections::maybe(<Type>);
|
||||
import std::io;
|
||||
|
||||
struct Maybe
|
||||
struct Maybe (Printable)
|
||||
{
|
||||
Type value;
|
||||
bool has_value;
|
||||
}
|
||||
|
||||
fn usz! Maybe.to_format(&self, Formatter* f) @dynamic
|
||||
{
|
||||
if (self.has_value) return f.printf("[%s]", self.value);
|
||||
return f.printf("[EMPTY]");
|
||||
}
|
||||
|
||||
fn void Maybe.set(&self, Type val)
|
||||
{
|
||||
*self = { .value = val, .has_value = true };
|
||||
}
|
||||
|
||||
fn void Maybe.reset(&self)
|
||||
{
|
||||
*self = {};
|
||||
}
|
||||
|
||||
fn Maybe value(Type val)
|
||||
{
|
||||
return { .value = val, .has_value = true };
|
||||
}
|
||||
|
||||
fn Maybe Maybe.with_value(Type val) @operator(construct)
|
||||
{
|
||||
return { .value = val, .has_value = true };
|
||||
}
|
||||
|
||||
fn Maybe Maybe.empty() @operator(construct)
|
||||
{
|
||||
return { };
|
||||
}
|
||||
|
||||
const Maybe EMPTY = { };
|
||||
|
||||
macro Type! Maybe.get(self)
|
||||
|
||||
@@ -50,7 +50,7 @@ fn usz! Object.to_format(&self, Formatter* formatter) @dynamic
|
||||
usz n = formatter.printf("{")!;
|
||||
@stack_mem(1024; Allocator mem)
|
||||
{
|
||||
foreach (i, key : self.map.key_new_list(mem))
|
||||
foreach (i, key : self.map.copy_keys(mem))
|
||||
{
|
||||
if (i > 0) n += formatter.printf(",")!;
|
||||
n += formatter.printf(`"%s":`, key)!;
|
||||
@@ -63,11 +63,11 @@ fn usz! Object.to_format(&self, Formatter* formatter) @dynamic
|
||||
switch (self.type.kindof)
|
||||
{
|
||||
case SIGNED_INT:
|
||||
return formatter.printf("%d", self.i)!;
|
||||
return formatter.printf("%d", (int128)self.i)!;
|
||||
case UNSIGNED_INT:
|
||||
return formatter.printf("%d", (uint128)self.i)!;
|
||||
case FLOAT:
|
||||
return formatter.printf("%d", self.f)!;
|
||||
return formatter.printf("%g", self.f)!;
|
||||
case ENUM:
|
||||
return formatter.printf("%d", self.i)!;
|
||||
default:
|
||||
@@ -128,9 +128,9 @@ fn void Object.free(&self)
|
||||
self.array.free();
|
||||
case ObjectInternalMap:
|
||||
self.map.@each_entry(; ObjectInternalMapEntry* entry) {
|
||||
allocator::free(self.allocator, entry.key);
|
||||
entry.value.free();
|
||||
};
|
||||
self.map.free();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -148,43 +148,42 @@ fn bool Object.is_int(&self) @inline => self.type == int128.typeid;
|
||||
fn bool Object.is_keyable(&self) => self.is_empty() || self.is_map();
|
||||
fn bool Object.is_indexable(&self) => self.is_empty() || self.is_array();
|
||||
|
||||
/**
|
||||
* @require self.is_keyable()
|
||||
**/
|
||||
<*
|
||||
@require self.is_keyable()
|
||||
*>
|
||||
fn void Object.init_map_if_needed(&self) @private
|
||||
{
|
||||
if (self.is_empty())
|
||||
{
|
||||
self.type = ObjectInternalMap.typeid;
|
||||
self.map.new_init(.allocator = self.allocator);
|
||||
self.map.new_init(allocator: self.allocator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.is_indexable()
|
||||
**/
|
||||
<*
|
||||
@require self.is_indexable()
|
||||
*>
|
||||
fn void Object.init_array_if_needed(&self) @private
|
||||
{
|
||||
if (self.is_empty())
|
||||
{
|
||||
self.type = ObjectInternalList.typeid;
|
||||
self.array.new_init(.allocator = self.allocator);
|
||||
self.array.new_init(allocator: self.allocator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.is_keyable()
|
||||
**/
|
||||
<*
|
||||
@require self.is_keyable()
|
||||
*>
|
||||
fn void Object.set_object(&self, String key, Object* new_object) @private
|
||||
{
|
||||
self.init_map_if_needed();
|
||||
ObjectInternalMapEntry*! entry = self.map.get_entry(key);
|
||||
defer
|
||||
{
|
||||
(void)allocator::free(self.allocator, entry.key);
|
||||
(void)entry.value.free();
|
||||
}
|
||||
self.map.set(key.copy(self.map.allocator), new_object);
|
||||
self.map.set(key, new_object);
|
||||
}
|
||||
|
||||
macro Object* Object.object_from_value(&self, value) @private
|
||||
@@ -219,9 +218,9 @@ macro Object* Object.set(&self, String key, value)
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.is_indexable()
|
||||
**/
|
||||
<*
|
||||
@require self.is_indexable()
|
||||
*>
|
||||
macro Object* Object.set_at(&self, usz index, String key, value)
|
||||
{
|
||||
Object* val = self.object_from_value(value);
|
||||
@@ -229,10 +228,10 @@ macro Object* Object.set_at(&self, usz index, String key, value)
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.is_indexable()
|
||||
* @ensure return != null
|
||||
**/
|
||||
<*
|
||||
@require self.is_indexable()
|
||||
@ensure return != null
|
||||
*>
|
||||
macro Object* Object.push(&self, value)
|
||||
{
|
||||
Object* val = self.object_from_value(value);
|
||||
@@ -240,42 +239,42 @@ macro Object* Object.push(&self, value)
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.is_keyable()
|
||||
**/
|
||||
<*
|
||||
@require self.is_keyable()
|
||||
*>
|
||||
fn Object*! Object.get(&self, String key) => self.is_empty() ? SearchResult.MISSING? : self.map.get(key);
|
||||
|
||||
|
||||
fn bool Object.has_key(&self, String key) => self.is_map() && self.map.has_key(key);
|
||||
|
||||
/**
|
||||
* @require self.is_indexable()
|
||||
**/
|
||||
<*
|
||||
@require self.is_indexable()
|
||||
*>
|
||||
fn Object* Object.get_at(&self, usz index)
|
||||
{
|
||||
return self.array.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.is_indexable()
|
||||
**/
|
||||
<*
|
||||
@require self.is_indexable()
|
||||
*>
|
||||
fn usz Object.get_len(&self)
|
||||
{
|
||||
return self.array.len();
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.is_indexable()
|
||||
**/
|
||||
<*
|
||||
@require self.is_indexable()
|
||||
*>
|
||||
fn void Object.push_object(&self, Object* to_append)
|
||||
{
|
||||
self.init_array_if_needed();
|
||||
self.array.push(to_append);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.is_indexable()
|
||||
**/
|
||||
<*
|
||||
@require self.is_indexable()
|
||||
*>
|
||||
fn void Object.set_object_at(&self, usz index, Object* to_set)
|
||||
{
|
||||
self.init_array_if_needed();
|
||||
@@ -292,9 +291,9 @@ fn void Object.set_object_at(&self, usz index, Object* to_set)
|
||||
self.array.set_at(index, to_set);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $Type.kindof.is_int() "Expected an integer type."
|
||||
**/
|
||||
<*
|
||||
@require $Type.kindof.is_int() "Expected an integer type."
|
||||
*>
|
||||
macro get_integer_value(Object* value, $Type)
|
||||
{
|
||||
if (value.is_float())
|
||||
@@ -314,19 +313,19 @@ macro get_integer_value(Object* value, $Type)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @require self.is_indexable()
|
||||
* @require $Type.kindof.is_int() : "Expected an integer type"
|
||||
**/
|
||||
<*
|
||||
@require self.is_indexable()
|
||||
@require $Type.kindof.is_int() : "Expected an integer type"
|
||||
*>
|
||||
macro Object.get_integer_at(&self, $Type, usz index) @private
|
||||
{
|
||||
return get_integer_value(self.get_at(index), $Type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.is_keyable()
|
||||
* @require $Type.kindof.is_int() : "Expected an integer type"
|
||||
**/
|
||||
<*
|
||||
@require self.is_keyable()
|
||||
@require $Type.kindof.is_int() : "Expected an integer type"
|
||||
*>
|
||||
macro Object.get_integer(&self, $Type, String key) @private
|
||||
{
|
||||
return get_integer_value(self.get(key), $Type);
|
||||
@@ -356,9 +355,9 @@ fn uint! Object.get_uint_at(&self, usz index) => self.get_integer_at(uint, index
|
||||
fn ulong! Object.get_ulong_at(&self, usz index) => self.get_integer_at(ulong, index);
|
||||
fn uint128! Object.get_uint128_at(&self, usz index) => self.get_integer_at(uint128, index);
|
||||
|
||||
/**
|
||||
* @require self.is_keyable()
|
||||
**/
|
||||
<*
|
||||
@require self.is_keyable()
|
||||
*>
|
||||
fn String! Object.get_string(&self, String key)
|
||||
{
|
||||
Object* value = self.get(key)!;
|
||||
@@ -366,9 +365,9 @@ fn String! Object.get_string(&self, String key)
|
||||
return value.s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.is_indexable()
|
||||
**/
|
||||
<*
|
||||
@require self.is_indexable()
|
||||
*>
|
||||
fn String! Object.get_string_at(&self, usz index)
|
||||
{
|
||||
Object* value = self.get_at(index);
|
||||
@@ -376,9 +375,9 @@ fn String! Object.get_string_at(&self, usz index)
|
||||
return value.s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.is_keyable()
|
||||
**/
|
||||
<*
|
||||
@require self.is_keyable()
|
||||
*>
|
||||
macro String! Object.get_enum(&self, $EnumType, String key)
|
||||
{
|
||||
Object value = self.get(key)!;
|
||||
@@ -386,9 +385,9 @@ macro String! Object.get_enum(&self, $EnumType, String key)
|
||||
return ($EnumType)value.i;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.is_indexable()
|
||||
**/
|
||||
<*
|
||||
@require self.is_indexable()
|
||||
*>
|
||||
macro String! Object.get_enum_at(&self, $EnumType, usz index)
|
||||
{
|
||||
Object value = self.get_at(index);
|
||||
@@ -396,9 +395,9 @@ macro String! Object.get_enum_at(&self, $EnumType, usz index)
|
||||
return ($EnumType)value.i;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.is_keyable()
|
||||
**/
|
||||
<*
|
||||
@require self.is_keyable()
|
||||
*>
|
||||
fn bool! Object.get_bool(&self, String key)
|
||||
{
|
||||
Object* value = self.get(key)!;
|
||||
@@ -407,9 +406,9 @@ fn bool! Object.get_bool(&self, String key)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @require self.is_indexable()
|
||||
**/
|
||||
<*
|
||||
@require self.is_indexable()
|
||||
*>
|
||||
fn bool! Object.get_bool_at(&self, usz index)
|
||||
{
|
||||
Object* value = self.get_at(index);
|
||||
@@ -417,9 +416,9 @@ fn bool! Object.get_bool_at(&self, usz index)
|
||||
return value.b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.is_keyable()
|
||||
**/
|
||||
<*
|
||||
@require self.is_keyable()
|
||||
*>
|
||||
fn double! Object.get_float(&self, String key)
|
||||
{
|
||||
Object* value = self.get(key)!;
|
||||
@@ -436,9 +435,9 @@ fn double! Object.get_float(&self, String key)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.is_indexable()
|
||||
**/
|
||||
<*
|
||||
@require self.is_indexable()
|
||||
*>
|
||||
fn double! Object.get_float_at(&self, usz index)
|
||||
{
|
||||
Object* value = self.get_at(index);
|
||||
|
||||
@@ -46,6 +46,7 @@ fn void PrivatePriorityQueue.temp_init(&self, usz initial_capacity = 16) @inline
|
||||
self.heap.new_init(initial_capacity, allocator::temp()) @inline;
|
||||
}
|
||||
|
||||
|
||||
fn void PrivatePriorityQueue.push(&self, Type element)
|
||||
{
|
||||
self.heap.push(element);
|
||||
@@ -66,9 +67,18 @@ fn void PrivatePriorityQueue.push(&self, Type element)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self != null
|
||||
*/
|
||||
fn void PrivatePriorityQueue.remove_at(&self, usz index)
|
||||
{
|
||||
if (index == 0)
|
||||
{
|
||||
self.pop()!!;
|
||||
return;
|
||||
}
|
||||
self.heap.remove_at(index);
|
||||
}
|
||||
<*
|
||||
@require self != null
|
||||
*>
|
||||
fn Type! PrivatePriorityQueue.pop(&self)
|
||||
{
|
||||
usz i = 0;
|
||||
@@ -128,9 +138,9 @@ fn bool PrivatePriorityQueue.is_empty(&self)
|
||||
return self.heap.is_empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index < self.len()
|
||||
*/
|
||||
<*
|
||||
@require index < self.len()
|
||||
*>
|
||||
fn Type PrivatePriorityQueue.get(&self, usz index) @operator([])
|
||||
{
|
||||
return self.heap[index];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @require Type.is_ordered : "The type must be ordered"
|
||||
**/
|
||||
<*
|
||||
@require Type.is_ordered : "The type must be ordered"
|
||||
*>
|
||||
module std::collections::range(<Type>);
|
||||
import std::io;
|
||||
|
||||
@@ -21,9 +21,9 @@ fn bool Range.contains(&self, Type value) @inline
|
||||
return value >= self.start && value <= self.end;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index < self.len() : "Can't index into an empty range"
|
||||
**/
|
||||
<*
|
||||
@require index < self.len() : "Can't index into an empty range"
|
||||
*>
|
||||
fn Type Range.get(&self, usz index) @operator([])
|
||||
{
|
||||
return (Type)(self.start + (usz)index);
|
||||
@@ -31,12 +31,12 @@ fn Type Range.get(&self, usz index) @operator([])
|
||||
|
||||
fn String Range.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic @deprecated
|
||||
{
|
||||
return string::format("[%s..%s]", self.start, self.end, .allocator = allocator);
|
||||
return string::format("[%s..%s]", self.start, self.end, allocator: allocator);
|
||||
}
|
||||
|
||||
fn String Range.to_string(&self, Allocator allocator) @dynamic
|
||||
{
|
||||
return string::format("[%s..%s]", self.start, self.end, .allocator = allocator);
|
||||
return string::format("[%s..%s]", self.start, self.end, allocator: allocator);
|
||||
}
|
||||
|
||||
fn String Range.to_tstring(&self)
|
||||
@@ -78,7 +78,7 @@ fn String ExclusiveRange.to_new_string(&self, Allocator allocator = null) @dynam
|
||||
|
||||
fn String ExclusiveRange.to_string(&self, Allocator allocator) @dynamic
|
||||
{
|
||||
return string::format("[%s..<%s]", self.start, self.end, .allocator = allocator);
|
||||
return string::format("[%s..<%s]", self.start, self.end, allocator: allocator);
|
||||
}
|
||||
|
||||
fn String ExclusiveRange.to_tstring(&self)
|
||||
@@ -86,9 +86,9 @@ fn String ExclusiveRange.to_tstring(&self)
|
||||
return self.to_new_string(allocator::temp());
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index < self.len() : "Can't index into an empty range"
|
||||
**/
|
||||
<*
|
||||
@require index < self.len() : "Can't index into an empty range"
|
||||
*>
|
||||
fn Type ExclusiveRange.get(&self, usz index) @operator([])
|
||||
{
|
||||
return (Type)(self.start + index);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
<*
|
||||
@require values::@is_int(SIZE) &&& SIZE > 0 "The size must be positive integer"
|
||||
*>
|
||||
module std::collections::ringbuffer(<Type, SIZE>);
|
||||
|
||||
struct RingBuffer
|
||||
|
||||
476
lib/std/compression/qoi.c3
Normal file
476
lib/std/compression/qoi.c3
Normal file
@@ -0,0 +1,476 @@
|
||||
module std::compression::qoi;
|
||||
|
||||
const uint PIXELS_MAX = 400000000;
|
||||
|
||||
<*
|
||||
Colorspace.
|
||||
Purely informative. It will be saved to the file header,
|
||||
but does not affect how chunks are en-/decoded.
|
||||
*>
|
||||
enum QOIColorspace : char (char id)
|
||||
{
|
||||
SRGB = 0, // sRGB with linear alpha
|
||||
LINEAR = 1 // all channels linear
|
||||
}
|
||||
|
||||
<*
|
||||
Channels.
|
||||
The channels used in an image.
|
||||
AUTO can be used when decoding to automatically determine
|
||||
the channels from the file's header.
|
||||
*>
|
||||
enum QOIChannels : char (char id)
|
||||
{
|
||||
AUTO = 0,
|
||||
RGB = 3,
|
||||
RGBA = 4
|
||||
}
|
||||
|
||||
<*
|
||||
Descriptor.
|
||||
Contains information about an image.
|
||||
*>
|
||||
struct QOIDesc
|
||||
{
|
||||
uint width;
|
||||
uint height;
|
||||
QOIChannels channels;
|
||||
QOIColorspace colorspace;
|
||||
}
|
||||
|
||||
<*
|
||||
QOI Errors.
|
||||
These are all the possible bad outcomes.
|
||||
*>
|
||||
fault QOIError
|
||||
{
|
||||
INVALID_PARAMETERS,
|
||||
FILE_OPEN_FAILED,
|
||||
FILE_WRITE_FAILED,
|
||||
INVALID_DATA,
|
||||
TOO_MANY_PIXELS
|
||||
}
|
||||
|
||||
|
||||
// Let the user decide if they want to use std::io
|
||||
module std::compression::qoi @if(!$feature(QOI_NO_STDIO));
|
||||
import std::io;
|
||||
|
||||
<*
|
||||
Encode raw RGB or RGBA pixels into a QOI image and write it to the
|
||||
file system.
|
||||
|
||||
The desc struct must be filled with the image width, height, the
|
||||
used channels (QOIChannels.RGB or RGBA) and the colorspace
|
||||
(QOIColorspace.SRGB or LINEAR).
|
||||
|
||||
The function returns an optional, which can either be a QOIError
|
||||
or the number of bytes written on success.
|
||||
|
||||
@param [in] filename `The file's name to write the image to`
|
||||
@param [in] input `The raw RGB or RGBA pixels to encode`
|
||||
@param [&in] desc `The descriptor of the image`
|
||||
*>
|
||||
fn usz! write(String filename, char[] input, QOIDesc* desc)
|
||||
{
|
||||
@pool() {
|
||||
// encode data
|
||||
char[] output = encode(input, desc)!;
|
||||
|
||||
// open file
|
||||
File! f = file::open(filename, "wb");
|
||||
if (catch f) { return QOIError.FILE_OPEN_FAILED?; }
|
||||
|
||||
// write data to file and close it
|
||||
usz! written = f.write(output);
|
||||
if (catch written) { return QOIError.FILE_WRITE_FAILED?; }
|
||||
if (catch f.close()) { return QOIError.FILE_WRITE_FAILED?; }
|
||||
|
||||
return written;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Read and decode a QOI image from the file system.
|
||||
|
||||
If channels is set to QOIChannels.AUTO, the function will
|
||||
automatically determine the channels from the file's header.
|
||||
However, if channels is RGB or RGBA, the output format will be
|
||||
forced into this number of channels.
|
||||
|
||||
The desc struct will be filled with the width, height,
|
||||
channels and colorspace of the image.
|
||||
|
||||
The function returns an optional, which can either be a QOIError
|
||||
or a char[] pointing to the decoded pixels on success.
|
||||
|
||||
The returned pixel data should be free()d after use, or the decoding
|
||||
and use of the data should be wrapped in a @pool() { ... }; block.
|
||||
|
||||
@param [in] filename `The file's name to read the image from`
|
||||
@param [&out] desc `The descriptor to fill with the image's info`
|
||||
@param channels `The channels to be used`
|
||||
*>
|
||||
fn char[]! read(String filename, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap())
|
||||
{
|
||||
// read file
|
||||
char[]! data = file::load_new(filename);
|
||||
if (catch data) return QOIError.FILE_OPEN_FAILED?;
|
||||
defer mem::free(data);
|
||||
|
||||
// pass data to decode function
|
||||
return decode(data, desc, channels, allocator);
|
||||
}
|
||||
|
||||
|
||||
// Back to basic non-stdio mode
|
||||
module std::compression::qoi;
|
||||
import std::bits;
|
||||
|
||||
<*
|
||||
Encode raw RGB or RGBA pixels into a QOI image in memory.
|
||||
|
||||
The function returns an optional, which can either be a QOIError
|
||||
or a char[] pointing to the encoded data on success.
|
||||
|
||||
The returned qoi data should be free()d after use, or the encoding
|
||||
and use of the data should be wrapped in a @pool() { ... }; block.
|
||||
See the write() function for an example.
|
||||
|
||||
@param [in] input `The raw RGB or RGBA pixels to encode`
|
||||
@param [&in] desc `The descriptor of the image`
|
||||
*>
|
||||
fn char[]! encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::heap())
|
||||
{
|
||||
// check info in desc
|
||||
if (desc.width == 0 || desc.height == 0) return QOIError.INVALID_PARAMETERS?;
|
||||
if (desc.channels == AUTO) return QOIError.INVALID_PARAMETERS?;
|
||||
uint pixels = desc.width * desc.height;
|
||||
if (pixels > PIXELS_MAX) return QOIError.TOO_MANY_PIXELS?;
|
||||
|
||||
// check input data size
|
||||
uint image_size = pixels * desc.channels.id;
|
||||
if (image_size != input.len) return QOIError.INVALID_DATA?;
|
||||
|
||||
// allocate memory for encoded data (output)
|
||||
// header + chunk tag and RGB(A) data for each pixel + end of stream
|
||||
uint max_size = Header.sizeof + pixels + image_size + END_OF_STREAM.len;
|
||||
char[] output = allocator::alloc_array(allocator, char, max_size); // no need to init
|
||||
defer catch allocator::free(allocator, output);
|
||||
|
||||
// write header
|
||||
*(Header*)output.ptr = {
|
||||
.be_magic = bswap('qoif'),
|
||||
.be_width = bswap(desc.width),
|
||||
.be_height = bswap(desc.height),
|
||||
.channels = desc.channels.id,
|
||||
.colorspace = desc.colorspace.id
|
||||
};
|
||||
|
||||
uint pos = Header.sizeof; // Current position in output
|
||||
uint loc; // Current position in image (top-left corner)
|
||||
uint loc_end = image_size - desc.channels.id; // End of image data
|
||||
char run_length = 0; // Length of the current run
|
||||
|
||||
Pixel[64] palette; // Zero-initialized by default
|
||||
Pixel prev = { 0, 0, 0, 255 };
|
||||
Pixel p = { 0, 0, 0, 255 };
|
||||
|
||||
ichar[<3>] diff; // pre-allocate for diff
|
||||
ichar[<3>] luma; // ...and luma
|
||||
|
||||
// write chunks
|
||||
for (loc = 0; loc < image_size; loc += desc.channels.id)
|
||||
{
|
||||
// set previous pixel
|
||||
prev = p;
|
||||
|
||||
// get current pixel
|
||||
p[:3] = input[loc:3]; // cutesy slices :3
|
||||
if (desc.channels == RGBA) p.a = input[loc + 3];
|
||||
|
||||
// check if we can run the previous pixel
|
||||
if (prev == p) {
|
||||
run_length++;
|
||||
if (run_length == 62 || loc == loc_end) {
|
||||
*@extract(OpRun, output, &pos) = { OP_RUN, run_length - 1 };
|
||||
run_length = 0;
|
||||
}
|
||||
} else {
|
||||
// end last run if there was one
|
||||
if (run_length > 0) {
|
||||
*@extract(OpRun, output, &pos) = { OP_RUN, run_length - 1 };
|
||||
run_length = 0;
|
||||
}
|
||||
|
||||
switch {
|
||||
// check if we can index the palette
|
||||
case (palette[p.hash()] == p):
|
||||
*@extract(OpIndex, output, &pos) = {
|
||||
OP_INDEX,
|
||||
p.hash()
|
||||
};
|
||||
|
||||
// check if we can use diff or luma
|
||||
case (prev != p && prev.a == p.a):
|
||||
// diff the pixels
|
||||
diff = p.rgb - prev.rgb;
|
||||
if (
|
||||
diff.r > -3 && diff.r < 2 &&
|
||||
diff.g > -3 && diff.g < 2 &&
|
||||
diff.b > -3 && diff.b < 2
|
||||
) {
|
||||
*@extract(OpDiff, output, &pos) = {
|
||||
OP_DIFF,
|
||||
(char)diff.r + 2,
|
||||
(char)diff.g + 2,
|
||||
(char)diff.b + 2
|
||||
};
|
||||
palette[p.hash()] = p;
|
||||
} else {
|
||||
// check luma eligibility
|
||||
luma = { diff.r - diff.g, diff.g, diff.b - diff.g };
|
||||
if (
|
||||
luma.r >= -8 && luma.r <= 7 &&
|
||||
luma.g >= -32 && luma.g <= 31 &&
|
||||
luma.b >= -8 && luma.b <= 7
|
||||
) {
|
||||
*@extract(OpLuma, output, &pos) = {
|
||||
OP_LUMA,
|
||||
(char)luma.g + 32,
|
||||
(char)luma.r + 8,
|
||||
(char)luma.b + 8
|
||||
};
|
||||
palette[p.hash()] = p;
|
||||
} else { nextcase; }
|
||||
}
|
||||
|
||||
// worst case scenario: just encode the raw pixel
|
||||
default:
|
||||
if (prev.a != p.a) {
|
||||
*@extract(OpRGBA, output, &pos) = { OP_RGBA, p.r, p.g, p.b, p.a };
|
||||
} else {
|
||||
*@extract(OpRGB, output, &pos) = { OP_RGB, p.r, p.g, p.b };
|
||||
}
|
||||
palette[p.hash()] = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write end of stream
|
||||
output[pos:END_OF_STREAM.len] = END_OF_STREAM;
|
||||
pos += END_OF_STREAM.len;
|
||||
|
||||
return output[:pos];
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Decode a QOI image from memory.
|
||||
|
||||
If channels is set to QOIChannels.AUTO, the function will
|
||||
automatically determine the channels from the file's header.
|
||||
However, if channels is RGB or RGBA, the output format will be
|
||||
forced into this number of channels.
|
||||
|
||||
The desc struct will be filled with the width, height,
|
||||
channels and colorspace of the image.
|
||||
|
||||
The function returns an optional, which can either be a QOIError
|
||||
or a char[] pointing to the decoded pixels on success.
|
||||
|
||||
The returned pixel data should be free()d after use, or the decoding
|
||||
and use of the data should be wrapped in a @pool() { ... }; block.
|
||||
|
||||
@param [in] data `The QOI image data to decode`
|
||||
@param [&out] desc `The descriptor to fill with the image's info`
|
||||
@param channels `The channels to be used`
|
||||
*>
|
||||
fn char[]! decode(char[] data, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap())
|
||||
{
|
||||
// check input data
|
||||
if (data.len < Header.sizeof + END_OF_STREAM.len) return QOIError.INVALID_DATA?;
|
||||
|
||||
// get header
|
||||
Header* header = (Header*)data.ptr;
|
||||
|
||||
// check magic bytes (FourCC)
|
||||
if (bswap(header.be_magic) != 'qoif') return QOIError.INVALID_DATA?;
|
||||
|
||||
// copy header data to desc
|
||||
desc.width = bswap(header.be_width);
|
||||
desc.height = bswap(header.be_height);
|
||||
desc.channels = @enumcast(QOIChannels, header.channels)!; // Rethrow if invalid
|
||||
desc.colorspace = @enumcast(QOIColorspace, header.colorspace)!; // Rethrow if invalid
|
||||
if (desc.channels == AUTO) return QOIError.INVALID_DATA?; // Channels must be specified in the header
|
||||
|
||||
// check width and height
|
||||
if (desc.width == 0 || desc.height == 0) return QOIError.INVALID_DATA?;
|
||||
|
||||
// check pixel count
|
||||
ulong pixels = (ulong)desc.width * (ulong)desc.height;
|
||||
if (pixels > PIXELS_MAX) return QOIError.TOO_MANY_PIXELS?;
|
||||
|
||||
uint pos = Header.sizeof; // Current position in data
|
||||
uint loc; // Current position in image (top-left corner)
|
||||
char run_length = 0; // Length of the current run
|
||||
char tag; // Current chunk tag
|
||||
|
||||
Pixel[64] palette; // Zero-initialized by default
|
||||
Pixel p = { 0, 0, 0, 255 };
|
||||
|
||||
if (channels == AUTO) channels = desc.channels;
|
||||
|
||||
// allocate memory for image data
|
||||
usz image_size = (usz)pixels * channels.id;
|
||||
char[] image = allocator::alloc_array(allocator, char, image_size);
|
||||
defer catch allocator::free(allocator, image);
|
||||
|
||||
for (loc = 0; loc < image_size; loc += channels.id)
|
||||
{
|
||||
// get chunk tag
|
||||
tag = data[pos];
|
||||
|
||||
// check for chunk type
|
||||
switch
|
||||
{
|
||||
case run_length > 0:
|
||||
run_length--;
|
||||
|
||||
case tag == OP_RGB:
|
||||
OpRGB* op = @extract(OpRGB, data, &pos);
|
||||
p = { op.red, op.green, op.blue, p.a };
|
||||
palette[p.hash()] = p;
|
||||
|
||||
case tag == OP_RGBA:
|
||||
OpRGBA* op = @extract(OpRGBA, data, &pos);
|
||||
p = { op.red, op.green, op.blue, op.alpha };
|
||||
palette[p.hash()] = p;
|
||||
|
||||
case tag >> 6 == OP_INDEX:
|
||||
OpIndex* op = @extract(OpIndex, data, &pos);
|
||||
p = palette[op.index];
|
||||
|
||||
case tag >> 6 == OP_DIFF:
|
||||
OpDiff* op = @extract(OpDiff, data, &pos);
|
||||
p.r += op.diff_red - 2;
|
||||
p.g += op.diff_green - 2;
|
||||
p.b += op.diff_blue - 2;
|
||||
palette[p.hash()] = p;
|
||||
|
||||
case tag >> 6 == OP_LUMA:
|
||||
OpLuma* op = @extract(OpLuma, data, &pos);
|
||||
int diff_green = op.diff_green - 32;
|
||||
p.r += (char)(op.diff_red_minus_green - 8 + diff_green);
|
||||
p.g += (char)(diff_green);
|
||||
p.b += (char)(op.diff_blue_minus_green - 8 + diff_green);
|
||||
palette[p.hash()] = p;
|
||||
|
||||
case tag >> 6 == OP_RUN:
|
||||
OpRun* op = @extract(OpRun, data, &pos);
|
||||
run_length = op.run;
|
||||
}
|
||||
|
||||
// draw the pixel
|
||||
if (channels == RGBA) { image[loc:4] = p.rgba; } else { image[loc:3] = p.rgb; }
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ***************************************************************************
|
||||
// *** ***
|
||||
// *** Main functions are at the top to make the file more readable. ***
|
||||
// *** From here on, helper functions and types are defined. ***
|
||||
// *** ***
|
||||
// ***************************************************************************
|
||||
module std::compression::qoi @private;
|
||||
|
||||
// 8-bit opcodes
|
||||
const OP_RGB = 0b11111110;
|
||||
const OP_RGBA = 0b11111111;
|
||||
// 2-bit opcodes
|
||||
const OP_INDEX = 0b00;
|
||||
const OP_DIFF = 0b01;
|
||||
const OP_LUMA = 0b10;
|
||||
const OP_RUN = 0b11;
|
||||
|
||||
struct Header @packed
|
||||
{
|
||||
uint be_magic; // magic bytes "qoif"
|
||||
uint be_width; // image width in pixels (BE)
|
||||
uint be_height; // image height in pixels (BE)
|
||||
|
||||
// informative fields
|
||||
char channels; // 3 = RGB, 4 = RGB
|
||||
char colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear
|
||||
}
|
||||
|
||||
const char[*] END_OF_STREAM = {0, 0, 0, 0, 0, 0, 0, 1};
|
||||
|
||||
// inefficient, but it's only run once at a time
|
||||
macro @enumcast($Type, raw)
|
||||
{
|
||||
foreach (value : $Type.values) {
|
||||
if (value.id == raw) return value;
|
||||
}
|
||||
return QOIError.INVALID_DATA?;
|
||||
}
|
||||
|
||||
distinct Pixel = inline char[<4>];
|
||||
macro char Pixel.hash(Pixel p) {
|
||||
return (p.r * 3 + p.g * 5 + p.b * 7 + p.a * 11) % 64;
|
||||
}
|
||||
|
||||
struct OpRGB // No need to use @packed here, the alignment is 1 anyways.
|
||||
{
|
||||
char tag;
|
||||
char red;
|
||||
char green;
|
||||
char blue;
|
||||
}
|
||||
struct OpRGBA @packed
|
||||
{
|
||||
char tag;
|
||||
char red;
|
||||
char green;
|
||||
char blue;
|
||||
char alpha;
|
||||
}
|
||||
bitstruct OpIndex : char
|
||||
{
|
||||
char tag : 6..7;
|
||||
char index : 0..5;
|
||||
}
|
||||
bitstruct OpDiff : char
|
||||
{
|
||||
char tag : 6..7;
|
||||
char diff_red : 4..5;
|
||||
char diff_green : 2..3;
|
||||
char diff_blue : 0..1;
|
||||
}
|
||||
bitstruct OpLuma : ushort
|
||||
{
|
||||
char tag : 6..7;
|
||||
char diff_green : 0..5;
|
||||
char diff_red_minus_green : 12..15;
|
||||
char diff_blue_minus_green : 8..11;
|
||||
}
|
||||
bitstruct OpRun : char
|
||||
{
|
||||
char tag : 6..7;
|
||||
char run : 0..5;
|
||||
}
|
||||
|
||||
// Macro used to locate chunks in data buffers.
|
||||
// Can be used both for reading and writing.
|
||||
macro @extract($Type, char[] data, uint* pos)
|
||||
{
|
||||
// slice data, then double cast
|
||||
$Type* chunk = ($Type*)data[*pos : $Type.sizeof].ptr;
|
||||
*pos += $Type.sizeof;
|
||||
return chunk;
|
||||
}
|
||||
@@ -10,13 +10,14 @@ struct ArenaAllocator (Allocator)
|
||||
usz used;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a memory arena for use using the provided bytes.
|
||||
**/
|
||||
fn void ArenaAllocator.init(&self, char[] data)
|
||||
<*
|
||||
Initialize a memory arena for use using the provided bytes.
|
||||
*>
|
||||
fn ArenaAllocator* ArenaAllocator.init(&self, char[] data)
|
||||
{
|
||||
self.data = data;
|
||||
self.used = 0;
|
||||
return self;
|
||||
}
|
||||
|
||||
fn void ArenaAllocator.clear(&self)
|
||||
@@ -30,9 +31,9 @@ struct ArenaAllocatorHeader @local
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
/*
|
||||
* @require ptr != null
|
||||
**/
|
||||
<*
|
||||
@require ptr != null
|
||||
*>
|
||||
fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic
|
||||
{
|
||||
assert((uptr)ptr >= (uptr)self.data.ptr, "Pointer originates from a different allocator.");
|
||||
@@ -47,11 +48,11 @@ fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic
|
||||
fn usz ArenaAllocator.mark(&self) @dynamic => self.used;
|
||||
fn void ArenaAllocator.reset(&self, usz mark) @dynamic => self.used = mark;
|
||||
|
||||
/**
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
* @require size > 0
|
||||
**/
|
||||
<*
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
@require size > 0
|
||||
*>
|
||||
fn void*! ArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
|
||||
{
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
@@ -69,12 +70,12 @@ fn void*! ArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz a
|
||||
return mem;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
* @require old_pointer != null
|
||||
* @require size > 0
|
||||
**/
|
||||
<*
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
@require old_pointer != null
|
||||
@require size > 0
|
||||
*>
|
||||
fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignment) @dynamic
|
||||
{
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
|
||||
@@ -12,10 +12,10 @@ struct DynamicArenaAllocator (Allocator)
|
||||
usz page_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] allocator
|
||||
* @require page_size >= 128
|
||||
**/
|
||||
<*
|
||||
@param [&inout] allocator
|
||||
@require page_size >= 128
|
||||
*>
|
||||
fn void DynamicArenaAllocator.init(&self, usz page_size, Allocator allocator)
|
||||
{
|
||||
self.page = null;
|
||||
@@ -24,7 +24,6 @@ fn void DynamicArenaAllocator.init(&self, usz page_size, Allocator allocator)
|
||||
self.backing_allocator = allocator;
|
||||
}
|
||||
|
||||
import std::io;
|
||||
fn void DynamicArenaAllocator.free(&self)
|
||||
{
|
||||
DynamicArenaPage* page = self.page;
|
||||
@@ -61,10 +60,10 @@ struct DynamicArenaChunk @local
|
||||
usz size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require ptr
|
||||
* @require self.page `tried to free pointer on invalid allocator`
|
||||
*/
|
||||
<*
|
||||
@require ptr
|
||||
@require self.page `tried to free pointer on invalid allocator`
|
||||
*>
|
||||
fn void DynamicArenaAllocator.release(&self, void* ptr, bool) @dynamic
|
||||
{
|
||||
DynamicArenaPage* current_page = self.page;
|
||||
@@ -75,11 +74,11 @@ fn void DynamicArenaAllocator.release(&self, void* ptr, bool) @dynamic
|
||||
current_page.current_stack_ptr = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require size > 0 `Resize doesn't support zeroing`
|
||||
* @require old_pointer != null `Resize doesn't handle null pointers`
|
||||
* @require self.page `tried to realloc pointer on invalid allocator`
|
||||
*/
|
||||
<*
|
||||
@require size > 0 `Resize doesn't support zeroing`
|
||||
@require old_pointer != null `Resize doesn't handle null pointers`
|
||||
@require self.page `tried to realloc pointer on invalid allocator`
|
||||
*>
|
||||
fn void*! DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
|
||||
{
|
||||
DynamicArenaPage* current_page = self.page;
|
||||
@@ -127,10 +126,10 @@ fn void DynamicArenaAllocator.reset(&self, usz mark = 0) @dynamic
|
||||
self.page = page;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require math::is_power_of_2(alignment)
|
||||
* @require size > 0
|
||||
*/
|
||||
<*
|
||||
@require math::is_power_of_2(alignment)
|
||||
@require size > 0
|
||||
*>
|
||||
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.
|
||||
@@ -157,10 +156,10 @@ fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @loca
|
||||
return mem_start;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require size > 0 `acquire expects size > 0`
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
*/
|
||||
<*
|
||||
@require size > 0 `acquire expects size > 0`
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
*>
|
||||
fn void*! DynamicArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
|
||||
{
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
|
||||
@@ -11,10 +11,10 @@ struct SimpleHeapAllocator (Allocator)
|
||||
Header* free_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require allocator "An underlying memory provider must be given"
|
||||
* @require !self.free_list "The allocator may not be already initialized"
|
||||
**/
|
||||
<*
|
||||
@require allocator "An underlying memory provider must be given"
|
||||
@require !self.free_list "The allocator may not be already initialized"
|
||||
*>
|
||||
fn void SimpleHeapAllocator.init(&self, MemoryAllocFn allocator)
|
||||
{
|
||||
self.alloc_fn = allocator;
|
||||
@@ -49,9 +49,9 @@ fn void SimpleHeapAllocator.release(&self, void* old_pointer, bool aligned) @dyn
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require old_pointer && bytes > 0
|
||||
**/
|
||||
<*
|
||||
@require old_pointer && bytes > 0
|
||||
*>
|
||||
fn void*! SimpleHeapAllocator._realloc(&self, void* old_pointer, usz bytes) @local
|
||||
{
|
||||
// Find the block header.
|
||||
@@ -98,7 +98,7 @@ fn void*! SimpleHeapAllocator._alloc(&self, usz bytes) @local
|
||||
return current + 1;
|
||||
case current.size > aligned_bytes:
|
||||
Header* unallocated = (Header*)((char*)current + aligned_bytes + Header.sizeof);
|
||||
unallocated.size = current.size - aligned_bytes;
|
||||
unallocated.size = current.size - aligned_bytes - Header.sizeof;
|
||||
unallocated.next = current.next;
|
||||
if (current == self.free_list)
|
||||
{
|
||||
|
||||
@@ -3,15 +3,20 @@
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
|
||||
module std::core::mem::allocator @if(env::LIBC);
|
||||
import std::io;
|
||||
import libc;
|
||||
|
||||
const LibcAllocator LIBC_ALLOCATOR = {};
|
||||
distinct LibcAllocator (Allocator) = uptr;
|
||||
distinct LibcAllocator (Allocator, Printable) = uptr;
|
||||
|
||||
fn String LibcAllocator.to_string(&self, Allocator allocator) @dynamic => "Libc allocator".copy(allocator);
|
||||
fn usz! LibcAllocator.to_format(&self, Formatter *format) @dynamic => format.print("Libc allocator");
|
||||
|
||||
module std::core::mem::allocator @if(env::POSIX);
|
||||
import std::os;
|
||||
import libc;
|
||||
|
||||
|
||||
fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
|
||||
{
|
||||
if (init_type == ZERO)
|
||||
@@ -23,7 +28,7 @@ fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz a
|
||||
mem::clear(data, bytes, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return data;
|
||||
}
|
||||
return libc::calloc(1, bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
|
||||
return libc::calloc(1, bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -36,17 +41,17 @@ fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz a
|
||||
{
|
||||
if (!(data = libc::malloc(bytes))) return AllocationFailure.OUT_OF_MEMORY?;
|
||||
}
|
||||
$if env::TESTING:
|
||||
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
|
||||
$endif
|
||||
return data;
|
||||
$if env::TESTING:
|
||||
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
|
||||
$endif
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
|
||||
{
|
||||
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?;
|
||||
|
||||
$switch
|
||||
@@ -85,10 +90,10 @@ fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz a
|
||||
}
|
||||
void* data = alignment > 0 ? win32::_aligned_malloc(bytes, alignment) : libc::malloc(bytes);
|
||||
if (!data) return AllocationFailure.OUT_OF_MEMORY?;
|
||||
$if env::TESTING:
|
||||
$if env::TESTING:
|
||||
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
|
||||
$endif
|
||||
return data;
|
||||
return data;
|
||||
}
|
||||
|
||||
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
|
||||
@@ -104,13 +109,13 @@ fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
|
||||
{
|
||||
if (aligned)
|
||||
{
|
||||
win32::_aligned_free(old_ptr);
|
||||
return;
|
||||
win32::_aligned_free(old_ptr);
|
||||
return;
|
||||
}
|
||||
libc::free(old_ptr);
|
||||
}
|
||||
|
||||
module std::core::mem::allocator @if(!env::WIN32 && !env::POSIX && !env::WASM_NOLIBC);
|
||||
module std::core::mem::allocator @if(!env::WIN32 && !env::POSIX && env::LIBC);
|
||||
import libc;
|
||||
|
||||
fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
|
||||
@@ -118,16 +123,16 @@ fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz a
|
||||
if (init_type == ZERO)
|
||||
{
|
||||
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?;
|
||||
}
|
||||
else
|
||||
{
|
||||
void* data = alignment ? @aligned_alloc(libc::malloc, bytes, alignment)!! : libc::malloc(bytes);
|
||||
if (!data) return AllocationFailure.OUT_OF_MEMORY?;
|
||||
$if env::TESTING:
|
||||
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
|
||||
$endif
|
||||
return data;
|
||||
if (!data) return AllocationFailure.OUT_OF_MEMORY?;
|
||||
$if env::TESTING:
|
||||
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
|
||||
$endif
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,11 @@ struct OnStackAllocatorExtraChunk @local
|
||||
void* data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] allocator
|
||||
* Initialize a memory arena for use using the provided bytes.
|
||||
**/
|
||||
<*
|
||||
Initialize a memory arena for use using the provided bytes.
|
||||
|
||||
@param [&inout] allocator
|
||||
*>
|
||||
fn void OnStackAllocator.init(&self, char[] data, Allocator allocator)
|
||||
{
|
||||
self.data = data;
|
||||
@@ -54,14 +55,14 @@ struct OnStackAllocatorHeader
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require old_pointer
|
||||
**/
|
||||
<*
|
||||
@require old_pointer
|
||||
*>
|
||||
fn void OnStackAllocator.release(&self, void* old_pointer, bool aligned) @dynamic
|
||||
{
|
||||
if (allocation_in_stack_mem(self, old_pointer)) return;
|
||||
on_stack_allocator_remove_chunk(self, old_pointer);
|
||||
self.release(old_pointer, aligned);
|
||||
self.backing_allocator.release(old_pointer, aligned);
|
||||
}
|
||||
|
||||
fn bool allocation_in_stack_mem(OnStackAllocator* a, void* ptr) @local
|
||||
@@ -98,11 +99,11 @@ fn OnStackAllocatorExtraChunk* on_stack_allocator_find_chunk(OnStackAllocator* a
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require size > 0
|
||||
* @require old_pointer != null
|
||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
**/
|
||||
<*
|
||||
@require size > 0
|
||||
@require old_pointer != null
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
*>
|
||||
fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
|
||||
{
|
||||
if (!allocation_in_stack_mem(self, old_pointer))
|
||||
@@ -119,10 +120,10 @@ fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignm
|
||||
return mem;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
* @require size > 0
|
||||
**/
|
||||
<*
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
@require size > 0
|
||||
*>
|
||||
fn void*! OnStackAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
|
||||
{
|
||||
bool aligned = alignment > 0;
|
||||
|
||||
@@ -32,9 +32,9 @@ struct TempAllocatorPage
|
||||
macro usz TempAllocatorPage.pagesize(&self) => self.size & ~PAGE_IS_ALIGNED;
|
||||
macro bool TempAllocatorPage.is_aligned(&self) => self.size & PAGE_IS_ALIGNED == PAGE_IS_ALIGNED;
|
||||
|
||||
/**
|
||||
* @require size >= 16
|
||||
**/
|
||||
<*
|
||||
@require size >= 16
|
||||
*>
|
||||
fn TempAllocator*! new_temp_allocator(usz size, Allocator allocator)
|
||||
{
|
||||
TempAllocator* temp = allocator::alloc_with_padding(allocator, TempAllocator, size)!;
|
||||
@@ -68,6 +68,7 @@ fn void TempAllocator.reset(&self, usz mark) @dynamic
|
||||
TempAllocatorPage *last_page = self.last_page;
|
||||
while (last_page && last_page.mark > mark)
|
||||
{
|
||||
self.used = last_page.mark;
|
||||
TempAllocatorPage *to_free = last_page;
|
||||
last_page = last_page.prev_page;
|
||||
self._free_page(to_free)!!;
|
||||
@@ -79,7 +80,7 @@ fn void TempAllocator.reset(&self, usz mark) @dynamic
|
||||
usz cleaned = self.used - mark;
|
||||
if (cleaned > 0)
|
||||
{
|
||||
$if env::COMPILER_SAFE_MODE:
|
||||
$if env::COMPILER_SAFE_MODE && !env::ADDRESS_SANITIZER:
|
||||
self.data[mark : cleaned] = 0xAA;
|
||||
$endif
|
||||
asan::poison_memory_region(&self.data[mark], cleaned);
|
||||
@@ -133,11 +134,11 @@ fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @d
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require size > 0
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
**/
|
||||
<*
|
||||
@require size > 0
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
*>
|
||||
fn void*! TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
|
||||
{
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
@@ -178,8 +179,10 @@ fn void*! TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz al
|
||||
{
|
||||
mem = allocator::malloc_aligned(self.backing_allocator, total_alloc_size, alignment)!;
|
||||
}
|
||||
void* start = mem;
|
||||
mem += mem::aligned_offset(TempAllocatorPage.sizeof, alignment);
|
||||
page = (TempAllocatorPage*)mem - 1;
|
||||
page.start = mem;
|
||||
page.start = start;
|
||||
page.size = size | PAGE_IS_ALIGNED;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -26,29 +26,29 @@ struct TrackingAllocator (Allocator)
|
||||
usz allocs_total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a tracking allocator to wrap (and track) another allocator.
|
||||
*
|
||||
* @param [&inout] allocator "The allocator to track"
|
||||
**/
|
||||
<*
|
||||
Initialize a tracking allocator to wrap (and track) another allocator.
|
||||
|
||||
@param [&inout] allocator "The allocator to track"
|
||||
*>
|
||||
fn void TrackingAllocator.init(&self, Allocator allocator)
|
||||
{
|
||||
*self = { .inner_allocator = allocator };
|
||||
self.map.new_init(.allocator = allocator);
|
||||
self.map.new_init(allocator: allocator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free this tracking allocator.
|
||||
**/
|
||||
<*
|
||||
Free this tracking allocator.
|
||||
*>
|
||||
fn void TrackingAllocator.free(&self)
|
||||
{
|
||||
self.map.free();
|
||||
*self = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return "the total allocated memory not yet freed."
|
||||
**/
|
||||
<*
|
||||
@return "the total allocated memory not yet freed."
|
||||
*>
|
||||
fn usz TrackingAllocator.allocated(&self)
|
||||
{
|
||||
usz allocated = 0;
|
||||
@@ -59,14 +59,14 @@ fn usz TrackingAllocator.allocated(&self)
|
||||
return allocated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return "the total memory allocated (freed or not)."
|
||||
**/
|
||||
<*
|
||||
@return "the total memory allocated (freed or not)."
|
||||
*>
|
||||
fn usz TrackingAllocator.total_allocated(&self) => self.mem_total;
|
||||
|
||||
/**
|
||||
* @return "the total number of allocations (freed or not)."
|
||||
**/
|
||||
<*
|
||||
@return "the total number of allocations (freed or not)."
|
||||
*>
|
||||
fn usz TrackingAllocator.total_allocation_count(&self) => self.allocs_total;
|
||||
|
||||
fn Allocation[] TrackingAllocator.allocations_tlist(&self, Allocator allocator)
|
||||
@@ -74,9 +74,9 @@ fn Allocation[] TrackingAllocator.allocations_tlist(&self, Allocator allocator)
|
||||
return self.map.value_tlist();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return "the number of non-freed allocations."
|
||||
**/
|
||||
<*
|
||||
@return "the number of non-freed allocations."
|
||||
*>
|
||||
fn usz TrackingAllocator.allocation_count(&self) => self.map.count;
|
||||
|
||||
fn void*! TrackingAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
|
||||
@@ -86,8 +86,7 @@ fn void*! TrackingAllocator.acquire(&self, usz size, AllocInitType init_type, us
|
||||
void*[MAX_BACKTRACE] bt;
|
||||
backtrace::capture_current(&bt);
|
||||
self.map.set((uptr)data, { data, size, bt });
|
||||
self.mem_total += size;
|
||||
self.allocs_total++;
|
||||
self.mem_total += size;
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -98,7 +97,7 @@ fn void*! TrackingAllocator.resize(&self, void* old_pointer, usz size, usz align
|
||||
void*[MAX_BACKTRACE] bt;
|
||||
backtrace::capture_current(&bt);
|
||||
self.map.set((uptr)data, { data, size, bt });
|
||||
self.mem_total += size;
|
||||
self.mem_total += size;
|
||||
self.allocs_total++;
|
||||
return data;
|
||||
}
|
||||
@@ -106,9 +105,9 @@ fn void*! TrackingAllocator.resize(&self, void* old_pointer, usz size, usz align
|
||||
fn void TrackingAllocator.release(&self, void* old_pointer, bool is_aligned) @dynamic
|
||||
{
|
||||
if (catch self.map.remove((uptr)old_pointer))
|
||||
{
|
||||
unreachable("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);
|
||||
}
|
||||
|
||||
@@ -133,14 +132,14 @@ fn void! TrackingAllocator.fprint_report(&self, OutStream out)
|
||||
if (!allocs[0].backtrace[0])
|
||||
{
|
||||
io::fprintn(out, "======== Memory Report ========")!;
|
||||
io::fprintn(out, "Size in bytes Address")!;
|
||||
foreach (i, &allocation : allocs)
|
||||
{
|
||||
entries++;
|
||||
total += allocation.size;
|
||||
io::fprintfn(out, "%13s %p", allocation.size, allocation.ptr)!;
|
||||
}
|
||||
io::fprintn(out, "===============================")!;
|
||||
io::fprintn(out, "Size in bytes Address")!;
|
||||
foreach (i, &allocation : allocs)
|
||||
{
|
||||
entries++;
|
||||
total += allocation.size;
|
||||
io::fprintfn(out, "%13s %p", allocation.size, allocation.ptr)!;
|
||||
}
|
||||
io::fprintn(out, "===============================")!;
|
||||
|
||||
}
|
||||
else
|
||||
@@ -170,48 +169,48 @@ fn void! TrackingAllocator.fprint_report(&self, OutStream out)
|
||||
io::fprintn(out, "* NO ALLOCATIONS FOUND *")!;
|
||||
}
|
||||
io::fprintfn(out, "- Total currently allocated memory: %d", total)!;
|
||||
io::fprintfn(out, "- Total current allocations: %d", entries)!;
|
||||
io::fprintfn(out, "- Total allocations (freed and retained): %d", self.allocs_total)!;
|
||||
io::fprintfn(out, "- Total allocated memory (freed and retained): %d", self.mem_total)!;
|
||||
if (leaks)
|
||||
{
|
||||
io::fprintfn(out, "- Total current allocations: %d", entries)!;
|
||||
io::fprintfn(out, "- Total allocations (freed and retained): %d", self.allocs_total)!;
|
||||
io::fprintfn(out, "- Total allocated memory (freed and retained): %d", self.mem_total)!;
|
||||
if (leaks)
|
||||
{
|
||||
io::fprintn(out)!;
|
||||
io::fprintn(out, "Full leak report:")!;
|
||||
foreach (i, &allocation : allocs)
|
||||
{
|
||||
if (!allocation.backtrace[3])
|
||||
{
|
||||
io::fprintfn(out, "Allocation %d (%d bytes) - no backtrace available.", i + 1, allocation.size)!;
|
||||
continue;
|
||||
foreach (i, &allocation : allocs)
|
||||
{
|
||||
if (!allocation.backtrace[3])
|
||||
{
|
||||
io::fprintfn(out, "Allocation %d (%d bytes) - no backtrace available.", i + 1, allocation.size)!;
|
||||
continue;
|
||||
}
|
||||
BacktraceList backtraces = {};
|
||||
usz end = MAX_BACKTRACE;
|
||||
foreach (j, val : allocation.backtrace)
|
||||
{
|
||||
if (!val)
|
||||
{
|
||||
end = j;
|
||||
break;
|
||||
BacktraceList backtraces = {};
|
||||
usz end = MAX_BACKTRACE;
|
||||
foreach (j, val : allocation.backtrace)
|
||||
{
|
||||
if (!val)
|
||||
{
|
||||
end = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
BacktraceList list = backtrace::symbolize_backtrace(allocation.backtrace[3..(end - 1)], allocator::temp())!;
|
||||
io::fprintfn(out, "Allocation %d (%d bytes): ", i + 1, allocation.size)!;
|
||||
foreach (trace : list)
|
||||
{
|
||||
if (trace.has_file())
|
||||
{
|
||||
io::fprintfn(out, " %s (in %s:%d)", trace.function, trace.file, trace.line);
|
||||
continue;
|
||||
}
|
||||
if (trace.is_unknown())
|
||||
{
|
||||
io::fprintfn(out, " ??? (in unknown)");
|
||||
continue;
|
||||
}
|
||||
BacktraceList list = backtrace::symbolize_backtrace(allocation.backtrace[3..(end - 1)], allocator::temp())!;
|
||||
io::fprintfn(out, "Allocation %d (%d bytes): ", i + 1, allocation.size)!;
|
||||
foreach (trace : list)
|
||||
{
|
||||
if (trace.has_file())
|
||||
{
|
||||
io::fprintfn(out, " %s (in %s:%d)", trace.function, trace.file, trace.line);
|
||||
continue;
|
||||
}
|
||||
if (trace.is_unknown())
|
||||
{
|
||||
io::fprintfn(out, " ??? (in unknown)");
|
||||
continue;
|
||||
}
|
||||
io::fprintfn(out, " %s (source unavailable)", trace.function);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
module std::core::array;
|
||||
import std::core::array::slice;
|
||||
|
||||
/**
|
||||
* @param [in] array
|
||||
* @param [in] element
|
||||
* @return "the first index of the element"
|
||||
* @return! SearchResult.MISSING
|
||||
**/
|
||||
<*
|
||||
@param [in] array
|
||||
@param [in] element
|
||||
@return "the first index of the element"
|
||||
@return! SearchResult.MISSING
|
||||
*>
|
||||
macro index_of(array, element)
|
||||
{
|
||||
foreach (i, &e : array)
|
||||
@@ -16,25 +16,26 @@ macro index_of(array, element)
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require @typekind(array) == VECTOR || @typekind(array) == ARRAY
|
||||
* @require @typekind(array[0]) == VECTOR || @typekind(array[0]) == ARRAY
|
||||
**/
|
||||
macro slice2d(array, x = 0, xlen = 0, y = 0, ylen = 0)
|
||||
<*
|
||||
@require @typekind(array_ptr) == POINTER
|
||||
@require @typekind(*array_ptr) == VECTOR || @typekind(*array_ptr) == ARRAY
|
||||
@require @typekind((*array_ptr)[0]) == VECTOR || @typekind((*array_ptr)[0]) == ARRAY
|
||||
*>
|
||||
macro slice2d(array_ptr, x = 0, xlen = 0, y = 0, ylen = 0)
|
||||
{
|
||||
if (xlen < 1) xlen = $typeof(array[0]).len + xlen;
|
||||
if (ylen < 1) ylen = $typeof(array).len + ylen;
|
||||
var $ElementType = $typeof(array[0][0]);
|
||||
return Slice2d(<$ElementType>) { ($ElementType*)&array, $typeof(array[0]).len, y, ylen, x, xlen };
|
||||
if (xlen < 1) xlen = $typeof((*array_ptr)[0]).len + xlen;
|
||||
if (ylen < 1) ylen = $typeof((*array_ptr)).len + ylen;
|
||||
var $ElementType = $typeof((*array_ptr)[0][0]);
|
||||
return Slice2d(<$ElementType>) { ($ElementType*)array_ptr, $typeof((*array_ptr)[0]).len, y, ylen, x, xlen };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param [in] array
|
||||
* @param [in] element
|
||||
* @return "the last index of the element"
|
||||
* @return! SearchResult.MISSING
|
||||
**/
|
||||
<*
|
||||
@param [in] array
|
||||
@param [in] element
|
||||
@return "the last index of the element"
|
||||
@return! SearchResult.MISSING
|
||||
*>
|
||||
macro rindex_of(array, element)
|
||||
{
|
||||
foreach_r (i, &e : array)
|
||||
@@ -44,18 +45,18 @@ macro rindex_of(array, element)
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate two arrays or slices, returning a slice containing the concatenation of them.
|
||||
*
|
||||
* @param [in] arr1
|
||||
* @param [in] arr2
|
||||
* @param [&inout] allocator "The allocator to use, default is the heap allocator"
|
||||
* @require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY
|
||||
* @require @typekind(arr2) == SLICE || @typekind(arr2) == ARRAY
|
||||
* @require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
|
||||
* @ensure result.len == arr1.len + arr2.len
|
||||
**/
|
||||
macro concat_new(arr1, arr2, Allocator allocator = allocator::heap())
|
||||
<*
|
||||
Concatenate two arrays or slices, returning a slice containing the concatenation of them.
|
||||
|
||||
@param [in] arr1
|
||||
@param [in] arr2
|
||||
@param [&inout] allocator "The allocator to use, default is the heap allocator"
|
||||
@require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY
|
||||
@require @typekind(arr2) == SLICE || @typekind(arr2) == ARRAY
|
||||
@require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
|
||||
@ensure result.len == arr1.len + arr2.len
|
||||
*>
|
||||
macro concat(arr1, arr2, Allocator allocator) @nodiscard
|
||||
{
|
||||
var $Type = $typeof(arr1[0]);
|
||||
$Type[] result = allocator::alloc_array(allocator, $Type, arr1.len + arr2.len);
|
||||
@@ -69,19 +70,34 @@ macro concat_new(arr1, arr2, Allocator allocator = allocator::heap())
|
||||
}
|
||||
return result;
|
||||
}
|
||||
<*
|
||||
Concatenate two arrays or slices, returning a slice containing the concatenation of them.
|
||||
|
||||
/**
|
||||
* Concatenate two arrays or slices, returning a slice containing the concatenation of them,
|
||||
* allocated using the temp allocator.
|
||||
*
|
||||
* @param [in] arr1
|
||||
* @param [in] arr2
|
||||
* @require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY
|
||||
* @require @typekind(arr2) == SLICE || @typekind(arr2) == ARRAY
|
||||
* @require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
|
||||
* @ensure result.len == arr1.len + arr2.len
|
||||
**/
|
||||
macro tconcat(arr1, arr2) => concat(arr1, arr2, allocator::temp());
|
||||
@param [in] arr1
|
||||
@param [in] arr2
|
||||
@param [&inout] allocator "The allocator to use, default is the heap allocator"
|
||||
@require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY
|
||||
@require @typekind(arr2) == SLICE || @typekind(arr2) == ARRAY
|
||||
@require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
|
||||
@ensure return.len == arr1.len + arr2.len
|
||||
*>
|
||||
macro concat_new(arr1, arr2, Allocator allocator = allocator::heap()) @nodiscard
|
||||
{
|
||||
return concat(arr1, arr2, allocator);
|
||||
}
|
||||
|
||||
<*
|
||||
Concatenate two arrays or slices, returning a slice containing the concatenation of them,
|
||||
allocated using the temp allocator.
|
||||
|
||||
@param [in] arr1
|
||||
@param [in] arr2
|
||||
@require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY
|
||||
@require @typekind(arr2) == SLICE || @typekind(arr2) == ARRAY
|
||||
@require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
|
||||
@ensure return.len == arr1.len + arr2.len
|
||||
*>
|
||||
macro tconcat(arr1, arr2) @nodiscard => concat(arr1, arr2, allocator::temp());
|
||||
|
||||
module std::core::array::slice(<Type>);
|
||||
|
||||
@@ -127,18 +143,48 @@ macro void Slice2d.@each_ref(&self; @body(usz[<2>], Type*))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require idy >= 0 && idy < self.ylen
|
||||
**/
|
||||
macro Type[] Slice2d.get(self, usz idy) @operator([])
|
||||
<*
|
||||
@require idy >= 0 && idy < self.ylen
|
||||
*>
|
||||
macro Type[] Slice2d.get_row(self, usz idy) @operator([])
|
||||
{
|
||||
return (self.ptr + self.inner_len * (idy + self.ystart))[self.xstart:self.xlen];
|
||||
}
|
||||
|
||||
/**
|
||||
* @require y >= 0 && y < self.ylen
|
||||
* @require x >= 0 && x < self.xlen
|
||||
**/
|
||||
macro Type Slice2d.get_coord(self, usz[<2>] coord)
|
||||
{
|
||||
return *self.get_coord_ref(coord);
|
||||
}
|
||||
|
||||
macro Type Slice2d.get_xy(self, x, y)
|
||||
{
|
||||
return *self.get_xy_ref(x, y);
|
||||
}
|
||||
|
||||
macro Type* Slice2d.get_xy_ref(self, x, y)
|
||||
{
|
||||
return self.ptr + self.inner_len * (y + self.ystart) + self.xstart + x;
|
||||
}
|
||||
|
||||
macro Type* Slice2d.get_coord_ref(self, usz[<2>] coord)
|
||||
{
|
||||
return self.get_xy_ref(coord.x, coord.y);
|
||||
}
|
||||
|
||||
macro void Slice2d.set_coord(self, usz[<2>] coord, Type value)
|
||||
{
|
||||
*self.get_coord_ref(coord) = value;
|
||||
}
|
||||
|
||||
macro void Slice2d.set_xy(self, x, y, Type value)
|
||||
{
|
||||
*self.get_xy_ref(x, y) = value;
|
||||
}
|
||||
|
||||
<*
|
||||
@require y >= 0 && y < self.ylen
|
||||
@require x >= 0 && x < self.xlen
|
||||
*>
|
||||
fn Slice2d Slice2d.slice(&self, isz x = 0, isz xlen = 0, isz y = 0, isz ylen = 0)
|
||||
{
|
||||
if (xlen < 1) xlen = self.xlen + xlen;
|
||||
|
||||
@@ -87,10 +87,10 @@ bitstruct UInt128LE : uint128 @littleendian
|
||||
uint128 val : 0..127;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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_array_or_slice_of_char(bytes) "argument must be an array, a pointer to an array or a slice of char"
|
||||
@require is_bitorder($Type) "type must be a bitorder integer"
|
||||
*>
|
||||
macro read(bytes, $Type)
|
||||
{
|
||||
char[] s;
|
||||
@@ -103,10 +103,10 @@ macro read(bytes, $Type)
|
||||
return bitcast(*(char[$Type.sizeof]*)s.ptr, $Type).val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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_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"
|
||||
*>
|
||||
macro write(x, bytes, $Type)
|
||||
{
|
||||
char[] s;
|
||||
|
||||
@@ -4,56 +4,58 @@
|
||||
module std::core::builtin;
|
||||
import libc, std::hash, std::io, std::os::backtrace;
|
||||
|
||||
/**
|
||||
* Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds.
|
||||
**/
|
||||
/*
|
||||
Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds.
|
||||
*/
|
||||
fault IteratorResult { NO_MORE_ELEMENT }
|
||||
|
||||
/**
|
||||
* Use `SearchResult` when trying to return a value from some collection but the element is missing.
|
||||
**/
|
||||
/*
|
||||
Use `SearchResult` when trying to return a value from some collection but the element is missing.
|
||||
*/
|
||||
fault SearchResult { MISSING }
|
||||
|
||||
/**
|
||||
* Use `CastResult` when an attempt at conversion fails.
|
||||
**/
|
||||
/*
|
||||
Use `CastResult` when an attempt at conversion fails.
|
||||
*/
|
||||
fault CastResult { TYPE_MISMATCH }
|
||||
|
||||
|
||||
def VoidFn = fn void();
|
||||
|
||||
/**
|
||||
* Stores a variable on the stack, then restores it at the end of the
|
||||
* macro scope.
|
||||
*
|
||||
* @param variable `the variable to store and restore`
|
||||
**/
|
||||
macro void @scope(&variable; @body) @builtin
|
||||
<*
|
||||
Stores a variable on the stack, then restores it at the end of the
|
||||
macro scope.
|
||||
|
||||
@param #variable `the variable to store and restore`
|
||||
@require values::@is_lvalue(#variable)
|
||||
*>
|
||||
macro void @scope(#variable; @body) @builtin
|
||||
{
|
||||
var temp = *variable;
|
||||
defer *variable = temp;
|
||||
var temp = #variable;
|
||||
defer #variable = temp;
|
||||
@body();
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap two variables
|
||||
* @require $assignable(*b, $typeof(*a)) && $assignable(*a, $typeof(*b))
|
||||
**/
|
||||
macro void @swap(&a, &b) @builtin
|
||||
<*
|
||||
Swap two variables
|
||||
@require $defined(#a = #b, #b = #a) `The values must be mutually assignable`
|
||||
*>
|
||||
macro void @swap(#a, #b) @builtin
|
||||
{
|
||||
var temp = *a;
|
||||
*a = *b;
|
||||
*b = temp;
|
||||
var temp = #a;
|
||||
#a = #b;
|
||||
#b = temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an `any` type to a type, returning an failure if there is a type mismatch.
|
||||
*
|
||||
* @param v `the any to convert to the given type.`
|
||||
* @param $Type `the type to convert to`
|
||||
* @return `The any.ptr converted to its type.`
|
||||
* @ensure @typeis(return, $Type*)
|
||||
* @return! CastResult.TYPE_MISMATCH
|
||||
**/
|
||||
<*
|
||||
Convert an `any` type to a type, returning an failure if there is a type mismatch.
|
||||
|
||||
@param v `the any to convert to the given type.`
|
||||
@param $Type `the type to convert to`
|
||||
@return `The any.ptr converted to its type.`
|
||||
@ensure @typeis(return, $Type*)
|
||||
@return! CastResult.TYPE_MISMATCH
|
||||
*>
|
||||
macro anycast(any v, $Type) @builtin
|
||||
{
|
||||
if (v.type != $Type.typeid) return CastResult.TYPE_MISMATCH?;
|
||||
@@ -71,23 +73,23 @@ fn bool print_backtrace(String message, int backtraces_to_ignore) @if(env::NATIV
|
||||
if (catch backtrace) return false;
|
||||
if (backtrace.len() <= backtraces_to_ignore) return false;
|
||||
io::eprint("\nERROR: '");
|
||||
io::eprint(message);
|
||||
io::eprintn("'");
|
||||
io::eprint(message);
|
||||
io::eprintn("'");
|
||||
foreach (i, &trace : backtrace)
|
||||
{
|
||||
if (i < backtraces_to_ignore) continue;
|
||||
String inline_suffix = trace.is_inline ? " [inline]" : "";
|
||||
if (trace.is_unknown())
|
||||
{
|
||||
{
|
||||
if (i < backtraces_to_ignore) continue;
|
||||
String inline_suffix = trace.is_inline ? " [inline]" : "";
|
||||
if (trace.is_unknown())
|
||||
{
|
||||
io::eprintfn(" in ???%s", inline_suffix);
|
||||
continue;
|
||||
}
|
||||
if (trace.has_file())
|
||||
{
|
||||
io::eprintfn(" in %s (%s:%d) [%s]%s", trace.function, trace.file, trace.line, trace.object_file, inline_suffix);
|
||||
continue;
|
||||
}
|
||||
io::eprintfn(" in %s (source unavailable) [%s]%s", trace.function, trace.object_file, inline_suffix);
|
||||
{
|
||||
io::eprintfn(" in %s (%s:%d) [%s]%s", trace.function, trace.file, trace.line, trace.object_file, inline_suffix);
|
||||
continue;
|
||||
}
|
||||
io::eprintfn(" in %s (source unavailable) [%s]%s", trace.function, trace.object_file, inline_suffix);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
@@ -98,19 +100,34 @@ fn void default_panic(String message, String file, String function, uint line) @
|
||||
if (!print_backtrace(message, 2))
|
||||
{
|
||||
io::eprintfn("\nERROR: '%s', in %s (%s:%d)", message, function, file, line);
|
||||
return;
|
||||
}
|
||||
$endif
|
||||
$$trap();
|
||||
|
||||
}
|
||||
|
||||
macro void abort(String string = "Unrecoverable error reached", ...) @builtin @noreturn
|
||||
{
|
||||
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat);
|
||||
$$trap();
|
||||
}
|
||||
|
||||
bool in_panic @local = false;
|
||||
|
||||
fn void default_panic(String message, String file, String function, uint line) @if(!env::NATIVE_STACKTRACE)
|
||||
{
|
||||
if (in_panic)
|
||||
{
|
||||
io::eprintn("Panic inside of panic.");
|
||||
return;
|
||||
}
|
||||
in_panic = true;
|
||||
$if $defined(io::stderr):
|
||||
io::eprint("\nERROR: '");
|
||||
io::eprint(message);
|
||||
io::eprintfn("', in %s (%s:%d)", function, file, line);
|
||||
$endif
|
||||
in_panic = false;
|
||||
$$trap();
|
||||
}
|
||||
|
||||
@@ -120,20 +137,28 @@ PanicFn panic = &default_panic;
|
||||
|
||||
fn void panicf(String fmt, String file, String function, uint line, args...)
|
||||
{
|
||||
if (in_panic)
|
||||
{
|
||||
io::eprint("Panic inside of panic: ");
|
||||
io::eprintn(fmt);
|
||||
return;
|
||||
}
|
||||
in_panic = true;
|
||||
@stack_mem(512; Allocator allocator)
|
||||
{
|
||||
DString s;
|
||||
s.new_init(.allocator = allocator);
|
||||
s.new_init(allocator: allocator);
|
||||
s.appendf(fmt, ...args);
|
||||
in_panic = false;
|
||||
panic(s.str_view(), file, function, line);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the path as unreachable. This will panic in safe mode, and in fast will simply be assumed
|
||||
* never happens.
|
||||
* @param [in] string "The panic message or format string"
|
||||
**/
|
||||
<*
|
||||
Marks the path as unreachable. This will panic in safe mode, and in fast will simply be assumed
|
||||
never happens.
|
||||
@param [in] string "The panic message or format string"
|
||||
*>
|
||||
macro void unreachable(String string = "Unreachable statement reached.", ...) @builtin @noreturn
|
||||
{
|
||||
$if env::COMPILER_SAFE_MODE:
|
||||
@@ -142,19 +167,19 @@ macro void unreachable(String string = "Unreachable statement reached.", ...) @b
|
||||
$$unreachable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the path as unsupported, this is similar to unreachable.
|
||||
* @param [in] string "The error message"
|
||||
**/
|
||||
<*
|
||||
Marks the path as unsupported, this is similar to unreachable.
|
||||
@param [in] string "The error message"
|
||||
*>
|
||||
macro void unsupported(String string = "Unsupported function invoked") @builtin @noreturn
|
||||
{
|
||||
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat);
|
||||
$$unreachable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unconditionally break into an attached debugger when reached.
|
||||
**/
|
||||
<*
|
||||
Unconditionally break into an attached debugger when reached.
|
||||
*>
|
||||
macro void breakpoint() @builtin
|
||||
{
|
||||
$$breakpoint();
|
||||
@@ -175,13 +200,13 @@ macro any.as_inner(&self)
|
||||
return $$any_make(self.ptr, self.type.inner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param expr "the expression to cast"
|
||||
* @param $Type "the type to cast to"
|
||||
*
|
||||
* @require $sizeof(expr) == $Type.sizeof "Cannot bitcast between types of different size."
|
||||
* @ensure @typeis(return, $Type)
|
||||
**/
|
||||
<*
|
||||
@param expr "the expression to cast"
|
||||
@param $Type "the type to cast to"
|
||||
|
||||
@require $sizeof(expr) == $Type.sizeof "Cannot bitcast between types of different size."
|
||||
@ensure @typeis(return, $Type)
|
||||
*>
|
||||
macro bitcast(expr, $Type) @builtin
|
||||
{
|
||||
$if $Type.alignof <= $alignof(expr):
|
||||
@@ -193,82 +218,100 @@ macro bitcast(expr, $Type) @builtin
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $Type `The type of the enum`
|
||||
* @param [in] enum_name `The name of the enum to search for`
|
||||
* @require $Type.kindof == ENUM `Only enums may be used`
|
||||
* @ensure @typeis(return, $Type)
|
||||
* @return! SearchResult.MISSING
|
||||
**/
|
||||
<*
|
||||
@param $Type `The type of the enum`
|
||||
@param [in] enum_name `The name of the enum to search for`
|
||||
@require $Type.kindof == ENUM `Only enums may be used`
|
||||
@ensure @typeis(return, $Type)
|
||||
@return! SearchResult.MISSING
|
||||
*>
|
||||
macro enum_by_name($Type, String enum_name) @builtin
|
||||
{
|
||||
typeid x = $Type.typeid;
|
||||
foreach (i, name : x.names)
|
||||
{
|
||||
if (name == enum_name) return ($Type)i;
|
||||
if (name == enum_name) return $Type.from_ordinal(i);
|
||||
}
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark an expression as likely to be true
|
||||
*
|
||||
* @param #value "expression to be marked likely"
|
||||
* @param $probability "in the range 0 - 1"
|
||||
* @require $probability >= 0 && $probability <= 1.0
|
||||
**/
|
||||
<*
|
||||
@param $Type `The type of the enum`
|
||||
@require $Type.kindof == ENUM `Only enums may be used`
|
||||
@require $defined($Type.#value) `Expected '#value' to match an enum associated value`
|
||||
@require $assignable(value, $typeof($Type{}.#value)) `Expected the value to match the type of the associated value`
|
||||
@ensure @typeis(return, $Type)
|
||||
@return! SearchResult.MISSING
|
||||
*>
|
||||
macro @enum_from_value($Type, #value, value) @builtin
|
||||
{
|
||||
usz elements = $Type.elements;
|
||||
foreach (e : $Type.values)
|
||||
{
|
||||
if (e.#value == value) return e;
|
||||
}
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
<*
|
||||
Mark an expression as likely to be true
|
||||
|
||||
@param #value "expression to be marked likely"
|
||||
@param $probability "in the range 0 - 1"
|
||||
@require $probability >= 0 && $probability <= 1.0
|
||||
*>
|
||||
macro bool @likely(bool #value, $probability = 1.0) @builtin
|
||||
{
|
||||
$switch
|
||||
$case env::BUILTIN_EXPECT_IS_DISABLED:
|
||||
$switch
|
||||
$case env::BUILTIN_EXPECT_IS_DISABLED:
|
||||
return #value;
|
||||
$case $probability == 1.0:
|
||||
$case $probability == 1.0:
|
||||
return $$expect(#value, true);
|
||||
$default:
|
||||
$default:
|
||||
return $$expect_with_probability(#value, true, $probability);
|
||||
$endswitch
|
||||
$endswitch
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark an expression as unlikely to be true
|
||||
*
|
||||
* @param #value "expression to be marked unlikely"
|
||||
* @param $probability "in the range 0 - 1"
|
||||
* @require $probability >= 0 && $probability <= 1.0
|
||||
**/
|
||||
<*
|
||||
Mark an expression as unlikely to be true
|
||||
|
||||
@param #value "expression to be marked unlikely"
|
||||
@param $probability "in the range 0 - 1"
|
||||
@require $probability >= 0 && $probability <= 1.0
|
||||
*>
|
||||
macro bool @unlikely(bool #value, $probability = 1.0) @builtin
|
||||
{
|
||||
$switch
|
||||
$case env::BUILTIN_EXPECT_IS_DISABLED:
|
||||
$switch
|
||||
$case env::BUILTIN_EXPECT_IS_DISABLED:
|
||||
return #value;
|
||||
$case $probability == 1.0:
|
||||
$case $probability == 1.0:
|
||||
return $$expect(#value, false);
|
||||
$default:
|
||||
$default:
|
||||
return $$expect_with_probability(#value, false, $probability);
|
||||
$endswitch
|
||||
$endswitch
|
||||
}
|
||||
|
||||
/**
|
||||
* @require values::@is_int(#value) || values::@is_bool(#value)
|
||||
* @require $assignable(expected, $typeof(#value))
|
||||
* @require $probability >= 0 && $probability <= 1.0
|
||||
**/
|
||||
<*
|
||||
@require values::@is_int(#value) || values::@is_bool(#value)
|
||||
@require $assignable(expected, $typeof(#value))
|
||||
@require $probability >= 0 && $probability <= 1.0
|
||||
*>
|
||||
macro @expect(#value, expected, $probability = 1.0) @builtin
|
||||
{
|
||||
$switch
|
||||
$case env::BUILTIN_EXPECT_IS_DISABLED:
|
||||
$switch
|
||||
$case env::BUILTIN_EXPECT_IS_DISABLED:
|
||||
return #value == expected;
|
||||
$case $probability == 1.0:
|
||||
$case $probability == 1.0:
|
||||
return $$expect(#value, ($typeof(#value))expected);
|
||||
$default:
|
||||
$default:
|
||||
return $$expect_with_probability(#value, expected, $probability);
|
||||
$endswitch
|
||||
$endswitch
|
||||
}
|
||||
|
||||
/**
|
||||
* Locality for prefetch, levels 0 - 3, corresponding
|
||||
* to "extremely local" to "no locality"
|
||||
**/
|
||||
<*
|
||||
Locality for prefetch, levels 0 - 3, corresponding
|
||||
to "extremely local" to "no locality"
|
||||
*>
|
||||
enum PrefetchLocality
|
||||
{
|
||||
NO_LOCALITY,
|
||||
@@ -277,13 +320,13 @@ enum PrefetchLocality
|
||||
VERY_NEAR,
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefetch a pointer.
|
||||
<*
|
||||
Prefetch a pointer.
|
||||
|
||||
* @param [in] ptr `Pointer to prefetch`
|
||||
* @param $locality `Locality ranging from none to extremely local`
|
||||
* @param $write `Prefetch for write, otherwise prefetch for read.`
|
||||
**/
|
||||
@param [in] ptr `Pointer to prefetch`
|
||||
@param $locality `Locality ranging from none to extremely local`
|
||||
@param $write `Prefetch for write, otherwise prefetch for read.`
|
||||
*>
|
||||
macro @prefetch(void* ptr, PrefetchLocality $locality = VERY_NEAR, bool $write = false) @builtin
|
||||
{
|
||||
$if !env::BUILTIN_PREFETCH_IS_DISABLED:
|
||||
@@ -300,22 +343,36 @@ macro swizzle2(v, v2, ...) @builtin
|
||||
{
|
||||
return $$swizzle2(v, v2, $vasplat);
|
||||
}
|
||||
<*
|
||||
Return the excuse in the Optional if it is Empty, otherwise
|
||||
return a null fault.
|
||||
|
||||
@require @typekind(#expr) == OPTIONAL : `@catch expects an Optional value`
|
||||
*>
|
||||
macro anyfault @catch(#expr) @builtin
|
||||
{
|
||||
if (catch f = #expr) return f;
|
||||
return anyfault {};
|
||||
}
|
||||
|
||||
<*
|
||||
Check if an Optional expression holds a value or is empty, returning true
|
||||
if it has a value.
|
||||
|
||||
@require @typekind(#expr) == OPTIONAL : `@ok expects an Optional value`
|
||||
*>
|
||||
macro bool @ok(#expr) @builtin
|
||||
{
|
||||
if (catch #expr) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
macro char[] @as_char_view(&value) @builtin
|
||||
<*
|
||||
@require $defined(&#value, (char*)&#value) "This must be a value that can be viewed as a char array"
|
||||
*>
|
||||
macro char[] @as_char_view(#value) @builtin
|
||||
{
|
||||
return ((char*)value)[:$sizeof(*value)];
|
||||
return ((char*)&#value)[:$sizeof(#value)];
|
||||
}
|
||||
|
||||
macro isz @str_find(String $string, String $needle) @builtin => $$str_find($string, $needle);
|
||||
@@ -323,21 +380,39 @@ macro String @str_upper(String $str) @builtin => $$str_upper($str);
|
||||
macro String @str_lower(String $str) @builtin => $$str_lower($str);
|
||||
macro uint @str_hash(String $str) @builtin => $$str_hash($str);
|
||||
|
||||
macro uint int.hash(int i) => i;
|
||||
macro uint uint.hash(uint i) => i;
|
||||
macro uint short.hash(short s) => s;
|
||||
macro uint ushort.hash(ushort s) => s;
|
||||
macro uint char.hash(char c) => c;
|
||||
macro uint ichar.hash(ichar c) => c;
|
||||
macro uint long.hash(long i) => (uint)((i >> 32) ^ i);
|
||||
macro uint ulong.hash(ulong i) => (uint)((i >> 32) ^ i);
|
||||
macro uint int128.hash(int128 i) => (uint)((i >> 96) ^ (i >> 64) ^ (i >> 32) ^ i);
|
||||
macro uint uint128.hash(uint128 i) => (uint)((i >> 96) ^ (i >> 64) ^ (i >> 32) ^ i);
|
||||
macro uint bool.hash(bool b) => (uint)b;
|
||||
macro uint typeid.hash(typeid t) => ((ulong)(uptr)t).hash();
|
||||
macro @generic_hash_core(h, value)
|
||||
{
|
||||
h ^= (uint)value; // insert lowest 32 bits
|
||||
h *= 0x96f59e5b; // diffuse them up
|
||||
h ^= h >> 16; // diffuse them down
|
||||
return h;
|
||||
}
|
||||
|
||||
macro @generic_hash(value)
|
||||
{
|
||||
uint h = @generic_hash_core((uint)0x3efd4391, value);
|
||||
$for (var $cnt = 4; $cnt < $sizeof(value); $cnt += 4)
|
||||
value >>= 32; // reduce value
|
||||
h = @generic_hash_core(h, value);
|
||||
$endfor
|
||||
return h;
|
||||
}
|
||||
|
||||
macro uint int.hash(int i) => @generic_hash(i);
|
||||
macro uint uint.hash(uint i) => @generic_hash(i);
|
||||
macro uint short.hash(short s) => @generic_hash(s);
|
||||
macro uint ushort.hash(ushort s) => @generic_hash(s);
|
||||
macro uint char.hash(char c) => @generic_hash(c);
|
||||
macro uint ichar.hash(ichar c) => @generic_hash(c);
|
||||
macro uint long.hash(long i) => @generic_hash(i);
|
||||
macro uint ulong.hash(ulong i) => @generic_hash(i);
|
||||
macro uint int128.hash(int128 i) => @generic_hash(i);
|
||||
macro uint uint128.hash(uint128 i) => @generic_hash(i);
|
||||
macro uint bool.hash(bool b) => @generic_hash(b);
|
||||
macro uint typeid.hash(typeid t) => @generic_hash(((ulong)(uptr)t));
|
||||
macro uint String.hash(String c) => (uint)fnv32a::encode(c);
|
||||
macro uint char[].hash(char[] c) => (uint)fnv32a::encode(c);
|
||||
macro uint void*.hash(void* ptr) => ((ulong)(uptr)ptr).hash();
|
||||
macro uint void*.hash(void* ptr) => @generic_hash(((ulong)(uptr)ptr));
|
||||
|
||||
distinct EmptySlot = void*;
|
||||
const EmptySlot EMPTY_MACRO_SLOT @builtin = null;
|
||||
@@ -345,9 +420,9 @@ macro @is_empty_macro_slot(#arg) @builtin => @typeis(#arg, EmptySlot);
|
||||
macro @is_valid_macro_slot(#arg) @builtin => !@typeis(#arg, EmptySlot);
|
||||
|
||||
const MAX_FRAMEADDRESS = 128;
|
||||
/**
|
||||
* @require n >= 0
|
||||
**/
|
||||
<*
|
||||
@require n >= 0
|
||||
*>
|
||||
macro void* get_frameaddress(int n)
|
||||
{
|
||||
if (n > MAX_FRAMEADDRESS) return null;
|
||||
@@ -486,9 +561,9 @@ macro void* get_frameaddress(int n)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require n >= 0
|
||||
**/
|
||||
<*
|
||||
@require n >= 0
|
||||
*>
|
||||
macro void* get_returnaddress(int n)
|
||||
{
|
||||
if (n > MAX_FRAMEADDRESS) return null;
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::core::builtin;
|
||||
|
||||
/**
|
||||
* @require types::@comparable_value(a) && types::@comparable_value(b)
|
||||
**/
|
||||
<*
|
||||
@require types::@comparable_value(a) && types::@comparable_value(b)
|
||||
*>
|
||||
macro less(a, b) @builtin
|
||||
{
|
||||
$switch
|
||||
@@ -18,9 +18,9 @@ macro less(a, b) @builtin
|
||||
$endswitch
|
||||
}
|
||||
|
||||
/**
|
||||
* @require types::@comparable_value(a) && types::@comparable_value(b)
|
||||
**/
|
||||
<*
|
||||
@require types::@comparable_value(a) && types::@comparable_value(b)
|
||||
*>
|
||||
macro less_eq(a, b) @builtin
|
||||
{
|
||||
$switch
|
||||
@@ -33,9 +33,9 @@ macro less_eq(a, b) @builtin
|
||||
$endswitch
|
||||
}
|
||||
|
||||
/**
|
||||
* @require types::@comparable_value(a) && types::@comparable_value(b)
|
||||
**/
|
||||
<*
|
||||
@require types::@comparable_value(a) && types::@comparable_value(b)
|
||||
*>
|
||||
macro greater(a, b) @builtin
|
||||
{
|
||||
$switch
|
||||
@@ -48,9 +48,9 @@ macro greater(a, b) @builtin
|
||||
$endswitch
|
||||
}
|
||||
|
||||
/**
|
||||
* @require types::@comparable_value(a) && types::@comparable_value(b)
|
||||
**/
|
||||
<*
|
||||
@require types::@comparable_value(a) && types::@comparable_value(b)
|
||||
*>
|
||||
macro int compare_to(a, b) @builtin
|
||||
{
|
||||
$switch
|
||||
@@ -62,9 +62,9 @@ macro int compare_to(a, b) @builtin
|
||||
return (int)(a > b) - (int)(a < b);
|
||||
$endswitch
|
||||
}
|
||||
/**
|
||||
* @require types::@comparable_value(a) && types::@comparable_value(b)
|
||||
**/
|
||||
<*
|
||||
@require types::@comparable_value(a) && types::@comparable_value(b)
|
||||
*>
|
||||
macro greater_eq(a, b) @builtin
|
||||
{
|
||||
$switch
|
||||
@@ -77,9 +77,9 @@ macro greater_eq(a, b) @builtin
|
||||
$endswitch
|
||||
}
|
||||
|
||||
/**
|
||||
* @require types::@equatable_value(a) && types::@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
|
||||
{
|
||||
$switch
|
||||
|
||||
@@ -29,6 +29,12 @@ def CUChar = char;
|
||||
|
||||
def CChar = $typefrom($$C_CHAR_IS_SIGNED ? ichar.typeid : char.typeid);
|
||||
|
||||
enum CBool : char
|
||||
{
|
||||
FALSE,
|
||||
TRUE
|
||||
}
|
||||
|
||||
// Helper macros
|
||||
macro typeid signed_int_from_bitsize(usz $bitsize) @private
|
||||
{
|
||||
|
||||
@@ -9,10 +9,10 @@ const uint UTF16_SURROGATE_BITS @private = 10;
|
||||
const uint UTF16_SURROGATE_LOW_VALUE @private = 0xDC00;
|
||||
const uint UTF16_SURROGATE_HIGH_VALUE @private = 0xD800;
|
||||
|
||||
/**
|
||||
* @param c `The utf32 codepoint to convert`
|
||||
* @param [out] output `the resulting buffer`
|
||||
**/
|
||||
<*
|
||||
@param c `The utf32 codepoint to convert`
|
||||
@param [out] output `the resulting buffer`
|
||||
*>
|
||||
fn usz! char32_to_utf8(Char32 c, char[] output)
|
||||
{
|
||||
if (!output.len) return UnicodeResult.CONVERSION_FAILED?;
|
||||
@@ -45,12 +45,12 @@ fn usz! char32_to_utf8(Char32 c, char[] output)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a code pointer into 1-2 UTF16 characters.
|
||||
*
|
||||
* @param c `The character to convert.`
|
||||
* @param [inout] output `the resulting UTF16 buffer to write to.`
|
||||
**/
|
||||
<*
|
||||
Convert a code pointer into 1-2 UTF16 characters.
|
||||
|
||||
@param c `The character to convert.`
|
||||
@param [inout] output `the resulting UTF16 buffer to write to.`
|
||||
*>
|
||||
fn void char32_to_utf16_unsafe(Char32 c, Char16** output)
|
||||
{
|
||||
if (c < UTF16_SURROGATE_OFFSET)
|
||||
@@ -66,13 +66,13 @@ fn void char32_to_utf16_unsafe(Char32 c, Char16** output)
|
||||
(*output)++[0] = (Char16)low;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert 1-2 UTF16 data points into UTF8.
|
||||
*
|
||||
* @param [in] ptr `The UTF16 data to convert.`
|
||||
* @param [inout] available `amount of UTF16 data available.`
|
||||
* @param [inout] output `the resulting utf8 buffer to write to.`
|
||||
**/
|
||||
<*
|
||||
Convert 1-2 UTF16 data points into UTF8.
|
||||
|
||||
@param [in] ptr `The UTF16 data to convert.`
|
||||
@param [inout] available `amount of UTF16 data available.`
|
||||
@param [inout] output `the resulting utf8 buffer to write to.`
|
||||
*>
|
||||
fn void! char16_to_utf8_unsafe(Char16 *ptr, usz *available, char** output)
|
||||
{
|
||||
Char16 high = *ptr;
|
||||
@@ -100,10 +100,10 @@ fn void! char16_to_utf8_unsafe(Char16 *ptr, usz *available, char** output)
|
||||
char32_to_utf8_unsafe(uc, output);
|
||||
*available = 2;
|
||||
}
|
||||
/**
|
||||
* @param c `The utf32 codepoint to convert`
|
||||
* @param [inout] output `the resulting buffer`
|
||||
**/
|
||||
<*
|
||||
@param c `The utf32 codepoint to convert`
|
||||
@param [inout] output `the resulting buffer`
|
||||
*>
|
||||
fn usz char32_to_utf8_unsafe(Char32 c, char** output)
|
||||
{
|
||||
switch
|
||||
@@ -129,11 +129,11 @@ fn usz char32_to_utf8_unsafe(Char32 c, char** output)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [in] ptr `pointer to the first character to parse`
|
||||
* @param [inout] size `Set to max characters to read, set to characters read`
|
||||
* @return `the parsed 32 bit codepoint`
|
||||
**/
|
||||
<*
|
||||
@param [in] ptr `pointer to the first character to parse`
|
||||
@param [inout] size `Set to max characters to read, set to characters read`
|
||||
@return `the parsed 32 bit codepoint`
|
||||
*>
|
||||
fn Char32! utf8_to_char32(char* ptr, usz* size)
|
||||
{
|
||||
usz max_size = *size;
|
||||
@@ -184,10 +184,10 @@ fn Char32! utf8_to_char32(char* ptr, usz* size)
|
||||
return uc + c & 0x3F;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param utf8 `An UTF-8 encoded slice of bytes`
|
||||
* @return `the number of encoded code points`
|
||||
**/
|
||||
<*
|
||||
@param utf8 `An UTF-8 encoded slice of bytes`
|
||||
@return `the number of encoded code points`
|
||||
*>
|
||||
fn usz utf8_codepoints(String utf8)
|
||||
{
|
||||
usz len = 0;
|
||||
@@ -198,11 +198,11 @@ fn usz utf8_codepoints(String utf8)
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the UTF8 length required to encode an UTF32 array.
|
||||
* @param [in] utf32 `the utf32 data to calculate from`
|
||||
* @return `the length of the resulting UTF8 array`
|
||||
**/
|
||||
<*
|
||||
Calculate the UTF8 length required to encode an UTF32 array.
|
||||
@param [in] utf32 `the utf32 data to calculate from`
|
||||
@return `the length of the resulting UTF8 array`
|
||||
*>
|
||||
fn usz utf8len_for_utf32(Char32[] utf32)
|
||||
{
|
||||
usz len = 0;
|
||||
@@ -223,11 +223,11 @@ fn usz utf8len_for_utf32(Char32[] utf32)
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the UTF8 length required to encode an UTF16 array.
|
||||
* @param [in] utf16 `the utf16 data to calculate from`
|
||||
* @return `the length of the resulting UTF8 array`
|
||||
**/
|
||||
<*
|
||||
Calculate the UTF8 length required to encode an UTF16 array.
|
||||
@param [in] utf16 `the utf16 data to calculate from`
|
||||
@return `the length of the resulting UTF8 array`
|
||||
*>
|
||||
fn usz utf8len_for_utf16(Char16[] utf16)
|
||||
{
|
||||
usz len = 0;
|
||||
@@ -255,11 +255,11 @@ fn usz utf8len_for_utf16(Char16[] utf16)
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the UTF16 length required to encode a UTF8 array.
|
||||
* @param utf8 `the utf8 data to calculate from`
|
||||
* @return `the length of the resulting UTF16 array`
|
||||
**/
|
||||
<*
|
||||
Calculate the UTF16 length required to encode a UTF8 array.
|
||||
@param utf8 `the utf8 data to calculate from`
|
||||
@return `the length of the resulting UTF16 array`
|
||||
*>
|
||||
fn usz utf16len_for_utf8(String utf8)
|
||||
{
|
||||
usz len = utf8.len;
|
||||
@@ -279,10 +279,10 @@ fn usz utf16len_for_utf8(String utf8)
|
||||
return len16;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [in] utf32 `the UTF32 array to check the length for`
|
||||
* @return `the required length of an UTF16 array to hold the UTF32 data.`
|
||||
**/
|
||||
<*
|
||||
@param [in] utf32 `the UTF32 array to check the length for`
|
||||
@return `the required length of an UTF16 array to hold the UTF32 data.`
|
||||
*>
|
||||
fn usz utf16len_for_utf32(Char32[] utf32)
|
||||
{
|
||||
usz len = utf32.len;
|
||||
@@ -293,13 +293,13 @@ fn usz utf16len_for_utf32(Char32[] utf32)
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an UTF32 array to an UTF8 array.
|
||||
*
|
||||
* @param [in] utf32
|
||||
* @param [out] utf8_buffer
|
||||
* @return `the number of bytes written.`
|
||||
**/
|
||||
<*
|
||||
Convert an UTF32 array to an UTF8 array.
|
||||
|
||||
@param [in] utf32
|
||||
@param [out] utf8_buffer
|
||||
@return `the number of bytes written.`
|
||||
*>
|
||||
fn usz! utf32to8(Char32[] utf32, char[] utf8_buffer)
|
||||
{
|
||||
char[] buffer = utf8_buffer;
|
||||
@@ -313,13 +313,13 @@ fn usz! utf32to8(Char32[] utf32, char[] utf8_buffer)
|
||||
return utf8_buffer.len - buffer.len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an UTF8 array to an UTF32 array.
|
||||
*
|
||||
* @param [in] utf8
|
||||
* @param [out] utf32_buffer
|
||||
* @return `the number of Char32s written.`
|
||||
**/
|
||||
<*
|
||||
Convert an UTF8 array to an UTF32 array.
|
||||
|
||||
@param [in] utf8
|
||||
@param [out] utf32_buffer
|
||||
@return `the number of Char32s written.`
|
||||
*>
|
||||
fn usz! utf8to32(String utf8, Char32[] utf32_buffer)
|
||||
{
|
||||
usz len = utf8.len;
|
||||
@@ -339,14 +339,14 @@ fn usz! utf8to32(String utf8, Char32[] utf32_buffer)
|
||||
return len32;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy an array of UTF16 data into an UTF8 buffer without bounds
|
||||
* checking. This will assume the buffer is sufficiently large to hold
|
||||
* the converted data.
|
||||
*
|
||||
* @param [in] utf16 `The UTF16 array containing the data to convert.`
|
||||
* @param [out] utf8_buffer `the (sufficiently large) buffer to hold the UTF16 data.`
|
||||
**/
|
||||
<*
|
||||
Copy an array of UTF16 data into an UTF8 buffer without bounds
|
||||
checking. This will assume the buffer is sufficiently large to hold
|
||||
the converted data.
|
||||
|
||||
@param [in] utf16 `The UTF16 array containing the data to convert.`
|
||||
@param [out] utf8_buffer `the (sufficiently large) buffer to hold the UTF16 data.`
|
||||
*>
|
||||
fn void! utf16to8_unsafe(Char16[] utf16, char* utf8_buffer)
|
||||
{
|
||||
usz len16 = utf16.len;
|
||||
@@ -358,14 +358,14 @@ fn void! utf16to8_unsafe(Char16[] utf16, char* utf8_buffer)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy an array of UTF8 data into an UTF32 buffer without bounds
|
||||
* checking. This will assume the buffer is sufficiently large to hold
|
||||
* the converted data.
|
||||
*
|
||||
* @param [in] utf8 `The UTF8 buffer containing the data to convert.`
|
||||
* @param [out] utf32_buffer `the (sufficiently large) buffer to hold the UTF8 data.`
|
||||
**/
|
||||
<*
|
||||
Copy an array of UTF8 data into an UTF32 buffer without bounds
|
||||
checking. This will assume the buffer is sufficiently large to hold
|
||||
the converted data.
|
||||
|
||||
@param [in] utf8 `The UTF8 buffer containing the data to convert.`
|
||||
@param [out] utf32_buffer `the (sufficiently large) buffer to hold the UTF8 data.`
|
||||
*>
|
||||
fn void! utf8to32_unsafe(String utf8, Char32* utf32_buffer)
|
||||
{
|
||||
usz len = utf8.len;
|
||||
@@ -378,14 +378,14 @@ fn void! utf8to32_unsafe(String utf8, Char32* utf32_buffer)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy an array of UTF8 data into an UTF16 buffer without bounds
|
||||
* checking. This will assume the buffer is sufficiently large to hold
|
||||
* the converted data.
|
||||
*
|
||||
* @param [in] utf8 `The UTF8 buffer containing the data to convert.`
|
||||
* @param [out] utf16_buffer `the (sufficiently large) buffer to hold the UTF8 data.`
|
||||
**/
|
||||
<*
|
||||
Copy an array of UTF8 data into an UTF16 buffer without bounds
|
||||
checking. This will assume the buffer is sufficiently large to hold
|
||||
the converted data.
|
||||
|
||||
@param [in] utf8 `The UTF8 buffer containing the data to convert.`
|
||||
@param [out] utf16_buffer `the (sufficiently large) buffer to hold the UTF8 data.`
|
||||
*>
|
||||
fn void! utf8to16_unsafe(String utf8, Char16* utf16_buffer)
|
||||
{
|
||||
usz len = utf8.len;
|
||||
@@ -398,14 +398,14 @@ fn void! utf8to16_unsafe(String utf8, Char16* utf16_buffer)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy an array of UTF32 code points into an UTF8 buffer without bounds
|
||||
* checking. This will assume the buffer is sufficiently large to hold
|
||||
* the converted data.
|
||||
*
|
||||
* @param [in] utf32 `The UTF32 buffer containing the data to convert.`
|
||||
* @param [out] utf8_buffer `the (sufficiently large) buffer to hold the UTF8 data.`
|
||||
**/
|
||||
<*
|
||||
Copy an array of UTF32 code points into an UTF8 buffer without bounds
|
||||
checking. This will assume the buffer is sufficiently large to hold
|
||||
the converted data.
|
||||
|
||||
@param [in] utf32 `The UTF32 buffer containing the data to convert.`
|
||||
@param [out] utf8_buffer `the (sufficiently large) buffer to hold the UTF8 data.`
|
||||
*>
|
||||
fn void utf32to8_unsafe(Char32[] utf32, char* utf8_buffer)
|
||||
{
|
||||
char* start = utf8_buffer;
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
module std::core::dstring;
|
||||
import std::io;
|
||||
|
||||
distinct DString (OutStream) = void*;
|
||||
distinct DString (OutStream) = DStringOpaque*;
|
||||
distinct DStringOpaque = void;
|
||||
|
||||
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())
|
||||
{
|
||||
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
|
||||
@@ -18,9 +19,9 @@ fn DString DString.new_init(&self, usz capacity = MIN_CAPACITY, Allocator alloca
|
||||
return *self = (DString)data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require !self.data() "String already initialized"
|
||||
**/
|
||||
<*
|
||||
@require !self.data() "String already initialized"
|
||||
*>
|
||||
fn DString DString.temp_init(&self, usz capacity = MIN_CAPACITY)
|
||||
{
|
||||
self.new_init(capacity, allocator::temp()) @inline;
|
||||
@@ -77,24 +78,24 @@ fn void DString.replace(&self, String needle, String replacement)
|
||||
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]);
|
||||
{
|
||||
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]);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -132,15 +133,15 @@ fn usz DString.capacity(self)
|
||||
return self.data().capacity;
|
||||
}
|
||||
|
||||
fn usz DString.len(&self) @dynamic
|
||||
fn usz DString.len(&self) @dynamic @operator(len)
|
||||
{
|
||||
if (!*self) return 0;
|
||||
return self.data().len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require new_size <= self.len()
|
||||
*/
|
||||
<*
|
||||
@require new_size <= self.len()
|
||||
*>
|
||||
fn void DString.chop(self, usz new_size)
|
||||
{
|
||||
if (!self) return;
|
||||
@@ -154,19 +155,39 @@ fn String DString.str_view(self)
|
||||
return (String)data.chars[:data.len];
|
||||
}
|
||||
|
||||
fn void DString.append_utf32(&self, Char32[] chars)
|
||||
<*
|
||||
@require index < self.len()
|
||||
@require self.data() "Empty string"
|
||||
*>
|
||||
fn char DString.char_at(self, usz index) @operator([])
|
||||
{
|
||||
return self.data().chars[index];
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.len()
|
||||
@require self.data() "Empty string"
|
||||
*>
|
||||
fn char* DString.char_ref(&self, usz index) @operator(&[])
|
||||
{
|
||||
return &self.data().chars[index];
|
||||
}
|
||||
|
||||
fn usz DString.append_utf32(&self, Char32[] chars)
|
||||
{
|
||||
self.reserve(chars.len);
|
||||
usz end = self.len();
|
||||
foreach (Char32 c : chars)
|
||||
{
|
||||
self.append_char32(c);
|
||||
}
|
||||
return self.data().len - end;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index < self.len()
|
||||
**/
|
||||
fn void DString.set(self, usz index, char c)
|
||||
<*
|
||||
@require index < self.len()
|
||||
*>
|
||||
fn void DString.set(self, usz index, char c) @operator([]=)
|
||||
{
|
||||
self.data().chars[index] = c;
|
||||
}
|
||||
@@ -182,10 +203,10 @@ fn void DString.append_repeat(&self, char c, usz times)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require c <= 0x10ffff
|
||||
*/
|
||||
fn void DString.append_char32(&self, Char32 c)
|
||||
<*
|
||||
@require c <= 0x10ffff
|
||||
*>
|
||||
fn usz DString.append_char32(&self, Char32 c)
|
||||
{
|
||||
char[4] buffer @noinit;
|
||||
char* p = &buffer;
|
||||
@@ -194,6 +215,7 @@ fn void DString.append_char32(&self, Char32 c)
|
||||
StringData* data = self.data();
|
||||
data.chars[data.len:n] = buffer[:n];
|
||||
data.len += n;
|
||||
return n;
|
||||
}
|
||||
|
||||
fn DString DString.tcopy(&self) => self.copy(allocator::temp());
|
||||
@@ -330,20 +352,20 @@ fn void DString.append_char(&self, char c)
|
||||
data.chars[data.len++] = c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require start < self.len()
|
||||
* @require end < self.len()
|
||||
* @require end >= start "End must be same or equal to the start"
|
||||
**/
|
||||
<*
|
||||
@require start < self.len()
|
||||
@require end < self.len()
|
||||
@require end >= start "End must be same or equal to the start"
|
||||
*>
|
||||
fn void DString.delete_range(&self, usz start, usz end)
|
||||
{
|
||||
self.delete(start, end - start + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require start < self.len()
|
||||
* @require start + len <= self.len()
|
||||
**/
|
||||
<*
|
||||
@require start < self.len()
|
||||
@require start + len <= self.len()
|
||||
*>
|
||||
fn void DString.delete(&self, usz start, usz len = 1)
|
||||
{
|
||||
if (!len) return;
|
||||
@@ -387,7 +409,10 @@ macro void DString.append(&self, value)
|
||||
$endswitch
|
||||
}
|
||||
|
||||
fn void DString.insert_at(&self, usz index, String s)
|
||||
<*
|
||||
@require index <= self.len()
|
||||
*>
|
||||
fn void DString.insert_chars_at(&self, usz index, String s)
|
||||
{
|
||||
if (s.len == 0) return;
|
||||
self.reserve(s.len);
|
||||
@@ -419,6 +444,103 @@ fn void DString.insert_at(&self, usz index, String s)
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@require index <= self.len()
|
||||
*>
|
||||
fn void DString.insert_string_at(&self, usz index, DString str)
|
||||
{
|
||||
StringData* other = str.data();
|
||||
if (!other) return;
|
||||
self.insert_at(index, str.str_view());
|
||||
}
|
||||
|
||||
<*
|
||||
@require index <= self.len()
|
||||
*>
|
||||
fn void DString.insert_char_at(&self, usz index, char c)
|
||||
{
|
||||
self.reserve(1);
|
||||
StringData* data = self.data();
|
||||
|
||||
char* start = &data.chars[index];
|
||||
mem::move(start + 1, start, self.len() - index);
|
||||
data.chars[index] = c;
|
||||
data.len++;
|
||||
}
|
||||
|
||||
<*
|
||||
@require index <= self.len()
|
||||
*>
|
||||
fn usz DString.insert_char32_at(&self, usz index, Char32 c)
|
||||
{
|
||||
char[4] buffer @noinit;
|
||||
char* p = &buffer;
|
||||
usz n = conv::char32_to_utf8_unsafe(c, &p);
|
||||
|
||||
self.reserve(n);
|
||||
StringData* data = self.data();
|
||||
|
||||
char* start = &data.chars[index];
|
||||
mem::move(start + n, start, self.len() - index);
|
||||
data.chars[index:n] = buffer[:n];
|
||||
data.len += n;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
<*
|
||||
@require index <= self.len()
|
||||
*>
|
||||
fn usz DString.insert_utf32_at(&self, usz index, Char32[] chars)
|
||||
{
|
||||
usz n = conv::utf8len_for_utf32(chars);
|
||||
|
||||
self.reserve(n);
|
||||
StringData* data = self.data();
|
||||
|
||||
char* start = &data.chars[index];
|
||||
mem::move(start + n, start, self.len() - index);
|
||||
|
||||
char[4] buffer @noinit;
|
||||
|
||||
foreach(c : chars)
|
||||
{
|
||||
char* p = &buffer;
|
||||
usz m = conv::char32_to_utf8_unsafe(c, &p);
|
||||
data.chars[index:m] = buffer[:m];
|
||||
index += m;
|
||||
}
|
||||
|
||||
data.len += n;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
macro void DString.insert_at(&self, usz index, value)
|
||||
{
|
||||
var $Type = $typeof(value);
|
||||
$switch ($Type)
|
||||
$case char:
|
||||
$case ichar:
|
||||
self.insert_char_at(index, value);
|
||||
$case DString:
|
||||
self.insert_string_at(index, value);
|
||||
$case String:
|
||||
self.insert_chars_at(index, value);
|
||||
$case Char32:
|
||||
self.insert_char32_at(index, value);
|
||||
$default:
|
||||
$switch
|
||||
$case $defined((Char32)value):
|
||||
self.insert_char32_at(index, (Char32)value);
|
||||
$case $defined((String)value):
|
||||
self.insert_chars_at(index, (String)value);
|
||||
$default:
|
||||
$error "Unsupported type for insert";
|
||||
$endswitch
|
||||
$endswitch
|
||||
}
|
||||
|
||||
fn usz! DString.appendf(&self, String format, args...) @maydiscard
|
||||
{
|
||||
if (!self.data()) self.new_init(format.len + 20);
|
||||
@@ -467,6 +589,19 @@ fn void! out_string_append_fn(void* data, char c) @private
|
||||
s.append_char(c);
|
||||
}
|
||||
|
||||
fn void DString.reverse(self)
|
||||
{
|
||||
StringData *data = self.data();
|
||||
if (!data) return;
|
||||
isz mid = data.len / 2;
|
||||
for (isz i = 0; i < mid; i++)
|
||||
{
|
||||
char temp = data.chars[i];
|
||||
isz reverse_index = data.len - 1 - i;
|
||||
data.chars[i] = data.chars[reverse_index];
|
||||
data.chars[reverse_index] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
fn StringData* DString.data(self) @inline @private
|
||||
{
|
||||
|
||||
@@ -115,13 +115,15 @@ enum ArchType
|
||||
XTENSA, // Xtensa
|
||||
}
|
||||
|
||||
const OsType OS_TYPE = (OsType)$$OS_TYPE;
|
||||
const ArchType ARCH_TYPE = (ArchType)$$ARCH_TYPE;
|
||||
const String COMPILER_BUILD_HASH = $$BUILD_HASH;
|
||||
const String COMPILER_BUILD_DATE = $$BUILD_DATE;
|
||||
const OsType OS_TYPE = OsType.from_ordinal($$OS_TYPE);
|
||||
const ArchType ARCH_TYPE = ArchType.from_ordinal($$ARCH_TYPE);
|
||||
const bool ARCH_32_BIT = $$REGISTER_SIZE == 32;
|
||||
const bool ARCH_64_BIT = $$REGISTER_SIZE == 64;
|
||||
const bool LIBC = $$COMPILER_LIBC_AVAILABLE;
|
||||
const bool NO_LIBC = !$$COMPILER_LIBC_AVAILABLE;
|
||||
const CompilerOptLevel COMPILER_OPT_LEVEL = (CompilerOptLevel)$$COMPILER_OPT_LEVEL;
|
||||
const CompilerOptLevel COMPILER_OPT_LEVEL = CompilerOptLevel.from_ordinal($$COMPILER_OPT_LEVEL);
|
||||
const bool BIG_ENDIAN = $$PLATFORM_BIG_ENDIAN;
|
||||
const bool I128_NATIVE_SUPPORT = $$PLATFORM_I128_SUPPORTED;
|
||||
const bool F16_SUPPORT = $$PLATFORM_F16_SUPPORTED;
|
||||
@@ -133,7 +135,7 @@ const bool BACKTRACE = $$BACKTRACE;
|
||||
const usz LLVM_VERSION = $$LLVM_VERSION;
|
||||
const bool BENCHMARKING = $$BENCHMARKING;
|
||||
const bool TESTING = $$TESTING;
|
||||
const MemoryEnvironment MEMORY_ENV = (MemoryEnvironment)$$MEMORY_ENVIRONMENT;
|
||||
const MemoryEnvironment MEMORY_ENV = MemoryEnvironment.from_ordinal($$MEMORY_ENVIRONMENT);
|
||||
const bool TRACK_MEMORY = DEBUG_SYMBOLS && (COMPILER_SAFE_MODE || TESTING);
|
||||
const bool X86_64 = ARCH_TYPE == X86_64;
|
||||
const bool X86 = ARCH_TYPE == X86;
|
||||
@@ -146,6 +148,7 @@ const bool POSIX = LIBC && os_is_posix();
|
||||
const bool OPENBSD = LIBC && OS_TYPE == OPENBSD;
|
||||
const bool FREEBSD = LIBC && OS_TYPE == FREEBSD;
|
||||
const bool NETBSD = LIBC && OS_TYPE == NETBSD;
|
||||
const bool BSD_FAMILY = env::FREEBSD || env::OPENBSD || env::NETBSD;
|
||||
const bool WASI = LIBC && OS_TYPE == WASI;
|
||||
const bool WASM_NOLIBC @builtin = !LIBC && ARCH_TYPE == ArchType.WASM32 || ARCH_TYPE == ArchType.WASM64;
|
||||
const bool ADDRESS_SANITIZER = $$ADDRESS_SANITIZER;
|
||||
|
||||
@@ -13,191 +13,204 @@ 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.
|
||||
*
|
||||
* @param ptr "The pointer address to load from."
|
||||
* @param mask "The mask for the load"
|
||||
* @param passthru "The value to use for non masked values"
|
||||
* @require $assignable(&&passthru, $typeof(ptr)) : "Pointer and passthru must match"
|
||||
* @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
|
||||
* @require passthru.len == mask.len : "Mask and passthru must have the same length"
|
||||
*
|
||||
* @return "A vector with the loaded values where the mask is true, passthru where the mask is false"
|
||||
**/
|
||||
<*
|
||||
Load a vector from memory according to a mask assuming default alignment.
|
||||
|
||||
@param ptr "The pointer address to load from."
|
||||
@param mask "The mask for the load"
|
||||
@param passthru "The value to use for non masked values"
|
||||
@require $assignable(&&passthru, $typeof(ptr)) : "Pointer and passthru must match"
|
||||
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
|
||||
@require passthru.len == mask.len : "Mask and passthru must have the same length"
|
||||
|
||||
@return "A vector with the loaded values where the mask is true, passthru where the mask is false"
|
||||
*>
|
||||
macro masked_load(ptr, bool[<*>] mask, passthru)
|
||||
{
|
||||
return $$masked_load(ptr, mask, passthru, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a vector from memory according to a mask.
|
||||
*
|
||||
* @param ptr "The pointer address to load from."
|
||||
* @param mask "The mask for the load"
|
||||
* @param passthru "The value to use for non masked values"
|
||||
* @param $alignment "The alignment to assume for the pointer"
|
||||
*
|
||||
* @require $assignable(&&passthru, $typeof(ptr)) : "Pointer and passthru must match"
|
||||
* @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
|
||||
* @require passthru.len == mask.len : "Mask and passthru must have the same length"
|
||||
* @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||
*
|
||||
* @return "A vector with the loaded values where the mask is true, passthru where the mask is false"
|
||||
**/
|
||||
<*
|
||||
Load a vector from memory according to a mask.
|
||||
|
||||
@param ptr "The pointer address to load from."
|
||||
@param mask "The mask for the load"
|
||||
@param passthru "The value to use for non masked values"
|
||||
@param $alignment "The alignment to assume for the pointer"
|
||||
|
||||
@require $assignable(&&passthru, $typeof(ptr)) : "Pointer and passthru must match"
|
||||
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
|
||||
@require passthru.len == mask.len : "Mask and passthru must have the same length"
|
||||
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||
|
||||
@return "A vector with the loaded values where the mask is true, passthru where the mask is false"
|
||||
*>
|
||||
macro @masked_load_aligned(ptr, bool[<*>] mask, passthru, usz $alignment)
|
||||
{
|
||||
return $$masked_load(ptr, mask, passthru, $alignment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load values from a pointer vector, assuming default alignment.
|
||||
*
|
||||
* @param ptrvec "The vector of pointers to load from."
|
||||
* @param mask "The mask for the load"
|
||||
* @param passthru "The value to use for non masked values"
|
||||
*
|
||||
* @require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
|
||||
* @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
|
||||
* @require $assignable(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match"
|
||||
* @require 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"
|
||||
*
|
||||
* @return "A vector with the loaded values where the mask is true, passthru where the mask is false"
|
||||
**/
|
||||
<*
|
||||
Load values from a pointer vector, assuming default alignment.
|
||||
|
||||
@param ptrvec "The vector of pointers to load from."
|
||||
@param mask "The mask for the load"
|
||||
@param passthru "The value to use for non masked values"
|
||||
|
||||
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
|
||||
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
|
||||
@require $assignable(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match"
|
||||
@require 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"
|
||||
|
||||
@return "A vector with the loaded values where the mask is true, passthru where the mask is false"
|
||||
*>
|
||||
macro gather(ptrvec, bool[<*>] mask, passthru)
|
||||
{
|
||||
return $$gather(ptrvec, mask, passthru, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load values from a pointer vector.
|
||||
*
|
||||
* @param ptrvec "The vector of pointers to load from."
|
||||
* @param mask "The mask for the load"
|
||||
* @param passthru "The value to use for non masked values"
|
||||
* @param $alignment "The alignment to assume for the pointers"
|
||||
*
|
||||
* @require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
|
||||
* @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
|
||||
* @require $assignable(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match"
|
||||
* @require passthru.len == mask.len : "Mask and passthru must have the same length"
|
||||
* @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
|
||||
* @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||
*
|
||||
* @return "A vector with the loaded values where the mask is true, passthru where the mask is false"
|
||||
**/
|
||||
<*
|
||||
Load values from a pointer vector.
|
||||
|
||||
@param ptrvec "The vector of pointers to load from."
|
||||
@param mask "The mask for the load"
|
||||
@param passthru "The value to use for non masked values"
|
||||
@param $alignment "The alignment to assume for the pointers"
|
||||
|
||||
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
|
||||
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
|
||||
@require $assignable(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match"
|
||||
@require passthru.len == mask.len : "Mask and passthru must have the same length"
|
||||
@require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
|
||||
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||
|
||||
@return "A vector with the loaded values where the mask is true, passthru where the mask is false"
|
||||
*>
|
||||
macro @gather_aligned(ptrvec, bool[<*>] mask, passthru, usz $alignment)
|
||||
{
|
||||
return $$gather(ptrvec, mask, passthru, $alignment);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Store parts of a vector according to the mask, assuming default alignment.
|
||||
*
|
||||
* @param ptr "The pointer address to store to."
|
||||
* @param value "The value to store masked"
|
||||
* @param mask "The mask for the store"
|
||||
*
|
||||
* @require $assignable(&&value, $typeof(ptr)) : "Pointer and value must match"
|
||||
* @require @typekind(value) == VECTOR : "Expected value to be a vector"
|
||||
* @require value.len == mask.len : "Mask and value must have the same length"
|
||||
**/
|
||||
<*
|
||||
Store parts of a vector according to the mask, assuming default alignment.
|
||||
|
||||
@param ptr "The pointer address to store to."
|
||||
@param value "The value to store masked"
|
||||
@param mask "The mask for the store"
|
||||
|
||||
@require $assignable(&&value, $typeof(ptr)) : "Pointer and value must match"
|
||||
@require @typekind(value) == VECTOR : "Expected value to be a vector"
|
||||
@require value.len == mask.len : "Mask and value must have the same length"
|
||||
*>
|
||||
macro masked_store(ptr, value, bool[<*>] mask)
|
||||
{
|
||||
return $$masked_store(ptr, value, mask, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ptr "The pointer address to store to."
|
||||
* @param value "The value to store masked"
|
||||
* @param mask "The mask for the store"
|
||||
* @param $alignment "The alignment of the pointer"
|
||||
*
|
||||
* @require $assignable(&&value, $typeof(ptr)) : "Pointer and value must match"
|
||||
* @require @typekind(value) == VECTOR : "Expected value to be a vector"
|
||||
* @require value.len == mask.len : "Mask and value must have the same length"
|
||||
* @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||
*
|
||||
**/
|
||||
<*
|
||||
@param ptr "The pointer address to store to."
|
||||
@param value "The value to store masked"
|
||||
@param mask "The mask for the store"
|
||||
@param $alignment "The alignment of the pointer"
|
||||
|
||||
@require $assignable(&&value, $typeof(ptr)) : "Pointer and value must match"
|
||||
@require @typekind(value) == VECTOR : "Expected value to be a vector"
|
||||
@require value.len == mask.len : "Mask and value must have the same length"
|
||||
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||
|
||||
*>
|
||||
macro @masked_store_aligned(ptr, value, bool[<*>] mask, usz $alignment)
|
||||
{
|
||||
return $$masked_store(ptr, value, mask, $alignment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ptrvec "The vector pointer containing the addresses to store to."
|
||||
* @param value "The value to store masked"
|
||||
* @param mask "The mask for the store"
|
||||
* @require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
|
||||
* @require @typekind(value) == VECTOR : "Expected value to be a vector"
|
||||
* @require $assignable(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match"
|
||||
* @require 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"
|
||||
*
|
||||
**/
|
||||
<*
|
||||
@param ptrvec "The vector pointer containing the addresses to store to."
|
||||
@param value "The value to store masked"
|
||||
@param mask "The mask for the store"
|
||||
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
|
||||
@require @typekind(value) == VECTOR : "Expected value to be a vector"
|
||||
@require $assignable(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match"
|
||||
@require 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"
|
||||
|
||||
*>
|
||||
macro scatter(ptrvec, value, bool[<*>] mask)
|
||||
{
|
||||
return $$scatter(ptrvec, value, mask, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ptrvec "The vector pointer containing the addresses to store to."
|
||||
* @param value "The value to store masked"
|
||||
* @param mask "The mask for the store"
|
||||
* @param $alignment "The alignment of the load"
|
||||
*
|
||||
* @require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
|
||||
* @require @typekind(value) == VECTOR : "Expected value to be a vector"
|
||||
* @require $assignable(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match"
|
||||
* @require value.len == mask.len : "Mask and value must have the same length"
|
||||
* @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
|
||||
* @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||
**/
|
||||
<*
|
||||
@param ptrvec "The vector pointer containing the addresses to store to."
|
||||
@param value "The value to store masked"
|
||||
@param mask "The mask for the store"
|
||||
@param $alignment "The alignment of the load"
|
||||
|
||||
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
|
||||
@require @typekind(value) == VECTOR : "Expected value to be a vector"
|
||||
@require $assignable(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match"
|
||||
@require value.len == mask.len : "Mask and value must have the same length"
|
||||
@require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
|
||||
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||
*>
|
||||
macro @scatter_aligned(ptrvec, value, bool[<*>] mask, usz $alignment)
|
||||
{
|
||||
return $$scatter(ptrvec, value, mask, $alignment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [in] x "The variable or dereferenced pointer to load."
|
||||
* @param $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
|
||||
<*
|
||||
@param #x "The variable or dereferenced pointer to load."
|
||||
@param $alignment "The alignment to assume for the load"
|
||||
@return "The value of the variable"
|
||||
|
||||
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
|
||||
*>
|
||||
macro @unaligned_load(#x, usz $alignment) @builtin
|
||||
{
|
||||
return $$unaligned_load(x, $alignment);
|
||||
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
|
||||
<*
|
||||
@param #x "The variable or dereferenced pointer to store to."
|
||||
@param value "The value to store."
|
||||
@param $alignment "The alignment to assume for the store"
|
||||
@return "The value stored"
|
||||
|
||||
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
|
||||
@require $defined(#x = value) : "The value doesn't match the variable"
|
||||
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||
*>
|
||||
macro @unaligned_store(#x, value, usz $alignment) @builtin
|
||||
{
|
||||
return $$unaligned_store(x, ($typeof(*x))value, $alignment);
|
||||
return $$unaligned_store(&#x, ($typeof(#x))value, $alignment);
|
||||
}
|
||||
|
||||
macro @volatile_load(&x) @builtin
|
||||
<*
|
||||
@param #x "The variable or dereferenced pointer to load."
|
||||
@return "The value of the variable"
|
||||
|
||||
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
|
||||
*>
|
||||
macro @volatile_load(#x) @builtin
|
||||
{
|
||||
return $$volatile_load(x);
|
||||
return $$volatile_load(&#x);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $assignable(y, $typeof(*x)) : "The value doesn't match the variable"
|
||||
**/
|
||||
macro @volatile_store(&x, y) @builtin
|
||||
<*
|
||||
@param #x "The variable or dereferenced pointer to store to."
|
||||
@param value "The value to store."
|
||||
@return "The value stored"
|
||||
|
||||
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
|
||||
@require $defined(#x = value) : "The value doesn't match the variable"
|
||||
*>
|
||||
macro @volatile_store(#x, value) @builtin
|
||||
{
|
||||
return $$volatile_store(x, ($typeof(*x))y);
|
||||
return $$volatile_store(&#x, ($typeof(#x))value);
|
||||
}
|
||||
|
||||
enum AtomicOrdering : int
|
||||
@@ -211,58 +224,60 @@ enum AtomicOrdering : int
|
||||
SEQ_CONSISTENT, // Acquire semantics, ordered with other seq_consistent
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [in] x "the variable or dereferenced pointer to load."
|
||||
* @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
* @param $volatile "whether the load should be volatile, defaults to 'false'"
|
||||
* @return "returns the value of x"
|
||||
*
|
||||
* @require $ordering != AtomicOrdering.RELEASE "Release ordering is not valid for load."
|
||||
* @require $ordering != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid for load."
|
||||
* @require types::may_load_atomic($typeof(x)) "Only integer, float and pointers may be used."
|
||||
* @require @typekind(x) == POINTER "You can only load from a pointer"
|
||||
**/
|
||||
macro @atomic_load(&x, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
|
||||
<*
|
||||
@param #x "the variable or dereferenced pointer to load."
|
||||
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@param $volatile "whether the load should be volatile, defaults to 'false'"
|
||||
@return "returns the value of x"
|
||||
|
||||
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
|
||||
@require $ordering != AtomicOrdering.RELEASE "Release ordering is not valid for load."
|
||||
@require $ordering != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid for load."
|
||||
@require types::may_load_atomic($typeof(#x)) "Only integer, float and pointers may be used."
|
||||
*>
|
||||
macro @atomic_load(#x, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
|
||||
{
|
||||
return $$atomic_load(x, $volatile, (int)$ordering);
|
||||
return $$atomic_load(&#x, $volatile, $ordering.ordinal);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [out] x "the variable or dereferenced pointer to store to."
|
||||
* @param value "the value to store."
|
||||
* @param $ordering "the atomic ordering of the store, defaults to SEQ_CONSISTENT"
|
||||
* @param $volatile "whether the store should be volatile, defaults to 'false'"
|
||||
*
|
||||
* @require $ordering != AtomicOrdering.ACQUIRE "Acquire ordering is not valid for store."
|
||||
* @require $ordering != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid for store."
|
||||
* @require types::may_load_atomic($typeof(x)) "Only integer, float and pointers may be used."
|
||||
**/
|
||||
macro void @atomic_store(&x, value, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
|
||||
<*
|
||||
@param #x "the variable or dereferenced pointer to store to."
|
||||
@param value "the value to store."
|
||||
@param $ordering "the atomic ordering of the store, defaults to SEQ_CONSISTENT"
|
||||
@param $volatile "whether the store should be volatile, defaults to 'false'"
|
||||
|
||||
@require $ordering != AtomicOrdering.ACQUIRE "Acquire ordering is not valid for store."
|
||||
@require $ordering != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid for store."
|
||||
@require types::may_load_atomic($typeof(#x)) "Only integer, float and pointers may be used."
|
||||
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
|
||||
@require $defined(#x = value) : "The value doesn't match the variable"
|
||||
*>
|
||||
macro void @atomic_store(#x, value, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
|
||||
{
|
||||
$$atomic_store(x, value, $volatile, (int)$ordering);
|
||||
$$atomic_store(&#x, value, $volatile, $ordering.ordinal);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $success != AtomicOrdering.NOT_ATOMIC && $success != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
* @require $failure != AtomicOrdering.RELEASE && $failure != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid."
|
||||
**/
|
||||
<*
|
||||
@require $success != AtomicOrdering.NOT_ATOMIC && $success != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
@require $failure != AtomicOrdering.RELEASE && $failure != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid."
|
||||
*>
|
||||
macro compare_exchange(ptr, compare, value, AtomicOrdering $success = SEQ_CONSISTENT, AtomicOrdering $failure = SEQ_CONSISTENT, bool $volatile = true, bool $weak = false, usz $alignment = 0)
|
||||
{
|
||||
return $$compare_exchange(ptr, compare, value, $volatile, $weak, $success.ordinal, $failure.ordinal, $alignment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $success != AtomicOrdering.NOT_ATOMIC && $success != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
* @require $failure != AtomicOrdering.RELEASE && $failure != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid."
|
||||
**/
|
||||
<*
|
||||
@require $success != AtomicOrdering.NOT_ATOMIC && $success != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
|
||||
@require $failure != AtomicOrdering.RELEASE && $failure != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid."
|
||||
*>
|
||||
macro compare_exchange_volatile(ptr, compare, value, AtomicOrdering $success = SEQ_CONSISTENT, AtomicOrdering $failure = SEQ_CONSISTENT)
|
||||
{
|
||||
return compare_exchange(ptr, compare, value, $success, $failure, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require math::is_power_of_2(alignment)
|
||||
**/
|
||||
<*
|
||||
@require math::is_power_of_2(alignment)
|
||||
*>
|
||||
fn usz aligned_offset(usz offset, usz alignment)
|
||||
{
|
||||
return alignment * ((offset + alignment - 1) / alignment);
|
||||
@@ -273,14 +288,19 @@ macro void* aligned_pointer(void* ptr, usz alignment)
|
||||
return (void*)(uptr)aligned_offset((uptr)ptr, alignment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require math::is_power_of_2(alignment)
|
||||
**/
|
||||
<*
|
||||
@require math::is_power_of_2(alignment)
|
||||
*>
|
||||
fn bool ptr_is_aligned(void* ptr, usz alignment) @inline
|
||||
{
|
||||
return (uptr)ptr & ((uptr)alignment - 1) == 0;
|
||||
}
|
||||
|
||||
macro void zero_volatile(char[] data)
|
||||
{
|
||||
$$memset(data.ptr, (char)0, data.len, true, (usz)1);
|
||||
}
|
||||
|
||||
macro void clear(void* dst, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
|
||||
{
|
||||
$$memset(dst, (char)0, len, $is_volatile, $dst_align);
|
||||
@@ -291,94 +311,96 @@ macro void clear_inline(void* dst, usz $len, usz $dst_align = 0, bool $is_volati
|
||||
$$memset_inline(dst, (char)0, $len, $is_volatile, $dst_align);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy memory from src to dst efficiently, assuming the memory ranges do not overlap.
|
||||
*
|
||||
* @param [&out] dst "The destination to copy to"
|
||||
* @param [&in] src "The source to copy from"
|
||||
* @param len "The number of bytes to copy"
|
||||
* @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
|
||||
* @param $src_align "the alignment of the destination if different from the default, 0 assumes the default"
|
||||
* @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
|
||||
*
|
||||
* @require len == 0 || dst + len <= src || src + len <= dst : "Ranges may not overlap"
|
||||
**/
|
||||
<*
|
||||
Copy memory from src to dst efficiently, assuming the memory ranges do not overlap.
|
||||
|
||||
@param [&out] dst "The destination to copy to"
|
||||
@param [&in] src "The source to copy from"
|
||||
@param len "The number of bytes to copy"
|
||||
@param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
|
||||
@param $src_align "the alignment of the destination if different from the default, 0 assumes the default"
|
||||
@param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
|
||||
|
||||
@require len == 0 || dst + len <= src || src + len <= dst : "Ranges may not overlap"
|
||||
*>
|
||||
macro void copy(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false, bool $inlined = false)
|
||||
{
|
||||
$$memcpy(dst, src, len, $is_volatile, $dst_align, $src_align);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy memory from src to dst efficiently, assuming the memory ranges do not overlap, it
|
||||
* will always be inlined and never call memcopy
|
||||
*
|
||||
* @param [&out] dst "The destination to copy to"
|
||||
* @param [&in] src "The source to copy from"
|
||||
* @param $len "The number of bytes to copy"
|
||||
* @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
|
||||
* @param $src_align "the alignment of the destination if different from the default, 0 assumes the default"
|
||||
* @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
|
||||
*
|
||||
* @require $len == 0 || dst + $len <= src || src + $len <= dst : "Ranges may not overlap"
|
||||
**/
|
||||
<*
|
||||
Copy memory from src to dst efficiently, assuming the memory ranges do not overlap, it
|
||||
will always be inlined and never call memcopy
|
||||
|
||||
@param [&out] dst "The destination to copy to"
|
||||
@param [&in] src "The source to copy from"
|
||||
@param $len "The number of bytes to copy"
|
||||
@param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
|
||||
@param $src_align "the alignment of the destination if different from the default, 0 assumes the default"
|
||||
@param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
|
||||
|
||||
@require $len == 0 || dst + $len <= src || src + $len <= dst : "Ranges may not overlap"
|
||||
*>
|
||||
macro void copy_inline(void* dst, void* src, usz $len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false)
|
||||
{
|
||||
$$memcpy_inline(dst, src, $len, $is_volatile, $dst_align, $src_align);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy memory from src to dst but correctly handle the possibility of overlapping ranges.
|
||||
*
|
||||
* @param [&out] dst "The destination to copy to"
|
||||
* @param [&in] src "The source to copy from"
|
||||
* @param len "The number of bytes to copy"
|
||||
* @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
|
||||
* @param $src_align "the alignment of the destination if different from the default, 0 assumes the default"
|
||||
* @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
|
||||
**/
|
||||
<*
|
||||
Copy memory from src to dst but correctly handle the possibility of overlapping ranges.
|
||||
|
||||
@param [&out] dst "The destination to copy to"
|
||||
@param [&in] src "The source to copy from"
|
||||
@param len "The number of bytes to copy"
|
||||
@param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
|
||||
@param $src_align "the alignment of the destination if different from the default, 0 assumes the default"
|
||||
@param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
|
||||
*>
|
||||
macro void move(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false)
|
||||
{
|
||||
$$memmove(dst, src, len, $is_volatile, $dst_align, $src_align);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all memory in a region to that of the provided byte.
|
||||
*
|
||||
* @param [&out] dst "The destination to copy to"
|
||||
* @param val "The value to copy into memory"
|
||||
* @param len "The number of bytes to copy"
|
||||
* @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
|
||||
* @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
|
||||
*
|
||||
* @ensure !len || (dst[0] == val && dst[len - 1] == val)
|
||||
**/
|
||||
<*
|
||||
Sets all memory in a region to that of the provided byte.
|
||||
|
||||
@param [&out] dst "The destination to copy to"
|
||||
@param val "The value to copy into memory"
|
||||
@param len "The number of bytes to copy"
|
||||
@param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
|
||||
@param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
|
||||
|
||||
@ensure !len || (dst[0] == val && dst[len - 1] == val)
|
||||
*>
|
||||
macro void set(void* dst, char val, usz len, usz $dst_align = 0, bool $is_volatile = false)
|
||||
{
|
||||
$$memset(dst, val, len, $is_volatile, $dst_align);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all memory in a region to that of the provided byte. Never calls OS memset.
|
||||
*
|
||||
* @param [&out] dst "The destination to copy to"
|
||||
* @param val "The value to copy into memory"
|
||||
* @param $len "The number of bytes to copy"
|
||||
* @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
|
||||
* @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
|
||||
*
|
||||
* @ensure !$len || (dst[0] == val && dst[$len - 1] == val)
|
||||
**/
|
||||
<*
|
||||
Sets all memory in a region to that of the provided byte. Never calls OS memset.
|
||||
|
||||
@param [&out] dst "The destination to copy to"
|
||||
@param val "The value to copy into memory"
|
||||
@param $len "The number of bytes to copy"
|
||||
@param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
|
||||
@param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
|
||||
|
||||
@ensure !$len || (dst[0] == val && dst[$len - 1] == val)
|
||||
*>
|
||||
macro void set_inline(void* dst, char val, usz $len, usz $dst_align = 0, bool $is_volatile = false)
|
||||
{
|
||||
$$memset_inline(dst, val, $len, $is_volatile, $dst_align);
|
||||
}
|
||||
/**
|
||||
* @require values::@inner_kind(a) == TypeKind.SLICE || values::@inner_kind(a) == TypeKind.POINTER
|
||||
* @require values::@inner_kind(b) == TypeKind.SLICE || values::@inner_kind(b) == TypeKind.POINTER
|
||||
* @require values::@inner_kind(a) != TypeKind.SLICE || len == -1
|
||||
* @require values::@inner_kind(a) != TypeKind.POINTER || len > -1
|
||||
* @require values::@assign_to(a, b) && values::@assign_to(b, a)
|
||||
**/
|
||||
<*
|
||||
Test if n elements are equal in a slice, pointed to by a pointer etc.
|
||||
|
||||
@require values::@inner_kind(a) == TypeKind.SLICE || values::@inner_kind(a) == TypeKind.POINTER
|
||||
@require values::@inner_kind(b) == TypeKind.SLICE || values::@inner_kind(b) == TypeKind.POINTER
|
||||
@require values::@inner_kind(a) != TypeKind.SLICE || len == -1
|
||||
@require values::@inner_kind(a) != TypeKind.POINTER || len > -1
|
||||
@require values::@assign_to(a, b) && values::@assign_to(b, a)
|
||||
*>
|
||||
macro bool equals(a, b, isz len = -1, usz $align = 0)
|
||||
{
|
||||
$if !$align:
|
||||
@@ -424,15 +446,19 @@ macro bool equals(a, b, isz len = -1, usz $align = 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
<*
|
||||
Check if an allocation must be aligned given the type.
|
||||
|
||||
macro type_alloc_must_be_aligned($Type)
|
||||
@return `true if the alignment of the type exceeds DEFAULT_MEM_ALIGNMENT.`
|
||||
*>
|
||||
macro bool type_alloc_must_be_aligned($Type)
|
||||
{
|
||||
return $Type.alignof > DEFAULT_MEM_ALIGNMENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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())
|
||||
{
|
||||
Allocator old_allocator = allocator::thread_allocator;
|
||||
@@ -441,21 +467,71 @@ macro void @scoped(Allocator allocator; @body())
|
||||
@body();
|
||||
}
|
||||
|
||||
macro void @report_heap_allocs_in_scope(;@body())
|
||||
<*
|
||||
Run the tracking allocator in the scope, then
|
||||
print out stats.
|
||||
|
||||
@param $enabled "Set to false to disable tracking"
|
||||
*>
|
||||
macro void @report_heap_allocs_in_scope($enabled = true; @body())
|
||||
{
|
||||
TrackingAllocator tracker;
|
||||
tracker.init(allocator::thread_allocator);
|
||||
Allocator old_allocator = allocator::thread_allocator;
|
||||
allocator::thread_allocator = &tracker;
|
||||
defer
|
||||
{
|
||||
allocator::thread_allocator = old_allocator;
|
||||
tracker.print_report();
|
||||
tracker.free();
|
||||
}
|
||||
$if $enabled:
|
||||
TrackingAllocator tracker;
|
||||
tracker.init(allocator::thread_allocator);
|
||||
Allocator old_allocator = allocator::thread_allocator;
|
||||
allocator::thread_allocator = &tracker;
|
||||
defer
|
||||
{
|
||||
allocator::thread_allocator = old_allocator;
|
||||
tracker.print_report();
|
||||
tracker.free();
|
||||
}
|
||||
$endif
|
||||
@body();
|
||||
}
|
||||
|
||||
<*
|
||||
Assert on memory leak in the scope of the macro body.
|
||||
|
||||
@param $report "Set to false to disable memory report"
|
||||
*>
|
||||
macro void @assert_leak($report = true; @body()) @builtin
|
||||
{
|
||||
$if env::DEBUG_SYMBOLS || $feature(MEMORY_ASSERTS):
|
||||
TrackingAllocator tracker;
|
||||
tracker.init(allocator::thread_allocator);
|
||||
Allocator old_allocator = allocator::thread_allocator;
|
||||
allocator::thread_allocator = &tracker;
|
||||
defer
|
||||
{
|
||||
allocator::thread_allocator = old_allocator;
|
||||
defer tracker.free();
|
||||
usz allocated = tracker.allocated();
|
||||
if (allocated)
|
||||
{
|
||||
DString report = dstring::new();
|
||||
defer report.free();
|
||||
$if $report:
|
||||
report.append_char('\n');
|
||||
(void)tracker.fprint_report(&report);
|
||||
$endif
|
||||
assert(allocated == 0, "Memory leak detected"
|
||||
" (%d bytes allocated).%s",
|
||||
allocated, report.str_view());
|
||||
}
|
||||
}
|
||||
$endif
|
||||
@body();
|
||||
}
|
||||
|
||||
<*
|
||||
Allocate [size] bytes on the stack to use for allocation,
|
||||
with the heap allocator as the backing allocator.
|
||||
|
||||
Release everything on scope exit.
|
||||
|
||||
@param $size `the size of the buffer`
|
||||
*>
|
||||
macro void @stack_mem(usz $size; @body(Allocator mem)) @builtin
|
||||
{
|
||||
char[$size] buffer;
|
||||
@@ -483,10 +559,9 @@ struct TempState
|
||||
TempAllocator* current;
|
||||
usz mark;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push the current temp allocator. A push must always be balanced with a pop using the current state.
|
||||
**/
|
||||
<*
|
||||
Push the current temp allocator. A push must always be balanced with a pop using the current state.
|
||||
*>
|
||||
fn TempState temp_push(TempAllocator* other = null)
|
||||
{
|
||||
TempAllocator* current = allocator::temp();
|
||||
@@ -498,9 +573,9 @@ fn TempState temp_push(TempAllocator* other = null)
|
||||
return { old, current, current.used };
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop the current temp allocator. A pop must always be balanced with a push.
|
||||
**/
|
||||
<*
|
||||
Pop the current temp allocator. A pop must always be balanced with a push.
|
||||
*>
|
||||
fn void temp_pop(TempState old_state)
|
||||
{
|
||||
assert(allocator::thread_temp_allocator == old_state.current, "Tried to pop temp allocators out of order.");
|
||||
@@ -509,19 +584,21 @@ fn void temp_pop(TempState old_state)
|
||||
allocator::thread_temp_allocator = old_state.old;
|
||||
}
|
||||
|
||||
macro void @pool(TempAllocator* #other_temp = null; @body) @builtin
|
||||
<*
|
||||
@require @is_empty_macro_slot(#other_temp) ||| $assignable(#other_temp, Allocator) "Must be an allocator"
|
||||
*>
|
||||
macro void @pool(#other_temp = EMPTY_MACRO_SLOT; @body) @builtin
|
||||
{
|
||||
TempAllocator* current = allocator::temp();
|
||||
var $has_arg = !$is_const(#other_temp);
|
||||
$if $has_arg:
|
||||
$if @is_valid_macro_slot(#other_temp):
|
||||
TempAllocator* original = current;
|
||||
if (current == (void*)#other_temp) current = allocator::temp_allocator_next();
|
||||
if (current == #other_temp.ptr) current = allocator::temp_allocator_next();
|
||||
$endif
|
||||
usz mark = current.used;
|
||||
defer
|
||||
{
|
||||
current.reset(mark);
|
||||
$if $has_arg:
|
||||
$if @is_valid_macro_slot(#other_temp):
|
||||
allocator::thread_temp_allocator = original;
|
||||
$endif;
|
||||
}
|
||||
@@ -575,10 +652,10 @@ fn void* malloc(usz size) @builtin @inline @nodiscard
|
||||
return allocator::malloc(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.
|
||||
**/
|
||||
<*
|
||||
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)!!;
|
||||
@@ -590,11 +667,11 @@ fn void* tmalloc(usz size, usz alignment = 0) @builtin @inline @nodiscard
|
||||
return allocator::temp().acquire(size, NO_ZERO, alignment)!!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $vacount < 2 : "Too many arguments."
|
||||
* @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
* @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
|
||||
**/
|
||||
<*
|
||||
@require $vacount < 2 : "Too many arguments."
|
||||
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
|
||||
*>
|
||||
macro new($Type, ...) @nodiscard
|
||||
{
|
||||
$if $vacount == 0:
|
||||
@@ -606,12 +683,12 @@ macro new($Type, ...) @nodiscard
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
|
||||
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
|
||||
* @require $vacount < 2 : "Too many arguments."
|
||||
* @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
**/
|
||||
<*
|
||||
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:
|
||||
@@ -623,27 +700,27 @@ macro new_aligned($Type, ...) @nodiscard
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 alloc($Type) @nodiscard
|
||||
{
|
||||
return ($Type*)malloc($Type.sizeof);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
<*
|
||||
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($Type) @nodiscard
|
||||
{
|
||||
return ($Type*)malloc_aligned($Type.sizeof, $Type.alignof);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $vacount < 2 : "Too many arguments."
|
||||
* @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
**/
|
||||
<*
|
||||
@require $vacount < 2 : "Too many arguments."
|
||||
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
*>
|
||||
macro temp_new($Type, ...) @nodiscard
|
||||
{
|
||||
$if $vacount == 0:
|
||||
@@ -661,35 +738,35 @@ macro temp_alloc($Type) @nodiscard
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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"
|
||||
*>
|
||||
macro new_array($Type, usz elements) @nodiscard
|
||||
{
|
||||
return allocator::new_array(allocator::heap(), $Type, elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
<*
|
||||
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($Type, usz elements) @nodiscard
|
||||
{
|
||||
return allocator::new_array_aligned(allocator::heap(), $Type, elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_array_aligned' instead"
|
||||
**/
|
||||
<*
|
||||
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_array_aligned' instead"
|
||||
*>
|
||||
macro alloc_array($Type, usz elements) @nodiscard
|
||||
{
|
||||
return allocator::alloc_array(allocator::heap(), $Type, elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
<*
|
||||
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($Type, usz elements) @nodiscard
|
||||
{
|
||||
return allocator::alloc_array_aligned(allocator::heap(), $Type, elements);
|
||||
@@ -710,10 +787,10 @@ fn void* calloc(usz size) @builtin @inline @nodiscard
|
||||
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.
|
||||
**/
|
||||
<*
|
||||
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
|
||||
{
|
||||
return allocator::calloc_aligned(allocator::heap(), size, alignment)!!;
|
||||
@@ -748,7 +825,44 @@ fn void free_aligned(void* ptr) @builtin @inline
|
||||
fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin @inline @nodiscard
|
||||
{
|
||||
if (!size) return null;
|
||||
if (!ptr) return tmalloc(size, alignment);
|
||||
if (!ptr) return tmalloc(size, alignment);
|
||||
return allocator::temp().resize(ptr, size, alignment)!!;
|
||||
}
|
||||
|
||||
module std::core::mem @if(env::NO_LIBC);
|
||||
|
||||
fn CInt __memcmp(void* s1, void* s2, usz n) @weak @export("memcmp")
|
||||
{
|
||||
char* p1 = s1;
|
||||
char* p2 = s2;
|
||||
for (usz i = 0; i < n; i++, p1++, p2++)
|
||||
{
|
||||
char c1 = *p1;
|
||||
char c2 = *p2;
|
||||
if (c1 < c2) return -1;
|
||||
if (c1 > c2) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn void* __memset(void* str, CInt c, usz n) @weak @export("memset")
|
||||
{
|
||||
char* p = str;
|
||||
char cc = (char)c;
|
||||
for (usz i = 0; i < n; i++, p++)
|
||||
{
|
||||
*p = cc;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
fn void* __memcpy(void* dst, void* src, usz n) @weak @export("memcpy")
|
||||
{
|
||||
char* d = dst;
|
||||
char* s = src;
|
||||
for (usz i = 0; i < n; i++, d++, s++)
|
||||
{
|
||||
*d = *s;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
@@ -20,22 +20,22 @@ interface Allocator
|
||||
{
|
||||
fn void reset(usz mark) @optional;
|
||||
fn usz mark() @optional;
|
||||
/**
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
* @require size > 0
|
||||
**/
|
||||
<*
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
@require size > 0
|
||||
*>
|
||||
fn void*! acquire(usz size, AllocInitType init_type, usz alignment = 0);
|
||||
/**
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
* @require ptr != null
|
||||
* @require new_size > 0
|
||||
**/
|
||||
<*
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
@require ptr != null
|
||||
@require new_size > 0
|
||||
*>
|
||||
fn void*! resize(void* ptr, usz new_size, usz alignment = 0);
|
||||
/**
|
||||
* @require ptr != null
|
||||
**/
|
||||
<*
|
||||
@require ptr != null
|
||||
*>
|
||||
fn void release(void* ptr, bool aligned);
|
||||
}
|
||||
|
||||
@@ -143,14 +143,14 @@ macro void free_aligned(Allocator allocator, void* ptr)
|
||||
$if env::TESTING:
|
||||
((char*)ptr)[0] = 0xBA;
|
||||
$endif
|
||||
allocator.release(ptr, .aligned = 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 == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
**/
|
||||
<*
|
||||
@require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_aligned' instead"
|
||||
@require $vacount < 2 : "Too many arguments."
|
||||
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
*>
|
||||
macro new(Allocator allocator, $Type, ...) @nodiscard
|
||||
{
|
||||
$if $vacount == 0:
|
||||
@@ -162,11 +162,11 @@ macro new(Allocator allocator, $Type, ...) @nodiscard
|
||||
$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 == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
**/
|
||||
<*
|
||||
@require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_aligned' instead"
|
||||
@require $vacount < 2 : "Too many arguments."
|
||||
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
*>
|
||||
macro new_try(Allocator allocator, $Type, ...) @nodiscard
|
||||
{
|
||||
$if $vacount == 0:
|
||||
@@ -178,12 +178,12 @@ macro new_try(Allocator allocator, $Type, ...) @nodiscard
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
|
||||
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
|
||||
* @require $vacount < 2 : "Too many arguments."
|
||||
* @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
|
||||
**/
|
||||
<*
|
||||
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:
|
||||
@@ -195,92 +195,92 @@ macro new_aligned($Type, ...) @nodiscard
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT
|
||||
**/
|
||||
<*
|
||||
@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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
|
||||
**/
|
||||
<*
|
||||
@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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
|
||||
**/
|
||||
<*
|
||||
@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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
<*
|
||||
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
|
||||
**/
|
||||
<*
|
||||
@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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_array_aligned' instead"
|
||||
**/
|
||||
<*
|
||||
@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)!!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_array_aligned' instead"
|
||||
**/
|
||||
<*
|
||||
@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];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
<*
|
||||
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]!!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_array_aligned' instead"
|
||||
**/
|
||||
<*
|
||||
@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)!!;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
<*
|
||||
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]!!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_array_aligned' instead"
|
||||
**/
|
||||
<*
|
||||
@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];
|
||||
@@ -300,11 +300,11 @@ fn any clone_any(Allocator allocator, any value) @nodiscard
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @require bytes > 0
|
||||
* @require alignment > 0
|
||||
* @require bytes <= isz.max
|
||||
**/
|
||||
<*
|
||||
@require bytes > 0
|
||||
@require alignment > 0
|
||||
@require bytes <= isz.max
|
||||
*>
|
||||
macro void*! @aligned_alloc(#alloc_fn, usz bytes, usz alignment)
|
||||
{
|
||||
if (alignment < void*.alignof) alignment = void*.alignof;
|
||||
@@ -338,10 +338,10 @@ macro void! @aligned_free(#free_fn, void* old_pointer)
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @require bytes > 0
|
||||
* @require alignment > 0
|
||||
**/
|
||||
<*
|
||||
@require bytes > 0
|
||||
@require alignment > 0
|
||||
*>
|
||||
macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes, usz alignment)
|
||||
{
|
||||
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
|
||||
@@ -412,9 +412,9 @@ fn void destroy_temp_allocators_after_exit() @finalizer(65535) @local @if(env::L
|
||||
destroy_temp_allocators();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to destroy any memory used by the temp allocators. This will invalidate all temp memory.
|
||||
**/
|
||||
<*
|
||||
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;
|
||||
|
||||
@@ -25,14 +25,22 @@ enum X86Feature
|
||||
{
|
||||
ADX,
|
||||
AES,
|
||||
AMX_AVX512,
|
||||
AMX_FP8,
|
||||
AMX_MOVRS,
|
||||
AMX_TF32,
|
||||
AMX_TRANSPOSE,
|
||||
AMX_BF16,
|
||||
AMX_COMPLEX,
|
||||
AMX_FP16,
|
||||
AMX_INT8,
|
||||
AMX_TILE,
|
||||
APXF,
|
||||
AVX,
|
||||
AVX10_1_256,
|
||||
AVX10_1_512,
|
||||
AVX10_2_256,
|
||||
AVX10_2_512,
|
||||
AVX2,
|
||||
AVX5124FMAPS,
|
||||
AVX5124VNNIW,
|
||||
@@ -83,6 +91,7 @@ enum X86Feature
|
||||
MOVBE,
|
||||
MOVDIR64B,
|
||||
MOVDIRI,
|
||||
MOVRS,
|
||||
MWAITX,
|
||||
PCLMUL,
|
||||
PCONFIG,
|
||||
@@ -156,6 +165,7 @@ fn void x86_initialize_cpu_features()
|
||||
add_feature_if_bit(AMX_FP16, leaf7s1.eax, 21);
|
||||
add_feature_if_bit(AMX_INT8, leaf7.edx, 25);
|
||||
add_feature_if_bit(AMX_TILE, leaf7.edx, 24);
|
||||
add_feature_if_bit(APXF, leaf7s1.edx, 21);
|
||||
add_feature_if_bit(AVX, feat.ecx, 28);
|
||||
add_feature_if_bit(AVX10_1_256, leaf7s1.edx, 19);
|
||||
add_feature_if_bit(AVX10_1_512, leaf_24.ebx, 18);
|
||||
@@ -255,4 +265,4 @@ fn void x86_initialize_cpu_features()
|
||||
add_feature_if_bit(XSAVEOPT, leaf_d.eax, 0);
|
||||
add_feature_if_bit(XSAVES, leaf_d.eax, 3);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,13 @@ macro int @main_to_int_main_args(#m, int argc, char** argv)
|
||||
return #m(list);
|
||||
}
|
||||
|
||||
macro int @_main_runner(#m, int argc, char** argv)
|
||||
{
|
||||
String[] list = args_to_strings(argc, argv);
|
||||
defer free(list.ptr);
|
||||
return #m(list) ? 0 : 1;
|
||||
}
|
||||
|
||||
macro int @main_to_void_main_args(#m, int argc, char** argv)
|
||||
{
|
||||
String[] list = args_to_strings(argc, argv);
|
||||
@@ -157,6 +164,13 @@ macro int @wmain_to_int_main_args(#m, int argc, Char16** argv)
|
||||
return #m(args);
|
||||
}
|
||||
|
||||
macro int @_wmain_runner(#m, int argc, Char16** argv)
|
||||
{
|
||||
String[] args = wargs_strings(argc, argv);
|
||||
defer release_wargs(args);
|
||||
return #m(args) ? 0 : 1;
|
||||
}
|
||||
|
||||
macro int @wmain_to_void_main_args(#m, int argc, Char16** argv)
|
||||
{
|
||||
String[] args = wargs_strings(argc, argv);
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
module std::core::runtime;
|
||||
import libc, std::time, std::io, std::sort;
|
||||
|
||||
struct ReflectedParam (Printable) @if(!$defined(ReflectedParam))
|
||||
{
|
||||
String name;
|
||||
typeid type;
|
||||
}
|
||||
|
||||
struct AnyRaw
|
||||
{
|
||||
void* ptr;
|
||||
@@ -16,229 +22,6 @@ struct SliceRaw
|
||||
usz len;
|
||||
}
|
||||
|
||||
def BenchmarkFn = fn void!();
|
||||
|
||||
struct BenchmarkUnit
|
||||
{
|
||||
String name;
|
||||
BenchmarkFn func;
|
||||
}
|
||||
|
||||
fn BenchmarkUnit[] benchmark_collection_create(Allocator allocator = allocator::heap())
|
||||
{
|
||||
BenchmarkFn[] fns = $$BENCHMARK_FNS;
|
||||
String[] names = $$BENCHMARK_NAMES;
|
||||
BenchmarkUnit[] benchmarks = allocator::alloc_array(allocator, BenchmarkUnit, names.len);
|
||||
foreach (i, benchmark : fns)
|
||||
{
|
||||
benchmarks[i] = { names[i], fns[i] };
|
||||
}
|
||||
return benchmarks;
|
||||
}
|
||||
|
||||
const DEFAULT_BENCHMARK_WARMUP_ITERATIONS = 3;
|
||||
const DEFAULT_BENCHMARK_MAX_ITERATIONS = 10000;
|
||||
|
||||
uint benchmark_warmup_iterations @private = DEFAULT_BENCHMARK_WARMUP_ITERATIONS;
|
||||
uint benchmark_max_iterations @private = DEFAULT_BENCHMARK_MAX_ITERATIONS;
|
||||
|
||||
fn void set_benchmark_warmup_iterations(uint value) @builtin
|
||||
{
|
||||
benchmark_warmup_iterations = value;
|
||||
}
|
||||
|
||||
fn void set_benchmark_max_iterations(uint value) @builtin
|
||||
{
|
||||
assert(value > 0);
|
||||
benchmark_max_iterations = value;
|
||||
}
|
||||
|
||||
fn bool run_benchmarks(BenchmarkUnit[] benchmarks)
|
||||
{
|
||||
int benchmarks_passed = 0;
|
||||
int benchmark_count = benchmarks.len;
|
||||
usz max_name;
|
||||
|
||||
foreach (&unit : benchmarks)
|
||||
{
|
||||
if (max_name < unit.name.len) max_name = unit.name.len;
|
||||
}
|
||||
|
||||
usz len = max_name + 9;
|
||||
|
||||
DString name = dstring::temp_with_capacity(64);
|
||||
name.append_repeat('-', len / 2);
|
||||
name.append(" BENCHMARKS ");
|
||||
name.append_repeat('-', len - len / 2);
|
||||
|
||||
io::printn(name);
|
||||
|
||||
name.clear();
|
||||
|
||||
long sys_clock_started;
|
||||
long sys_clock_finished;
|
||||
long sys_clocks;
|
||||
Clock clock;
|
||||
anyfault err;
|
||||
|
||||
foreach(unit : benchmarks)
|
||||
{
|
||||
defer name.clear();
|
||||
name.appendf("Benchmarking %s ", unit.name);
|
||||
name.append_repeat('.', max_name - unit.name.len + 2);
|
||||
io::printf("%s ", name.str_view());
|
||||
|
||||
for (uint i = 0; i < benchmark_warmup_iterations; i++)
|
||||
{
|
||||
err = @catch(unit.func()) @inline;
|
||||
@volatile_load(err);
|
||||
}
|
||||
|
||||
clock = std::time::clock::now();
|
||||
sys_clock_started = $$sysclock();
|
||||
|
||||
for (uint i = 0; i < benchmark_max_iterations; i++)
|
||||
{
|
||||
err = @catch(unit.func()) @inline;
|
||||
@volatile_load(err);
|
||||
}
|
||||
|
||||
sys_clock_finished = $$sysclock();
|
||||
NanoDuration nano_seconds = clock.mark();
|
||||
sys_clocks = sys_clock_finished - sys_clock_started;
|
||||
|
||||
if (err)
|
||||
{
|
||||
io::printfn("[failed] Failed due to: %s", err);
|
||||
continue;
|
||||
}
|
||||
|
||||
io::printfn("[ok] %.2f ns, %.2f CPU's clocks", (float)nano_seconds / benchmark_max_iterations, (float)sys_clocks / benchmark_max_iterations);
|
||||
benchmarks_passed++;
|
||||
}
|
||||
|
||||
io::printfn("\n%d benchmark%s run.\n", benchmark_count, benchmark_count > 1 ? "s" : "");
|
||||
io::printfn("Benchmarks Result: %s. %d passed, %d failed.",
|
||||
benchmarks_passed < benchmark_count ? "FAILED" : "ok",
|
||||
benchmarks_passed,
|
||||
benchmark_count - benchmarks_passed);
|
||||
|
||||
return benchmark_count == benchmarks_passed;
|
||||
}
|
||||
|
||||
fn bool default_benchmark_runner()
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
return run_benchmarks(benchmark_collection_create(allocator::temp()));
|
||||
};
|
||||
}
|
||||
|
||||
def TestFn = fn void!();
|
||||
|
||||
struct TestUnit
|
||||
{
|
||||
String name;
|
||||
TestFn func;
|
||||
}
|
||||
|
||||
fn TestUnit[] test_collection_create(Allocator allocator = allocator::heap())
|
||||
{
|
||||
TestFn[] fns = $$TEST_FNS;
|
||||
String[] names = $$TEST_NAMES;
|
||||
TestUnit[] tests = allocator::alloc_array(allocator, TestUnit, names.len);
|
||||
foreach (i, test : fns)
|
||||
{
|
||||
tests[i] = { names[i], fns[i] };
|
||||
}
|
||||
return tests;
|
||||
}
|
||||
|
||||
struct TestContext
|
||||
{
|
||||
JmpBuf buf;
|
||||
}
|
||||
|
||||
// Sort the tests by their name in ascending order.
|
||||
fn int cmp_test_unit(TestUnit a, TestUnit b)
|
||||
{
|
||||
usz an = a.name.len;
|
||||
usz bn = b.name.len;
|
||||
if (an > bn) @swap(a, b);
|
||||
foreach (i, ac : a.name)
|
||||
{
|
||||
char bc = b.name[i];
|
||||
if (ac != bc) return an > bn ? bc - ac : ac - bc;
|
||||
}
|
||||
return (int)(an - bn);
|
||||
}
|
||||
|
||||
TestContext* test_context @private;
|
||||
|
||||
fn void test_panic(String message, String file, String function, uint line)
|
||||
{
|
||||
io::printn("[error]");
|
||||
io::print("\n Error: ");
|
||||
io::print(message);
|
||||
io::printn();
|
||||
io::printfn(" - in %s %s:%s.\n", function, file, line);
|
||||
libc::longjmp(&test_context.buf, 1);
|
||||
}
|
||||
|
||||
fn bool run_tests(TestUnit[] tests)
|
||||
{
|
||||
usz max_name;
|
||||
foreach (&unit : tests)
|
||||
{
|
||||
if (max_name < unit.name.len) max_name = unit.name.len;
|
||||
}
|
||||
quicksort(tests, &cmp_test_unit);
|
||||
|
||||
TestContext context;
|
||||
test_context = &context;
|
||||
|
||||
PanicFn old_panic = builtin::panic;
|
||||
defer builtin::panic = old_panic;
|
||||
builtin::panic = &test_panic;
|
||||
int tests_passed = 0;
|
||||
int test_count = tests.len;
|
||||
DString name = dstring::temp_with_capacity(64);
|
||||
usz len = max_name + 9;
|
||||
name.append_repeat('-', len / 2);
|
||||
name.append(" TESTS ");
|
||||
name.append_repeat('-', len - len / 2);
|
||||
io::printn(name);
|
||||
name.clear();
|
||||
foreach(unit : tests)
|
||||
{
|
||||
defer name.clear();
|
||||
name.appendf("Testing %s ", unit.name);
|
||||
name.append_repeat('.', max_name - unit.name.len + 2);
|
||||
io::printf("%s ", name.str_view());
|
||||
if (libc::setjmp(&context.buf) == 0)
|
||||
{
|
||||
if (catch err = unit.func())
|
||||
{
|
||||
io::printfn("[failed] Failed due to: %s", err);
|
||||
continue;
|
||||
}
|
||||
io::printn("[ok]");
|
||||
tests_passed++;
|
||||
}
|
||||
}
|
||||
io::printfn("\n%d test%s run.\n", test_count, test_count > 1 ? "s" : "");
|
||||
io::printfn("Test Result: %s. %d passed, %d failed.",
|
||||
tests_passed < test_count ? "FAILED" : "ok", tests_passed, test_count - tests_passed);
|
||||
return test_count == tests_passed;
|
||||
}
|
||||
|
||||
fn bool default_test_runner()
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
return run_tests(test_collection_create(allocator::temp()));
|
||||
};
|
||||
}
|
||||
|
||||
module std::core::runtime @if(WASM_NOLIBC);
|
||||
|
||||
|
||||
177
lib/std/core/runtime_benchmark.c3
Normal file
177
lib/std/core/runtime_benchmark.c3
Normal file
@@ -0,0 +1,177 @@
|
||||
module std::core::runtime;
|
||||
import libc, std::time, std::io, std::sort;
|
||||
|
||||
def BenchmarkFn = fn void!() @if($$OLD_TEST);
|
||||
def BenchmarkFn = fn void() @if(!$$OLD_TEST);
|
||||
|
||||
struct BenchmarkUnit
|
||||
{
|
||||
String name;
|
||||
BenchmarkFn func;
|
||||
}
|
||||
|
||||
fn BenchmarkUnit[] benchmark_collection_create(Allocator allocator = allocator::heap())
|
||||
{
|
||||
BenchmarkFn[] fns = $$BENCHMARK_FNS;
|
||||
String[] names = $$BENCHMARK_NAMES;
|
||||
BenchmarkUnit[] benchmarks = allocator::alloc_array(allocator, BenchmarkUnit, names.len);
|
||||
foreach (i, benchmark : fns)
|
||||
{
|
||||
benchmarks[i] = { names[i], fns[i] };
|
||||
}
|
||||
return benchmarks;
|
||||
}
|
||||
|
||||
const DEFAULT_BENCHMARK_WARMUP_ITERATIONS = 3;
|
||||
const DEFAULT_BENCHMARK_MAX_ITERATIONS = 10000;
|
||||
|
||||
uint benchmark_warmup_iterations @private = DEFAULT_BENCHMARK_WARMUP_ITERATIONS;
|
||||
uint benchmark_max_iterations @private = DEFAULT_BENCHMARK_MAX_ITERATIONS;
|
||||
|
||||
fn void set_benchmark_warmup_iterations(uint value) @builtin
|
||||
{
|
||||
benchmark_warmup_iterations = value;
|
||||
}
|
||||
|
||||
fn void set_benchmark_max_iterations(uint value) @builtin
|
||||
{
|
||||
assert(value > 0);
|
||||
benchmark_max_iterations = value;
|
||||
}
|
||||
|
||||
fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @if($$OLD_TEST)
|
||||
{
|
||||
int benchmarks_passed = 0;
|
||||
int benchmark_count = benchmarks.len;
|
||||
usz max_name;
|
||||
|
||||
foreach (&unit : benchmarks)
|
||||
{
|
||||
if (max_name < unit.name.len) max_name = unit.name.len;
|
||||
}
|
||||
|
||||
usz len = max_name + 9;
|
||||
|
||||
DString name = dstring::temp_with_capacity(64);
|
||||
name.append_repeat('-', len / 2);
|
||||
name.append(" BENCHMARKS ");
|
||||
name.append_repeat('-', len - len / 2);
|
||||
|
||||
io::printn(name);
|
||||
|
||||
name.clear();
|
||||
|
||||
long sys_clock_started;
|
||||
long sys_clock_finished;
|
||||
long sys_clocks;
|
||||
Clock clock;
|
||||
anyfault err;
|
||||
|
||||
foreach(unit : benchmarks)
|
||||
{
|
||||
defer name.clear();
|
||||
name.appendf("Benchmarking %s ", unit.name);
|
||||
name.append_repeat('.', max_name - unit.name.len + 2);
|
||||
io::printf("%s ", name.str_view());
|
||||
|
||||
for (uint i = 0; i < benchmark_warmup_iterations; i++)
|
||||
{
|
||||
err = @catch(unit.func()) @inline;
|
||||
@volatile_load(err);
|
||||
}
|
||||
|
||||
clock = std::time::clock::now();
|
||||
sys_clock_started = $$sysclock();
|
||||
|
||||
for (uint i = 0; i < benchmark_max_iterations; i++)
|
||||
{
|
||||
err = @catch(unit.func()) @inline;
|
||||
@volatile_load(err);
|
||||
}
|
||||
|
||||
sys_clock_finished = $$sysclock();
|
||||
NanoDuration nano_seconds = clock.mark();
|
||||
sys_clocks = sys_clock_finished - sys_clock_started;
|
||||
|
||||
if (err)
|
||||
{
|
||||
io::printfn("[failed] Failed due to: %s", err);
|
||||
continue;
|
||||
}
|
||||
|
||||
io::printfn("[ok] %.2f ns, %.2f CPU's clocks", (float)nano_seconds / benchmark_max_iterations, (float)sys_clocks / benchmark_max_iterations);
|
||||
benchmarks_passed++;
|
||||
}
|
||||
|
||||
io::printfn("\n%d benchmark%s run.\n", benchmark_count, benchmark_count > 1 ? "s" : "");
|
||||
io::printfn("Benchmarks Result: %s. %d passed, %d failed.",
|
||||
benchmarks_passed < benchmark_count ? "FAILED" : "ok",
|
||||
benchmarks_passed,
|
||||
benchmark_count - benchmarks_passed);
|
||||
|
||||
return benchmark_count == benchmarks_passed;
|
||||
}
|
||||
|
||||
fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @if(!$$OLD_TEST)
|
||||
{
|
||||
usz max_name;
|
||||
|
||||
foreach (&unit : benchmarks)
|
||||
{
|
||||
if (max_name < unit.name.len) max_name = unit.name.len;
|
||||
}
|
||||
|
||||
usz len = max_name + 9;
|
||||
|
||||
DString name = dstring::temp_with_capacity(64);
|
||||
name.append_repeat('-', len / 2);
|
||||
name.append(" BENCHMARKS ");
|
||||
name.append_repeat('-', len - len / 2);
|
||||
|
||||
io::printn(name);
|
||||
|
||||
name.clear();
|
||||
|
||||
long sys_clock_started;
|
||||
long sys_clock_finished;
|
||||
long sys_clocks;
|
||||
Clock clock;
|
||||
|
||||
foreach(unit : benchmarks)
|
||||
{
|
||||
defer name.clear();
|
||||
name.appendf("Benchmarking %s ", unit.name);
|
||||
name.append_repeat('.', max_name - unit.name.len + 2);
|
||||
io::printf("%s ", name.str_view());
|
||||
|
||||
for (uint i = 0; i < benchmark_warmup_iterations; i++)
|
||||
{
|
||||
unit.func() @inline;
|
||||
}
|
||||
|
||||
clock = std::time::clock::now();
|
||||
sys_clock_started = $$sysclock();
|
||||
|
||||
for (uint i = 0; i < benchmark_max_iterations; i++)
|
||||
{
|
||||
unit.func() @inline;
|
||||
}
|
||||
|
||||
sys_clock_finished = $$sysclock();
|
||||
NanoDuration nano_seconds = clock.mark();
|
||||
sys_clocks = sys_clock_finished - sys_clock_started;
|
||||
|
||||
io::printfn("[COMPLETE] %.2f ns, %.2f CPU's clocks", (float)nano_seconds / benchmark_max_iterations, (float)sys_clocks / benchmark_max_iterations);
|
||||
}
|
||||
|
||||
io::printfn("\n%d benchmark%s run.\n", benchmarks.len, benchmarks.len > 1 ? "s" : "");
|
||||
return true;
|
||||
}
|
||||
|
||||
fn bool default_benchmark_runner(String[] args)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
return run_benchmarks(benchmark_collection_create(allocator::temp()));
|
||||
};
|
||||
}
|
||||
157
lib/std/core/runtime_test.c3
Normal file
157
lib/std/core/runtime_test.c3
Normal file
@@ -0,0 +1,157 @@
|
||||
// Copyright (c) 2025 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::core::runtime;
|
||||
import libc, std::time, std::io, std::sort;
|
||||
|
||||
def TestFn = fn void!() @if($$OLD_TEST);
|
||||
def TestFn = fn void() @if(!$$OLD_TEST);
|
||||
|
||||
struct TestUnit
|
||||
{
|
||||
String name;
|
||||
TestFn func;
|
||||
}
|
||||
|
||||
fn TestUnit[] test_collection_create(Allocator allocator = allocator::heap())
|
||||
{
|
||||
TestFn[] fns = $$TEST_FNS;
|
||||
String[] names = $$TEST_NAMES;
|
||||
TestUnit[] tests = allocator::alloc_array(allocator, TestUnit, names.len);
|
||||
foreach (i, test : fns)
|
||||
{
|
||||
tests[i] = { names[i], fns[i] };
|
||||
}
|
||||
return tests;
|
||||
}
|
||||
|
||||
struct TestContext
|
||||
{
|
||||
JmpBuf buf;
|
||||
}
|
||||
|
||||
// Sort the tests by their name in ascending order.
|
||||
fn int cmp_test_unit(TestUnit a, TestUnit b)
|
||||
{
|
||||
usz an = a.name.len;
|
||||
usz bn = b.name.len;
|
||||
if (an > bn) @swap(a, b);
|
||||
foreach (i, ac : a.name)
|
||||
{
|
||||
char bc = b.name[i];
|
||||
if (ac != bc) return an > bn ? bc - ac : ac - bc;
|
||||
}
|
||||
return (int)(an - bn);
|
||||
}
|
||||
|
||||
TestContext* test_context @private;
|
||||
|
||||
fn void test_panic(String message, String file, String function, uint line)
|
||||
{
|
||||
io::printn("[error]");
|
||||
io::print("\n Error: ");
|
||||
io::print(message);
|
||||
io::printn();
|
||||
io::printfn(" - in %s %s:%s.\n", function, file, line);
|
||||
libc::longjmp(&test_context.buf, 1);
|
||||
}
|
||||
|
||||
fn bool run_tests(TestUnit[] tests) @if($$OLD_TEST)
|
||||
{
|
||||
usz max_name;
|
||||
foreach (&unit : tests)
|
||||
{
|
||||
if (max_name < unit.name.len) max_name = unit.name.len;
|
||||
}
|
||||
quicksort(tests, &cmp_test_unit);
|
||||
|
||||
TestContext context;
|
||||
test_context = &context;
|
||||
|
||||
PanicFn old_panic = builtin::panic;
|
||||
defer builtin::panic = old_panic;
|
||||
builtin::panic = &test_panic;
|
||||
int tests_passed = 0;
|
||||
int test_count = tests.len;
|
||||
DString name = dstring::temp_with_capacity(64);
|
||||
usz len = max_name + 9;
|
||||
name.append_repeat('-', len / 2);
|
||||
name.append(" TESTS ");
|
||||
name.append_repeat('-', len - len / 2);
|
||||
io::printn(name);
|
||||
name.clear();
|
||||
foreach(unit : tests)
|
||||
{
|
||||
defer name.clear();
|
||||
name.appendf("Testing %s ", unit.name);
|
||||
name.append_repeat('.', max_name - unit.name.len + 2);
|
||||
io::printf("%s ", name.str_view());
|
||||
(void)io::stdout().flush();
|
||||
if (libc::setjmp(&context.buf) == 0)
|
||||
{
|
||||
if (catch err = unit.func())
|
||||
{
|
||||
io::printfn("[failed] Failed due to: %s", err);
|
||||
continue;
|
||||
}
|
||||
io::printn("[ok]");
|
||||
tests_passed++;
|
||||
}
|
||||
}
|
||||
io::printfn("\n%d test%s run.\n", test_count, test_count > 1 ? "s" : "");
|
||||
io::printfn("Test Result: %s. %d passed, %d failed.",
|
||||
tests_passed < test_count ? "FAILED" : "ok", tests_passed, test_count - tests_passed);
|
||||
return test_count == tests_passed;
|
||||
}
|
||||
|
||||
fn bool run_tests(TestUnit[] tests) @if(!$$OLD_TEST)
|
||||
{
|
||||
usz max_name;
|
||||
foreach (&unit : tests)
|
||||
{
|
||||
if (max_name < unit.name.len) max_name = unit.name.len;
|
||||
}
|
||||
quicksort(tests, &cmp_test_unit);
|
||||
|
||||
TestContext context;
|
||||
test_context = &context;
|
||||
|
||||
PanicFn old_panic = builtin::panic;
|
||||
defer builtin::panic = old_panic;
|
||||
builtin::panic = &test_panic;
|
||||
int tests_passed = 0;
|
||||
int test_count = tests.len;
|
||||
DString name = dstring::temp_with_capacity(64);
|
||||
usz len = max_name + 9;
|
||||
name.append_repeat('-', len / 2);
|
||||
name.append(" TESTS ");
|
||||
name.append_repeat('-', len - len / 2);
|
||||
io::printn(name);
|
||||
name.clear();
|
||||
foreach(unit : tests)
|
||||
{
|
||||
defer name.clear();
|
||||
name.appendf("Testing %s ", unit.name);
|
||||
name.append_repeat('.', max_name - unit.name.len + 2);
|
||||
io::printf("%s ", name.str_view());
|
||||
(void)io::stdout().flush();
|
||||
if (libc::setjmp(&context.buf) == 0)
|
||||
{
|
||||
unit.func();
|
||||
io::printn("[ok]");
|
||||
tests_passed++;
|
||||
}
|
||||
}
|
||||
io::printfn("\n%d test%s run.\n", test_count, test_count > 1 ? "s" : "");
|
||||
io::printfn("Test Result: %s. %d passed, %d failed.",
|
||||
tests_passed < test_count ? "FAILED" : "ok", tests_passed, test_count - tests_passed);
|
||||
return test_count == tests_passed;
|
||||
}
|
||||
|
||||
fn bool default_test_runner(String[] args)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
return run_tests(test_collection_create(allocator::temp()));
|
||||
};
|
||||
}
|
||||
@@ -14,21 +14,21 @@ module std::core::sanitizer::asan;
|
||||
|
||||
def ErrorCallback = fn void (ZString);
|
||||
|
||||
/**
|
||||
* Marks a memory region ([addr, addr+size)) as unaddressable.
|
||||
*
|
||||
* This memory must be previously allocated by your program. Instrumented
|
||||
* code is forbidden from accessing addresses in this region until it is
|
||||
* unpoisoned. This function is not guaranteed to poison the entire region -
|
||||
* it could poison only a subregion of [addr, addr+size) due to ASan
|
||||
* alignment restrictions.
|
||||
*
|
||||
* NOTE This function is not thread-safe because no two threads can poison or
|
||||
* unpoison memory in the same memory region simultaneously.
|
||||
*
|
||||
* @param addr "Start of memory region."
|
||||
* @param size "Size of memory region."
|
||||
**/
|
||||
<*
|
||||
Marks a memory region ([addr, addr+size)) as unaddressable.
|
||||
|
||||
This memory must be previously allocated by your program. Instrumented
|
||||
code is forbidden from accessing addresses in this region until it is
|
||||
unpoisoned. This function is not guaranteed to poison the entire region -
|
||||
it could poison only a subregion of [addr, addr+size) due to ASan
|
||||
alignment restrictions.
|
||||
|
||||
NOTE This function is not thread-safe because no two threads can poison or
|
||||
unpoison memory in the same memory region simultaneously.
|
||||
|
||||
@param addr "Start of memory region."
|
||||
@param size "Size of memory region."
|
||||
*>
|
||||
macro poison_memory_region(void* addr, usz size)
|
||||
{
|
||||
$if env::ADDRESS_SANITIZER:
|
||||
@@ -36,20 +36,20 @@ macro poison_memory_region(void* addr, usz size)
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a memory region ([addr, addr+size)) as addressable.
|
||||
*
|
||||
* This memory must be previously allocated by your program. Accessing
|
||||
* addresses in this region is allowed until this region is poisoned again.
|
||||
* This function could unpoison a super-region of [addr, addr+size) due
|
||||
* to ASan alignment restrictions.
|
||||
*
|
||||
* NOTE This function is not thread-safe because no two threads can
|
||||
* poison or unpoison memory in the same memory region simultaneously.
|
||||
*
|
||||
* @param addr "Start of memory region."
|
||||
* @param size "Size of memory region."
|
||||
**/
|
||||
<*
|
||||
Marks a memory region ([addr, addr+size)) as addressable.
|
||||
|
||||
This memory must be previously allocated by your program. Accessing
|
||||
addresses in this region is allowed until this region is poisoned again.
|
||||
This function could unpoison a super-region of [addr, addr+size) due
|
||||
to ASan alignment restrictions.
|
||||
|
||||
NOTE This function is not thread-safe because no two threads can
|
||||
poison or unpoison memory in the same memory region simultaneously.
|
||||
|
||||
@param addr "Start of memory region."
|
||||
@param size "Size of memory region."
|
||||
*>
|
||||
macro unpoison_memory_region(void* addr, usz size)
|
||||
{
|
||||
$if env::ADDRESS_SANITIZER:
|
||||
@@ -57,11 +57,11 @@ macro unpoison_memory_region(void* addr, usz size)
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an address is poisoned.
|
||||
* @return "True if 'addr' is poisoned (that is, 1-byte read/write access to this address would result in an error report from ASan). Otherwise returns false."
|
||||
* @param addr "Address to check."
|
||||
**/
|
||||
<*
|
||||
Checks if an address is poisoned.
|
||||
@return "True if 'addr' is poisoned (that is, 1-byte read/write access to this address would result in an error report from ASan). Otherwise returns false."
|
||||
@param addr "Address to check."
|
||||
*>
|
||||
macro bool address_is_poisoned(void* addr)
|
||||
{
|
||||
$if env::ADDRESS_SANITIZER:
|
||||
@@ -71,28 +71,28 @@ macro bool address_is_poisoned(void* addr)
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a region is poisoned.
|
||||
*
|
||||
* If at least one byte in [beg, beg+size) is poisoned, returns the
|
||||
* address of the first such byte. Otherwise returns 0.
|
||||
*
|
||||
* @param beg "Start of memory region."
|
||||
* @param size "Start of memory region."
|
||||
* @return "Address of first poisoned byte."
|
||||
**/
|
||||
<*
|
||||
Checks if a region is poisoned.
|
||||
|
||||
If at least one byte in [beg, beg+size) is poisoned, returns the
|
||||
address of the first such byte. Otherwise returns 0.
|
||||
|
||||
@param beg "Start of memory region."
|
||||
@param size "Start of memory region."
|
||||
@return "Address of first poisoned byte."
|
||||
*>
|
||||
macro void* region_is_poisoned(void* beg, usz size)
|
||||
{
|
||||
$if env::ADDRESS_SANITIZER:
|
||||
return __asan_region_is_poisoned(addr);
|
||||
return __asan_region_is_poisoned(beg, size);
|
||||
$else
|
||||
return null;
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the callback function to be called during ASan error reporting.
|
||||
**/
|
||||
<*
|
||||
Sets the callback function to be called during ASan error reporting.
|
||||
*>
|
||||
fn void set_error_report_callback(ErrorCallback callback)
|
||||
{
|
||||
$if env::ADDRESS_SANITIZER:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
module std::core::string;
|
||||
import std::ascii;
|
||||
import std::io;
|
||||
|
||||
distinct String @if(!$defined(String)) = inline char[];
|
||||
distinct ZString = inline char*;
|
||||
@@ -33,77 +34,77 @@ fault NumberConversion
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a temporary ZString created using the formatting function.
|
||||
*
|
||||
* @param [in] fmt `The formatting string`
|
||||
**/
|
||||
macro ZString tformat_zstr(String fmt, ...)
|
||||
<*
|
||||
Return a temporary ZString created using the formatting function.
|
||||
|
||||
@param [in] fmt `The formatting string`
|
||||
*>
|
||||
fn ZString tformat_zstr(String fmt, args...)
|
||||
{
|
||||
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
|
||||
str.appendf(fmt, $vasplat);
|
||||
DString str = dstring::temp_with_capacity(fmt.len + args.len * 8);
|
||||
str.appendf(fmt, ...args);
|
||||
return str.zstr_view();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new String created using the formatting function.
|
||||
*
|
||||
* @param [inout] allocator `The allocator to use`
|
||||
* @param [in] fmt `The formatting string`
|
||||
**/
|
||||
macro String format(String fmt, ..., Allocator allocator)
|
||||
<*
|
||||
Return a new String created using the formatting function.
|
||||
|
||||
@param [inout] allocator `The allocator to use`
|
||||
@param [in] fmt `The formatting string`
|
||||
*>
|
||||
fn String format(String fmt, args..., Allocator allocator)
|
||||
{
|
||||
@pool(allocator)
|
||||
{
|
||||
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
|
||||
str.appendf(fmt, $vasplat);
|
||||
DString str = dstring::temp_with_capacity(fmt.len + args.len * 8);
|
||||
str.appendf(fmt, ...args);
|
||||
return str.copy_str(allocator);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a heap allocated String created using the formatting function.
|
||||
*
|
||||
* @param [in] fmt `The formatting string`
|
||||
**/
|
||||
macro String new_format(String fmt, ..., Allocator allocator = null) => format(fmt, $vasplat, .allocator = allocator ?: allocator::heap());
|
||||
<*
|
||||
Return a heap allocated String created using the formatting function.
|
||||
|
||||
/**
|
||||
* Return a temporary String created using the formatting function.
|
||||
*
|
||||
* @param [in] fmt `The formatting string`
|
||||
**/
|
||||
macro String tformat(String fmt, ...)
|
||||
@param [in] fmt `The formatting string`
|
||||
*>
|
||||
fn String new_format(String fmt, args..., Allocator allocator = null) => format(fmt, ...args, allocator: allocator ?: allocator::heap());
|
||||
|
||||
<*
|
||||
Return a temporary String created using the formatting function.
|
||||
|
||||
@param [in] fmt `The formatting string`
|
||||
*>
|
||||
fn String tformat(String fmt, args...)
|
||||
{
|
||||
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
|
||||
str.appendf(fmt, $vasplat);
|
||||
DString str = dstring::temp_with_capacity(fmt.len + args.len * 8);
|
||||
str.appendf(fmt, ...args);
|
||||
return str.str_view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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())
|
||||
<*
|
||||
Return a new ZString created using the formatting function.
|
||||
|
||||
@param [in] fmt `The formatting string`
|
||||
@param [inout] allocator `The allocator to use`
|
||||
*>
|
||||
fn ZString new_format_zstr(String fmt, args..., Allocator allocator = allocator::heap())
|
||||
{
|
||||
@pool(allocator)
|
||||
{
|
||||
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
|
||||
str.appendf(fmt, $vasplat);
|
||||
DString str = dstring::temp_with_capacity(fmt.len + args.len * 8);
|
||||
str.appendf(fmt, ...args);
|
||||
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`
|
||||
**/
|
||||
<*
|
||||
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)
|
||||
{
|
||||
foreach (ch : set) if (ch == c) return true;
|
||||
@@ -135,33 +136,59 @@ fn String join_new(String[] s, String joiner, Allocator allocator = allocator::h
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove characters from the front and end of a string.
|
||||
*
|
||||
* @param [in] string `The string to trim`
|
||||
* @param [in] to_trim `The set of characters to trim, defaults to whitespace`
|
||||
* @pure
|
||||
* @return `a substring of the string passed in`
|
||||
**/
|
||||
<*
|
||||
Remove characters from the front and end of a string.
|
||||
|
||||
@param [in] string `The string to trim`
|
||||
@param [in] to_trim `The set of characters to trim, defaults to whitespace`
|
||||
@pure
|
||||
@return `a substring of the string passed in`
|
||||
*>
|
||||
fn String String.trim(string, String to_trim = "\t\n\r ")
|
||||
{
|
||||
return string.trim_left(to_trim).trim_right(to_trim);
|
||||
}
|
||||
|
||||
<*
|
||||
Remove characters from the front of a string.
|
||||
|
||||
@param [in] string `The string to trim`
|
||||
@param [in] to_trim `The set of characters to trim, defaults to whitespace`
|
||||
@pure
|
||||
@return `a substring of the string passed in`
|
||||
*>
|
||||
fn String String.trim_left(string, String to_trim = "\t\n\r ")
|
||||
{
|
||||
usz start = 0;
|
||||
usz len = string.len;
|
||||
while (start < len && char_in_set(string[start], to_trim)) start++;
|
||||
if (start == len) return string[:0];
|
||||
usz end = len - 1;
|
||||
while (end > start && char_in_set(string[end], to_trim)) end--;
|
||||
return string[start..end];
|
||||
return string[start..];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the String starts with the needle.
|
||||
*
|
||||
* @param [in] string
|
||||
* @param [in] needle
|
||||
* @pure
|
||||
* @return `'true' if the string starts with the needle`
|
||||
**/
|
||||
<*
|
||||
Remove characters from the end of a string.
|
||||
|
||||
@param [in] string `The string to trim`
|
||||
@param [in] to_trim `The set of characters to trim, defaults to whitespace`
|
||||
@pure
|
||||
@return `a substring of the string passed in`
|
||||
*>
|
||||
fn String String.trim_right(string, String to_trim = "\t\n\r ")
|
||||
{
|
||||
usz len = string.len;
|
||||
while (len > 0 && char_in_set(string[len - 1], to_trim)) len--;
|
||||
return string[:len];
|
||||
}
|
||||
|
||||
<*
|
||||
Check if the String starts with the needle.
|
||||
|
||||
@param [in] string
|
||||
@param [in] needle
|
||||
@pure
|
||||
@return `'true' if the string starts with the needle`
|
||||
*>
|
||||
fn bool String.starts_with(string, String needle)
|
||||
{
|
||||
if (needle.len > string.len) return false;
|
||||
@@ -169,14 +196,14 @@ fn bool String.starts_with(string, String needle)
|
||||
return string[:needle.len] == needle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the String ends with the needle.
|
||||
*
|
||||
* @param [in] string
|
||||
* @param [in] needle
|
||||
* @pure
|
||||
* @return `'true' if the string ends with the needle`
|
||||
**/
|
||||
<*
|
||||
Check if the String ends with the needle.
|
||||
|
||||
@param [in] string
|
||||
@param [in] needle
|
||||
@pure
|
||||
@return `'true' if the string ends with the needle`
|
||||
*>
|
||||
fn bool String.ends_with(string, String needle)
|
||||
{
|
||||
if (needle.len > string.len) return false;
|
||||
@@ -184,28 +211,28 @@ fn bool String.ends_with(string, String needle)
|
||||
return string[^needle.len..] == needle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip the front of the string if the prefix exists.
|
||||
*
|
||||
* @param [in] string
|
||||
* @param [in] needle
|
||||
* @pure
|
||||
* @return `the substring with the prefix removed`
|
||||
**/
|
||||
<*
|
||||
Strip the front of the string if the prefix exists.
|
||||
|
||||
@param [in] string
|
||||
@param [in] needle
|
||||
@pure
|
||||
@return `the substring with the prefix removed`
|
||||
*>
|
||||
fn String String.strip(string, String needle)
|
||||
{
|
||||
if (!needle.len || !string.starts_with(needle)) return string;
|
||||
return string[needle.len..];
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip the end of the string if the suffix exists.
|
||||
*
|
||||
* @param [in] string
|
||||
* @param [in] needle
|
||||
* @pure
|
||||
* @return `the substring with the suffix removed`
|
||||
**/
|
||||
<*
|
||||
Strip the end of the string if the suffix exists.
|
||||
|
||||
@param [in] string
|
||||
@param [in] needle
|
||||
@pure
|
||||
@return `the substring with the suffix removed`
|
||||
*>
|
||||
fn String String.strip_end(string, String needle)
|
||||
{
|
||||
if (!needle.len || !string.ends_with(needle)) return string;
|
||||
@@ -213,17 +240,19 @@ fn String String.strip_end(string, String needle)
|
||||
return string[:(string.len - needle.len)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a string into parts, e.g "a|b|c" split with "|" yields { "a", "b", "c" }
|
||||
*
|
||||
* @param [in] s
|
||||
* @param [in] needle
|
||||
* @param [&inout] allocator "The allocator to use for the String[]"
|
||||
* @param max "Max number of elements, 0 means no limit, defaults to 0"
|
||||
* @require needle.len > 0 "The needle must be at least 1 character long"
|
||||
* @ensure return.len > 0
|
||||
**/
|
||||
fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = allocator::heap())
|
||||
<*
|
||||
Split a string into parts, e.g "a|b|c" split with "|" yields { "a", "b", "c" }
|
||||
|
||||
@param [in] s
|
||||
@param [in] needle
|
||||
@param max "Max number of elements, 0 means no limit, defaults to 0"
|
||||
@param skip_empty "True to skip empty elements"
|
||||
@param [&inout] allocator "The allocator to use for the String[]"
|
||||
|
||||
@require needle.len > 0 "The needle must be at least 1 character long"
|
||||
@ensure return.len > 0
|
||||
*>
|
||||
fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = allocator::heap(), bool skip_empty = false)
|
||||
{
|
||||
usz capacity = 16;
|
||||
usz i = 0;
|
||||
@@ -243,6 +272,11 @@ fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = al
|
||||
res = s;
|
||||
no_more = true;
|
||||
}
|
||||
if (!res.len && skip_empty)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == capacity)
|
||||
{
|
||||
capacity *= 2;
|
||||
@@ -253,51 +287,98 @@ fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = al
|
||||
return holder[:i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a string into parts, e.g "a|b|c" split with "|" yields { "a", "b", "c" }, using the heap allocator
|
||||
* to store the parts.
|
||||
*
|
||||
* @param [in] s
|
||||
* @param [in] needle
|
||||
* @param max "Max number of elements, 0 means no limit, defaults to 0"
|
||||
* @require needle.len > 0 "The needle must be at least 1 character long"
|
||||
* @ensure return.len > 0
|
||||
**/
|
||||
fn String[] String.new_split(s, String needle, usz max = 0) => s.split(needle, max, allocator::heap()) @inline;
|
||||
<*
|
||||
Split a string into parts, e.g "a|b|c" split with "|" yields { "a", "b", "c" }, using the heap allocator
|
||||
to store the parts.
|
||||
|
||||
/**
|
||||
* This function is identical to String.split, but implicitly uses the
|
||||
* temporary allocator.
|
||||
*
|
||||
* @param [in] s
|
||||
* @param [in] needle
|
||||
* @param max "Max number of elements, 0 means no limit, defaults to 0"
|
||||
**/
|
||||
fn String[] String.tsplit(s, String needle, usz max = 0) => s.split(needle, max, allocator::temp()) @inline;
|
||||
@param [in] s
|
||||
@param [in] needle
|
||||
@param max "Max number of elements, 0 means no limit, defaults to 0"
|
||||
@param skip_empty "True to skip empty elements"
|
||||
@require needle.len > 0 "The needle must be at least 1 character long"
|
||||
@ensure return.len > 0
|
||||
*>
|
||||
fn String[] String.new_split(s, String needle, usz max = 0, bool skip_empty) => s.split(needle, max, allocator::heap(), skip_empty) @inline;
|
||||
|
||||
/**
|
||||
* Check if a substring is found in the string.
|
||||
<*
|
||||
This function is identical to String.split, but implicitly uses the
|
||||
temporary allocator.
|
||||
|
||||
* @param [in] s
|
||||
* @param [in] needle "The string to look for."
|
||||
* @pure
|
||||
* @return "true if the string contains the substring, false otherwise"
|
||||
**/
|
||||
@param [in] s
|
||||
@param [in] needle
|
||||
@param max "Max number of elements, 0 means no limit, defaults to 0"
|
||||
@param skip_empty "True to skip empty elements"
|
||||
*>
|
||||
fn String[] String.tsplit(s, String needle, usz max = 0, bool skip_empty = false) => s.split(needle, max, allocator::temp(), skip_empty) @inline;
|
||||
|
||||
fault SplitResult { BUFFER_EXCEEDED }
|
||||
|
||||
<*
|
||||
Split a string into parts, e.g "a|b|c" split with "|" yields { "a", "b", "c" }
|
||||
|
||||
@param [in] s
|
||||
@param [in] needle
|
||||
@param [inout] buffer
|
||||
@param max "Max number of elements, 0 means no limit, defaults to 0"
|
||||
@require needle.len > 0 "The needle must be at least 1 character long"
|
||||
@ensure return.len > 0
|
||||
@return! SplitResult.BUFFER_EXCEEDED `If there are more elements than would fit the buffer`
|
||||
*>
|
||||
fn String[]! String.split_to_buffer(s, String needle, String[] buffer, usz max = 0, bool skip_empty = false)
|
||||
{
|
||||
usz max_capacity = buffer.len;
|
||||
usz i = 0;
|
||||
bool no_more = false;
|
||||
while (!no_more)
|
||||
{
|
||||
usz! index = i == max - 1 ? SearchResult.MISSING? : s.index_of(needle);
|
||||
String res @noinit;
|
||||
if (try index)
|
||||
{
|
||||
res = s[:index];
|
||||
s = s[index + needle.len..];
|
||||
}
|
||||
else
|
||||
{
|
||||
res = s;
|
||||
no_more = true;
|
||||
}
|
||||
if (!res.len && skip_empty)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (i == max_capacity)
|
||||
{
|
||||
return SplitResult.BUFFER_EXCEEDED?;
|
||||
}
|
||||
buffer[i++] = res;
|
||||
}
|
||||
return buffer[:i];
|
||||
}
|
||||
|
||||
<*
|
||||
Check if a substring is found in the string.
|
||||
|
||||
@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)
|
||||
{
|
||||
return @ok(s.index_of(needle));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the index of the first incidence of a string.
|
||||
*
|
||||
* @param [in] s
|
||||
* @param needle "The character to look for"
|
||||
* @pure
|
||||
* @ensure return < s.len
|
||||
* @return "the index of the needle"
|
||||
* @return! SearchResult.MISSING "if the needle cannot be found"
|
||||
**/
|
||||
<*
|
||||
Find the index of the first incidence of a string.
|
||||
|
||||
@param [in] s
|
||||
@param needle "The character to look for"
|
||||
@pure
|
||||
@ensure return < s.len
|
||||
@return "the index of the needle"
|
||||
@return! SearchResult.MISSING "if the needle cannot be found"
|
||||
*>
|
||||
fn usz! String.index_of_char(s, char needle)
|
||||
{
|
||||
foreach (i, c : s)
|
||||
@@ -307,17 +388,40 @@ fn usz! String.index_of_char(s, char needle)
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the index of the first incidence of a character.
|
||||
*
|
||||
* @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"
|
||||
**/
|
||||
<*
|
||||
Find the index of the first incidence of a one of the chars.
|
||||
|
||||
@param [in] s
|
||||
@param [in] needle "The characters to look for"
|
||||
@pure
|
||||
@ensure return < s.len
|
||||
@return "the index of the needle"
|
||||
@return! SearchResult.MISSING "if the needle cannot be found"
|
||||
*>
|
||||
fn usz! String.index_of_chars(String s, char[] needle)
|
||||
{
|
||||
foreach (i, c : s)
|
||||
{
|
||||
foreach (j, pin : needle)
|
||||
{
|
||||
if (c == pin) return i;
|
||||
}
|
||||
}
|
||||
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
<*
|
||||
Find the index of the first incidence of a character.
|
||||
|
||||
@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;
|
||||
@@ -329,16 +433,16 @@ fn usz! String.index_of_char_from(s, char needle, usz start_index)
|
||||
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
|
||||
* @ensure return < s.len
|
||||
* @return "the index of the needle"
|
||||
* @return! SearchResult.MISSING "if the needle cannot be found"
|
||||
**/
|
||||
<*
|
||||
Find the index of the first incidence of a character starting from the end.
|
||||
|
||||
@param [in] s
|
||||
@param needle "the character to find"
|
||||
@pure
|
||||
@ensure return < s.len
|
||||
@return "the index of the needle"
|
||||
@return! SearchResult.MISSING "if the needle cannot be found"
|
||||
*>
|
||||
fn usz! String.rindex_of_char(s, char needle)
|
||||
{
|
||||
foreach_r (i, c : s)
|
||||
@@ -348,17 +452,17 @@ fn usz! String.rindex_of_char(s, char needle)
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the index of the first incidence of a string.
|
||||
*
|
||||
* @param [in] s
|
||||
* @param [in] needle
|
||||
* @pure
|
||||
* @ensure return < s.len
|
||||
* @require needle.len > 0 : "The needle must be len 1 or more"
|
||||
* @return "the index of the needle"
|
||||
* @return! SearchResult.MISSING "if the needle cannot be found"
|
||||
**/
|
||||
<*
|
||||
Find the index of the first incidence of a string.
|
||||
|
||||
@param [in] s
|
||||
@param [in] needle
|
||||
@pure
|
||||
@ensure return < s.len
|
||||
@require needle.len > 0 : "The needle must be len 1 or more"
|
||||
@return "the index of the needle"
|
||||
@return! SearchResult.MISSING "if the needle cannot be found"
|
||||
*>
|
||||
fn usz! String.index_of(s, String needle)
|
||||
{
|
||||
usz needed = needle.len;
|
||||
@@ -373,17 +477,17 @@ fn usz! String.index_of(s, String needle)
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the index of the last incidence of a string.
|
||||
*
|
||||
* @param [in] s
|
||||
* @param [in] needle
|
||||
* @pure
|
||||
* @ensure return < s.len
|
||||
* @require needle.len > 0 "The needle must be len 1 or more"
|
||||
* @return "the index of the needle"
|
||||
* @return! SearchResult.MISSING "if the needle cannot be found"
|
||||
**/
|
||||
<*
|
||||
Find the index of the last incidence of a string.
|
||||
|
||||
@param [in] s
|
||||
@param [in] needle
|
||||
@pure
|
||||
@ensure return < s.len
|
||||
@require needle.len > 0 "The needle must be len 1 or more"
|
||||
@return "the index of the needle"
|
||||
@return! SearchResult.MISSING "if the needle cannot be found"
|
||||
*>
|
||||
fn usz! String.rindex_of(s, String needle)
|
||||
{
|
||||
usz needed = needle.len;
|
||||
@@ -448,6 +552,11 @@ fn String String.tconcat(s1, String s2) => s1.concat(s2, allocator::temp());
|
||||
|
||||
fn ZString String.zstr_tcopy(s) => s.zstr_copy(allocator::temp()) @inline;
|
||||
|
||||
<*
|
||||
Copy this string, by duplicating the string, always adding a zero byte
|
||||
sentinel, so that it safely can be converted to a ZString by a
|
||||
cast.
|
||||
*>
|
||||
fn String String.copy(s, Allocator allocator = allocator::heap())
|
||||
{
|
||||
usz len = s.len;
|
||||
@@ -459,14 +568,14 @@ fn String String.copy(s, Allocator allocator = allocator::heap())
|
||||
|
||||
fn void String.free(&s, Allocator allocator = allocator::heap())
|
||||
{
|
||||
if (!s.len) return;
|
||||
if (!s.ptr) return;
|
||||
allocator::free(allocator, s.ptr);
|
||||
*s = "";
|
||||
}
|
||||
|
||||
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::heap())
|
||||
{
|
||||
return z.str_view().copy(allocator) @inline;
|
||||
}
|
||||
@@ -476,12 +585,12 @@ fn String ZString.tcopy(z)
|
||||
return z.str_view().copy(allocator::temp()) @inline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an UTF-8 string to UTF-16
|
||||
* @return "The UTF-16 string as a slice, allocated using the given allocator"
|
||||
* @return! UnicodeResult.INVALID_UTF8 "If the string contained an invalid UTF-8 sequence"
|
||||
* @return! AllocationFailure "If allocation of the string fails"
|
||||
**/
|
||||
<*
|
||||
Convert an UTF-8 string to UTF-16
|
||||
@return "The UTF-16 string as a slice, allocated using the given allocator"
|
||||
@return! UnicodeResult.INVALID_UTF8 "If the string contained an invalid UTF-8 sequence"
|
||||
@return! AllocationFailure "If allocation of the string fails"
|
||||
*>
|
||||
fn Char16[]! String.to_new_utf16(s, Allocator allocator = allocator::heap())
|
||||
{
|
||||
usz len16 = conv::utf16len_for_utf8(s);
|
||||
@@ -491,12 +600,12 @@ fn Char16[]! String.to_new_utf16(s, Allocator allocator = allocator::heap())
|
||||
return data[:len16];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an UTF-8 string to UTF-16
|
||||
* @return "The UTF-16 string as a slice, allocated using the given allocator"
|
||||
* @return! UnicodeResult.INVALID_UTF8 "If the string contained an invalid UTF-8 sequence"
|
||||
* @return! AllocationFailure "If allocation of the string fails"
|
||||
**/
|
||||
<*
|
||||
Convert an UTF-8 string to UTF-16
|
||||
@return "The UTF-16 string as a slice, allocated using the given allocator"
|
||||
@return! UnicodeResult.INVALID_UTF8 "If the string contained an invalid UTF-8 sequence"
|
||||
@return! AllocationFailure "If allocation of the string fails"
|
||||
*>
|
||||
fn Char16[]! String.to_temp_utf16(s)
|
||||
{
|
||||
return s.to_new_utf16(allocator::temp());
|
||||
@@ -522,12 +631,12 @@ fn Char32[]! String.to_utf32(s, Allocator allocator)
|
||||
fn Char32[]! String.to_new_utf32(s) => s.to_utf32(allocator::heap()) @inline;
|
||||
fn Char32[]! String.to_temp_utf32(s) => s.to_utf32(allocator::temp()) @inline;
|
||||
|
||||
/**
|
||||
* Convert a string to ASCII lower case.
|
||||
*
|
||||
* @param [inout] s
|
||||
* @pure
|
||||
**/
|
||||
<*
|
||||
Convert a string to ASCII lower case.
|
||||
|
||||
@param [inout] s
|
||||
@pure
|
||||
*>
|
||||
fn void String.convert_ascii_to_lower(s)
|
||||
{
|
||||
foreach (&c : s) if (c.is_upper() @pure) *c += 'a' - 'A';
|
||||
@@ -540,30 +649,30 @@ fn String String.new_ascii_to_lower(s, Allocator allocator = allocator::heap())
|
||||
return copy;
|
||||
}
|
||||
|
||||
fn String String.temp_ascii_to_lower(s, Allocator allocator = allocator::heap())
|
||||
fn String String.temp_ascii_to_lower(s)
|
||||
{
|
||||
return s.new_ascii_to_lower(allocator::temp());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to ASCII upper case.
|
||||
*
|
||||
* @param [inout] s
|
||||
* @pure
|
||||
**/
|
||||
<*
|
||||
Convert a string to ASCII upper case.
|
||||
|
||||
@param [inout] s
|
||||
@pure
|
||||
*>
|
||||
fn void String.convert_ascii_to_upper(s)
|
||||
{
|
||||
foreach (&c : s) if (c.is_lower() @pure) *c -= 'a' - 'A';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string converted to ASCII upper case.
|
||||
*
|
||||
* @param [in] s
|
||||
* @param [inout] allocator
|
||||
*
|
||||
* @return `a new String converted to ASCII upper case.`
|
||||
**/
|
||||
<*
|
||||
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);
|
||||
@@ -576,10 +685,10 @@ fn StringIterator String.iterator(s)
|
||||
return { s, 0 };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [in] s
|
||||
* @return `a temporary String converted to ASCII upper case.`
|
||||
**/
|
||||
<*
|
||||
@param [in] s
|
||||
@return `a temporary String converted to ASCII upper case.`
|
||||
*>
|
||||
fn String String.temp_ascii_to_upper(s)
|
||||
{
|
||||
return s.new_ascii_to_upper(allocator::temp());
|
||||
@@ -627,9 +736,9 @@ fn usz String.utf8_codepoints(s)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @require (base <= 10 && base > 1) || base == 16 : "Unsupported base"
|
||||
**/
|
||||
<*
|
||||
@require (base <= 10 && base > 1) || base == 16 : "Unsupported base"
|
||||
*>
|
||||
macro String.to_integer(string, $Type, int base = 10)
|
||||
{
|
||||
usz len = string.len;
|
||||
@@ -718,7 +827,12 @@ fn float! String.to_float(s) => s.to_real(float);
|
||||
|
||||
fn Splitter String.splitter(self, String split)
|
||||
{
|
||||
return Splitter { self, split, 0 };
|
||||
return { .string = self, .split = split };
|
||||
}
|
||||
|
||||
fn Splitter String.tokenize(self, String split)
|
||||
{
|
||||
return { .string = self, .split = split, .tokenize = true };
|
||||
}
|
||||
|
||||
struct Splitter
|
||||
@@ -726,6 +840,8 @@ struct Splitter
|
||||
String string;
|
||||
String split;
|
||||
usz current;
|
||||
bool tokenize;
|
||||
int last_index;
|
||||
}
|
||||
|
||||
fn void Splitter.reset(&self)
|
||||
@@ -735,18 +851,33 @@ fn void Splitter.reset(&self)
|
||||
|
||||
fn String! Splitter.next(&self)
|
||||
{
|
||||
usz len = self.string.len;
|
||||
usz current = self.current;
|
||||
if (current >= len) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
String remaining = self.string[current..];
|
||||
usz! next = remaining.index_of(self.split);
|
||||
if (try next)
|
||||
while (true)
|
||||
{
|
||||
defer self.current = current + next + self.split.len;
|
||||
return remaining[:next];
|
||||
usz len = self.string.len;
|
||||
usz current = self.current;
|
||||
if (current >= len) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
String remaining = self.string[current..];
|
||||
usz! next = remaining.index_of(self.split);
|
||||
if (try next)
|
||||
{
|
||||
self.current = current + next + self.split.len;
|
||||
if (!next && self.tokenize) continue;
|
||||
return remaining[:next];
|
||||
}
|
||||
self.current = len;
|
||||
return remaining;
|
||||
}
|
||||
self.current = len;
|
||||
return remaining;
|
||||
}
|
||||
|
||||
macro String new_struct_to_str(x, Allocator allocator = allocator::heap())
|
||||
{
|
||||
DString s;
|
||||
@stack_mem(512; Allocator mem)
|
||||
{
|
||||
s.new_init(allocator: mem);
|
||||
io::fprint(&s, x)!!;
|
||||
return s.copy_str(allocator);
|
||||
};
|
||||
}
|
||||
|
||||
macro String temp_struct_to_str(x) => new_struct_to_str(x, allocator::temp());
|
||||
|
||||
@@ -31,9 +31,9 @@ const MASK = KMAX - 1;
|
||||
const B1B_DIG = 2;
|
||||
const uint[2] B1B_MAX = { 9007199, 254740991 };
|
||||
|
||||
/**
|
||||
* @require chars.len > 0
|
||||
**/
|
||||
<*
|
||||
@require chars.len > 0
|
||||
*>
|
||||
macro double! decfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
{
|
||||
uint[KMAX] x;
|
||||
@@ -266,7 +266,7 @@ macro double! decfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
y *= sign;
|
||||
|
||||
bool denormal;
|
||||
/* Limit precision for denormal results */
|
||||
// Limit precision for denormal results
|
||||
uint bits = $bits;
|
||||
if (bits > math::DOUBLE_MANT_DIG + e2 - $emin)
|
||||
{
|
||||
|
||||
@@ -8,18 +8,24 @@ fault ConversionResult
|
||||
VALUE_OUT_OF_RANGE,
|
||||
VALUE_OUT_OF_UNSIGNED_RANGE,
|
||||
}
|
||||
/**
|
||||
* @require $Type.kindof.is_int() || $Type.kindof == TypeKind.ENUM "Argument was not an integer"
|
||||
**/
|
||||
|
||||
<*
|
||||
@require $Type.kindof.is_int() "Type was not an integer"
|
||||
@require v.type.kindof == ENUM "Value was not an enum"
|
||||
*>
|
||||
macro any_to_enum_ordinal(any v, $Type)
|
||||
{
|
||||
return any_to_int(v.as_inner(), $Type);
|
||||
}
|
||||
|
||||
<*
|
||||
@require $Type.kindof.is_int() "Type was not an integer"
|
||||
@require v.type.kindof.is_int() "Value was not an integer"
|
||||
*>
|
||||
macro any_to_int(any v, $Type)
|
||||
{
|
||||
typeid any_type = v.type;
|
||||
TypeKind kind = any_type.kindof;
|
||||
if (kind == TypeKind.ENUM)
|
||||
{
|
||||
any_type = any_type.inner;
|
||||
kind = any_type.kindof;
|
||||
}
|
||||
bool is_mixed_signed = $Type.kindof != any_type.kindof;
|
||||
$Type max = $Type.max;
|
||||
$Type min = $Type.min;
|
||||
@@ -123,9 +129,9 @@ macro bool is_slice_convertable($Type)
|
||||
macro bool is_bool($Type) @const => $Type.kindof == TypeKind.BOOL;
|
||||
macro bool is_int($Type) @const => $Type.kindof == TypeKind.SIGNED_INT || $Type.kindof == TypeKind.UNSIGNED_INT;
|
||||
|
||||
/**
|
||||
* @require is_numerical($Type) "Expected a numerical type"
|
||||
**/
|
||||
<*
|
||||
@require is_numerical($Type) "Expected a numerical type"
|
||||
*>
|
||||
macro bool is_signed($Type) @const
|
||||
{
|
||||
$switch (inner_kind($Type))
|
||||
@@ -139,9 +145,9 @@ macro bool is_signed($Type) @const
|
||||
$endswitch
|
||||
}
|
||||
|
||||
/**
|
||||
* @require is_numerical($Type) "Expected a numerical type"
|
||||
**/
|
||||
<*
|
||||
@require is_numerical($Type) "Expected a numerical type"
|
||||
*>
|
||||
macro bool is_unsigned($Type) @const
|
||||
{
|
||||
$switch (inner_kind($Type))
|
||||
@@ -260,9 +266,9 @@ macro bool may_load_atomic($Type) @const
|
||||
macro lower_to_atomic_compatible_type($Type) @const
|
||||
{
|
||||
$switch ($Type.kindof)
|
||||
$case SIGNED_INT:
|
||||
$case SIGNED_INT:
|
||||
$case UNSIGNED_INT:
|
||||
return $Type.typeid;
|
||||
return $Type.typeid;
|
||||
$case DISTINCT:
|
||||
return lower_to_atomic_compatible_type($Type.inner);
|
||||
$case FLOAT:
|
||||
@@ -304,9 +310,9 @@ macro bool is_equatable_type($Type) @const
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a type implements the copy protocol.
|
||||
**/
|
||||
<*
|
||||
Checks if a type implements the copy protocol.
|
||||
*>
|
||||
macro bool implements_copy($Type) @const
|
||||
{
|
||||
return $defined($Type.copy) && $defined($Type.free);
|
||||
|
||||
@@ -3,9 +3,9 @@ module std::core::values;
|
||||
macro typeid @typeid(#value) @const @builtin => $typeof(#value).typeid;
|
||||
macro TypeKind @typekind(#value) @const @builtin => $typeof(#value).kindof;
|
||||
macro bool @typeis(#value, $Type) @const @builtin => $typeof(#value).typeid == $Type.typeid;
|
||||
/**
|
||||
* 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) @const => $typeof(#value1).typeid == $typeof(#value2).typeid;
|
||||
macro bool @is_bool(#value) @const => types::is_bool($typeof(#value));
|
||||
macro bool @is_int(#value) @const => types::is_int($typeof(#value));
|
||||
@@ -16,6 +16,7 @@ macro bool @is_promotable_to_float(#value) @const => types::is_promotable_to_flo
|
||||
macro bool @is_vector(#value) @const => types::is_vector($typeof(#value));
|
||||
macro bool @is_same_vector_type(#value1, #value2) @const => types::is_same_vector_type($typeof(#value1), $typeof(#value2));
|
||||
macro bool @assign_to(#value1, #value2) @const => $assignable(#value1, $typeof(#value2));
|
||||
macro bool @is_lvalue(#value) => $defined(#value = #value);
|
||||
|
||||
macro promote_int(x)
|
||||
{
|
||||
|
||||
@@ -1,2 +1,12 @@
|
||||
module std::crypto;
|
||||
|
||||
fn bool safe_compare(void* data1, void* data2, usz len)
|
||||
{
|
||||
char match = 0;
|
||||
for (usz i = 0; i < len; i++)
|
||||
{
|
||||
match = match | (mem::@volatile_load(((char*)data1)[i]) ^ mem::@volatile_load(((char*)data2)[i]));
|
||||
}
|
||||
return match == 0;
|
||||
}
|
||||
|
||||
|
||||
12
lib/std/crypto/dh.c3
Normal file
12
lib/std/crypto/dh.c3
Normal file
@@ -0,0 +1,12 @@
|
||||
module std::crypto::dh;
|
||||
import std::math::bigint;
|
||||
|
||||
fn BigInt generate_secret(BigInt p, BigInt x, BigInt y)
|
||||
{
|
||||
return y.mod_pow(x, p);
|
||||
}
|
||||
|
||||
fn BigInt public_key(BigInt p, BigInt g, BigInt x)
|
||||
{
|
||||
return g.mod_pow(x, p);
|
||||
}
|
||||
@@ -9,12 +9,12 @@ struct Rc4
|
||||
char[256] state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the RC4 state.
|
||||
*
|
||||
* @param [in] key "The key to use"
|
||||
* @require key.len > 0 "The key must be at least 1 byte long"
|
||||
**/
|
||||
<*
|
||||
Initialize the RC4 state.
|
||||
|
||||
@param [in] key "The key to use"
|
||||
@require key.len > 0 "The key must be at least 1 byte long"
|
||||
*>
|
||||
fn void Rc4.init(&self, char[] key)
|
||||
{
|
||||
// Init the state matrix
|
||||
@@ -28,11 +28,11 @@ fn void Rc4.init(&self, char[] key)
|
||||
self.j = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a single pass of en/decryption using a particular key.
|
||||
* @param [in] key
|
||||
* @param [inout] data
|
||||
**/
|
||||
<*
|
||||
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;
|
||||
@@ -40,13 +40,13 @@ fn void crypt(char[] key, char[] data)
|
||||
rc4.crypt(data, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt or decrypt a sequence of bytes.
|
||||
*
|
||||
* @param [in] in "The input"
|
||||
* @param [out] out "The output"
|
||||
* @require in.len <= out.len "Output would overflow"
|
||||
**/
|
||||
<*
|
||||
Encrypt or decrypt a sequence of bytes.
|
||||
|
||||
@param [in] in "The input"
|
||||
@param [out] out "The output"
|
||||
@require in.len <= out.len "Output would overflow"
|
||||
*>
|
||||
fn void Rc4.crypt(&self, char[] in, char[] out)
|
||||
{
|
||||
uint i = self.i;
|
||||
@@ -64,11 +64,11 @@ fn void Rc4.crypt(&self, char[] in, char[] out)
|
||||
self.j = j;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the rc4 state.
|
||||
*
|
||||
* @param [&out] self "The RC4 State"
|
||||
**/
|
||||
<*
|
||||
Clear the rc4 state.
|
||||
|
||||
@param [&out] self "The RC4 State"
|
||||
*>
|
||||
fn void Rc4.destroy(&self)
|
||||
{
|
||||
*self = {};
|
||||
|
||||
406
lib/std/encoding/base32.c3
Normal file
406
lib/std/encoding/base32.c3
Normal file
@@ -0,0 +1,406 @@
|
||||
module std::encoding::base32;
|
||||
|
||||
// This module implements base32 encoding according to RFC 4648
|
||||
// (https://www.rfc-editor.org/rfc/rfc4648)
|
||||
|
||||
struct Base32Alphabet
|
||||
{
|
||||
char[32] encoding;
|
||||
char[256] reverse;
|
||||
}
|
||||
|
||||
const char NO_PAD = 0;
|
||||
const char DEFAULT_PAD = '=';
|
||||
|
||||
<*
|
||||
Encode the content of src into a newly allocated string
|
||||
@param [in] src "The input to be encoded."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@param alphabet "The alphabet to use"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "The encoded string."
|
||||
*>
|
||||
fn String! encode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
char[] dst = allocator::alloc_array(allocator, char, encode_len(src.len, padding));
|
||||
return encode_buffer(src, dst, padding, alphabet);
|
||||
}
|
||||
|
||||
<*
|
||||
Decode the content of src into a newly allocated char array.
|
||||
@param [in] src "The input to be encoded."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@param alphabet "The alphabet to use"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "The decoded data."
|
||||
*>
|
||||
fn char[]! decode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
char[] dst = allocator::alloc_array(allocator, char, decode_len(src.len, padding));
|
||||
return decode_buffer(src, dst, padding, alphabet);
|
||||
}
|
||||
|
||||
fn String! encode_new(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::heap(), padding, alphabet);
|
||||
fn String! encode_temp(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::temp(), padding, alphabet);
|
||||
fn char[]! decode_new(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::heap(), padding, alphabet);
|
||||
fn char[]! decode_temp(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::temp(), padding, alphabet);
|
||||
|
||||
<*
|
||||
Calculate the length in bytes of the decoded data.
|
||||
@param n "Length in bytes of input."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "Length in bytes of the decoded data."
|
||||
*>
|
||||
fn usz decode_len(usz n, char padding)
|
||||
{
|
||||
if (padding) return (n / 8) * 5;
|
||||
// no padding
|
||||
usz trailing = n % 8;
|
||||
return n / 8 * 5 + (trailing * 5 ) / 8;
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the length in bytes of the encoded data.
|
||||
@param n "Length in bytes on input."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "Length in bytes of the encoded data."
|
||||
*>
|
||||
fn usz encode_len(usz n, char padding)
|
||||
{
|
||||
// A character is encoded into 8 x 5-bit blocks.
|
||||
if (padding) return (n + 4) / 5 * 8;
|
||||
|
||||
// no padding
|
||||
usz trailing = n % 5;
|
||||
return n / 5 * 8 + (trailing * 8 + 4) / 5;
|
||||
}
|
||||
|
||||
<*
|
||||
Decode the content of src into dst, which must be properly sized.
|
||||
@param src "The input to be decoded."
|
||||
@param dst "The decoded input."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@param alphabet "The alphabet to use"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@require dst.len >= decode_len(src.len, padding) "Destination buffer too small"
|
||||
@return "The resulting dst buffer"
|
||||
@return! DecodingFailure
|
||||
*>
|
||||
fn char[]! decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
if (src.len == 0) return dst[:0];
|
||||
char* dst_ptr = dst;
|
||||
usz dn = decode_len(src.len, padding);
|
||||
usz n;
|
||||
char[8] buf;
|
||||
while (src.len > 0 && dst.len > 0)
|
||||
{
|
||||
usz i @noinit;
|
||||
// load 8 bytes into buffer
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
if (src.len == 0)
|
||||
{
|
||||
if (padding > 0) return DecodingFailure.INVALID_PADDING?;
|
||||
break;
|
||||
}
|
||||
if (src[0] == padding) break;
|
||||
buf[i] = alphabet.reverse[src[0]];
|
||||
if (buf[i] == INVALID) return DecodingFailure.INVALID_CHARACTER?;
|
||||
src = src[1..];
|
||||
}
|
||||
|
||||
// extract 5-bytes from the buffer which contains 8 x 5 bit chunks
|
||||
switch (i)
|
||||
{
|
||||
case 8:
|
||||
// |66677777| dst[4]
|
||||
// | 77777| buf[7]
|
||||
// |666 | buf[6] << 5
|
||||
dst[4] = buf[7] | buf[6] << 5;
|
||||
n++;
|
||||
nextcase 7;
|
||||
case 7:
|
||||
// |45555566| dst[3]
|
||||
// | 66| buf[6] >> 3
|
||||
// | 55555 | buf[5] << 2
|
||||
// |4 | buf[4] << 7
|
||||
dst[3] = buf[6] >> 3 | buf[5] << 2 | buf[4] << 7;
|
||||
n++;
|
||||
nextcase 5;
|
||||
case 5:
|
||||
// |33334444| dst[2]
|
||||
// | 4444| buf[4] >> 1
|
||||
// |3333 | buf[3] << 4
|
||||
dst[2] = buf[4] >> 1 | buf[3] << 4;
|
||||
n++;
|
||||
nextcase 4;
|
||||
case 4:
|
||||
// |11222223| dst[1]
|
||||
// | 3| buf[3] >> 4
|
||||
// | 22222 | buf[2] << 1
|
||||
// |11 | buf[1] << 6
|
||||
dst[1] = buf[3] >> 4 | buf[2] << 1 | buf[1] << 6;
|
||||
n++;
|
||||
nextcase 2;
|
||||
case 2:
|
||||
// |00000111| dst[0]
|
||||
// | 111| buf[1] >> 2
|
||||
// |00000 | buf[0] << 3
|
||||
dst[0] = buf[1] >> 2 | buf[0] << 3;
|
||||
n++;
|
||||
default:
|
||||
return DecodingFailure.INVALID_CHARACTER?;
|
||||
}
|
||||
if (dst.len < 5) break;
|
||||
dst = dst[5..];
|
||||
}
|
||||
return dst_ptr[:n];
|
||||
}
|
||||
|
||||
<*
|
||||
Encode the content of src into dst, which must be properly sized.
|
||||
@param [in] src "The input to be encoded."
|
||||
@param [inout] dst "The encoded input."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@param alphabet "The alphabet to use"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@require dst.len >= encode_len(src.len, padding) "Destination buffer too small"
|
||||
@return "The encoded size."
|
||||
*>
|
||||
fn String encode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
if (src.len == 0) return (String)dst[:0];
|
||||
|
||||
char* dst_ptr = dst;
|
||||
usz n = (src.len / 5) * 5;
|
||||
usz dn = encode_len(src.len, padding);
|
||||
|
||||
uint msb, lsb;
|
||||
for (usz i = 0; i < n; i += 5)
|
||||
{
|
||||
// to fit 40 bits we need two 32-bit uints
|
||||
msb = (uint)src[i] << 24 | (uint)src[i+1] << 16
|
||||
| (uint)src[i+2] << 8 | (uint)src[i+3];
|
||||
lsb = msb << 8 | (uint)src[i+4];
|
||||
|
||||
// now slice them into 5-bit chunks and translate to the
|
||||
// alphabet.
|
||||
dst[0] = alphabet.encoding[(msb >> 27) & MASK];
|
||||
dst[1] = alphabet.encoding[(msb >> 22) & MASK];
|
||||
dst[2] = alphabet.encoding[(msb >> 17) & MASK];
|
||||
dst[3] = alphabet.encoding[(msb >> 12) & MASK];
|
||||
dst[4] = alphabet.encoding[(msb >> 7) & MASK];
|
||||
dst[5] = alphabet.encoding[(msb >> 2) & MASK];
|
||||
dst[6] = alphabet.encoding[(lsb >> 5) & MASK];
|
||||
dst[7] = alphabet.encoding[lsb & MASK];
|
||||
|
||||
dst = dst[8..];
|
||||
}
|
||||
|
||||
usz trailing = src.len - n;
|
||||
if (trailing == 0) return (String)dst_ptr[:dn];
|
||||
|
||||
msb = 0;
|
||||
switch (trailing)
|
||||
{
|
||||
case 4:
|
||||
msb |= (uint)src[n+3];
|
||||
lsb = msb << 8;
|
||||
dst[6] = alphabet.encoding[(lsb >> 5) & MASK];
|
||||
dst[5] = alphabet.encoding[(msb >> 2) & MASK];
|
||||
nextcase 3;
|
||||
case 3:
|
||||
msb |= (uint)src[n+2] << 8;
|
||||
dst[4] = alphabet.encoding[(msb >> 7) & MASK];
|
||||
nextcase 2;
|
||||
case 2:
|
||||
msb |= (uint)src[n+1] << 16;
|
||||
dst[3] = alphabet.encoding[(msb >> 12) & MASK];
|
||||
dst[2] = alphabet.encoding[(msb >> 17) & MASK];
|
||||
nextcase 1;
|
||||
case 1:
|
||||
msb |= (uint)src[n] << 24;
|
||||
dst[1] = alphabet.encoding[(msb >> 22) & MASK];
|
||||
dst[0] = alphabet.encoding[(msb >> 27) & MASK];
|
||||
}
|
||||
|
||||
// add the padding
|
||||
if (padding > 0)
|
||||
{
|
||||
for (usz i = (trailing * 8 / 5) + 1; i < 8; i++)
|
||||
{
|
||||
dst[i] = padding;
|
||||
}
|
||||
}
|
||||
return (String)dst_ptr[:dn];
|
||||
}
|
||||
|
||||
const uint MASK @private = 0b11111;
|
||||
const char INVALID @private = 0xff;
|
||||
|
||||
const int STD_PADDING = '=';
|
||||
const int NO_PADDING = -1;
|
||||
|
||||
fault Base32Error
|
||||
{
|
||||
DUPLICATE_IN_ALPHABET,
|
||||
PADDING_IN_ALPHABET,
|
||||
INVALID_CHARACTER_IN_ALPHABET,
|
||||
DESTINATION_TOO_SMALL,
|
||||
INVALID_PADDING,
|
||||
CORRUPT_INPUT
|
||||
}
|
||||
|
||||
struct Base32Encoder @deprecated
|
||||
{
|
||||
Base32Alphabet alphabet;
|
||||
char padding;
|
||||
}
|
||||
|
||||
<*
|
||||
@param encoder "The 32-character alphabet for encoding."
|
||||
@param padding "Set to a negative value to disable padding."
|
||||
@require padding < 256
|
||||
*>
|
||||
fn void! Base32Encoder.init(&self, Alphabet encoder = STD_ALPHABET, int padding = STD_PADDING)
|
||||
{
|
||||
encoder.validate(padding)!;
|
||||
*self = { .alphabet = { .encoding = (char[32])encoder }, .padding = padding < 0 ? (char)0 : (char)padding};
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the length in bytes of the encoded data.
|
||||
@param n "Length in bytes on input."
|
||||
@return "Length in bytes of the encoded data."
|
||||
*>
|
||||
fn usz Base32Encoder.encode_len(&self, usz n)
|
||||
{
|
||||
return encode_len(n, self.padding);
|
||||
}
|
||||
|
||||
<*
|
||||
Encode the content of src into dst, which must be properly sized.
|
||||
@param [in] src "The input to be encoded."
|
||||
@param [inout] dst "The encoded input."
|
||||
@return "The encoded size."
|
||||
@return! Base32Error.DESTINATION_TOO_SMALL
|
||||
*>
|
||||
fn usz! Base32Encoder.encode(&self, char[] src, char[] dst)
|
||||
{
|
||||
usz dn = self.encode_len(src.len);
|
||||
if (dst.len < dn) return Base32Error.DESTINATION_TOO_SMALL?;
|
||||
return encode_buffer(src, dst, self.padding, &self.alphabet).len;
|
||||
}
|
||||
|
||||
struct Base32Decoder @deprecated
|
||||
{
|
||||
Base32Alphabet alphabet;
|
||||
char padding;
|
||||
}
|
||||
|
||||
<*
|
||||
@param decoder "The alphabet used for decoding."
|
||||
@param padding "Set to a negative value to disable padding."
|
||||
@require padding < 256
|
||||
*>
|
||||
fn void! Base32Decoder.init(&self, Alphabet decoder = STD_ALPHABET, int padding = STD_PADDING)
|
||||
{
|
||||
decoder.validate(padding)!;
|
||||
*self = { .alphabet = { .encoding = (char[32])decoder }, .padding = padding < 0 ? (char)0 : (char)padding };
|
||||
|
||||
self.alphabet.reverse[..] = INVALID;
|
||||
foreach (char i, c : decoder)
|
||||
{
|
||||
self.alphabet.reverse[c] = i;
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the length in bytes of the decoded data.
|
||||
@param n "Length in bytes of input."
|
||||
@return "Length in bytes of the decoded data."
|
||||
*>
|
||||
fn usz Base32Decoder.decode_len(&self, usz n)
|
||||
{
|
||||
return decode_len(n, self.padding);
|
||||
}
|
||||
|
||||
<*
|
||||
Decode the content of src into dst, which must be properly sized.
|
||||
@param src "The input to be decoded."
|
||||
@param dst "The decoded input."
|
||||
@return "The decoded size."
|
||||
@return! Base32Error.DESTINATION_TOO_SMALL, Base32Error.CORRUPT_INPUT
|
||||
*>
|
||||
fn usz! Base32Decoder.decode(&self, char[] src, char[] dst)
|
||||
{
|
||||
if (src.len == 0) return 0;
|
||||
usz dn = self.decode_len(src.len);
|
||||
if (dst.len < dn) return Base32Error.DESTINATION_TOO_SMALL?;
|
||||
return decode_buffer(src, dst, self.padding, &self.alphabet).len;
|
||||
}
|
||||
|
||||
|
||||
// Validate the 32-character alphabet to make sure that no character occurs
|
||||
// twice and that the padding is not present in the alphabet.
|
||||
fn void! Alphabet.validate(&self, int padding)
|
||||
{
|
||||
bool[256] checked;
|
||||
foreach (c : self)
|
||||
{
|
||||
if (checked[c])
|
||||
{
|
||||
return Base32Error.DUPLICATE_IN_ALPHABET?;
|
||||
}
|
||||
checked[c] = true;
|
||||
if (c == '\r' || c == '\n')
|
||||
{
|
||||
return Base32Error.INVALID_CHARACTER_IN_ALPHABET?;
|
||||
}
|
||||
}
|
||||
if (padding >= 0)
|
||||
{
|
||||
char pad = (char)padding;
|
||||
if (pad == '\r' || pad == '\n')
|
||||
{
|
||||
return Base32Error.INVALID_PADDING?;
|
||||
}
|
||||
if (checked[pad])
|
||||
{
|
||||
return Base32Error.PADDING_IN_ALPHABET?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
distinct Alphabet = char[32];
|
||||
// Standard base32 Alphabet
|
||||
const Alphabet STD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
// Extended Hex Alphabet
|
||||
const Alphabet HEX_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
|
||||
|
||||
const Base32Alphabet STANDARD = {
|
||||
.encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",
|
||||
.reverse = x`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffff1a1b1c1d1e1fffffffffffffffff
|
||||
ff000102030405060708090a0b0c0d0e0f10111213141516171819ffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`
|
||||
};
|
||||
|
||||
const Base32Alphabet HEX = {
|
||||
.encoding = "0123456789ABCDEFGHIJKLMNOPQRSTUV",
|
||||
.reverse = x`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff00010203040506070809ffffffffffff
|
||||
ff0a0b0c0d0e0f101112131415161718191a1b1c1d1e1fffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`
|
||||
};
|
||||
@@ -5,14 +5,260 @@ import std::core::bitorder;
|
||||
// Specifically this section:
|
||||
// https://www.rfc-editor.org/rfc/rfc4648#section-4
|
||||
|
||||
const char NO_PAD = 0;
|
||||
const char DEFAULT_PAD = '=';
|
||||
|
||||
struct Base64Alphabet
|
||||
{
|
||||
char[64] encoding;
|
||||
char[256] reverse;
|
||||
}
|
||||
|
||||
const Base64Alphabet STANDARD = {
|
||||
.encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
|
||||
.reverse =
|
||||
x`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffff3effffff3f3435363738393a3b3c3dffffffffffff
|
||||
ff000102030405060708090a0b0c0d0e0f10111213141516171819ffffffffff
|
||||
ff1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233ffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`
|
||||
};
|
||||
|
||||
const Base64Alphabet URL = {
|
||||
.encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
|
||||
.reverse =
|
||||
x`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffff3effff3435363738393a3b3c3dffffffffffff
|
||||
ff000102030405060708090a0b0c0d0e0f10111213141516171819ffffffff3f
|
||||
ff1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233ffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`
|
||||
};
|
||||
|
||||
const STD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
const URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||
|
||||
fn String encode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
char[] dst = allocator::alloc_array(allocator, char, encode_len(src.len, padding));
|
||||
return encode_buffer(src, dst, padding, alphabet);
|
||||
}
|
||||
|
||||
fn char[]! decode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
char[] dst = allocator::alloc_array(allocator, char, decode_len(src.len, padding))!;
|
||||
return decode_buffer(src, dst, padding, alphabet);
|
||||
}
|
||||
|
||||
fn String encode_new(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::heap(), padding, alphabet);
|
||||
fn String encode_temp(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::temp(), padding, alphabet);
|
||||
fn char[]! decode_new(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::heap(), padding, alphabet);
|
||||
fn char[]! decode_temp(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::temp(), padding, alphabet);
|
||||
|
||||
|
||||
<*
|
||||
Calculate the size of the encoded data.
|
||||
@param n "Size of the input to be encoded."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "The size of the input once encoded."
|
||||
*>
|
||||
fn usz encode_len(usz n, char padding)
|
||||
{
|
||||
if (padding) return (n + 2) / 3 * 4;
|
||||
usz trailing = n % 3;
|
||||
return n / 3 * 4 + (trailing * 4 + 2) / 3;
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the size of the decoded data.
|
||||
@param n "Size of the input to be decoded."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "The size of the input once decoded."
|
||||
@return! DecodingFailure.INVALID_PADDING
|
||||
*>
|
||||
fn usz! decode_len(usz n, char padding)
|
||||
{
|
||||
usz dn = n / 4 * 3;
|
||||
usz trailing = n % 4;
|
||||
if (padding)
|
||||
{
|
||||
if (trailing != 0) return DecodingFailure.INVALID_PADDING?;
|
||||
// source size is multiple of 4
|
||||
return dn;
|
||||
}
|
||||
if (trailing == 1) return DecodingFailure.INVALID_PADDING?;
|
||||
return dn + trailing * 3 / 4;
|
||||
}
|
||||
|
||||
<*
|
||||
Encode the content of src into dst, which must be properly sized.
|
||||
@param src "The input to be encoded."
|
||||
@param dst "The encoded input."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@param alphabet "The alphabet to use"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "The encoded size."
|
||||
@return! Base64Error.DESTINATION_TOO_SMALL
|
||||
*>
|
||||
fn String encode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
if (src.len == 0) return (String)dst[:0];
|
||||
usz dn = encode_len(src.len, padding);
|
||||
char* dst_ptr = dst;
|
||||
assert(dst.len >= dn);
|
||||
usz trailing = src.len % 3;
|
||||
char[] src3 = src[:^trailing];
|
||||
|
||||
while (src3.len > 0)
|
||||
{
|
||||
uint group = (uint)src3[0] << 16 | (uint)src3[1] << 8 | (uint)src3[2];
|
||||
dst[0] = alphabet.encoding[group >> 18 & MASK];
|
||||
dst[1] = alphabet.encoding[group >> 12 & MASK];
|
||||
dst[2] = alphabet.encoding[group >> 6 & MASK];
|
||||
dst[3] = alphabet.encoding[group & MASK];
|
||||
dst = dst[4..];
|
||||
src3 = src3[3..];
|
||||
}
|
||||
|
||||
// Encode the remaining bytes according to:
|
||||
// https://www.rfc-editor.org/rfc/rfc4648#section-3.5
|
||||
switch (trailing)
|
||||
{
|
||||
case 1:
|
||||
uint group = (uint)src[^1] << 16;
|
||||
dst[0] = alphabet.encoding[group >> 18 & MASK];
|
||||
dst[1] = alphabet.encoding[group >> 12 & MASK];
|
||||
if (padding > 0)
|
||||
{
|
||||
dst[2] = padding;
|
||||
dst[3] = padding;
|
||||
}
|
||||
case 2:
|
||||
uint group = (uint)src[^2] << 16 | (uint)src[^1] << 8;
|
||||
dst[0] = alphabet.encoding[group >> 18 & MASK];
|
||||
dst[1] = alphabet.encoding[group >> 12 & MASK];
|
||||
dst[2] = alphabet.encoding[group >> 6 & MASK];
|
||||
if (padding > 0)
|
||||
{
|
||||
dst[3] = padding;
|
||||
}
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
return (String)dst_ptr[:dn];
|
||||
}
|
||||
|
||||
<*
|
||||
Decode the content of src into dst, which must be properly sized.
|
||||
@param src "The input to be decoded."
|
||||
@param dst "The decoded input."
|
||||
@param padding "The padding character or 0 if none"
|
||||
@param alphabet "The alphabet to use"
|
||||
@require (decode_len(src.len, padding) ?? 0) <= dst.len "Destination buffer too small"
|
||||
@require padding < 0xFF "Invalid padding character"
|
||||
@return "The decoded data."
|
||||
@return! DecodingFailure
|
||||
*>
|
||||
fn char[]! decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
|
||||
{
|
||||
if (src.len == 0) return dst[:0];
|
||||
usz dn = decode_len(src.len, padding)!;
|
||||
assert(dst.len >= dn);
|
||||
|
||||
usz trailing = src.len % 4;
|
||||
char* dst_ptr = dst;
|
||||
char[] src4 = src;
|
||||
switch
|
||||
{
|
||||
case !padding:
|
||||
src4 = src[:^trailing];
|
||||
default:
|
||||
// If there is padding, keep the last 4 bytes for later.
|
||||
// NB. src.len >= 4 as decode_len passed
|
||||
trailing = 4;
|
||||
if (src[^1] == padding) src4 = src[:^4];
|
||||
}
|
||||
while (src4.len > 0)
|
||||
{
|
||||
char c0 = alphabet.reverse[src4[0]];
|
||||
char c1 = alphabet.reverse[src4[1]];
|
||||
char c2 = alphabet.reverse[src4[2]];
|
||||
char c3 = alphabet.reverse[src4[3]];
|
||||
switch (0xFF)
|
||||
{
|
||||
case c0:
|
||||
case c1:
|
||||
case c2:
|
||||
case c3:
|
||||
return DecodingFailure.INVALID_CHARACTER?;
|
||||
}
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6 | (uint)c3;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dst[1] = (char)(group >> 8);
|
||||
dst[2] = (char)group;
|
||||
dst = dst[3..];
|
||||
src4 = src4[4..];
|
||||
}
|
||||
|
||||
if (trailing == 0) return dst_ptr[:dn];
|
||||
|
||||
src = src[^trailing..];
|
||||
char c0 = alphabet.reverse[src[0]];
|
||||
char c1 = alphabet.reverse[src[1]];
|
||||
if (c0 == 0xFF || c1 == 0xFF) return DecodingFailure.INVALID_PADDING?;
|
||||
if (!padding)
|
||||
{
|
||||
switch (src.len)
|
||||
{
|
||||
case 2:
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12;
|
||||
dst[0] = (char)(group >> 16);
|
||||
case 3:
|
||||
char c2 = alphabet.reverse[src[2]];
|
||||
if (c2 == 0xFF) return DecodingFailure.INVALID_CHARACTER?;
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dst[1] = (char)(group >> 8);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Valid paddings are:
|
||||
// 2: xx==
|
||||
// 1: xxx=
|
||||
switch (padding)
|
||||
{
|
||||
case src[2]:
|
||||
if (src[3] != padding) return DecodingFailure.INVALID_PADDING?;
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dn -= 2;
|
||||
case src[3]:
|
||||
char c2 = alphabet.reverse[src[2]];
|
||||
if (c2 == 0xFF) return DecodingFailure.INVALID_CHARACTER?;
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dst[1] = (char)(group >> 8);
|
||||
dn -= 1;
|
||||
}
|
||||
}
|
||||
return dst_ptr[:dn];
|
||||
}
|
||||
|
||||
const MASK @private = 0b111111;
|
||||
|
||||
struct Base64Encoder
|
||||
struct Base64Encoder @deprecated
|
||||
{
|
||||
int padding;
|
||||
char padding;
|
||||
String alphabet;
|
||||
}
|
||||
|
||||
@@ -25,243 +271,109 @@ fault Base64Error
|
||||
INVALID_CHARACTER,
|
||||
}
|
||||
|
||||
/**
|
||||
* @param alphabet "The alphabet used for encoding."
|
||||
* @param padding "Set to a negative value to disable padding."
|
||||
* @require alphabet.len == 64
|
||||
* @require padding < 256
|
||||
* @return! Base64Error.DUPLICATE_IN_ALPHABET, Base64Error.PADDING_IN_ALPHABET
|
||||
**/
|
||||
fn void! Base64Encoder.init(&self, String alphabet, int padding = '=')
|
||||
<*
|
||||
@param alphabet "The alphabet used for encoding."
|
||||
@param padding "Set to a negative value to disable padding."
|
||||
@require alphabet.len == 64
|
||||
@require padding < 256
|
||||
@return! Base64Error.DUPLICATE_IN_ALPHABET, Base64Error.PADDING_IN_ALPHABET
|
||||
*>
|
||||
fn Base64Encoder*! Base64Encoder.init(&self, String alphabet, int padding = '=')
|
||||
{
|
||||
check_alphabet(alphabet, padding)!;
|
||||
*self = { .padding = padding, .alphabet = alphabet };
|
||||
*self = { .padding = padding < 0 ? 0 : (char)padding, .alphabet = alphabet };
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the size of the encoded data.
|
||||
* @param n "Size of the input to be encoded."
|
||||
* @return "The size of the input once encoded."
|
||||
**/
|
||||
<*
|
||||
Calculate the size of the encoded data.
|
||||
@param n "Size of the input to be encoded."
|
||||
@return "The size of the input once encoded."
|
||||
*>
|
||||
fn usz Base64Encoder.encode_len(&self, usz n)
|
||||
{
|
||||
if (self.padding >= 0) return (n + 2) / 3 * 4;
|
||||
usz trailing = n % 3;
|
||||
return n / 3 * 4 + (trailing * 4 + 2) / 3;
|
||||
return encode_len(n, self.padding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the content of src into dst, which must be properly sized.
|
||||
* @param src "The input to be encoded."
|
||||
* @param dst "The encoded input."
|
||||
* @return "The encoded size."
|
||||
* @return! Base64Error.DESTINATION_TOO_SMALL
|
||||
**/
|
||||
<*
|
||||
Encode the content of src into dst, which must be properly sized.
|
||||
@param src "The input to be encoded."
|
||||
@param dst "The encoded input."
|
||||
@return "The encoded size."
|
||||
@return! Base64Error.DESTINATION_TOO_SMALL
|
||||
*>
|
||||
fn usz! Base64Encoder.encode(&self, char[] src, char[] dst)
|
||||
{
|
||||
if (src.len == 0) return 0;
|
||||
usz dn = self.encode_len(src.len);
|
||||
if (dst.len < dn) return Base64Error.DESTINATION_TOO_SMALL?;
|
||||
usz trailing = src.len % 3;
|
||||
char[] src3 = src[:^trailing];
|
||||
|
||||
while (src3.len > 0)
|
||||
{
|
||||
uint group = (uint)src3[0] << 16 | (uint)src3[1] << 8 | (uint)src3[2];
|
||||
dst[0] = self.alphabet[group >> 18 & MASK];
|
||||
dst[1] = self.alphabet[group >> 12 & MASK];
|
||||
dst[2] = self.alphabet[group >> 6 & MASK];
|
||||
dst[3] = self.alphabet[group & MASK];
|
||||
dst = dst[4..];
|
||||
src3 = src3[3..];
|
||||
}
|
||||
|
||||
// Encode the remaining bytes according to:
|
||||
// https://www.rfc-editor.org/rfc/rfc4648#section-3.5
|
||||
switch (trailing)
|
||||
{
|
||||
case 1:
|
||||
uint group = (uint)src[^1] << 16;
|
||||
dst[0] = self.alphabet[group >> 18 & MASK];
|
||||
dst[1] = self.alphabet[group >> 12 & MASK];
|
||||
if (self.padding >= 0)
|
||||
{
|
||||
char pad = (char)self.padding;
|
||||
dst[2] = pad;
|
||||
dst[3] = pad;
|
||||
}
|
||||
case 2:
|
||||
uint group = (uint)src[^2] << 16 | (uint)src[^1] << 8;
|
||||
dst[0] = self.alphabet[group >> 18 & MASK];
|
||||
dst[1] = self.alphabet[group >> 12 & MASK];
|
||||
dst[2] = self.alphabet[group >> 6 & MASK];
|
||||
if (self.padding >= 0)
|
||||
{
|
||||
char pad = (char)self.padding;
|
||||
dst[3] = pad;
|
||||
}
|
||||
}
|
||||
return dn;
|
||||
Base64Alphabet a = { .encoding = self.alphabet[:64] };
|
||||
return encode_buffer(src, dst, self.padding, &a).len;
|
||||
}
|
||||
|
||||
struct Base64Decoder
|
||||
struct Base64Decoder @deprecated
|
||||
{
|
||||
int padding;
|
||||
String alphabet;
|
||||
char[256] reverse;
|
||||
char invalid;
|
||||
char padding;
|
||||
Base64Alphabet encoding;
|
||||
bool init_done;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param alphabet "The alphabet used for encoding."
|
||||
* @param padding "Set to a negative value to disable padding."
|
||||
* @require alphabet.len == 64
|
||||
* @require padding < 256
|
||||
* @return! Base64Error.DUPLICATE_IN_ALPHABET, Base64Error.PADDING_IN_ALPHABET
|
||||
**/
|
||||
import std;
|
||||
<*
|
||||
@param alphabet "The alphabet used for encoding."
|
||||
@param padding "Set to a negative value to disable padding."
|
||||
@require alphabet.len == 64
|
||||
@require padding < 256
|
||||
@return! Base64Error.DUPLICATE_IN_ALPHABET, Base64Error.PADDING_IN_ALPHABET
|
||||
*>
|
||||
fn void! Base64Decoder.init(&self, String alphabet, int padding = '=')
|
||||
{
|
||||
self.init_done = true;
|
||||
check_alphabet(alphabet, padding)!;
|
||||
*self = { .padding = padding, .alphabet = alphabet };
|
||||
*self = { .padding = padding < 0 ? 0 : (char)padding, .encoding.encoding = alphabet[:64] };
|
||||
|
||||
self.encoding.reverse[..] = 0xFF;
|
||||
|
||||
bool[256] checked;
|
||||
foreach (i, c : alphabet)
|
||||
{
|
||||
checked[c] = true;
|
||||
self.reverse[c] = (char)i;
|
||||
}
|
||||
if (padding < 0)
|
||||
{
|
||||
self.invalid = 255;
|
||||
return;
|
||||
}
|
||||
// Find a character for invalid neither in the alphabet nor equal to the padding.
|
||||
char pad = (char)padding;
|
||||
foreach (i, ok : checked)
|
||||
{
|
||||
if (!ok && (char)i != pad)
|
||||
{
|
||||
self.invalid = (char)i;
|
||||
break;
|
||||
}
|
||||
self.encoding.reverse[c] = (char)i;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the size of the decoded data.
|
||||
* @param n "Size of the input to be decoded."
|
||||
* @return "The size of the input once decoded."
|
||||
* @return! Base64Error.INVALID_PADDING
|
||||
**/
|
||||
<*
|
||||
Calculate the size of the decoded data.
|
||||
@param n "Size of the input to be decoded."
|
||||
@return "The size of the input once decoded."
|
||||
@return! Base64Error.INVALID_PADDING
|
||||
*>
|
||||
fn usz! Base64Decoder.decode_len(&self, usz n)
|
||||
{
|
||||
usz dn = n / 4 * 3;
|
||||
usz trailing = n % 4;
|
||||
if (self.padding >= 0)
|
||||
{
|
||||
if (trailing != 0) return Base64Error.INVALID_PADDING?;
|
||||
// source size is multiple of 4
|
||||
}
|
||||
else
|
||||
{
|
||||
if (trailing == 1) return Base64Error.INVALID_PADDING?;
|
||||
dn += trailing * 3 / 4;
|
||||
}
|
||||
return dn;
|
||||
return decode_len(n, self.padding) ?? Base64Error.INVALID_PADDING?;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the content of src into dst, which must be properly sized.
|
||||
* @param src "The input to be decoded."
|
||||
* @param dst "The decoded input."
|
||||
* @return "The decoded size."
|
||||
* @return! Base64Error.DESTINATION_TOO_SMALL, Base64Error.INVALID_PADDING, Base64Error.INVALID_CHARACTER
|
||||
**/
|
||||
<*
|
||||
Decode the content of src into dst, which must be properly sized.
|
||||
@param src "The input to be decoded."
|
||||
@param dst "The decoded input."
|
||||
@return "The decoded size."
|
||||
@return! Base64Error.DESTINATION_TOO_SMALL, Base64Error.INVALID_PADDING, Base64Error.INVALID_CHARACTER
|
||||
*>
|
||||
fn usz! Base64Decoder.decode(&self, char[] src, char[] dst)
|
||||
{
|
||||
if (src.len == 0) return 0;
|
||||
usz dn = self.decode_len(src.len)!;
|
||||
if (dst.len < dn) return Base64Error.DESTINATION_TOO_SMALL?;
|
||||
|
||||
usz trailing = src.len % 4;
|
||||
char[] src4 = src;
|
||||
switch
|
||||
char[]! decoded = decode_buffer(src, dst, self.padding, &self.encoding);
|
||||
if (catch err = decoded)
|
||||
{
|
||||
case self.padding < 0:
|
||||
src4 = src[:^trailing];
|
||||
case DecodingFailure.INVALID_PADDING:
|
||||
return Base64Error.INVALID_PADDING?;
|
||||
case DecodingFailure.INVALID_CHARACTER:
|
||||
return Base64Error.INVALID_CHARACTER?;
|
||||
default:
|
||||
// If there is padding, keep the last 4 bytes for later.
|
||||
// NB. src.len >= 4 as decode_len passed
|
||||
trailing = 4;
|
||||
char pad = (char)self.padding;
|
||||
if (src[^1] == pad) src4 = src[:^4];
|
||||
return err?;
|
||||
}
|
||||
while (src4.len > 0)
|
||||
{
|
||||
char c0 = self.reverse[src4[0]];
|
||||
char c1 = self.reverse[src4[1]];
|
||||
char c2 = self.reverse[src4[2]];
|
||||
char c3 = self.reverse[src4[3]];
|
||||
switch (self.invalid)
|
||||
{
|
||||
case c0:
|
||||
case c1:
|
||||
case c2:
|
||||
case c3:
|
||||
return Base64Error.INVALID_CHARACTER?;
|
||||
}
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6 | (uint)c3;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dst[1] = (char)(group >> 8);
|
||||
dst[2] = (char)group;
|
||||
dst = dst[3..];
|
||||
src4 = src4[4..];
|
||||
}
|
||||
|
||||
if (trailing == 0) return dn;
|
||||
|
||||
src = src[^trailing..];
|
||||
char c0 = self.reverse[src[0]];
|
||||
char c1 = self.reverse[src[1]];
|
||||
if (c0 == self.invalid || c1 == self.invalid) return Base64Error.INVALID_PADDING?;
|
||||
if (self.padding < 0)
|
||||
{
|
||||
switch (src.len)
|
||||
{
|
||||
case 2:
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12;
|
||||
dst[0] = (char)(group >> 16);
|
||||
case 3:
|
||||
char c2 = self.reverse[src[2]];
|
||||
if (c2 == self.invalid) return Base64Error.INVALID_CHARACTER?;
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dst[1] = (char)(group >> 8);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Valid paddings are:
|
||||
// 2: xx==
|
||||
// 1: xxx=
|
||||
char pad = (char)self.padding;
|
||||
switch (pad)
|
||||
{
|
||||
case src[2]:
|
||||
if (src[3] != pad) return Base64Error.INVALID_PADDING?;
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dn -= 2;
|
||||
case src[3]:
|
||||
char c2 = self.reverse[src[2]];
|
||||
if (c2 == self.invalid) return Base64Error.INVALID_CHARACTER?;
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dst[1] = (char)(group >> 8);
|
||||
dn -= 1;
|
||||
}
|
||||
}
|
||||
return dn;
|
||||
return decoded.len;
|
||||
}
|
||||
|
||||
// Make sure that all bytes in the alphabet are unique and
|
||||
@@ -285,4 +397,5 @@ fn void! check_alphabet(String alphabet, int padding) @local
|
||||
if (checked[c]) return Base64Error.DUPLICATE_IN_ALPHABET?;
|
||||
checked[c] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,34 +1,73 @@
|
||||
module std::encoding::csv;
|
||||
import std::io;
|
||||
|
||||
|
||||
struct CsvReader
|
||||
{
|
||||
InStream stream;
|
||||
String separator;
|
||||
}
|
||||
|
||||
struct CsvRow (Printable)
|
||||
{
|
||||
String[] list;
|
||||
String row;
|
||||
Allocator allocator;
|
||||
}
|
||||
|
||||
fn usz! CsvRow.to_format(&self, Formatter* f) @dynamic
|
||||
{
|
||||
return f.printf("%s", self.list);
|
||||
}
|
||||
|
||||
fn usz CsvRow.len(&self) @operator(len)
|
||||
{
|
||||
return self.list.len;
|
||||
}
|
||||
|
||||
<*
|
||||
@require col < self.list.len
|
||||
*>
|
||||
fn String CsvRow.get_col(&self, usz col) @operator([])
|
||||
{
|
||||
return self.list[col];
|
||||
}
|
||||
|
||||
fn void CsvReader.init(&self, InStream stream, String separator = ",")
|
||||
{
|
||||
self.stream = stream;
|
||||
self.separator = separator;
|
||||
}
|
||||
|
||||
fn String[]! CsvReader.read_new_row(self, Allocator allocator = allocator::heap())
|
||||
fn CsvRow! CsvReader.read_new_row(self)
|
||||
{
|
||||
return self.read_new_row_with_allocator(allocator::temp()) @inline;
|
||||
return self.read_row(allocator::heap()) @inline;
|
||||
}
|
||||
|
||||
fn String[]! CsvReader.read_new_row_with_allocator(self, Allocator allocator = allocator::heap())
|
||||
<*
|
||||
@param [&inout] allocator
|
||||
*>
|
||||
fn CsvRow! CsvReader.read_row(self, Allocator allocator)
|
||||
{
|
||||
@pool(allocator)
|
||||
{
|
||||
return io::treadline(self.stream).split(self.separator, .allocator = allocator);
|
||||
};
|
||||
String row = io::readline(self.stream, allocator: allocator)!;
|
||||
defer catch allocator::free(allocator, row);
|
||||
String[] list = row.split(self.separator, allocator: allocator);
|
||||
return { list, row, allocator };
|
||||
}
|
||||
|
||||
fn String[]! CsvReader.read_temp_row(self)
|
||||
fn CsvRow! CsvReader.read_temp_row(self)
|
||||
{
|
||||
return self.read_new_row_with_allocator(allocator::temp()) @inline;
|
||||
return self.read_row(allocator::temp()) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.allocator `Row already freed`
|
||||
*>
|
||||
fn void CsvRow.free(&self)
|
||||
{
|
||||
allocator::free(self.allocator, self.list);
|
||||
allocator::free(self.allocator, self.row);
|
||||
self.allocator = null;
|
||||
}
|
||||
|
||||
fn void! CsvReader.skip_row(self) @maydiscard
|
||||
@@ -39,26 +78,21 @@ fn void! CsvReader.skip_row(self) @maydiscard
|
||||
};
|
||||
}
|
||||
|
||||
macro CsvReader.@each_row(self, int rows = int.max; @body(String[] row))
|
||||
macro void! CsvReader.@each_row(self, int rows = int.max; @body(String[] row)) @maydiscard
|
||||
{
|
||||
InputStream* stream = self.stream;
|
||||
InStream stream = self.stream;
|
||||
String sep = self.separator;
|
||||
while (rows--)
|
||||
{
|
||||
@stack_mem(512; Allocator mem)
|
||||
{
|
||||
String[] parts;
|
||||
@pool()
|
||||
String! s = io::readline(stream, mem);
|
||||
if (catch err = s)
|
||||
{
|
||||
String! s = stream.treadline();
|
||||
if (catch err = s)
|
||||
{
|
||||
if (err == IoError.EOF) return;
|
||||
return err?;
|
||||
}
|
||||
parts = s.split(sep, .allocator = mem);
|
||||
};
|
||||
@body(parts);
|
||||
if (err == IoError.EOF) return;
|
||||
return err?;
|
||||
}
|
||||
@body(s.split(sep, allocator: mem));
|
||||
};
|
||||
}
|
||||
}
|
||||
7
lib/std/encoding/encoding.c3
Normal file
7
lib/std/encoding/encoding.c3
Normal file
@@ -0,0 +1,7 @@
|
||||
module std::encoding;
|
||||
|
||||
fault DecodingFailure
|
||||
{
|
||||
INVALID_CHARACTER,
|
||||
INVALID_PADDING,
|
||||
}
|
||||
109
lib/std/encoding/hex.c3
Normal file
109
lib/std/encoding/hex.c3
Normal file
@@ -0,0 +1,109 @@
|
||||
module std::encoding::hex;
|
||||
import std::encoding @norecurse;
|
||||
|
||||
// The implementation is based on https://www.rfc-editor.org/rfc/rfc4648
|
||||
|
||||
fn String encode_buffer(char[] code, char[] buffer)
|
||||
{
|
||||
return (String)buffer[:encode_bytes(code, buffer)];
|
||||
}
|
||||
|
||||
fn char[]! decode_buffer(char[] code, char[] buffer)
|
||||
{
|
||||
return buffer[:decode_bytes(code, buffer)!];
|
||||
}
|
||||
|
||||
fn String encode(char[] code, Allocator allocator)
|
||||
{
|
||||
char[] data = allocator::alloc_array(allocator, char, encode_len(code.len));
|
||||
return (String)data[:encode_bytes(code, data)];
|
||||
}
|
||||
|
||||
fn char[]! decode(char[] code, Allocator allocator)
|
||||
{
|
||||
char[] data = allocator::alloc_array(allocator, char, decode_len(code.len));
|
||||
return data[:decode_bytes(code, data)!];
|
||||
}
|
||||
|
||||
fn String encode_new(char[] code) @inline => encode(code, allocator::heap());
|
||||
fn String encode_temp(char[] code) @inline => encode(code, allocator::temp());
|
||||
fn char[]! decode_new(char[] code) @inline => decode(code, allocator::heap());
|
||||
fn char[]! decode_temp(char[] code) @inline => decode(code, allocator::temp());
|
||||
|
||||
<*
|
||||
Calculate the size of the encoded data.
|
||||
@param n "Size of the input to be encoded."
|
||||
@return "The size of the input once encoded."
|
||||
*>
|
||||
fn usz encode_len(usz n) => n * 2;
|
||||
|
||||
<*
|
||||
Encode the content of src into dst, which must be properly sized.
|
||||
@param src "The input to be encoded."
|
||||
@param dst "The encoded input."
|
||||
@return "The encoded size."
|
||||
@require dst.len >= encode_len(src.len) "Destination array is not large enough"
|
||||
*>
|
||||
fn usz encode_bytes(char[] src, char[] dst)
|
||||
{
|
||||
usz j = 0;
|
||||
foreach (v : src)
|
||||
{
|
||||
dst[j] = HEXALPHABET[v >> 4];
|
||||
dst[j + 1] = HEXALPHABET[v & 0x0f];
|
||||
j = j + 2;
|
||||
}
|
||||
return src.len * 2;
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the size of the decoded data.
|
||||
@param n "Size of the input to be decoded."
|
||||
@return "The size of the input once decoded."
|
||||
*>
|
||||
macro usz decode_len(usz n) => n / 2;
|
||||
|
||||
<*
|
||||
Decodes src into bytes. Returns the actual number of bytes written to dst.
|
||||
|
||||
Expects that src only contains hexadecimal characters and that src has even
|
||||
length.
|
||||
|
||||
@param src "The input to be decoded."
|
||||
@param dst "The decoded input."
|
||||
@require src.len % 2 == 0 "src is not of even length"
|
||||
@require dst.len >= decode_len(src.len) "Destination array is not large enough"
|
||||
@return! DecodingFailure.INVALID_CHARACTER
|
||||
*>
|
||||
fn usz! decode_bytes(char[] src, char[] dst)
|
||||
{
|
||||
usz i;
|
||||
for (usz j = 1; j < src.len; j += 2)
|
||||
{
|
||||
char a = HEXREVERSE[src[j - 1]];
|
||||
char b = HEXREVERSE[src[j]];
|
||||
if (a > 0x0f || b > 0x0f) return DecodingFailure.INVALID_CHARACTER?;
|
||||
dst[i] = (a << 4) | b;
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
const char[*] HEXALPHABET @private = "0123456789abcdef";
|
||||
const char[*] HEXREVERSE @private =
|
||||
x`ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
00010203040506070809ffffffffffff
|
||||
ff0a0b0c0d0e0fffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ff0a0b0c0d0e0fffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffff`;
|
||||
@@ -27,11 +27,13 @@ fn Object*! temp_parse_string(String s)
|
||||
|
||||
fn Object*! parse(InStream s, Allocator allocator = allocator::heap())
|
||||
{
|
||||
JsonContext context = { .last_string = dstring::new_with_capacity(64, allocator), .stream = s, .allocator = allocator };
|
||||
defer context.last_string.free();
|
||||
@pool(allocator)
|
||||
@stack_mem(512; Allocator mem)
|
||||
{
|
||||
return parse_any(&context);
|
||||
JsonContext context = { .last_string = dstring::new_with_capacity(64, mem), .stream = s, .allocator = allocator };
|
||||
@pool(allocator)
|
||||
{
|
||||
return parse_any(&context);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -102,9 +104,9 @@ fn Object*! parse_any(JsonContext* context) @local
|
||||
|
||||
fn JsonTokenType! lex_number(JsonContext *context, char c) @local
|
||||
{
|
||||
@pool()
|
||||
@stack_mem(256; Allocator mem)
|
||||
{
|
||||
DString t = dstring::temp_with_capacity(32);
|
||||
DString t = dstring::new_with_capacity(32, allocator: mem);
|
||||
bool negate = c == '-';
|
||||
if (negate)
|
||||
{
|
||||
@@ -152,32 +154,34 @@ fn JsonTokenType! lex_number(JsonContext *context, char c) @local
|
||||
fn Object*! parse_map(JsonContext* context) @local
|
||||
{
|
||||
Object* map = object::new_obj(context.allocator);
|
||||
JsonTokenType token = advance(context)!;
|
||||
defer catch map.free();
|
||||
JsonTokenType token = advance(context)!;
|
||||
|
||||
DString temp_key = dstring::new_with_capacity(32, context.allocator);
|
||||
defer temp_key.free();
|
||||
while (token != JsonTokenType.RBRACE)
|
||||
@stack_mem(256; Allocator mem)
|
||||
{
|
||||
if (token != JsonTokenType.STRING) return JsonParsingError.UNEXPECTED_CHARACTER?;
|
||||
DString string = context.last_string;
|
||||
if (map.has_key(string.str_view())) return JsonParsingError.DUPLICATE_MEMBERS?;
|
||||
// Copy the key to our temp holder. We do this to work around the issue
|
||||
// if the temp allocator should be used as the default allocator.
|
||||
temp_key.clear();
|
||||
temp_key.append(string);
|
||||
parse_expected(context, COLON)!;
|
||||
Object* element = parse_any(context)!;
|
||||
map.set(temp_key.str_view(), element);
|
||||
token = advance(context)!;
|
||||
if (token == JsonTokenType.COMMA)
|
||||
DString temp_key = dstring::new_with_capacity(32, mem);
|
||||
while (token != JsonTokenType.RBRACE)
|
||||
{
|
||||
if (token != JsonTokenType.STRING) return JsonParsingError.UNEXPECTED_CHARACTER?;
|
||||
DString string = context.last_string;
|
||||
if (map.has_key(string.str_view())) return JsonParsingError.DUPLICATE_MEMBERS?;
|
||||
// Copy the key to our temp holder, since our
|
||||
// last_string may be used in parse_any
|
||||
temp_key.clear();
|
||||
temp_key.append(string);
|
||||
parse_expected(context, COLON)!;
|
||||
Object* element = parse_any(context)!;
|
||||
map.set(temp_key.str_view(), element);
|
||||
token = advance(context)!;
|
||||
continue;
|
||||
if (token == JsonTokenType.COMMA)
|
||||
{
|
||||
token = advance(context)!;
|
||||
continue;
|
||||
}
|
||||
if (token != JsonTokenType.RBRACE) return JsonParsingError.UNEXPECTED_CHARACTER?;
|
||||
}
|
||||
if (token != JsonTokenType.RBRACE) return JsonParsingError.UNEXPECTED_CHARACTER?;
|
||||
}
|
||||
return map;
|
||||
return map;
|
||||
};
|
||||
}
|
||||
|
||||
fn Object*! parse_array(JsonContext* context) @local
|
||||
@@ -382,6 +386,7 @@ fn JsonTokenType! lex_string(JsonContext* context)
|
||||
default:
|
||||
return JsonParsingError.INVALID_ESCAPE_SEQUENCE?;
|
||||
}
|
||||
context.last_string.append(c);
|
||||
}
|
||||
return STRING;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ distinct Fnv32a = uint;
|
||||
const FNV32A_START @private = 0x811c9dc5;
|
||||
const FNV32A_MUL @private = 0x01000193;
|
||||
|
||||
macro void @update(uint* &h, char x) @private => *h = (*h * FNV32A_MUL) ^ x;
|
||||
macro void update(h, char x) @private => *h = (*h ^ ($typeof(*h))x) * FNV32A_MUL;
|
||||
|
||||
fn void Fnv32a.init(&self)
|
||||
{
|
||||
@@ -17,17 +17,17 @@ fn void Fnv32a.init(&self)
|
||||
|
||||
fn void Fnv32a.update(&self, char[] data)
|
||||
{
|
||||
uint h = (uint)*self;
|
||||
Fnv32a h = *self;
|
||||
foreach (char x : data)
|
||||
{
|
||||
@update(h, x);
|
||||
update(&h, x);
|
||||
}
|
||||
*self = (Fnv32a)h;
|
||||
*self = h;
|
||||
}
|
||||
|
||||
macro void Fnv32a.update_char(&self, char c)
|
||||
{
|
||||
@update(*self, x);
|
||||
update(self, c);
|
||||
}
|
||||
|
||||
fn uint encode(char[] data)
|
||||
@@ -35,7 +35,7 @@ fn uint encode(char[] data)
|
||||
uint h = FNV32A_START;
|
||||
foreach (char x : data)
|
||||
{
|
||||
@update(h, x);
|
||||
update(&h, x);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ distinct Fnv64a = ulong;
|
||||
const FNV64A_START @private = 0xcbf29ce484222325;
|
||||
const FNV64A_MUL @private = 0x00000100000001b3;
|
||||
|
||||
macro void @update(ulong* &h, char x) @private => *h = (*h * FNV64A_MUL) ^ x;
|
||||
macro void update(h, char x) @private => *h = (*h ^ ($typeof(*h))x) * FNV64A_MUL;
|
||||
|
||||
fn void Fnv64a.init(&self)
|
||||
{
|
||||
@@ -17,17 +17,17 @@ fn void Fnv64a.init(&self)
|
||||
|
||||
fn void Fnv64a.update(&self, char[] data)
|
||||
{
|
||||
ulong h = (ulong)*self;
|
||||
Fnv64a h = *self;
|
||||
foreach (char x : data)
|
||||
{
|
||||
@update(h, x);
|
||||
update(&h, x);
|
||||
}
|
||||
*self = (Fnv64a)h;
|
||||
*self = h;
|
||||
}
|
||||
|
||||
macro void Fnv64a.update_char(&self, char c)
|
||||
{
|
||||
@update(*self, x);
|
||||
update(self, c);
|
||||
}
|
||||
|
||||
fn ulong encode(char[] data)
|
||||
@@ -35,7 +35,7 @@ fn ulong encode(char[] data)
|
||||
ulong h = FNV64A_START;
|
||||
foreach (char x : data)
|
||||
{
|
||||
@update(h, x);
|
||||
update(&h, x);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
107
lib/std/hash/hmac.c3
Normal file
107
lib/std/hash/hmac.c3
Normal file
@@ -0,0 +1,107 @@
|
||||
module std::hash::hmac(<HashAlg, HASH_BYTES, BLOCK_BYTES>);
|
||||
import std::crypto;
|
||||
|
||||
struct Hmac
|
||||
{
|
||||
HashAlg a, b;
|
||||
}
|
||||
|
||||
fn char[HASH_BYTES] hash(char[] key, char[] message)
|
||||
{
|
||||
Hmac hmac @noinit;
|
||||
hmac.init(key);
|
||||
hmac.update(message);
|
||||
return hmac.final();
|
||||
}
|
||||
|
||||
<*
|
||||
@require output.len > 0 "Output must be greater than zero"
|
||||
@require output.len < int.max / HASH_BYTES "Output is too large"
|
||||
*>
|
||||
fn void pbkdf2(char[] pw, char[] salt, uint iterations, char[] output)
|
||||
{
|
||||
usz l = output.len / HASH_BYTES;
|
||||
usz r = output.len % HASH_BYTES;
|
||||
|
||||
Hmac hmac;
|
||||
hmac.init(pw);
|
||||
|
||||
char[] dst_curr = output;
|
||||
for (usz i = 1; i <= l; i++)
|
||||
{
|
||||
@derive(&hmac, salt, iterations, i, dst_curr[:HASH_BYTES]);
|
||||
dst_curr = dst_curr[HASH_BYTES..];
|
||||
}
|
||||
|
||||
if (r > 0)
|
||||
{
|
||||
char[HASH_BYTES] tmp;
|
||||
@derive(&hmac, salt, iterations, l + 1, &tmp);
|
||||
dst_curr[..] = tmp[:dst_curr.len];
|
||||
mem::zero_volatile(&tmp);
|
||||
}
|
||||
}
|
||||
|
||||
fn void Hmac.init(&self, char[] key)
|
||||
{
|
||||
char[BLOCK_BYTES] buffer;
|
||||
if (key.len > BLOCK_BYTES)
|
||||
{
|
||||
self.a.init();
|
||||
self.a.update(key);
|
||||
buffer[:HASH_BYTES] = self.a.final()[..];
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[:key.len] = key[..];
|
||||
}
|
||||
|
||||
foreach (&b : buffer) *b ^= IPAD;
|
||||
|
||||
self.a.init();
|
||||
self.a.update(&buffer);
|
||||
|
||||
foreach (&b : buffer) *b ^= IPAD ^ OPAD;
|
||||
|
||||
self.b.init();
|
||||
self.b.update(&buffer);
|
||||
|
||||
mem::zero_volatile(&buffer);
|
||||
}
|
||||
|
||||
fn void Hmac.update(&self, char[] data)
|
||||
{
|
||||
self.a.update(data);
|
||||
}
|
||||
|
||||
fn char[HASH_BYTES] Hmac.final(&self)
|
||||
{
|
||||
self.b.update(&&self.a.final());
|
||||
return self.b.final();
|
||||
}
|
||||
|
||||
const IPAD @private = 0x36;
|
||||
const OPAD @private = 0x5C;
|
||||
|
||||
macro @derive(Hmac *hmac_start, char[] salt, uint iterations, usz index, char[] out)
|
||||
{
|
||||
assert(out.len == HASH_BYTES);
|
||||
char[HASH_BYTES] tmp @noinit;
|
||||
defer mem::zero_volatile(&tmp);
|
||||
Hmac hmac = *hmac_start;
|
||||
hmac.update(salt);
|
||||
UIntBE be = { (uint)index };
|
||||
hmac.update(&&bitcast(be, char[4]));
|
||||
tmp = hmac.final();
|
||||
out[..] = tmp;
|
||||
for (int it = 1; it < iterations; it++)
|
||||
{
|
||||
hmac = *hmac_start;
|
||||
hmac.update(&tmp);
|
||||
tmp = hmac.final();
|
||||
foreach (i, v : tmp)
|
||||
{
|
||||
out[i] ^= v;
|
||||
}
|
||||
}
|
||||
}
|
||||
225
lib/std/hash/md5.c3
Normal file
225
lib/std/hash/md5.c3
Normal file
@@ -0,0 +1,225 @@
|
||||
module std::hash::md5;
|
||||
import std::hash::hmac;
|
||||
import std::bits;
|
||||
|
||||
const BLOCK_BYTES = 64;
|
||||
const HASH_BYTES = 16;
|
||||
|
||||
struct Md5
|
||||
{
|
||||
uint lo, hi;
|
||||
uint a, b, c, d;
|
||||
char[64] buffer;
|
||||
uint[16] block;
|
||||
}
|
||||
|
||||
def HmacMd5 = Hmac(<Md5, HASH_BYTES, BLOCK_BYTES>);
|
||||
def hmac = hmac::hash(<Md5, HASH_BYTES, BLOCK_BYTES>);
|
||||
def pbkdf2 = hmac::pbkdf2(<Md5, HASH_BYTES, BLOCK_BYTES>);
|
||||
|
||||
fn char[HASH_BYTES] hash(char[] data)
|
||||
{
|
||||
Md5 md5;
|
||||
md5.init();
|
||||
md5.update(data);
|
||||
return md5.final();
|
||||
}
|
||||
|
||||
fn void Md5.init(&self)
|
||||
{
|
||||
self.a = 0x67452301;
|
||||
self.b = 0xefcdab89;
|
||||
self.c = 0x98badcfe;
|
||||
self.d = 0x10325476;
|
||||
|
||||
self.lo = 0;
|
||||
self.hi = 0;
|
||||
}
|
||||
|
||||
|
||||
fn void Md5.update(&ctx, char[] data)
|
||||
{
|
||||
uint saved_lo = ctx.lo;
|
||||
if ((ctx.lo = (saved_lo + data.len) & 0x1fffffff) < saved_lo) ctx.hi++;
|
||||
ctx.hi += data.len >> 29;
|
||||
|
||||
usz used = (usz)saved_lo & 0x3f;
|
||||
|
||||
if (used)
|
||||
{
|
||||
usz available = 64 - used;
|
||||
|
||||
if (data.len < available)
|
||||
{
|
||||
ctx.buffer[used:data.len] = data[..];
|
||||
return;
|
||||
}
|
||||
ctx.buffer[used:available] = data[:available];
|
||||
data = data[available..];
|
||||
body(ctx, &ctx.buffer, 64);
|
||||
}
|
||||
|
||||
if (data.len >= 64)
|
||||
{
|
||||
data = body(ctx, data, data.len & ~(usz)0x3f)[:data.len & 0x3f];
|
||||
}
|
||||
ctx.buffer[:data.len] = data[..];
|
||||
}
|
||||
|
||||
fn char[HASH_BYTES] Md5.final(&ctx)
|
||||
{
|
||||
usz used = (usz)ctx.lo & 0x3f;
|
||||
ctx.buffer[used++] = 0x80;
|
||||
|
||||
usz available = 64 - used;
|
||||
|
||||
if (available < 8)
|
||||
{
|
||||
ctx.buffer[used:available] = 0;
|
||||
body(ctx, &ctx.buffer, 64);
|
||||
used = 0;
|
||||
available = 64;
|
||||
}
|
||||
ctx.buffer[used:available - 8] = 0;
|
||||
|
||||
ctx.lo <<= 3;
|
||||
ctx.buffer[56:4] = bitcast(ctx.lo, char[4])[..];
|
||||
ctx.buffer[60:4] = bitcast(ctx.hi, char[4])[..];
|
||||
|
||||
body(ctx, &ctx.buffer, 64);
|
||||
|
||||
char[16] res @noinit;
|
||||
res[0:4] = bitcast(ctx.a, char[4]);
|
||||
res[4:4] = bitcast(ctx.b, char[4]);
|
||||
res[8:4] = bitcast(ctx.c, char[4]);
|
||||
res[12:4] = bitcast(ctx.d, char[4]);
|
||||
*ctx = {};
|
||||
return res;
|
||||
}
|
||||
|
||||
module std::hash::md5 @private;
|
||||
|
||||
// Implementation
|
||||
macro @f(x, y, z) => z ^ (x & (y ^ z));
|
||||
macro @g(x, y, z) => y ^ (z & (x ^ y));
|
||||
macro @h(x, y, z) => (x ^ y) ^ z;
|
||||
macro @h2(x, y, z) => x ^ (y ^ z);
|
||||
macro @i(x, y, z) => y ^ (x | ~z);
|
||||
|
||||
macro @step(#f, a, b, c, d, ptr, n, t, s)
|
||||
{
|
||||
*a += #f(b, c, d) + *(uint *)&ptr[n * 4] + t;
|
||||
*a = (*a << s) | ((*a & 0xffffffff) >> (32 - s));
|
||||
*a += b;
|
||||
}
|
||||
|
||||
|
||||
fn char* body(Md5* ctx, void* data, usz size)
|
||||
{
|
||||
char* ptr;
|
||||
uint a, b, c, d;
|
||||
uint saved_a, saved_b, saved_c, saved_d;
|
||||
ptr = data;
|
||||
a = ctx.a;
|
||||
b = ctx.b;
|
||||
c = ctx.c;
|
||||
d = ctx.d;
|
||||
|
||||
do
|
||||
{
|
||||
saved_a = a;
|
||||
saved_b = b;
|
||||
saved_c = c;
|
||||
saved_d = d;
|
||||
|
||||
/* Round 1 */
|
||||
@step(@f, &a, b, c, d, ptr, 0, 0xd76aa478, 7) ;
|
||||
@step(@f, &d, a, b, c, ptr, 1, 0xe8c7b756, 12) ;
|
||||
@step(@f, &c, d, a, b, ptr, 2, 0x242070db, 17) ;
|
||||
@step(@f, &b, c, d, a, ptr, 3, 0xc1bdceee, 22) ;
|
||||
@step(@f, &a, b, c, d, ptr, 4, 0xf57c0faf, 7) ;
|
||||
@step(@f, &d, a, b, c, ptr, 5, 0x4787c62a, 12) ;
|
||||
@step(@f, &c, d, a, b, ptr, 6, 0xa8304613, 17) ;
|
||||
@step(@f, &b, c, d, a, ptr, 7, 0xfd469501, 22) ;
|
||||
@step(@f, &a, b, c, d, ptr, 8, 0x698098d8, 7) ;
|
||||
@step(@f, &d, a, b, c, ptr, 9, 0x8b44f7af, 12) ;
|
||||
@step(@f, &c, d, a, b, ptr, 10, 0xffff5bb1, 17);
|
||||
@step(@f, &b, c, d, a, ptr, 11, 0x895cd7be, 22);
|
||||
@step(@f, &a, b, c, d, ptr, 12, 0x6b901122, 7) ;
|
||||
@step(@f, &d, a, b, c, ptr, 13, 0xfd987193, 12);
|
||||
@step(@f, &c, d, a, b, ptr, 14, 0xa679438e, 17);
|
||||
@step(@f, &b, c, d, a, ptr, 15, 0x49b40821, 22);
|
||||
|
||||
/* Round 2 */
|
||||
@step(@g, &a, b, c, d, ptr, 1, 0xf61e2562, 5) ;
|
||||
@step(@g, &d, a, b, c, ptr, 6, 0xc040b340, 9) ;
|
||||
@step(@g, &c, d, a, b, ptr, 11, 0x265e5a51, 14);
|
||||
@step(@g, &b, c, d, a, ptr, 0, 0xe9b6c7aa, 20) ;
|
||||
@step(@g, &a, b, c, d, ptr, 5, 0xd62f105d, 5) ;
|
||||
@step(@g, &d, a, b, c, ptr, 10, 0x02441453, 9) ;
|
||||
@step(@g, &c, d, a, b, ptr, 15, 0xd8a1e681, 14);
|
||||
@step(@g, &b, c, d, a, ptr, 4, 0xe7d3fbc8, 20) ;
|
||||
@step(@g, &a, b, c, d, ptr, 9, 0x21e1cde6, 5) ;
|
||||
@step(@g, &d, a, b, c, ptr, 14, 0xc33707d6, 9) ;
|
||||
@step(@g, &c, d, a, b, ptr, 3, 0xf4d50d87, 14) ;
|
||||
@step(@g, &b, c, d, a, ptr, 8, 0x455a14ed, 20) ;
|
||||
@step(@g, &a, b, c, d, ptr, 13, 0xa9e3e905, 5) ;
|
||||
@step(@g, &d, a, b, c, ptr, 2, 0xfcefa3f8, 9) ;
|
||||
@step(@g, &c, d, a, b, ptr, 7, 0x676f02d9, 14) ;
|
||||
@step(@g, &b, c, d, a, ptr, 12, 0x8d2a4c8a, 20);
|
||||
|
||||
/* Round 3 */
|
||||
@step(@h, &a, b, c, d, ptr, 5, 0xfffa3942, 4);
|
||||
@step(@h2, &d, a, b, c, ptr, 8, 0x8771f681, 11);
|
||||
@step(@h, &c, d, a, b, ptr, 11, 0x6d9d6122, 16);
|
||||
@step(@h2, &b, c, d, a, ptr, 14, 0xfde5380c, 23);
|
||||
@step(@h, &a, b, c, d, ptr, 1, 0xa4beea44, 4);
|
||||
@step(@h2, &d, a, b, c, ptr, 4, 0x4bdecfa9, 11);
|
||||
@step(@h, &c, d, a, b, ptr, 7, 0xf6bb4b60, 16);
|
||||
@step(@h2, &b, c, d, a, ptr, 10, 0xbebfbc70, 23);
|
||||
@step(@h, &a, b, c, d, ptr, 13, 0x289b7ec6, 4) ;
|
||||
@step(@h2, &d, a, b, c, ptr, 0, 0xeaa127fa, 11) ;
|
||||
@step(@h, &c, d, a, b, ptr, 3, 0xd4ef3085, 16) ;
|
||||
@step(@h2, &b, c, d, a, ptr, 6, 0x04881d05, 23) ;
|
||||
@step(@h, &a, b, c, d, ptr, 9, 0xd9d4d039, 4) ;
|
||||
@step(@h2, &d, a, b, c, ptr, 12, 0xe6db99e5, 11) ;
|
||||
@step(@h, &c, d, a, b, ptr, 15, 0x1fa27cf8, 16) ;
|
||||
@step(@h2, &b, c, d, a, ptr, 2, 0xc4ac5665, 23) ;
|
||||
|
||||
/* Round 4 */
|
||||
@step(@i, &a, b, c, d, ptr, 0, 0xf4292244, 6) ;
|
||||
@step(@i, &d, a, b, c, ptr, 7, 0x432aff97, 10) ;
|
||||
@step(@i, &c, d, a, b, ptr, 14, 0xab9423a7, 15) ;
|
||||
@step(@i, &b, c, d, a, ptr, 5, 0xfc93a039, 21) ;
|
||||
@step(@i, &a, b, c, d, ptr, 12, 0x655b59c3, 6) ;
|
||||
@step(@i, &d, a, b, c, ptr, 3, 0x8f0ccc92, 10) ;
|
||||
@step(@i, &c, d, a, b, ptr, 10, 0xffeff47d, 15) ;
|
||||
@step(@i, &b, c, d, a, ptr, 1, 0x85845dd1, 21) ;
|
||||
@step(@i, &a, b, c, d, ptr, 8, 0x6fa87e4f, 6) ;
|
||||
@step(@i, &d, a, b, c, ptr, 15, 0xfe2ce6e0, 10) ;
|
||||
@step(@i, &c, d, a, b, ptr, 6, 0xa3014314, 15) ;
|
||||
@step(@i, &b, c, d, a, ptr, 13, 0x4e0811a1, 21) ;
|
||||
@step(@i, &a, b, c, d, ptr, 4, 0xf7537e82, 6) ;
|
||||
@step(@i, &d, a, b, c, ptr, 11, 0xbd3af235, 10) ;
|
||||
@step(@i, &c, d, a, b, ptr, 2, 0x2ad7d2bb, 15) ;
|
||||
@step(@i, &b, c, d, a, ptr, 9, 0xeb86d391, 21) ;
|
||||
|
||||
a += saved_a;
|
||||
b += saved_b;
|
||||
c += saved_c;
|
||||
d += saved_d;
|
||||
|
||||
ptr += 64;
|
||||
|
||||
} while (size -= 64);
|
||||
|
||||
ctx.a = a;
|
||||
ctx.b = b;
|
||||
ctx.c = c;
|
||||
ctx.d = d;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,13 +5,29 @@
|
||||
// Implementation was off Steve Reid's SHA-1 C implementation
|
||||
|
||||
module std::hash::sha1;
|
||||
import std::hash::hmac;
|
||||
import std::bits;
|
||||
|
||||
const BLOCK_BYTES = 64;
|
||||
const HASH_BYTES = 20;
|
||||
|
||||
struct Sha1
|
||||
{
|
||||
uint[5] state;
|
||||
uint[2] count;
|
||||
char[64] buffer;
|
||||
char[BLOCK_BYTES] buffer;
|
||||
}
|
||||
|
||||
def HmacSha1 = Hmac(<Sha1, HASH_BYTES, BLOCK_BYTES>);
|
||||
def hmac = hmac::hash(<Sha1, HASH_BYTES, BLOCK_BYTES>);
|
||||
def pbkdf2 = hmac::pbkdf2(<Sha1, HASH_BYTES, BLOCK_BYTES>);
|
||||
|
||||
fn char[HASH_BYTES] hash(char[] data)
|
||||
{
|
||||
Sha1 sha1 @noinit;
|
||||
sha1.init();
|
||||
sha1.update(data);
|
||||
return sha1.final();
|
||||
}
|
||||
|
||||
fn void Sha1.init(&self)
|
||||
@@ -28,10 +44,10 @@ fn void Sha1.init(&self)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [in] data
|
||||
* @require data.len <= uint.max
|
||||
**/
|
||||
<*
|
||||
@param [in] data
|
||||
@require data.len <= uint.max
|
||||
*>
|
||||
fn void Sha1.update(&self, char[] data)
|
||||
{
|
||||
uint j = self.count[0];
|
||||
@@ -55,7 +71,7 @@ fn void Sha1.update(&self, char[] data)
|
||||
}
|
||||
|
||||
|
||||
fn char[20] Sha1.final(&self)
|
||||
fn char[HASH_BYTES] Sha1.final(&self)
|
||||
{
|
||||
char[8] finalcount;
|
||||
for (uint i = 0; i < 8; i++)
|
||||
@@ -69,31 +85,31 @@ fn char[20] Sha1.final(&self)
|
||||
}
|
||||
|
||||
self.update(&finalcount);
|
||||
char[20] digest;
|
||||
for (uint i = 0; i < 20; i++)
|
||||
char[HASH_BYTES] digest;
|
||||
for (uint i = 0; i < HASH_BYTES; i++)
|
||||
{
|
||||
digest[i] = (char)((self.state[i >> 2] >> ((3 - (i & 3)) * 8)) & 0xFF);
|
||||
}
|
||||
|
||||
// Clear mem
|
||||
mem::clear(self, Sha1.sizeof);
|
||||
*self = {};
|
||||
finalcount = {};
|
||||
return digest;
|
||||
}
|
||||
|
||||
union Long16 @local
|
||||
{
|
||||
char[64] c;
|
||||
char[BLOCK_BYTES] c;
|
||||
uint[16] l;
|
||||
}
|
||||
|
||||
macro @blk(&block, i) @local
|
||||
macro blk(block, i) @local
|
||||
{
|
||||
return (block.l[i & 15] = (block.l[(i + 13) & 15] ^ block.l[(i + 8) & 15]
|
||||
^ block.l[(i + 2) & 15] ^ block.l[i & 15]).rotl(1));
|
||||
}
|
||||
|
||||
macro @blk0(&block, i) @local
|
||||
macro blk0(block, i) @local
|
||||
{
|
||||
$if env::BIG_ENDIAN:
|
||||
return block.l[i];
|
||||
@@ -103,45 +119,45 @@ macro @blk0(&block, i) @local
|
||||
$endif
|
||||
}
|
||||
|
||||
macro @r0(&block, v, &wref, x, y, &z, i) @local
|
||||
macro r0(block, v, wref, x, y, z, i) @local
|
||||
{
|
||||
var w = *wref;
|
||||
*z += ((w & (x ^ y)) ^ y) + @blk0(*block, i) + 0x5A827999 + v.rotl(5);
|
||||
*z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + v.rotl(5);
|
||||
*wref = w.rotl(30);
|
||||
}
|
||||
|
||||
macro @r1(&block, v, &wref, x, y, &z, i) @local
|
||||
macro r1(block, v, wref, x, y, z, i) @local
|
||||
{
|
||||
var w = *wref;
|
||||
*z += ((w & (x ^ y)) ^ y) + @blk(*block, i) + 0x5A827999 + v.rotl(5);
|
||||
*z += ((w & (x ^ y)) ^ y) + blk(block, i) + 0x5A827999 + v.rotl(5);
|
||||
*wref = w.rotl(30);
|
||||
}
|
||||
|
||||
macro @r2(&block, v, &wref, x, y, &z, i) @local
|
||||
macro r2(block, v, wref, x, y, z, i) @local
|
||||
{
|
||||
var w = *wref;
|
||||
*z += (w ^ x ^ y) + @blk(*block, i) + 0x6ED9EBA1 + v.rotl(5);
|
||||
*z += (w ^ x ^ y) + blk(block, i) + 0x6ED9EBA1 + v.rotl(5);
|
||||
*wref = w.rotl(30);
|
||||
}
|
||||
|
||||
macro @r3(&block, v, &wref, x, y, &z, i) @local
|
||||
macro r3(block, v, wref, x, y, z, i) @local
|
||||
{
|
||||
var w = *wref;
|
||||
*z += (((w | x) & y) | (w & x)) + @blk(*block, i) + 0x8F1BBCDC + v.rotl(5);
|
||||
*z += (((w | x) & y) | (w & x)) + blk(block, i) + 0x8F1BBCDC + v.rotl(5);
|
||||
*wref = w.rotl(30);
|
||||
}
|
||||
|
||||
macro @r4(&block, v, &wref, x, y, &z, i) @local
|
||||
macro r4(block, v, wref, x, y, z, i) @local
|
||||
{
|
||||
var w = *wref;
|
||||
*z += (w ^ x ^ y) + @blk(*block, i) + 0xCA62C1D6 + v.rotl(5);
|
||||
*z += (w ^ x ^ y) + blk(block, i) + 0xCA62C1D6 + v.rotl(5);
|
||||
*wref = w.rotl(30);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] state
|
||||
* @param [&in] buffer
|
||||
**/
|
||||
<*
|
||||
@param [&inout] state
|
||||
@param [&in] buffer
|
||||
*>
|
||||
fn void sha1_transform(uint* state, char* buffer) @local
|
||||
{
|
||||
Long16 block;
|
||||
@@ -151,86 +167,86 @@ fn void sha1_transform(uint* state, char* buffer) @local
|
||||
uint c = state[2];
|
||||
uint d = state[3];
|
||||
uint e = state[4];
|
||||
@r0(block, a, b, c, d, e, 0);
|
||||
@r0(block, e, a, b, c, d, 1);
|
||||
@r0(block, d, e, a, b, c, 2);
|
||||
@r0(block, c, d, e, a, b, 3);
|
||||
@r0(block, b, c, d, e, a, 4);
|
||||
@r0(block, a, b, c, d, e, 5);
|
||||
@r0(block, e, a, b, c, d, 6);
|
||||
@r0(block, d, e, a, b, c, 7);
|
||||
@r0(block, c, d, e, a, b, 8);
|
||||
@r0(block, b, c, d, e, a, 9);
|
||||
@r0(block, a, b, c, d, e, 10);
|
||||
@r0(block, e, a, b, c, d, 11);
|
||||
@r0(block, d, e, a, b, c, 12);
|
||||
@r0(block, c, d, e, a, b, 13);
|
||||
@r0(block, b, c, d, e, a, 14);
|
||||
@r0(block, a, b, c, d, e, 15);
|
||||
@r1(block, e, a, b, c, d, 16);
|
||||
@r1(block, d, e, a, b, c, 17);
|
||||
@r1(block, c, d, e, a, b, 18);
|
||||
@r1(block, b, c, d, e, a, 19);
|
||||
@r2(block, a, b, c, d, e, 20);
|
||||
@r2(block, e, a, b, c, d, 21);
|
||||
@r2(block, d, e, a, b, c, 22);
|
||||
@r2(block, c, d, e, a, b, 23);
|
||||
@r2(block, b, c, d, e, a, 24);
|
||||
@r2(block, a, b, c, d, e, 25);
|
||||
@r2(block, e, a, b, c, d, 26);
|
||||
@r2(block, d, e, a, b, c, 27);
|
||||
@r2(block, c, d, e, a, b, 28);
|
||||
@r2(block, b, c, d, e, a, 29);
|
||||
@r2(block, a, b, c, d, e, 30);
|
||||
@r2(block, e, a, b, c, d, 31);
|
||||
@r2(block, d, e, a, b, c, 32);
|
||||
@r2(block, c, d, e, a, b, 33);
|
||||
@r2(block, b, c, d, e, a, 34);
|
||||
@r2(block, a, b, c, d, e, 35);
|
||||
@r2(block, e, a, b, c, d, 36);
|
||||
@r2(block, d, e, a, b, c, 37);
|
||||
@r2(block, c, d, e, a, b, 38);
|
||||
@r2(block, b, c, d, e, a, 39);
|
||||
@r3(block, a, b, c, d, e, 40);
|
||||
@r3(block, e, a, b, c, d, 41);
|
||||
@r3(block, d, e, a, b, c, 42);
|
||||
@r3(block, c, d, e, a, b, 43);
|
||||
@r3(block, b, c, d, e, a, 44);
|
||||
@r3(block, a, b, c, d, e, 45);
|
||||
@r3(block, e, a, b, c, d, 46);
|
||||
@r3(block, d, e, a, b, c, 47);
|
||||
@r3(block, c, d, e, a, b, 48);
|
||||
@r3(block, b, c, d, e, a, 49);
|
||||
@r3(block, a, b, c, d, e, 50);
|
||||
@r3(block, e, a, b, c, d, 51);
|
||||
@r3(block, d, e, a, b, c, 52);
|
||||
@r3(block, c, d, e, a, b, 53);
|
||||
@r3(block, b, c, d, e, a, 54);
|
||||
@r3(block, a, b, c, d, e, 55);
|
||||
@r3(block, e, a, b, c, d, 56);
|
||||
@r3(block, d, e, a, b, c, 57);
|
||||
@r3(block, c, d, e, a, b, 58);
|
||||
@r3(block, b, c, d, e, a, 59);
|
||||
@r4(block, a, b, c, d, e, 60);
|
||||
@r4(block, e, a, b, c, d, 61);
|
||||
@r4(block, d, e, a, b, c, 62);
|
||||
@r4(block, c, d, e, a, b, 63);
|
||||
@r4(block, b, c, d, e, a, 64);
|
||||
@r4(block, a, b, c, d, e, 65);
|
||||
@r4(block, e, a, b, c, d, 66);
|
||||
@r4(block, d, e, a, b, c, 67);
|
||||
@r4(block, c, d, e, a, b, 68);
|
||||
@r4(block, b, c, d, e, a, 69);
|
||||
@r4(block, a, b, c, d, e, 70);
|
||||
@r4(block, e, a, b, c, d, 71);
|
||||
@r4(block, d, e, a, b, c, 72);
|
||||
@r4(block, c, d, e, a, b, 73);
|
||||
@r4(block, b, c, d, e, a, 74);
|
||||
@r4(block, a, b, c, d, e, 75);
|
||||
@r4(block, e, a, b, c, d, 76);
|
||||
@r4(block, d, e, a, b, c, 77);
|
||||
@r4(block, c, d, e, a, b, 78);
|
||||
@r4(block, b, c, d, e, a, 79);
|
||||
r0(&block, a, &b, c, d, &e, 0);
|
||||
r0(&block, e, &a, b, c, &d, 1);
|
||||
r0(&block, d, &e, a, b, &c, 2);
|
||||
r0(&block, c, &d, e, a, &b, 3);
|
||||
r0(&block, b, &c, d, e, &a, 4);
|
||||
r0(&block, a, &b, c, d, &e, 5);
|
||||
r0(&block, e, &a, b, c, &d, 6);
|
||||
r0(&block, d, &e, a, b, &c, 7);
|
||||
r0(&block, c, &d, e, a, &b, 8);
|
||||
r0(&block, b, &c, d, e, &a, 9);
|
||||
r0(&block, a, &b, c, d, &e, 10);
|
||||
r0(&block, e, &a, b, c, &d, 11);
|
||||
r0(&block, d, &e, a, b, &c, 12);
|
||||
r0(&block, c, &d, e, a, &b, 13);
|
||||
r0(&block, b, &c, d, e, &a, 14);
|
||||
r0(&block, a, &b, c, d, &e, 15);
|
||||
r1(&block, e, &a, b, c, &d, 16);
|
||||
r1(&block, d, &e, a, b, &c, 17);
|
||||
r1(&block, c, &d, e, a, &b, 18);
|
||||
r1(&block, b, &c, d, e, &a, 19);
|
||||
r2(&block, a, &b, c, d, &e, 20);
|
||||
r2(&block, e, &a, b, c, &d, 21);
|
||||
r2(&block, d, &e, a, b, &c, 22);
|
||||
r2(&block, c, &d, e, a, &b, 23);
|
||||
r2(&block, b, &c, d, e, &a, 24);
|
||||
r2(&block, a, &b, c, d, &e, 25);
|
||||
r2(&block, e, &a, b, c, &d, 26);
|
||||
r2(&block, d, &e, a, b, &c, 27);
|
||||
r2(&block, c, &d, e, a, &b, 28);
|
||||
r2(&block, b, &c, d, e, &a, 29);
|
||||
r2(&block, a, &b, c, d, &e, 30);
|
||||
r2(&block, e, &a, b, c, &d, 31);
|
||||
r2(&block, d, &e, a, b, &c, 32);
|
||||
r2(&block, c, &d, e, a, &b, 33);
|
||||
r2(&block, b, &c, d, e, &a, 34);
|
||||
r2(&block, a, &b, c, d, &e, 35);
|
||||
r2(&block, e, &a, b, c, &d, 36);
|
||||
r2(&block, d, &e, a, b, &c, 37);
|
||||
r2(&block, c, &d, e, a, &b, 38);
|
||||
r2(&block, b, &c, d, e, &a, 39);
|
||||
r3(&block, a, &b, c, d, &e, 40);
|
||||
r3(&block, e, &a, b, c, &d, 41);
|
||||
r3(&block, d, &e, a, b, &c, 42);
|
||||
r3(&block, c, &d, e, a, &b, 43);
|
||||
r3(&block, b, &c, d, e, &a, 44);
|
||||
r3(&block, a, &b, c, d, &e, 45);
|
||||
r3(&block, e, &a, b, c, &d, 46);
|
||||
r3(&block, d, &e, a, b, &c, 47);
|
||||
r3(&block, c, &d, e, a, &b, 48);
|
||||
r3(&block, b, &c, d, e, &a, 49);
|
||||
r3(&block, a, &b, c, d, &e, 50);
|
||||
r3(&block, e, &a, b, c, &d, 51);
|
||||
r3(&block, d, &e, a, b, &c, 52);
|
||||
r3(&block, c, &d, e, a, &b, 53);
|
||||
r3(&block, b, &c, d, e, &a, 54);
|
||||
r3(&block, a, &b, c, d, &e, 55);
|
||||
r3(&block, e, &a, b, c, &d, 56);
|
||||
r3(&block, d, &e, a, b, &c, 57);
|
||||
r3(&block, c, &d, e, a, &b, 58);
|
||||
r3(&block, b, &c, d, e, &a, 59);
|
||||
r4(&block, a, &b, c, d, &e, 60);
|
||||
r4(&block, e, &a, b, c, &d, 61);
|
||||
r4(&block, d, &e, a, b, &c, 62);
|
||||
r4(&block, c, &d, e, a, &b, 63);
|
||||
r4(&block, b, &c, d, e, &a, 64);
|
||||
r4(&block, a, &b, c, d, &e, 65);
|
||||
r4(&block, e, &a, b, c, &d, 66);
|
||||
r4(&block, d, &e, a, b, &c, 67);
|
||||
r4(&block, c, &d, e, a, &b, 68);
|
||||
r4(&block, b, &c, d, e, &a, 69);
|
||||
r4(&block, a, &b, c, d, &e, 70);
|
||||
r4(&block, e, &a, b, c, &d, 71);
|
||||
r4(&block, d, &e, a, b, &c, 72);
|
||||
r4(&block, c, &d, e, a, &b, 73);
|
||||
r4(&block, b, &c, d, e, &a, 74);
|
||||
r4(&block, a, &b, c, d, &e, 75);
|
||||
r4(&block, e, &a, b, c, &d, 76);
|
||||
r4(&block, d, &e, a, b, &c, 77);
|
||||
r4(&block, c, &d, e, a, &b, 78);
|
||||
r4(&block, b, &c, d, e, &a, 79);
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
|
||||
176
lib/std/hash/sha256.c3
Normal file
176
lib/std/hash/sha256.c3
Normal file
@@ -0,0 +1,176 @@
|
||||
module std::hash::sha256;
|
||||
|
||||
import std::hash::hmac;
|
||||
|
||||
const BLOCK_SIZE = 64;
|
||||
const HASH_SIZE = 32;
|
||||
|
||||
const uint[64] K @local = {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
||||
};
|
||||
|
||||
// Right rotate function
|
||||
macro uint @rotr(uint x, uint n) @local => (((x) >> (n)) | ((x) << (32 - (n))));
|
||||
|
||||
// SHA-256 functions
|
||||
macro uint @ch(uint x, uint y, uint z) @local => (x & y) ^ (~x & z);
|
||||
macro uint @maj(uint x, uint y, uint z) @local => (x & y) ^ (x & z) ^ (y & z);
|
||||
macro uint @_sigma0(uint x) @local => @rotr(x, 2) ^ @rotr(x, 13) ^ @rotr(x, 22);
|
||||
macro uint @_sigma1(uint x) @local => @rotr(x, 6) ^ @rotr(x, 11) ^ @rotr(x, 25);
|
||||
macro uint @sigma0(uint x) @local => @rotr(x, 7) ^ @rotr(x, 18) ^ (x >> 3);
|
||||
macro uint @sigma1(uint x) @local => @rotr(x, 17) ^ @rotr(x, 19) ^ (x >> 10);
|
||||
|
||||
struct Sha256
|
||||
{
|
||||
uint[8] state;
|
||||
ulong bitcount;
|
||||
char[BLOCK_SIZE] buffer;
|
||||
}
|
||||
|
||||
def HmacSha256 = Hmac(<Sha256, HASH_SIZE, BLOCK_SIZE>);
|
||||
def hmac = hmac::hash(<Sha256, HASH_SIZE, BLOCK_SIZE>);
|
||||
def pbkdf2 = hmac::pbkdf2(<Sha256, HASH_SIZE, BLOCK_SIZE>);
|
||||
|
||||
fn char[HASH_SIZE] hash(char[] data)
|
||||
{
|
||||
Sha256 sha256 @noinit;
|
||||
sha256.init();
|
||||
sha256.update(data);
|
||||
return sha256.final();
|
||||
}
|
||||
|
||||
fn void Sha256.init(&self)
|
||||
{
|
||||
// Sha256 initialization constants
|
||||
*self = {
|
||||
.state = {
|
||||
0x6A09E667,
|
||||
0xBB67AE85,
|
||||
0x3C6EF372,
|
||||
0xA54FF53A,
|
||||
0x510E527F,
|
||||
0x9B05688C,
|
||||
0x1F83D9AB,
|
||||
0x5BE0CD19
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] data
|
||||
@require data.len <= uint.max
|
||||
*>
|
||||
fn void Sha256.update(&self, char[] data) {
|
||||
uint i = 0;
|
||||
uint len = data.len;
|
||||
uint buffer_pos = (uint)(self.bitcount / 8) % BLOCK_SIZE;
|
||||
self.bitcount += (ulong)(len * 8);
|
||||
|
||||
while (len--) {
|
||||
self.buffer[buffer_pos++] = data[i++];
|
||||
if (buffer_pos == BLOCK_SIZE) {
|
||||
sha256_transform(&self.state, &self.buffer);
|
||||
buffer_pos = 0; // Reset buffer position
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn char[HASH_SIZE] Sha256.final(&self) {
|
||||
char[HASH_SIZE] hash;
|
||||
ulong i = (self.bitcount / 8) % BLOCK_SIZE;
|
||||
|
||||
// Append 0x80 to the buffer
|
||||
self.buffer[i++] = 0x80;
|
||||
|
||||
// Pad the buffer with zeros
|
||||
if (i > BLOCK_SIZE - 8) {
|
||||
while (i < BLOCK_SIZE) {
|
||||
self.buffer[i++] = 0x00;
|
||||
}
|
||||
sha256_transform(&self.state, &self.buffer);
|
||||
i = 0; // Reset buffer index after transformation
|
||||
}
|
||||
|
||||
while (i < BLOCK_SIZE - 8) {
|
||||
self.buffer[i++] = 0x00;
|
||||
}
|
||||
|
||||
// Append the bitcount in big-endian format
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
self.buffer[BLOCK_SIZE - 8 + j] = (char)((self.bitcount >> (56 - j * 8)) & 0xFF);
|
||||
}
|
||||
|
||||
sha256_transform(&self.state, &self.buffer);
|
||||
|
||||
// Convert state to the final hash
|
||||
for (i = 0; i < 8; ++i) {
|
||||
hash[i * 4] = (char)((self.state[i] >> 24) & 0xFF);
|
||||
hash[i * 4 + 1] = (char)((self.state[i] >> 16) & 0xFF);
|
||||
hash[i * 4 + 2] = (char)((self.state[i] >> 8) & 0xFF);
|
||||
hash[i * 4 + 3] = (char)(self.state[i] & 0xFF);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] state
|
||||
@param [&in] buffer
|
||||
*>
|
||||
fn void sha256_transform(uint* state, char* buffer) @local {
|
||||
uint a, b, c, d, e, f, g, h, t1, t2;
|
||||
uint[64] m;
|
||||
int i;
|
||||
|
||||
// Prepare the message schedule
|
||||
for (i = 0; i < 16; ++i) {
|
||||
m[i] = ((uint)buffer[i * 4] << 24) | ((uint)buffer[i * 4 + 1] << 16) |
|
||||
((uint)buffer[i * 4 + 2] << 8) | ((uint)buffer[i * 4 + 3]); // Ensure values are cast to uint for correct shifts
|
||||
}
|
||||
for (i = 16; i < 64; ++i) {
|
||||
m[i] = @sigma1(m[i - 2]) + m[i - 7] + @sigma0(m[i - 15]) + m[i - 16];
|
||||
}
|
||||
|
||||
// Initialize working variables
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
e = state[4];
|
||||
f = state[5];
|
||||
g = state[6];
|
||||
h = state[7];
|
||||
|
||||
// Perform the main SHA-256 compression function
|
||||
for (i = 0; i < 64; ++i) {
|
||||
t1 = h + @_sigma1(e) + @ch(e, f, g) + K[i] + m[i];
|
||||
t2 = @_sigma0(a) + @maj(a, b, c);
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = d + t1;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = t1 + t2;
|
||||
}
|
||||
|
||||
// Update the state
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
state[5] += f;
|
||||
state[6] += g;
|
||||
state[7] += h;
|
||||
a = b = c = d = e = f = g = h = t1 = t2 = i = 0;
|
||||
m[:64] = buffer[:64] = 0;
|
||||
}
|
||||
@@ -17,10 +17,10 @@ fn void BitReader.clear(&self) @inline
|
||||
self.len = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require nbits <= 8
|
||||
* @require self.len + nbits <= uint.sizeof * 8
|
||||
**/
|
||||
<*
|
||||
@require nbits <= 8
|
||||
@require self.len + nbits <= uint.sizeof * 8
|
||||
*>
|
||||
fn char! BitReader.read_bits(&self, uint nbits)
|
||||
{
|
||||
uint bits = self.bits;
|
||||
@@ -45,6 +45,10 @@ struct BitWriter
|
||||
uint len;
|
||||
}
|
||||
|
||||
// c3 doesn't allow to shift more than bit width of a variable,
|
||||
// so use closest byte boundary of 24 instead of 32
|
||||
const int WRITER_BITS = 24;
|
||||
|
||||
fn void BitWriter.init(&self, OutStream byte_writer)
|
||||
{
|
||||
*self = { .writer = byte_writer };
|
||||
@@ -53,7 +57,9 @@ fn void BitWriter.init(&self, OutStream byte_writer)
|
||||
fn void! BitWriter.flush(&self)
|
||||
{
|
||||
if (self.len == 0) return;
|
||||
uint bits = self.bits << (32 - self.len);
|
||||
|
||||
int padding = ($sizeof(self.bits) * 8 - self.len);
|
||||
uint bits = self.bits << padding;
|
||||
uint n = (self.len + 7) / 8;
|
||||
char[4] buffer;
|
||||
bitorder::write(bits, &buffer, UIntBE);
|
||||
@@ -61,25 +67,28 @@ fn void! BitWriter.flush(&self)
|
||||
self.len = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require nbits <= 8
|
||||
**/
|
||||
<*
|
||||
@require nbits <= 32
|
||||
*>
|
||||
fn void! BitWriter.write_bits(&self, uint bits, uint nbits)
|
||||
{
|
||||
if (nbits == 0) return;
|
||||
uint n = self.len + nbits;
|
||||
uint to_write = n / 8;
|
||||
uint left = n % 8;
|
||||
if (to_write > 0)
|
||||
while (self.len + nbits > WRITER_BITS)
|
||||
{
|
||||
ulong lbits;
|
||||
if (self.len > 0) lbits = (ulong)self.bits << (64 - self.len);
|
||||
lbits |= (ulong)(bits >> left) << (64 - (n - left));
|
||||
char[8] buffer;
|
||||
bitorder::write(lbits, &buffer, ULongBE);
|
||||
io::write_all(self.writer, buffer[:to_write])!;
|
||||
uint to_push = WRITER_BITS - self.len;
|
||||
uint bits_to_push = (bits >> (nbits - to_push)) & ((1 << to_push) - 1);
|
||||
|
||||
self.bits <<= to_push;
|
||||
self.bits |= bits_to_push;
|
||||
self.len += to_push;
|
||||
nbits -= to_push;
|
||||
|
||||
self.flush()!;
|
||||
}
|
||||
self.bits <<= left;
|
||||
self.bits |= bits & ((1 << left) - 1);
|
||||
self.len = left;
|
||||
|
||||
if (nbits == 0) return;
|
||||
|
||||
self.bits <<= nbits;
|
||||
self.bits |= bits & ((1 << nbits) - 1);
|
||||
self.len += nbits;
|
||||
}
|
||||
@@ -37,17 +37,17 @@ fn usz! get_size(String path)
|
||||
fn void! delete(String filename) => os::native_remove(filename) @inline;
|
||||
|
||||
|
||||
/**
|
||||
* @require self.file != null
|
||||
**/
|
||||
<*
|
||||
@require self.file != null
|
||||
*>
|
||||
fn void! File.reopen(&self, String filename, String mode)
|
||||
{
|
||||
self.file = os::native_freopen(self.file, filename, mode)!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.file != null
|
||||
**/
|
||||
<*
|
||||
@require self.file != null
|
||||
*>
|
||||
fn usz! File.seek(&self, isz offset, Seek seek_mode = Seek.SET) @dynamic
|
||||
{
|
||||
os::native_fseek(self.file, offset, seek_mode)!;
|
||||
@@ -57,9 +57,9 @@ fn usz! File.seek(&self, isz offset, Seek seek_mode = Seek.SET) @dynamic
|
||||
|
||||
/*
|
||||
Implement later
|
||||
/**
|
||||
* @require self.file == null
|
||||
**/
|
||||
<*
|
||||
@require self.file == null
|
||||
*>
|
||||
fn void! File.memopen(File* file, char[] data, String mode)
|
||||
{
|
||||
@pool()
|
||||
@@ -71,17 +71,17 @@ fn void! File.memopen(File* file, char[] data, String mode)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @require self.file != null
|
||||
*/
|
||||
<*
|
||||
@require self.file != null
|
||||
*>
|
||||
fn void! File.write_byte(&self, char c) @dynamic
|
||||
{
|
||||
if (!libc::fputc(c, self.file)) return IoError.EOF?;
|
||||
return os::native_fputc(c, self.file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] self
|
||||
*/
|
||||
<*
|
||||
@param [&inout] self
|
||||
*>
|
||||
fn void! File.close(&self) @inline @dynamic
|
||||
{
|
||||
if (self.file && libc::fclose(self.file))
|
||||
@@ -105,26 +105,26 @@ fn void! File.close(&self) @inline @dynamic
|
||||
self.file = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.file
|
||||
*/
|
||||
<*
|
||||
@require self.file
|
||||
*>
|
||||
fn bool File.eof(&self) @inline
|
||||
{
|
||||
return libc::feof(self.file) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [in] buffer
|
||||
*/
|
||||
<*
|
||||
@param [in] buffer
|
||||
*>
|
||||
fn usz! File.read(&self, char[] buffer) @dynamic
|
||||
{
|
||||
return os::native_fread(self.file, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [out] buffer
|
||||
* @require self.file `File must be initialized`
|
||||
*/
|
||||
<*
|
||||
@param [out] buffer
|
||||
@require self.file `File must be initialized`
|
||||
*>
|
||||
fn usz! File.write(&self, char[] buffer) @dynamic
|
||||
{
|
||||
return os::native_fwrite(self.file, buffer);
|
||||
@@ -138,13 +138,13 @@ fn char! File.read_byte(&self) @dynamic
|
||||
return (char)c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load up to buffer.len characters. Returns IoError.OVERFLOW if the file is longer
|
||||
* than the buffer.
|
||||
*
|
||||
* @param filename "The path to the file to read"
|
||||
* @param [in] buffer "The buffer to read to"
|
||||
**/
|
||||
<*
|
||||
Load up to buffer.len characters. Returns IoError.OVERFLOW if the file is longer
|
||||
than the buffer.
|
||||
|
||||
@param filename "The path to the file to read"
|
||||
@param [in] buffer "The buffer to read to"
|
||||
*>
|
||||
fn char[]! load_buffer(String filename, char[] buffer)
|
||||
{
|
||||
File file = open(filename, "rb")!;
|
||||
@@ -182,9 +182,20 @@ fn char[]! load_temp(String filename)
|
||||
return load_new(filename, allocator::temp());
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.file `File must be initialized`
|
||||
*/
|
||||
fn void! save(String filename, char[] data)
|
||||
{
|
||||
File file = open(filename, "wb")!;
|
||||
defer (void)file.close();
|
||||
while (data.len)
|
||||
{
|
||||
usz written = file.write(data)!;
|
||||
data = data[written..];
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.file `File must be initialized`
|
||||
*>
|
||||
fn void! File.flush(&self) @dynamic
|
||||
{
|
||||
libc::fflush(self.file);
|
||||
|
||||
@@ -15,23 +15,53 @@ fault PrintFault
|
||||
{
|
||||
BUFFER_EXCEEDED,
|
||||
INTERNAL_BUFFER_EXCEEDED,
|
||||
INVALID_FORMAT_STRING,
|
||||
MISSING_ARG,
|
||||
INVALID_ARGUMENT_TYPE,
|
||||
}
|
||||
|
||||
fault FormattingFault
|
||||
{
|
||||
UNTERMINATED_FORMAT,
|
||||
MISSING_ARG,
|
||||
INVALID_WIDTH_ARG,
|
||||
INVALID_FORMAT_TYPE,
|
||||
INVALID_FORMAT,
|
||||
NOT_ENOUGH_ARGUMENTS,
|
||||
INVALID_ARGUMENT,
|
||||
}
|
||||
|
||||
def OutputFn = fn void!(void* buffer, char c);
|
||||
def FloatType = double;
|
||||
|
||||
|
||||
macro bool is_struct_with_default_print($Type)
|
||||
{
|
||||
return $Type.kindof == STRUCT
|
||||
&&& !$defined($Type.to_format)
|
||||
&&& !$defined($Type.to_new_string)
|
||||
&&& !$defined($Type.to_string);
|
||||
}
|
||||
|
||||
<*
|
||||
Introspect a struct and print it to a formatter
|
||||
|
||||
@require @typekind(value) == STRUCT `This macro is only valid on macros`
|
||||
*>
|
||||
macro usz! struct_to_format(value, Formatter* f, bool $force_dump)
|
||||
{
|
||||
var $Type = $typeof(value);
|
||||
usz total = f.print("{ ")!;
|
||||
$foreach ($i, $member : $Type.membersof)
|
||||
$if $i > 0:
|
||||
total += f.print(", ")!;
|
||||
$endif
|
||||
$if $member.nameof != "":
|
||||
total += f.printf("%s: ", $member.nameof)!;
|
||||
$endif
|
||||
$if ($force_dump &&& $member.typeid.kindof == STRUCT) |||
|
||||
is_struct_with_default_print($typefrom($member.typeid)):
|
||||
total += struct_to_format($member.get(value), f, $force_dump)!;
|
||||
$else
|
||||
total += f.printf("%s", $member.get(value))!;
|
||||
$endif
|
||||
$endforeach
|
||||
return total + f.print(" }");
|
||||
}
|
||||
|
||||
fn usz! ReflectedParam.to_format(&self, Formatter* f) @dynamic
|
||||
{
|
||||
return f.printf("[Parameter '%s']", self.name);
|
||||
}
|
||||
|
||||
fn usz! Formatter.printf(&self, String format, args...)
|
||||
{
|
||||
@@ -48,6 +78,7 @@ struct Formatter
|
||||
uint width;
|
||||
uint prec;
|
||||
usz idx;
|
||||
anyfault first_fault;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +100,12 @@ fn void Formatter.init(&self, OutputFn out_fn, void* data = null)
|
||||
|
||||
fn usz! Formatter.out(&self, char c) @private
|
||||
{
|
||||
self.out_fn(self.data, c)!;
|
||||
if (catch err = self.out_fn(self.data, c))
|
||||
{
|
||||
if (self.first_fault) return self.first_fault?;
|
||||
self.first_fault = err;
|
||||
return err?;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -121,6 +157,7 @@ fn usz! Formatter.out_str(&self, any arg) @private
|
||||
case ANYFAULT:
|
||||
case FAULT:
|
||||
return self.out_substr((*(anyfault*)arg.ptr).nameof);
|
||||
case INTERFACE:
|
||||
case ANY:
|
||||
return self.out_str(*(any*)arg);
|
||||
case OPTIONAL:
|
||||
@@ -136,7 +173,7 @@ fn usz! Formatter.out_str(&self, any arg) @private
|
||||
}
|
||||
self.flags = {};
|
||||
self.width = 0;
|
||||
return self.ntoa_any(arg, 10);
|
||||
return self.ntoa_any(arg, 10) ?? self.out_substr("<INVALID>");
|
||||
case FLOAT:
|
||||
PrintFlags flags = self.flags;
|
||||
uint width = self.width;
|
||||
@@ -147,7 +184,7 @@ fn usz! Formatter.out_str(&self, any arg) @private
|
||||
}
|
||||
self.flags = {};
|
||||
self.width = 0;
|
||||
return self.ftoa(float_from_any(arg)!!);
|
||||
return self.ftoa(float_from_any(arg)) ?? self.out_substr("ERR");
|
||||
case BOOL:
|
||||
return self.out_substr(*(bool*)arg.ptr ? "true" : "false");
|
||||
default:
|
||||
@@ -158,7 +195,7 @@ fn usz! Formatter.out_str(&self, any arg) @private
|
||||
switch (arg.type.kindof)
|
||||
{
|
||||
case ENUM:
|
||||
usz i = types::any_to_int(arg, usz)!!;
|
||||
usz i = types::any_to_enum_ordinal(arg, usz)!!;
|
||||
assert(i < arg.type.names.len, "Illegal enum value found, numerical value was %d.", i);
|
||||
return self.out_substr(arg.type.names[i]);
|
||||
case STRUCT:
|
||||
@@ -170,6 +207,10 @@ fn usz! Formatter.out_str(&self, any arg) @private
|
||||
case FUNC:
|
||||
return self.out_substr("<function>");
|
||||
case DISTINCT:
|
||||
if (arg.type == String.typeid)
|
||||
{
|
||||
return self.out_substr(*(String*)arg);
|
||||
}
|
||||
if (arg.type == ZString.typeid)
|
||||
{
|
||||
return self.out_substr(*(ZString*)arg ? ((ZString*)arg).str_view() : "(null)");
|
||||
@@ -197,8 +238,7 @@ fn usz! Formatter.out_str(&self, any arg) @private
|
||||
self.width = width;
|
||||
}
|
||||
self.width = 0;
|
||||
self.out_substr("0x")!;
|
||||
return self.ntoa_any(arg, 16);
|
||||
return self.out_substr("0x")! + self.ntoa_any(arg, 16);
|
||||
case ARRAY:
|
||||
// this is SomeType[*] so grab the "SomeType"
|
||||
PrintFlags flags = self.flags;
|
||||
@@ -252,10 +292,6 @@ fn usz! Formatter.out_str(&self, any arg) @private
|
||||
case SLICE:
|
||||
// this is SomeType[] so grab the "SomeType"
|
||||
typeid inner = arg.type.inner;
|
||||
if (inner == char.typeid)
|
||||
{
|
||||
return self.out_substr(*(String*)arg);
|
||||
}
|
||||
if (inner == void.typeid) inner = char.typeid;
|
||||
PrintFlags flags = self.flags;
|
||||
uint width = self.width;
|
||||
@@ -280,6 +316,9 @@ fn usz! Formatter.out_str(&self, any arg) @private
|
||||
}
|
||||
len += self.out(']')!;
|
||||
return len;
|
||||
case ANY:
|
||||
case INTERFACE:
|
||||
unreachable("Already handled");
|
||||
default:
|
||||
}
|
||||
return self.out_substr("Invalid type");
|
||||
@@ -292,10 +331,31 @@ fn void! out_null_fn(void* data @unused, char c @unused) @private
|
||||
{
|
||||
}
|
||||
|
||||
macro usz! @report_fault(Formatter* f, $fault)
|
||||
{
|
||||
(void)f.out_substr($fault);
|
||||
return PrintFault.INVALID_FORMAT?;
|
||||
}
|
||||
|
||||
macro usz! @wrap_bad(Formatter* f, #action)
|
||||
{
|
||||
usz! len = #action;
|
||||
if (catch err = len)
|
||||
{
|
||||
case PrintFault.BUFFER_EXCEEDED:
|
||||
case PrintFault.INTERNAL_BUFFER_EXCEEDED:
|
||||
return f.first_err(err)?;
|
||||
default:
|
||||
err = f.first_err(PrintFault.INVALID_ARGUMENT);
|
||||
f.out_substr("<INVALID>")!;
|
||||
return err?;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
fn usz! Formatter.vprintf(&self, String format, any[] anys)
|
||||
{
|
||||
self.first_fault = {};
|
||||
if (!self.out_fn)
|
||||
{
|
||||
// use null output function
|
||||
@@ -315,7 +375,7 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys)
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
if (i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
|
||||
if (i >= format_len) return @report_fault(self, "%ERR");
|
||||
c = format[i];
|
||||
if (c == '%')
|
||||
{
|
||||
@@ -335,11 +395,12 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys)
|
||||
case '#': self.flags.hash = true;
|
||||
default: break FLAG_EVAL;
|
||||
}
|
||||
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
|
||||
if (++i >= format_len) return @report_fault(self, "%ERR");
|
||||
c = format[i];
|
||||
}
|
||||
// evaluate width field
|
||||
int w = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i)!;
|
||||
int! w = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i);
|
||||
if (catch w) return @report_fault(self, "%ERR");
|
||||
c = format[i];
|
||||
if (w < 0)
|
||||
{
|
||||
@@ -352,15 +413,21 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys)
|
||||
if (c == '.')
|
||||
{
|
||||
self.flags.precision = true;
|
||||
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
|
||||
int prec = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i)!;
|
||||
if (++i >= format_len) return @report_fault(self, "<BAD FORMAT>");
|
||||
int! prec = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i);
|
||||
if (catch prec) return @report_fault(self, "<BAD FORMAT>");
|
||||
self.prec = prec < 0 ? 0 : prec;
|
||||
c = format[i];
|
||||
}
|
||||
|
||||
// evaluate specifier
|
||||
uint base = 0;
|
||||
if (variant_index >= anys.len) return PrintFault.MISSING_ARG?;
|
||||
if (variant_index >= anys.len)
|
||||
{
|
||||
self.first_err(PrintFault.NOT_ENOUGH_ARGUMENTS);
|
||||
total_len += self.out_substr("<MISSING>")!;
|
||||
continue;
|
||||
}
|
||||
any current = anys[variant_index++];
|
||||
switch (c)
|
||||
{
|
||||
@@ -386,25 +453,25 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys)
|
||||
self.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'a':
|
||||
total_len += self.atoa(float_from_any(current)!!)!;
|
||||
total_len += @wrap_bad(self, self.atoa(float_from_any(current)))!;
|
||||
continue;
|
||||
case 'F' :
|
||||
self.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'f':
|
||||
total_len += self.ftoa(float_from_any(current)!!)!;
|
||||
total_len += @wrap_bad(self, self.ftoa(float_from_any(current)))!;
|
||||
continue;
|
||||
case 'E':
|
||||
self.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'e':
|
||||
total_len += self.etoa(float_from_any(current)!!)!;
|
||||
total_len += @wrap_bad(self, self.etoa(float_from_any(current)))!;
|
||||
continue;
|
||||
case 'G':
|
||||
self.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'g':
|
||||
total_len += self.gtoa(float_from_any(current)!!)!;
|
||||
total_len += @wrap_bad(self, self.gtoa(float_from_any(current)))!;
|
||||
continue;
|
||||
case 'c':
|
||||
total_len += self.out_char(current)!;
|
||||
@@ -417,11 +484,14 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys)
|
||||
total_len += self.pad(' ', self.width, len)!;
|
||||
continue;
|
||||
}
|
||||
OutputFn out_fn = self.out_fn;
|
||||
self.out_fn = (OutputFn)&out_null_fn;
|
||||
usz len = self.out_str(current)!;
|
||||
self.out_fn = out_fn;
|
||||
total_len += self.pad(' ', self.width, len)!;
|
||||
if (self.width)
|
||||
{
|
||||
OutputFn out_fn = self.out_fn;
|
||||
self.out_fn = (OutputFn)&out_null_fn;
|
||||
usz len = self.out_str(current)!;
|
||||
self.out_fn = out_fn;
|
||||
total_len += self.pad(' ', self.width, len)!;
|
||||
}
|
||||
total_len += self.out_str(current)!;
|
||||
continue;
|
||||
case 'p':
|
||||
@@ -429,7 +499,9 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys)
|
||||
self.flags.hash = true;
|
||||
base = 16;
|
||||
default:
|
||||
return PrintFault.INVALID_FORMAT_STRING?;
|
||||
self.first_err(PrintFault.INVALID_FORMAT);
|
||||
total_len += self.out_substr("<BAD FORMAT>")!;
|
||||
continue;
|
||||
}
|
||||
if (base != 10)
|
||||
{
|
||||
@@ -440,14 +512,13 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys)
|
||||
if (self.flags.precision) self.flags.zeropad = false;
|
||||
|
||||
bool is_neg;
|
||||
uint128 v = int_from_any(current, &is_neg)!!;
|
||||
|
||||
total_len += self.ntoa(v, is_neg, base)!;
|
||||
total_len += @wrap_bad(self, self.ntoa(int_from_any(current, &is_neg), is_neg, base))!;
|
||||
}
|
||||
// termination
|
||||
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
|
||||
|
||||
// return written chars without terminating \0
|
||||
if (self.first_fault) return self.first_fault?;
|
||||
return total_len;
|
||||
}
|
||||
|
||||
@@ -461,4 +532,4 @@ fn usz! Formatter.print(&self, String str)
|
||||
}
|
||||
foreach (c : str) self.out(c)!;
|
||||
return self.idx;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,18 @@ import std::math;
|
||||
const char[16] XDIGITS_H = "0123456789ABCDEF";
|
||||
const char[16] XDIGITS_L = "0123456789abcdef";
|
||||
|
||||
fault FormattingFault
|
||||
{
|
||||
BAD_FORMAT
|
||||
}
|
||||
|
||||
macro Formatter.first_err(&self, anyfault f)
|
||||
{
|
||||
if (self.first_fault) return self.first_fault;
|
||||
self.first_fault = f;
|
||||
return f;
|
||||
}
|
||||
|
||||
fn usz! Formatter.adjust(&self, usz len) @local
|
||||
{
|
||||
if (!self.flags.left) return 0;
|
||||
@@ -21,6 +33,7 @@ fn uint128! int_from_any(any arg, bool *is_neg) @private
|
||||
case TypeKind.ENUM:
|
||||
return int_from_any(arg.as_inner(), is_neg);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
*is_neg = false;
|
||||
switch (arg)
|
||||
@@ -59,7 +72,7 @@ fn uint128! int_from_any(any arg, bool *is_neg) @private
|
||||
double d = *arg;
|
||||
return (uint128)((*is_neg = d < 0) ? -d : d);
|
||||
default:
|
||||
return PrintFault.INVALID_ARGUMENT_TYPE?;
|
||||
return FormattingFault.BAD_FORMAT?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,19 +114,19 @@ fn FloatType! float_from_any(any arg) @private
|
||||
case double:
|
||||
return (FloatType)*arg;
|
||||
default:
|
||||
return PrintFault.INVALID_ARGUMENT_TYPE?;
|
||||
return FormattingFault.BAD_FORMAT?;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read a simple integer value, typically for formatting.
|
||||
*
|
||||
* @param [inout] len_ptr "the length remaining."
|
||||
* @param [in] buf "the buf to read from."
|
||||
* @param maxlen "the maximum len that can be read."
|
||||
* @return "The result of the atoi."
|
||||
**/
|
||||
<*
|
||||
Read a simple integer value, typically for formatting.
|
||||
|
||||
@param [inout] len_ptr "the length remaining."
|
||||
@param [in] buf "the buf to read from."
|
||||
@param maxlen "the maximum len that can be read."
|
||||
@return "The result of the atoi."
|
||||
*>
|
||||
fn uint simple_atoi(char* buf, usz maxlen, usz* len_ptr) @inline @private
|
||||
{
|
||||
uint i = 0;
|
||||
@@ -201,8 +214,7 @@ fn usz! Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
|
||||
// Add padding
|
||||
if (!self.flags.left) len += self.pad(' ', self.width, 3 + pl)!;
|
||||
String s = self.flags.uppercase ? "INF" : "inf";
|
||||
if (y != y) s = self.flags.uppercase ? "NAN" : "nan";
|
||||
len += s.len;
|
||||
if (math::is_nan(y)) s = self.flags.uppercase ? "NAN" : "nan";
|
||||
if (pl) len += self.out(is_neg ? '-' : '+')!;
|
||||
len += self.out_chars(s)!;
|
||||
if (self.flags.left) len += self.pad(' ', self.width, 3 + pl)!;
|
||||
@@ -588,12 +600,15 @@ 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
|
||||
{
|
||||
bool is_neg;
|
||||
uint128 val = int_from_any(arg, &is_neg)!!;
|
||||
return self.ntoa(val, is_neg, base) @inline;
|
||||
return self.ntoa(int_from_any(arg, &is_neg)!!, is_neg, base) @inline;
|
||||
}
|
||||
|
||||
fn usz! Formatter.out_char(&self, any arg) @private
|
||||
{
|
||||
if (!arg.type.kindof.is_int())
|
||||
{
|
||||
return self.out_substr("<NOT CHAR>");
|
||||
}
|
||||
usz len = 1;
|
||||
uint l = 1;
|
||||
// pre padding
|
||||
@@ -630,7 +645,7 @@ fn usz! Formatter.out_reverse(&self, char[] buf) @private
|
||||
// pad spaces up to given width
|
||||
if (!self.flags.zeropad && !self.flags.left)
|
||||
{
|
||||
n += self.pad(' ', self.width, len)!;
|
||||
n += self.pad(' ', self.width, len)!;
|
||||
}
|
||||
// reverse string
|
||||
while (len) n += self.out(buf[--len])!;
|
||||
@@ -640,17 +655,6 @@ fn usz! Formatter.out_reverse(&self, char[] buf) @private
|
||||
return n;
|
||||
}
|
||||
|
||||
fn void! printf_advance_format(usz format_len, usz *index_ptr) @inline @private
|
||||
{
|
||||
usz val = ++(*index_ptr);
|
||||
if (val >= format_len) return FormattingFault.UNTERMINATED_FORMAT?;
|
||||
}
|
||||
|
||||
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?;
|
||||
return args_ptr[(*arg_index_ptr)++];
|
||||
}
|
||||
|
||||
fn int! printf_parse_format_field(
|
||||
any* args_ptr, usz args_len, usz* args_index_ptr,
|
||||
@@ -659,9 +663,11 @@ fn int! printf_parse_format_field(
|
||||
char c = format_ptr[*index_ptr];
|
||||
if (c.is_digit()) return simple_atoi(format_ptr, format_len, index_ptr);
|
||||
if (c != '*') return 0;
|
||||
printf_advance_format(format_len, index_ptr)!;
|
||||
any val = next_any(args_ptr, args_len, args_index_ptr)!;
|
||||
if (!val.type.kindof.is_int()) return FormattingFault.INVALID_WIDTH_ARG?;
|
||||
usz len = ++(*index_ptr);
|
||||
if (len >= format_len) return FormattingFault.BAD_FORMAT?;
|
||||
if (*args_index_ptr >= args_len) return FormattingFault.BAD_FORMAT?;
|
||||
any val = args_ptr[(*args_index_ptr)++];
|
||||
if (!val.type.kindof.is_int()) return FormattingFault.BAD_FORMAT?;
|
||||
uint! intval = types::any_to_int(val, int);
|
||||
return intval ?? FormattingFault.INVALID_WIDTH_ARG?;
|
||||
return intval ?? FormattingFault.BAD_FORMAT?;
|
||||
}
|
||||
|
||||
305
lib/std/io/io.c3
305
lib/std/io/io.c3
@@ -45,16 +45,16 @@ fault IoError
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read from a stream (default is stdin) to the next "\n"
|
||||
* 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.`
|
||||
**/
|
||||
<*
|
||||
Read from a stream (default is stdin) to the next "\n"
|
||||
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())
|
||||
{
|
||||
bool $is_stream = @typeis(stream, InStream);
|
||||
@@ -65,52 +65,52 @@ macro String! readline(stream = io::stdin(), Allocator allocator = allocator::he
|
||||
char val = stream.read_byte()!;
|
||||
$endif
|
||||
|
||||
if (val == '\n') return "";
|
||||
@pool(allocator)
|
||||
{
|
||||
DString str = dstring::temp_with_capacity(256);
|
||||
if (val != '\r') str.append(val);
|
||||
while (1)
|
||||
{
|
||||
$if $is_stream:
|
||||
char! c = func((void*)stream);
|
||||
$else
|
||||
char! c = stream.read_byte();
|
||||
$endif
|
||||
if (catch err = c)
|
||||
{
|
||||
if (err == IoError.EOF) break;
|
||||
return err?;
|
||||
}
|
||||
if (c == '\r') continue;
|
||||
if (c == '\n') break;
|
||||
str.append_char(c);
|
||||
}
|
||||
return str.copy_str(allocator);
|
||||
};
|
||||
if (val == '\n') return "";
|
||||
@pool(allocator)
|
||||
{
|
||||
DString str = dstring::temp_with_capacity(256);
|
||||
if (val != '\r') str.append(val);
|
||||
while (1)
|
||||
{
|
||||
$if $is_stream:
|
||||
char! c = func((void*)stream);
|
||||
$else
|
||||
char! c = stream.read_byte();
|
||||
$endif
|
||||
if (catch err = c)
|
||||
{
|
||||
if (err == IoError.EOF) break;
|
||||
return err?;
|
||||
}
|
||||
if (c == '\r') continue;
|
||||
if (c == '\n') break;
|
||||
str.append_char(c);
|
||||
}
|
||||
return str.copy_str(allocator);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.`
|
||||
**/
|
||||
<*
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.`
|
||||
*/
|
||||
<*
|
||||
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)
|
||||
{
|
||||
var $Type = $typeof(x);
|
||||
@@ -122,19 +122,25 @@ macro usz! fprint(out, x)
|
||||
$if $assignable(x, String):
|
||||
return out.write((String)x);
|
||||
$else
|
||||
return fprintf(out, "%s", x);
|
||||
$if is_struct_with_default_print($Type):
|
||||
Formatter formatter;
|
||||
formatter.init(&out_putstream_fn, &&(OutStream)out);
|
||||
return struct_to_format(x, &formatter, false);
|
||||
$else
|
||||
return fprintf(out, "%s", x);
|
||||
$endif
|
||||
$endif
|
||||
$endswitch
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`
|
||||
**/
|
||||
<*
|
||||
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;
|
||||
@@ -142,14 +148,14 @@ fn usz! fprintf(OutStream out, String format, args...)
|
||||
return formatter.vprintf(format, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`
|
||||
**/
|
||||
<*
|
||||
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;
|
||||
@@ -160,9 +166,9 @@ fn usz! fprintfn(OutStream out, String format, args...) @maydiscard
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require @is_outstream(out) "The output must implement OutStream"
|
||||
*/
|
||||
<*
|
||||
@require @is_outstream(out) "The output must implement OutStream"
|
||||
*>
|
||||
macro usz! fprintn(out, x = "")
|
||||
{
|
||||
usz len = fprint(out, x)!;
|
||||
@@ -176,37 +182,37 @@ macro usz! fprintn(out, x = "")
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print any value to stdout.
|
||||
**/
|
||||
<*
|
||||
Print any value to stdout.
|
||||
*>
|
||||
macro void print(x)
|
||||
{
|
||||
(void)fprint(io::stdout(), x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print any value to stdout, appending an '\n’ after.
|
||||
*
|
||||
* @param x "The value to print"
|
||||
**/
|
||||
<*
|
||||
Print any value to stdout, appending an '\n’ after.
|
||||
|
||||
@param x "The value to print"
|
||||
*>
|
||||
macro void printn(x = "")
|
||||
{
|
||||
(void)fprintn(io::stdout(), x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print any value to stderr.
|
||||
**/
|
||||
<*
|
||||
Print any value to stderr.
|
||||
*>
|
||||
macro void eprint(x)
|
||||
{
|
||||
(void)fprint(io::stderr(), x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print any value to stderr, appending an '\n’ after.
|
||||
*
|
||||
* @param x "The value to print"
|
||||
**/
|
||||
<*
|
||||
Print any value to stderr, appending an '\n’ after.
|
||||
|
||||
@param x "The value to print"
|
||||
*>
|
||||
macro void eprintn(x)
|
||||
{
|
||||
(void)fprintn(io::stderr(), x);
|
||||
@@ -224,20 +230,20 @@ fn void! out_putchar_fn(void* data @unused, char c) @private
|
||||
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`
|
||||
**/
|
||||
<*
|
||||
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
|
||||
{
|
||||
Formatter formatter;
|
||||
@@ -245,30 +251,30 @@ fn usz! printf(String format, args...) @maydiscard
|
||||
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`
|
||||
**/
|
||||
<*
|
||||
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
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_putchar_fn);
|
||||
usz len = formatter.vprintf(format, args)!;
|
||||
usz! len = formatter.vprintf(format, args);
|
||||
putchar('\n');
|
||||
io::stdout().flush()!;
|
||||
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`
|
||||
**/
|
||||
<*
|
||||
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
|
||||
{
|
||||
Formatter formatter;
|
||||
@@ -278,32 +284,32 @@ fn usz! eprintf(String format, args...) @maydiscard
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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`
|
||||
**/
|
||||
<*
|
||||
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
|
||||
{
|
||||
Formatter formatter;
|
||||
OutStream stream = stderr();
|
||||
formatter.init(&out_putstream_fn, &stream);
|
||||
usz len = formatter.vprintf(format, args)! + 1;
|
||||
usz! len = formatter.vprintf(format, args);
|
||||
stderr().write_byte('\n')!;
|
||||
stderr().flush()!;
|
||||
return len;
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.`
|
||||
**/
|
||||
<*
|
||||
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
|
||||
{
|
||||
Formatter formatter;
|
||||
@@ -332,19 +338,19 @@ struct BufferData @private
|
||||
module std::io @if (env::LIBC);
|
||||
import libc;
|
||||
|
||||
/**
|
||||
* Libc `putchar`, prints a single character to stdout.
|
||||
**/
|
||||
<*
|
||||
Libc `putchar`, prints a single character to stdout.
|
||||
*>
|
||||
fn void putchar(char c) @inline
|
||||
{
|
||||
libc::putchar(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get standard out.
|
||||
*
|
||||
* @return `stdout as a File`
|
||||
**/
|
||||
<*
|
||||
Get standard out.
|
||||
|
||||
@return `stdout as a File`
|
||||
*>
|
||||
fn File* stdout()
|
||||
{
|
||||
static File file;
|
||||
@@ -352,11 +358,11 @@ fn File* stdout()
|
||||
return &file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get standard err.
|
||||
*
|
||||
* @return `stderr as a File`
|
||||
**/
|
||||
<*
|
||||
Get standard err.
|
||||
|
||||
@return `stderr as a File`
|
||||
*>
|
||||
fn File* stderr()
|
||||
{
|
||||
static File file;
|
||||
@@ -364,11 +370,11 @@ fn File* stderr()
|
||||
return &file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get standard in.
|
||||
*
|
||||
* @return `stdin as a File`
|
||||
**/
|
||||
<*
|
||||
Get standard in.
|
||||
|
||||
@return `stdin as a File`
|
||||
*>
|
||||
fn File* stdin()
|
||||
{
|
||||
static File file;
|
||||
@@ -401,3 +407,4 @@ fn File* stdin()
|
||||
{
|
||||
return &stdin_file;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
module std::io::os @if(env::LIBC);
|
||||
import libc;
|
||||
|
||||
/**
|
||||
* @require mode.len > 0
|
||||
* @require filename.len > 0
|
||||
**/
|
||||
<*
|
||||
@require mode.len > 0
|
||||
@require filename.len > 0
|
||||
*>
|
||||
fn void*! native_fopen(String filename, String mode) @inline
|
||||
{
|
||||
@pool()
|
||||
@@ -41,10 +41,10 @@ fn void! native_remove(String filename)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @require mode.len > 0
|
||||
* @require filename.len > 0
|
||||
**/
|
||||
<*
|
||||
@require mode.len > 0
|
||||
@require filename.len > 0
|
||||
*>
|
||||
fn void*! native_freopen(void* file, String filename, String mode) @inline
|
||||
{
|
||||
@pool()
|
||||
@@ -60,7 +60,7 @@ fn void*! native_freopen(void* file, String filename, String mode) @inline
|
||||
|
||||
fn void! native_fseek(void* file, isz offset, Seek seek_mode) @inline
|
||||
{
|
||||
if (libc::fseek(file, (SeekIndex)offset, (CInt)seek_mode)) return file_seek_errno()?;
|
||||
if (libc::fseek(file, (SeekIndex)offset, seek_mode.ordinal)) return file_seek_errno()?;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,6 +75,11 @@ fn usz! native_fwrite(CFile file, char[] buffer) @inline
|
||||
return libc::fwrite(buffer.ptr, 1, buffer.len, file);
|
||||
}
|
||||
|
||||
fn void! native_fputc(CInt c, CFile stream) @inline
|
||||
{
|
||||
if (libc::fputc(c, stream) == libc::EOF) return IoError.EOF?;
|
||||
}
|
||||
|
||||
fn usz! native_fread(CFile file, char[] buffer) @inline
|
||||
{
|
||||
return libc::fread(buffer.ptr, 1, buffer.len, file);
|
||||
|
||||
@@ -9,6 +9,7 @@ def FtellFn = fn usz!(void*);
|
||||
def FwriteFn = fn usz!(void*, char[] buffer);
|
||||
def FreadFn = fn usz!(void*, char[] buffer);
|
||||
def RemoveFn = fn void!(String);
|
||||
def FputcFn = fn void!(int, void*);
|
||||
|
||||
FopenFn native_fopen_fn @weak @if(!$defined(native_fopen_fn));
|
||||
FcloseFn native_fclose_fn @weak @if(!$defined(native_fclose_fn));
|
||||
@@ -18,32 +19,33 @@ FtellFn native_ftell_fn @weak @if(!$defined(native_ftell_fn));
|
||||
FwriteFn native_fwrite_fn @weak @if(!$defined(native_fwrite_fn));
|
||||
FreadFn native_fread_fn @weak @if(!$defined(native_fread_fn));
|
||||
RemoveFn native_remove_fn @weak @if(!$defined(native_remove_fn));
|
||||
FputcFn native_fputc_fn @weak @if(!$defined(native_fputc_fn));
|
||||
|
||||
/**
|
||||
* @require mode.len > 0
|
||||
* @require filename.len > 0
|
||||
**/
|
||||
<*
|
||||
@require mode.len > 0
|
||||
@require filename.len > 0
|
||||
*>
|
||||
fn void*! native_fopen(String filename, String mode) @inline
|
||||
{
|
||||
if (native_fopen_fn) return native_fopen_fn(filename, mode);
|
||||
return IoError.UNSUPPORTED_OPERATION?;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a file.
|
||||
*
|
||||
* @require filename.len > 0
|
||||
**/
|
||||
<*
|
||||
Delete a file.
|
||||
|
||||
@require filename.len > 0
|
||||
*>
|
||||
fn void! native_remove(String filename) @inline
|
||||
{
|
||||
if (native_remove_fn) return native_remove_fn(filename);
|
||||
return IoError.UNSUPPORTED_OPERATION?;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require mode.len > 0
|
||||
* @require filename.len > 0
|
||||
**/
|
||||
<*
|
||||
@require mode.len > 0
|
||||
@require filename.len > 0
|
||||
*>
|
||||
fn void*! native_freopen(void* file, String filename, String mode) @inline
|
||||
{
|
||||
if (native_freopen_fn) return native_freopen_fn(file, filename, mode);
|
||||
@@ -73,3 +75,9 @@ fn usz! native_fread(CFile file, char[] buffer) @inline
|
||||
if (native_fread_fn) return native_fread_fn(file, buffer);
|
||||
return IoError.UNSUPPORTED_OPERATION?;
|
||||
}
|
||||
|
||||
fn void! native_fputc(CInt c, CFile stream) @inline
|
||||
{
|
||||
if (native_fputc_fn) return native_fputc_fn(c, stream);
|
||||
return IoError.UNSUPPORTED_OPERATION?;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
module std::io::os;
|
||||
import libc, std::os, std::io;
|
||||
|
||||
fn void! native_stat(Stat* stat, String path) @if(env::DARWIN || env::LINUX)
|
||||
fn void! native_stat(Stat* stat, String path) @if(env::DARWIN || env::LINUX || env::BSD_FAMILY)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
$if env::DARWIN || env::LINUX:
|
||||
$if env::DARWIN || env::LINUX || env::BSD_FAMILY:
|
||||
int res = libc::stat(path.zstr_tcopy(), stat);
|
||||
$else
|
||||
unreachable("Stat unimplemented");
|
||||
@@ -71,6 +71,9 @@ fn bool native_file_or_dir_exists(String path)
|
||||
{
|
||||
$switch
|
||||
$case env::DARWIN:
|
||||
$case env::FREEBSD:
|
||||
$case env::NETBSD:
|
||||
$case env::OPENBSD:
|
||||
$case env::LINUX:
|
||||
Stat stat;
|
||||
return @ok(native_stat(&stat, path));
|
||||
@@ -93,6 +96,9 @@ fn bool native_is_file(String path)
|
||||
{
|
||||
$switch
|
||||
$case env::DARWIN:
|
||||
$case env::FREEBSD:
|
||||
$case env::NETBSD:
|
||||
$case env::OPENBSD:
|
||||
$case env::LINUX:
|
||||
Stat stat;
|
||||
return @ok(native_stat(&stat, path)) && libc_S_ISTYPE(stat.st_mode, libc::S_IFREG);
|
||||
@@ -105,7 +111,7 @@ fn bool native_is_file(String path)
|
||||
|
||||
fn bool native_is_dir(String path)
|
||||
{
|
||||
$if env::DARWIN || env::LINUX:
|
||||
$if env::DARWIN || env::LINUX || env::BSD_FAMILY:
|
||||
Stat stat;
|
||||
return @ok(native_stat(&stat, path)) && libc_S_ISTYPE(stat.st_mode, libc::S_IFDIR);
|
||||
$else
|
||||
|
||||
@@ -4,7 +4,7 @@ import std::io, std::os;
|
||||
fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator allocator)
|
||||
{
|
||||
PathList list;
|
||||
list.new_init(.allocator = allocator);
|
||||
list.new_init(allocator: allocator);
|
||||
DIRPtr directory = posix::opendir(dir.str_view() ? dir.as_zstr() : (ZString)".");
|
||||
defer if (directory) posix::closedir(directory);
|
||||
if (!directory) return (path::is_dir(dir) ? IoError.CANNOT_READ_DIR : IoError.FILE_NOT_DIR)?;
|
||||
@@ -27,7 +27,7 @@ import std::time, std::os, std::io;
|
||||
fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator allocator)
|
||||
{
|
||||
PathList list;
|
||||
list.new_init(.allocator = allocator);
|
||||
list.new_init(allocator: allocator);
|
||||
|
||||
@pool(allocator)
|
||||
{
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
module std::io::os @if(env::POSIX);
|
||||
import std::io, std::os, libc;
|
||||
|
||||
/**
|
||||
* @require dir.str_view()
|
||||
**/
|
||||
<*
|
||||
@require dir.str_view()
|
||||
*>
|
||||
fn void! native_rmtree(Path dir)
|
||||
{
|
||||
DIRPtr directory = posix::opendir(dir.as_zstr());
|
||||
|
||||
@@ -96,6 +96,13 @@ enum MkdirPermissions
|
||||
USER_AND_ADMIN
|
||||
}
|
||||
|
||||
<*
|
||||
Create a directory on a given path, optionally recursive.
|
||||
|
||||
@param path `The path to create`
|
||||
@param recursive `If directories in between should be created if they're missing, defaults to false`
|
||||
@param permissions `The permissions to set on the directory`
|
||||
*>
|
||||
fn bool! mkdir(Path path, bool recursive = false, MkdirPermissions permissions = NORMAL)
|
||||
{
|
||||
if (!path.path_string.len) return PathResult.INVALID_PATH?;
|
||||
@@ -111,12 +118,22 @@ fn bool! mkdir(Path path, bool recursive = false, MkdirPermissions permissions =
|
||||
return os::native_mkdir(path, permissions);
|
||||
}
|
||||
|
||||
<*
|
||||
Tries to delete directory, which must be empty.
|
||||
|
||||
@param path `The path to delete`
|
||||
@return `true if there was a directory to delete, false otherwise`
|
||||
@return! PathResult.INVALID_PATH `if the path was invalid`
|
||||
*>
|
||||
fn bool! rmdir(Path path)
|
||||
{
|
||||
if (!path.path_string.len) return PathResult.INVALID_PATH?;
|
||||
return os::native_rmdir(path);
|
||||
}
|
||||
|
||||
<*
|
||||
Like [rmdir] but deletes a directory even if it contains items.
|
||||
*>
|
||||
fn void! rmtree(Path path)
|
||||
{
|
||||
if (!path.path_string.len) return PathResult.INVALID_PATH?;
|
||||
@@ -127,11 +144,21 @@ fn void! rmtree(Path path)
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
Creates a new path.
|
||||
|
||||
@return! PathResult.INVALID_PATH `if the path was invalid`
|
||||
*>
|
||||
fn Path! new(String path, Allocator allocator = allocator::heap(), PathEnv path_env = DEFAULT_PATH_ENV)
|
||||
{
|
||||
return { normalize(path.copy(allocator), path_env), path_env };
|
||||
}
|
||||
|
||||
<*
|
||||
Creates a new path using the temp allocator.
|
||||
|
||||
@return! PathResult.INVALID_PATH `if the path was invalid`
|
||||
*>
|
||||
fn Path! temp_new(String path, PathEnv path_env = DEFAULT_PATH_ENV)
|
||||
{
|
||||
return new(path, allocator::temp(), path_env);
|
||||
@@ -141,7 +168,7 @@ fn Path! new_win32_wstring(WString path, Allocator allocator = allocator::heap()
|
||||
{
|
||||
@pool(allocator)
|
||||
{
|
||||
return path::new(string::temp_from_wstring(path)!, .allocator = allocator);
|
||||
return path::new(string::temp_from_wstring(path)!, allocator: allocator);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -165,11 +192,11 @@ fn Path! Path.append(self, String filename, Allocator allocator = allocator::hea
|
||||
return self.new_append(filename, allocator) @inline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the string to the current path.
|
||||
*
|
||||
* @param [in] filename
|
||||
**/
|
||||
<*
|
||||
Append the string to the current path.
|
||||
|
||||
@param [in] filename
|
||||
*>
|
||||
fn Path! Path.new_append(self, String filename, Allocator allocator = allocator::heap())
|
||||
{
|
||||
if (!self.path_string.len) return new(filename, allocator, self.env)!;
|
||||
@@ -226,9 +253,9 @@ fn Path! Path.absolute(self, Allocator allocator = allocator::heap()) @deprecate
|
||||
return self.new_absolute(allocator) @inline;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self.env == DEFAULT_PATH_ENV : "This method is only available on native paths"
|
||||
**/
|
||||
<*
|
||||
@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();
|
||||
@@ -283,14 +310,14 @@ fn String Path.dirname(self)
|
||||
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`
|
||||
**/
|
||||
<*
|
||||
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();
|
||||
@@ -352,6 +379,13 @@ fn usz! volume_name_len(String path, PathEnv path_env) @local
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Get the path of the parent. This does not allocate, but returns a slice
|
||||
of the path itself.
|
||||
|
||||
@return `The parent of the path as a non-allocated path`
|
||||
@return! PathResult.NO_PARENT `if this path does not have a parent`
|
||||
*>
|
||||
fn Path! Path.parent(self)
|
||||
{
|
||||
if (self.path_string.len == 1 && is_separator(self.path_string[0], self.env)) return PathResult.NO_PARENT?;
|
||||
@@ -509,18 +543,18 @@ fn String Path.root_directory(self)
|
||||
|
||||
def PathWalker = fn bool! (Path, bool is_dir, void*);
|
||||
|
||||
/*
|
||||
* Walk the path recursively. PathWalker is run on every file and
|
||||
* directory found. Return true to abort the walk.
|
||||
* @require self.env == DEFAULT_PATH_ENV : "This method is only available on native paths"
|
||||
*/
|
||||
<*
|
||||
Walk the path recursively. PathWalker is run on every file and
|
||||
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)
|
||||
{
|
||||
const PATH_MAX = 512;
|
||||
@stack_mem(PATH_MAX; Allocator allocator)
|
||||
{
|
||||
Path abs = self.new_absolute(allocator)!;
|
||||
PathList files = new_ls(abs, .allocator = allocator)!;
|
||||
PathList files = new_ls(abs, allocator: allocator)!;
|
||||
foreach (f : files)
|
||||
{
|
||||
if (f.str_view() == "." || f.str_view() == "..") continue;
|
||||
|
||||
@@ -47,28 +47,28 @@ macro bool @is_outstream(#expr)
|
||||
return $assignable(#expr, OutStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&out] ref
|
||||
* @require @is_instream(stream)
|
||||
**/
|
||||
<*
|
||||
@param [&out] ref
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro usz! read_any(stream, any ref)
|
||||
{
|
||||
return read_all(stream, ((char*)ref)[:ref.type.sizeof]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] ref "the object to write."
|
||||
* @require @is_outstream(stream)
|
||||
* @ensure return == ref.type.sizeof
|
||||
*/
|
||||
<*
|
||||
@param [&in] ref "the object to write."
|
||||
@require @is_outstream(stream)
|
||||
@ensure return == ref.type.sizeof
|
||||
*>
|
||||
macro usz! write_any(stream, any ref)
|
||||
{
|
||||
return write_all(stream, ((char*)ref)[:ref.type.sizeof]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require @is_instream(stream)
|
||||
*/
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro usz! read_all(stream, char[] buffer)
|
||||
{
|
||||
if (buffer.len == 0) return 0;
|
||||
@@ -77,9 +77,25 @@ macro usz! read_all(stream, char[] buffer)
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require @is_outstream(stream)
|
||||
*/
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro char[]! read_new_fully(stream, Allocator allocator = allocator::heap())
|
||||
{
|
||||
usz len = available(stream)!;
|
||||
char* data = allocator::malloc_try(allocator, len)!;
|
||||
defer catch allocator::free(allocator, data);
|
||||
usz read = 0;
|
||||
while (read < len)
|
||||
{
|
||||
read += stream.read(data[read:len - read])!;
|
||||
}
|
||||
return data[:len];
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_outstream(stream)
|
||||
*>
|
||||
macro usz! write_all(stream, char[] buffer)
|
||||
{
|
||||
if (buffer.len == 0) return 0;
|
||||
@@ -88,7 +104,12 @@ macro usz! write_all(stream, char[] buffer)
|
||||
return n;
|
||||
}
|
||||
|
||||
macro usz! @read_using_read_byte(&s, char[] buffer)
|
||||
macro usz! @read_using_read_byte(&s, char[] buffer) @deprecated
|
||||
{
|
||||
return read_using_read_byte(*s, buffer);
|
||||
}
|
||||
|
||||
macro usz! read_using_read_byte(s, char[] buffer)
|
||||
{
|
||||
usz len = 0;
|
||||
foreach (&cptr : buffer)
|
||||
@@ -105,17 +126,26 @@ macro usz! @read_using_read_byte(&s, char[] buffer)
|
||||
return len;
|
||||
}
|
||||
|
||||
macro void! @write_byte_using_write(&s, char c)
|
||||
macro void! write_byte_using_write(s, char c)
|
||||
{
|
||||
char[1] buff = { c };
|
||||
(*s).write(&buff)!;
|
||||
s.write(&buff)!;
|
||||
}
|
||||
|
||||
macro void! @write_byte_using_write(&s, char c) @deprecated
|
||||
{
|
||||
return write_byte_using_write(*s, c);
|
||||
}
|
||||
|
||||
macro char! @read_byte_using_read(&s)
|
||||
macro char! @read_byte_using_read(&s) @deprecated
|
||||
{
|
||||
return read_byte_using_read(*s);
|
||||
}
|
||||
|
||||
macro char! read_byte_using_read(s)
|
||||
{
|
||||
char[1] buffer;
|
||||
usz read = (*s).read(&buffer)!;
|
||||
usz read = s.read(&buffer)!;
|
||||
if (read != 1) return IoError.EOF?;
|
||||
return buffer[0];
|
||||
}
|
||||
@@ -123,13 +153,23 @@ macro char! @read_byte_using_read(&s)
|
||||
def ReadByteFn = fn char!();
|
||||
|
||||
|
||||
macro usz! @write_using_write_byte(&s, char[] bytes)
|
||||
macro usz! write_using_write_byte(s, char[] bytes)
|
||||
{
|
||||
foreach (c : bytes) s.write_byte(self, c)!;
|
||||
return bytes.len;
|
||||
}
|
||||
|
||||
macro void! @pushback_using_seek(&s)
|
||||
macro usz! @write_using_write_byte(&s, char[] bytes) @deprecated
|
||||
{
|
||||
return write_using_write_byte(*s, bytes);
|
||||
}
|
||||
|
||||
macro void! pushback_using_seek(s)
|
||||
{
|
||||
s.seek(-1, CURSOR)!;
|
||||
}
|
||||
|
||||
macro void! @pushback_using_seek(&s) @deprecated
|
||||
{
|
||||
s.seek(-1, CURSOR)!;
|
||||
}
|
||||
@@ -170,10 +210,10 @@ macro usz! copy_through_buffer(InStream in, OutStream dst, char[] buffer) @local
|
||||
|
||||
const char[*] MAX_VARS @private = { [2] = 3, [4] = 5, [8] = 10 };
|
||||
|
||||
/**
|
||||
* @require @is_instream(stream)
|
||||
* @require @typekind(x_ptr) == POINTER && $typeof(x_ptr).inner.kindof.is_int()
|
||||
**/
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
@require @typekind(x_ptr) == POINTER && $typeof(x_ptr).inner.kindof.is_int()
|
||||
*>
|
||||
macro usz! read_varint(stream, x_ptr)
|
||||
{
|
||||
var $Type = $typefrom($typeof(x_ptr).inner);
|
||||
@@ -207,10 +247,10 @@ macro usz! read_varint(stream, x_ptr)
|
||||
}
|
||||
return MathError.OVERFLOW?;
|
||||
}
|
||||
/**
|
||||
* @require @is_outstream(stream)
|
||||
* @require @typekind(x).is_int()
|
||||
**/
|
||||
<*
|
||||
@require @is_outstream(stream)
|
||||
@require @typekind(x).is_int()
|
||||
*>
|
||||
macro usz! write_varint(stream, x)
|
||||
{
|
||||
var $Type = $typeof(x);
|
||||
@@ -225,4 +265,205 @@ macro usz! write_varint(stream, x)
|
||||
}
|
||||
buffer[i] = (char)x;
|
||||
return write_all(stream, buffer[:i + 1]);
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro ushort! read_be_ushort(stream)
|
||||
{
|
||||
char hi_byte = stream.read_byte()!;
|
||||
char lo_byte = stream.read_byte()!;
|
||||
return (ushort)(hi_byte << 8 | lo_byte);
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro short! read_be_short(stream)
|
||||
{
|
||||
return read_be_ushort(stream);
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_outstream(stream)
|
||||
*>
|
||||
macro void! write_be_short(stream, ushort s)
|
||||
{
|
||||
stream.write_byte((char)(s >> 8))!;
|
||||
stream.write_byte((char)s)!;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro uint! read_be_uint(stream)
|
||||
{
|
||||
uint val = stream.read_byte()! << 24;
|
||||
val += stream.read_byte()! << 16;
|
||||
val += stream.read_byte()! << 8;
|
||||
return val + stream.read_byte()!;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro int! read_be_int(stream)
|
||||
{
|
||||
return read_be_uint(stream);
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_outstream(stream)
|
||||
*>
|
||||
macro void! write_be_int(stream, uint s)
|
||||
{
|
||||
stream.write_byte((char)(s >> 24))!;
|
||||
stream.write_byte((char)(s >> 16))!;
|
||||
stream.write_byte((char)(s >> 8))!;
|
||||
stream.write_byte((char)s)!;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro ulong! read_be_ulong(stream)
|
||||
{
|
||||
ulong val = (ulong)stream.read_byte()! << 56;
|
||||
val += (ulong)stream.read_byte()! << 48;
|
||||
val += (ulong)stream.read_byte()! << 40;
|
||||
val += (ulong)stream.read_byte()! << 32;
|
||||
val += (ulong)stream.read_byte()! << 24;
|
||||
val += (ulong)stream.read_byte()! << 16;
|
||||
val += (ulong)stream.read_byte()! << 8;
|
||||
return val + stream.read_byte()!;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro long! read_be_long(stream)
|
||||
{
|
||||
return read_be_ulong(stream);
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_outstream(stream)
|
||||
*>
|
||||
macro void! write_be_long(stream, ulong s)
|
||||
{
|
||||
stream.write_byte((char)(s >> 56))!;
|
||||
stream.write_byte((char)(s >> 48))!;
|
||||
stream.write_byte((char)(s >> 40))!;
|
||||
stream.write_byte((char)(s >> 32))!;
|
||||
stream.write_byte((char)(s >> 24))!;
|
||||
stream.write_byte((char)(s >> 16))!;
|
||||
stream.write_byte((char)(s >> 8))!;
|
||||
stream.write_byte((char)s)!;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro uint128! read_be_uint128(stream)
|
||||
{
|
||||
uint128 val = (uint128)stream.read_byte()! << 120;
|
||||
val += (uint128)stream.read_byte()! << 112;
|
||||
val += (uint128)stream.read_byte()! << 104;
|
||||
val += (uint128)stream.read_byte()! << 96;
|
||||
val += (uint128)stream.read_byte()! << 88;
|
||||
val += (uint128)stream.read_byte()! << 80;
|
||||
val += (uint128)stream.read_byte()! << 72;
|
||||
val += (uint128)stream.read_byte()! << 64;
|
||||
val += (uint128)stream.read_byte()! << 56;
|
||||
val += (uint128)stream.read_byte()! << 48;
|
||||
val += (uint128)stream.read_byte()! << 40;
|
||||
val += (uint128)stream.read_byte()! << 32;
|
||||
val += (uint128)stream.read_byte()! << 24;
|
||||
val += (uint128)stream.read_byte()! << 16;
|
||||
val += (uint128)stream.read_byte()! << 8;
|
||||
return val + stream.read_byte()!;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro int128! read_be_int128(stream)
|
||||
{
|
||||
return read_be_uint128(stream);
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_outstream(stream)
|
||||
*>
|
||||
macro void! write_be_int128(stream, uint128 s)
|
||||
{
|
||||
stream.write_byte((char)(s >> 120))!;
|
||||
stream.write_byte((char)(s >> 112))!;
|
||||
stream.write_byte((char)(s >> 104))!;
|
||||
stream.write_byte((char)(s >> 96))!;
|
||||
stream.write_byte((char)(s >> 88))!;
|
||||
stream.write_byte((char)(s >> 80))!;
|
||||
stream.write_byte((char)(s >> 72))!;
|
||||
stream.write_byte((char)(s >> 64))!;
|
||||
stream.write_byte((char)(s >> 56))!;
|
||||
stream.write_byte((char)(s >> 48))!;
|
||||
stream.write_byte((char)(s >> 40))!;
|
||||
stream.write_byte((char)(s >> 32))!;
|
||||
stream.write_byte((char)(s >> 24))!;
|
||||
stream.write_byte((char)(s >> 16))!;
|
||||
stream.write_byte((char)(s >> 8))!;
|
||||
stream.write_byte((char)s)!;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_outstream(stream)
|
||||
@require data.len < 256 "Data exceeded 255"
|
||||
*>
|
||||
macro usz! write_tiny_bytearray(stream, char[] data)
|
||||
{
|
||||
stream.write_byte((char)data.len)!;
|
||||
return stream.write(data) + 1;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro char[]! read_tiny_bytearray(stream, Allocator allocator)
|
||||
{
|
||||
int len = stream.read_byte()!;
|
||||
if (!len) return {};
|
||||
char[] data = allocator::alloc_array(allocator, char, len);
|
||||
io::read_all(stream, data)!;
|
||||
return data;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_outstream(stream)
|
||||
@require data.len < 0x1000 "Data exceeded 65535"
|
||||
*>
|
||||
macro usz! write_short_bytearray(stream, char[] data)
|
||||
{
|
||||
io::write_be_short(stream, (ushort)data.len)!;
|
||||
return stream.write(data) + 2;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro char[]! read_short_bytearray(stream, Allocator allocator)
|
||||
{
|
||||
int len = io::read_be_ushort(stream)!;
|
||||
if (!len) return {};
|
||||
char[] data = allocator::alloc_array(allocator, char, len);
|
||||
io::read_all(stream, data)!;
|
||||
return data;
|
||||
}
|
||||
|
||||
<*
|
||||
Wrap bytes for reading using io functions.
|
||||
*>
|
||||
fn ByteReader wrap_bytes(char[] bytes)
|
||||
{
|
||||
return { bytes, 0 };
|
||||
}
|
||||
@@ -8,12 +8,12 @@ struct ReadBuffer (InStream)
|
||||
usz write_idx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Buffer reads from a stream.
|
||||
* @param [inout] self
|
||||
* @require bytes.len > 0
|
||||
* @require self.bytes.len == 0 "Init may not run on already initialized data"
|
||||
**/
|
||||
<*
|
||||
Buffer reads from a stream.
|
||||
@param [inout] self
|
||||
@require bytes.len > 0
|
||||
@require self.bytes.len == 0 "Init may not run on already initialized data"
|
||||
*>
|
||||
fn ReadBuffer* ReadBuffer.init(&self, InStream wrapped_stream, char[] bytes)
|
||||
{
|
||||
*self = { .wrapped_stream = wrapped_stream, .bytes = bytes };
|
||||
@@ -68,12 +68,12 @@ struct WriteBuffer (OutStream)
|
||||
usz index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Buffer writes to a stream. Call `flush` when done writing to the buffer.
|
||||
* @param [inout] self
|
||||
* @require bytes.len > 0 "Non-empty buffer required"
|
||||
* @require self.bytes.len == 0 "Init may not run on already initialized data"
|
||||
**/
|
||||
<*
|
||||
Buffer writes to a stream. Call `flush` when done writing to the buffer.
|
||||
@param [inout] self
|
||||
@require bytes.len > 0 "Non-empty buffer required"
|
||||
@require self.bytes.len == 0 "Init may not run on already initialized data"
|
||||
*>
|
||||
fn WriteBuffer* WriteBuffer.init(&self, OutStream wrapped_stream, char[] bytes)
|
||||
{
|
||||
*self = { .wrapped_stream = wrapped_stream, .bytes = bytes };
|
||||
@@ -121,13 +121,16 @@ fn usz! WriteBuffer.write(&self, char[] bytes) @dynamic
|
||||
fn void! WriteBuffer.write_byte(&self, char c) @dynamic
|
||||
{
|
||||
usz n = self.bytes.len - self.index;
|
||||
if (n == 0) self.write_pending()!;
|
||||
self.bytes[0] = c;
|
||||
self.index = 1;
|
||||
if (n == 0)
|
||||
{
|
||||
self.write_pending()!;
|
||||
}
|
||||
self.bytes[self.index] = c;
|
||||
self.index += 1;
|
||||
}
|
||||
|
||||
fn void! WriteBuffer.write_pending(&self) @local
|
||||
{
|
||||
self.index -= self.wrapped_stream.write(self.bytes[:self.index])!;
|
||||
if (self.index != 0) return IoError.INCOMPLETE_WRITE?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,11 +11,11 @@ struct ByteBuffer (InStream, OutStream)
|
||||
bool has_last;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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."
|
||||
**/
|
||||
<*
|
||||
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 };
|
||||
@@ -29,10 +29,10 @@ fn ByteBuffer*! ByteBuffer.temp_init(&self, usz max_read, usz initial_capacity =
|
||||
return self.new_init(max_read, initial_capacity, allocator::temp());
|
||||
}
|
||||
|
||||
/**
|
||||
* @require buf.len > 0
|
||||
* @require self.bytes.len == 0 "Buffer already initialized."
|
||||
**/
|
||||
<*
|
||||
@require buf.len > 0
|
||||
@require self.bytes.len == 0 "Buffer already initialized."
|
||||
*>
|
||||
fn ByteBuffer*! ByteBuffer.init_with_buffer(&self, char[] buf)
|
||||
{
|
||||
*self = { .max_read = buf.len, .bytes = buf };
|
||||
@@ -93,9 +93,9 @@ fn char! ByteBuffer.read_byte(&self) @dynamic
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only the last byte of a successful read can be pushed back.
|
||||
*/
|
||||
<*
|
||||
Only the last byte of a successful read can be pushed back.
|
||||
*>
|
||||
fn void! ByteBuffer.pushback_byte(&self) @dynamic
|
||||
{
|
||||
if (!self.has_last) return IoError.EOF?;
|
||||
@@ -131,7 +131,7 @@ fn usz! ByteBuffer.available(&self) @inline @dynamic
|
||||
fn void! ByteBuffer.grow(&self, usz n)
|
||||
{
|
||||
n = math::next_power_of_2(n);
|
||||
char* p = allocator::realloc_aligned(self.allocator, self.bytes, n, .alignment = char.alignof)!;
|
||||
char* p = allocator::realloc_aligned(self.allocator, self.bytes, n, alignment: char.alignof)!;
|
||||
self.bytes = p[:n];
|
||||
}
|
||||
|
||||
|
||||
@@ -8,23 +8,23 @@ struct ByteWriter (OutStream)
|
||||
Allocator allocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] self
|
||||
* @param [&inout] allocator
|
||||
* @require self.bytes.len == 0 "Init may not run on already initialized data"
|
||||
* @ensure (bool)allocator, self.index == 0
|
||||
**/
|
||||
<*
|
||||
@param [&inout] self
|
||||
@param [&inout] allocator
|
||||
@require self.bytes.len == 0 "Init may not run on already initialized data"
|
||||
@ensure (bool)allocator, self.index == 0
|
||||
*>
|
||||
fn ByteWriter* ByteWriter.new_init(&self, Allocator allocator = allocator::heap())
|
||||
{
|
||||
*self = { .bytes = {}, .allocator = allocator };
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] self
|
||||
* @require self.bytes.len == 0 "Init may not run on already initialized data"
|
||||
* @ensure self.index == 0
|
||||
**/
|
||||
<*
|
||||
@param [&inout] self
|
||||
@require self.bytes.len == 0 "Init may not run on already initialized data"
|
||||
@ensure self.index == 0
|
||||
*>
|
||||
fn ByteWriter* ByteWriter.temp_init(&self)
|
||||
{
|
||||
return self.new_init(allocator::temp()) @inline;
|
||||
@@ -72,10 +72,10 @@ fn void! ByteWriter.write_byte(&self, char c) @dynamic
|
||||
self.bytes[self.index++] = c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] self
|
||||
* @param reader
|
||||
**/
|
||||
<*
|
||||
@param [&inout] self
|
||||
@param reader
|
||||
*>
|
||||
fn usz! ByteWriter.read_from(&self, InStream reader) @dynamic
|
||||
{
|
||||
usz start_index = self.index;
|
||||
|
||||
@@ -6,10 +6,10 @@ struct LimitReader (InStream)
|
||||
usz limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] wrapped_stream "The stream to read from"
|
||||
* @param limit "The max limit to read"
|
||||
**/
|
||||
<*
|
||||
@param [&inout] wrapped_stream "The stream to read from"
|
||||
@param limit "The max limit to read"
|
||||
*>
|
||||
fn LimitReader* LimitReader.init(&self, InStream wrapped_stream, usz limit)
|
||||
{
|
||||
*self = { .wrapped_stream = wrapped_stream, .limit = limit };
|
||||
|
||||
70
lib/std/io/stream/multireader.c3
Normal file
70
lib/std/io/stream/multireader.c3
Normal file
@@ -0,0 +1,70 @@
|
||||
module std::io;
|
||||
|
||||
/* MultiReader implements the InStream interface and provides a logical
|
||||
* concatenation of the provided readers. They are read sequentially. If all the
|
||||
* data has been read, IoError.EOF is returned.
|
||||
*/
|
||||
struct MultiReader (InStream)
|
||||
{
|
||||
InStream[] readers;
|
||||
usz index;
|
||||
Allocator allocator;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
@param [&inout] self
|
||||
@param [&inout] allocator
|
||||
@require self.readers.len == 0 "Init may not run on already initialized data"
|
||||
@ensure self.index == 0
|
||||
*>
|
||||
fn MultiReader* MultiReader.new_init(&self, InStream... readers, Allocator allocator = allocator::heap())
|
||||
{
|
||||
InStream []copy = allocator::new_array(allocator, InStream, readers.len);
|
||||
copy[..] = readers[..];
|
||||
*self = { .readers = copy, .allocator = allocator };
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] self
|
||||
@require self.readers.len == 0 "Init may not run on already initialized data"
|
||||
@ensure self.index == 0
|
||||
*>
|
||||
fn MultiReader* MultiReader.temp_init(&self, InStream... readers)
|
||||
{
|
||||
return self.new_init(...readers, allocator: allocator::temp());
|
||||
}
|
||||
|
||||
fn void MultiReader.free(&self)
|
||||
{
|
||||
if (!self.allocator) return;
|
||||
allocator::free(self.allocator, self.readers);
|
||||
*self = {};
|
||||
}
|
||||
|
||||
fn usz! MultiReader.read(&self, char[] bytes) @dynamic
|
||||
{
|
||||
InStream r = self.readers[self.index];
|
||||
usz! n = r.read(bytes);
|
||||
if (catch err = n)
|
||||
{
|
||||
case IoError.EOF:
|
||||
self.index++;
|
||||
if (self.index >= self.readers.len)
|
||||
{
|
||||
return IoError.EOF?;
|
||||
}
|
||||
return self.read(bytes);
|
||||
default:
|
||||
return err?;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
fn char! MultiReader.read_byte(&self) @dynamic
|
||||
{
|
||||
char[1] data;
|
||||
self.read(data[..])!;
|
||||
return data[0];
|
||||
}
|
||||
59
lib/std/io/stream/multiwriter.c3
Normal file
59
lib/std/io/stream/multiwriter.c3
Normal file
@@ -0,0 +1,59 @@
|
||||
module std::io;
|
||||
|
||||
/* MultiWriter implements the OutStream interface and duplicates any write
|
||||
* operation to all the wrapped writers.
|
||||
*/
|
||||
struct MultiWriter (OutStream)
|
||||
{
|
||||
OutStream[] writers;
|
||||
Allocator allocator;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] self
|
||||
@param [&inout] allocator
|
||||
@require writers.len > 0
|
||||
@require self.writers.len == 0 "Init may not run on already initialized data"
|
||||
*>
|
||||
fn MultiWriter* MultiWriter.new_init(&self, OutStream... writers, Allocator allocator = allocator::heap())
|
||||
{
|
||||
OutStream[] copy = allocator::new_array(allocator, OutStream, writers.len);
|
||||
copy[..] = writers[..];
|
||||
*self = { .writers = copy, .allocator = allocator };
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] self
|
||||
@require writers.len > 0
|
||||
@require self.writers.len == 0 "Init may not run on already initialized data"
|
||||
*>
|
||||
fn MultiWriter* MultiWriter.temp_init(&self, OutStream... writers)
|
||||
{
|
||||
return self.new_init(...writers, allocator: allocator::temp());
|
||||
}
|
||||
|
||||
fn void MultiWriter.free(&self)
|
||||
{
|
||||
if (!self.allocator) return;
|
||||
allocator::free(self.allocator, self.writers);
|
||||
*self = {};
|
||||
}
|
||||
|
||||
fn usz! MultiWriter.write(&self, char[] bytes) @dynamic
|
||||
{
|
||||
usz n;
|
||||
foreach (w : self.writers)
|
||||
{
|
||||
n = w.write(bytes)!;
|
||||
if (n != bytes.len) return IoError.INCOMPLETE_WRITE?;
|
||||
}
|
||||
return bytes.len;
|
||||
}
|
||||
|
||||
fn void! MultiWriter.write_byte(&self, char c) @dynamic
|
||||
{
|
||||
char[1] data;
|
||||
data[0] = c;
|
||||
self.write(data[..])!;
|
||||
}
|
||||
@@ -8,23 +8,23 @@ struct Scanner (InStream)
|
||||
usz read_idx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scanner provides a way to read delimited data (with newlines as the default).
|
||||
* The supplied buffer must be at least as large as the expected data length
|
||||
* including its pattern.
|
||||
*
|
||||
* @param [&in] stream "The stream to read data from."
|
||||
* @require buffer.len > 0 "Non-empty buffer required."
|
||||
**/
|
||||
<*
|
||||
Scanner provides a way to read delimited data (with newlines as the default).
|
||||
The supplied buffer must be at least as large as the expected data length
|
||||
including its pattern.
|
||||
|
||||
@param [&in] stream "The stream to read data from."
|
||||
@require buffer.len > 0 "Non-empty buffer required."
|
||||
*>
|
||||
fn void Scanner.init(&self, InStream stream, char[] buffer)
|
||||
{
|
||||
*self = { .wrapped_stream = stream, .buf = buffer };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return and clear any remaining unscanned data.
|
||||
**/
|
||||
<*
|
||||
Return and clear any remaining unscanned data.
|
||||
*>
|
||||
fn char[] Scanner.flush(&self) @dynamic
|
||||
{
|
||||
assert(self.read_idx >= self.pattern_idx);
|
||||
@@ -40,11 +40,11 @@ fn void! Scanner.close(&self) @dynamic
|
||||
if (&self.wrapped_stream.close) return self.wrapped_stream.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the stream for the next split character and return data up to the match.
|
||||
* @require pattern.len > 0 "Non-empty pattern required."
|
||||
* @require self.buf.len > pattern.len "Pattern too large."
|
||||
**/
|
||||
<*
|
||||
Scan the stream for the next split character and return data up to the match.
|
||||
@require pattern.len > 0 "Non-empty pattern required."
|
||||
@require self.buf.len > pattern.len "Pattern too large."
|
||||
*>
|
||||
fn char[]! Scanner.scan(&self, String pattern = "\n")
|
||||
{
|
||||
if (self.read_idx == 0)
|
||||
|
||||
42
lib/std/io/stream/teereader.c3
Normal file
42
lib/std/io/stream/teereader.c3
Normal file
@@ -0,0 +1,42 @@
|
||||
module std::io;
|
||||
|
||||
struct TeeReader (InStream)
|
||||
{
|
||||
InStream r;
|
||||
OutStream w;
|
||||
}
|
||||
|
||||
<* Returns a reader that implements InStream and that will write any data read
|
||||
from the wrapped reader r to the writer w. There is no internal buffering.
|
||||
|
||||
@param [&inout] r "Stream r to read from."
|
||||
@param [&inout] w "Stream w to write to what it reads from r."
|
||||
*>
|
||||
macro TeeReader tee_reader(InStream r, OutStream w) => { r, w };
|
||||
|
||||
<*
|
||||
@param [&inout] self
|
||||
@param [&inout] r "Stream r to read from."
|
||||
@param [&inout] w "Stream w to write to what it reads from r."
|
||||
*>
|
||||
fn TeeReader* TeeReader.init(&self, InStream r, OutStream w)
|
||||
{
|
||||
*self = tee_reader(r, w);
|
||||
return self;
|
||||
}
|
||||
|
||||
fn usz! TeeReader.read(&self, char[] bytes) @dynamic
|
||||
{
|
||||
usz nr, nw;
|
||||
nr = self.r.read(bytes)!;
|
||||
nw = self.w.write(bytes[:nr])!;
|
||||
if (nr != nw) return IoError.GENERAL_ERROR?;
|
||||
return nr;
|
||||
}
|
||||
|
||||
fn char! TeeReader.read_byte(&self) @dynamic
|
||||
{
|
||||
char[1] data;
|
||||
self.read(data[..])!;
|
||||
return data[0];
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module libc;
|
||||
|
||||
|
||||
// Constants need to be per os/arch
|
||||
const int EXIT_FAILURE = 1;
|
||||
const int EXIT_SUCCESS = 0;
|
||||
@@ -44,7 +43,8 @@ const CInt SIGINT = 2;
|
||||
const CInt SIGQUIT = 3;
|
||||
const CInt SIGILL = 4;
|
||||
const CInt SIGTRAP = 5;
|
||||
const CInt SIGABTR = 6;
|
||||
const CInt SIGABRT = 6;
|
||||
const CInt SIGABTR @deprecated("use SIGABRT") = SIGABRT;
|
||||
const CInt SIGBUS = BSD_FLAVOR_SIG ? 10 : 7; // Or Mips
|
||||
const CInt SIGFPE = 8;
|
||||
const CInt SIGKILL = 9;
|
||||
@@ -59,17 +59,11 @@ const CInt SIGTSTP = BSD_FLAVOR_SIG ? 18 : 20;
|
||||
const CInt SIGCONT = BSD_FLAVOR_SIG ? 19 : 18;
|
||||
const CInt SIGCHLD = BSD_FLAVOR_SIG ? 20 : 17;
|
||||
|
||||
const bool BSD_FLAVOR_SIG @local = env::OPENBSD || env::DARWIN || env::FREEBSD || env::NETBSD;
|
||||
const bool BSD_FLAVOR_SIG @local = env::DARWIN || env::BSD_FAMILY;
|
||||
|
||||
def Time_t = $typefrom(env::WIN32 ? long.typeid : CLong.typeid);
|
||||
def Off_t = $typefrom(env::WIN32 ? int.typeid : usz.typeid);
|
||||
|
||||
struct Timespec
|
||||
{
|
||||
Time_t tv_sec;
|
||||
CLong tv_nsec;
|
||||
}
|
||||
|
||||
module libc @if(env::LIBC);
|
||||
|
||||
extern fn void abort();
|
||||
@@ -115,11 +109,12 @@ extern fn ZString getenv(ZString name);
|
||||
extern fn ZString gets(char* buffer);
|
||||
extern fn Tm* gmtime(Time_t* timer);
|
||||
extern fn Tm* gmtime_r(Time_t *timer, Tm* buf) @if(!env::WIN32);
|
||||
extern fn CInt isatty(Fd fd) @if(!env::WIN32);
|
||||
extern fn CLong labs(CLong x);
|
||||
extern fn LongDivResult ldiv(CLong number, CLong denom);
|
||||
extern fn Tm* localtime(Time_t* timer);
|
||||
extern fn Tm* localtime_r(Time_t* timer, Tm* result) @if(!env::WIN32);
|
||||
extern fn void longjmp(JmpBuf* buffer, CInt value);
|
||||
extern fn void longjmp(JmpBuf* buffer, CInt value) @if(!env::NETBSD && !env::OPENBSD);
|
||||
extern fn void* malloc(usz size);
|
||||
extern fn void* memchr(void* str, CInt c, usz n);
|
||||
extern fn CInt memcmp(void* buf1, void* buf2, usz count);
|
||||
@@ -143,7 +138,7 @@ extern fn void rewind(CFile stream);
|
||||
extern fn CInt scanf(ZString format, ...);
|
||||
extern fn void setbuf(CFile stream, char* buffer);
|
||||
extern fn int setenv(ZString name, ZString value, CInt overwrite);
|
||||
extern fn CInt setjmp(JmpBuf* buffer) @if(!env::WIN32);
|
||||
extern fn CInt setjmp(JmpBuf* buffer) @if(!env::WIN32 && !env::NETBSD && !env::OPENBSD);
|
||||
extern fn void setvbuf(CFile stream, char* buf, CInt type, usz size);
|
||||
extern fn SignalFunction signal(CInt sig, SignalFunction function);
|
||||
extern fn CInt snprintf(char* buffer, usz size, ZString format, ...);
|
||||
@@ -203,23 +198,39 @@ macro CFile stdin() => __stdin;
|
||||
macro CFile stdout() => __stdout;
|
||||
macro CFile stderr() => __stderr;
|
||||
|
||||
module libc @if(env::DARWIN);
|
||||
module libc @if(env::NETBSD || env::OPENBSD);
|
||||
extern fn int fcntl(CInt socket, int cmd, ...);
|
||||
extern fn int _setjmp(void*);
|
||||
macro int setjmp(void* ptr) => _setjmp(ptr);
|
||||
extern fn int _longjmp(void*, int);
|
||||
macro usz longjmp(void* ptr, CInt i) => _longjmp(ptr, i);
|
||||
extern fn usz malloc_size(void* ptr);
|
||||
extern fn void* aligned_alloc(usz align, usz size);
|
||||
macro CFile stdin() { return fdopen(0, "r"); }
|
||||
macro CFile stdout() { return fdopen(1, "w"); }
|
||||
macro CFile stderr() { return fdopen(2, "w"); }
|
||||
|
||||
module libc @if(env::DARWIN || env::FREEBSD);
|
||||
extern CFile __stdinp;
|
||||
extern CFile __stdoutp;
|
||||
extern CFile __stderrp;
|
||||
extern fn usz malloc_size(void* ptr);
|
||||
extern fn usz malloc_size(void* ptr) @if(!env::FREEBSD);
|
||||
extern fn void* aligned_alloc(usz align, usz size);
|
||||
macro CFile stdin() => __stdinp;
|
||||
macro CFile stdout() => __stdoutp;
|
||||
macro CFile stderr() => __stderrp;
|
||||
|
||||
module libc @if(env::FREEBSD);
|
||||
extern fn usz malloc_usable_size(void* ptr);
|
||||
macro usz malloc_size(void* ptr) => malloc_usable_size(ptr);
|
||||
|
||||
module libc @if(env::WIN32);
|
||||
macro usz malloc_size(void* ptr) => _msize(ptr);
|
||||
macro CFile stdin() => __acrt_iob_func(STDIN_FD);
|
||||
macro CFile stdout() => __acrt_iob_func(STDOUT_FD);
|
||||
macro CFile stderr() => __acrt_iob_func(STDERR_FD);
|
||||
|
||||
module libc @if(env::LIBC && !env::WIN32 && !env::LINUX && !env::DARWIN);
|
||||
module libc @if(env::LIBC && !env::WIN32 && !env::LINUX && !env::DARWIN && !env::BSD_FAMILY);
|
||||
macro CFile stdin() { return (CFile*)(uptr)STDIN_FD; }
|
||||
macro CFile stdout() { return (CFile*)(uptr)STDOUT_FD; }
|
||||
macro CFile stderr() { return (CFile*)(uptr)STDERR_FD; }
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
module libc;
|
||||
import std::time;
|
||||
|
||||
/**
|
||||
* @require self >= 0
|
||||
**/
|
||||
<*
|
||||
Return a "timespec" from a duration.
|
||||
|
||||
@require self >= 0
|
||||
*>
|
||||
fn TimeSpec NanoDuration.to_timespec(self) @inline
|
||||
{
|
||||
CLong ns = (CLong)(self % 1000_000_000);
|
||||
@@ -11,9 +13,11 @@ fn TimeSpec NanoDuration.to_timespec(self) @inline
|
||||
return { .s = sec, .ns = ns };
|
||||
}
|
||||
|
||||
/**
|
||||
* @require self >= 0
|
||||
**/
|
||||
<*
|
||||
Convert a duration to a timespec.
|
||||
|
||||
@require self >= 0
|
||||
*>
|
||||
fn TimeSpec Duration.to_timespec(self) @inline
|
||||
{
|
||||
CLong ns = (CLong)(1000 * (self % time::SEC));
|
||||
|
||||
@@ -17,10 +17,10 @@ struct Stat
|
||||
Gid_t st_gid;
|
||||
Dev_t st_rdev;
|
||||
|
||||
Timespec st_atimespec; // time of last access
|
||||
Timespec st_mtimespec; // time of last data modification
|
||||
Timespec st_ctimespec; // time of last status change
|
||||
Timespec st_birthtimespec; // time of file creation(birth)
|
||||
TimeSpec st_atimespec; // time of last access
|
||||
TimeSpec st_mtimespec; // time of last data modification
|
||||
TimeSpec st_ctimespec; // time of last status change
|
||||
TimeSpec st_birthtimespec; // time of file creation(birth)
|
||||
Off_t st_size; // file size, in bytes
|
||||
Blkcnt_t st_blocks; // blocks allocated for file
|
||||
Blksize_t st_blocksize; // optimal blocksize for I/O
|
||||
|
||||
62
lib/std/libc/os/freebsd.c3
Normal file
62
lib/std/libc/os/freebsd.c3
Normal file
@@ -0,0 +1,62 @@
|
||||
module libc @if(env::FREEBSD);
|
||||
|
||||
// Checked for x86_64, this is notoriously incorrect when comparing with Rust code etc
|
||||
|
||||
def Blksize_t = $typefrom(env::X86_64 ? long.typeid : CInt.typeid);
|
||||
def Nlink_t = $typefrom(env::X86_64 ? ulong.typeid : CUInt.typeid);
|
||||
def Blkcnt_t = long;
|
||||
def Ino_t = ulong;
|
||||
def Dev_t = ulong;
|
||||
def Mode_t = uint;
|
||||
def Ino64_t = ulong;
|
||||
def Blkcnt64_t = long;
|
||||
|
||||
struct Stat @if(env::X86_64)
|
||||
{
|
||||
Dev_t st_dev;
|
||||
Ino_t st_ino;
|
||||
Nlink_t st_nlink;
|
||||
Mode_t st_mode;
|
||||
Uid_t st_uid;
|
||||
Gid_t st_gid;
|
||||
CInt __pad0;
|
||||
Dev_t st_rdev;
|
||||
Off_t st_size;
|
||||
Blksize_t st_blksize;
|
||||
Blkcnt_t st_blocks;
|
||||
Time_t st_atime;
|
||||
long st_atime_nsec;
|
||||
Time_t st_mtime;
|
||||
long st_mtime_nsec;
|
||||
Time_t st_ctime;
|
||||
long st_ctime_nsec;
|
||||
long[3] __unused;
|
||||
}
|
||||
|
||||
struct Stat @if(!env::X86_64)
|
||||
{
|
||||
Dev_t st_dev;
|
||||
Ino_t st_ino;
|
||||
Mode_t st_mode;
|
||||
Nlink_t st_nlink;
|
||||
Uid_t st_uid;
|
||||
Gid_t st_gid;
|
||||
Dev_t st_rdev;
|
||||
CInt __pad1;
|
||||
Off_t st_size;
|
||||
Blksize_t st_blksize;
|
||||
CInt __pad2;
|
||||
Blkcnt_t st_blocks;
|
||||
Time_t st_atime;
|
||||
long st_atime_nsec;
|
||||
Time_t st_mtime;
|
||||
long st_mtime_nsec;
|
||||
Time_t st_ctime;
|
||||
long st_ctime_nsec;
|
||||
CInt[2] __unused;
|
||||
}
|
||||
|
||||
extern fn CInt stat(ZString path, Stat* stat);
|
||||
|
||||
extern fn CInt get_nprocs();
|
||||
extern fn CInt get_nprocs_conf();
|
||||
@@ -11,6 +11,7 @@ const int RTLD_LAZY = 0x1;
|
||||
const int RTLD_NOW = 0x2;
|
||||
const int RTLD_LOCAL = 0x4;
|
||||
const int RTLD_GLOBAL = 0x8;
|
||||
const int RTLD_NODELETE = 0x1000;
|
||||
|
||||
def Pid_t = int;
|
||||
def Uid_t = uint;
|
||||
@@ -28,13 +29,13 @@ def SigActionFunction = fn void(CInt, void*, void*);
|
||||
struct Sigaction
|
||||
{
|
||||
union
|
||||
{
|
||||
SignalFunction sa_handler;
|
||||
SigActionFunction sa_sigaction;
|
||||
}
|
||||
CInt sa_flags @if(env::FREEBSD);
|
||||
{
|
||||
SignalFunction sa_handler;
|
||||
SigActionFunction sa_sigaction;
|
||||
}
|
||||
CInt sa_flags @if(env::BSD_FAMILY);
|
||||
Sigset_t sa_mask; // 128
|
||||
CInt sa_flags @if(!env::FREEBSD);
|
||||
CInt sa_flags @if(!env::BSD_FAMILY);
|
||||
void* sa_restorer @if(env::LINUX);
|
||||
}
|
||||
|
||||
@@ -61,17 +62,18 @@ module libc::termios @if(env::LIBC &&& env::POSIX);
|
||||
distinct Cc = char;
|
||||
distinct Speed = CUInt;
|
||||
distinct Tcflags = CUInt;
|
||||
distinct Tcactions = CInt;
|
||||
|
||||
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 Tcactions TCOOFF = 0;
|
||||
const Tcactions TCOON = 1;
|
||||
const Tcactions TCIOFF = 2;
|
||||
const Tcactions TCION = 3;
|
||||
const Tcactions TCIFLUSH = 0;
|
||||
const Tcactions TCOFLUSH = 1;
|
||||
const Tcactions TCIOFLUSH = 2;
|
||||
const Tcactions TCSANOW = 0;
|
||||
const Tcactions TCSADRAIN = 1;
|
||||
const Tcactions TCSAFLUSH = 2;
|
||||
const Speed B0 = 0000000;
|
||||
const Speed B50 = 0000001;
|
||||
const Speed B75 = 0000002;
|
||||
@@ -104,72 +106,72 @@ 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;
|
||||
const Tcflags VINTR = 0;
|
||||
const Tcflags VQUIT = 1;
|
||||
const Tcflags VERASE = 2;
|
||||
const Tcflags VKILL = 3;
|
||||
const Tcflags VEOF = 4;
|
||||
const Tcflags VTIME = 5;
|
||||
const Tcflags VMIN = 6;
|
||||
const Tcflags VSWTC = 7;
|
||||
const Tcflags VSTART = 8;
|
||||
const Tcflags VSTOP = 9;
|
||||
const Tcflags VSUSP = 10;
|
||||
const Tcflags VEOL = 11;
|
||||
const Tcflags VREPRINT = 12;
|
||||
const Tcflags VDISCARD = 13;
|
||||
const Tcflags VWERASE = 14;
|
||||
const Tcflags VLNEXT = 15;
|
||||
const Tcflags VEOL2 = 16;
|
||||
const Tcflags ISIG = 0000001;
|
||||
const Tcflags ICANON = 0000002;
|
||||
const Tcflags ECHO = 0000010;
|
||||
const Tcflags ECHOE = 0000020;
|
||||
const Tcflags ECHOK = 0000040;
|
||||
const Tcflags ECHONL = 0000100;
|
||||
const Tcflags NOFLSH = 0000200;
|
||||
const Tcflags TOSTOP = 0000400;
|
||||
const Tcflags IEXTEN = 0100000;
|
||||
const Tcflags CSIZE = 0000060;
|
||||
const Tcflags CS5 = 0000000;
|
||||
const Tcflags CS6 = 0000020;
|
||||
const Tcflags CS7 = 0000040;
|
||||
const Tcflags CS8 = 0000060;
|
||||
const Tcflags CSTOPB = 0000100;
|
||||
const Tcflags CREAD = 0000200;
|
||||
const Tcflags PARENB = 0000400;
|
||||
const Tcflags PARODD = 0001000;
|
||||
const Tcflags HUPCL = 0002000;
|
||||
const Tcflags CLOCAL = 0004000;
|
||||
const Tcflags OPOST = 0000001;
|
||||
const Tcflags OLCUC = 0000002;
|
||||
const Tcflags ONLCR = 0000004;
|
||||
const Tcflags OCRNL = 0000010;
|
||||
const Tcflags ONOCR = 0000020;
|
||||
const Tcflags ONLRET = 0000040;
|
||||
const Tcflags OFILL = 0000100;
|
||||
const Tcflags OFDEL = 0000200;
|
||||
const Tcflags VTDLY = 0040000;
|
||||
const Tcflags VT0 = 0000000;
|
||||
const Tcflags VT1 = 0040000;
|
||||
const Tcflags IGNBRK = 0000001;
|
||||
const Tcflags BRKINT = 0000002;
|
||||
const Tcflags IGNPAR = 0000004;
|
||||
const Tcflags PARMRK = 0000010;
|
||||
const Tcflags INPCK = 0000020;
|
||||
const Tcflags ISTRIP = 0000040;
|
||||
const Tcflags INLCR = 0000100;
|
||||
const Tcflags IGNCR = 0000200;
|
||||
const Tcflags ICRNL = 0000400;
|
||||
const Tcflags IUCLC = 0001000;
|
||||
const Tcflags IXON = 0002000;
|
||||
const Tcflags IXANY = 0004000;
|
||||
const Tcflags IXOFF = 0010000;
|
||||
const Tcflags IMAXBEL = 0020000;
|
||||
const Tcflags IUTF8 = 0040000;
|
||||
|
||||
extern fn CInt tcgetattr(Fd fd, Termios* self);
|
||||
extern fn CInt tcsetattr(Fd fd, CInt optional_actions, Termios* self);
|
||||
extern fn CInt tcsetattr(Fd fd, Tcactions 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);
|
||||
@@ -181,13 +183,13 @@ 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;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ extern fn CInt _fseeki64(CFile, long, int); def fseek = _fseeki64;
|
||||
extern fn CLong _ftelli64(CFile); def ftell = _ftelli64;
|
||||
extern fn Errno _get_timezone(CLong *timezone);
|
||||
extern fn Tm* _gmtime64_s(Tm* buf, Time_t *timer);
|
||||
extern fn CInt _isatty(Fd fd); def isatty = _isatty;
|
||||
extern fn Tm* _localtime64_s(Tm* buf, Time_t *timer);
|
||||
extern fn Time_t _mkgmtime64(Tm* timeptr); def timegm = _mkgmtime64;
|
||||
extern fn Time_t _mktime64(Tm *timeptr); def mktime = _mktime64;
|
||||
|
||||
@@ -9,7 +9,7 @@ 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);
|
||||
fn int Termios.setAttr(Termios* self, Fd fd, Tcactions optional_actions) => tcsetattr(fd, optional_actions, self);
|
||||
|
||||
module libc::termios @if(!env::LIBC ||| !env::POSIX);
|
||||
|
||||
|
||||
1115
lib/std/math/bigint.c3
Normal file
1115
lib/std/math/bigint.c3
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user