mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 20:11:17 +00:00
Compare commits
923 Commits
v0.2.3.2-a
...
0.4godbolt
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82c3facb65 | ||
|
|
266dba466c | ||
|
|
379a5f670f | ||
|
|
8eaad81800 | ||
|
|
4baacc7d52 | ||
|
|
0de47d7c83 | ||
|
|
cfd21f8ca2 | ||
|
|
d0e8944c56 | ||
|
|
3e54d13b62 | ||
|
|
b30d130d92 | ||
|
|
4cf98dab93 | ||
|
|
ea1a5435bb | ||
|
|
275e3c6a09 | ||
|
|
9de02efa01 | ||
|
|
e0cfb39d79 | ||
|
|
d4259368a2 | ||
|
|
07b107ff5e | ||
|
|
b794c893d6 | ||
|
|
2e498a426e | ||
|
|
0778537540 | ||
|
|
ddd0497922 | ||
|
|
a877d4458c | ||
|
|
3a725d1348 | ||
|
|
353a072b75 | ||
|
|
8eddbfb708 | ||
|
|
021bcdcf21 | ||
|
|
bff7b492a2 | ||
|
|
4d0f73a8f5 | ||
|
|
6210522c75 | ||
|
|
13f808b552 | ||
|
|
dc30c8edc2 | ||
|
|
ee5ad170e0 | ||
|
|
172d561f07 | ||
|
|
3dd6675e1b | ||
|
|
db8c46d6c5 | ||
|
|
6fc38bbcb9 | ||
|
|
184cc19d36 | ||
|
|
e25c06a065 | ||
|
|
f2f514da74 | ||
|
|
619fa26c26 | ||
|
|
e8642d6797 | ||
|
|
d1c2fbd79f | ||
|
|
2a79e0f1cf | ||
|
|
c847650579 | ||
|
|
edd2f1c717 | ||
|
|
0a12686237 | ||
|
|
8059dc1539 | ||
|
|
809321e20c | ||
|
|
d14e778232 | ||
|
|
18c1b20ea0 | ||
|
|
ad7ee06635 | ||
|
|
0f80d985fa | ||
|
|
316af36723 | ||
|
|
9850adfa56 | ||
|
|
172ae8a3a5 | ||
|
|
07e3ced84e | ||
|
|
1c4ab48da7 | ||
|
|
0a826d588f | ||
|
|
93fa135d28 | ||
|
|
9c145996b0 | ||
|
|
02d073675a | ||
|
|
3beed8a0f1 | ||
|
|
5851de30ab | ||
|
|
5ee0d52ff1 | ||
|
|
cc87c77af3 | ||
|
|
24147a85f7 | ||
|
|
954521228e | ||
|
|
9fa634b78b | ||
|
|
48a35b3277 | ||
|
|
a041c53cdd | ||
|
|
9f068ce84d | ||
|
|
8b0df0ee11 | ||
|
|
1b667cbc93 | ||
|
|
7075f834dd | ||
|
|
e2b9a35dfe | ||
|
|
3cb94a2857 | ||
|
|
3237f87a09 | ||
|
|
1b27264f07 | ||
|
|
fb761b0cc5 | ||
|
|
4ffeada3c7 | ||
|
|
2607062cb6 | ||
|
|
7a2d73c690 | ||
|
|
d2a16961cf | ||
|
|
39dd3e40a6 | ||
|
|
1480b8f872 | ||
|
|
b94c647ead | ||
|
|
9b81623680 | ||
|
|
cfba19ab77 | ||
|
|
89de0a70d2 | ||
|
|
33cc2d889b | ||
|
|
ab1c025c05 | ||
|
|
7008dab113 | ||
|
|
300f4d38ab | ||
|
|
9eee250b10 | ||
|
|
a4231823df | ||
|
|
d1626ada6f | ||
|
|
6ac99ed83c | ||
|
|
ff4c35fae1 | ||
|
|
549e1b6029 | ||
|
|
1035de3b36 | ||
|
|
306677300a | ||
|
|
f9923de7f9 | ||
|
|
488472ecbb | ||
|
|
ea3b32f68b | ||
|
|
a2911292d8 | ||
|
|
3449d2ea88 | ||
|
|
3372f36e9d | ||
|
|
07e4aab48b | ||
|
|
0dcad6f5cf | ||
|
|
0314f9534f | ||
|
|
f8208f946b | ||
|
|
b9dbefbe1b | ||
|
|
6188a8b5df | ||
|
|
9db845903e | ||
|
|
cc19168c7b | ||
|
|
1c83a484da | ||
|
|
bd8bff85d6 | ||
|
|
dd4edfb747 | ||
|
|
8ad8af861e | ||
|
|
feaf8b3b2c | ||
|
|
10272dbf38 | ||
|
|
2f255ac5c3 | ||
|
|
a7ce0f95e6 | ||
|
|
fc0cad2894 | ||
|
|
2a6339a25e | ||
|
|
f86aa136cb | ||
|
|
b175b9318a | ||
|
|
8f5676b488 | ||
|
|
b5a2b5c68a | ||
|
|
afa41f0c10 | ||
|
|
b6ff6bae8e | ||
|
|
e3416a1c40 | ||
|
|
d35d50555e | ||
|
|
34eac23e23 | ||
|
|
19963e4e19 | ||
|
|
62fbf4da47 | ||
|
|
6e8c69cd52 | ||
|
|
ea163636d3 | ||
|
|
3da9008fdc | ||
|
|
9061699adf | ||
|
|
d6a1cecb9d | ||
|
|
baa2e474b5 | ||
|
|
a0a5c940f1 | ||
|
|
0aa776d61b | ||
|
|
c26655a05a | ||
|
|
818396b6f3 | ||
|
|
4519eebe4d | ||
|
|
adc424ba1d | ||
|
|
b85d521dc1 | ||
|
|
8b099293a3 | ||
|
|
8d306ce64d | ||
|
|
df77b692d6 | ||
|
|
b7e19b75d0 | ||
|
|
61e26d8188 | ||
|
|
03cd56e46b | ||
|
|
8184fba34b | ||
|
|
3cfef690d3 | ||
|
|
5e457be605 | ||
|
|
3b49b87784 | ||
|
|
2fa3ae7bc9 | ||
|
|
1548cd06ef | ||
|
|
18de9a146b | ||
|
|
ddd8be0f38 | ||
|
|
74d868d113 | ||
|
|
b3f15a867c | ||
|
|
0a0cc4b5df | ||
|
|
f313e90e28 | ||
|
|
a6b9405e21 | ||
|
|
508cc8f29f | ||
|
|
1843870dfd | ||
|
|
926dbfc535 | ||
|
|
cc189a8166 | ||
|
|
c89dbba6d1 | ||
|
|
9e4203e267 | ||
|
|
6cef75b608 | ||
|
|
3c4796d65a | ||
|
|
e0252a6d7a | ||
|
|
505543e9b4 | ||
|
|
86e085e0c0 | ||
|
|
4a102698b2 | ||
|
|
5a65a57e42 | ||
|
|
38b44a7265 | ||
|
|
0f7d21330a | ||
|
|
dce171670f | ||
|
|
6b928c7a3d | ||
|
|
6407eb47a4 | ||
|
|
3b3dd334e0 | ||
|
|
3b3773663a | ||
|
|
0f4d20f168 | ||
|
|
1d8e341572 | ||
|
|
f9b6f1da0e | ||
|
|
6a3219ad43 | ||
|
|
f916aa9189 | ||
|
|
c665a431ad | ||
|
|
4ea81fe636 | ||
|
|
0afd55425a | ||
|
|
dbb759713f | ||
|
|
61cc8163f9 | ||
|
|
adf84e38d0 | ||
|
|
ae4658933a | ||
|
|
539d733ceb | ||
|
|
124a18a486 | ||
|
|
3046a9f0c2 | ||
|
|
a77e0bf7b3 | ||
|
|
84582f86b6 | ||
|
|
3f5fb5da92 | ||
|
|
2fb85e5e95 | ||
|
|
e604d40fd3 | ||
|
|
77608e137e | ||
|
|
52d7e58c19 | ||
|
|
5ea15eb708 | ||
|
|
f89bf9ea2f | ||
|
|
4ddb9d9fbc | ||
|
|
1095f098ef | ||
|
|
9f63f77f22 | ||
|
|
9a08c9d821 | ||
|
|
445239b418 | ||
|
|
03b659373a | ||
|
|
4561bf5a85 | ||
|
|
6eb65d5b37 | ||
|
|
6a73c8e90e | ||
|
|
5c7d859fdb | ||
|
|
a95710c93f | ||
|
|
39801a304d | ||
|
|
a22ebbb0ef | ||
|
|
f37f779e5a | ||
|
|
b508a43f8f | ||
|
|
c9e1e2d763 | ||
|
|
1e18e576c7 | ||
|
|
5151586450 | ||
|
|
e09628b664 | ||
|
|
92507ee388 | ||
|
|
1ab304dc64 | ||
|
|
e284d49dd9 | ||
|
|
6da6288ad8 | ||
|
|
925e4e6e46 | ||
|
|
bd12ef0a53 | ||
|
|
2123e81e8e | ||
|
|
50b0958fb6 | ||
|
|
026861051a | ||
|
|
dc16f65c8e | ||
|
|
3298ff2e15 | ||
|
|
aded1cb736 | ||
|
|
da65de2d01 | ||
|
|
c9e40cfa37 | ||
|
|
43dc2d650c | ||
|
|
5b2b4e900f | ||
|
|
6c9de52de7 | ||
|
|
98f3decc1a | ||
|
|
c485a89940 | ||
|
|
73000680e5 | ||
|
|
009bbeb48f | ||
|
|
38be3d57dd | ||
|
|
ad48770977 | ||
|
|
8390655d79 | ||
|
|
4330740cf8 | ||
|
|
4a99190f96 | ||
|
|
f8a505754d | ||
|
|
23a78a9ae5 | ||
|
|
bf222557fb | ||
|
|
aa33536ab1 | ||
|
|
7b1fc87566 | ||
|
|
a6f2eb22a9 | ||
|
|
ef7365224f | ||
|
|
f122d290f1 | ||
|
|
90f5c24d4c | ||
|
|
bcbcc3100f | ||
|
|
f6e798c283 | ||
|
|
f622b3bd4c | ||
|
|
fe54b84aed | ||
|
|
f46697bc54 | ||
|
|
e2b4a19c81 | ||
|
|
9e77fe7787 | ||
|
|
7223435df6 | ||
|
|
fe9ace713b | ||
|
|
03cdc8e3b1 | ||
|
|
352e09970c | ||
|
|
e8a8ac8bc1 | ||
|
|
334c004f1e | ||
|
|
aa534e2b3f | ||
|
|
2ee2bc3129 | ||
|
|
b88e5a8079 | ||
|
|
c339261d1e | ||
|
|
8008fb2c18 | ||
|
|
10219eccb2 | ||
|
|
1a510207e8 | ||
|
|
a21236d661 | ||
|
|
a749a4d265 | ||
|
|
b5afa98507 | ||
|
|
abf0f64ac0 | ||
|
|
dcf0b4c580 | ||
|
|
a9ed514fe5 | ||
|
|
8b0d409695 | ||
|
|
50d2a04c48 | ||
|
|
1864c69f31 | ||
|
|
af0174f360 | ||
|
|
2a4d43d7c7 | ||
|
|
13cb637cb4 | ||
|
|
de4bfe470e | ||
|
|
eaaa5362a5 | ||
|
|
1ea5625183 | ||
|
|
f7659776fc | ||
|
|
ed99e09c4e | ||
|
|
e9181a75e6 | ||
|
|
963b8f28ef | ||
|
|
5721fcc224 | ||
|
|
287de8f499 | ||
|
|
44dfeb621d | ||
|
|
eb87eb1987 | ||
|
|
c15fb7460c | ||
|
|
927ad2001f | ||
|
|
7647378e7c | ||
|
|
10b0b5f9c7 | ||
|
|
299ec1814b | ||
|
|
07700ed2c5 | ||
|
|
0ae586585a | ||
|
|
285299dcd5 | ||
|
|
2fefed5bda | ||
|
|
da1a45f718 | ||
|
|
bbef94b0fd | ||
|
|
c093f16fd0 | ||
|
|
5ff726d8d1 | ||
|
|
49eacb8824 | ||
|
|
450113d161 | ||
|
|
998c56533b | ||
|
|
73619817ba | ||
|
|
70f6ad1b27 | ||
|
|
e070bf22ee | ||
|
|
b086c85d9f | ||
|
|
66d87b25a3 | ||
|
|
d9e81c6035 | ||
|
|
98ae13c03d | ||
|
|
d9ea953b5c | ||
|
|
4cdfac092b | ||
|
|
1ef43cbc13 | ||
|
|
093bc2089a | ||
|
|
9b5e2b8578 | ||
|
|
2d377ada45 | ||
|
|
02374c6aab | ||
|
|
155c8862c9 | ||
|
|
f73b507ccb | ||
|
|
26e4662c3b | ||
|
|
ede224662c | ||
|
|
bd0e8f1ef1 | ||
|
|
e6a5f98606 | ||
|
|
e15dbd4907 | ||
|
|
ae7aa65f35 | ||
|
|
d13b7ac96a | ||
|
|
03fe2b575d | ||
|
|
f86ef8a743 | ||
|
|
7d58ce0dcb | ||
|
|
f8f249ee2c | ||
|
|
0adb15139f | ||
|
|
76ee384a4c | ||
|
|
b1ed066e55 | ||
|
|
160659c4e3 | ||
|
|
dfe3128b16 | ||
|
|
8f269a4f13 | ||
|
|
1ae251478b | ||
|
|
effec3a1f6 | ||
|
|
4d08fee30e | ||
|
|
5d9a7ab0a6 | ||
|
|
5e184f04e7 | ||
|
|
b2b1a3489a | ||
|
|
fc41179636 | ||
|
|
959c418e8b | ||
|
|
9424bba49f | ||
|
|
314369d069 | ||
|
|
ec3d77f4bd | ||
|
|
ab78663f3c | ||
|
|
348495b4c8 | ||
|
|
6523982f14 | ||
|
|
df8595cd64 | ||
|
|
febd11fa95 | ||
|
|
feba7b8ed2 | ||
|
|
3624c2a72c | ||
|
|
d5f965e137 | ||
|
|
70a429f832 | ||
|
|
7fa129932d | ||
|
|
73ac0b8ea0 | ||
|
|
407ed5a63d | ||
|
|
fa064276bc | ||
|
|
c84f82559c | ||
|
|
bb20a38cdb | ||
|
|
f010f6a926 | ||
|
|
ec1a5d97c9 | ||
|
|
870e716f59 | ||
|
|
d33ff212a7 | ||
|
|
ee533c5500 | ||
|
|
a281dbe812 | ||
|
|
1d39fc475f | ||
|
|
f5a1894876 | ||
|
|
db06f99445 | ||
|
|
05d4ec55f6 | ||
|
|
dcfcf460a5 | ||
|
|
eb86b83bd7 | ||
|
|
3d844b8722 | ||
|
|
6bd72c2ec4 | ||
|
|
4783946476 | ||
|
|
9f7ed00f04 | ||
|
|
f05ffc84d8 | ||
|
|
55f7046da8 | ||
|
|
c5b9b6c761 | ||
|
|
5029dc703e | ||
|
|
cbb731b42b | ||
|
|
258a6ba97a | ||
|
|
e1b5b0b60c | ||
|
|
58647043f4 | ||
|
|
379f9e60bf | ||
|
|
a4d4c27ca6 | ||
|
|
81bea9bad6 | ||
|
|
52f3948026 | ||
|
|
46c182f3d1 | ||
|
|
cc71b96c38 | ||
|
|
ad18d9ba48 | ||
|
|
4d5821408d | ||
|
|
48ee567f81 | ||
|
|
321c713687 | ||
|
|
be5c82cfa6 | ||
|
|
4fa4b2a631 | ||
|
|
9b14340a57 | ||
|
|
e7fad16d0f | ||
|
|
62e3b8063e | ||
|
|
c7f0f58e82 | ||
|
|
35549c21bc | ||
|
|
6220bda4a3 | ||
|
|
81a2474f75 | ||
|
|
28f2247602 | ||
|
|
1541354587 | ||
|
|
a66c0942f8 | ||
|
|
fd9d300b06 | ||
|
|
b60862ec7a | ||
|
|
c8166f6fdb | ||
|
|
4d27150952 | ||
|
|
d4aec525f5 | ||
|
|
ba9b203c52 | ||
|
|
7a797d0f28 | ||
|
|
755d6f93b7 | ||
|
|
7c2555bd3b | ||
|
|
469ec54282 | ||
|
|
1af41ee394 | ||
|
|
fa51402a16 | ||
|
|
0805750f52 | ||
|
|
03c627f646 | ||
|
|
3a09f71830 | ||
|
|
3d110850df | ||
|
|
b16e0b377e | ||
|
|
fa89ea7b79 | ||
|
|
a0a2e27127 | ||
|
|
e31967cc65 | ||
|
|
58f8e00376 | ||
|
|
581e2adbf0 | ||
|
|
e4e1541cd7 | ||
|
|
8eefce235f | ||
|
|
7cb03de16b | ||
|
|
4fbdb79b65 | ||
|
|
97de44b424 | ||
|
|
72c7da404e | ||
|
|
5adf09dd53 | ||
|
|
bdc7833b83 | ||
|
|
1a9a71f4c4 | ||
|
|
757ca3716e | ||
|
|
7a07b8ff13 | ||
|
|
bbc3792e7c | ||
|
|
a7110b52eb | ||
|
|
5d15ec23bb | ||
|
|
fffb8a1d0c | ||
|
|
54d6b1a4ec | ||
|
|
bb9c8fb93e | ||
|
|
b863ae2ec5 | ||
|
|
27a0e12979 | ||
|
|
dabe5769dd | ||
|
|
e82a7e7918 | ||
|
|
73fd9371eb | ||
|
|
4a296032ab | ||
|
|
5386b6fe50 | ||
|
|
c9ae0779e7 | ||
|
|
e33c81f85b | ||
|
|
a4e603442b | ||
|
|
6b1370ba76 | ||
|
|
52bcf4654a | ||
|
|
57e1084734 | ||
|
|
821b6a7ffa | ||
|
|
1858600449 | ||
|
|
656faa55bf | ||
|
|
b2a975945a | ||
|
|
f6ff3a0756 | ||
|
|
d95e0b4733 | ||
|
|
fd642e333b | ||
|
|
377f3eeb2e | ||
|
|
08c9400e02 | ||
|
|
afe466d7b3 | ||
|
|
ae51214c47 | ||
|
|
15586b3076 | ||
|
|
9a3e7fd34b | ||
|
|
9a69a13b04 | ||
|
|
0a17857737 | ||
|
|
31d151aa30 | ||
|
|
63d9853bd3 | ||
|
|
b72718ba2a | ||
|
|
213745fccd | ||
|
|
1a98fa0efe | ||
|
|
fc943a98ef | ||
|
|
7805fb8d1c | ||
|
|
d93c7090f6 | ||
|
|
44df6eb75b | ||
|
|
5cacc41925 | ||
|
|
571728a42e | ||
|
|
b1c78edc5e | ||
|
|
852c08e5fe | ||
|
|
7092a9f825 | ||
|
|
f513b6237f | ||
|
|
48fb342834 | ||
|
|
24e216e034 | ||
|
|
d4c1a08fcd | ||
|
|
ac7823cabb | ||
|
|
af9b99bd5a | ||
|
|
90dfc24491 | ||
|
|
aa337049ea | ||
|
|
92b4eeaa35 | ||
|
|
5ac35a49fd | ||
|
|
7a835418a7 | ||
|
|
1bf3bb51bf | ||
|
|
fd3591c8b7 | ||
|
|
cfcf5cbc16 | ||
|
|
9473adbc35 | ||
|
|
3eee9ddc06 | ||
|
|
ebd7611164 | ||
|
|
fba4df8f84 | ||
|
|
28c7db7414 | ||
|
|
167bb85a7e | ||
|
|
29918fd126 | ||
|
|
f881d195b0 | ||
|
|
ccda6dba40 | ||
|
|
31d0bb9684 | ||
|
|
1c46f7050c | ||
|
|
878bbed929 | ||
|
|
f93c20ca34 | ||
|
|
9866c7f9e9 | ||
|
|
00b880e35e | ||
|
|
398e19d727 | ||
|
|
046469843c | ||
|
|
3461f08a53 | ||
|
|
db5816edd6 | ||
|
|
6d2ab0c985 | ||
|
|
f966250185 | ||
|
|
cc8884d3d1 | ||
|
|
550bca79e9 | ||
|
|
272f134e78 | ||
|
|
a109efd14c | ||
|
|
b48ae91cbb | ||
|
|
14df5c2ed9 | ||
|
|
04695489b4 | ||
|
|
331f9b23f8 | ||
|
|
9886d381c0 | ||
|
|
12c17b62cf | ||
|
|
2698ba1a94 | ||
|
|
6f5f5feb97 | ||
|
|
64d883cb99 | ||
|
|
1adc8b8264 | ||
|
|
c02ce5ce2a | ||
|
|
e36e4b60e4 | ||
|
|
1d808be4b9 | ||
|
|
7065c28a08 | ||
|
|
da4df9d626 | ||
|
|
a7e4dda360 | ||
|
|
4471ccff13 | ||
|
|
cdff5c3e26 | ||
|
|
cc1bc58ed0 | ||
|
|
812bd8b3d0 | ||
|
|
7e0a29ef40 | ||
|
|
c1de3f059e | ||
|
|
62c1d2ddb5 | ||
|
|
b313bec69d | ||
|
|
036859c0c8 | ||
|
|
56a6e0b112 | ||
|
|
18f7f35e80 | ||
|
|
1d572f3e7c | ||
|
|
002ee006c1 | ||
|
|
8afbccd3fe | ||
|
|
6576725ed8 | ||
|
|
d3a053e049 | ||
|
|
4afec24434 | ||
|
|
29edd6e54e | ||
|
|
547d30eb1e | ||
|
|
6cf3c9f46b | ||
|
|
4beb7eff8f | ||
|
|
48a31cfa48 | ||
|
|
cd1138447e | ||
|
|
c29ad77cdb | ||
|
|
1c15ebe6d2 | ||
|
|
a68efec5e8 | ||
|
|
3f6b0646b3 | ||
|
|
28a8e17690 | ||
|
|
2a7d46844a | ||
|
|
92542ac1f9 | ||
|
|
59b41f8deb | ||
|
|
abfccb5576 | ||
|
|
ea5d7cd2e7 | ||
|
|
ca21b1daac | ||
|
|
9fdd66af42 | ||
|
|
d403912ec7 | ||
|
|
05f222616e | ||
|
|
253dbf3603 | ||
|
|
cfbfc29e84 | ||
|
|
bb020a1752 | ||
|
|
c8a614e43f | ||
|
|
bb28f6e61c | ||
|
|
b1d83e2ccd | ||
|
|
df41caabdd | ||
|
|
2f5d51c92c | ||
|
|
224390ce5a | ||
|
|
09d50ebf6c | ||
|
|
2d608a4d51 | ||
|
|
d511f150a7 | ||
|
|
f4dc4f64f2 | ||
|
|
6035cb4600 | ||
|
|
3d1eaad6b9 | ||
|
|
ca2bb505b6 | ||
|
|
8b4b4273cc | ||
|
|
7c91c56f3d | ||
|
|
5edafc5b2f | ||
|
|
dbb0dc302d | ||
|
|
e09e5c06d3 | ||
|
|
b0c55ff777 | ||
|
|
60832019bd | ||
|
|
42b5445225 | ||
|
|
9691d50a6f | ||
|
|
29a9769651 | ||
|
|
15e1db78a7 | ||
|
|
2f23d56a12 | ||
|
|
22ee082d00 | ||
|
|
212bc7d9af | ||
|
|
890c4bc435 | ||
|
|
7df7dd2933 | ||
|
|
ada8652209 | ||
|
|
bf8288ed1c | ||
|
|
9b6e4f9d11 | ||
|
|
ecdcd8f959 | ||
|
|
828724f593 | ||
|
|
f6eb20f725 | ||
|
|
ade4065480 | ||
|
|
b19cef4bc1 | ||
|
|
151cbfd706 | ||
|
|
b99db4be24 | ||
|
|
8743223dd6 | ||
|
|
2e2a1ca21a | ||
|
|
34bd5fa6da | ||
|
|
131bf5ed34 | ||
|
|
78134316b7 | ||
|
|
b31629c5e8 | ||
|
|
bbfc2fc8ab | ||
|
|
1940f6967c | ||
|
|
adf2b2e818 | ||
|
|
6a9b14d107 | ||
|
|
8b8a8d81db | ||
|
|
d1fadf6428 | ||
|
|
05d8e6c7b8 | ||
|
|
6789fab93c | ||
|
|
3490814d73 | ||
|
|
3799dbb082 | ||
|
|
60d7c8aa14 | ||
|
|
55598b2de8 | ||
|
|
4fe2a70ee1 | ||
|
|
ab56b2d047 | ||
|
|
1061b4e1dd | ||
|
|
e02362de0c | ||
|
|
c414459075 | ||
|
|
8f6fb8b7d7 | ||
|
|
fc296ea579 | ||
|
|
4258fe4d01 | ||
|
|
0565e87e5e | ||
|
|
6a48f81485 | ||
|
|
322d714305 | ||
|
|
069a2d40cb | ||
|
|
9b0dfe8ba3 | ||
|
|
2802b2b96d | ||
|
|
4f4bc80953 | ||
|
|
e45853c0cb | ||
|
|
f54a93890a | ||
|
|
6b4e4f6114 | ||
|
|
bf5683b41c | ||
|
|
73351e0aa9 | ||
|
|
df3e51c17e | ||
|
|
d11d80e896 | ||
|
|
7226bff6ea | ||
|
|
fd82f9685f | ||
|
|
c70d6716da | ||
|
|
e57c2710d9 | ||
|
|
d42193dbd6 | ||
|
|
9f9f24c3cc | ||
|
|
f3e326fcd9 | ||
|
|
ee32a5fd47 | ||
|
|
06917f2e65 | ||
|
|
ba66aaaf12 | ||
|
|
9639ad6a73 | ||
|
|
cf56825d26 | ||
|
|
e5bcb74822 | ||
|
|
8eb295bf5b | ||
|
|
bd6c3db413 | ||
|
|
21fd2c4485 | ||
|
|
bc8fbdb54a | ||
|
|
8922399c36 | ||
|
|
1e7ad2e241 | ||
|
|
4f212f7634 | ||
|
|
2c5ae858b8 | ||
|
|
ef95c1a630 | ||
|
|
b2be8349ed | ||
|
|
01e9086666 | ||
|
|
66b763193d | ||
|
|
36e40e59cb | ||
|
|
dce33ba5b5 | ||
|
|
ec291d4a9d | ||
|
|
e6ad9c324d | ||
|
|
3450016978 | ||
|
|
827499ca80 | ||
|
|
556be2ff7f | ||
|
|
8adb0faa06 | ||
|
|
b6450861d2 | ||
|
|
1e56948a22 | ||
|
|
e4e8abbc6c | ||
|
|
3f60443d66 | ||
|
|
f53dd95aa7 | ||
|
|
1743036104 | ||
|
|
a2fa61f58b | ||
|
|
4059d22315 | ||
|
|
05f0059b1b | ||
|
|
bc3b58b3e3 | ||
|
|
99ea0afcbf | ||
|
|
23461b179f | ||
|
|
d916f111b3 | ||
|
|
0f4a43717e | ||
|
|
c5a862f4d1 | ||
|
|
fb22a36aa9 | ||
|
|
889bc27800 | ||
|
|
eed5b7db54 | ||
|
|
5683fe3f8c | ||
|
|
684ad9e663 | ||
|
|
a6b29bccb7 | ||
|
|
71623a1874 | ||
|
|
16179d2513 | ||
|
|
fd8cd6a8e2 | ||
|
|
180b17b213 | ||
|
|
4764981708 | ||
|
|
3cf057ff95 | ||
|
|
ea3b50d039 | ||
|
|
041c096801 | ||
|
|
d942dfbc99 | ||
|
|
a176ae353b | ||
|
|
8a840746f6 | ||
|
|
869aa7ed76 | ||
|
|
1d5ff71b21 | ||
|
|
439ae3e468 | ||
|
|
8f2ae41ea5 | ||
|
|
fd1353f0f1 | ||
|
|
bbeed12600 | ||
|
|
0a7200ac24 | ||
|
|
f509c85514 | ||
|
|
06d3bc8915 | ||
|
|
a0be188902 | ||
|
|
0a9a014e4a | ||
|
|
85ee021585 | ||
|
|
f6de1f7b74 | ||
|
|
cf61f427d4 | ||
|
|
eb54e46569 | ||
|
|
cf0a04977a | ||
|
|
1a9b8095b6 | ||
|
|
012f258fa2 | ||
|
|
eefe782dd6 | ||
|
|
6f77fdf800 | ||
|
|
8f6dd64483 | ||
|
|
04c3efc3c3 | ||
|
|
4575e3dd8d | ||
|
|
b5b625e54a | ||
|
|
52cc4f8ba5 | ||
|
|
4e559e886c | ||
|
|
802398dd44 | ||
|
|
7c76fd02c6 | ||
|
|
dee0199f10 | ||
|
|
5cbc619f13 | ||
|
|
01902b523a | ||
|
|
157d1a959b | ||
|
|
4b3232ead6 | ||
|
|
cc8d266827 | ||
|
|
6d6d410d13 | ||
|
|
6b34a8c82e | ||
|
|
c39c7c3147 | ||
|
|
d360e97439 | ||
|
|
d305f9da49 | ||
|
|
572aafe8b9 | ||
|
|
c52629d60f | ||
|
|
497eef5328 | ||
|
|
680b077eb1 | ||
|
|
5ddbf50e83 | ||
|
|
908ac220c6 | ||
|
|
958db2f594 | ||
|
|
8aa00b015b | ||
|
|
379a66a14b | ||
|
|
0887d1e7dc | ||
|
|
4a6f587c9f | ||
|
|
e54679c01e | ||
|
|
4bc47a195b | ||
|
|
b003b05d5d | ||
|
|
b066c25432 | ||
|
|
bbda3a679f | ||
|
|
49efa4fafc | ||
|
|
4153cbe16d | ||
|
|
d6d4c0a912 | ||
|
|
06124ddb9f | ||
|
|
e1fc028694 | ||
|
|
a9c2e59f60 | ||
|
|
eb81c00ada | ||
|
|
26325f0fd2 | ||
|
|
910b2179f9 | ||
|
|
e20e6071c8 | ||
|
|
cae1933f0f | ||
|
|
bc2d789c2e | ||
|
|
2c802878bb | ||
|
|
c4595d3024 | ||
|
|
20ea6421cc | ||
|
|
f3c3636ac7 | ||
|
|
5a467f11ca | ||
|
|
eef5e1b498 | ||
|
|
60b17004a4 | ||
|
|
6ff05bae6d | ||
|
|
5d30189626 | ||
|
|
1bb9c2d249 | ||
|
|
e31d189837 | ||
|
|
aa7da00323 | ||
|
|
74fa81336f | ||
|
|
3242bcabc0 | ||
|
|
164a1ef59d | ||
|
|
17a03bc104 | ||
|
|
8d4d3b4d5b | ||
|
|
9a7b9bb7a4 | ||
|
|
1ba03f75c2 | ||
|
|
7595f2e17b | ||
|
|
90898d6ed6 | ||
|
|
aafa0d08a2 | ||
|
|
3c9761c946 | ||
|
|
05c9b7cb33 | ||
|
|
bb39cf20c0 | ||
|
|
dad8cd24b1 | ||
|
|
39b1b59011 | ||
|
|
685688f884 | ||
|
|
a1b69a3f50 | ||
|
|
f1f268df4a | ||
|
|
6febd8a143 | ||
|
|
8cc8c321a2 | ||
|
|
0358724451 | ||
|
|
86072ae21f | ||
|
|
0af448ee71 | ||
|
|
a4a6ea16ce | ||
|
|
4d4bbbdebc | ||
|
|
bfde58b9a5 | ||
|
|
7b04e7cf85 | ||
|
|
b7fa3549a3 | ||
|
|
974cd0acc5 | ||
|
|
b52b42d4da | ||
|
|
e2621617f1 | ||
|
|
d3fed67dbe | ||
|
|
91996e5973 | ||
|
|
3156fcb4aa | ||
|
|
8309d84fdb | ||
|
|
4efb433934 | ||
|
|
7142ce2f0c | ||
|
|
a436a9b069 | ||
|
|
fb56d380cc | ||
|
|
7bd76c973c | ||
|
|
42465039e9 | ||
|
|
df0b1df1df | ||
|
|
137b474f44 | ||
|
|
4662133893 | ||
|
|
15f902579b | ||
|
|
4e47f0b624 | ||
|
|
4f09b0c351 | ||
|
|
29e7af843a | ||
|
|
e2a3000c39 | ||
|
|
9942be54dc | ||
|
|
b87e14cba8 | ||
|
|
4ca7ba048b | ||
|
|
1b38e24a9f | ||
|
|
f284a1c575 | ||
|
|
86723540f3 | ||
|
|
aa239c6a87 | ||
|
|
09d9968e97 | ||
|
|
76e4eea4a8 | ||
|
|
43a4967987 | ||
|
|
ab5bacc78a | ||
|
|
af34eec61d | ||
|
|
f48661d35e | ||
|
|
5592d19152 | ||
|
|
55f676dd5c | ||
|
|
96571e2b65 | ||
|
|
4a3f5c4eb3 | ||
|
|
1db23defbc | ||
|
|
6c9b5fd30d | ||
|
|
8e93642535 | ||
|
|
d36fc9b19e | ||
|
|
5dea48101f | ||
|
|
b4df56db54 | ||
|
|
1b086e06f1 | ||
|
|
aca5adebd5 | ||
|
|
dc097fe130 | ||
|
|
ae371d105d | ||
|
|
de6ea0d1ae | ||
|
|
020eba720b | ||
|
|
0a4f35154a | ||
|
|
fb9be722bc | ||
|
|
e4c7dde30b | ||
|
|
1b103a3e22 | ||
|
|
b87b67ebbb | ||
|
|
17dcb742c6 | ||
|
|
b7e423adc2 | ||
|
|
0aef2810c8 | ||
|
|
2b2be6b491 | ||
|
|
49d13c23bb | ||
|
|
bcda6d71c9 | ||
|
|
c76e8e0713 | ||
|
|
29b3535460 | ||
|
|
fd1eafe5bf | ||
|
|
468921225d | ||
|
|
50853bb5de |
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
$ cat .gitattributes
|
||||
* text=auto
|
||||
*.c3 linguist-language=C
|
||||
*.c3t linguist-language=C
|
||||
536
.github/workflows/main.yml
vendored
536
.github/workflows/main.yml
vendored
@@ -6,66 +6,171 @@ on:
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
env:
|
||||
LLVM_RELEASE_VERSION: 16
|
||||
|
||||
jobs:
|
||||
|
||||
build-msvc:
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
# Don't abort runners if a single one fails
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [ Release, Debug ]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: cmd
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: CMake
|
||||
run: |
|
||||
cmake -B build -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
cmake --build build --config ${{ matrix.build_type }}
|
||||
|
||||
- name: Compile and run some examples
|
||||
run: |
|
||||
cd resources
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\hello_world_many.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\time.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\fannkuch-redux.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\contextfree\boolerr.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\load_world.c3
|
||||
|
||||
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe --debug-log run hello_world_win32
|
||||
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
cd resources/testproject
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe --debug-log build hello_world_win32_lib
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
python3.exe src/tester.py ..\build\${{ matrix.build_type }}\c3c.exe test_suite/
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-test unit -g1 --safe
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: c3-windows-${{ matrix.build_type }}
|
||||
path: build\${{ matrix.build_type }}\c3c.exe
|
||||
|
||||
build-msys2-mingw:
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
# Don't abort runners if a single one fails
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Release, Debug]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: MINGW64
|
||||
update: true
|
||||
install: git binutils mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-llvm mingw-w64-x86_64-polly mingw-w64-x86_64-python mingw-w64-x86_64-lld
|
||||
|
||||
install: git binutils 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-15.0.3-1-any.pkg.tar.zst
|
||||
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-15.0.3-1-any.pkg.tar.zst
|
||||
- name: CMake
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
cmake .. -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
cmake --build .
|
||||
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
cmake --build build
|
||||
|
||||
- name: Compile and run some examples
|
||||
run: |
|
||||
cd resources
|
||||
../build/c3c compile-run examples/hello_world_many.c3
|
||||
../build/c3c compile-run examples/time.c3
|
||||
../build/c3c compile-run examples/fannkuch-redux.c3
|
||||
../build/c3c compile-run examples/contextfree/boolerr.c3
|
||||
../build/c3c compile-run examples/load_world.c3
|
||||
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build
|
||||
../../build/c3c run --debug-log
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
./build/c3c vendor-fetch raylib
|
||||
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build hello_world_lib --debug-log
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
python3 src/tester.py ../build/c3c.exe test_suite/
|
||||
|
||||
|
||||
build-msys2-clang:
|
||||
runs-on: windows-latest
|
||||
#if: ${{ false }}
|
||||
strategy:
|
||||
# Don't abort runners if a single one fails
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Release, Debug]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: CLANG64
|
||||
update: true
|
||||
update: false
|
||||
install: git binutils mingw-w64-clang-x86_64-cmake mingw-w64-clang-x86_64-toolchain mingw-w64-clang-x86_64-python
|
||||
|
||||
- name: CMake
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
cmake .. -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
cmake --build .
|
||||
cmake -B build -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
cmake --build build
|
||||
|
||||
- name: Compile and run some examples
|
||||
run: |
|
||||
cd resources
|
||||
../build/c3c compile-run examples/hello_world_many.c3
|
||||
../build/c3c compile-run examples/time.c3
|
||||
../build/c3c compile-run examples/fannkuch-redux.c3
|
||||
../build/c3c compile-run examples/contextfree/boolerr.c3
|
||||
../build/c3c compile-run examples/load_world.c3
|
||||
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build
|
||||
../../build/c3c run --debug-log
|
||||
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build hello_world_lib --debug-log
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
@@ -75,27 +180,418 @@ jobs:
|
||||
build-linux:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
# Don't abort runners if a single one fails
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Release, Debug]
|
||||
llvm_version: [16, 17]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: (Ubuntu) Download LLVM
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install common deps
|
||||
run: |
|
||||
sudo apt-get install zlib1g zlib1g-dev clang-11 libllvm11 llvm-11 llvm-11-dev llvm-11-runtime liblld-11-dev liblld-11 python3
|
||||
sudo apt-get install zlib1g zlib1g-dev python3 ninja-build curl
|
||||
|
||||
- name: Install Clang ${{matrix.llvm_version}}
|
||||
run: |
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
if [[ "${{matrix.llvm_version}}" < 17 ]]; then
|
||||
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${{matrix.llvm_version}} main"
|
||||
else
|
||||
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main"
|
||||
fi
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev
|
||||
sudo apt-get install -y libmlir-${{matrix.llvm_version}} libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
|
||||
sudo apt-get install -y libpolly-${{matrix.llvm_version}}-dev
|
||||
- name: CMake
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
cmake .. -DLLVM_DIR=/usr/lib/llvm-11/cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
cmake --build .
|
||||
cmake -B build \
|
||||
-G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
|
||||
-DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} \
|
||||
-DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} \
|
||||
-DCMAKE_LINKER=lld-link-${{matrix.llvm_version}} \
|
||||
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
|
||||
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
|
||||
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
|
||||
-DC3_LLVM_VERSION=${{matrix.llvm_version}}
|
||||
cmake --build build
|
||||
|
||||
- name: Compile and run some examples
|
||||
run: |
|
||||
cd resources
|
||||
../build/c3c compile examples/base64.c3
|
||||
../build/c3c compile examples/binarydigits.c3
|
||||
../build/c3c compile examples/brainfk.c3
|
||||
../build/c3c compile examples/factorial_macro.c3
|
||||
../build/c3c compile examples/fasta.c3
|
||||
../build/c3c compile examples/gameoflife.c3
|
||||
../build/c3c compile examples/hash.c3
|
||||
../build/c3c compile examples/levenshtein.c3
|
||||
../build/c3c compile examples/load_world.c3
|
||||
../build/c3c compile examples/map.c3
|
||||
../build/c3c compile examples/mandelbrot.c3
|
||||
../build/c3c compile examples/plus_minus.c3
|
||||
../build/c3c compile examples/nbodies.c3
|
||||
../build/c3c compile examples/spectralnorm.c3
|
||||
../build/c3c compile examples/swap.c3
|
||||
../build/c3c compile examples/contextfree/boolerr.c3
|
||||
../build/c3c compile examples/contextfree/dynscope.c3
|
||||
../build/c3c compile examples/contextfree/guess_number.c3
|
||||
../build/c3c compile examples/contextfree/multi.c3
|
||||
../build/c3c compile examples/contextfree/cleanup.c3
|
||||
../build/c3c compile-run examples/hello_world_many.c3
|
||||
../build/c3c compile-run examples/time.c3
|
||||
../build/c3c compile-run examples/fannkuch-redux.c3
|
||||
../build/c3c compile-run examples/contextfree/boolerr.c3
|
||||
../build/c3c compile-run examples/load_world.c3
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
../build/c3c compile-test unit -g1 --safe
|
||||
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build
|
||||
../../build/c3c run --debug-log
|
||||
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log --forcelinker
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
python3 src/tester.py ../build/c3c test_suite/
|
||||
|
||||
- name: bundle_output
|
||||
if: matrix.llvm_version == 16
|
||||
run: |
|
||||
mkdir linux
|
||||
cp -r lib linux
|
||||
cp msvc_build_libraries.py linux
|
||||
cp build/c3c linux
|
||||
tar czf c3-linux-${{matrix.build_type}}.tar.gz linux
|
||||
|
||||
- name: upload artifacts
|
||||
if: matrix.llvm_version == 16
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: c3-linux-${{matrix.build_type}}
|
||||
path: c3-linux-${{matrix.build_type}}.tar.gz
|
||||
|
||||
build-linux-ubuntu20:
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
# Don't abort runners if a single one fails
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Release, Debug]
|
||||
llvm_version: [16]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install common deps
|
||||
run: |
|
||||
sudo apt-get install zlib1g zlib1g-dev python3 ninja-build curl
|
||||
|
||||
- name: Install Clang ${{matrix.llvm_version}}
|
||||
run: |
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
if [[ "${{matrix.llvm_version}}" < 17 ]]; then
|
||||
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${{matrix.llvm_version}} main"
|
||||
else
|
||||
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main"
|
||||
fi
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev
|
||||
sudo apt-get install -y libmlir-${{matrix.llvm_version}} libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
|
||||
sudo apt-get install -y libpolly-${{matrix.llvm_version}}-dev
|
||||
- name: CMake
|
||||
run: |
|
||||
cmake -B build \
|
||||
-G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
|
||||
-DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} \
|
||||
-DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} \
|
||||
-DCMAKE_LINKER=lld-link-${{matrix.llvm_version}} \
|
||||
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
|
||||
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
|
||||
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
|
||||
-DC3_LLVM_VERSION=${{matrix.llvm_version}}
|
||||
cmake --build build
|
||||
|
||||
- name: Compile and run some examples
|
||||
run: |
|
||||
cd resources
|
||||
../build/c3c compile examples/base64.c3
|
||||
../build/c3c compile examples/binarydigits.c3
|
||||
../build/c3c compile examples/brainfk.c3
|
||||
../build/c3c compile examples/factorial_macro.c3
|
||||
../build/c3c compile examples/fasta.c3
|
||||
../build/c3c compile examples/gameoflife.c3
|
||||
../build/c3c compile examples/hash.c3
|
||||
../build/c3c compile examples/levenshtein.c3
|
||||
../build/c3c compile examples/load_world.c3
|
||||
../build/c3c compile examples/map.c3
|
||||
../build/c3c compile examples/mandelbrot.c3
|
||||
../build/c3c compile examples/plus_minus.c3
|
||||
../build/c3c compile examples/nbodies.c3
|
||||
../build/c3c compile examples/spectralnorm.c3
|
||||
../build/c3c compile examples/swap.c3
|
||||
../build/c3c compile examples/contextfree/boolerr.c3
|
||||
../build/c3c compile examples/contextfree/dynscope.c3
|
||||
../build/c3c compile examples/contextfree/guess_number.c3
|
||||
../build/c3c compile examples/contextfree/multi.c3
|
||||
../build/c3c compile examples/contextfree/cleanup.c3
|
||||
../build/c3c compile-run examples/hello_world_many.c3
|
||||
../build/c3c compile-run examples/time.c3
|
||||
../build/c3c compile-run examples/fannkuch-redux.c3
|
||||
../build/c3c compile-run examples/contextfree/boolerr.c3
|
||||
../build/c3c compile-run examples/load_world.c3
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
../build/c3c compile-test unit -g1 --safe
|
||||
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log
|
||||
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log --forcelinker
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
python3 src/tester.py ../build/c3c test_suite/
|
||||
|
||||
- name: bundle_output
|
||||
if: matrix.llvm_version == 16
|
||||
run: |
|
||||
mkdir linux
|
||||
cp -r lib linux
|
||||
cp msvc_build_libraries.py linux
|
||||
cp build/c3c linux
|
||||
tar czf c3-ubuntu-20-${{matrix.build_type}}.tar.gz linux
|
||||
|
||||
- name: upload artifacts
|
||||
if: matrix.llvm_version == 16
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: c3-ubuntu-20-${{matrix.build_type}}
|
||||
path: c3-ubuntu-20-${{matrix.build_type}}.tar.gz
|
||||
|
||||
build-mac:
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
# Don't abort runners if a single one fails
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Release, Debug]
|
||||
llvm_version: [15, 16]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Download LLVM
|
||||
run: |
|
||||
brew install llvm@${{ matrix.llvm_version }} ninja curl
|
||||
echo "/usr/local/opt/llvm@${{ matrix.llvm_version }}/bin" >> $GITHUB_PATH
|
||||
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
||||
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
||||
|
||||
- name: CMake
|
||||
run: |
|
||||
cmake -B build -G Ninja -DC3_LLVM_VERSION=${{matrix.llvm_version}} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
cmake --build build
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
./build/c3c vendor-fetch raylib
|
||||
|
||||
- name: Compile and run some examples
|
||||
run: |
|
||||
cd resources
|
||||
../build/c3c compile-run examples/hello_world_many.c3
|
||||
../build/c3c compile-run examples/time.c3
|
||||
../build/c3c compile-run examples/fannkuch-redux.c3
|
||||
../build/c3c compile-run examples/contextfree/boolerr.c3
|
||||
../build/c3c compile-run examples/load_world.c3
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
../build/c3c compile-test unit -g1 --safe
|
||||
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log
|
||||
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log --forcelinker
|
||||
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build hello_world_lib --debug-log
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
python3 src/tester.py ../build/c3c test_suite/
|
||||
|
||||
- name: bundle_output
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION
|
||||
run: |
|
||||
mkdir macos
|
||||
cp -r lib macos
|
||||
cp msvc_build_libraries.py macos
|
||||
cp build/c3c macos
|
||||
zip -r c3-macos-${{matrix.build_type}}.zip macos
|
||||
|
||||
- name: upload artifacts
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: c3-macos-${{matrix.build_type}}
|
||||
path: c3-macos-${{matrix.build_type}}.zip
|
||||
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-msvc, build-linux, build-mac]
|
||||
if: github.ref == 'refs/heads/master'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: delete tag
|
||||
continue-on-error: true
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
github.rest.git.deleteRef({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: 'tags/latest',
|
||||
sha: context.sha
|
||||
})
|
||||
- name: create tag
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
github.rest.git.createRef({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: 'refs/tags/latest',
|
||||
sha: context.sha
|
||||
})
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
- 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: cp install_win_reqs.bat c3-windows-Release
|
||||
- run: cp install_win_reqs.bat c3-windows-Debug
|
||||
- run: zip -r c3-windows-Release.zip c3-windows-Release
|
||||
- run: zip -r c3-windows-Debug.zip c3-windows-Debug
|
||||
|
||||
- id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: latest
|
||||
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
|
||||
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -52,7 +52,6 @@ Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
/build/
|
||||
/cmake-build-debug/
|
||||
.idea/
|
||||
/resources/grammar.tab.c
|
||||
/resources/grammar.vcg
|
||||
@@ -60,3 +59,11 @@ dkms.conf
|
||||
/resources/y.tab.c
|
||||
/resources/y.tab.h
|
||||
/bin/
|
||||
|
||||
#visual studio files
|
||||
.vs/
|
||||
.vscode/
|
||||
out/
|
||||
|
||||
/cmake-build-debug/
|
||||
/cmake-build-release/
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "tilde-backend"]
|
||||
path = tilde-backend
|
||||
url = https://github.com/c3lang/tilde-backend
|
||||
407
CMakeLists.txt
407
CMakeLists.txt
@@ -1,173 +1,388 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(c3c)
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
SET(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL)
|
||||
SET(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
|
||||
# Grab the version
|
||||
file(READ "src/version.h" ver)
|
||||
if (NOT ${ver} MATCHES "COMPILER_VERSION \"([0-9]+.[0-9]+.[0-9]+)\"")
|
||||
message(FATAL_ERROR "Compiler version could not be parsed from version.h")
|
||||
endif()
|
||||
|
||||
#set(CMAKE_BUILD_TYPE Release)
|
||||
#set(CMAKE_CXX_FLAGS_RELEASE "-O3")
|
||||
# Set the project and version
|
||||
project(c3c VERSION ${CMAKE_MATCH_1})
|
||||
message("C3C version: ${CMAKE_PROJECT_VERSION}")
|
||||
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
# Enable fetching (for Windows)
|
||||
include(FetchContent)
|
||||
include(FeatureSummary)
|
||||
|
||||
|
||||
set(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL)
|
||||
set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
|
||||
|
||||
# We use C11 and C++17
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
if(MSVC)
|
||||
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 /EHsc")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Od /Zi /EHsc")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -fno-exceptions")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -gdwarf-3 -fno-exceptions")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3 -fno-exceptions")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3 -fno-exceptions")
|
||||
endif()
|
||||
#set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O1 -fsanitize=undefined")
|
||||
#set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O1 -fsanitize=undefined")
|
||||
#set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3 -fsanitize=undefined")
|
||||
#set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3 -fsanitize=undefined")
|
||||
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined")
|
||||
|
||||
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")
|
||||
|
||||
set(C3_USE_MIMALLOC OFF)
|
||||
if(C3_USE_MIMALLOC)
|
||||
option(MI_BUILD_TESTS OFF)
|
||||
option(MI_BUILD_SHARED OFF)
|
||||
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}
|
||||
)
|
||||
FetchContent_MakeAvailable(mimalloc)
|
||||
endif()
|
||||
if (NOT WIN32)
|
||||
find_package(CURL)
|
||||
endif()
|
||||
if (NOT C3_LLVM_VERSION STREQUAL "auto")
|
||||
if (${C3_LLVM_VERSION} VERSION_LESS 15 OR ${C3_LLVM_VERSION} VERSION_GREATER 17)
|
||||
message(FATAL_ERROR "LLVM ${C3_LLVM_VERSION} is not supported!")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(Git QUIET)
|
||||
if(C3_USE_TB AND GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
# Update submodules as needed
|
||||
option(GIT_SUBMODULE "Check submodules during build" ON)
|
||||
if(GIT_SUBMODULE)
|
||||
message(STATUS "Submodule update")
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
RESULT_VARIABLE GIT_SUBMOD_RESULT)
|
||||
if(NOT GIT_SUBMOD_RESULT EQUAL "0")
|
||||
message(FATAL_ERROR "git submodule update --init --recursive failed with ${GIT_SUBMOD_RESULT}, please checkout submodules")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||
if (C3_LLVM_VERSION STREQUAL "auto")
|
||||
set(C3_LLVM_VERSION "16")
|
||||
endif()
|
||||
FetchContent_Declare(
|
||||
LLVM_Windows
|
||||
URL https://github.com/c3lang/win-llvm/releases/download/llvm_16_0_2/llvm-16.0.2-windows-amd64-msvc17-libcmt.7z
|
||||
)
|
||||
FetchContent_Declare(
|
||||
LLVM_Windows_debug
|
||||
URL https://github.com/c3lang/win-llvm/releases/download/llvm_16_0_2/llvm-16.0.2-windows-amd64-msvc17-libcmt-dbg.7z
|
||||
)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
message("Loading Windows LLVM debug libraries, this may take a while...")
|
||||
FetchContent_MakeAvailable(LLVM_Windows_debug)
|
||||
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_windows_debug_SOURCE_DIR} ${CMAKE_SYSTEM_PREFIX_PATH})
|
||||
else()
|
||||
message("Loading Windows LLVM libraries, this may take a while...")
|
||||
FetchContent_MakeAvailable(LLVM_Windows)
|
||||
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_windows_SOURCE_DIR} ${CMAKE_SYSTEM_PREFIX_PATH})
|
||||
endif()
|
||||
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()
|
||||
|
||||
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
|
||||
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
|
||||
message(STATUS "Libraries located in: ${LLVM_LIBRARY_DIRS}")
|
||||
|
||||
include_directories(${LLVM_INCLUDE_DIRS})
|
||||
link_directories(${LLVM_LIBRARY_DIRS})
|
||||
add_definitions(${LLVM_DEFINITIONS})
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
AllTargetsAsmParsers
|
||||
AllTargetsCodeGens
|
||||
AllTargetsDescs
|
||||
AllTargetsDisassemblers
|
||||
AllTargetsInfos
|
||||
AllTargetsCodeGens
|
||||
Analysis
|
||||
AsmPrinter
|
||||
BitReader
|
||||
Core
|
||||
DebugInfoPDB
|
||||
InstCombine
|
||||
IrReader
|
||||
LibDriver
|
||||
Linker
|
||||
LTO
|
||||
MC
|
||||
MCDisassembler
|
||||
native
|
||||
nativecodegen
|
||||
Object
|
||||
Option
|
||||
ScalarOpts
|
||||
Support
|
||||
Target
|
||||
TransformUtils
|
||||
native
|
||||
nativecodegen
|
||||
AsmPrinter
|
||||
Linker
|
||||
LTO
|
||||
WindowsManifest
|
||||
DebugInfoPDB
|
||||
LibDriver
|
||||
Option
|
||||
IrReader
|
||||
)
|
||||
|
||||
WindowsDriver
|
||||
)
|
||||
|
||||
llvm_map_components_to_libnames(llvm_libs ${LLVM_LINK_COMPONENTS})
|
||||
file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/lib)
|
||||
file(COPY ${CMAKE_SOURCE_DIR}/lib DESTINATION ${CMAKE_BINARY_DIR})
|
||||
|
||||
file(COPY ${CMAKE_SOURCE_DIR}/resources/lib DESTINATION ${CMAKE_BINARY_DIR})
|
||||
|
||||
find_library(LLD_COFF NAMES lldCOFF.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_COMMON NAMES lldCommon.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_CORE NAMES lldCore.a liblldCore.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_WASM NAMES lldWasm.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_MINGW NAMES lldMinGW.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_ELF NAMES lldELF.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_DRIVER NAMES lldDriver.a liblldDriver.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_READER_WRITER NAMES lldReaderWriter.a liblldReaderWriter.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_MACHO NAMES lldMachO.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_YAML NAMES lldYAML.a liblldYAML.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
# 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)
|
||||
|
||||
set(lld_libs
|
||||
if (${LLVM_PACKAGE_VERSION} VERSION_GREATER_EQUAL 16)
|
||||
find_library(LLD_LOONG NAMES libLLVMLoongArchCodeGen.lib libLLVMLoongArchAsmParser.lib libLLVMLoongArchCodeGen.a libLLVMLoongArchAsmParser.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
set(lld_libs
|
||||
${LLD_COFF}
|
||||
${LLD_COMMON}
|
||||
${LLD_WASM}
|
||||
${LLD_MINGW}
|
||||
${LLD_ELF}
|
||||
${LLD_DRIVER}
|
||||
${LLD_READER_WRITER}
|
||||
${LLD_MACHO}
|
||||
${LLD_YAML}
|
||||
${LLD_CORE}
|
||||
)
|
||||
else()
|
||||
set(lld_libs
|
||||
${LLD_COFF}
|
||||
${LLD_COMMON}
|
||||
${LLD_WASM}
|
||||
${LLD_MINGW}
|
||||
${LLD_ELF}
|
||||
${LLD_MACHO}
|
||||
)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
set(lld_libs ${lld_libs} xar)
|
||||
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
|
||||
src/main.c
|
||||
src/build/builder.c
|
||||
src/build/build_options.c
|
||||
src/build/project_creation.c
|
||||
src/utils/errors.c
|
||||
src/utils/file_utils.c
|
||||
src/compiler/lexer.c
|
||||
src/compiler/tokens.c
|
||||
src/compiler/symtab.c
|
||||
src/compiler/parser.c
|
||||
src/compiler_tests/tests.c
|
||||
src/compiler_tests/benchmark.c
|
||||
src/utils/malloc.c
|
||||
src/compiler/compiler.c
|
||||
src/compiler/semantic_analyser.c
|
||||
src/compiler/source_file.c
|
||||
src/compiler/diagnostics.c
|
||||
src/compiler/ast.c
|
||||
src/compiler/bigint.c
|
||||
src/compiler/context.c
|
||||
src/compiler/sema_expr.c
|
||||
src/compiler/enums.h
|
||||
src/compiler/sema_casts.c
|
||||
src/compiler/target.c
|
||||
src/compiler/c_abi_internal.h
|
||||
src/compiler/codegen_general.c
|
||||
src/compiler/compiler.c
|
||||
src/compiler/compiler.h
|
||||
src/compiler/types.c
|
||||
src/compiler/module.c
|
||||
src/compiler/llvm_codegen.c
|
||||
src/utils/stringutils.c
|
||||
src/compiler/dwarf.h
|
||||
src/compiler/context.c
|
||||
src/compiler/copying.c
|
||||
src/compiler/llvm_codegen_stmt.c
|
||||
src/compiler/llvm_codegen_expr.c
|
||||
src/compiler/diagnostics.c
|
||||
src/compiler/dwarf.h
|
||||
src/compiler/enums.h
|
||||
src/compiler/float.c
|
||||
src/compiler/headers.c
|
||||
src/compiler/lexer.c
|
||||
src/compiler/libraries.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
|
||||
src/compiler/abi/c_abi_wasm.c
|
||||
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_module.c
|
||||
src/compiler/llvm_codegen_type.c
|
||||
src/compiler/llvm_codegen_expr.c
|
||||
src/compiler/llvm_codegen_function.c
|
||||
src/compiler/llvm_codegen_c_abi.c
|
||||
src/build/builder.c
|
||||
src/utils/toml.c src/build/project.c
|
||||
src/compiler/sema_name_resolution.c
|
||||
src/target_info/target_info.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
|
||||
src/compiler/parse_global.c
|
||||
src/compiler/parser.c
|
||||
src/compiler/parser_internal.h
|
||||
src/compiler/parse_stmt.c
|
||||
src/compiler/parse_global.c
|
||||
src/compiler/sema_passes.c
|
||||
src/compiler/sema_internal.h
|
||||
src/compiler/sema_casts.c
|
||||
src/compiler/sema_decls.c
|
||||
src/compiler/sema_types.c
|
||||
src/compiler/sema_expr.c
|
||||
src/compiler/sema_internal.h
|
||||
src/compiler/sema_name_resolution.c
|
||||
src/compiler/sema_errors.c
|
||||
src/compiler/sema_builtins.c
|
||||
src/compiler/sema_initializers.c
|
||||
src/compiler/semantic_analyser.c
|
||||
src/compiler/sema_passes.c
|
||||
src/compiler/sema_stmts.c
|
||||
src/compiler/number.c
|
||||
src/compiler/linker.c
|
||||
src/compiler/sema_types.c
|
||||
src/compiler/source_file.c
|
||||
src/compiler/symtab.c
|
||||
src/compiler/target.c
|
||||
src/compiler/sema_asm.c
|
||||
src/compiler_tests/benchmark.c
|
||||
src/compiler_tests/tests.c
|
||||
src/compiler/tokens.c
|
||||
src/compiler/types.c
|
||||
src/main.c
|
||||
src/utils/errors.c
|
||||
src/utils/file_utils.c
|
||||
src/utils/find_msvc.c
|
||||
src/utils/malloc.c
|
||||
src/utils/stringutils.c
|
||||
src/utils/taskqueue.c
|
||||
src/utils/json.c
|
||||
src/build/project.c
|
||||
src/utils/vmem.c
|
||||
src/utils/vmem.h
|
||||
src/utils/whereami.c
|
||||
src/utils/taskqueue.c
|
||||
src/compiler/llvm_codegen_c_abi_x86.c
|
||||
src/compiler/c_abi_internal.h
|
||||
src/compiler/llvm_codegen_c_abi_x64.c
|
||||
src/compiler/llvm_codegen_c_abi_win64.c
|
||||
src/compiler/llvm_codegen_c_abi_aarch64.c
|
||||
src/compiler/headers.c
|
||||
src/compiler/llvm_codegen_c_abi_riscv.c
|
||||
src/compiler/llvm_codegen_c_abi_wasm.c)
|
||||
src/utils/cpus.c
|
||||
src/utils/unzipper.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)
|
||||
|
||||
target_compile_options(c3c PRIVATE -Wall -Werror -Wno-unknown-pragmas -Wno-unused-result
|
||||
-Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
|
||||
|
||||
if (C3_USE_TB)
|
||||
file(GLOB tilde-sources
|
||||
tilde-backend/src/tb/*.c
|
||||
tilde-backend/src/tb/codegen/*.c
|
||||
tilde-backend/src/tb/bigint/*.c
|
||||
tilde-backend/src/tb/objects/*.c
|
||||
tilde-backend/src/tb/system/*.c
|
||||
tilde-backend/src/tb/debug/cv/*.c
|
||||
tilde-backend/src/tb/opt/*.c
|
||||
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
|
||||
src/compiler/tilde_codegen_value.c
|
||||
src/compiler/tilde_codegen_storeload.c
|
||||
src/compiler/tilde_codegen_expr.c
|
||||
src/compiler/tilde_codegen_stmt.c
|
||||
src/compiler/tilde_codegen_type.c
|
||||
src/compiler/tilde_codegen_abi.c
|
||||
src/compiler/tilde_codegen_storeload.c)
|
||||
|
||||
target_compile_definitions(c3c PUBLIC TB_AVAILABLE=1)
|
||||
target_link_libraries(c3c tilde-backend)
|
||||
add_library(tilde-backend STATIC ${tilde-sources})
|
||||
target_include_directories(tilde-backend PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/tilde-backend/src/" "${CMAKE_SOURCE_DIR}/tilde-backend/include")
|
||||
|
||||
target_include_directories(c3c PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/tilde-backend/include/")
|
||||
else()
|
||||
target_compile_definitions(c3c PUBLIC TB_AVAILABLE=0)
|
||||
endif()
|
||||
|
||||
|
||||
target_include_directories(c3c PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/src/")
|
||||
|
||||
|
||||
target_include_directories(c3c_wrappers PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/wrapper/src/")
|
||||
|
||||
|
||||
message(STATUS "Found LLD ${lld_libs}")
|
||||
target_include_directories(miniz PUBLIC
|
||||
"${CMAKE_SOURCE_DIR}/dependencies/miniz/")
|
||||
|
||||
target_link_libraries(c3c_wrappers ${llvm_libs} ${lld_libs})
|
||||
#target_link_libraries(c3c m ${llvm_libs} c3c_wrappers lldCommon lldCore lldCOFF lldWASM lldMinGW lldELF lldDriver lldReaderWriter lldMachO lldYAML)
|
||||
target_link_libraries(c3c m ${llvm_libs} c3c_wrappers ${lld_libs})
|
||||
target_link_libraries(c3c ${llvm_libs} miniz c3c_wrappers ${lld_libs})
|
||||
|
||||
if(C3_USE_MIMALLOC)
|
||||
target_link_libraries(c3c mimalloc-static)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
if (CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE_C_COMPILE_ID STREQUAL "GNU")
|
||||
target_link_options(c3c PRIVATE -pthread)
|
||||
target_link_libraries(c3c Winhttp.lib)
|
||||
endif()
|
||||
|
||||
if (CURL_FOUND)
|
||||
target_link_libraries(c3c ${CURL_LIBRARIES})
|
||||
target_include_directories(c3c PRIVATE ${CURL_INCLUDES})
|
||||
target_compile_definitions(c3c PUBLIC CURL_FOUND=1)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
target_compile_options(miniz PUBLIC /MT)
|
||||
if (C3_USE_TB)
|
||||
target_compile_options(tilde-backend PUBLIC /MT)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
else()
|
||||
message(STATUS "using gcc/clang warning switches")
|
||||
target_link_options(c3c PRIVATE -pthread)
|
||||
target_compile_options(c3c PRIVATE -pthread -Wall -Werror -Wno-unknown-pragmas -Wno-unused-result
|
||||
-Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
|
||||
if (WIN32)
|
||||
target_compile_definitions(c3c PRIVATE USE_PTHREAD=1)
|
||||
target_compile_options(c3c PRIVATE -mlong-double-64)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
install(TARGETS c3c DESTINATION bin)
|
||||
|
||||
feature_summary(WHAT ALL)
|
||||
|
||||
15
CMakeSettings.json
Normal file
15
CMakeSettings.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "build",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "Release",
|
||||
"inheritEnvironments": [ "msvc_x64_x64" ],
|
||||
"buildRoot": "${projectDir}\\out\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\out\\install\\${name}",
|
||||
"cmakeCommandArgs": "-DLLVM_DIR=C:\\llvm\\llvm\\build\\lib\\cmake\\llvm -DCMAKE_CXX_FLAGS:STRING=\"${CMAKE_CXX_FLAGS} /J\"",
|
||||
"buildCommandArgs": "-v",
|
||||
"ctestCommandArgs": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -116,7 +116,8 @@ more tests as you go along.
|
||||
### Don't bring in dependencies
|
||||
|
||||
External libraries has maintainability issues. Try to depend on as few libraries
|
||||
as possible. Currently c3c only depends on LLVM and libc.
|
||||
as possible. Currently, c3c only depends on LLVM and libc with an optional
|
||||
dependency on libcurl.
|
||||
|
||||
Do use rewrites of subsets of other libraries to bring in functionality, but avoid
|
||||
copying in libraries that needs to be updated separately.
|
||||
|
||||
20
LICENSE_STDLIB
Normal file
20
LICENSE_STDLIB
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2022 Christoffer Lernö and contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
345
README.md
345
README.md
@@ -1,10 +1,22 @@
|
||||
# C3 Language
|
||||
|
||||
C3 is a C-like language trying to be "an incremental improvement over C" rather than a whole new language.
|
||||
C3 owes a lot to the ideas of the [C2 language](http://c2lang.org): to iterate on top of C without trying to be a
|
||||
whole new language.
|
||||
C3 is a programming language that builds on the syntax and semantics of the C language,
|
||||
with the goal of evolving it while still retaining familiarity for C programmers.
|
||||
|
||||
C3 tries to be an alternative in the C/C++ niche: fast and close to the metal.
|
||||
It's an evolution, not a revolution: the C-like
|
||||
for programmers who like C.
|
||||
|
||||
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).
|
||||
|
||||
The manual for C3 can be found at [www.c3-lang.org](http://www.c3-lang.org).
|
||||
|
||||

|
||||
|
||||
Thanks to full ABI compatibility with C, it's possible to mix C and C3 in the same project with no effort. As a demonstration, vkQuake was compiled with a small portion of the code converted to C3 and compiled with the c3c compiler. (The fork can be found at https://github.com/c3lang/vkQuake)
|
||||
|
||||
### Design Principles
|
||||
- Procedural "get things done"-type of language.
|
||||
@@ -15,29 +27,97 @@ C3 tries to be an alternative in the C/C++ niche: fast and close to the metal.
|
||||
- Avoid "big ideas" & the "more is better" fallacy.
|
||||
- Introduce some higher level conveniences where the value is great.
|
||||
|
||||
C3 owes its inspiration to the [C2 language](http://c2lang.org): to iterate on top of C without trying to be a
|
||||
whole new language.
|
||||
|
||||
### Example code
|
||||
|
||||
Create a `main.c3` file with:
|
||||
```c++
|
||||
module hello_world;
|
||||
import std::io;
|
||||
The following code shows [generic modules](http://www.c3-lang.org/generics/) (more examples can be found at http://www.c3-lang.org/examples/).
|
||||
|
||||
func void main()
|
||||
```c++
|
||||
module stack <Type>;
|
||||
// Above: the parameterized type is applied to the entire module.
|
||||
|
||||
struct Stack
|
||||
{
|
||||
io::printf("Hello, world!\n");
|
||||
usz capacity;
|
||||
usz size;
|
||||
Type* elems;
|
||||
}
|
||||
|
||||
// The type methods offers dot syntax calls,
|
||||
// so this function can either be called
|
||||
// Stack.push(&my_stack, ...) or
|
||||
// my_stack.push(...)
|
||||
fn void Stack.push(Stack* this, Type element)
|
||||
{
|
||||
if (this.capacity == this.size)
|
||||
{
|
||||
this.capacity *= 2;
|
||||
if (this.capacity < 16) this.capacity = 16;
|
||||
this.elems = mem::realloc(this.elems, Type.sizeof * this.capacity);
|
||||
}
|
||||
this.elems[this.size++] = element;
|
||||
}
|
||||
|
||||
fn Type Stack.pop(Stack* this)
|
||||
{
|
||||
assert(this.size > 0);
|
||||
return this.elems[--this.size];
|
||||
}
|
||||
|
||||
fn bool Stack.empty(Stack* this)
|
||||
{
|
||||
return !this.size;
|
||||
}
|
||||
```
|
||||
|
||||
Make sure you have the standard libraries at either `../lib/std/` or `/lib/std/`.
|
||||
Testing it out:
|
||||
|
||||
Then run
|
||||
```sh
|
||||
c3c compile main.c3
|
||||
```cpp
|
||||
import stack;
|
||||
|
||||
// Define our new types, the first will implicitly create
|
||||
// a complete copy of the entire Stack module with "Type" set to "int"
|
||||
def IntStack = Stack<int>;
|
||||
// The second creates another copy with "Type" set to "double"
|
||||
def DoubleStack = Stack<double>;
|
||||
|
||||
// If we had added "define IntStack2 = Stack<int>"
|
||||
// no additional copy would have been made (since we already
|
||||
// have an parameterization of Stack<int>) so it would
|
||||
// be same as declaring IntStack2 an alias of IntStack
|
||||
|
||||
// Importing an external C function is straightforward
|
||||
// here is an example of importing libc's printf:
|
||||
extern fn int printf(char* format, ...);
|
||||
|
||||
fn void main()
|
||||
{
|
||||
IntStack stack;
|
||||
// Note that C3 uses zero initialization by default
|
||||
// so the above is equivalent to IntStack stack = {};
|
||||
|
||||
stack.push(1);
|
||||
// The above can also be written IntStack.push(&stack, 1);
|
||||
|
||||
stack.push(2);
|
||||
|
||||
// Prints pop: 2
|
||||
printf("pop: %d\n", stack.pop());
|
||||
// Prints pop: 1
|
||||
printf("pop: %d\n", stack.pop());
|
||||
|
||||
DoubleStack dstack;
|
||||
dstack.push(2.3);
|
||||
dstack.push(3.141);
|
||||
dstack.push(1.1235);
|
||||
// Prints pop: 1.123500
|
||||
printf("pop: %f\n", dstack.pop());
|
||||
}
|
||||
```
|
||||
|
||||
The generated binary will be called `a.out`.
|
||||
|
||||
### In what ways do C3 differ from C?
|
||||
### In what ways does C3 differ from C?
|
||||
|
||||
- No mandatory header files
|
||||
- New semantic macro system
|
||||
@@ -50,7 +130,6 @@ The generated binary will be called `a.out`.
|
||||
- Defer
|
||||
- Value methods
|
||||
- Associated enum data
|
||||
- Built-in hooks for convenient string handling
|
||||
- No preprocessor
|
||||
- Less undefined behaviour and runtime checks in "safe" mode
|
||||
- Limited operator overloading to enable userland dynamic arrays
|
||||
@@ -58,112 +137,60 @@ The generated binary will be called `a.out`.
|
||||
|
||||
### Current status
|
||||
|
||||
It's possible to try out the current C3 compiler in the browser: https://ide.judge0.com/ – this is courtesy of the
|
||||
developer of Judge0.
|
||||
The current version of the compiler is alpha release 0.4.
|
||||
|
||||
Design work is still being done in the design draft here: https://c3lang.github.io/c3docs/. If you have any suggestions, send a mail to [christoffer@aegik.com](mailto:christoffer@aegik.com), [file an issue](https://github.com/c3lang/c3c/issues) or discuss
|
||||
C3 on its dedicated Discord: https://discord.gg/qN76R87
|
||||
Design work on C3 is complete aside from fleshing out details, such as
|
||||
inline asm. As the standard library work progresses, changes and improvements
|
||||
to the language will happen continuously.
|
||||
Follow the issues [here](https://github.com/c3lang/c3c/issues).
|
||||
|
||||
The compiler should compile on Linux, Windows (under Mingw or MSYS2) and MacOS,
|
||||
but needs some install documentation for Windows.
|
||||
If you have suggestions on how to improve the language, either [file an issue](https://github.com/c3lang/c3c/issues)
|
||||
or discuss C3 on its dedicated Discord: [https://discord.gg/qN76R87](https://discord.gg/qN76R87).
|
||||
|
||||
Due to its ABI compatibility with C, it's possible to mix C and C3 in the same project.
|
||||
As a demonstration, vkQuake was compiled with a small portion of the code converted
|
||||
to C3 and compiled with the c3c compiler:
|
||||
The compiler is currently verified to compile on Linux, Windows and MacOS.
|
||||
|
||||

|
||||
|
||||
(The vkFork is at https://github.com/c3lang/vkQuake)
|
||||
|
||||
#### Todo / done
|
||||
|
||||
- [x] For/while/do
|
||||
- [x] `if`/ternary
|
||||
- [x] Structs
|
||||
- [x] Union
|
||||
- [x] Enums
|
||||
- [x] Value methods
|
||||
- [x] Compound literals
|
||||
- [x] Designated initalizers
|
||||
- [x] Slicing syntax
|
||||
- [x] Arrays and subarrays
|
||||
- [x] Modules
|
||||
- [x] `$unreachable`
|
||||
- [x] Compile time assert with `$assert`
|
||||
- [x] Compiler guiding `assert`
|
||||
- [x] C code calling by declaring methods `extern`
|
||||
- [x] Compile time variables
|
||||
- [x] Basic macros
|
||||
- [x] 4cc, 8cc, 2cc
|
||||
- [x] Enum type inference in switch/assignment
|
||||
- [x] Integer type inference
|
||||
- [x] Error type
|
||||
- [x] Failable error handling
|
||||
- [x] `try` for conditional execution
|
||||
- [x] `catch` for error handling
|
||||
- [x] Implicit unwrap after `catch`
|
||||
- [x] `sizeof`
|
||||
- [x] `typeof`
|
||||
- [x] 2s complement wrapping operators
|
||||
- [x] Labelled break / continue
|
||||
- [x] `nextcase` statement
|
||||
- [x] Expression blocks
|
||||
- [x] Do-without-while
|
||||
- [x] Foreach statement
|
||||
- [x] Templates
|
||||
- [x] Distinct types
|
||||
- [x] Built-in linking
|
||||
- [x] CT only macros evaluating to constants
|
||||
- [x] range initializers e.g. `{ [1..2] = 2 }`
|
||||
- [x] Trailing body macros e.g. `@foo(1, 100; int a) { bar(a); };`
|
||||
- [x] Complex macros
|
||||
- [x] CT type constants
|
||||
- [ ] Anonymous structs
|
||||
- [ ] Complete C ABI conformance *in progress*
|
||||
- [ ] Debug info *in progress*
|
||||
- [ ] Virtual type *in progress*
|
||||
- [ ] Enum associated data support
|
||||
- [ ] Windows support *in progress*
|
||||
- [ ] All attributes *in progress*
|
||||
- [ ] Associative array literals
|
||||
- [ ] Reflection methods
|
||||
- [ ] LTO/ThinLTO setup
|
||||
- [ ] `global` / `shared` for globals
|
||||
- [ ] Escape macros
|
||||
- [ ] Implicit capturing macros
|
||||
- [ ] Subarray initializers
|
||||
- [ ] Bitstructs
|
||||
- [ ] `asm` section
|
||||
- [ ] `$switch`
|
||||
- [ ] `$for`
|
||||
- [ ] Pre-post conditions
|
||||
- [ ] Stdlib inclusion
|
||||
- [ ] String functions
|
||||
- [ ] Simd vector types
|
||||
|
||||
#### What can you help with?
|
||||
|
||||
- If you wish to contribute with ideas, please file issues on the c3docs: https://github.com/c3lang/c3docs instead of the compiler.
|
||||
- Discuss the language on discord to help iron out syntax.
|
||||
- If you wish to contribute with ideas, please file issues or discuss on Discord.
|
||||
- Interested in contributing to the stdlib? Please get in touch on Discord.
|
||||
- Are you a Windows dev? Please help make the compiler work on Windows!
|
||||
- Install instructions for other Linux and Unix variants are appreciated.
|
||||
- Compilation instructions for other Linux and Unix variants are appreciated.
|
||||
- Would you like to contribute bindings to some library? It would be nice to have support for SDL, Raylib and more.
|
||||
- Build something with C3 and show it off and give feedback. The language is still open for significant tweaks.
|
||||
- Start work on the C -> C3 converter which takes C code and does a "best effort" to translate it to C3. The first version only needs to work on C headers.
|
||||
- Do you have some specific area you have deep knowledge of and could help make C3 even better at doing? File or comment on issues.
|
||||
|
||||
#### Installing on Ubuntu 20.10
|
||||
### Installing
|
||||
|
||||
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 11: `sudo apt-get install clang-11 zlib1g zlib1g-dev libllvm11 llvm-11 llvm-11-dev llvm-11-runtime liblld-11-dev liblld-11`
|
||||
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 for debug: `cmake -DLLVM_DIR=/usr/lib/llvm-11/cmake -DCMAKE_BUILD_TYPE=Debug ..`
|
||||
9. Build: `cmake --build .`
|
||||
#### Installing on Windows with precompiled binaries
|
||||
1. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest/c3-windows.zip](https://github.com/c3lang/c3c/releases/download/latest/c3-windows.zip)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-windows-debug.zip))
|
||||
2. Unzip exe and standard lib.
|
||||
3. If you don't have Visual Studio 17 installed you can either do so, or run the `msvc_build_libraries.py` Python script which will download the necessary files to compile on Windows.
|
||||
4. Run `c3c.exe`.
|
||||
|
||||
You should now have a `c3c` executable.
|
||||
#### Installing on Debian with precompiled binaries
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest/c3-linux.tar.gz](https://github.com/c3lang/c3c/releases/download/latest/c3-linux.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-linux-debug.tar.gz))
|
||||
2. Unpack executable and standard lib.
|
||||
3. Run `./c3c`.
|
||||
|
||||
You can try it out by running some sample code: `./c3c compile ../resources/examples/hash.c3`
|
||||
#### Installing on Mac 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))
|
||||
3. Unzip executable and standard lib.
|
||||
4. Run `./c3c`.
|
||||
|
||||
#### Installing on Arch Linux
|
||||
There is an AUR package for the c3c compiler : [c3c-git](https://aur.archlinux.org/packages/c3c-git)
|
||||
You can use your AUR package manager or clone it manually:
|
||||
```sh
|
||||
git clone https://aur.archlinux.org/c3c-git.git
|
||||
cd c3c-git
|
||||
makepkg -si
|
||||
```
|
||||
|
||||
#### Building via Docker
|
||||
|
||||
@@ -185,10 +212,104 @@ A `c3c` executable will be found under `bin/`.
|
||||
#### Installing on OS X using Homebrew
|
||||
|
||||
2. Install CMake: `brew install cmake`
|
||||
3. Install LLVM 11: `brew install llvm`
|
||||
3. Install LLVM 15: `brew install llvm`
|
||||
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 for debug: `cmake ..`
|
||||
9. Build: `cmake --build .`
|
||||
|
||||
#### Getting started with a "hello world"
|
||||
|
||||
Create a `main.c3` file with:
|
||||
```c++
|
||||
module hello_world;
|
||||
import std::io;
|
||||
|
||||
fn void main()
|
||||
{
|
||||
io::printn("Hello, world!");
|
||||
}
|
||||
```
|
||||
|
||||
Make sure you have the standard libraries at either `../lib/std/` or `/lib/std/`.
|
||||
|
||||
Then run
|
||||
```sh
|
||||
c3c compile main.c3
|
||||
```
|
||||
|
||||
The generated binary will by default be named after the module that contains the main
|
||||
function. In our case that is `hello_world`, so the resulting binary will be
|
||||
called `hello_world` or `hello_world.exe`depending on platform.
|
||||
|
||||
### Compiling
|
||||
|
||||
#### 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)
|
||||
2. Install CMake
|
||||
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
|
||||
4. Enter the C3C directory `cd c3c`.
|
||||
5. Set up the CMake build `cmake -B build -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=Release`
|
||||
6. Build: `cmake --build build --config Release`
|
||||
7. You should now have the c3c.exe
|
||||
|
||||
You should now have a `c3c` executable.
|
||||
|
||||
You can try it out by running some sample code: `c3c.exe compile ../resources/examples/hash.c3`
|
||||
|
||||
*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
|
||||
|
||||
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 15 (or greater: C3C supports LLVM 15-17): `sudo apt-get install clang-15 zlib1g zlib1g-dev libllvm15 llvm-15 llvm-15-dev llvm-15-runtime liblld-15-dev liblld-15`
|
||||
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 .`
|
||||
|
||||
You should now have a `c3c` executable.
|
||||
|
||||
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.
|
||||
2. Install or compile LLVM and LLD *libraries* (version 15+ or higher)
|
||||
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 for debug: `cmake ..`. At this point you may need to manually
|
||||
provide the link path to the LLVM CMake directories, e.g. `cmake -DLLVM_DIR=/usr/local/opt/llvm/lib/cmake/llvm/ ..`
|
||||
8. Build: `cmake --build .`
|
||||
|
||||
*A note on compiling for Linux/Unix/MacOS: to be able to fetch vendor libraries
|
||||
libcurl is needed. The CMake script should detect it if it is available. Note that
|
||||
this functionality is non-essential and it is perfectly fine to user the compiler without it.*
|
||||
|
||||
#### Licensing
|
||||
|
||||
The C3 compiler is licensed under LGPL 3.0, the standard library itself is
|
||||
MIT licensed.
|
||||
|
||||
#### Editor plugins
|
||||
|
||||
Editor plugins can be found at https://github.com/c3lang/editor-plugins.
|
||||
|
||||
#### Contributing unit tests
|
||||
|
||||
1. Write the test, either adding to existing test files in `/test/unit/` or add
|
||||
a new file. (If testing the standard library, put it in the `/test/unit/stdlib/` subdirectory).
|
||||
2. Make sure that the test functions have the `@test` attribute.
|
||||
3. Run tests and see that they pass. (Recommended settings: `c3c compile-test --safe -g1 -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.
|
||||
|
||||
@@ -1,42 +1,44 @@
|
||||
#!/bin/bash
|
||||
## build-with-docker.sh
|
||||
## @author gdm85
|
||||
## @modified by Kenta
|
||||
##
|
||||
## Script to build c3c for either Ubuntu 18 or Ubuntu 20.
|
||||
## Script to build c3c for Ubuntu 22
|
||||
##
|
||||
#
|
||||
|
||||
if [ $# -ne 1 -a $# -ne 2 ]; then
|
||||
echo "Usage: build-with-docker.sh (18|20) [Debug|Release]" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
read -p "Select Build Type: Debug/Release: " config
|
||||
|
||||
set -e
|
||||
|
||||
if [ -z "$2" ]; then
|
||||
CMAKE_BUILD_TYPE=Debug
|
||||
else
|
||||
CMAKE_BUILD_TYPE="$2"
|
||||
DOCKER=docker
|
||||
DOCKER_RUN=""
|
||||
IMAGE="c3c-builder"
|
||||
if type podman 2>/dev/null >/dev/null; then
|
||||
DOCKER=podman
|
||||
DOCKER_RUN="--userns=keep-id"
|
||||
IMAGE="localhost/$IMAGE"
|
||||
fi
|
||||
|
||||
TAG="$1"
|
||||
if [ "$1" = 18 ]; then
|
||||
UBUNTU_VERSION="18.04"
|
||||
DEPS="llvm-10-dev liblld-10-dev libclang-10-dev"
|
||||
elif [ "$1" = 20 ]; then
|
||||
UBUNTU_VERSION="20.04"
|
||||
DEPS="llvm-11-dev liblld-11-dev clang-11 libllvm11 llvm-11-runtime"
|
||||
if [ $config == "Debug" ]; then
|
||||
echo "debug???"
|
||||
CMAKE_BUILD_TYPE=Debug
|
||||
else
|
||||
echo "ERROR: expected 18 or 20 as Ubuntu version argument" 1>&2
|
||||
exit 2
|
||||
CMAKE_BUILD_TYPE="$config"
|
||||
fi
|
||||
|
||||
cd docker && docker build -t c3c-builder:$TAG --build-arg UID=$(id -u) --build-arg GID=$(id -g) \
|
||||
--build-arg DEPS="$DEPS" --build-arg UBUNTU_VERSION="$UBUNTU_VERSION" .
|
||||
UBUNTU_VERSION="22.10"
|
||||
LLVM_VERSION="15"
|
||||
|
||||
IMAGE="$IMAGE:22"
|
||||
|
||||
cd docker && $DOCKER build -t $IMAGE\
|
||||
--build-arg DEPS="llvm-$LLVM_VERSION-dev liblld-$LLVM_VERSION-dev clang-$LLVM_VERSION libllvm$LLVM_VERSION llvm-$LLVM_VERSION-runtime" \
|
||||
--build-arg UBUNTU_VERSION="$UBUNTU_VERSION" .
|
||||
cd ..
|
||||
|
||||
rm -rf build bin lib
|
||||
rm -rf build bin
|
||||
mkdir -p build bin
|
||||
|
||||
exec docker run -ti --rm -v "$PWD":/home/c3c/source -w /home/c3c/source c3c-builder:$TAG bash -c \
|
||||
"cd build && cmake -DLLVM_DIR=/usr/lib/llvm-11/cmake -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE .. && cmake --build . && mv c3c ../bin/ && mv lib ../lib"
|
||||
exec $DOCKER run -ti --rm --tmpfs=/tmp $DOCKER_RUN -v "$PWD":/home/c3c/source -w /home/c3c/source $IMAGE bash -c \
|
||||
"cd build && cmake -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE -DC3_LLVM_VERSION=$LLVM_VERSION .. && cmake --build . && mv c3c lib ../bin/"
|
||||
|
||||
7833
dependencies/miniz/miniz.c
vendored
Normal file
7833
dependencies/miniz/miniz.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1422
dependencies/miniz/miniz.h
vendored
Normal file
1422
dependencies/miniz/miniz.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && export TERM=xterm && apt-get update
|
||||
ARG GID=1000
|
||||
ARG UID=1000
|
||||
|
||||
RUN groupadd --gid=$GID c3c && useradd --gid=$GID --uid=$GID --create-home --shell /bin/bash c3c
|
||||
RUN groupadd -o --gid=$GID c3c && useradd --gid=$GID --uid=$GID --create-home --shell /bin/bash c3c
|
||||
|
||||
USER c3c
|
||||
|
||||
17
install_win_reqs.bat
Normal file
17
install_win_reqs.bat
Normal file
@@ -0,0 +1,17 @@
|
||||
@echo off
|
||||
|
||||
set DOWNLOAD_URL=https://aka.ms/vs/17/release
|
||||
|
||||
mkdir tmp 2> NUL
|
||||
|
||||
if not exist "tmp\vs_buildtools.exe" (
|
||||
bitsadmin /transfer /download /priority foreground %DOWNLOAD_URL%/vs_buildtools.exe %CD%\tmp\vs_buildtools.exe
|
||||
)
|
||||
|
||||
echo Preparing Build Tools, please wait...
|
||||
tmp\vs_BuildTools.exe --quiet --wait --layout tmp\ --add Microsoft.VisualStudio.Component.Windows10SDK.19041
|
||||
|
||||
echo Installing Build Tools, please wait...
|
||||
tmp\vs_BuildTools.exe --quiet --wait --noweb --add Microsoft.VisualStudio.Component.Windows10SDK.19041
|
||||
|
||||
REM rmdir tmp /s /q
|
||||
73
lib/std/ascii.c3
Normal file
73
lib/std/ascii.c3
Normal file
@@ -0,0 +1,73 @@
|
||||
module std::ascii;
|
||||
|
||||
macro bool in_range_m(c, start, len) => (uint)(c - start) < len;
|
||||
macro bool is_lower_m(c) => in_range_m(c, 0x61, 26);
|
||||
macro bool is_upper_m(c) => in_range_m(c, 0x41, 26);
|
||||
macro bool is_digit_m(c) => in_range_m(c, 0x30, 10);
|
||||
macro bool is_bdigit_m(c) => in_range_m(c, 0x30, 2);
|
||||
macro bool is_odigit_m(c) => in_range_m(c, 0x30, 8);
|
||||
macro bool is_xdigit_m(c) => in_range_m(c | 32, 0x61, 6) || is_digit_m(c);
|
||||
macro bool is_alpha_m(c) => in_range_m(c | 32, 0x61, 26);
|
||||
macro bool is_print_m(c) => in_range_m(c, 0x20, 95);
|
||||
macro bool is_graph_m(c) => in_range_m(c, 0x21, 94);
|
||||
macro bool is_space_m(c) => in_range_m(c, 0x9, 5) || c == 0x20;
|
||||
macro bool is_alnum_m(c) => is_alpha_m(c) || is_digit_m(c);
|
||||
macro bool is_punct_m(c) => !is_alnum_m(c) && is_graph_m(c);
|
||||
macro bool is_blank_m(c) => c == 0x20 || c == 0x9;
|
||||
macro bool is_cntrl_m(c) => c < 0x20 || c == 0x7f;
|
||||
macro to_lower_m(c) => is_upper_m(c) ? c + 0x20 : c;
|
||||
macro to_upper_m(c) => is_lower_m(c) ? c - 0x20 : c;
|
||||
|
||||
fn bool in_range(char c, char start, char len) => in_range_m(c, start, len);
|
||||
fn bool is_lower(char c) => is_lower_m(c);
|
||||
fn bool is_upper(char c) => is_upper_m(c);
|
||||
fn bool is_digit(char c) => is_digit_m(c);
|
||||
fn bool is_bdigit(char c) => is_bdigit_m(c);
|
||||
fn bool is_odigit(char c) => is_odigit_m(c);
|
||||
fn bool is_xdigit(char c) => is_xdigit_m(c);
|
||||
fn bool is_alpha(char c) => is_alpha_m(c);
|
||||
fn bool is_print(char c) => is_print_m(c);
|
||||
fn bool is_graph(char c) => is_graph_m(c);
|
||||
fn bool is_space(char c) => is_space_m(c);
|
||||
fn bool is_alnum(char c) => is_alnum_m(c);
|
||||
fn bool is_punct(char c) => is_punct_m(c);
|
||||
fn bool is_blank(char c) => is_blank_m(c);
|
||||
fn bool is_cntrl(char c) => is_cntrl_m(c);
|
||||
fn char to_lower(char c) => (char)to_lower_m(c);
|
||||
fn char to_upper(char c) => (char)to_upper_m(c);
|
||||
|
||||
fn bool char.in_range(char c, char start, char len) => in_range_m(c, start, len);
|
||||
fn bool char.is_lower(char c) => is_lower_m(c);
|
||||
fn bool char.is_upper(char c) => is_upper_m(c);
|
||||
fn bool char.is_digit(char c) => is_digit_m(c);
|
||||
fn bool char.is_bdigit(char c) => is_bdigit_m(c);
|
||||
fn bool char.is_odigit(char c) => is_odigit_m(c);
|
||||
fn bool char.is_xdigit(char c) => is_xdigit_m(c);
|
||||
fn bool char.is_alpha(char c) => is_alpha_m(c);
|
||||
fn bool char.is_print(char c) => is_print_m(c);
|
||||
fn bool char.is_graph(char c) => is_graph_m(c);
|
||||
fn bool char.is_space(char c) => is_space_m(c);
|
||||
fn bool char.is_alnum(char c) => is_alnum_m(c);
|
||||
fn bool char.is_punct(char c) => is_punct_m(c);
|
||||
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);
|
||||
|
||||
fn bool uint.in_range(uint c, uint start, uint len) => in_range_m(c, start, len);
|
||||
fn bool uint.is_lower(uint c) => is_lower_m(c);
|
||||
fn bool uint.is_upper(uint c) => is_upper_m(c);
|
||||
fn bool uint.is_digit(uint c) => is_digit_m(c);
|
||||
fn bool uint.is_bdigit(uint c) => is_bdigit_m(c);
|
||||
fn bool uint.is_odigit(uint c) => is_odigit_m(c);
|
||||
fn bool uint.is_xdigit(uint c) => is_xdigit_m(c);
|
||||
fn bool uint.is_alpha(uint c) => is_alpha_m(c);
|
||||
fn bool uint.is_print(uint c) => is_print_m(c);
|
||||
fn bool uint.is_graph(uint c) => is_graph_m(c);
|
||||
fn bool uint.is_space(uint c) => is_space_m(c);
|
||||
fn bool uint.is_alnum(uint c) => is_alnum_m(c);
|
||||
fn bool uint.is_punct(uint c) => is_punct_m(c);
|
||||
fn bool uint.is_blank(uint c) => is_blank_m(c);
|
||||
fn bool uint.is_cntrl(uint c) => is_cntrl_m(c);
|
||||
fn uint uint.to_lower(uint c) => (uint)to_lower_m(c);
|
||||
fn uint uint.to_upper(uint c) => (uint)to_upper_m(c);
|
||||
5
lib/std/atomic.c3
Normal file
5
lib/std/atomic.c3
Normal file
@@ -0,0 +1,5 @@
|
||||
// Copyright (c) 2021 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::atomic;
|
||||
|
||||
173
lib/std/bits.c3
Normal file
173
lib/std/bits.c3
Normal file
@@ -0,0 +1,173 @@
|
||||
module std::bits;
|
||||
|
||||
|
||||
/**
|
||||
* @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`
|
||||
**/
|
||||
macro bswap(i) @builtin => $$bswap(i);
|
||||
|
||||
|
||||
macro uint[<*>].popcount(uint[<*>] i) => $$popcount(i);
|
||||
macro uint[<*>].ctz(uint[<*>] i) => $$ctz(i);
|
||||
macro uint[<*>].clz(uint[<*>] i) => $$clz(i);
|
||||
macro uint[<*>] uint[<*>].fshl(uint[<*>] hi, uint[<*>] lo, uint[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro uint[<*>] uint[<*>].fshr(uint[<*>] hi, uint[<*>] lo, uint[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro uint[<*>] uint[<*>].rotl(uint[<*>] i, uint[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro uint[<*>] uint[<*>].rotr(uint[<*>] i, uint[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro int[<*>].popcount(int[<*>] i) => $$popcount(i);
|
||||
macro int[<*>].ctz(int[<*>] i) => $$ctz(i);
|
||||
macro int[<*>].clz(int[<*>] i) => $$clz(i);
|
||||
macro int[<*>] int[<*>].fshl(int[<*>] hi, int[<*>] lo, int[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro int[<*>] int[<*>].fshr(int[<*>] hi, int[<*>] lo, int[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro int[<*>] int[<*>].rotl(int[<*>] i, int[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro int[<*>] int[<*>].rotr(int[<*>] i, int[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro ushort[<*>].popcount(ushort[<*>] i) => $$popcount(i);
|
||||
macro ushort[<*>].ctz(ushort[<*>] i) => $$ctz(i);
|
||||
macro ushort[<*>].clz(ushort[<*>] i) => $$clz(i);
|
||||
macro ushort[<*>] ushort[<*>].fshl(ushort[<*>] hi, ushort[<*>] lo, ushort[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro ushort[<*>] ushort[<*>].fshr(ushort[<*>] hi, ushort[<*>] lo, ushort[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro ushort[<*>] ushort[<*>].rotl(ushort[<*>] i, ushort[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro ushort[<*>] ushort[<*>].rotr(ushort[<*>] i, ushort[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro short[<*>].popcount(short[<*>] i) => $$popcount(i);
|
||||
macro short[<*>].ctz(short[<*>] i) => $$ctz(i);
|
||||
macro short[<*>].clz(short[<*>] i) => $$clz(i);
|
||||
macro short[<*>] short[<*>].fshl(short[<*>] hi, short[<*>] lo, short[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro short[<*>] short[<*>].fshr(short[<*>] hi, short[<*>] lo, short[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro short[<*>] short[<*>].rotl(short[<*>] i, short[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro short[<*>] short[<*>].rotr(short[<*>] i, short[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro char[<*>].popcount(char[<*>] i) => $$popcount(i);
|
||||
macro char[<*>].ctz(char[<*>] i) => $$ctz(i);
|
||||
macro char[<*>].clz(char[<*>] i) => $$clz(i);
|
||||
macro char[<*>] char[<*>].fshl(char[<*>] hi, char[<*>] lo, char[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro char[<*>] char[<*>].fshr(char[<*>] hi, char[<*>] lo, char[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro char[<*>] char[<*>].rotl(char[<*>] i, char[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro char[<*>] char[<*>].rotr(char[<*>] i, char[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro ichar[<*>].popcount(ichar[<*>] i) => $$popcount(i);
|
||||
macro ichar[<*>].ctz(ichar[<*>] i) => $$ctz(i);
|
||||
macro ichar[<*>].clz(ichar[<*>] i) => $$clz(i);
|
||||
macro ichar[<*>] ichar[<*>].fshl(ichar[<*>] hi, ichar[<*>] lo, ichar[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro ichar[<*>] ichar[<*>].fshr(ichar[<*>] hi, ichar[<*>] lo, ichar[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro ichar[<*>] ichar[<*>].rotl(ichar[<*>] i, ichar[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro ichar[<*>] ichar[<*>].rotr(ichar[<*>] i, ichar[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro ulong[<*>].popcount(ulong[<*>] i) => $$popcount(i);
|
||||
macro ulong[<*>].ctz(ulong[<*>] i) => $$ctz(i);
|
||||
macro ulong[<*>].clz(ulong[<*>] i) => $$clz(i);
|
||||
macro ulong[<*>] ulong[<*>].fshl(ulong[<*>] hi, ulong[<*>] lo, ulong[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro ulong[<*>] ulong[<*>].fshr(ulong[<*>] hi, ulong[<*>] lo, ulong[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro ulong[<*>] ulong[<*>].rotl(ulong[<*>] i, ulong[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro ulong[<*>] ulong[<*>].rotr(ulong[<*>] i, ulong[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro long[<*>].popcount(long[<*>] i) => $$popcount(i);
|
||||
macro long[<*>].ctz(long[<*>] i) => $$ctz(i);
|
||||
macro long[<*>].clz(long[<*>] i) => $$clz(i);
|
||||
macro long[<*>] long[<*>].fshl(long[<*>] hi, long[<*>] lo, long[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro long[<*>] long[<*>].fshr(long[<*>] hi, long[<*>] lo, long[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro long[<*>] long[<*>].rotl(long[<*>] i, long[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro long[<*>] long[<*>].rotr(long[<*>] i, long[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro uint128[<*>].popcount(uint128[<*>] i) => $$popcount(i);
|
||||
macro uint128[<*>].ctz(uint128[<*>] i) => $$ctz(i);
|
||||
macro uint128[<*>].clz(uint128[<*>] i) => $$clz(i);
|
||||
macro uint128[<*>] uint128[<*>].fshl(uint128[<*>] hi, uint128[<*>] lo, uint128[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro uint128[<*>] uint128[<*>].fshr(uint128[<*>] hi, uint128[<*>] lo, uint128[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro uint128[<*>] uint128[<*>].rotl(uint128[<*>] i, uint128[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro uint128[<*>] uint128[<*>].rotr(uint128[<*>] i, uint128[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro int128[<*>].popcount(int128[<*>] i) => $$popcount(i);
|
||||
macro int128[<*>].ctz(int128[<*>] i) => $$ctz(i);
|
||||
macro int128[<*>].clz(int128[<*>] i) => $$clz(i);
|
||||
macro int128[<*>] int128[<*>].fshl(int128[<*>] hi, int128[<*>] lo, int128[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro int128[<*>] int128[<*>].fshr(int128[<*>] hi, int128[<*>] lo, int128[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro int128[<*>] int128[<*>].rotl(int128[<*>] i, int128[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro int128[<*>] int128[<*>].rotr(int128[<*>] i, int128[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro uint.popcount(uint i) => $$popcount(i);
|
||||
macro uint.ctz(uint i) => $$ctz(i);
|
||||
macro uint.clz(uint i) => $$clz(i);
|
||||
macro uint uint.fshl(uint hi, uint lo, uint shift) => $$fshl(hi, lo, shift);
|
||||
macro uint uint.fshr(uint hi, uint lo, uint shift) => $$fshr(hi, lo, shift);
|
||||
macro uint uint.rotl(uint i, uint shift) => $$fshl(i, i, shift);
|
||||
macro uint uint.rotr(uint i, uint shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro int.popcount(int i) => $$popcount(i);
|
||||
macro int.ctz(int i) => $$ctz(i);
|
||||
macro int.clz(int i) => $$clz(i);
|
||||
macro int int.fshl(int hi, int lo, int shift) => $$fshl(hi, lo, shift);
|
||||
macro int int.fshr(int hi, int lo, int shift) => $$fshr(hi, lo, shift);
|
||||
macro int int.rotl(int i, int shift) => $$fshl(i, i, shift);
|
||||
macro int int.rotr(int i, int shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro ushort.popcount(ushort i) => $$popcount(i);
|
||||
macro ushort.ctz(ushort i) => $$ctz(i);
|
||||
macro ushort.clz(ushort i) => $$clz(i);
|
||||
macro ushort ushort.fshl(ushort hi, ushort lo, ushort shift) => $$fshl(hi, lo, shift);
|
||||
macro ushort ushort.fshr(ushort hi, ushort lo, ushort shift) => $$fshr(hi, lo, shift);
|
||||
macro ushort ushort.rotl(ushort i, ushort shift) => $$fshl(i, i, shift);
|
||||
macro ushort ushort.rotr(ushort i, ushort shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro short.popcount(short i) => $$popcount(i);
|
||||
macro short.ctz(short i) => $$ctz(i);
|
||||
macro short.clz(short i) => $$clz(i);
|
||||
macro short short.fshl(short hi, short lo, short shift) => $$fshl(hi, lo, shift);
|
||||
macro short short.fshr(short hi, short lo, short shift) => $$fshr(hi, lo, shift);
|
||||
macro short short.rotl(short i, short shift) => $$fshl(i, i, shift);
|
||||
macro short short.rotr(short i, short shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro char.popcount(char i) => $$popcount(i);
|
||||
macro char.ctz(char i) => $$ctz(i);
|
||||
macro char.clz(char i) => $$clz(i);
|
||||
macro char char.fshl(char hi, char lo, char shift) => $$fshl(hi, lo, shift);
|
||||
macro char char.fshr(char hi, char lo, char shift) => $$fshr(hi, lo, shift);
|
||||
macro char char.rotl(char i, char shift) => $$fshl(i, i, shift);
|
||||
macro char char.rotr(char i, char shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro ichar.popcount(ichar i) => $$popcount(i);
|
||||
macro ichar.ctz(ichar i) => $$ctz(i);
|
||||
macro ichar.clz(ichar i) => $$clz(i);
|
||||
macro ichar ichar.fshl(ichar hi, ichar lo, ichar shift) => $$fshl(hi, lo, shift);
|
||||
macro ichar ichar.fshr(ichar hi, ichar lo, ichar shift) => $$fshr(hi, lo, shift);
|
||||
macro ichar ichar.rotl(ichar i, ichar shift) => $$fshl(i, i, shift);
|
||||
macro ichar ichar.rotr(ichar i, ichar shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro ulong.popcount(ulong i) => $$popcount(i);
|
||||
macro ulong.ctz(ulong i) => $$ctz(i);
|
||||
macro ulong.clz(ulong i) => $$clz(i);
|
||||
macro ulong ulong.fshl(ulong hi, ulong lo, ulong shift) => $$fshl(hi, lo, shift);
|
||||
macro ulong ulong.fshr(ulong hi, ulong lo, ulong shift) => $$fshr(hi, lo, shift);
|
||||
macro ulong ulong.rotl(ulong i, ulong shift) => $$fshl(i, i, shift);
|
||||
macro ulong ulong.rotr(ulong i, ulong shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro long.popcount(long i) => $$popcount(i);
|
||||
macro long.ctz(long i) => $$ctz(i);
|
||||
macro long.clz(long i) => $$clz(i);
|
||||
macro long long.fshl(long hi, long lo, long shift) => $$fshl(hi, lo, shift);
|
||||
macro long long.fshr(long hi, long lo, long shift) => $$fshr(hi, lo, shift);
|
||||
macro long long.rotl(long i, long shift) => $$fshl(i, i, shift);
|
||||
macro long long.rotr(long i, long shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro uint128.popcount(uint128 i) => $$popcount(i);
|
||||
macro uint128.ctz(uint128 i) => $$ctz(i);
|
||||
macro uint128.clz(uint128 i) => $$clz(i);
|
||||
macro uint128 uint128.fshl(uint128 hi, uint128 lo, uint128 shift) => $$fshl(hi, lo, shift);
|
||||
macro uint128 uint128.fshr(uint128 hi, uint128 lo, uint128 shift) => $$fshr(hi, lo, shift);
|
||||
macro uint128 uint128.rotl(uint128 i, uint128 shift) => $$fshl(i, i, shift);
|
||||
macro uint128 uint128.rotr(uint128 i, uint128 shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro int128.popcount(int128 i) => $$popcount(i);
|
||||
macro int128.ctz(int128 i) => $$ctz(i);
|
||||
macro int128.clz(int128 i) => $$clz(i);
|
||||
macro int128 int128.fshl(int128 hi, int128 lo, int128 shift) => $$fshl(hi, lo, shift);
|
||||
macro int128 int128.fshr(int128 hi, int128 lo, int128 shift) => $$fshr(hi, lo, shift);
|
||||
macro int128 int128.rotl(int128 i, int128 shift) => $$fshl(i, i, shift);
|
||||
macro int128 int128.rotr(int128 i, int128 shift) => $$fshr(i, i, shift);
|
||||
18
lib/std/collections/enummap.c3
Normal file
18
lib/std/collections/enummap.c3
Normal file
@@ -0,0 +1,18 @@
|
||||
module std::collections::enummap<Enum, ValueType>;
|
||||
|
||||
struct EnumMap
|
||||
{
|
||||
ValueType[Enum.len] values;
|
||||
}
|
||||
|
||||
fn void EnumMap.init(EnumMap* this, ValueType init_value)
|
||||
{
|
||||
foreach(&a : this.values)
|
||||
{
|
||||
*a = init_value;
|
||||
}
|
||||
}
|
||||
|
||||
fn uint EnumMap.len(EnumMap* this) @operator(len) => this.values.len;
|
||||
fn ValueType EnumMap.get(EnumMap* this, Enum key) @operator([]) => this.values[key.ordinal];
|
||||
fn void EnumMap.set(EnumMap* this, Enum key, ValueType value) @operator([]=) => this.values[key.ordinal] = value;
|
||||
148
lib/std/collections/enumset.c3
Normal file
148
lib/std/collections/enumset.c3
Normal file
@@ -0,0 +1,148 @@
|
||||
// Copyright (c) 2021 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 Enum.kindof == TypeKind.ENUM : "Only enums maybe be used with an enumset"
|
||||
**/
|
||||
module std::collections::enumset<Enum>;
|
||||
|
||||
|
||||
const IS_CHAR_ARRAY = Enum.elements > 128;
|
||||
|
||||
$switch
|
||||
|
||||
$case (Enum.elements > 128):
|
||||
def EnumSetType @private = char[(Enum.elements + 7) / 8];
|
||||
|
||||
$case (Enum.elements > 64):
|
||||
def EnumSetType @private = uint128;
|
||||
|
||||
$case (Enum.elements > 32 || $$C_INT_SIZE > 32):
|
||||
def EnumSetType @private = ulong;
|
||||
|
||||
$case (Enum.elements > 16 || $$C_INT_SIZE > 16):
|
||||
def EnumSetType @private = uint;
|
||||
|
||||
$case (Enum.elements > 8 || $$C_INT_SIZE > 8):
|
||||
def EnumSetType @private = ushort;
|
||||
|
||||
$default:
|
||||
def EnumSetType @private = char;
|
||||
|
||||
$endswitch
|
||||
|
||||
def EnumSet = distinct EnumSetType;
|
||||
|
||||
fn void EnumSet.add(EnumSet* this, Enum v)
|
||||
{
|
||||
$if IS_CHAR_ARRAY:
|
||||
(*this)[v / 8] |= (char)(1u << (v % 8));
|
||||
$else
|
||||
*this = (EnumSet)((EnumSetType)*this | 1u << (EnumSetType)v);
|
||||
$endif
|
||||
}
|
||||
|
||||
fn void EnumSet.clear(EnumSet* this)
|
||||
{
|
||||
$if IS_CHAR_ARRAY:
|
||||
*this = {};
|
||||
$else
|
||||
*this = 0;
|
||||
$endif
|
||||
}
|
||||
|
||||
fn bool EnumSet.remove(EnumSet* this, Enum v)
|
||||
{
|
||||
$if IS_CHAR_ARRAY:
|
||||
if (!this.has(v) @inline) return false;
|
||||
(*this)[v / 8] &= (char)~(1 << (v % 8));
|
||||
return true;
|
||||
$else
|
||||
EnumSetType old = (EnumSetType)*this;
|
||||
EnumSetType new = old & ~(1u << (EnumSetType)v);
|
||||
*this = (EnumSet)new;
|
||||
return old != new;
|
||||
$endif
|
||||
}
|
||||
|
||||
fn bool EnumSet.has(EnumSet* this, Enum v)
|
||||
{
|
||||
$if IS_CHAR_ARRAY:
|
||||
return (bool)(((*this)[v / 8] << (v % 8)) & 0x01);
|
||||
$else
|
||||
return ((EnumSetType)*this & (1u << (EnumSetType)v)) != 0;
|
||||
$endif
|
||||
}
|
||||
|
||||
fn void EnumSet.add_all(EnumSet* this, EnumSet s)
|
||||
{
|
||||
$if IS_CHAR_ARRAY:
|
||||
foreach (i, c : s) (*this)[i] |= c;
|
||||
$else
|
||||
*this = (EnumSet)((EnumSetType)*this | (EnumSetType)s);
|
||||
$endif
|
||||
}
|
||||
|
||||
fn void EnumSet.retain_all(EnumSet* this, EnumSet s)
|
||||
{
|
||||
$if IS_CHAR_ARRAY:
|
||||
foreach (i, c : s) (*this)[i] &= c;
|
||||
$else
|
||||
*this = (EnumSet)((EnumSetType)*this & (EnumSetType)s);
|
||||
$endif
|
||||
}
|
||||
|
||||
fn void EnumSet.remove_all(EnumSet* this, EnumSet s)
|
||||
{
|
||||
$if IS_CHAR_ARRAY:
|
||||
foreach (i, c : s) (*this)[i] &= ~c;
|
||||
$else
|
||||
*this = (EnumSet)((EnumSetType)*this & ~(EnumSetType)s);
|
||||
$endif
|
||||
}
|
||||
|
||||
fn EnumSet EnumSet.and_of(EnumSet* this, EnumSet s)
|
||||
{
|
||||
$if IS_CHAR_ARRAY:
|
||||
EnumSet copy = *this;
|
||||
copy.retain_all(s);
|
||||
return copy;
|
||||
$else
|
||||
return (EnumSet)((EnumSetType)*this & (EnumSetType)s);
|
||||
$endif
|
||||
}
|
||||
|
||||
fn EnumSet EnumSet.or_of(EnumSet* this, EnumSet s)
|
||||
{
|
||||
$if IS_CHAR_ARRAY:
|
||||
EnumSet copy = *this;
|
||||
copy.add_all(s);
|
||||
return copy;
|
||||
$else
|
||||
return (EnumSet)((EnumSetType)*this | (EnumSetType)s);
|
||||
$endif
|
||||
}
|
||||
|
||||
|
||||
fn EnumSet EnumSet.diff_of(EnumSet* this, EnumSet s)
|
||||
{
|
||||
$if IS_CHAR_ARRAY:
|
||||
EnumSet copy = *this;
|
||||
copy.remove_all(s);
|
||||
return copy;
|
||||
$else
|
||||
return (EnumSet)((EnumSetType)*this & ~(EnumSetType)s);
|
||||
$endif
|
||||
}
|
||||
|
||||
fn EnumSet EnumSet.xor_of(EnumSet* this, EnumSet s)
|
||||
{
|
||||
$if IS_CHAR_ARRAY:
|
||||
EnumSet copy = *this;
|
||||
foreach (i, c : s) copy[i] ^= c;
|
||||
return copy;
|
||||
$else
|
||||
return (EnumSet)((EnumSetType)*this ^ (EnumSetType)s);
|
||||
$endif
|
||||
}
|
||||
312
lib/std/collections/linkedlist.c3
Normal file
312
lib/std/collections/linkedlist.c3
Normal file
@@ -0,0 +1,312 @@
|
||||
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::collections::linkedlist<Type>;
|
||||
|
||||
struct Node @private
|
||||
{
|
||||
Node *next;
|
||||
Node *prev;
|
||||
Type value;
|
||||
}
|
||||
|
||||
struct LinkedList
|
||||
{
|
||||
Allocator *allocator;
|
||||
usz size;
|
||||
Node *_first;
|
||||
Node *_last;
|
||||
}
|
||||
|
||||
fn void LinkedList.push(LinkedList* list, Type value)
|
||||
{
|
||||
list.link_first(value);
|
||||
}
|
||||
|
||||
fn void LinkedList.push_last(LinkedList* list, Type value)
|
||||
{
|
||||
list.link_last(value);
|
||||
}
|
||||
|
||||
fn void LinkedList.init(LinkedList* list, Allocator* using = mem::heap())
|
||||
{
|
||||
*list = { .allocator = using };
|
||||
}
|
||||
|
||||
fn void LinkedList.tinit(LinkedList* list) => list.init(mem::temp()) @inline;
|
||||
|
||||
/**
|
||||
* @require list.allocator
|
||||
**/
|
||||
macro void LinkedList.@free_node(LinkedList &list, Node* node) @private
|
||||
{
|
||||
list.allocator.free(node)!!;
|
||||
}
|
||||
macro Node* LinkedList.@alloc_node(LinkedList &list) @private
|
||||
{
|
||||
if (!list.allocator) list.allocator = mem::heap();
|
||||
return malloc(Node, .using = list.allocator);
|
||||
}
|
||||
|
||||
fn void LinkedList.link_first(LinkedList* list, Type value) @private
|
||||
{
|
||||
Node *first = list._first;
|
||||
Node *new_node = list.@alloc_node();
|
||||
*new_node = { .next = first, .value = value };
|
||||
list._first = new_node;
|
||||
if (!first)
|
||||
{
|
||||
list._last = new_node;
|
||||
}
|
||||
else
|
||||
{
|
||||
first.prev = new_node;
|
||||
}
|
||||
list.size++;
|
||||
}
|
||||
|
||||
fn void LinkedList.link_last(LinkedList* list, Type value) @private
|
||||
{
|
||||
Node *last = list._last;
|
||||
Node *new_node = list.@alloc_node();
|
||||
*new_node = { .prev = last, .value = value };
|
||||
list._last = new_node;
|
||||
if (!last)
|
||||
{
|
||||
list._first = new_node;
|
||||
}
|
||||
else
|
||||
{
|
||||
last.next = new_node;
|
||||
}
|
||||
list.size++;
|
||||
}
|
||||
|
||||
fn Type! peek(LinkedList* list) => list.first() @inline;
|
||||
fn Type! peek_last(LinkedList* list) => list.last() @inline;
|
||||
|
||||
fn Type! LinkedList.first(LinkedList *list)
|
||||
{
|
||||
if (!list._first) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
return list._first.value;
|
||||
}
|
||||
|
||||
fn Type! LinkedList.last(LinkedList* list)
|
||||
{
|
||||
if (!list._last) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
return list._last.value;
|
||||
}
|
||||
|
||||
fn void LinkedList.free(LinkedList* list) => list.clear() @inline;
|
||||
|
||||
fn void LinkedList.clear(LinkedList* list)
|
||||
{
|
||||
for (Node* node = list._first; node != null;)
|
||||
{
|
||||
Node* next = node.next;
|
||||
list.@free_node(node);
|
||||
node = next;
|
||||
}
|
||||
list._first = null;
|
||||
list._last = null;
|
||||
list.size = 0;
|
||||
}
|
||||
|
||||
fn usz LinkedList.len(LinkedList* list) @inline => list.size;
|
||||
|
||||
/**
|
||||
* @require index < list.size
|
||||
**/
|
||||
macro Node* LinkedList.node_at_index(LinkedList* list, usz index)
|
||||
{
|
||||
if (index * 2 >= list.size)
|
||||
{
|
||||
Node* node = list._last;
|
||||
index = list.size - index - 1;
|
||||
while (index--) node = node.prev;
|
||||
return node;
|
||||
}
|
||||
Node* node = list._first;
|
||||
while (index--) node = node.next;
|
||||
return node;
|
||||
}
|
||||
/**
|
||||
* @require index < list.size
|
||||
**/
|
||||
fn Type LinkedList.get(LinkedList* list, usz index)
|
||||
{
|
||||
return list.node_at_index(index).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index < list.size
|
||||
**/
|
||||
fn void LinkedList.set(LinkedList* list, usz index, Type element)
|
||||
{
|
||||
list.node_at_index(index).value = element;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index < list.size
|
||||
**/
|
||||
fn void LinkedList.remove(LinkedList* list, usz index)
|
||||
{
|
||||
list.unlink(list.node_at_index(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index <= list.size
|
||||
**/
|
||||
fn void LinkedList.insert(LinkedList* list, usz index, Type element)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
list.push(element);
|
||||
case list.size:
|
||||
list.push_last(element);
|
||||
default:
|
||||
list.link_before(list.node_at_index(index), element);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @require succ != null
|
||||
**/
|
||||
fn void LinkedList.link_before(LinkedList *list, Node *succ, Type value) @private
|
||||
{
|
||||
Node* pred = succ.prev;
|
||||
Node* new_node = malloc(Node);
|
||||
*new_node = { .prev = pred, .next = succ, .value = value };
|
||||
succ.prev = new_node;
|
||||
if (!pred)
|
||||
{
|
||||
list._first = new_node;
|
||||
}
|
||||
else
|
||||
{
|
||||
pred.next = new_node;
|
||||
}
|
||||
list.size++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require list && list._first
|
||||
**/
|
||||
fn void LinkedList.unlink_first(LinkedList* list) @private
|
||||
{
|
||||
Node* f = list._first;
|
||||
Node* next = f.next;
|
||||
list.@free_node(f);
|
||||
list._first = next;
|
||||
if (!next)
|
||||
{
|
||||
list._last = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
next.prev = null;
|
||||
}
|
||||
list.size--;
|
||||
}
|
||||
|
||||
fn bool LinkedList.remove_value(LinkedList* list, Type t)
|
||||
{
|
||||
for (Node* node = list._first; node != null; node = node.next)
|
||||
{
|
||||
if (node.value == t)
|
||||
{
|
||||
list.unlink(node);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn bool LinkedList.remove_last_value(LinkedList* list, Type t)
|
||||
{
|
||||
for (Node* node = list._last; node != null; node = node.prev)
|
||||
{
|
||||
if (node.value == t)
|
||||
{
|
||||
list.unlink(node);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] list
|
||||
**/
|
||||
fn Type! LinkedList.pop(LinkedList* list)
|
||||
{
|
||||
if (!list._first) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
defer list.unlink_first();
|
||||
return list._first.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] list
|
||||
**/
|
||||
fn void! LinkedList.remove_last(LinkedList* list)
|
||||
{
|
||||
if (!list._first) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
list.unlink_last();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] list
|
||||
**/
|
||||
fn void! LinkedList.remove_first(LinkedList* list)
|
||||
{
|
||||
if (!list._first) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
list.unlink_first();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] list
|
||||
* @require list._last
|
||||
**/
|
||||
fn void LinkedList.unlink_last(LinkedList *list) @inline @private
|
||||
{
|
||||
Node* l = list._last;
|
||||
Node* prev = l.prev;
|
||||
list._last = prev;
|
||||
list.@free_node(l);
|
||||
if (!prev)
|
||||
{
|
||||
list._first = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev.next = null;
|
||||
}
|
||||
list.size--;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require list != null, x != null
|
||||
**/
|
||||
fn void LinkedList.unlink(LinkedList* list, Node* x) @private
|
||||
{
|
||||
Node* next = x.next;
|
||||
Node* prev = x.prev;
|
||||
if (!prev)
|
||||
{
|
||||
list._first = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev.next = next;
|
||||
}
|
||||
if (!next)
|
||||
{
|
||||
list._last = prev;
|
||||
}
|
||||
else
|
||||
{
|
||||
next.prev = prev;
|
||||
}
|
||||
list.@free_node(x);
|
||||
list.size--;
|
||||
}
|
||||
408
lib/std/collections/list.c3
Normal file
408
lib/std/collections/list.c3
Normal file
@@ -0,0 +1,408 @@
|
||||
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::collections::list<Type>;
|
||||
import std::math;
|
||||
|
||||
def ElementPredicate = fn bool(Type *type);
|
||||
|
||||
struct List
|
||||
{
|
||||
usz size;
|
||||
usz capacity;
|
||||
Allocator *allocator;
|
||||
Type *entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require using != null "A valid allocator must be provided"
|
||||
**/
|
||||
fn void List.init(List* list, usz initial_capacity = 16, Allocator* using = mem::heap())
|
||||
{
|
||||
list.allocator = using;
|
||||
list.size = 0;
|
||||
if (initial_capacity > 0)
|
||||
{
|
||||
initial_capacity = math::next_power_of_2(initial_capacity);
|
||||
list.entries = malloc_aligned(Type, initial_capacity, .alignment = Type[1].alignof, .using = using)!!;
|
||||
}
|
||||
else
|
||||
{
|
||||
list.entries = null;
|
||||
}
|
||||
list.capacity = initial_capacity;
|
||||
}
|
||||
|
||||
fn void List.tinit(List* list, usz initial_capacity = 16)
|
||||
{
|
||||
list.init(initial_capacity, mem::temp()) @inline;
|
||||
}
|
||||
|
||||
fn String List.to_string(List* list, Allocator* using = mem::heap()) @dynamic
|
||||
{
|
||||
if (!list.size) return "[]".copy(using);
|
||||
if (list.size == 1) return string::printf("[%s]", list.entries[0], .using = using);
|
||||
|
||||
@stack_mem(512 + 128; Allocator* mem)
|
||||
{
|
||||
DString str;
|
||||
str.init(512, mem);
|
||||
str.append("[");
|
||||
foreach (i, element : list.entries[:list.size])
|
||||
{
|
||||
if (i != 0) str.append(", ");
|
||||
str.printf("%s", element);
|
||||
}
|
||||
str.printf("]");
|
||||
return str.copy_str(using);
|
||||
};
|
||||
}
|
||||
|
||||
fn void List.push(List* list, Type element) @inline
|
||||
{
|
||||
list.append(element);
|
||||
}
|
||||
|
||||
fn void List.append(List* list, Type element)
|
||||
{
|
||||
list.ensure_capacity();
|
||||
list.entries[list.size++] = element;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require list.size > 0
|
||||
*/
|
||||
fn Type List.pop(List* list)
|
||||
{
|
||||
return list.entries[--list.size];
|
||||
}
|
||||
|
||||
fn void List.clear(List* list)
|
||||
{
|
||||
list.size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require list.size > 0
|
||||
*/
|
||||
fn Type List.pop_first(List* list)
|
||||
{
|
||||
Type value = list.entries[0];
|
||||
list.remove_at(0);
|
||||
return value;
|
||||
}
|
||||
|
||||
fn void List.remove_at(List* list, usz index)
|
||||
{
|
||||
for (usz i = index + 1; i < list.size; i++)
|
||||
{
|
||||
list.entries[i - 1] = list.entries[i];
|
||||
}
|
||||
list.size--;
|
||||
}
|
||||
|
||||
fn void List.add_all(List* list, List* other_list)
|
||||
{
|
||||
if (!other_list.size) return;
|
||||
list.reserve(other_list.size);
|
||||
foreach (&value : other_list)
|
||||
{
|
||||
list.entries[list.size++] = *value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn Type[] List.to_array(List* list, Allocator* using = mem::heap())
|
||||
{
|
||||
if (!list.size) return Type[] {};
|
||||
Type[] result = malloc(Type, list.size, .using = using);
|
||||
result[..] = list.entries[:list.size];
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the elements in a list.
|
||||
*
|
||||
* @param [&inout] list "The list to reverse"
|
||||
**/
|
||||
fn void List.reverse(List* list)
|
||||
{
|
||||
if (list.size < 2) return;
|
||||
usz half = list.size / 2U;
|
||||
usz end = list.size - 1;
|
||||
for (usz i = 0; i < half; i++)
|
||||
{
|
||||
@swap(list.entries[i], list.entries[end - i]);
|
||||
}
|
||||
}
|
||||
|
||||
fn Type[] List.array_view(List* list)
|
||||
{
|
||||
return list.entries[:list.size];
|
||||
}
|
||||
|
||||
fn void List.add_array(List* list, Type[] array)
|
||||
{
|
||||
if (!array.len) return;
|
||||
list.reserve(array.len);
|
||||
foreach (&value : array)
|
||||
{
|
||||
list.entries[list.size++] = *value;
|
||||
}
|
||||
}
|
||||
|
||||
fn void List.push_front(List* list, Type type) @inline
|
||||
{
|
||||
list.insert_at(0, type);
|
||||
}
|
||||
|
||||
fn void List.insert_at(List* list, usz index, Type type)
|
||||
{
|
||||
list.ensure_capacity();
|
||||
for (usz i = list.size; i > index; i--)
|
||||
{
|
||||
list.entries[i] = list.entries[i - 1];
|
||||
}
|
||||
list.size++;
|
||||
list.entries[index] = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index < list.size
|
||||
**/
|
||||
fn void List.set_at(List* list, usz index, Type type)
|
||||
{
|
||||
list.entries[index] = type;
|
||||
}
|
||||
|
||||
fn void List.remove_last(List* list)
|
||||
{
|
||||
list.size--;
|
||||
}
|
||||
|
||||
fn void List.remove_first(List* list)
|
||||
{
|
||||
list.remove_at(0);
|
||||
}
|
||||
|
||||
fn Type* List.first(List* list)
|
||||
{
|
||||
return list.size ? &list.entries[0] : null;
|
||||
}
|
||||
|
||||
fn Type* List.last(List* list)
|
||||
{
|
||||
return list.size ? &list.entries[list.size - 1] : null;
|
||||
}
|
||||
|
||||
fn bool List.is_empty(List* list)
|
||||
{
|
||||
return !list.size;
|
||||
}
|
||||
|
||||
fn usz List.len(List* list) @operator(len)
|
||||
{
|
||||
return list.size;
|
||||
}
|
||||
|
||||
fn Type List.get(List* list, usz index)
|
||||
{
|
||||
return list.entries[index];
|
||||
}
|
||||
|
||||
fn void List.free(List* list)
|
||||
{
|
||||
if (!list.allocator) return;
|
||||
free_aligned(list.entries, .using = list.allocator);
|
||||
list.capacity = 0;
|
||||
list.size = 0;
|
||||
list.entries = null;
|
||||
}
|
||||
|
||||
fn void List.swap(List* list, usz i, usz j)
|
||||
{
|
||||
@swap(list.entries[i], list.entries[j]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] list "The list to remove elements from"
|
||||
* @param filter "The function to determine if it should be removed or not"
|
||||
* @return "the number of deleted elements"
|
||||
**/
|
||||
fn usz List.remove_if(List* list, ElementPredicate filter)
|
||||
{
|
||||
usz size = list.size;
|
||||
for (usz i = size; i > 0; i--)
|
||||
{
|
||||
if (filter(&list.entries[i - 1])) continue;
|
||||
for (usz j = i; j < size; j++)
|
||||
{
|
||||
list.entries[j - 1] = list.entries[j];
|
||||
}
|
||||
list.size--;
|
||||
}
|
||||
return size - list.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] list "The list to remove elements from"
|
||||
* @param selection "The function to determine if it should be kept or not"
|
||||
* @return "the number of deleted elements"
|
||||
**/
|
||||
fn usz List.retain_if(List* list, ElementPredicate selection)
|
||||
{
|
||||
usz size = list.size;
|
||||
for (usz i = size; i > 0; i--)
|
||||
{
|
||||
if (!selection(&list.entries[i - 1])) continue;
|
||||
for (usz j = i; j < size; j++)
|
||||
{
|
||||
list.entries[j - 1] = list.entries[j];
|
||||
}
|
||||
list.size--;
|
||||
}
|
||||
return size - list.size;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reserve at least min_capacity
|
||||
**/
|
||||
fn void List.reserve(List* list, usz min_capacity)
|
||||
{
|
||||
if (!min_capacity) return;
|
||||
if (list.capacity >= min_capacity) return;
|
||||
if (!list.allocator) list.allocator = mem::heap();
|
||||
min_capacity = math::next_power_of_2(min_capacity);
|
||||
list.entries = realloc_aligned(list.entries, Type.sizeof * min_capacity, .alignment = Type[1].alignof, .using = list.allocator) ?? null;
|
||||
list.capacity = min_capacity;
|
||||
}
|
||||
|
||||
macro Type List.@item_at(List &list, usz index) @operator([])
|
||||
{
|
||||
return list.entries[index];
|
||||
}
|
||||
|
||||
fn Type* List.get_ref(List* list, usz index) @operator(&[]) @inline
|
||||
{
|
||||
return &list.entries[index];
|
||||
}
|
||||
|
||||
fn void List.ensure_capacity(List* list, usz added = 1) @inline @private
|
||||
{
|
||||
usz new_size = list.size + added;
|
||||
if (list.capacity > new_size) return;
|
||||
|
||||
assert(new_size < usz.max / 2U);
|
||||
usz new_capacity = list.capacity ? 2U * list.capacity : 16U;
|
||||
while (new_size >= new_capacity) new_capacity *= 2U;
|
||||
list.reserve(new_capacity);
|
||||
}
|
||||
|
||||
// Functions for equatable types
|
||||
|
||||
$if types::is_equatable_type(Type):
|
||||
|
||||
fn usz! List.index_of(List* list, Type type)
|
||||
{
|
||||
foreach (i, v : list)
|
||||
{
|
||||
if (v == type) return i;
|
||||
}
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
fn usz! List.rindex_of(List* list, Type type)
|
||||
{
|
||||
foreach_r (i, v : list)
|
||||
{
|
||||
if (v == type) return i;
|
||||
}
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
fn bool List.equals(List* list, List other_list)
|
||||
{
|
||||
if (list.size != other_list.size) return false;
|
||||
foreach (i, v : list)
|
||||
{
|
||||
if (v != other_list.entries[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for presence of a value in a list.
|
||||
*
|
||||
* @param [&in] list "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(List* list, Type value)
|
||||
{
|
||||
foreach (i, v : list)
|
||||
{
|
||||
if (v == value) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param [&inout] list "The list to remove elements from"
|
||||
* @param value "The value to remove"
|
||||
* @return "the number of deleted elements."
|
||||
**/
|
||||
fn usz List.remove(List* list, Type value)
|
||||
{
|
||||
usz size = list.size;
|
||||
for (usz i = size; i > 0; i--)
|
||||
{
|
||||
if (list.entries[i - 1] != value) continue;
|
||||
for (usz j = i; j < size; j++)
|
||||
{
|
||||
list.entries[j - 1] = list.entries[j];
|
||||
}
|
||||
list.size--;
|
||||
}
|
||||
return size - list.size;
|
||||
}
|
||||
|
||||
fn void List.remove_all(List* list, List* other_list)
|
||||
{
|
||||
if (!other_list.size) return;
|
||||
foreach (v : other_list) list.remove(v);
|
||||
}
|
||||
|
||||
|
||||
$endif
|
||||
|
||||
$if Type.kindof == POINTER:
|
||||
|
||||
/**
|
||||
* @param [&in] list
|
||||
* @return "The number non-null values in the list"
|
||||
**/
|
||||
fn usz List.compact_count(List* list)
|
||||
{
|
||||
usz vals = 0;
|
||||
foreach (v : list) if (v) vals++;
|
||||
return vals;
|
||||
}
|
||||
|
||||
fn usz List.compact(List* list)
|
||||
{
|
||||
usz size = list.size;
|
||||
for (usz i = size; i > 0; i--)
|
||||
{
|
||||
if (list.entries[i - 1]) continue;
|
||||
for (usz j = i; j < size; j++)
|
||||
{
|
||||
list.entries[j - 1] = list.entries[j];
|
||||
}
|
||||
list.size--;
|
||||
}
|
||||
return size - list.size;
|
||||
}
|
||||
|
||||
$endif
|
||||
368
lib/std/collections/map.c3
Normal file
368
lib/std/collections/map.c3
Normal file
@@ -0,0 +1,368 @@
|
||||
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::collections::map<Key, Value>;
|
||||
import std::math;
|
||||
|
||||
const uint DEFAULT_INITIAL_CAPACITY = 16;
|
||||
const uint MAXIMUM_CAPACITY = 1u << 31;
|
||||
const float DEFAULT_LOAD_FACTOR = 0.75;
|
||||
|
||||
|
||||
struct HashMap
|
||||
{
|
||||
Entry*[] table;
|
||||
Allocator* allocator;
|
||||
uint count; // Number of elements
|
||||
uint threshold; // Resize limit
|
||||
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 !map.allocator "Map was already initialized"
|
||||
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
* @require using != null "The allocator must be non-null"
|
||||
**/
|
||||
fn void HashMap.init(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator* using = mem::heap())
|
||||
{
|
||||
capacity = math::next_power_of_2(capacity);
|
||||
map.allocator = using;
|
||||
map.load_factor = load_factor;
|
||||
map.threshold = (uint)(capacity * load_factor);
|
||||
map.table = calloc(Entry*, capacity, .using = using);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require capacity > 0 "The capacity must be 1 or higher"
|
||||
* @require load_factor > 0.0 "The load factor must be higher than 0"
|
||||
* @require !map.allocator "Map was already initialized"
|
||||
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
**/
|
||||
fn void HashMap.tinit(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
map.init(capacity, load_factor, mem::temp());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(HashMap* map)
|
||||
{
|
||||
return map.allocator != null;
|
||||
}
|
||||
|
||||
fn void HashMap.init_from_map(HashMap* map, HashMap* other_map, Allocator* using = mem::heap())
|
||||
{
|
||||
map.init(other_map.table.len, other_map.load_factor, using);
|
||||
map.put_all_for_create(other_map);
|
||||
}
|
||||
|
||||
fn void HashMap.tinit_from_map(HashMap* map, HashMap* other_map)
|
||||
{
|
||||
map.init_from_map(other_map, mem::temp()) @inline;
|
||||
}
|
||||
|
||||
fn bool HashMap.is_empty(HashMap* map) @inline
|
||||
{
|
||||
return !map.count;
|
||||
}
|
||||
|
||||
fn Value*! HashMap.get_ref(HashMap* map, Key key)
|
||||
{
|
||||
if (!map.count) return SearchResult.MISSING?;
|
||||
uint hash = rehash(key.hash());
|
||||
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key)) return &e.value;
|
||||
}
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
fn Entry*! HashMap.get_entry(HashMap* map, Key key)
|
||||
{
|
||||
if (!map.count) return SearchResult.MISSING?;
|
||||
uint hash = rehash(key.hash());
|
||||
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key)) return e;
|
||||
}
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value or update and
|
||||
**/
|
||||
macro Value HashMap.@get_or_set(HashMap* map, Key key, Value #expr)
|
||||
{
|
||||
if (!map.count)
|
||||
{
|
||||
Value val = #expr;
|
||||
map.set(key, val);
|
||||
return val;
|
||||
}
|
||||
uint hash = rehash(key.hash());
|
||||
uint index = index_for(hash, map.table.len);
|
||||
for (Entry *e = map.table[index]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key)) return e.value;
|
||||
}
|
||||
Value val = #expr;
|
||||
map.add_entry(hash, key, val, index);
|
||||
return val;
|
||||
}
|
||||
|
||||
fn Value! HashMap.get(HashMap* map, Key key) @operator([])
|
||||
{
|
||||
return *map.get_ref(key) @inline;
|
||||
}
|
||||
|
||||
fn bool HashMap.has_key(HashMap* map, Key key)
|
||||
{
|
||||
return @ok(map.get_ref(key));
|
||||
}
|
||||
|
||||
fn bool HashMap.set(HashMap* map, Key key, Value value) @operator([]=)
|
||||
{
|
||||
// If the map isn't initialized, use the defaults to initialize it.
|
||||
if (!map.allocator)
|
||||
{
|
||||
map.init();
|
||||
}
|
||||
uint hash = rehash(key.hash());
|
||||
uint index = index_for(hash, map.table.len);
|
||||
for (Entry *e = map.table[index]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key))
|
||||
{
|
||||
e.value = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
map.add_entry(hash, key, value, index);
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void! HashMap.remove(HashMap* map, Key key) @maydiscard
|
||||
{
|
||||
if (!map.remove_entry_for_key(key)) return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
fn void HashMap.clear(HashMap* map)
|
||||
{
|
||||
if (!map.count) return;
|
||||
foreach (Entry** &entry_ref : map.table)
|
||||
{
|
||||
Entry* entry = *entry_ref;
|
||||
if (!entry) continue;
|
||||
map.free_internal(entry);
|
||||
*entry_ref = null;
|
||||
}
|
||||
map.count = 0;
|
||||
}
|
||||
|
||||
fn void HashMap.free(HashMap* map)
|
||||
{
|
||||
if (!map.allocator) return;
|
||||
map.clear();
|
||||
map.free_internal(map.table.ptr);
|
||||
map.table = Entry*[] {};
|
||||
}
|
||||
|
||||
fn Key[] HashMap.key_tlist(HashMap* map)
|
||||
{
|
||||
return map.key_list(mem::temp()) @inline;
|
||||
}
|
||||
|
||||
fn Key[] HashMap.key_list(HashMap* map, Allocator* using = mem::heap())
|
||||
{
|
||||
if (!map.count) return Key[] {};
|
||||
|
||||
Key[] list = calloc(Key, map.count, .using = using);
|
||||
usz index = 0;
|
||||
foreach (Entry* entry : map.table)
|
||||
{
|
||||
while (entry)
|
||||
{
|
||||
list[index++] = entry.key;
|
||||
entry = entry.next;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
fn Value[] HashMap.value_tlist(HashMap* map)
|
||||
{
|
||||
return map.value_list(mem::temp()) @inline;
|
||||
}
|
||||
|
||||
fn Value[] HashMap.value_list(HashMap* map, Allocator* using = mem::heap())
|
||||
{
|
||||
if (!map.count) return Value[] {};
|
||||
Value[] list = calloc(Value, map.count, .using = using);
|
||||
usz index = 0;
|
||||
foreach (Entry* entry : map.table)
|
||||
{
|
||||
while (entry)
|
||||
{
|
||||
list[index++] = entry.value;
|
||||
entry = entry.next;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
$if types::is_equatable(Value):
|
||||
fn bool HashMap.has_value(HashMap* map, Value v)
|
||||
{
|
||||
if (!map.count) return false;
|
||||
foreach (Entry* entry : map.table)
|
||||
{
|
||||
while (entry)
|
||||
{
|
||||
if (equals(v, entry.value)) return true;
|
||||
entry = entry.next;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
$endif
|
||||
|
||||
// --- private methods
|
||||
|
||||
fn void HashMap.add_entry(HashMap* map, uint hash, Key key, Value value, uint bucket_index) @private
|
||||
{
|
||||
Entry* entry = malloc(Entry, .using = map.allocator);
|
||||
*entry = { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] };
|
||||
map.table[bucket_index] = entry;
|
||||
if (map.count++ >= map.threshold)
|
||||
{
|
||||
map.resize(map.table.len * 2);
|
||||
}
|
||||
}
|
||||
|
||||
fn void HashMap.resize(HashMap* map, uint new_capacity) @private
|
||||
{
|
||||
Entry*[] old_table = map.table;
|
||||
uint old_capacity = old_table.len;
|
||||
if (old_capacity == MAXIMUM_CAPACITY)
|
||||
{
|
||||
map.threshold = uint.max;
|
||||
return;
|
||||
}
|
||||
Entry*[] new_table = calloc(Entry*, new_capacity, .using = map.allocator);
|
||||
map.transfer(new_table);
|
||||
map.table = new_table;
|
||||
map.free_internal(old_table.ptr);
|
||||
map.threshold = (uint)(new_capacity * map.load_factor);
|
||||
}
|
||||
|
||||
fn uint rehash(uint hash) @inline @private
|
||||
{
|
||||
hash ^= (hash >> 20) ^ (hash >> 12);
|
||||
return hash ^ ((hash >> 7) ^ (hash >> 4));
|
||||
}
|
||||
|
||||
macro uint index_for(uint hash, uint capacity) @private
|
||||
{
|
||||
return hash & (capacity - 1);
|
||||
}
|
||||
|
||||
fn void HashMap.transfer(HashMap* map, Entry*[] new_table) @private
|
||||
{
|
||||
Entry*[] src = map.table;
|
||||
uint new_capacity = new_table.len;
|
||||
foreach (uint j, Entry *e : src)
|
||||
{
|
||||
if (!e) continue;
|
||||
do
|
||||
{
|
||||
Entry* next = e.next;
|
||||
uint i = index_for(e.hash, new_capacity);
|
||||
e.next = new_table[i];
|
||||
new_table[i] = e;
|
||||
e = next;
|
||||
}
|
||||
while (e);
|
||||
}
|
||||
}
|
||||
|
||||
fn void HashMap.put_all_for_create(HashMap* map, HashMap* other_map) @private
|
||||
{
|
||||
if (!other_map.count) return;
|
||||
foreach (Entry *e : other_map.table)
|
||||
{
|
||||
if (!e) continue;
|
||||
map.put_for_create(e.key, e.value);
|
||||
}
|
||||
}
|
||||
|
||||
fn void HashMap.put_for_create(HashMap* map, Key key, Value value) @private
|
||||
{
|
||||
uint hash = rehash(key.hash());
|
||||
uint i = index_for(hash, map.table.len);
|
||||
for (Entry *e = map.table[i]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key))
|
||||
{
|
||||
e.value = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
map.create_entry(hash, key, value, i);
|
||||
}
|
||||
|
||||
fn void HashMap.free_internal(HashMap* map, void* ptr) @inline @private
|
||||
{
|
||||
map.allocator.free(ptr)!!;
|
||||
}
|
||||
|
||||
fn bool HashMap.remove_entry_for_key(HashMap* map, Key key) @private
|
||||
{
|
||||
uint hash = rehash(key.hash());
|
||||
uint i = index_for(hash, map.table.len);
|
||||
Entry* prev = map.table[i];
|
||||
Entry* e = prev;
|
||||
while (e)
|
||||
{
|
||||
Entry *next = e.next;
|
||||
if (e.hash == hash && equals(key, e.key))
|
||||
{
|
||||
map.count--;
|
||||
if (prev == e)
|
||||
{
|
||||
map.table[i] = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev.next = next;
|
||||
}
|
||||
map.free_internal(e);
|
||||
return true;
|
||||
}
|
||||
prev = e;
|
||||
e = next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void HashMap.create_entry(HashMap* map, uint hash, Key key, Value value, int bucket_index) @private
|
||||
{
|
||||
Entry *e = map.table[bucket_index];
|
||||
Entry* entry = malloc(Entry, .using = map.allocator);
|
||||
*entry = { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] };
|
||||
map.table[bucket_index] = entry;
|
||||
map.count++;
|
||||
}
|
||||
|
||||
struct Entry
|
||||
{
|
||||
uint hash;
|
||||
Key key;
|
||||
Value value;
|
||||
Entry* next;
|
||||
}
|
||||
474
lib/std/collections/object.c3
Normal file
474
lib/std/collections/object.c3
Normal file
@@ -0,0 +1,474 @@
|
||||
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::collections::object;
|
||||
import std::collections::map;
|
||||
import std::collections::list;
|
||||
import std::io;
|
||||
|
||||
const Object TRUE_OBJECT = { .b = true, .type = bool.typeid };
|
||||
const Object FALSE_OBJECT = { .b = false, .type = bool.typeid };
|
||||
const Object NULL_OBJECT = { .type = void*.typeid };
|
||||
|
||||
struct Object
|
||||
{
|
||||
typeid type;
|
||||
Allocator* allocator;
|
||||
union
|
||||
{
|
||||
uint128 i;
|
||||
double f;
|
||||
bool b;
|
||||
String s;
|
||||
void* other;
|
||||
ObjectInternalList array;
|
||||
ObjectInternalMap map;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn void! Object.to_format(Object* o, Formatter* formatter) @dynamic
|
||||
{
|
||||
switch (o.type)
|
||||
{
|
||||
case void:
|
||||
formatter.printf("{}")!;
|
||||
case void*:
|
||||
formatter.printf("null")!;
|
||||
case String:
|
||||
formatter.printf(`"%s"`, o.s)!;
|
||||
case bool:
|
||||
formatter.printf(o.b ? "true" : "false")!;
|
||||
case ObjectInternalList:
|
||||
formatter.printf("[")!;
|
||||
foreach (i, ol : o.array)
|
||||
{
|
||||
formatter.printf(i == 0 ? " " : ", ")!;
|
||||
ol.to_format(formatter)!;
|
||||
}
|
||||
formatter.printf(" ]")!;
|
||||
case ObjectInternalMap:
|
||||
formatter.printf("{")!;
|
||||
@pool()
|
||||
{
|
||||
foreach (i, key : o.map.key_tlist())
|
||||
{
|
||||
formatter.printf(i == 0 ? " " : ", ")!;
|
||||
formatter.printf(`"%s": `, key)!;
|
||||
o.map.get(key).to_format(formatter)!;
|
||||
}
|
||||
};
|
||||
formatter.printf(" }")!;
|
||||
default:
|
||||
switch (o.type.kindof)
|
||||
{
|
||||
case SIGNED_INT:
|
||||
formatter.printf("%d", o.i)!;
|
||||
case UNSIGNED_INT:
|
||||
formatter.printf("%d", (uint128)o.i)!;
|
||||
case FLOAT:
|
||||
formatter.printf("%d", o.f)!;
|
||||
case ENUM:
|
||||
formatter.printf("%d", o.i)!;
|
||||
default:
|
||||
formatter.printf("<>")!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn Object* new_obj(Allocator* using = mem::heap())
|
||||
{
|
||||
Object* o = malloc(Object, .using = using);
|
||||
*o = { .allocator = using, .type = void.typeid };
|
||||
return o;
|
||||
}
|
||||
|
||||
fn Object* new_null()
|
||||
{
|
||||
return &NULL_OBJECT;
|
||||
}
|
||||
|
||||
fn Object* new_int(int128 i, Allocator* using = mem::heap())
|
||||
{
|
||||
Object* o = malloc(Object, .using = using);
|
||||
*o = { .i = i, .allocator = using, .type = int128.typeid };
|
||||
return o;
|
||||
}
|
||||
|
||||
macro Object* new_enum(e, Allocator* using = mem::heap())
|
||||
{
|
||||
Object* o = malloc(Object, .using = using);
|
||||
*o = { .i = (int128)e, .allocator = using, .type = $typeof(e).typeid };
|
||||
return o;
|
||||
}
|
||||
|
||||
fn Object* new_float(double f, Allocator* using = mem::current_allocator())
|
||||
{
|
||||
Object* o = malloc(Object, .using = using);
|
||||
*o = { .f = f, .allocator = using, .type = double.typeid };
|
||||
return o;
|
||||
}
|
||||
|
||||
fn Object* new_string(String s, Allocator* using = mem::heap())
|
||||
{
|
||||
Object* o = malloc(Object, .using = using);
|
||||
*o = { .s = s.copy(using), .allocator = using, .type = String.typeid };
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
fn Object* new_bool(bool b)
|
||||
{
|
||||
return b ? &TRUE_OBJECT : &FALSE_OBJECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] o
|
||||
**/
|
||||
fn void Object.free(Object* o)
|
||||
{
|
||||
switch (o.type)
|
||||
{
|
||||
case void:
|
||||
break;
|
||||
case String:
|
||||
free(o.s, .using = o.allocator);
|
||||
case ObjectInternalList:
|
||||
foreach (ol : o.array)
|
||||
{
|
||||
ol.free();
|
||||
}
|
||||
o.array.free();
|
||||
case ObjectInternalMap:
|
||||
@pool()
|
||||
{
|
||||
foreach (key : o.map.key_tlist())
|
||||
{
|
||||
o.map.get(key).free();
|
||||
free(key, .using = o.allocator);
|
||||
}
|
||||
o.map.free();
|
||||
};
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (o.allocator) free(o, .using = o.allocator);
|
||||
}
|
||||
|
||||
fn bool Object.is_null(Object* this) @inline => this == &NULL_OBJECT;
|
||||
fn bool Object.is_empty(Object* this) @inline => this.type == void.typeid;
|
||||
fn bool Object.is_map(Object* this) @inline => this.type == ObjectInternalMap.typeid;
|
||||
fn bool Object.is_array(Object* this) @inline => this.type == ObjectInternalList.typeid;
|
||||
fn bool Object.is_bool(Object* this) @inline => this.type == bool.typeid;
|
||||
fn bool Object.is_string(Object* this) @inline => this.type == String.typeid;
|
||||
fn bool Object.is_float(Object* this) @inline => this.type == double.typeid;
|
||||
fn bool Object.is_int(Object* this) @inline => this.type == int128.typeid;
|
||||
fn bool Object.is_keyable(Object* this) => this.is_empty() || this.is_map();
|
||||
fn bool Object.is_indexable(Object* this) => this.is_empty() || this.is_array();
|
||||
|
||||
/**
|
||||
* @require o.is_keyable()
|
||||
**/
|
||||
fn void Object.init_map_if_needed(Object* o) @private
|
||||
{
|
||||
if (o.is_empty())
|
||||
{
|
||||
o.type = ObjectInternalMap.typeid;
|
||||
o.map.init(.using = o.allocator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require o.is_indexable()
|
||||
**/
|
||||
fn void Object.init_array_if_needed(Object* o) @private
|
||||
{
|
||||
if (o.is_empty())
|
||||
{
|
||||
o.type = ObjectInternalList.typeid;
|
||||
o.array.init(.using = o.allocator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require o.is_keyable()
|
||||
**/
|
||||
fn void Object.set_object(Object* o, String key, Object* new_object) @private
|
||||
{
|
||||
o.init_map_if_needed();
|
||||
ObjectInternalMapEntry*! entry = o.map.get_entry(key);
|
||||
defer
|
||||
{
|
||||
(void)free(entry.key, .using = o.allocator);
|
||||
entry.value.free();
|
||||
}
|
||||
o.map.set(key.copy(o.map.allocator), new_object);
|
||||
}
|
||||
|
||||
macro Object* object_from_value(value) @private
|
||||
{
|
||||
var $Type = $typeof(value);
|
||||
|
||||
$switch
|
||||
$case types::is_int($Type):
|
||||
return new_int(value);
|
||||
$case types::is_float($Type):
|
||||
return new_float(value);
|
||||
$case $Type.typeid == String.typeid:
|
||||
return new_string(value);
|
||||
$case $Type.typeid == bool.typeid:
|
||||
return new_bool(value);
|
||||
$case $Type.typeid == Object*.typeid:
|
||||
return value;
|
||||
$case $Type.typeid == void*.typeid:
|
||||
assert(value == null);
|
||||
return &NULL_OBJECT;
|
||||
$case $checks(String s = value):
|
||||
return new_string(value);
|
||||
$default:
|
||||
$error "Unsupported object type.";
|
||||
$endswitch
|
||||
|
||||
}
|
||||
|
||||
macro Object* Object.set(Object* o, String key, value)
|
||||
{
|
||||
Object* val = object_from_value(value);
|
||||
o.set_object(key, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require o.is_indexable()
|
||||
**/
|
||||
macro Object* Object.set_at(Object* o, usz index, String key, value)
|
||||
{
|
||||
Object* val = object_from_value(value);
|
||||
o.set_object_at(key, index, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require o.is_indexable()
|
||||
* @ensure return != null
|
||||
**/
|
||||
macro Object* Object.append(Object* o, value)
|
||||
{
|
||||
Object* val = object_from_value(value);
|
||||
o.append_object(val);
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require o.is_keyable()
|
||||
**/
|
||||
fn Object*! Object.get(Object* o, String key) => o.is_empty() ? SearchResult.MISSING? : o.map.get(key);
|
||||
|
||||
|
||||
fn bool Object.has_key(Object* o, String key) => o.is_map() && o.map.has_key(key);
|
||||
|
||||
/**
|
||||
* @require o.is_indexable()
|
||||
**/
|
||||
fn Object* Object.get_at(Object* o, usz index)
|
||||
{
|
||||
return o.array.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require o.is_indexable()
|
||||
**/
|
||||
fn void Object.append_object(Object* o, Object* to_append)
|
||||
{
|
||||
o.init_array_if_needed();
|
||||
o.array.append(to_append);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require o.is_indexable()
|
||||
**/
|
||||
fn void Object.set_object_at(Object* o, usz index, Object* to_set)
|
||||
{
|
||||
o.init_array_if_needed();
|
||||
while (o.array.len() < index)
|
||||
{
|
||||
o.array.append(&NULL_OBJECT);
|
||||
}
|
||||
if (o.array.len() == index)
|
||||
{
|
||||
o.array.append(to_set);
|
||||
return;
|
||||
}
|
||||
o.array.get(index).free();
|
||||
o.array.set_at(index, to_set);
|
||||
}
|
||||
|
||||
macro get_integer_value(Object* value, $Type)
|
||||
{
|
||||
if (value.is_float())
|
||||
{
|
||||
return ($Type)value.f;
|
||||
}
|
||||
if (value.is_string())
|
||||
{
|
||||
$if $Type.kindof == TypeKind.SIGNED_INT:
|
||||
return ($Type)value.s.to_int128();
|
||||
$else
|
||||
return ($Type)value.s.to_uint128();
|
||||
$endif
|
||||
}
|
||||
if (!value.is_int()) return NumberConversion.MALFORMED_INTEGER?;
|
||||
return ($Type)value.i;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require o.is_indexable()
|
||||
**/
|
||||
macro Object.get_integer_at(Object* o, $Type, usz index) @private
|
||||
{
|
||||
return get_integer_value(o.get_at(index), $Type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require o.is_keyable()
|
||||
**/
|
||||
macro Object.get_integer(Object* o, $Type, String key) @private
|
||||
{
|
||||
return get_integer_value(o.get(key), $Type);
|
||||
}
|
||||
|
||||
fn ichar! Object.get_ichar(Object* o, String key) => o.get_integer(ichar, key);
|
||||
fn short! Object.get_short(Object* o, String key) => o.get_integer(short, key);
|
||||
fn int! Object.get_int(Object* o, String key) => o.get_integer(int, key);
|
||||
fn long! Object.get_long(Object* o, String key) => o.get_integer(long, key);
|
||||
fn int128! Object.get_int128(Object* o, String key) => o.get_integer(int128, key);
|
||||
|
||||
fn ichar! Object.get_ichar_at(Object* o, usz index) => o.get_integer_at(ichar, index);
|
||||
fn short! Object.get_short_at(Object* o, usz index) => o.get_integer_at(short, index);
|
||||
fn int! Object.get_int_at(Object* o, usz index) => o.get_integer_at(int, index);
|
||||
fn long! Object.get_long_at(Object* o, usz index) => o.get_integer_at(long, index);
|
||||
fn int128! Object.get_int128_at(Object* o, usz index) => o.get_integer_at(int128, index);
|
||||
|
||||
fn char! Object.get_char(Object* o, String key) => o.get_integer(ichar, key);
|
||||
fn short! Object.get_ushort(Object* o, String key) => o.get_integer(ushort, key);
|
||||
fn uint! Object.get_uint(Object* o, String key) => o.get_integer(uint, key);
|
||||
fn ulong! Object.get_ulong(Object* o, String key) => o.get_integer(ulong, key);
|
||||
fn uint128! Object.get_uint128(Object* o, String key) => o.get_integer(uint128, key);
|
||||
|
||||
fn char! Object.get_char_at(Object* o, usz index) => o.get_integer_at(char, index);
|
||||
fn ushort! Object.get_ushort_at(Object* o, usz index) => o.get_integer_at(ushort, index);
|
||||
fn uint! Object.get_uint_at(Object* o, usz index) => o.get_integer_at(uint, index);
|
||||
fn ulong! Object.get_ulong_at(Object* o, usz index) => o.get_integer_at(ulong, index);
|
||||
fn uint128! Object.get_uint128_at(Object* o, usz index) => o.get_integer_at(uint128, index);
|
||||
|
||||
/**
|
||||
* @require o.is_keyable()
|
||||
**/
|
||||
fn String! Object.get_string(Object* o, String key)
|
||||
{
|
||||
Object* value = o.get(key)!;
|
||||
assert(value.is_string());
|
||||
return value.s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require o.is_indexable()
|
||||
**/
|
||||
fn String Object.get_string_at(Object* o, usz index)
|
||||
{
|
||||
Object* value = o.get_at(index);
|
||||
assert(value.is_string());
|
||||
return value.s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require o.is_keyable()
|
||||
**/
|
||||
macro String! Object.get_enum(Object* o, $EnumType, String key)
|
||||
{
|
||||
Object value = o.get(key)!;
|
||||
assert($EnumType.typeid == value.type);
|
||||
return ($EnumType)value.i;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require o.is_indexable()
|
||||
**/
|
||||
macro String Object.get_enum_at(Object* o, $EnumType, usz index)
|
||||
{
|
||||
Object value = o.get_at(index);
|
||||
assert($EnumType.typeid == value.type);
|
||||
return ($EnumType)value.i;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require o.is_keyable()
|
||||
**/
|
||||
fn bool! Object.get_bool(Object* o, String key)
|
||||
{
|
||||
Object* value = o.get(key)!;
|
||||
assert(value.is_bool());
|
||||
return value.b;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @require o.is_indexable()
|
||||
**/
|
||||
fn bool Object.get_bool_at(Object* o, usz index)
|
||||
{
|
||||
Object* value = o.get_at(index);
|
||||
assert(value.is_bool());
|
||||
return value.b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require o.is_keyable()
|
||||
**/
|
||||
fn double! Object.get_float(Object* o, String key)
|
||||
{
|
||||
Object* value = o.get(key)!;
|
||||
switch (value.type.kindof)
|
||||
{
|
||||
case SIGNED_INT:
|
||||
return (double)value.i;
|
||||
case UNSIGNED_INT:
|
||||
return (double)(uint128)value.i;
|
||||
case FLOAT:
|
||||
return value.f;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require o.is_indexable()
|
||||
**/
|
||||
fn double Object.get_float_at(Object* o, usz index)
|
||||
{
|
||||
Object* value = o.get_at(index);
|
||||
switch (value.type.kindof)
|
||||
{
|
||||
case SIGNED_INT:
|
||||
return (double)value.i;
|
||||
case UNSIGNED_INT:
|
||||
return (double)(uint128)value.i;
|
||||
case FLOAT:
|
||||
return value.f;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
fn Object* Object.get_or_create_obj(Object* o, String key)
|
||||
{
|
||||
if (try obj = o.get(key) && !obj.is_null()) return obj;
|
||||
Object* container = new_obj();
|
||||
o.set(key, container);
|
||||
return container;
|
||||
}
|
||||
|
||||
def ObjectInternalMap @private = HashMap<String, Object*>;
|
||||
def ObjectInternalList @private = List<Object*>;
|
||||
def ObjectInternalMapEntry @private = Entry<String, Object*>;
|
||||
|
||||
113
lib/std/collections/priorityqueue.c3
Normal file
113
lib/std/collections/priorityqueue.c3
Normal file
@@ -0,0 +1,113 @@
|
||||
// priorityqueue.c3
|
||||
// A priority queue using a classic binary heap for C3.
|
||||
//
|
||||
// Copyright (c) 2022 David Kopec
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
module std::collections::priorityqueue<Type>;
|
||||
import std::collections::list;
|
||||
|
||||
def Heap = List<Type>;
|
||||
|
||||
struct PriorityQueue
|
||||
{
|
||||
Heap heap;
|
||||
bool max; // true if max-heap, false if min-heap
|
||||
}
|
||||
|
||||
fn void PriorityQueue.push(PriorityQueue* pq, Type element)
|
||||
{
|
||||
pq.heap.push(element);
|
||||
usz i = pq.heap.len() - 1;
|
||||
while (i > 0)
|
||||
{
|
||||
usz parent = (i - 1) / 2;
|
||||
if ((pq.max && greater(pq.heap.get(i), pq.heap.get(parent))) || (!pq.max && less(pq.heap.get(i), pq.heap.get(parent))))
|
||||
{
|
||||
pq.heap.swap(i, parent);
|
||||
i = parent;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require pq != null
|
||||
*/
|
||||
fn Type! PriorityQueue.pop(PriorityQueue* pq)
|
||||
{
|
||||
usz i = 0;
|
||||
usz len = pq.heap.len() @inline;
|
||||
if (!len) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
usz newCount = len - 1;
|
||||
pq.heap.swap(0, newCount);
|
||||
while ((2 * i + 1) < newCount)
|
||||
{
|
||||
usz j = 2 * i + 1;
|
||||
if (((j + 1) < newCount) &&
|
||||
((pq.max && greater(pq.heap.get(j + 1), pq.heap[j]))
|
||||
|| (!pq.max && less(pq.heap.get(j + 1), pq.heap.get(j)))))
|
||||
{
|
||||
j++;
|
||||
}
|
||||
if ((pq.max && less(pq.heap.get(i), pq.heap.get(j))) || (!pq.max && greater(pq.heap.get(i), pq.heap.get(j))))
|
||||
{
|
||||
pq.heap.swap(i, j);
|
||||
i = j;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return pq.heap.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @require pq != null
|
||||
*/
|
||||
fn Type! PriorityQueue.peek(PriorityQueue* pq)
|
||||
{
|
||||
if (!pq.len()) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
return pq.heap.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require pq != null
|
||||
*/
|
||||
fn void PriorityQueue.free(PriorityQueue* pq)
|
||||
{
|
||||
pq.heap.free();
|
||||
}
|
||||
|
||||
/**
|
||||
* @require pq != null
|
||||
*/
|
||||
fn usz PriorityQueue.len(PriorityQueue* pq) @operator(len)
|
||||
{
|
||||
return pq.heap.len();
|
||||
}
|
||||
|
||||
/**
|
||||
* @require pq != null, index < pq.len()
|
||||
*/
|
||||
fn Type PriorityQueue.peek_at(PriorityQueue* pq, usz index) @operator([])
|
||||
{
|
||||
return pq.heap[index];
|
||||
}
|
||||
149
lib/std/core/allocators/arena_allocator.c3
Normal file
149
lib/std/core/allocators/arena_allocator.c3
Normal file
@@ -0,0 +1,149 @@
|
||||
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::core::mem::allocator;
|
||||
|
||||
struct ArenaAllocator
|
||||
{
|
||||
inline Allocator allocator;
|
||||
char[] data;
|
||||
usz used;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a memory arena for use using the provided bytes.
|
||||
*
|
||||
* @require this != null
|
||||
**/
|
||||
fn void ArenaAllocator.init(ArenaAllocator* this, char[] data)
|
||||
{
|
||||
this.function = &arena_allocator_function;
|
||||
this.data = data;
|
||||
this.used = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require this != null
|
||||
**/
|
||||
fn void ArenaAllocator.reset(ArenaAllocator* this)
|
||||
{
|
||||
this.used = 0;
|
||||
}
|
||||
|
||||
struct ArenaAllocatorHeader
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
}
|
||||
/**
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
* @require data `unexpectedly missing the allocator`
|
||||
*/
|
||||
fn void*! arena_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
|
||||
{
|
||||
ArenaAllocator* arena = (ArenaAllocator*)data;
|
||||
bool clear = false;
|
||||
switch (kind)
|
||||
{
|
||||
case CALLOC:
|
||||
case ALIGNED_CALLOC:
|
||||
clear = true;
|
||||
nextcase;
|
||||
case ALLOC:
|
||||
case ALIGNED_ALLOC:
|
||||
assert(!old_pointer, "Unexpected old pointer for alloc.");
|
||||
if (!size) return null;
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
void* mem = arena._alloc(size, alignment, offset)!;
|
||||
if (clear) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return mem;
|
||||
case ALIGNED_REALLOC:
|
||||
case REALLOC:
|
||||
if (!size) nextcase FREE;
|
||||
if (!old_pointer) nextcase ALLOC;
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
return arena._realloc(old_pointer, size, alignment, offset)!;
|
||||
case ALIGNED_FREE:
|
||||
case FREE:
|
||||
if (!old_pointer) return null;
|
||||
assert((uptr)old_pointer >= (uptr)arena.data.ptr, "Pointer originates from a different allocator.");
|
||||
ArenaAllocatorHeader* header = old_pointer - ArenaAllocatorHeader.sizeof;
|
||||
// Reclaim memory if it's the last element.
|
||||
if (old_pointer + header.size == &arena.data[arena.used])
|
||||
{
|
||||
arena.used -= header.size + ArenaAllocatorHeader.sizeof;
|
||||
}
|
||||
return null;
|
||||
case MARK:
|
||||
return (void*)(uptr)arena.used;
|
||||
case RESET:
|
||||
arena.used = size;
|
||||
return null;
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
|
||||
/**
|
||||
* @require alignment > 0 `alignment must be non zero`
|
||||
* @require math::is_power_of_2(alignment)
|
||||
* @require size > 0
|
||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
|
||||
* @require offset <= size && offset >= 0
|
||||
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
|
||||
* @require this != null
|
||||
**/
|
||||
fn void*! ArenaAllocator._alloc(ArenaAllocator* this, usz size, usz alignment, usz offset) @private
|
||||
{
|
||||
usz total_len = this.data.len;
|
||||
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?;
|
||||
void* start_mem = this.data.ptr;
|
||||
void* unaligned_pointer_to_offset = start_mem + this.used + ArenaAllocatorHeader.sizeof + offset;
|
||||
void* aligned_pointer_to_offset = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
|
||||
usz end = (usz)(aligned_pointer_to_offset - this.data.ptr) + size - offset;
|
||||
if (end > total_len) return AllocationFailure.OUT_OF_MEMORY?;
|
||||
this.used = end;
|
||||
void* mem = aligned_pointer_to_offset - offset;
|
||||
ArenaAllocatorHeader* header = mem - ArenaAllocatorHeader.sizeof;
|
||||
header.size = size;
|
||||
return mem;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require alignment > 0 `alignment must be non zero`
|
||||
* @require math::is_power_of_2(alignment)
|
||||
* @require size > 0
|
||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
|
||||
* @require offset <= size && offset >= 0
|
||||
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
|
||||
* @require this != null
|
||||
**/
|
||||
fn void*! ArenaAllocator._realloc(ArenaAllocator* this, void *old_pointer, usz size, usz alignment, usz offset) @private
|
||||
{
|
||||
assert(old_pointer >= this.data.ptr, "Pointer originates from a different allocator.");
|
||||
usz total_len = this.data.len;
|
||||
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?;
|
||||
ArenaAllocatorHeader* header = old_pointer - ArenaAllocatorHeader.sizeof;
|
||||
usz old_size = header.size;
|
||||
// Do last allocation and alignment match?
|
||||
if (&this.data[this.used] == old_pointer + old_size && mem::ptr_is_aligned(old_pointer + offset, alignment))
|
||||
{
|
||||
if (old_size >= size)
|
||||
{
|
||||
this.used -= old_size - size;
|
||||
}
|
||||
else
|
||||
{
|
||||
usz new_used = this.used + size - old_size;
|
||||
if (new_used > total_len) return AllocationFailure.OUT_OF_MEMORY?;
|
||||
this.used = new_used;
|
||||
}
|
||||
header.size = size;
|
||||
return old_pointer;
|
||||
}
|
||||
// Otherwise just allocate new memory.
|
||||
void* mem = this._alloc(size, alignment, offset)!;
|
||||
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return mem;
|
||||
}
|
||||
247
lib/std/core/allocators/dynamic_arena.c3
Normal file
247
lib/std/core/allocators/dynamic_arena.c3
Normal file
@@ -0,0 +1,247 @@
|
||||
// Copyright (c) 2021 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::mem::allocator;
|
||||
|
||||
struct DynamicArenaAllocator
|
||||
{
|
||||
inline Allocator allocator;
|
||||
Allocator* backing_allocator;
|
||||
DynamicArenaPage* page;
|
||||
DynamicArenaPage* unused_page;
|
||||
usz page_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require page_size >= 128
|
||||
* @require this != null
|
||||
**/
|
||||
fn void DynamicArenaAllocator.init(DynamicArenaAllocator* this, usz page_size, Allocator* using = mem::heap())
|
||||
{
|
||||
this.function = &dynamic_arena_allocator_function;
|
||||
this.page = null;
|
||||
this.unused_page = null;
|
||||
this.page_size = page_size;
|
||||
this.backing_allocator = using;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require this != null
|
||||
**/
|
||||
fn void DynamicArenaAllocator.free(DynamicArenaAllocator* this)
|
||||
{
|
||||
DynamicArenaPage* page = this.page;
|
||||
while (page)
|
||||
{
|
||||
DynamicArenaPage* next_page = page.prev_arena;
|
||||
free(page, .using = this.backing_allocator);
|
||||
page = next_page;
|
||||
}
|
||||
page = this.unused_page;
|
||||
while (page)
|
||||
{
|
||||
DynamicArenaPage* next_page = page.prev_arena;
|
||||
free(page, .using = this.backing_allocator);
|
||||
page = next_page;
|
||||
}
|
||||
this.page = null;
|
||||
this.unused_page = null;
|
||||
}
|
||||
|
||||
struct DynamicArenaPage
|
||||
{
|
||||
void* memory;
|
||||
void* prev_arena;
|
||||
usz total;
|
||||
usz used;
|
||||
void* last_ptr;
|
||||
}
|
||||
|
||||
struct DynamicArenaChunk @local
|
||||
{
|
||||
usz size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require ptr && this
|
||||
* @require this.page `tried to free pointer on invalid allocator`
|
||||
*/
|
||||
fn void DynamicArenaAllocator.free_ptr(DynamicArenaAllocator* this, void* ptr) @private
|
||||
{
|
||||
DynamicArenaPage* current_page = this.page;
|
||||
if (ptr == current_page.last_ptr)
|
||||
{
|
||||
current_page.used = (usz)((ptr - DEFAULT_SIZE_PREFIX) - current_page.memory);
|
||||
}
|
||||
current_page.last_ptr = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require old_pointer && size > 0
|
||||
* @require this.page `tried to realloc pointer on invalid allocator`
|
||||
*/
|
||||
fn void*! DynamicArenaAllocator._realloc(DynamicArenaAllocator* this, void* old_pointer, usz size, usz alignment, usz offset) @local
|
||||
{
|
||||
DynamicArenaPage* current_page = this.page;
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
usz* old_size_ptr = old_pointer - DEFAULT_SIZE_PREFIX;
|
||||
usz old_size = *old_size_ptr;
|
||||
// We have the old pointer and it's correctly aligned.
|
||||
if (old_size >= size && mem::ptr_is_aligned(old_pointer, alignment))
|
||||
{
|
||||
*old_size_ptr = size;
|
||||
if (current_page.last_ptr == old_pointer)
|
||||
{
|
||||
current_page.used = (usz)((old_pointer - DEFAULT_SIZE_PREFIX) - current_page.memory);
|
||||
}
|
||||
return old_pointer;
|
||||
}
|
||||
if REUSE: (current_page.last_ptr == old_pointer && mem::ptr_is_aligned(old_pointer, alignment))
|
||||
{
|
||||
assert(size > old_size);
|
||||
usz add_size = size - old_size;
|
||||
if (add_size + current_page.used > current_page.total) break REUSE;
|
||||
*old_size_ptr = size;
|
||||
current_page.used += add_size;
|
||||
return old_pointer;
|
||||
}
|
||||
void* new_mem = this._alloc(size, alignment, offset)!;
|
||||
mem::copy(new_mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return new_mem;
|
||||
}
|
||||
|
||||
fn void DynamicArenaAllocator.reset(DynamicArenaAllocator* this) @private
|
||||
{
|
||||
DynamicArenaPage* page = this.page;
|
||||
DynamicArenaPage** unused_page_ptr = &this.unused_page;
|
||||
while (page)
|
||||
{
|
||||
DynamicArenaPage* next_page = page.prev_arena;
|
||||
page.used = 0;
|
||||
DynamicArenaPage* prev_unused = *unused_page_ptr;
|
||||
*unused_page_ptr = page;
|
||||
page.prev_arena = prev_unused;
|
||||
page = next_page;
|
||||
}
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require math::is_power_of_2(alignment)
|
||||
* @require size > 0
|
||||
*/
|
||||
fn void*! DynamicArenaAllocator._alloc_new(DynamicArenaAllocator* this, usz size, usz alignment, usz offset) @local
|
||||
{
|
||||
// First, make sure that we can align it, extending the page size if needed.
|
||||
usz page_size = max(this.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + offset, alignment) - offset);
|
||||
|
||||
// Grab the page without alignment (we do it ourselves)
|
||||
void* mem = this.backing_allocator.alloc(page_size)!;
|
||||
DynamicArenaPage*! page = malloc(DynamicArenaPage, .using = this.backing_allocator);
|
||||
if (catch err = page)
|
||||
{
|
||||
free(mem, .using = this.backing_allocator);
|
||||
return err?;
|
||||
}
|
||||
page.memory = mem;
|
||||
void* mem_start = mem::aligned_pointer(mem + offset + DynamicArenaChunk.sizeof, alignment) - offset;
|
||||
assert(mem_start + size < mem + page_size);
|
||||
DynamicArenaChunk* chunk = (DynamicArenaChunk*)mem_start - 1;
|
||||
chunk.size = size;
|
||||
page.prev_arena = this.page;
|
||||
page.total = page_size;
|
||||
page.used = mem_start + size - page.memory;
|
||||
this.page = page;
|
||||
page.last_ptr = mem_start;
|
||||
return mem_start;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
* @require size > 0
|
||||
* @require this
|
||||
*/
|
||||
fn void*! DynamicArenaAllocator._alloc(DynamicArenaAllocator* this, usz size, usz alignment, usz offset) @local
|
||||
{
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
DynamicArenaPage* page = this.page;
|
||||
if (!page && this.unused_page)
|
||||
{
|
||||
this.page = page = this.unused_page;
|
||||
this.unused_page = page.prev_arena;
|
||||
page.prev_arena = null;
|
||||
}
|
||||
if (!page) return this._alloc_new(size, alignment, offset);
|
||||
void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof + offset, alignment) - offset;
|
||||
usz new_used = start - page.memory + size;
|
||||
if ALLOCATE_NEW: (new_used > page.total)
|
||||
{
|
||||
if ((page = this.unused_page))
|
||||
{
|
||||
start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof + offset, alignment) - offset;
|
||||
new_used = start + size - page.memory;
|
||||
if (page.total >= new_used)
|
||||
{
|
||||
this.unused_page = page.prev_arena;
|
||||
page.prev_arena = this.page;
|
||||
this.page = page;
|
||||
break ALLOCATE_NEW;
|
||||
}
|
||||
}
|
||||
return this._alloc_new(size, alignment, offset);
|
||||
}
|
||||
|
||||
page.used = new_used;
|
||||
assert(start + size == page.memory + page.used);
|
||||
void* mem = start;
|
||||
DynamicArenaChunk* chunk = (DynamicArenaChunk*)mem - 1;
|
||||
chunk.size = size;
|
||||
return mem;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
* @require data `unexpectedly missing the allocator`
|
||||
*/
|
||||
fn void*! dynamic_arena_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
|
||||
{
|
||||
DynamicArenaAllocator* allocator = (DynamicArenaAllocator*)data;
|
||||
switch (kind)
|
||||
{
|
||||
case CALLOC:
|
||||
case ALIGNED_CALLOC:
|
||||
assert(!old_pointer, "Unexpected no old pointer for calloc.");
|
||||
if (!size) return null;
|
||||
void* mem = allocator._alloc(size, alignment, offset)!;
|
||||
mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return mem;
|
||||
case ALLOC:
|
||||
case ALIGNED_ALLOC:
|
||||
assert(!old_pointer, "Unexpected no old pointer for alloc.");
|
||||
if (!size) return null;
|
||||
return allocator._alloc(size, alignment, offset);
|
||||
case REALLOC:
|
||||
case ALIGNED_REALLOC:
|
||||
if (!size)
|
||||
{
|
||||
if (!old_pointer) return null;
|
||||
allocator.free_ptr(old_pointer);
|
||||
return null;
|
||||
}
|
||||
if (!old_pointer) return allocator._alloc(size, alignment, offset);
|
||||
void* mem = allocator._realloc(old_pointer, size, alignment, offset)!;
|
||||
return mem;
|
||||
case ALIGNED_FREE:
|
||||
case FREE:
|
||||
if (!old_pointer) return null;
|
||||
allocator.free_ptr(old_pointer);
|
||||
return null;
|
||||
case MARK:
|
||||
unreachable("Tried to mark a dynamic arena");
|
||||
case RESET:
|
||||
allocator.reset();
|
||||
return null;
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
223
lib/std/core/allocators/heap_allocator.c3
Normal file
223
lib/std/core/allocators/heap_allocator.c3
Normal file
@@ -0,0 +1,223 @@
|
||||
// Copyright (c) 2021 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::mem::allocator;
|
||||
|
||||
def MemoryAllocFn = fn char[]!(usz);
|
||||
|
||||
struct SimpleHeapAllocator
|
||||
{
|
||||
inline Allocator allocator;
|
||||
MemoryAllocFn alloc_fn;
|
||||
Header* free_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require this "Unexpectedly missing the allocator"
|
||||
* @require allocator "An underlying memory provider must be given"
|
||||
* @require !this.free_list "The allocator may not be already initialized"
|
||||
**/
|
||||
fn void SimpleHeapAllocator.init(SimpleHeapAllocator* this, MemoryAllocFn allocator)
|
||||
{
|
||||
this.alloc_fn = allocator;
|
||||
this.allocator = { &simple_heap_allocator_function };
|
||||
this.free_list = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
* @require this `unexpectedly missing the allocator`
|
||||
*/
|
||||
fn void*! simple_heap_allocator_function(Allocator* this, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
|
||||
{
|
||||
SimpleHeapAllocator* heap = (SimpleHeapAllocator*)this;
|
||||
switch (kind)
|
||||
{
|
||||
case ALIGNED_ALLOC:
|
||||
return @aligned_alloc(heap._alloc, size, alignment, offset);
|
||||
case ALLOC:
|
||||
return heap._alloc(size);
|
||||
case ALIGNED_CALLOC:
|
||||
return @aligned_calloc(heap._calloc, size, alignment, offset);
|
||||
case CALLOC:
|
||||
return heap._calloc(size);
|
||||
case ALIGNED_REALLOC:
|
||||
if (!size) nextcase ALIGNED_FREE;
|
||||
if (!old_pointer) nextcase ALIGNED_CALLOC;
|
||||
return @aligned_realloc(heap._calloc, heap._free, old_pointer, size, alignment, offset);
|
||||
case REALLOC:
|
||||
if (!size) nextcase FREE;
|
||||
if (!old_pointer) nextcase CALLOC;
|
||||
return heap._realloc(old_pointer, size);
|
||||
case RESET:
|
||||
return AllocationFailure.UNSUPPORTED_OPERATION?;
|
||||
case ALIGNED_FREE:
|
||||
@aligned_free(heap._free, old_pointer)!;
|
||||
return null;
|
||||
case FREE:
|
||||
heap._free(old_pointer);
|
||||
return null;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require this && old_pointer && bytes > 0
|
||||
**/
|
||||
fn void*! SimpleHeapAllocator._realloc(SimpleHeapAllocator* this, void* old_pointer, usz bytes)
|
||||
{
|
||||
// Find the block header.
|
||||
Header* block = (Header*)old_pointer - 1;
|
||||
if (block.size >= bytes) return old_pointer;
|
||||
void* new = this._alloc(bytes)!;
|
||||
usz max_to_copy = math::min(block.size, bytes);
|
||||
mem::copy(new, old_pointer, max_to_copy);
|
||||
this._free(old_pointer);
|
||||
return new;
|
||||
}
|
||||
|
||||
fn void*! SimpleHeapAllocator._calloc(SimpleHeapAllocator* this, usz bytes) @local
|
||||
{
|
||||
void* data = this._alloc(bytes)!;
|
||||
mem::clear(data, bytes, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return data;
|
||||
}
|
||||
|
||||
fn void*! SimpleHeapAllocator._alloc(SimpleHeapAllocator* this, usz bytes) @local
|
||||
{
|
||||
usz aligned_bytes = mem::aligned_offset(bytes, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
if (!this.free_list)
|
||||
{
|
||||
this.add_block(aligned_bytes)!;
|
||||
}
|
||||
|
||||
Header* current = this.free_list;
|
||||
Header* previous = current;
|
||||
while (current)
|
||||
{
|
||||
switch
|
||||
{
|
||||
case current.size >= aligned_bytes && current.size <= aligned_bytes + Header.sizeof + 64:
|
||||
if (current == previous)
|
||||
{
|
||||
this.free_list = current.next;
|
||||
}
|
||||
else
|
||||
{
|
||||
previous.next = current.next;
|
||||
}
|
||||
current.next = null;
|
||||
return current + 1;
|
||||
case current.size > aligned_bytes:
|
||||
Header* unallocated = (Header*)((char*)current + aligned_bytes + Header.sizeof);
|
||||
unallocated.size = current.size - aligned_bytes;
|
||||
unallocated.next = current.next;
|
||||
if (current == this.free_list)
|
||||
{
|
||||
this.free_list = unallocated;
|
||||
}
|
||||
else
|
||||
{
|
||||
previous.next = unallocated;
|
||||
}
|
||||
current.size = aligned_bytes;
|
||||
current.next = null;
|
||||
return current + 1;
|
||||
default:
|
||||
previous = current;
|
||||
current = current.next;
|
||||
}
|
||||
}
|
||||
this.add_block(aligned_bytes)!;
|
||||
return this.alloc(aligned_bytes);
|
||||
}
|
||||
|
||||
fn void! SimpleHeapAllocator.add_block(SimpleHeapAllocator* this, usz aligned_bytes) @local
|
||||
{
|
||||
assert(mem::aligned_offset(aligned_bytes, mem::DEFAULT_MEM_ALIGNMENT) == aligned_bytes);
|
||||
char[] result = this.alloc_fn(aligned_bytes + Header.sizeof)!;
|
||||
Header* new_block = (Header*)result.ptr;
|
||||
new_block.size = result.len - Header.sizeof;
|
||||
new_block.next = null;
|
||||
this._free(new_block + 1);
|
||||
}
|
||||
|
||||
|
||||
fn void SimpleHeapAllocator._free(SimpleHeapAllocator* this, void* ptr) @local
|
||||
{
|
||||
// Empty ptr -> do nothing.
|
||||
if (!ptr) return;
|
||||
|
||||
// Find the block header.
|
||||
Header* block = (Header*)ptr - 1;
|
||||
|
||||
// No free list? Then just return this.
|
||||
if (!this.free_list)
|
||||
{
|
||||
this.free_list = block;
|
||||
return;
|
||||
}
|
||||
|
||||
// Find where in the list it should be inserted.
|
||||
Header* current = this.free_list;
|
||||
Header* prev = current;
|
||||
while (current)
|
||||
{
|
||||
if (block < current)
|
||||
{
|
||||
// Between prev and current
|
||||
if (block > prev) break;
|
||||
// Before current
|
||||
if (current == prev) break;
|
||||
}
|
||||
prev = current;
|
||||
current = prev.next;
|
||||
}
|
||||
if (current)
|
||||
{
|
||||
// Insert after the current block.
|
||||
// Are the blocks adjacent?
|
||||
if (current == (Header*)((char*)(block + 1) + block.size))
|
||||
{
|
||||
// Merge
|
||||
block.size += current.size + Header.sizeof;
|
||||
block.next = current.next;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Chain to current
|
||||
block.next = current;
|
||||
}
|
||||
}
|
||||
if (prev == current)
|
||||
{
|
||||
// Swap new start of free list
|
||||
this.free_list = block;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Prev adjacent?
|
||||
if (block == (Header*)((char*)(prev + 1) + prev.size))
|
||||
{
|
||||
prev.size += block.size + Header.sizeof;
|
||||
prev.next = block.next;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Link prev to block
|
||||
prev.next = block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
union Header @private
|
||||
{
|
||||
struct
|
||||
{
|
||||
Header* next;
|
||||
usz size;
|
||||
}
|
||||
usz align;
|
||||
}
|
||||
136
lib/std/core/allocators/mem_allocator_fn.c3
Normal file
136
lib/std/core/allocators/mem_allocator_fn.c3
Normal file
@@ -0,0 +1,136 @@
|
||||
// Copyright (c) 2021 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::mem::allocator;
|
||||
import libc;
|
||||
|
||||
const Allocator _NULL_ALLOCATOR @private = { &null_allocator_fn };
|
||||
const Allocator _SYSTEM_ALLOCATOR @private = { &libc_allocator_fn };
|
||||
|
||||
fn void*! null_allocator_fn(Allocator* this, usz bytes, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case ALLOC:
|
||||
case CALLOC:
|
||||
case REALLOC:
|
||||
case ALIGNED_ALLOC:
|
||||
case ALIGNED_REALLOC:
|
||||
case ALIGNED_CALLOC:
|
||||
return AllocationFailure.OUT_OF_MEMORY?;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
struct AlignedBlock
|
||||
{
|
||||
usz len;
|
||||
void* start;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require bytes > 0
|
||||
* @require alignment > 0
|
||||
**/
|
||||
macro void*! @aligned_alloc(#alloc_fn, usz bytes, usz alignment, usz offset)
|
||||
{
|
||||
usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
|
||||
$if $checks(#alloc_fn(bytes)!):
|
||||
void* data = #alloc_fn(header + bytes)!;
|
||||
$else
|
||||
void* data = #alloc_fn(header + bytes);
|
||||
$endif
|
||||
void* mem = mem::aligned_pointer(data + header + offset, alignment) - offset;
|
||||
assert(mem > data);
|
||||
AlignedBlock* desc = (AlignedBlock*)mem - 1;
|
||||
*desc = { bytes, data };
|
||||
return mem;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require bytes > 0
|
||||
* @require alignment > 0
|
||||
**/
|
||||
macro void*! @aligned_calloc(#calloc_fn, usz bytes, usz alignment, usz offset)
|
||||
{
|
||||
usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
|
||||
$if $checks(#calloc_fn(bytes)!):
|
||||
void* data = #calloc_fn(header + bytes)!;
|
||||
$else
|
||||
void* data = #calloc_fn(header + bytes);
|
||||
$endif
|
||||
void* mem = mem::aligned_pointer(data + header + offset, alignment) - offset;
|
||||
AlignedBlock* desc = (AlignedBlock*)mem - 1;
|
||||
assert(mem > data);
|
||||
*desc = { bytes, data };
|
||||
return mem;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require bytes > 0
|
||||
* @require alignment > 0
|
||||
**/
|
||||
macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes, usz alignment, usz offset)
|
||||
{
|
||||
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
|
||||
void* data_start = desc.start;
|
||||
void* new_data = @aligned_calloc(#calloc_fn, bytes, alignment, offset)!;
|
||||
mem::copy(new_data, old_pointer, desc.len > bytes ? desc.len : bytes, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
$if $checks(#free_fn(data_start)!):
|
||||
#free_fn(data_start)!;
|
||||
$else
|
||||
#free_fn(data_start);
|
||||
$endif
|
||||
return new_data;
|
||||
}
|
||||
|
||||
macro void! @aligned_free(#free_fn, void* old_pointer)
|
||||
{
|
||||
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
|
||||
$if $checks(#free_fn(desc.start)!):
|
||||
#free_fn(desc.start)!;
|
||||
$else
|
||||
#free_fn(desc.start);
|
||||
$endif
|
||||
}
|
||||
|
||||
fn void*! libc_allocator_fn(Allocator* unused, usz bytes, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @inline
|
||||
{
|
||||
if (!alignment) alignment = mem::DEFAULT_MEM_ALIGNMENT;
|
||||
assert(math::is_power_of_2(alignment), "Alignment was not a power of 2");
|
||||
|
||||
void* data;
|
||||
switch (kind)
|
||||
{
|
||||
case ALIGNED_ALLOC:
|
||||
data = @aligned_alloc(libc::malloc, bytes, alignment, offset)!!;
|
||||
case ALLOC:
|
||||
data = libc::malloc(bytes);
|
||||
case ALIGNED_CALLOC:
|
||||
data = @aligned_calloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment, offset)!!;
|
||||
case CALLOC:
|
||||
data = libc::calloc(bytes, 1);
|
||||
case ALIGNED_REALLOC:
|
||||
if (!bytes) nextcase ALIGNED_FREE;
|
||||
if (!old_pointer) nextcase ALIGNED_CALLOC;
|
||||
data = @aligned_realloc(fn void*(usz bytes) => libc::calloc(bytes, 1), libc::free, old_pointer, bytes, alignment, offset)!!;
|
||||
case REALLOC:
|
||||
if (!bytes) nextcase FREE;
|
||||
if (!old_pointer) nextcase CALLOC;
|
||||
data = libc::realloc(old_pointer, bytes);
|
||||
case RESET:
|
||||
return AllocationFailure.UNSUPPORTED_OPERATION?;
|
||||
case ALIGNED_FREE:
|
||||
@aligned_free(libc::free, old_pointer)!!;
|
||||
return null;
|
||||
case FREE:
|
||||
libc::free(old_pointer);
|
||||
return null;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
if (!data) return AllocationFailure.OUT_OF_MEMORY?;
|
||||
return data;
|
||||
}
|
||||
235
lib/std/core/allocators/on_stack_allocator.c3
Normal file
235
lib/std/core/allocators/on_stack_allocator.c3
Normal file
@@ -0,0 +1,235 @@
|
||||
module std::core::mem::allocator;
|
||||
|
||||
struct OnStackAllocator
|
||||
{
|
||||
inline Allocator allocator;
|
||||
Allocator* backing_allocator;
|
||||
char[] data;
|
||||
usz used;
|
||||
OnStackAllocatorExtraChunk* chunk;
|
||||
}
|
||||
|
||||
macro void @stack_mem(usz $size; @body(Allocator* mem)) @builtin
|
||||
{
|
||||
char[$size] buffer;
|
||||
OnStackAllocator allocator;
|
||||
allocator.init(&buffer, mem::heap());
|
||||
defer allocator.free();
|
||||
@body(&allocator);
|
||||
}
|
||||
|
||||
macro void @stack_pool(usz $size; @body) @builtin
|
||||
{
|
||||
char[$size] buffer;
|
||||
OnStackAllocator allocator;
|
||||
allocator.init(&buffer, mem::heap());
|
||||
defer allocator.free();
|
||||
mem::@scoped(&allocator)
|
||||
{
|
||||
@body();
|
||||
};
|
||||
}
|
||||
|
||||
struct OnStackAllocatorExtraChunk @local
|
||||
{
|
||||
bool is_aligned;
|
||||
OnStackAllocatorExtraChunk* prev;
|
||||
void* data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a memory arena for use using the provided bytes.
|
||||
*
|
||||
* @require this != null
|
||||
**/
|
||||
fn void OnStackAllocator.init(OnStackAllocator* this, char[] data, Allocator* using = mem::heap())
|
||||
{
|
||||
this.function = &on_stack_allocator_function;
|
||||
this.data = data;
|
||||
this.backing_allocator = using;
|
||||
this.used = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require this != null
|
||||
**/
|
||||
fn void OnStackAllocator.free(OnStackAllocator* this)
|
||||
{
|
||||
OnStackAllocatorExtraChunk* chunk = this.chunk;
|
||||
while (chunk)
|
||||
{
|
||||
if (chunk.is_aligned)
|
||||
{
|
||||
this.backing_allocator.free_aligned(chunk.data)!!;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.backing_allocator.free(chunk.data)!!;
|
||||
}
|
||||
void* old = chunk;
|
||||
chunk = chunk.prev;
|
||||
this.backing_allocator.free(old)!!;
|
||||
}
|
||||
this.chunk = null;
|
||||
this.used = 0;
|
||||
}
|
||||
|
||||
struct OnStackAllocatorHeader
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
* @require data `unexpectedly missing the allocator`
|
||||
*/
|
||||
fn void*! on_stack_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
|
||||
{
|
||||
OnStackAllocator* allocator = (OnStackAllocator*)data;
|
||||
bool clear = false;
|
||||
switch (kind)
|
||||
{
|
||||
case CALLOC:
|
||||
case ALIGNED_CALLOC:
|
||||
clear = true;
|
||||
nextcase;
|
||||
case ALLOC:
|
||||
case ALIGNED_ALLOC:
|
||||
assert(!old_pointer, "Unexpected old pointer for alloc.");
|
||||
if (!size) return null;
|
||||
return on_stack_allocator_alloc(allocator, size, alignment, offset, clear, kind == AllocationKind.ALIGNED_ALLOC || kind == AllocationKind.ALIGNED_CALLOC);
|
||||
case ALIGNED_REALLOC:
|
||||
case REALLOC:
|
||||
if (!size) nextcase FREE;
|
||||
if (!old_pointer) nextcase ALLOC;
|
||||
return on_stack_allocator_realloc(allocator, old_pointer, size, alignment, offset, kind == AllocationKind.ALIGNED_REALLOC);
|
||||
case ALIGNED_FREE:
|
||||
case FREE:
|
||||
if (!old_pointer) return null;
|
||||
if (allocation_in_stack_mem(allocator, old_pointer)) return null;
|
||||
on_stack_allocator_remove_chunk(allocator, old_pointer);
|
||||
if (kind == AllocationKind.ALIGNED_FREE)
|
||||
{
|
||||
allocator.backing_allocator.free_aligned(old_pointer)!;
|
||||
}
|
||||
else
|
||||
{
|
||||
allocator.backing_allocator.free(old_pointer)!;
|
||||
}
|
||||
return null;
|
||||
case MARK:
|
||||
case RESET:
|
||||
return AllocationFailure.UNSUPPORTED_OPERATION?;
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
|
||||
fn bool allocation_in_stack_mem(OnStackAllocator* a, void* ptr) @local
|
||||
{
|
||||
return ptr >= a.data.ptr && ptr <= &a.data[^1];
|
||||
}
|
||||
|
||||
fn void on_stack_allocator_remove_chunk(OnStackAllocator* a, void* ptr) @local
|
||||
{
|
||||
OnStackAllocatorExtraChunk* chunk = a.chunk;
|
||||
OnStackAllocatorExtraChunk** addr = &a.chunk;
|
||||
while (chunk)
|
||||
{
|
||||
if (chunk.data == ptr)
|
||||
{
|
||||
*addr = chunk.prev;
|
||||
a.backing_allocator.free(chunk)!!;
|
||||
return;
|
||||
}
|
||||
addr = &chunk.prev;
|
||||
chunk = *addr;
|
||||
}
|
||||
unreachable("Missing chunk");
|
||||
}
|
||||
|
||||
fn OnStackAllocatorExtraChunk* on_stack_allocator_find_chunk(OnStackAllocator* a, void* ptr) @local
|
||||
{
|
||||
OnStackAllocatorExtraChunk* chunk = a.chunk;
|
||||
while (chunk)
|
||||
{
|
||||
if (chunk.data == ptr) return chunk;
|
||||
chunk = chunk.prev;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require size > 0
|
||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
|
||||
* @require offset <= size && offset >= 0
|
||||
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
|
||||
* @require a != null
|
||||
**/
|
||||
fn void*! on_stack_allocator_realloc(OnStackAllocator* a, void* old_pointer, usz size, usz alignment, usz offset, bool aligned) @local @inline
|
||||
{
|
||||
if (!allocation_in_stack_mem(a, old_pointer))
|
||||
{
|
||||
OnStackAllocatorExtraChunk* chunk = on_stack_allocator_find_chunk(a, old_pointer);
|
||||
assert(chunk, "Tried to realloc pointer not belonging to the allocator");
|
||||
if (aligned)
|
||||
{
|
||||
return chunk.data = a.backing_allocator.realloc_aligned(old_pointer, size, alignment, offset)!;
|
||||
}
|
||||
return chunk.data = a.backing_allocator.realloc(old_pointer, size)!;
|
||||
}
|
||||
|
||||
OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof;
|
||||
usz old_size = header.size;
|
||||
void* mem = on_stack_allocator_alloc(a, size, alignment, offset, true, aligned)!;
|
||||
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return mem;
|
||||
}
|
||||
|
||||
import std::io;
|
||||
/**
|
||||
* @require size > 0
|
||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
|
||||
* @require offset <= size && offset >= 0
|
||||
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
|
||||
* @require a != null
|
||||
**/
|
||||
fn void*! on_stack_allocator_alloc(OnStackAllocator* a, usz size, usz alignment, usz offset, bool clear, bool aligned) @local @inline
|
||||
{
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
usz total_len = a.data.len;
|
||||
void* start_mem = a.data.ptr;
|
||||
void* unaligned_pointer_to_offset = start_mem + a.used + OnStackAllocatorHeader.sizeof + offset;
|
||||
void* aligned_pointer_to_offset = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
|
||||
usz end = (usz)(aligned_pointer_to_offset - a.data.ptr) + size - offset;
|
||||
|
||||
Allocator* backing_allocator = a.backing_allocator;
|
||||
|
||||
if (end > total_len)
|
||||
{
|
||||
OnStackAllocatorExtraChunk* chunk = backing_allocator.alloc(OnStackAllocatorExtraChunk.sizeof)!;
|
||||
defer catch backing_allocator.free(chunk)!!;
|
||||
defer try a.chunk = chunk;
|
||||
*chunk = { .prev = a.chunk, .is_aligned = aligned };
|
||||
void* data @noinit;
|
||||
switch
|
||||
{
|
||||
case !aligned && !clear:
|
||||
data = backing_allocator.alloc(size)!;
|
||||
case aligned && !clear:
|
||||
data = backing_allocator.alloc_aligned(size, alignment, offset)!;
|
||||
case !aligned && clear:
|
||||
data = backing_allocator.calloc(size)!;
|
||||
case aligned && clear:
|
||||
data = backing_allocator.calloc_aligned(size, alignment, offset)!;
|
||||
}
|
||||
return chunk.data = data;
|
||||
}
|
||||
a.used = end;
|
||||
void *mem = aligned_pointer_to_offset - offset;
|
||||
OnStackAllocatorHeader* header = mem - OnStackAllocatorHeader.sizeof;
|
||||
header.size = size;
|
||||
return mem;
|
||||
}
|
||||
254
lib/std/core/allocators/temp_allocator.c3
Normal file
254
lib/std/core/allocators/temp_allocator.c3
Normal file
@@ -0,0 +1,254 @@
|
||||
module std::core::mem::allocator;
|
||||
import std::io;
|
||||
|
||||
struct TempAllocatorChunk @local
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
struct TempAllocator
|
||||
{
|
||||
inline Allocator allocator;
|
||||
Allocator* backing_allocator;
|
||||
TempAllocatorPage* last_page;
|
||||
usz used;
|
||||
usz capacity;
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
|
||||
const usz PAGE_IS_ALIGNED @private = (usz)isz.max + 1u;
|
||||
|
||||
|
||||
struct TempAllocatorPage
|
||||
{
|
||||
TempAllocatorPage* prev_page;
|
||||
void* start;
|
||||
usz mark;
|
||||
usz size;
|
||||
usz ident;
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
macro usz TempAllocatorPage.pagesize(TempAllocatorPage* page) => page.size & ~PAGE_IS_ALIGNED;
|
||||
macro bool TempAllocatorPage.is_aligned(TempAllocatorPage* page) => page.size & PAGE_IS_ALIGNED == PAGE_IS_ALIGNED;
|
||||
|
||||
/**
|
||||
* @require size >= 16
|
||||
**/
|
||||
fn TempAllocator*! new_temp(usz size, Allocator* using)
|
||||
{
|
||||
TempAllocator* allocator = malloc_checked(TempAllocator, .using = using, .end_padding = size)!;
|
||||
allocator.last_page = null;
|
||||
allocator.function = &temp_allocator_function;
|
||||
allocator.backing_allocator = using;
|
||||
allocator.used = 0;
|
||||
allocator.capacity = size;
|
||||
return allocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
* @require data `unexpectedly missing the allocator`
|
||||
*/
|
||||
fn void*! temp_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
|
||||
{
|
||||
TempAllocator* arena = (TempAllocator*)data;
|
||||
switch (kind)
|
||||
{
|
||||
case CALLOC:
|
||||
case ALIGNED_CALLOC:
|
||||
assert(!old_pointer, "Unexpected old pointer for alloc.");
|
||||
if (!size) return null;
|
||||
return arena._alloc(size, alignment_for_allocation(alignment), offset, true);
|
||||
case ALLOC:
|
||||
case ALIGNED_ALLOC:
|
||||
assert(!old_pointer, "Unexpected old pointer for alloc.");
|
||||
if (!size) return null;
|
||||
return arena._alloc(size, alignment_for_allocation(alignment), offset, false);
|
||||
case ALIGNED_REALLOC:
|
||||
case REALLOC:
|
||||
if (!size) nextcase FREE;
|
||||
if (!old_pointer) nextcase ALLOC;
|
||||
return arena._realloc(old_pointer, size, alignment_for_allocation(alignment), offset);
|
||||
case FREE:
|
||||
case ALIGNED_FREE:
|
||||
if (!old_pointer) return null;
|
||||
arena._free(old_pointer)!;
|
||||
return null;
|
||||
case MARK:
|
||||
return (void*)(uptr)arena.used;
|
||||
case RESET:
|
||||
arena._reset(size)!;
|
||||
return null;
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
|
||||
fn void! TempAllocator._free(TempAllocator* this, void* old_pointer) @local
|
||||
{
|
||||
// TODO fix free
|
||||
assert((uptr)old_pointer >= (uptr)&this.data, "Pointer originates from a different allocator.");
|
||||
usz old_size = *(usz*)(old_pointer - DEFAULT_SIZE_PREFIX);
|
||||
if (old_pointer + old_size == &this.data[this.used])
|
||||
{
|
||||
this.used -= old_size;
|
||||
}
|
||||
}
|
||||
fn void! TempAllocator._reset(TempAllocator* this, usz mark) @local
|
||||
{
|
||||
TempAllocatorPage *last_page = this.last_page;
|
||||
while (last_page && last_page.mark > mark)
|
||||
{
|
||||
TempAllocatorPage *to_free = last_page;
|
||||
last_page = last_page.prev_page;
|
||||
this._free_page(to_free)!;
|
||||
}
|
||||
this.last_page = last_page;
|
||||
this.used = mark;
|
||||
}
|
||||
|
||||
fn void! TempAllocator._free_page(TempAllocator* this, TempAllocatorPage* page) @inline @local
|
||||
{
|
||||
void* mem = page.start;
|
||||
if (page.is_aligned()) return this.backing_allocator.free_aligned(mem);
|
||||
return this.backing_allocator.free(mem);
|
||||
}
|
||||
|
||||
fn void*! TempAllocator._realloc_page(TempAllocator* this, TempAllocatorPage* page, usz size, usz alignment, usz offset) @inline @local
|
||||
{
|
||||
// Then the actual start pointer:
|
||||
void* real_pointer = page.start;
|
||||
|
||||
// Walk backwards to find the pointer to this page.
|
||||
TempAllocatorPage **pointer_to_prev = &this.last_page;
|
||||
// Remove the page from the list
|
||||
while (*pointer_to_prev != page)
|
||||
{
|
||||
pointer_to_prev = &((*pointer_to_prev).prev_page);
|
||||
}
|
||||
*pointer_to_prev = page.prev_page;
|
||||
usz page_size = page.pagesize();
|
||||
// Clear on size > original size.
|
||||
void* data = this._alloc(size, alignment, offset, false)!;
|
||||
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
if (page.is_aligned())
|
||||
{
|
||||
this.backing_allocator.free_aligned(real_pointer)!;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.backing_allocator.free(real_pointer)!;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
fn void*! TempAllocator._realloc(TempAllocator* this, void* pointer, usz size, usz alignment, usz offset) @inline @local
|
||||
{
|
||||
TempAllocatorChunk *chunk = pointer - TempAllocatorChunk.sizeof;
|
||||
if (chunk.size == (usz)-1)
|
||||
{
|
||||
assert(this.last_page, "Realloc of non temp pointer");
|
||||
// First grab the page
|
||||
TempAllocatorPage *page = pointer - TempAllocatorPage.sizeof;
|
||||
return this._realloc_page(page, size, alignment, offset);
|
||||
}
|
||||
|
||||
// TODO optimize last allocation
|
||||
TempAllocatorChunk* data = this._alloc(size, alignment, offset, size > chunk.size)!;
|
||||
mem::copy(data, pointer, chunk.size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require math::is_power_of_2(alignment)
|
||||
* @require size > 0
|
||||
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
* @require this != null
|
||||
**/
|
||||
fn void*! TempAllocator._alloc(TempAllocator* this, usz size, usz alignment, usz offset, bool clear) @local
|
||||
{
|
||||
void* start_mem = &this.data;
|
||||
void* starting_ptr = start_mem + this.used;
|
||||
void* aligned_header_start = mem::aligned_pointer(starting_ptr, TempAllocatorChunk.alignof);
|
||||
void* mem = aligned_header_start + TempAllocatorChunk.sizeof;
|
||||
if (alignment > TempAllocatorChunk.alignof)
|
||||
{
|
||||
mem = mem::aligned_pointer(mem + offset, alignment) - offset;
|
||||
}
|
||||
usz new_usage = (usz)(mem - start_mem) + size;
|
||||
|
||||
// Arena alignment, simple!
|
||||
if (new_usage <= this.capacity)
|
||||
{
|
||||
TempAllocatorChunk* chunk_start = mem - TempAllocatorChunk.sizeof;
|
||||
chunk_start.size = size;
|
||||
this.used = new_usage;
|
||||
if (clear) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return mem;
|
||||
}
|
||||
|
||||
// Fallback to backing allocator
|
||||
TempAllocatorPage* page;
|
||||
|
||||
// We have something we need to align.
|
||||
if (alignment > mem::DEFAULT_MEM_ALIGNMENT || offset)
|
||||
{
|
||||
// This is actually simpler, since it will create the offset for us.
|
||||
usz total_alloc_size = TempAllocatorPage.sizeof + size;
|
||||
if (clear)
|
||||
{
|
||||
page = this.backing_allocator.calloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)!;
|
||||
}
|
||||
else
|
||||
{
|
||||
page = this.backing_allocator.alloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)!;
|
||||
}
|
||||
page.start = page;
|
||||
page.size = size | PAGE_IS_ALIGNED;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Here we might need to pad
|
||||
usz padded_header_size = mem::aligned_offset(TempAllocatorPage.sizeof, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
usz total_alloc_size = padded_header_size + size;
|
||||
void* alloc = (clear ? this.backing_allocator.calloc(total_alloc_size) : this.backing_allocator.alloc(total_alloc_size))!;
|
||||
|
||||
// Find the page.
|
||||
page = alloc + padded_header_size - TempAllocatorPage.sizeof;
|
||||
assert(mem::ptr_is_aligned(page, TempAllocator.alignof));
|
||||
assert(mem::ptr_is_aligned(&page.data[0], mem::DEFAULT_MEM_ALIGNMENT));
|
||||
page.start = alloc;
|
||||
page.size = size;
|
||||
}
|
||||
|
||||
// Mark it as a page
|
||||
page.ident = ~(usz)0;
|
||||
// Store when it was created
|
||||
page.mark = ++this.used;
|
||||
// Hook up the page.
|
||||
page.prev_page = this.last_page;
|
||||
this.last_page = page;
|
||||
return &page.data[0];
|
||||
}
|
||||
|
||||
fn void TempAllocator.print_pages(TempAllocator* this, File f)
|
||||
{
|
||||
TempAllocatorPage *last_page = this.last_page;
|
||||
if (!last_page)
|
||||
{
|
||||
f.printf("No pages.\n");
|
||||
return;
|
||||
}
|
||||
f.printf("---Pages----\n");
|
||||
uint index = 0;
|
||||
while (last_page)
|
||||
{
|
||||
bool is_not_aligned = !(last_page.size & (1u64 << 63));
|
||||
f.printf("%d. Alloc: %d %d at %p%s\n", ++index,
|
||||
last_page.size & ~(1u64 << 63), last_page.mark, &last_page.data[0], is_not_aligned ? "" : " [aligned]");
|
||||
last_page = last_page.prev_page;
|
||||
}
|
||||
}
|
||||
105
lib/std/core/allocators/tracking_allocator.c3
Normal file
105
lib/std/core/allocators/tracking_allocator.c3
Normal file
@@ -0,0 +1,105 @@
|
||||
// Copyright (c) 2021 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::mem::allocator;
|
||||
import std::collections::map;
|
||||
|
||||
def PtrMap = HashMap<uptr, usz>;
|
||||
|
||||
// A simple tracking allocator.
|
||||
// It tracks allocations using a hash map but
|
||||
// is not compatible with allocators that uses mark()
|
||||
struct TrackingAllocator
|
||||
{
|
||||
inline Allocator allocator;
|
||||
Allocator* inner_allocator;
|
||||
PtrMap map;
|
||||
usz mem_total;
|
||||
usz allocs_total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a memory arena for use using the provided bytes.
|
||||
*
|
||||
* @require this != null
|
||||
**/
|
||||
fn void TrackingAllocator.init(TrackingAllocator* this, Allocator* using)
|
||||
{
|
||||
*this = { .inner_allocator = using, .allocator.function = &tracking_allocator_fn };
|
||||
this.map.init(.using = using);
|
||||
}
|
||||
|
||||
fn void TrackingAllocator.free(TrackingAllocator* this)
|
||||
{
|
||||
this.map.free();
|
||||
*this = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [inout] data
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! tracking_allocator_fn(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
|
||||
{
|
||||
TrackingAllocator* this = (TrackingAllocator*)data;
|
||||
void* result = this.inner_allocator.function(this.inner_allocator, size, alignment, offset, old_pointer, kind)!;
|
||||
switch (kind)
|
||||
{
|
||||
case CALLOC:
|
||||
case ALIGNED_CALLOC:
|
||||
case ALLOC:
|
||||
case ALIGNED_ALLOC:
|
||||
this.map.set((uptr)result, size);
|
||||
this.mem_total += size;
|
||||
this.allocs_total++;
|
||||
return result;
|
||||
case REALLOC:
|
||||
case ALIGNED_REALLOC:
|
||||
this.map.remove((uptr)old_pointer);
|
||||
this.map.set((uptr)result, size);
|
||||
this.mem_total += size;
|
||||
if (size > 0) this.allocs_total++;
|
||||
return result;
|
||||
case ALIGNED_FREE:
|
||||
case FREE:
|
||||
if (!old_pointer) return null;
|
||||
this.map.remove((uptr)old_pointer);
|
||||
return null;
|
||||
case MARK:
|
||||
// Unsupported
|
||||
return null;
|
||||
case RESET:
|
||||
this.map.clear();
|
||||
return null;
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
|
||||
fn usz TrackingAllocator.allocated(TrackingAllocator* this)
|
||||
{
|
||||
usz allocated = 0;
|
||||
@pool()
|
||||
{
|
||||
foreach (usz allocation : this.map.value_tlist())
|
||||
{
|
||||
allocated += allocation;
|
||||
}
|
||||
};
|
||||
return allocated;
|
||||
}
|
||||
|
||||
fn usz TrackingAllocator.total_allocated(TrackingAllocator* this)
|
||||
{
|
||||
return this.mem_total;
|
||||
}
|
||||
|
||||
fn usz TrackingAllocator.total_allocation_count(TrackingAllocator* this)
|
||||
{
|
||||
return this.allocs_total;
|
||||
}
|
||||
|
||||
fn usz TrackingAllocator.allocation_count(TrackingAllocator* this)
|
||||
{
|
||||
return this.map.count;
|
||||
}
|
||||
70
lib/std/core/array.c3
Normal file
70
lib/std/core/array.c3
Normal file
@@ -0,0 +1,70 @@
|
||||
module std::core::array;
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
if (*e == element) return i;
|
||||
}
|
||||
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)
|
||||
{
|
||||
if (*e == element) return i;
|
||||
}
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate two arrays or subarrays, returning a subarray containing the concatenation of them.
|
||||
*
|
||||
* @param [in] arr1
|
||||
* @param [in] arr2
|
||||
* @param [&inout] using "The allocator to use, default is the heap allocator"
|
||||
* @require @typekind(arr1) == SUBARRAY || @typekind(arr1) == ARRAY
|
||||
* @require @typekind(arr2) == SUBARRAY || @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* using = mem::heap())
|
||||
{
|
||||
var $Type = $typeof(arr1[0]);
|
||||
$Type[] result = malloc($Type, arr1.len + arr2.len, .using = using);
|
||||
if (arr1.len > 0)
|
||||
{
|
||||
mem::copy(result.ptr, &arr1[0], arr1.len * $Type.sizeof, $Type.alignof, $Type.alignof);
|
||||
}
|
||||
if (arr2.len > 0)
|
||||
{
|
||||
mem::copy(&result[arr1.len], &arr2[0], arr2.len * $Type.sizeof, $Type.alignof, $Type.alignof);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate two arrays or subarrays, returning a subarray containing the concatenation of them,
|
||||
* allocated using the temp allocator.
|
||||
*
|
||||
* @param [in] arr1
|
||||
* @param [in] arr2
|
||||
* @require @typekind(arr1) == SUBARRAY || @typekind(arr1) == ARRAY
|
||||
* @require @typekind(arr2) == SUBARRAY || @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, mem::temp());
|
||||
89
lib/std/core/bitorder.c3
Normal file
89
lib/std/core/bitorder.c3
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) 2023 Christoffer Lerno and contributors. 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::bitorder;
|
||||
|
||||
// This module contains types of different endianness.
|
||||
// *BE types represent big-endian types
|
||||
// *LE types represent little-endian types.
|
||||
|
||||
bitstruct ShortBE : short @bigendian
|
||||
{
|
||||
short val : 0..15;
|
||||
}
|
||||
|
||||
bitstruct UShortBE : ushort @bigendian
|
||||
{
|
||||
ushort val : 0..15;
|
||||
}
|
||||
|
||||
bitstruct IntBE : int @bigendian
|
||||
{
|
||||
int val : 0..31;
|
||||
}
|
||||
|
||||
bitstruct UIntBE : int @bigendian
|
||||
{
|
||||
uint val : 0..31;
|
||||
}
|
||||
|
||||
bitstruct LongBE : long @bigendian
|
||||
{
|
||||
long val : 0..63;
|
||||
}
|
||||
|
||||
bitstruct ULongBE : ulong @bigendian
|
||||
{
|
||||
ulong val : 0..63;
|
||||
}
|
||||
|
||||
bitstruct Int128BE : int128 @bigendian
|
||||
{
|
||||
int128 val : 0..127;
|
||||
}
|
||||
|
||||
bitstruct UInt128BE : uint128 @bigendian
|
||||
{
|
||||
uint128 val : 0..127;
|
||||
}
|
||||
|
||||
bitstruct ShortLE : short @littleendian
|
||||
{
|
||||
short val : 0..15;
|
||||
}
|
||||
|
||||
bitstruct UShortLE : ushort @littleendian
|
||||
{
|
||||
ushort val : 0..15;
|
||||
}
|
||||
|
||||
bitstruct IntLE : int @littleendian
|
||||
{
|
||||
int val : 0..31;
|
||||
}
|
||||
|
||||
bitstruct UIntLE : int @littleendian
|
||||
{
|
||||
uint val : 0..31;
|
||||
}
|
||||
|
||||
bitstruct LongLE : long @littleendian
|
||||
{
|
||||
long val : 0..63;
|
||||
}
|
||||
|
||||
bitstruct ULongLE : ulong @littleendian
|
||||
{
|
||||
ulong val : 0..63;
|
||||
}
|
||||
|
||||
bitstruct Int128LE : int128 @littleendian
|
||||
{
|
||||
int128 val : 0..127;
|
||||
}
|
||||
|
||||
bitstruct UInt128LE : uint128 @littleendian
|
||||
{
|
||||
uint128 val : 0..127;
|
||||
}
|
||||
|
||||
290
lib/std/core/builtin.c3
Normal file
290
lib/std/core/builtin.c3
Normal file
@@ -0,0 +1,290 @@
|
||||
// Copyright (c) 2021-2022 Christoffer Lerno and contributors. 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::builtin;
|
||||
import libc;
|
||||
import std::hash;
|
||||
|
||||
/**
|
||||
* Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds.
|
||||
**/
|
||||
fault IteratorResult
|
||||
{
|
||||
NO_MORE_ELEMENT
|
||||
}
|
||||
|
||||
/**
|
||||
* Use `SearchResult` when trying to return a value from some collection but the element is missing.
|
||||
**/
|
||||
fault SearchResult
|
||||
{
|
||||
MISSING
|
||||
}
|
||||
|
||||
/**
|
||||
* Use `CastResult` when an attempt at conversion fails.
|
||||
**/
|
||||
fault CastResult
|
||||
{
|
||||
TYPE_MISMATCH
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
var temp = variable;
|
||||
defer variable = temp;
|
||||
@body();
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap two variables
|
||||
**/
|
||||
macro void @swap(&a, &b) @builtin
|
||||
{
|
||||
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
|
||||
**/
|
||||
macro anycast(any v, $Type) @builtin
|
||||
{
|
||||
if (v.type != $Type.typeid) return CastResult.TYPE_MISMATCH?;
|
||||
return ($Type*)v.ptr;
|
||||
}
|
||||
|
||||
struct CallstackElement
|
||||
{
|
||||
CallstackElement* prev;
|
||||
String function;
|
||||
String file;
|
||||
uint line;
|
||||
}
|
||||
|
||||
fn void default_panic(String message, String file, String function, uint line)
|
||||
{
|
||||
CallstackElement* stack = $$stacktrace();
|
||||
$if $defined(io::stderr) && $defined(File.printf):
|
||||
|
||||
if (stack) stack = stack.prev;
|
||||
if (stack)
|
||||
{
|
||||
(void)io::stderr().print("\nERROR: '");
|
||||
(void)io::stderr().print(message);
|
||||
(void)io::stderr().printn("'");
|
||||
}
|
||||
else
|
||||
{
|
||||
(void)io::stderr().print("\nERROR: '");
|
||||
(void)io::stderr().print(message);
|
||||
(void)io::stderr().printfn("', in function %s (%s:%d)", function, file, line);
|
||||
}
|
||||
while (stack)
|
||||
{
|
||||
(void)io::stderr().printfn(" in function %s (%s:%d)", stack.function, stack.file, stack.line);
|
||||
if (stack == stack.prev) break;
|
||||
stack = stack.prev;
|
||||
}
|
||||
|
||||
$endif
|
||||
$$trap();
|
||||
}
|
||||
|
||||
def PanicFn = fn void(String message, String file, String function, uint line);
|
||||
|
||||
PanicFn panic = &default_panic;
|
||||
|
||||
fn void panicf(String fmt, String file, String function, uint line, args...)
|
||||
{
|
||||
@stack_mem(512; Allocator* mem)
|
||||
{
|
||||
DString s;
|
||||
s.init(.using = mem);
|
||||
s.printf(fmt, ...args);
|
||||
panic(s.str(), 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"
|
||||
**/
|
||||
macro void unreachable(String string = "Unreachable statement reached.", ...) @builtin @noreturn
|
||||
{
|
||||
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat());
|
||||
$$unreachable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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(result, $Type)
|
||||
**/
|
||||
macro bitcast(expr, $Type) @builtin
|
||||
{
|
||||
usz $size = $sizeof(expr);
|
||||
$Type x @noinit;
|
||||
mem::copy(&x, &expr, $size, $Type.alignof, $alignof(expr));
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
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
|
||||
{
|
||||
$if $probability == 1.0:
|
||||
return $$expect(#value, true);
|
||||
$else
|
||||
return $$expect_with_probability(#value, true, $probability);
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
$if $probability == 1.0:
|
||||
return $$expect(#value, false);
|
||||
$else
|
||||
return $$expect_with_probability(#value, false, $probability);
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @require values::@is_int(#value) || values::@is_bool(#value)
|
||||
* @checked $typeof(#value) a = expected
|
||||
* @require $probability >= 0 && $probability <= 1.0
|
||||
**/
|
||||
macro @expect(#value, expected, $probability = 1.0) @builtin
|
||||
{
|
||||
$if $probability == 1.0:
|
||||
return $$expect(#value, ($typeof(#value))expected);
|
||||
$else
|
||||
return $$expect_with_probability(#value, expected, $probability);
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Locality for prefetch, levels 0 - 3, corresponding
|
||||
* to "extremely local" to "no locality"
|
||||
**/
|
||||
enum PrefetchLocality
|
||||
{
|
||||
NO_LOCALITY,
|
||||
FAR,
|
||||
NEAR,
|
||||
VERY_NEAR,
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.`
|
||||
**/
|
||||
macro prefetch(void* ptr, PrefetchLocality $locality = VERY_NEAR, bool $write = false) @builtin
|
||||
{
|
||||
$$prefetch(ptr, $write ? 1 : 0, $locality.ordinal);
|
||||
}
|
||||
|
||||
macro swizzle(v, ...) @builtin
|
||||
{
|
||||
return $$swizzle(v, $vasplat());
|
||||
}
|
||||
|
||||
macro swizzle2(v, v2, ...) @builtin
|
||||
{
|
||||
return $$swizzle2(v, v2, $vasplat());
|
||||
}
|
||||
|
||||
macro bool @castable(#expr, $To) @builtin
|
||||
{
|
||||
return $checks(($To)#expr);
|
||||
}
|
||||
|
||||
macro bool @convertible(#expr, $To) @builtin
|
||||
{
|
||||
return $checks($To x = #expr);
|
||||
}
|
||||
|
||||
macro anyfault @catchof(#expr) @builtin
|
||||
{
|
||||
if (catch f = #expr) return f;
|
||||
return anyfault {};
|
||||
}
|
||||
|
||||
macro bool @ok(#expr) @builtin
|
||||
{
|
||||
if (catch #expr) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
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 uint String.hash(String c) => (uint)fnv32a::encode(c);
|
||||
macro uint char[].hash(char[] c) => (uint)fnv32a::encode(c);
|
||||
114
lib/std/core/builtin_comparison.c3
Normal file
114
lib/std/core/builtin_comparison.c3
Normal file
@@ -0,0 +1,114 @@
|
||||
// Copyright (c) 2021-2022 Christoffer Lerno and contributors. 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::builtin;
|
||||
|
||||
/**
|
||||
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
|
||||
**/
|
||||
macro less(a, b) @builtin
|
||||
{
|
||||
$switch
|
||||
$case $defined(a.less):
|
||||
return a.less(b);
|
||||
$case $defined(a.compare_to):
|
||||
return a.compare_to(b) < 0;
|
||||
$default:
|
||||
return a < b;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
/**
|
||||
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
|
||||
**/
|
||||
macro less_eq(a, b) @builtin
|
||||
{
|
||||
$switch
|
||||
$case $defined(a.less):
|
||||
return !b.less(a);
|
||||
$case $defined(a.compare_to):
|
||||
return a.compare_to(b) <= 0;
|
||||
$default:
|
||||
return a <= b;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
/**
|
||||
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
|
||||
**/
|
||||
macro greater(a, b) @builtin
|
||||
{
|
||||
$switch
|
||||
$case $defined(a.less):
|
||||
return b.less(a);
|
||||
$case $defined(a.compare_to):
|
||||
return a.compare_to(b) > 0;
|
||||
$default:
|
||||
return a > b;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
/**
|
||||
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
|
||||
**/
|
||||
macro greater_eq(a, b) @builtin
|
||||
{
|
||||
$switch
|
||||
$case $defined(a.less):
|
||||
return !a.less(b);
|
||||
$case $defined(a.compare_to):
|
||||
return a.compare_to(b) >= 0;
|
||||
$default:
|
||||
return a >= b;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
/**
|
||||
* @require types::is_equatable_value(a) && types::is_equatable_value(b) `values must be equatable`
|
||||
**/
|
||||
macro bool equals(a, b) @builtin
|
||||
{
|
||||
$switch
|
||||
$case $defined(a.equals):
|
||||
return a.equals(b);
|
||||
$case $defined(a.compare_to):
|
||||
return a.compare_to(b) == 0;
|
||||
$case $defined(a.less):
|
||||
return !a.less(b) && !b.less(a);
|
||||
$default:
|
||||
return a == b;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
macro min(x, ...) @builtin
|
||||
{
|
||||
$if $vacount == 1:
|
||||
return less(x, $vaarg(0)) ? x : $vaarg(0);
|
||||
$else
|
||||
var result = x;
|
||||
$for (var $i = 0; $i < $vacount; $i++)
|
||||
if (less($vaarg($i), result))
|
||||
{
|
||||
result = $vaarg($i);
|
||||
}
|
||||
$endfor
|
||||
return result;
|
||||
$endif
|
||||
}
|
||||
|
||||
macro max(x, ...) @builtin
|
||||
{
|
||||
$if $vacount == 1:
|
||||
return greater(x, $vaarg(0)) ? x : $vaarg(0);
|
||||
$else
|
||||
var result = x;
|
||||
$for (var $i = 0; $i < $vacount; $i++)
|
||||
if (greater($vaarg($i), result))
|
||||
{
|
||||
result = $vaarg($i);
|
||||
}
|
||||
$endfor
|
||||
return result;
|
||||
$endif
|
||||
}
|
||||
|
||||
87
lib/std/core/cinterop.c3
Normal file
87
lib/std/core/cinterop.c3
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) 2021 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::cinterop;
|
||||
|
||||
const C_INT_SIZE = $$C_INT_SIZE;
|
||||
const C_LONG_SIZE = $$C_LONG_SIZE;
|
||||
const C_SHORT_SIZE = $$C_SHORT_SIZE;
|
||||
const C_LONG_LONG_SIZE = $$C_LONG_LONG_SIZE;
|
||||
|
||||
$assert C_SHORT_SIZE < 32;
|
||||
$assert C_INT_SIZE < 128;
|
||||
$assert C_LONG_SIZE < 128;
|
||||
$assert C_LONG_LONG_SIZE <= 128;
|
||||
$assert C_SHORT_SIZE <= C_INT_SIZE;
|
||||
$assert C_INT_SIZE <= C_LONG_SIZE;
|
||||
$assert C_LONG_SIZE <= C_LONG_LONG_SIZE;
|
||||
|
||||
$switch ($$C_INT_SIZE)
|
||||
$case 64:
|
||||
def CInt = long;
|
||||
def CUInt = ulong;
|
||||
$case 32:
|
||||
def CInt = int;
|
||||
def CUInt = uint;
|
||||
$case 16:
|
||||
def CInt = short;
|
||||
def CUInt = ushort;
|
||||
$default:
|
||||
$error "Invalid C int size";
|
||||
$endswitch
|
||||
|
||||
$switch ($$C_LONG_SIZE)
|
||||
$case 64:
|
||||
def CLong = long;
|
||||
def CULong = ulong;
|
||||
$case 32:
|
||||
def CLong = int;
|
||||
def CULong = uint;
|
||||
$case 16:
|
||||
def CLong = short;
|
||||
def CULong = ushort;
|
||||
$default:
|
||||
$error "Invalid C long size";
|
||||
$endswitch
|
||||
|
||||
$switch ($$C_SHORT_SIZE)
|
||||
$case 32:
|
||||
def CShort = int;
|
||||
def CUShort = uint;
|
||||
$case 16:
|
||||
def CShort = short;
|
||||
def CUShort = ushort;
|
||||
$case 8:
|
||||
def CShort = ichar;
|
||||
def CUShort = char;
|
||||
$default:
|
||||
$error "Invalid C short size";
|
||||
$endswitch
|
||||
|
||||
$switch ($$C_LONG_LONG_SIZE)
|
||||
$case 128:
|
||||
def CLongLong = int128;
|
||||
def CULongLong = uint128;
|
||||
$case 64:
|
||||
def CLongLong = long;
|
||||
def CULongLong = ulong;
|
||||
$case 32:
|
||||
def CLongLong = int;
|
||||
def CULongLong = uint;
|
||||
$case 16:
|
||||
def CLongLong = short;
|
||||
def CULongLong = ushort;
|
||||
$default:
|
||||
$error "Invalid C long long size";
|
||||
$endswitch
|
||||
|
||||
|
||||
|
||||
def CSChar = ichar;
|
||||
def CUChar = char;
|
||||
|
||||
$if $$C_CHAR_IS_SIGNED:
|
||||
def CChar = ichar;
|
||||
$else
|
||||
def CChar = char;
|
||||
$endif
|
||||
415
lib/std/core/conv.c3
Normal file
415
lib/std/core/conv.c3
Normal file
@@ -0,0 +1,415 @@
|
||||
module std::core::string::conv;
|
||||
|
||||
const uint UTF16_SURROGATE_OFFSET @private = 0x10000;
|
||||
const uint UTF16_SURROGATE_GENERIC_MASK @private = 0xF800;
|
||||
const uint UTF16_SURROGATE_GENERIC_VALUE @private = 0xD800;
|
||||
const uint UTF16_SURROGATE_MASK @private = 0xFC00;
|
||||
const uint UTF16_SURROGATE_CODEPOINT_MASK @private = 0x03FF;
|
||||
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 available `the size available`
|
||||
**/
|
||||
fn usz! char32_to_utf8(Char32 c, char* output, usz available)
|
||||
{
|
||||
if (!available) return UnicodeResult.CONVERSION_FAILED?;
|
||||
switch (true)
|
||||
{
|
||||
case c <= 0x7f:
|
||||
output[0] = (char)c;
|
||||
return 1;
|
||||
case c <= 0x7ff:
|
||||
if (available < 2) return UnicodeResult.CONVERSION_FAILED?;
|
||||
output[0] = (char)(0xC0 | c >> 6);
|
||||
output[1] = (char)(0x80 | (c & 0x3F));
|
||||
return 2;
|
||||
case c <= 0xffff:
|
||||
if (available < 3) return UnicodeResult.CONVERSION_FAILED?;
|
||||
output[0] = (char)(0xE0 | c >> 12);
|
||||
output[1] = (char)(0x80 | (c >> 6 & 0x3F));
|
||||
output[2] = (char)(0x80 | (c & 0x3F));
|
||||
return 3;
|
||||
case c <= 0x10ffff:
|
||||
if (available < 4) return UnicodeResult.CONVERSION_FAILED?;
|
||||
output[0] = (char)(0xF0 | c >> 18);
|
||||
output[1] = (char)(0x80 | (c >> 12 & 0x3F));
|
||||
output[2] = (char)(0x80 | (c >> 6 & 0x3F));
|
||||
output[3] = (char)(0x80 | (c & 0x3F));
|
||||
return 4;
|
||||
default:
|
||||
// 0x10FFFF and above is not defined.
|
||||
return UnicodeResult.CONVERSION_FAILED?;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
(*output)++[0] = (Char16)c;
|
||||
return;
|
||||
}
|
||||
c -= UTF16_SURROGATE_OFFSET;
|
||||
Char16 low = (Char16)(UTF16_SURROGATE_LOW_VALUE | (c & UTF16_SURROGATE_CODEPOINT_MASK));
|
||||
c >>= UTF16_SURROGATE_BITS;
|
||||
Char16 high = (Char16)(UTF16_SURROGATE_HIGH_VALUE | (c & UTF16_SURROGATE_CODEPOINT_MASK));
|
||||
(*output)++[0] = (Char16)high;
|
||||
(*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.`
|
||||
**/
|
||||
fn void! char16_to_utf8_unsafe(Char16 *ptr, usz *available, char** output)
|
||||
{
|
||||
Char16 high = *ptr;
|
||||
if (high & UTF16_SURROGATE_GENERIC_MASK != UTF16_SURROGATE_GENERIC_VALUE)
|
||||
{
|
||||
char32_to_utf8_unsafe(high, output);
|
||||
*available = 1;
|
||||
return;
|
||||
}
|
||||
// Low surrogate first is an error
|
||||
if (high & UTF16_SURROGATE_MASK != UTF16_SURROGATE_HIGH_VALUE) return UnicodeResult.INVALID_UTF16?;
|
||||
|
||||
// Unmatched high surrogate is an error
|
||||
if (*available == 1) return UnicodeResult.INVALID_UTF16?;
|
||||
|
||||
Char16 low = ptr[1];
|
||||
|
||||
// Unmatched high surrogate, invalid
|
||||
if (low & UTF16_SURROGATE_MASK != UTF16_SURROGATE_LOW_VALUE) return UnicodeResult.INVALID_UTF16?;
|
||||
|
||||
// The high bits of the codepoint are the value bits of the high surrogate
|
||||
// The low bits of the codepoint are the value bits of the low surrogate
|
||||
Char32 uc = (high & UTF16_SURROGATE_CODEPOINT_MASK) << UTF16_SURROGATE_BITS
|
||||
| (low & UTF16_SURROGATE_CODEPOINT_MASK) + UTF16_SURROGATE_OFFSET;
|
||||
char32_to_utf8_unsafe(uc, output);
|
||||
*available = 2;
|
||||
}
|
||||
/**
|
||||
* @param c `The utf32 codepoint to convert`
|
||||
* @param [inout] output `the resulting buffer`
|
||||
**/
|
||||
fn void char32_to_utf8_unsafe(Char32 c, char** output)
|
||||
{
|
||||
switch (true)
|
||||
{
|
||||
case c < 0x7f:
|
||||
(*output)++[0] = (char)c;
|
||||
case c < 0x7ff:
|
||||
(*output)++[0] = (char)(0xC0 | c >> 6);
|
||||
(*output)++[0] = (char)(0x80 | (c & 0x3F));
|
||||
case c < 0xffff:
|
||||
(*output)++[0] = (char)(0xE0 | c >> 12);
|
||||
(*output)++[0] = (char)(0x80 | (c >> 6 & 0x3F));
|
||||
(*output)++[0] = (char)(0x80 | (c & 0x3F));
|
||||
default:
|
||||
(*output)++[0] = (char)(0xF0 | c >> 18);
|
||||
(*output)++[0] = (char)(0x80 | (c >> 12 & 0x3F));
|
||||
(*output)++[0] = (char)(0x80 | (c >> 6 & 0x3F));
|
||||
(*output)++[0] = (char)(0x80 | (c & 0x3F));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
if (max_size < 1) return UnicodeResult.INVALID_UTF8?;
|
||||
char c = (ptr++)[0];
|
||||
|
||||
if ((c & 0x80) == 0)
|
||||
{
|
||||
*size = 1;
|
||||
return c;
|
||||
}
|
||||
if ((c & 0xE0) == 0xC0)
|
||||
{
|
||||
if (max_size < 2) return UnicodeResult.INVALID_UTF8?;
|
||||
*size = 2;
|
||||
Char32 uc = (c & 0x1F) << 6;
|
||||
c = *ptr;
|
||||
// Overlong sequence or invalid second.
|
||||
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
|
||||
return uc + c & 0x3F;
|
||||
}
|
||||
if ((c & 0xF0) == 0xE0)
|
||||
{
|
||||
if (max_size < 3) return UnicodeResult.INVALID_UTF8?;
|
||||
*size = 3;
|
||||
Char32 uc = (c & 0x0F) << 12;
|
||||
c = ptr++[0];
|
||||
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
|
||||
uc += (c & 0x3F) << 6;
|
||||
c = ptr++[0];
|
||||
// Overlong sequence or invalid last
|
||||
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
|
||||
return uc + c & 0x3F;
|
||||
}
|
||||
if (max_size < 4) return UnicodeResult.INVALID_UTF8?;
|
||||
if ((c & 0xF8) != 0xF0) return UnicodeResult.INVALID_UTF8?;
|
||||
*size = 4;
|
||||
Char32 uc = (c & 0x07) << 18;
|
||||
c = ptr++[0];
|
||||
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
|
||||
uc += (c & 0x3F) << 12;
|
||||
c = ptr++[0];
|
||||
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
|
||||
uc += (c & 0x3F) << 6;
|
||||
c = ptr++[0];
|
||||
// Overlong sequence or invalid last
|
||||
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
|
||||
return uc + c & 0x3F;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
foreach (char c : utf8)
|
||||
{
|
||||
if (c & 0xC0 != 0x80) len++;
|
||||
}
|
||||
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`
|
||||
**/
|
||||
fn usz utf8len_for_utf32(Char32[] utf32)
|
||||
{
|
||||
usz len = 0;
|
||||
foreach (Char32 uc : utf32)
|
||||
{
|
||||
switch (true)
|
||||
{
|
||||
case uc < 0x7f:
|
||||
len++;
|
||||
case uc < 0x7ff:
|
||||
len += 2;
|
||||
case uc < 0xffff:
|
||||
len += 3;
|
||||
default:
|
||||
len += 4;
|
||||
}
|
||||
}
|
||||
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`
|
||||
**/
|
||||
fn usz utf8len_for_utf16(Char16[] utf16)
|
||||
{
|
||||
usz len = 0;
|
||||
usz len16 = utf16.len;
|
||||
for (usz i = 0; i < len16; i++)
|
||||
{
|
||||
Char16 c = utf16[i];
|
||||
if (c & UTF16_SURROGATE_GENERIC_MASK != UTF16_SURROGATE_GENERIC_VALUE)
|
||||
{
|
||||
if (c < 0x7f)
|
||||
{
|
||||
len++;
|
||||
continue;
|
||||
}
|
||||
if (c < 0x7ff)
|
||||
{
|
||||
len += 2;
|
||||
continue;
|
||||
}
|
||||
len += 3;
|
||||
continue;
|
||||
}
|
||||
len += 4;
|
||||
}
|
||||
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`
|
||||
**/
|
||||
fn usz utf16len_for_utf8(String utf8)
|
||||
{
|
||||
usz len = utf8.len;
|
||||
usz len16 = 0;
|
||||
for (usz i = 0; i < len; i++)
|
||||
{
|
||||
len16++;
|
||||
char c = utf8[i];
|
||||
if (c & 0x80 == 0) continue;
|
||||
i++;
|
||||
if (c & 0xE0 == 0xC0) continue;
|
||||
i++;
|
||||
if (c & 0xF0 == 0xE0) continue;
|
||||
i++;
|
||||
len16++;
|
||||
}
|
||||
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.`
|
||||
**/
|
||||
fn usz utf16len_for_utf32(Char32[] utf32)
|
||||
{
|
||||
usz len = utf32.len;
|
||||
foreach (Char32 uc : utf32)
|
||||
{
|
||||
if (uc >= UTF16_SURROGATE_OFFSET) len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, String utf8_buffer)
|
||||
{
|
||||
usz len = utf8_buffer.len;
|
||||
char* ptr = utf8_buffer.ptr;
|
||||
foreach (Char32 uc : utf32)
|
||||
{
|
||||
usz used = char32_to_utf8(uc, ptr, len) @inline!;
|
||||
len -= used;
|
||||
ptr += used;
|
||||
}
|
||||
// Zero terminate if there is space.
|
||||
if (len > 0) ptr[0] = 0;
|
||||
return utf8_buffer.len - len;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
Char32* ptr = utf32_buffer.ptr;
|
||||
usz len32 = 0;
|
||||
usz buf_len = utf32_buffer.len;
|
||||
for (usz i = 0; i < len;)
|
||||
{
|
||||
if (len32 == buf_len) return UnicodeResult.CONVERSION_FAILED?;
|
||||
usz width = len - i;
|
||||
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline!;
|
||||
i += width;
|
||||
ptr[len32++] = uc;
|
||||
}
|
||||
// Zero terminate if possible
|
||||
if (len32 + 1 < buf_len) ptr[len32] = 0;
|
||||
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.`
|
||||
**/
|
||||
fn void! utf16to8_unsafe(Char16[] utf16, char* utf8_buffer)
|
||||
{
|
||||
usz len16 = utf16.len;
|
||||
for (usz i = 0; i < len16;)
|
||||
{
|
||||
usz available = len16 - i;
|
||||
char16_to_utf8_unsafe(&utf16[i], &available, &utf8_buffer) @inline!;
|
||||
i += available;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
for (usz i = 0; i < len;)
|
||||
{
|
||||
usz width = len - i;
|
||||
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline!;
|
||||
i += width;
|
||||
(utf32_buffer++)[0] = uc;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
for (usz i = 0; i < len;)
|
||||
{
|
||||
usz width = len - i;
|
||||
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline!;
|
||||
char32_to_utf16_unsafe(uc, &utf16_buffer) @inline;
|
||||
i += width;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
foreach (Char32 uc : utf32)
|
||||
{
|
||||
char32_to_utf8_unsafe(uc, &utf8_buffer) @inline;
|
||||
}
|
||||
}
|
||||
414
lib/std/core/dstring.c3
Normal file
414
lib/std/core/dstring.c3
Normal file
@@ -0,0 +1,414 @@
|
||||
module std::core::dstring;
|
||||
|
||||
def DString = distinct void*;
|
||||
|
||||
const usz MIN_CAPACITY @private = 16;
|
||||
|
||||
/**
|
||||
* @require !str.data() "String already initialized"
|
||||
**/
|
||||
fn void DString.init(DString *str, usz capacity = MIN_CAPACITY, Allocator* using = mem::heap())
|
||||
{
|
||||
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
|
||||
StringData* data = malloc(StringData, 1, .using = using, .end_padding = capacity);
|
||||
data.allocator = using;
|
||||
data.len = 0;
|
||||
data.capacity = capacity;
|
||||
*str = (DString)data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require !str.data() "String already initialized"
|
||||
**/
|
||||
fn void DString.tinit(DString *str, usz capacity = MIN_CAPACITY) => str.init(capacity, mem::temp()) @inline;
|
||||
|
||||
fn DString new_with_capacity(usz capacity, Allocator* using = mem::heap())
|
||||
{
|
||||
DString dstr;
|
||||
dstr.init(capacity, using);
|
||||
return dstr;
|
||||
}
|
||||
|
||||
fn DString tnew_with_capacity(usz capacity) => new_with_capacity(capacity, mem::temp()) @inline;
|
||||
|
||||
fn DString new(String c = "", Allocator* using = mem::heap())
|
||||
{
|
||||
usz len = c.len;
|
||||
StringData* data = (StringData*)new_with_capacity(len, using);
|
||||
if (len)
|
||||
{
|
||||
data.len = len;
|
||||
mem::copy(&data.chars, c.ptr, len);
|
||||
}
|
||||
return (DString)data;
|
||||
}
|
||||
|
||||
fn DString tnew(String s = "") => new(s, mem::temp()) @inline;
|
||||
|
||||
fn DString DString.new_concat(DString a, DString b, Allocator* using = mem::heap())
|
||||
{
|
||||
DString string;
|
||||
string.init(a.len() + b.len(), using);
|
||||
string.append(a);
|
||||
string.append(b);
|
||||
return string;
|
||||
}
|
||||
|
||||
fn DString DString.new_tconcat(DString a, DString b) => a.new_concat(b, mem::temp());
|
||||
|
||||
fn ZString DString.zstr(DString str)
|
||||
{
|
||||
StringData* data = str.data();
|
||||
if (!data) return "";
|
||||
if (data.capacity == data.len)
|
||||
{
|
||||
str.reserve(1);
|
||||
data.chars[data.len] = 0;
|
||||
}
|
||||
else if (data.chars[data.len] != 0)
|
||||
{
|
||||
data.chars[data.len] = 0;
|
||||
}
|
||||
return (ZString)&data.chars[0];
|
||||
}
|
||||
|
||||
fn usz DString.capacity(DString this)
|
||||
{
|
||||
if (!this) return 0;
|
||||
return this.data().capacity;
|
||||
}
|
||||
|
||||
fn usz DString.len(DString this)
|
||||
{
|
||||
if (!this) return 0;
|
||||
return this.data().len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require new_size <= this.len()
|
||||
*/
|
||||
fn void DString.chop(DString this, usz new_size)
|
||||
{
|
||||
if (!this) return;
|
||||
this.data().len = new_size;
|
||||
}
|
||||
|
||||
fn String DString.str(DString str)
|
||||
{
|
||||
StringData* data = (StringData*)str;
|
||||
if (!data) return "";
|
||||
return (String)data.chars[:data.len];
|
||||
}
|
||||
|
||||
fn void DString.append_utf32(DString* str, Char32[] chars)
|
||||
{
|
||||
str.reserve(chars.len);
|
||||
foreach (Char32 c : chars)
|
||||
{
|
||||
str.append_char32(c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index < str.len()
|
||||
**/
|
||||
fn void DString.set(DString str, usz index, char c)
|
||||
{
|
||||
str.data().chars[index] = c;
|
||||
}
|
||||
|
||||
fn void DString.append_repeat(DString* str, char c, usz times)
|
||||
{
|
||||
if (times == 0) return;
|
||||
str.reserve(times);
|
||||
StringData* data = str.data();
|
||||
for (usz i = 0; i < times; i++)
|
||||
{
|
||||
data.chars[data.len++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require c <= 0x10ffff
|
||||
*/
|
||||
fn void DString.append_char32(DString* str, Char32 c)
|
||||
{
|
||||
if (c < 0x7f)
|
||||
{
|
||||
str.reserve(1);
|
||||
StringData* data = str.data();
|
||||
data.chars[data.len++] = (char)c;
|
||||
return;
|
||||
}
|
||||
if (c < 0x7ff)
|
||||
{
|
||||
str.reserve(2);
|
||||
StringData* data = str.data();
|
||||
data.chars[data.len++] = (char)(0xC0 | c >> 6);
|
||||
data.chars[data.len++] = (char)(0x80 | (c & 0x3F));
|
||||
return;
|
||||
}
|
||||
if (c < 0xffff)
|
||||
{
|
||||
str.reserve(3);
|
||||
StringData* data = str.data();
|
||||
data.chars[data.len++] = (char)(0xE0 | c >> 12);
|
||||
data.chars[data.len++] = (char)(0x80 | (c >> 6 & 0x3F));
|
||||
data.chars[data.len++] = (char)(0x80 | (c & 0x3F));
|
||||
return;
|
||||
}
|
||||
str.reserve(4);
|
||||
StringData* data = str.data();
|
||||
data.chars[data.len++] = (char)(0xF0 | c >> 18);
|
||||
data.chars[data.len++] = (char)(0x80 | (c >> 12 & 0x3F));
|
||||
data.chars[data.len++] = (char)(0x80 | (c >> 6 & 0x3F));
|
||||
data.chars[data.len++] = (char)(0x80 | (c & 0x3F));
|
||||
}
|
||||
|
||||
fn DString DString.tcopy(DString* str) => str.copy(mem::temp());
|
||||
|
||||
fn DString DString.copy(DString* str, Allocator* using = null)
|
||||
{
|
||||
if (!str)
|
||||
{
|
||||
if (using) return new_with_capacity(0, using);
|
||||
return (DString)null;
|
||||
}
|
||||
if (!using) using = mem::heap();
|
||||
StringData* data = str.data();
|
||||
DString new_string = new_with_capacity(data.capacity, using);
|
||||
mem::copy((char*)new_string.data(), (char*)data, StringData.sizeof + data.len);
|
||||
return new_string;
|
||||
}
|
||||
|
||||
fn ZString DString.copy_zstr(DString* str, Allocator* using = mem::heap())
|
||||
{
|
||||
usz str_len = str.len();
|
||||
if (!str_len)
|
||||
{
|
||||
return (ZString)calloc(1, .using = using);
|
||||
}
|
||||
char* zstr = malloc(str_len + 1, .using = using);
|
||||
StringData* data = str.data();
|
||||
mem::copy(zstr, &data.chars, str_len);
|
||||
zstr[str_len] = 0;
|
||||
return (ZString)zstr;
|
||||
}
|
||||
|
||||
fn String DString.copy_str(DString* str, Allocator* using = mem::heap())
|
||||
{
|
||||
return (String)str.copy_zstr(using)[:str.len()];
|
||||
}
|
||||
|
||||
fn String DString.tcopy_str(DString* str) => str.copy_str(mem::temp()) @inline;
|
||||
|
||||
fn bool DString.equals(DString str, DString other_string)
|
||||
{
|
||||
StringData *str1 = str.data();
|
||||
StringData *str2 = other_string.data();
|
||||
if (str1 == str2) return true;
|
||||
if (!str1) return str2.len == 0;
|
||||
if (!str2) return str1.len == 0;
|
||||
usz str1_len = str1.len;
|
||||
if (str1_len != str2.len) return false;
|
||||
for (int i = 0; i < str1_len; i++)
|
||||
{
|
||||
if (str1.chars[i] != str2.chars[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
fn void DString.free(DString* str)
|
||||
{
|
||||
if (!*str) return;
|
||||
StringData* data = str.data();
|
||||
if (!data) return;
|
||||
free(data, .using = data.allocator);
|
||||
*str = (DString)null;
|
||||
}
|
||||
|
||||
fn bool DString.less(DString str, DString other_string)
|
||||
{
|
||||
StringData* str1 = str.data();
|
||||
StringData* str2 = other_string.data();
|
||||
if (str1 == str2) return false;
|
||||
if (!str1) return str2.len != 0;
|
||||
if (!str2) return str1.len == 0;
|
||||
usz str1_len = str1.len;
|
||||
usz str2_len = str2.len;
|
||||
if (str1_len != str2_len) return str1_len < str2_len;
|
||||
for (int i = 0; i < str1_len; i++)
|
||||
{
|
||||
if (str1.chars[i] >= str2.chars[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
fn void DString.append_chars(DString* this, String str)
|
||||
{
|
||||
usz other_len = str.len;
|
||||
if (!other_len) return;
|
||||
if (!*this)
|
||||
{
|
||||
*this = new(str);
|
||||
return;
|
||||
}
|
||||
this.reserve(other_len);
|
||||
StringData* data = (StringData*)*this;
|
||||
mem::copy(&data.chars[data.len], str.ptr, other_len);
|
||||
data.len += other_len;
|
||||
}
|
||||
|
||||
fn Char32[] DString.copy_utf32(DString* this, Allocator* using = mem::heap())
|
||||
{
|
||||
return this.str().to_utf32(using) @inline!!;
|
||||
}
|
||||
|
||||
fn void DString.append_string(DString* this, DString str)
|
||||
{
|
||||
StringData* other = (StringData*)str;
|
||||
if (!other) return;
|
||||
this.append(str.str());
|
||||
}
|
||||
|
||||
fn void DString.clear(DString* str)
|
||||
{
|
||||
str.data().len = 0;
|
||||
}
|
||||
|
||||
fn void DString.append_char(DString* str, char c)
|
||||
{
|
||||
if (!*str)
|
||||
{
|
||||
*str = new_with_capacity(MIN_CAPACITY);
|
||||
}
|
||||
str.reserve(1);
|
||||
StringData* data = (StringData*)*str;
|
||||
data.chars[data.len++] = c;
|
||||
}
|
||||
|
||||
|
||||
macro void DString.append(DString* str, value)
|
||||
{
|
||||
var $Type = $typeof(value);
|
||||
$switch ($Type)
|
||||
$case char:
|
||||
$case ichar:
|
||||
str.append_char(value);
|
||||
$case DString:
|
||||
str.append_string(value);
|
||||
$case String:
|
||||
str.append_chars(value);
|
||||
$case Char32:
|
||||
str.append_char32(value);
|
||||
$default:
|
||||
$switch
|
||||
$case @convertible(value, Char32):
|
||||
str.append_char32(value);
|
||||
$case @convertible(value, String):
|
||||
str.append_chars(value);
|
||||
$default:
|
||||
$error "Unsupported type for append – use printf instead.";
|
||||
$endswitch
|
||||
$endswitch
|
||||
}
|
||||
|
||||
|
||||
fn usz! DString.printf(DString* str, String format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_string_append_fn, str);
|
||||
return formatter.vprintf(format, args);
|
||||
}
|
||||
|
||||
fn usz! DString.printfn(DString* str, String format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_string_append_fn, str);
|
||||
usz len = formatter.vprintf(format, args)!;
|
||||
str.append('\n');
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
fn DString new_join(String[] s, String joiner, Allocator* using = mem::heap())
|
||||
{
|
||||
if (!s.len) return (DString)null;
|
||||
usz total_size = joiner.len * s.len;
|
||||
foreach (String* &str : s)
|
||||
{
|
||||
total_size += str.len;
|
||||
}
|
||||
DString res = new_with_capacity(total_size, using);
|
||||
res.append(s[0]);
|
||||
foreach (String* &str : s[1..])
|
||||
{
|
||||
res.append(joiner);
|
||||
res.append(*str);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
fn void! out_string_append_fn(char c, void* data) @private
|
||||
{
|
||||
DString* s = data;
|
||||
s.append_char(c);
|
||||
}
|
||||
|
||||
|
||||
fn StringData* DString.data(DString str) @inline @private
|
||||
{
|
||||
return (StringData*)str;
|
||||
}
|
||||
|
||||
fn void DString.reserve(DString* str, usz addition)
|
||||
{
|
||||
StringData* data = str.data();
|
||||
if (!data)
|
||||
{
|
||||
*str = dstring::new_with_capacity(addition);
|
||||
return;
|
||||
}
|
||||
usz len = data.len + addition;
|
||||
if (data.capacity >= len) return;
|
||||
usz new_capacity = data.capacity *= 2;
|
||||
if (new_capacity < MIN_CAPACITY) new_capacity = MIN_CAPACITY;
|
||||
*str = (DString)realloc(data, StringData.sizeof + new_capacity, .using = data.allocator);
|
||||
}
|
||||
|
||||
fn usz! DString.read_from_stream(DString* string, Stream* reader)
|
||||
{
|
||||
if (reader.supports_available())
|
||||
{
|
||||
usz total_read = 0;
|
||||
while (usz available = reader.available()!)
|
||||
{
|
||||
string.reserve(available);
|
||||
StringData* data = string.data();
|
||||
usz len = reader.read(data.chars[data.len..(data.capacity - 1)])!;
|
||||
total_read += len;
|
||||
data.len += len;
|
||||
}
|
||||
return total_read;
|
||||
}
|
||||
usz total_read = 0;
|
||||
while (true)
|
||||
{
|
||||
// Reserve at least 16 bytes
|
||||
string.reserve(16);
|
||||
StringData* data = string.data();
|
||||
// Read into the rest of the buffer
|
||||
usz read = reader.read(data.chars[data.len..(data.capacity - 1)])!;
|
||||
data.len += read;
|
||||
// Ok, we reached the end.
|
||||
if (read < 16) return total_read;
|
||||
// Otherwise go another round
|
||||
}
|
||||
}
|
||||
|
||||
struct StringData @private
|
||||
{
|
||||
Allocator* allocator;
|
||||
usz len;
|
||||
usz capacity;
|
||||
char[*] chars;
|
||||
}
|
||||
226
lib/std/core/env.c3
Normal file
226
lib/std/core/env.c3
Normal file
@@ -0,0 +1,226 @@
|
||||
// Copyright (c) 2021 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::env;
|
||||
import libc;
|
||||
enum CompilerOptLevel
|
||||
{
|
||||
O0,
|
||||
O1,
|
||||
O2,
|
||||
O3
|
||||
}
|
||||
|
||||
enum MemoryEnvironment
|
||||
{
|
||||
NORMAL,
|
||||
SMALL,
|
||||
TINY,
|
||||
NONE
|
||||
}
|
||||
|
||||
enum OsType
|
||||
{
|
||||
UNKNOWN,
|
||||
NONE,
|
||||
ANANAS,
|
||||
CLOUD_ABI,
|
||||
DRAGON_FLY,
|
||||
FREEBSD,
|
||||
FUCHSIA,
|
||||
IOS,
|
||||
KFREEBSD,
|
||||
LINUX,
|
||||
PS3,
|
||||
MACOS,
|
||||
NETBSD,
|
||||
OPENBSD,
|
||||
SOLARIS,
|
||||
WIN32,
|
||||
HAIKU,
|
||||
MINIX,
|
||||
RTEMS,
|
||||
NACL, // Native Client
|
||||
CNK, // BG/P Compute-Node Kernel
|
||||
AIX,
|
||||
CUDA,
|
||||
NVOPENCL,
|
||||
AMDHSA,
|
||||
PS4,
|
||||
ELFIAMCU,
|
||||
TVOS,
|
||||
WATCHOS,
|
||||
MESA3D,
|
||||
CONTIKI,
|
||||
AMDPAL,
|
||||
HERMITCORE,
|
||||
HURD,
|
||||
WASI,
|
||||
EMSCRIPTEN,
|
||||
}
|
||||
|
||||
enum ArchType
|
||||
{
|
||||
UNKNOWN,
|
||||
ARM, // ARM (little endian): arm, armv.*, xscale
|
||||
ARMB, // ARM (big endian): armeb
|
||||
AARCH64, // AArch64 (little endian): aarch64
|
||||
AARCH64_BE, // AArch64 (big endian): aarch64_be
|
||||
AARCH64_32, // AArch64 (little endian) ILP32: aarch64_32
|
||||
ARC, // ARC: Synopsys ARC
|
||||
AVR, // AVR: Atmel AVR microcontroller
|
||||
BPFEL, // eBPF or extended BPF or 64-bit BPF (little endian)
|
||||
BPFEB, // eBPF or extended BPF or 64-bit BPF (big endian)
|
||||
HEXAGON, // Hexagon: hexagon
|
||||
MIPS, // MIPS: mips, mipsallegrex, mipsr6
|
||||
MIPSEL, // MIPSEL: mipsel, mipsallegrexe, mipsr6el
|
||||
MIPS64, // MIPS64: mips64, mips64r6, mipsn32, mipsn32r6
|
||||
MIPS64EL, // MIPS64EL: mips64el, mips64r6el, mipsn32el, mipsn32r6el
|
||||
MSP430, // MSP430: msp430
|
||||
PPC, // PPC: powerpc
|
||||
PPC64, // PPC64: powerpc64, ppu
|
||||
PPC64LE, // PPC64LE: powerpc64le
|
||||
R600, // R600: AMD GPUs HD2XXX - HD6XXX
|
||||
AMDGCN, // AMDGCN: AMD GCN GPUs
|
||||
RISCV32, // RISC-V (32-bit): riscv32
|
||||
RISCV64, // RISC-V (64-bit): riscv64
|
||||
SPARC, // Sparc: sparc
|
||||
SPARCV9, // Sparcv9: Sparcv9
|
||||
SPARCEL, // Sparc: (endianness = little). NB: 'Sparcle' is a CPU variant
|
||||
SYSTEMZ, // SystemZ: s390x
|
||||
TCE, // TCE (http://tce.cs.tut.fi/): tce
|
||||
TCELE, // TCE little endian (http://tce.cs.tut.fi/): tcele
|
||||
THUMB, // Thumb (little endian): thumb, thumbv.*
|
||||
THUMBEB, // Thumb (big endian): thumbeb
|
||||
X86, // X86: i[3-9]86
|
||||
X86_64, // X86-64: amd64, x86_64
|
||||
XCORE, // XCore: xcore
|
||||
NVPTX, // NVPTX: 32-bit
|
||||
NVPTX64, // NVPTX: 64-bit
|
||||
LE32, // le32: generic little-endian 32-bit CPU (PNaCl)
|
||||
LE64, // le64: generic little-endian 64-bit CPU (PNaCl)
|
||||
AMDIL, // AMDIL
|
||||
AMDIL64, // AMDIL with 64-bit pointers
|
||||
HSAIL, // AMD HSAIL
|
||||
HSAIL64, // AMD HSAIL with 64-bit pointers
|
||||
SPIR, // SPIR: standard portable IR for OpenCL 32-bit version
|
||||
SPIR64, // SPIR: standard portable IR for OpenCL 64-bit version
|
||||
KALIMBA, // Kalimba: generic kalimba
|
||||
SHAVE, // SHAVE: Movidius vector VLIW processors
|
||||
LANAI, // Lanai: Lanai 32-bit
|
||||
WASM32, // WebAssembly with 32-bit pointers
|
||||
WASM64, // WebAssembly with 64-bit pointers
|
||||
RSCRIPT32, // 32-bit RenderScript
|
||||
RSCRIPT64, // 64-bit RenderScript
|
||||
}
|
||||
|
||||
const OsType OS_TYPE = (OsType)$$OS_TYPE;
|
||||
const ArchType ARCH_TYPE = (ArchType)$$ARCH_TYPE;
|
||||
const bool COMPILER_LIBC_AVAILABLE = $$COMPILER_LIBC_AVAILABLE;
|
||||
const CompilerOptLevel COMPILER_OPT_LEVEL = (CompilerOptLevel)$$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;
|
||||
const bool F128_SUPPORT = $$PLATFORM_F128_SUPPORTED;
|
||||
const bool COMPILER_SAFE_MODE = $$COMPILER_SAFE_MODE;
|
||||
const usz LLVM_VERSION = $$LLVM_VERSION;
|
||||
const bool BENCHMARKING = $$BENCHMARKING;
|
||||
const bool TESTING = $$TESTING;
|
||||
const MemoryEnvironment MEMORY_ENV = (MemoryEnvironment)$$MEMORY_ENVIRONMENT;
|
||||
|
||||
macro bool os_is_win32()
|
||||
{
|
||||
return OS_TYPE == WIN32;
|
||||
}
|
||||
|
||||
macro bool os_is_darwin()
|
||||
{
|
||||
$switch (OS_TYPE)
|
||||
$case IOS:
|
||||
$case MACOS:
|
||||
$case TVOS:
|
||||
$case WATCHOS:
|
||||
return true;
|
||||
$default:
|
||||
return false;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
macro bool os_is_posix()
|
||||
{
|
||||
$switch (OS_TYPE)
|
||||
$case IOS:
|
||||
$case MACOS:
|
||||
$case NETBSD:
|
||||
$case LINUX:
|
||||
$case KFREEBSD:
|
||||
$case FREEBSD:
|
||||
$case OPENBSD:
|
||||
$case SOLARIS:
|
||||
$case TVOS:
|
||||
$case WATCHOS:
|
||||
return true;
|
||||
$case WIN32:
|
||||
$case WASI:
|
||||
$case EMSCRIPTEN:
|
||||
return false;
|
||||
$default:
|
||||
$echo("Assuming non-Posix environment");
|
||||
return false;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param [&in] name
|
||||
* @require name.len > 0
|
||||
**/
|
||||
fn String! get_var(String name)
|
||||
{
|
||||
$if COMPILER_LIBC_AVAILABLE && !os_is_win32():
|
||||
@pool()
|
||||
{
|
||||
ZString val = libc::getenv(name.zstr_tcopy());
|
||||
return val ? val.as_str() : SearchResult.MISSING?;
|
||||
};
|
||||
$else
|
||||
return "";
|
||||
$endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param [&in] name
|
||||
* @param [&in] value
|
||||
* @require name.len > 0
|
||||
**/
|
||||
fn void set_var(String name, String value, bool overwrite = true)
|
||||
{
|
||||
$if COMPILER_LIBC_AVAILABLE && !os_is_win32():
|
||||
@pool()
|
||||
{
|
||||
if (libc::setenv(name.zstr_tcopy(), value.zstr_copy(), (int)overwrite))
|
||||
{
|
||||
unreachable();
|
||||
}
|
||||
};
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] name
|
||||
* @require name.len > 0
|
||||
**/
|
||||
fn void clear_var(String name)
|
||||
{
|
||||
$if COMPILER_LIBC_AVAILABLE && !os_is_win32():
|
||||
@pool()
|
||||
{
|
||||
if (libc::unsetenv(name.zstr_tcopy()))
|
||||
{
|
||||
unreachable();
|
||||
}
|
||||
};
|
||||
$endif
|
||||
}
|
||||
|
||||
431
lib/std/core/mem.c3
Normal file
431
lib/std/core/mem.c3
Normal file
@@ -0,0 +1,431 @@
|
||||
// Copyright (c) 2021-2023 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::core::mem;
|
||||
|
||||
const MAX_MEMORY_ALIGNMENT = 0x1000_0000;
|
||||
const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2;
|
||||
|
||||
|
||||
macro @volatile_load(&x) @builtin
|
||||
{
|
||||
return $$volatile_load(&x);
|
||||
}
|
||||
|
||||
macro @volatile_store(&x, y) @builtin
|
||||
{
|
||||
return $$volatile_store(&x, ($typeof(x))y);
|
||||
}
|
||||
|
||||
enum AtomicOrdering : int
|
||||
{
|
||||
NOT_ATOMIC, // Not atomic
|
||||
UNORDERED, // No lock
|
||||
MONOTONIC, // Consistent ordering
|
||||
ACQUIRE, // Barrier locking load/store
|
||||
RELEASE, // Barrier releasing load/store
|
||||
ACQUIRE_RELEASE, // Barrier fence to load/store
|
||||
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."
|
||||
**/
|
||||
macro @atomic_load(&x, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
|
||||
{
|
||||
return $$atomic_load(&x, $volatile, (int)$ordering);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
{
|
||||
$$atomic_store(&x, value, $volatile, (int)$ordering);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
**/
|
||||
fn usz aligned_offset(usz offset, usz alignment)
|
||||
{
|
||||
return alignment * ((offset + alignment - 1) / alignment);
|
||||
}
|
||||
|
||||
macro void* aligned_pointer(void* ptr, usz alignment)
|
||||
{
|
||||
return (void*)(uptr)aligned_offset((uptr)ptr, 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 clear(void* dst, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
|
||||
{
|
||||
$if $inlined:
|
||||
$$memset_inline(dst, (char)0, len, $is_volatile, $dst_align);
|
||||
$else
|
||||
$$memset(dst, (char)0, len, $is_volatile, $dst_align);
|
||||
$endif
|
||||
}
|
||||
|
||||
macro void copy(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false, bool $inlined = false)
|
||||
{
|
||||
$if $inlined:
|
||||
$$memcpy_inline(dst, src, len, $is_volatile, $dst_align, $src_align);
|
||||
$else
|
||||
$$memcpy(dst, src, len, $is_volatile, $dst_align, $src_align);
|
||||
$endif
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
macro void set(void* dst, char val, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
|
||||
{
|
||||
$if $inlined:
|
||||
$$memset_inline(dst, val, len, $is_volatile, $dst_align);
|
||||
$else
|
||||
$$memset(dst, val, len, $is_volatile, $dst_align);
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @require values::@inner_kind(a) == TypeKind.SUBARRAY || values::@inner_kind(a) == TypeKind.POINTER
|
||||
* @require values::@inner_kind(b) == TypeKind.SUBARRAY || values::@inner_kind(b) == TypeKind.POINTER
|
||||
* @require values::@inner_kind(a) != TypeKind.SUBARRAY || len == -1
|
||||
* @require values::@inner_kind(a) != TypeKind.POINTER || len > -1
|
||||
* @checked (a = b), (b = a)
|
||||
**/
|
||||
macro bool equals(a, b, isz len = -1, usz $align = 0)
|
||||
{
|
||||
$if !$align:
|
||||
$align = $typeof(a[0]).alignof;
|
||||
$endif
|
||||
void* x @noinit;
|
||||
void* y @noinit;
|
||||
$if values::@inner_kind(a) == TypeKind.SUBARRAY:
|
||||
len = a.len;
|
||||
if (len != b.len) return false;
|
||||
x = a.ptr;
|
||||
y = b.ptr;
|
||||
$else
|
||||
x = a;
|
||||
y = b;
|
||||
assert(len >= 0, "A zero or positive length must be given when comparing pointers.");
|
||||
$endif
|
||||
|
||||
if (!len) return true;
|
||||
var $Type;
|
||||
$switch ($align)
|
||||
$case 1:
|
||||
$Type = char;
|
||||
$case 2:
|
||||
$Type = ushort;
|
||||
$case 4:
|
||||
$Type = uint;
|
||||
$case 8:
|
||||
$default:
|
||||
$Type = ulong;
|
||||
$endswitch
|
||||
var $step = $Type.sizeof;
|
||||
usz end = len / $step;
|
||||
for (usz i = 0; i < end; i++)
|
||||
{
|
||||
if ((($Type*)x)[i] != (($Type*)y)[i]) return false;
|
||||
}
|
||||
usz last = len % $align;
|
||||
for (usz i = len - last; i < len; i++)
|
||||
{
|
||||
if (((char*)x)[i] != ((char*)y)[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
macro @clone(&value) @builtin
|
||||
{
|
||||
$typeof(value)* x = malloc($typeof(value));
|
||||
*x = value;
|
||||
return x;
|
||||
}
|
||||
|
||||
macro @tclone(&value) @builtin
|
||||
{
|
||||
$typeof(value)* x = talloc($typeof(value));
|
||||
*x = value;
|
||||
return x;
|
||||
}
|
||||
|
||||
macro type_alloc_must_be_aligned($Type)
|
||||
{
|
||||
return $Type.alignof > DEFAULT_MEM_ALIGNMENT;
|
||||
}
|
||||
/**
|
||||
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
|
||||
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
|
||||
**/
|
||||
macro malloc(..., Allocator* using = mem::heap(), usz end_padding = 0) @builtin
|
||||
{
|
||||
return malloc_checked($vasplat(), .using = using, .end_padding = end_padding)!!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
|
||||
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
|
||||
**/
|
||||
macro malloc_checked(..., Allocator* using = mem::heap(), usz end_padding = 0) @builtin
|
||||
{
|
||||
$if $checks($vatype(0).sizeof):
|
||||
var $Type = $vatype(0);
|
||||
$assert !type_alloc_must_be_aligned($vatype(0)) : "Type must be allocated with malloc_aligned";
|
||||
$if $vacount == 2:
|
||||
usz size = $vaarg(1);
|
||||
return (($Type*)using.alloc($Type.sizeof * size + end_padding))[:size];
|
||||
$else
|
||||
return ($Type*)using.alloc($Type.sizeof + end_padding);
|
||||
$endif
|
||||
$else
|
||||
return using.alloc($vaarg(0) + end_padding);
|
||||
$endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
|
||||
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
|
||||
* @require alignment && math::is_power_of_2(alignment)
|
||||
**/
|
||||
macro malloc_aligned(..., usz alignment = 0, usz end_padding = 0, Allocator* using = mem::heap()) @builtin
|
||||
{
|
||||
$if $checks($vatype(0).sizeof):
|
||||
var $Type = $vatype(0);
|
||||
$if $vacount == 2:
|
||||
usz size = $vaarg(1);
|
||||
return (($Type*)using.alloc_aligned($Type.sizeof * size + end_padding, alignment))[:size];
|
||||
$else
|
||||
return ($Type*)using.alloc_aligned($Type.sizeof + end_padding, alignment);
|
||||
$endif
|
||||
$else
|
||||
return using.alloc_aligned($vaarg(0) + end_padding, alignment);
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
|
||||
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
|
||||
**/
|
||||
macro calloc(..., Allocator* using = mem::heap(), usz end_padding = 0) @builtin
|
||||
{
|
||||
return calloc_checked($vasplat(), .using = using, .end_padding = end_padding)!!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
|
||||
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
|
||||
**/
|
||||
macro calloc_checked(..., Allocator* using = mem::heap(), usz end_padding = 0) @builtin
|
||||
{
|
||||
$if $checks($vatype(0).sizeof):
|
||||
var $Type = $vatype(0);
|
||||
$assert !type_alloc_must_be_aligned($vatype(0)) : "Type must be allocated with calloc_aligned";
|
||||
$if $vacount == 2:
|
||||
usz size = $vaarg(1);
|
||||
return (($Type*)using.calloc($Type.sizeof * size + end_padding))[:size];
|
||||
$else
|
||||
return ($Type*)using.calloc($Type.sizeof + end_padding);
|
||||
$endif
|
||||
$else
|
||||
return using.calloc($vaarg(0) + end_padding);
|
||||
$endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
|
||||
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
|
||||
* @require alignment && math::is_power_of_2(alignment)
|
||||
**/
|
||||
macro calloc_aligned(..., usz alignment = 0, Allocator* using = mem::heap(), usz end_padding = 0) @builtin
|
||||
{
|
||||
$if $checks($vatype(0).sizeof):
|
||||
var $Type = $vatype(0);
|
||||
$if $vacount == 2:
|
||||
usz size = $vaarg(1);
|
||||
return (($Type*)using.calloc_aligned($Type.sizeof * size + end_padding, alignment))[:size];
|
||||
$else
|
||||
return ($Type*)using.calloc_aligned($Type.sizeof + end_padding, alignment);
|
||||
$endif
|
||||
$else
|
||||
return using.calloc_aligned($vaarg(0) + end_padding, alignment);
|
||||
$endif
|
||||
}
|
||||
|
||||
fn void* realloc(void *ptr, usz new_size, Allocator* using = mem::heap()) @builtin @inline
|
||||
{
|
||||
return using.realloc(ptr, new_size)!!;
|
||||
}
|
||||
|
||||
fn void*! realloc_checked(void *ptr, usz new_size, Allocator* using = mem::heap()) @builtin @inline
|
||||
{
|
||||
return using.realloc(ptr, new_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require alignment && math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! realloc_aligned(void *ptr, usz new_size, usz alignment, Allocator* using = mem::heap()) @builtin @inline
|
||||
{
|
||||
return using.realloc_aligned(ptr, new_size, alignment);
|
||||
}
|
||||
|
||||
macro void free(void* ptr, Allocator* using = mem::heap()) @builtin => using.free(ptr)!!;
|
||||
macro void! free_checked(void* ptr, Allocator* using = mem::heap()) @builtin => using.free(ptr);
|
||||
macro void free_aligned(void* ptr, Allocator* using = mem::heap()) @builtin => using.free_aligned(ptr)!!;
|
||||
macro void! free_aligned_checked(void* ptr, Allocator* using = mem::heap()) @builtin => using.free_aligned(ptr);
|
||||
|
||||
/**
|
||||
* Run with a specific allocator inside of the macro body.
|
||||
**/
|
||||
macro void @scoped(Allocator* using; @body())
|
||||
{
|
||||
Allocator* old_allocator = thread_allocator;
|
||||
thread_allocator = using;
|
||||
defer thread_allocator = old_allocator;
|
||||
@body();
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
|
||||
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
|
||||
**/
|
||||
macro tmalloc(..., usz end_padding = 0, usz alignment = DEFAULT_MEM_ALIGNMENT) @builtin
|
||||
{
|
||||
$if $checks($vatype(0).sizeof):
|
||||
var $Type = $vatype(0);
|
||||
$if $vacount == 2:
|
||||
usz size = $vaarg(1);
|
||||
return (($Type*)temp().alloc_aligned($Type.sizeof * size + end_padding, alignment))[:size]!!;
|
||||
$else
|
||||
return ($Type*)temp().alloc_aligned($Type.sizeof + end_padding, alignment)!!;
|
||||
$endif
|
||||
$else
|
||||
return temp().alloc_aligned($vaarg(0) + end_padding, alignment)!!;
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
|
||||
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
|
||||
**/
|
||||
macro tcalloc(..., usz end_padding = 0, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin
|
||||
{
|
||||
$if $checks($vatype(0).sizeof):
|
||||
var $Type = $vatype(0);
|
||||
$if $vacount == 2:
|
||||
usz size = $vaarg(1);
|
||||
return (($Type*)temp().calloc_aligned($Type.sizeof * size + end_padding, alignment))[:size]!!;
|
||||
$else
|
||||
return ($Type*)temp().calloc_aligned($Type.sizeof + end_padding, alignment)!!;
|
||||
$endif
|
||||
$else
|
||||
return temp().calloc_aligned($vaarg(0) + end_padding, alignment)!!;
|
||||
$endif
|
||||
}
|
||||
|
||||
fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin @inline
|
||||
{
|
||||
return temp().realloc_aligned(ptr, size, alignment)!!;
|
||||
}
|
||||
|
||||
macro void @pool(;@body) @builtin
|
||||
{
|
||||
TempAllocator* allocator = temp();
|
||||
usz mark = allocator.used;
|
||||
defer allocator.reset(mark);
|
||||
@body();
|
||||
}
|
||||
|
||||
macro void @allocating_pool(Allocator* using; @body(bool is_temp)) @builtin
|
||||
{
|
||||
TempAllocator* allocator = temp();
|
||||
usz mark = allocator.used;
|
||||
bool is_temp = allocator == using;
|
||||
defer if (!is_temp) allocator.reset(mark);
|
||||
@body(is_temp);
|
||||
}
|
||||
|
||||
tlocal Allocator* thread_allocator @private = allocator::LIBC_ALLOCATOR;
|
||||
tlocal TempAllocator* thread_temp_allocator @private = null;
|
||||
|
||||
macro TempAllocator* temp_allocator() => temp();
|
||||
|
||||
macro TempAllocator* temp()
|
||||
{
|
||||
if (!thread_temp_allocator)
|
||||
{
|
||||
$switch (env::MEMORY_ENV)
|
||||
$case NORMAL:
|
||||
thread_temp_allocator = allocator::new_temp(1024 * 256, thread_allocator)!!;
|
||||
$case SMALL:
|
||||
thread_temp_allocator = allocator::new_temp(1024 * 16, thread_allocator)!!;
|
||||
$case TINY:
|
||||
thread_temp_allocator = allocator::new_temp(1024 * 2, thread_allocator)!!;
|
||||
$case NONE:
|
||||
unreachable("Temp allocator must explicitly created when memory-env is set to 'none'.");
|
||||
$endswitch
|
||||
}
|
||||
return thread_temp_allocator;
|
||||
}
|
||||
|
||||
macro Allocator* current_allocator() => thread_allocator;
|
||||
macro Allocator* heap() => thread_allocator;
|
||||
|
||||
$if !env::COMPILER_LIBC_AVAILABLE && env::ARCH_TYPE == ArchType.WASM32 || env::ARCH_TYPE == ArchType.WASM64:
|
||||
|
||||
SimpleHeapAllocator wasm_allocator @private;
|
||||
|
||||
extern int __heap_base;
|
||||
|
||||
static initialize @priority(1)
|
||||
{
|
||||
allocator::wasm_memory.allocate_block(mem::DEFAULT_MEM_ALIGNMENT)!!; // Give us a valid null.
|
||||
// Check if we need to move the heap.
|
||||
uptr start = (uptr)&__heap_base;
|
||||
if (start > mem::DEFAULT_MEM_ALIGNMENT) allocator::wasm_memory.use = start;
|
||||
wasm_allocator.init(fn (x) => allocator::wasm_memory.allocate_block(x));
|
||||
thread_allocator = &wasm_allocator;
|
||||
}
|
||||
$endif
|
||||
|
||||
|
||||
109
lib/std/core/mem_allocator.c3
Normal file
109
lib/std/core/mem_allocator.c3
Normal file
@@ -0,0 +1,109 @@
|
||||
module std::core::mem::allocator;
|
||||
|
||||
const DEFAULT_SIZE_PREFIX = usz.sizeof;
|
||||
const DEFAULT_SIZE_PREFIX_ALIGNMENT = usz.alignof;
|
||||
|
||||
const Allocator* NULL_ALLOCATOR = &_NULL_ALLOCATOR;
|
||||
const Allocator* LIBC_ALLOCATOR = &_SYSTEM_ALLOCATOR;
|
||||
|
||||
def AllocatorFunction = fn void*!(Allocator* allocator, usz new_size, usz alignment, usz offset, void* old_pointer, AllocationKind kind);
|
||||
|
||||
struct Allocator
|
||||
{
|
||||
AllocatorFunction function;
|
||||
}
|
||||
|
||||
enum AllocationKind
|
||||
{
|
||||
ALLOC,
|
||||
CALLOC,
|
||||
REALLOC,
|
||||
FREE,
|
||||
ALIGNED_ALLOC,
|
||||
ALIGNED_CALLOC,
|
||||
ALIGNED_REALLOC,
|
||||
ALIGNED_FREE,
|
||||
RESET,
|
||||
MARK,
|
||||
}
|
||||
|
||||
fault AllocationFailure
|
||||
{
|
||||
OUT_OF_MEMORY,
|
||||
UNSUPPORTED_OPERATION,
|
||||
CHUNK_TOO_LARGE,
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn void*! Allocator.alloc(Allocator* allocator, usz size) @inline
|
||||
{
|
||||
return allocator.function(allocator, size, 0, 0, null, ALLOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require alignment && math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! Allocator.alloc_aligned(Allocator* allocator, usz size, usz alignment, usz offset = 0) @inline
|
||||
{
|
||||
return allocator.function(allocator, size, alignment, offset, null, ALIGNED_ALLOC);
|
||||
}
|
||||
|
||||
fn void*! Allocator.realloc(Allocator* allocator, void* old_pointer, usz size) @inline
|
||||
{
|
||||
return allocator.function(allocator, size, 0, 0, old_pointer, REALLOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require alignment && math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! Allocator.realloc_aligned(Allocator* allocator, void* old_pointer, usz size, usz alignment, usz offset = 0) @inline
|
||||
{
|
||||
return allocator.function(allocator, size, alignment, offset, old_pointer, ALIGNED_REALLOC);
|
||||
}
|
||||
|
||||
fn usz! Allocator.mark(Allocator* allocator) @inline
|
||||
{
|
||||
return (usz)(uptr)allocator.function(allocator, 0, 0, 0, null, MARK);
|
||||
}
|
||||
|
||||
|
||||
fn void*! Allocator.calloc(Allocator* allocator, usz size) @inline
|
||||
{
|
||||
return allocator.function(allocator, size, 0, 0, null, CALLOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require alignment && math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! Allocator.calloc_aligned(Allocator* allocator, usz size, usz alignment, usz offset = 0) @inline
|
||||
{
|
||||
return allocator.function(allocator, size, alignment, offset, null, ALIGNED_CALLOC);
|
||||
}
|
||||
|
||||
fn void! Allocator.free(Allocator* allocator, void* old_pointer) @inline
|
||||
{
|
||||
allocator.function(allocator, 0, 0, 0, old_pointer, FREE)!;
|
||||
}
|
||||
|
||||
fn void! Allocator.free_aligned(Allocator* allocator, void* old_pointer) @inline
|
||||
{
|
||||
allocator.function(allocator, 0, 0, 0, old_pointer, ALIGNED_FREE)!;
|
||||
}
|
||||
|
||||
fn void Allocator.reset(Allocator* allocator, usz mark = 0)
|
||||
{
|
||||
allocator.function(allocator, mark, 0, 0, null, RESET)!!;
|
||||
}
|
||||
|
||||
fn usz alignment_for_allocation(usz alignment) @inline @private
|
||||
{
|
||||
if (alignment < mem::DEFAULT_MEM_ALIGNMENT)
|
||||
{
|
||||
alignment = mem::DEFAULT_MEM_ALIGNMENT;
|
||||
}
|
||||
return alignment;
|
||||
}
|
||||
|
||||
|
||||
|
||||
32
lib/std/core/os/wasm_memory.c3
Normal file
32
lib/std/core/os/wasm_memory.c3
Normal file
@@ -0,0 +1,32 @@
|
||||
module std::core::mem::allocator;
|
||||
|
||||
|
||||
const usz WASM_BLOCK_SIZE = 65536;
|
||||
|
||||
WasmMemory wasm_memory;
|
||||
|
||||
struct WasmMemory
|
||||
{
|
||||
usz allocation;
|
||||
uptr use;
|
||||
}
|
||||
|
||||
fn char[]! WasmMemory.allocate_block(WasmMemory* this, usz bytes)
|
||||
{
|
||||
if (!this.allocation)
|
||||
{
|
||||
this.allocation = $$wasm_memory_size(0) * WASM_BLOCK_SIZE;
|
||||
}
|
||||
isz bytes_required = bytes + this.use - this.allocation;
|
||||
if (bytes_required <= 0)
|
||||
{
|
||||
defer this.use += bytes;
|
||||
return ((char*)this.use)[:bytes];
|
||||
}
|
||||
|
||||
usz blocks_required = (bytes_required + WASM_BLOCK_SIZE + 1) / WASM_BLOCK_SIZE;
|
||||
if ($$wasm_memory_grow(0, blocks_required) == -1) return AllocationFailure.OUT_OF_MEMORY?;
|
||||
this.allocation = $$wasm_memory_size(0) * WASM_BLOCK_SIZE;
|
||||
defer this.use += bytes;
|
||||
return ((char*)this.use)[:bytes];
|
||||
}
|
||||
168
lib/std/core/private/main_stub.c3
Normal file
168
lib/std/core/private/main_stub.c3
Normal file
@@ -0,0 +1,168 @@
|
||||
module std::core::main_stub;
|
||||
|
||||
macro usz _strlen(ptr) @private
|
||||
{
|
||||
usz len = 0;
|
||||
while (ptr[len]) len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
macro int @main_to_err_main(#m, int, char**)
|
||||
{
|
||||
if (catch #m()) return 1;
|
||||
return 0;
|
||||
}
|
||||
macro int @main_to_int_main(#m, int, char**) => #m();
|
||||
macro int @main_to_void_main(#m, int, char**)
|
||||
{
|
||||
#m();
|
||||
return 0;
|
||||
}
|
||||
|
||||
macro String[] args_to_strings(int argc, char** argv) @private
|
||||
{
|
||||
String[] list = malloc(String, argc);
|
||||
for (int i = 0; i < argc; i++)
|
||||
{
|
||||
char* arg = argv[i];
|
||||
usz len = 0;
|
||||
list[i] = (String)arg[:_strlen(arg)];
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
macro int @main_to_err_main_args(#m, int argc, char** argv)
|
||||
{
|
||||
String[] list = args_to_strings(argc, argv);
|
||||
defer free(list.ptr);
|
||||
if (catch #m(list)) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
macro int @main_to_int_main_args(#m, int argc, char** argv)
|
||||
{
|
||||
String[] list = args_to_strings(argc, argv);
|
||||
defer free(list.ptr);
|
||||
return #m(list);
|
||||
}
|
||||
|
||||
macro int @main_to_void_main_args(#m, int argc, char** argv)
|
||||
{
|
||||
String[] list = args_to_strings(argc, argv);
|
||||
defer free(list.ptr);
|
||||
#m(list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
$if env::os_is_win32():
|
||||
|
||||
extern fn Char16** _win_command_line_to_argv_w(ushort* cmd_line, int* argc_ptr) @extern("CommandLineToArgvW");
|
||||
|
||||
macro String[] win_command_line_to_strings(ushort* cmd_line) @private
|
||||
{
|
||||
int argc;
|
||||
Char16** argv = _win_command_line_to_argv_w(cmd_line, &argc);
|
||||
return wargs_strings(argc, argv);
|
||||
}
|
||||
|
||||
macro String[] wargs_strings(int argc, Char16** argv) @private
|
||||
{
|
||||
String[] list = malloc(String, argc);
|
||||
for (int i = 0; i < argc; i++)
|
||||
{
|
||||
Char16* arg = argv[i];
|
||||
Char16[] argstring = arg[:_strlen(arg)];
|
||||
list[i] = string::from_utf16(argstring) ?? "?".copy();
|
||||
}
|
||||
return list[:argc];
|
||||
}
|
||||
|
||||
macro void release_wargs(String[] list) @private
|
||||
{
|
||||
foreach (s : list) free(s.ptr);
|
||||
free(list.ptr);
|
||||
}
|
||||
|
||||
macro int @win_to_err_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
if (catch #m()) return 1;
|
||||
return 0;
|
||||
}
|
||||
macro int @win_to_int_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd) => #m();
|
||||
macro int @win_to_void_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
#m();
|
||||
return 0;
|
||||
}
|
||||
|
||||
macro int @win_to_err_main_args(#m, void* handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
String[] args = win_command_line_to_strings(cmd_line);
|
||||
defer release_wargs(args);
|
||||
if (catch #m(args)) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
macro int @win_to_int_main_args(#m, void* handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
String[] args = win_command_line_to_strings(cmd_line);
|
||||
defer release_wargs(args);
|
||||
return #m(args);
|
||||
}
|
||||
|
||||
macro int @win_to_void_main_args(#m, void* handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
String[] args = win_command_line_to_strings(cmd_line);
|
||||
defer release_wargs(args);
|
||||
#m(args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
macro int @win_to_err_main(#m, void* handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
String[] args = win_command_line_to_strings(cmd_line);
|
||||
defer release_wargs(args);
|
||||
if (catch #m(handle, args, show_cmd)) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
macro int @win_to_int_main(#m, void* handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
String[] args = win_command_line_to_strings(cmd_line);
|
||||
defer release_wargs(args);
|
||||
return #m(handle, args, show_cmd);
|
||||
}
|
||||
|
||||
macro int @win_to_void_main(#m, void* handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
String[] args = win_command_line_to_strings(cmd_line);
|
||||
defer release_wargs(args);
|
||||
#m(handle, args, show_cmd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
macro int @wmain_to_err_main_args(#m, int argc, Char16** argv)
|
||||
{
|
||||
String[] args = wargs_strings(argc, argv);
|
||||
defer release_wargs(args);
|
||||
if (catch #m(args)) return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
macro int @wmain_to_int_main_args(#m, int argc, Char16** argv)
|
||||
{
|
||||
String[] args = wargs_strings(argc, argv);
|
||||
defer release_wargs(args);
|
||||
return #m(args);
|
||||
}
|
||||
|
||||
macro int @wmain_to_void_main_args(#m, int argc, Char16** argv)
|
||||
{
|
||||
String[] args = wargs_strings(argc, argv);
|
||||
defer release_wargs(args);
|
||||
#m(args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
$endif
|
||||
99
lib/std/core/runtime.c3
Normal file
99
lib/std/core/runtime.c3
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) 2021 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;
|
||||
|
||||
struct VirtualAny
|
||||
{
|
||||
void* ptr;
|
||||
typeid type_id;
|
||||
}
|
||||
|
||||
struct SubArrayContainer
|
||||
{
|
||||
void* ptr;
|
||||
usz len;
|
||||
}
|
||||
|
||||
def TestFn = fn void!();
|
||||
|
||||
struct TestRunner
|
||||
{
|
||||
String[] test_names;
|
||||
TestFn[] test_fns;
|
||||
JmpBuf buf;
|
||||
}
|
||||
|
||||
fn TestRunner test_runner_create()
|
||||
{
|
||||
return TestRunner {
|
||||
.test_fns = $$TEST_FNS,
|
||||
.test_names = $$TEST_NAMES,
|
||||
};
|
||||
}
|
||||
|
||||
import libc;
|
||||
|
||||
TestRunner* current_runner @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(¤t_runner.buf, 1);
|
||||
}
|
||||
|
||||
fn bool TestRunner.run(TestRunner* runner)
|
||||
{
|
||||
current_runner = runner;
|
||||
PanicFn old_panic = builtin::panic;
|
||||
defer builtin::panic = old_panic;
|
||||
builtin::panic = &test_panic;
|
||||
int tests_passed = 0;
|
||||
int tests = runner.test_names.len;
|
||||
io::printn("----- TESTS -----");
|
||||
foreach(i, String name : runner.test_names)
|
||||
{
|
||||
io::printf("Testing %s ... ", name);
|
||||
if (libc::setjmp(&runner.buf) == 0)
|
||||
{
|
||||
if (catch err = runner.test_fns[i]())
|
||||
{
|
||||
io::printn("[failed]");
|
||||
continue;
|
||||
}
|
||||
io::printn("[ok]");
|
||||
tests_passed++;
|
||||
}
|
||||
}
|
||||
io::printfn("\n%d test(s) run.\n", tests);
|
||||
io::print("Test Result: ");
|
||||
if (tests_passed < tests)
|
||||
{
|
||||
io::print("FAILED");
|
||||
}
|
||||
else
|
||||
{
|
||||
io::print("ok");
|
||||
}
|
||||
io::printfn(". %d passed, %d failed.", tests_passed, tests - tests_passed);
|
||||
return tests == tests_passed;
|
||||
}
|
||||
|
||||
fn bool __run_default_test_runner()
|
||||
{
|
||||
return test_runner_create().run();
|
||||
}
|
||||
|
||||
$if !env::COMPILER_LIBC_AVAILABLE && env::ARCH_TYPE == ArchType.WASM32 || env::ARCH_TYPE == ArchType.WASM64:
|
||||
|
||||
extern fn void __wasm_call_ctors();
|
||||
fn void wasm_initialize() @extern("_initialize") @wasm
|
||||
{
|
||||
// The linker synthesizes this to call constructors.
|
||||
__wasm_call_ctors();
|
||||
}
|
||||
$endif
|
||||
514
lib/std/core/string.c3
Normal file
514
lib/std/core/string.c3
Normal file
@@ -0,0 +1,514 @@
|
||||
module std::core::string;
|
||||
import std::ascii;
|
||||
|
||||
def ZString = distinct inline char*;
|
||||
def Char32 = uint;
|
||||
def Char16 = ushort;
|
||||
|
||||
fault UnicodeResult
|
||||
{
|
||||
INVALID_UTF8,
|
||||
INVALID_UTF16,
|
||||
CONVERSION_FAILED,
|
||||
}
|
||||
|
||||
const uint SURROGATE_OFFSET @private = 0x10000;
|
||||
const uint SURROGATE_GENERIC_MASK @private = 0xF800;
|
||||
const uint SURROGATE_MASK @private = 0xFC00;
|
||||
const uint SURROGATE_CODEPOINT_MASK @private = 0x03FF;
|
||||
const uint SURROGATE_BITS @private = 10;
|
||||
const uint SURROGATE_LOW_VALUE @private = 0xDC00;
|
||||
const uint SURROGATE_HIGH_VALUE @private = 0xD800;
|
||||
|
||||
fault NumberConversion
|
||||
{
|
||||
EMPTY_STRING,
|
||||
NEGATIVE_VALUE,
|
||||
MALFORMED_INTEGER,
|
||||
INTEGER_OVERFLOW,
|
||||
MALFORMED_FLOAT,
|
||||
FLOAT_OUT_OF_RANGE,
|
||||
}
|
||||
|
||||
macro String printf(String fmt, ..., Allocator* using = mem::heap())
|
||||
{
|
||||
@stack_mem(256; Allocator* mem)
|
||||
{
|
||||
DString str;
|
||||
str.init(.using = mem);
|
||||
str.printf(fmt, $vasplat());
|
||||
return str.copy_str(using);
|
||||
};
|
||||
}
|
||||
|
||||
macro String tprintf(String fmt, ...)
|
||||
{
|
||||
DString str;
|
||||
str.tinit();
|
||||
str.printf(fmt, $vasplat());
|
||||
return str.str();
|
||||
}
|
||||
|
||||
macro bool char_in_set(char c, String set)
|
||||
{
|
||||
foreach (ch : set) if (ch == c) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn String join(String[] s, String joiner, Allocator* using = mem::heap())
|
||||
{
|
||||
if (!s)
|
||||
{
|
||||
return (String)(calloc(char, 2, .using = using)[:0]);
|
||||
}
|
||||
|
||||
usz total_size = joiner.len * s.len;
|
||||
foreach (String* &str : s)
|
||||
{
|
||||
total_size += str.len;
|
||||
}
|
||||
@stack_mem(256; Allocator* mem)
|
||||
{
|
||||
DString res = dstring::new_with_capacity(total_size, .using = mem);
|
||||
res.append(s[0]);
|
||||
foreach (String* &str : s[1..])
|
||||
{
|
||||
res.append(joiner);
|
||||
res.append(*str);
|
||||
}
|
||||
return res.copy_str(using);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [in] string
|
||||
* @param [in] to_trim
|
||||
**/
|
||||
fn String String.trim(String 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];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [in] string
|
||||
* @param [in] needle
|
||||
**/
|
||||
fn bool String.starts_with(String string, String needle)
|
||||
{
|
||||
if (needle.len > string.len) return false;
|
||||
if (!needle.len) return true;
|
||||
return string[:needle.len] == needle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [in] string
|
||||
* @param [in] needle
|
||||
**/
|
||||
fn bool String.ends_with(String string, String needle)
|
||||
{
|
||||
if (needle.len > string.len) return false;
|
||||
if (!needle.len) return true;
|
||||
return string[^needle.len..] == needle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip the front of the string if the prefix exists.
|
||||
*
|
||||
* @param [in] string
|
||||
* @param [in] needle
|
||||
**/
|
||||
fn String String.strip(String 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
|
||||
**/
|
||||
fn String String.strip_end(String string, String needle)
|
||||
{
|
||||
if (!needle.len || !string.ends_with(needle)) return string;
|
||||
// Note that this is the safe way if we want to support zero length.
|
||||
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] using "The allocator, defaults to the heap allocator"
|
||||
* @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(String s, String needle, usz max = 0, Allocator* using = mem::heap())
|
||||
{
|
||||
usz capacity = 16;
|
||||
usz i = 0;
|
||||
String* holder = malloc(String, capacity, .using = using);
|
||||
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 (i == capacity)
|
||||
{
|
||||
capacity *= 2;
|
||||
holder = realloc(holder, String.sizeof * capacity, .using = using);
|
||||
}
|
||||
holder[i++] = res;
|
||||
}
|
||||
return holder[:i];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(String s, String needle, usz max = 0)
|
||||
{
|
||||
return s.split(needle, max, mem::temp()) @inline;
|
||||
}
|
||||
|
||||
fn bool String.contains(String s, String needle)
|
||||
{
|
||||
return @ok(s.index_of(needle));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(String s, String needle)
|
||||
{
|
||||
usz match = 0;
|
||||
usz needed = needle.len;
|
||||
usz index_start = 0;
|
||||
char search = needle[0];
|
||||
foreach (usz i, char c : s)
|
||||
{
|
||||
if (c == search)
|
||||
{
|
||||
if (!match) index_start = i;
|
||||
match++;
|
||||
if (match == needed) return index_start;
|
||||
search = needle[match];
|
||||
continue;
|
||||
}
|
||||
if (match)
|
||||
{
|
||||
match = 0;
|
||||
search = needle[0];
|
||||
}
|
||||
}
|
||||
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"
|
||||
**/
|
||||
fn usz! String.rindex_of(String s, String needle)
|
||||
{
|
||||
usz match = 0;
|
||||
usz needed = needle.len;
|
||||
usz index_start = 0;
|
||||
char search = needle[^1];
|
||||
foreach_r (usz i, char c : s)
|
||||
{
|
||||
if (c == search)
|
||||
{
|
||||
if (!match) index_start = i;
|
||||
match++;
|
||||
if (match == needed) return index_start - needle.len + 1;
|
||||
search = needle[^(match + 1)];
|
||||
continue;
|
||||
}
|
||||
if (match)
|
||||
{
|
||||
match = 0;
|
||||
search = needle[^1];
|
||||
}
|
||||
}
|
||||
return SearchResult.MISSING?;
|
||||
}
|
||||
|
||||
fn String ZString.as_str(ZString str)
|
||||
{
|
||||
return (String)((char*)str)[:str.len()];
|
||||
}
|
||||
|
||||
fn usz ZString.char_len(ZString str)
|
||||
{
|
||||
usz len = 0;
|
||||
char* ptr = (char*)str;
|
||||
while (char c = ptr++[0])
|
||||
{
|
||||
if (c & 0xC0 != 0x80) len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
fn usz ZString.len(ZString str)
|
||||
{
|
||||
usz len = 0;
|
||||
char* ptr = (char*)str;
|
||||
while (char c = ptr++[0]) len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
fn ZString String.zstr_copy(String s, Allocator* using = mem::heap())
|
||||
{
|
||||
usz len = s.len;
|
||||
char* str = malloc(len + 1, .using = using);
|
||||
mem::copy(str, s.ptr, len);
|
||||
str[len] = 0;
|
||||
return (ZString)str;
|
||||
}
|
||||
|
||||
fn String String.concat(String s1, String s2, Allocator* using = mem::heap())
|
||||
{
|
||||
usz full_len = s1.len + s2.len;
|
||||
char* str = malloc(full_len + 1, .using = using);
|
||||
usz s1_len = s1.len;
|
||||
mem::copy(str, s1.ptr, s1_len);
|
||||
mem::copy(str + s1_len, s2.ptr, s2.len);
|
||||
str[full_len] = 0;
|
||||
return (String)str[:full_len];
|
||||
}
|
||||
|
||||
fn String String.tconcat(String s1, String s2) => s1.concat(s2, mem::temp());
|
||||
|
||||
|
||||
fn ZString String.zstr_tcopy(String s) => s.zstr_copy(mem::temp()) @inline;
|
||||
|
||||
fn String String.copy(String s, Allocator* using = mem::heap())
|
||||
{
|
||||
usz len = s.len;
|
||||
char* str = malloc(len + 1, .using = using);
|
||||
mem::copy(str, s.ptr, len);
|
||||
str[len] = 0;
|
||||
return (String)str[:len];
|
||||
}
|
||||
|
||||
fn String String.tcopy(String s) => s.copy(mem::temp()) @inline;
|
||||
|
||||
fn String ZString.copy(ZString z, Allocator* using = mem::heap()) => z.as_str().copy(using) @inline;
|
||||
fn String ZString.tcopy(ZString z) => z.as_str().copy(mem::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"
|
||||
**/
|
||||
fn Char16[]! String.to_utf16(String s, Allocator* using = mem::heap())
|
||||
{
|
||||
usz len16 = conv::utf16len_for_utf8(s);
|
||||
Char16* data = malloc_checked(Char16, len16 + 1, .using = using)!;
|
||||
conv::utf8to16_unsafe(s, data)!;
|
||||
data[len16] = 0;
|
||||
return data[:len16];
|
||||
}
|
||||
|
||||
fn Char32[]! String.to_utf32(String s, Allocator* using = mem::heap())
|
||||
{
|
||||
usz codepoints = conv::utf8_codepoints(s);
|
||||
Char32* data = malloc_checked(Char32, codepoints + 1, .using = using)!;
|
||||
conv::utf8to32_unsafe(s, data)!;
|
||||
data[codepoints] = 0;
|
||||
return data[:codepoints];
|
||||
}
|
||||
|
||||
fn void String.convert_ascii_to_lower(String s)
|
||||
{
|
||||
foreach (&c : s) if (*c >= 'A' && *c <= 'Z') *c += 'a' - 'A';
|
||||
}
|
||||
|
||||
fn String String.ascii_to_lower(String s, Allocator* using = mem::heap())
|
||||
{
|
||||
String copy = s.copy(using);
|
||||
copy.convert_ascii_to_lower();
|
||||
return copy;
|
||||
}
|
||||
|
||||
fn void String.convert_ascii_to_upper(String s)
|
||||
{
|
||||
foreach (&c : s) if (*c >= 'a' && *c <= 'z') *c -= 'a' - 'A';
|
||||
}
|
||||
|
||||
fn String String.ascii_to_upper(String s, Allocator* using = mem::heap())
|
||||
{
|
||||
String copy = s.copy(using);
|
||||
copy.convert_ascii_to_upper();
|
||||
return copy;
|
||||
}
|
||||
|
||||
fn String! from_utf32(Char32[] utf32, Allocator* using = mem::heap())
|
||||
{
|
||||
usz len = conv::utf8len_for_utf32(utf32);
|
||||
char* data = malloc_checked(len + 1, .using = using)!;
|
||||
defer catch free(data, .using = using);
|
||||
conv::utf32to8_unsafe(utf32, data);
|
||||
data[len] = 0;
|
||||
return (String)data[:len];
|
||||
}
|
||||
|
||||
fn String! from_utf16(Char16[] utf16, Allocator* using = mem::heap())
|
||||
{
|
||||
usz len = conv::utf8len_for_utf16(utf16);
|
||||
char* data = malloc_checked(len + 1, .using = using)!;
|
||||
defer catch free(data, .using = using);
|
||||
conv::utf16to8_unsafe(utf16, data)!;
|
||||
data[len] = 0;
|
||||
return (String)data[:len];
|
||||
}
|
||||
|
||||
fn String! from_zutf16(Char16* utf16_pointer, Allocator* using = mem::heap())
|
||||
{
|
||||
usz utf16_len;
|
||||
while (utf16_pointer[utf16_len] != 0) utf16_len++;
|
||||
Char16[] utf16 = utf16_pointer[:utf16_len];
|
||||
return from_utf16(utf16, using);
|
||||
}
|
||||
|
||||
fn usz String.utf8_codepoints(String s)
|
||||
{
|
||||
usz len = 0;
|
||||
foreach (char c : s)
|
||||
{
|
||||
if (c & 0xC0 != 0x80) len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
macro String.to_integer(String string, $Type)
|
||||
{
|
||||
usz len = string.len;
|
||||
usz index = 0;
|
||||
char* ptr = string.ptr;
|
||||
while (index < len && ascii::is_blank_m(ptr[index])) index++;
|
||||
if (len == index) return NumberConversion.EMPTY_STRING?;
|
||||
bool is_negative;
|
||||
switch (string[index])
|
||||
{
|
||||
case '-':
|
||||
if ($Type.min == 0) return NumberConversion.NEGATIVE_VALUE?;
|
||||
is_negative = true;
|
||||
index++;
|
||||
case '+':
|
||||
index++;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (len == index) return NumberConversion.MALFORMED_INTEGER?;
|
||||
$Type base = 10;
|
||||
if (string[index] == '0')
|
||||
{
|
||||
index++;
|
||||
if (index == len) return ($Type)0;
|
||||
switch (string[index])
|
||||
{
|
||||
case 'x':
|
||||
case 'X':
|
||||
base = 16;
|
||||
index++;
|
||||
case 'b':
|
||||
case 'B':
|
||||
base = 2;
|
||||
index++;
|
||||
case 'o':
|
||||
case 'O':
|
||||
base = 8;
|
||||
index++;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (len == index) return NumberConversion.MALFORMED_INTEGER?;
|
||||
}
|
||||
$Type value = 0;
|
||||
while (index != len)
|
||||
{
|
||||
char c = {|
|
||||
char ch = string[index++];
|
||||
if (base != 16 || ch < 'A') return (char)(ch - '0');
|
||||
if (ch <= 'F') return (char)(ch - 'A');
|
||||
if (ch < 'a') return NumberConversion.MALFORMED_INTEGER?;
|
||||
if (ch > 'f') return NumberConversion.MALFORMED_INTEGER?;
|
||||
return (char)(ch - 'a');
|
||||
|}!;
|
||||
if (c >= base) return NumberConversion.MALFORMED_INTEGER?;
|
||||
value = {|
|
||||
if (is_negative)
|
||||
{
|
||||
$Type new_value = value * base - c;
|
||||
if (new_value > value) return NumberConversion.INTEGER_OVERFLOW?;
|
||||
return new_value;
|
||||
}
|
||||
$Type new_value = value * base + c;
|
||||
if (new_value < value) return NumberConversion.INTEGER_OVERFLOW?;
|
||||
return new_value;
|
||||
|}!;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
fn Char16[]! String.to_temp_utf16(String s) => s.to_utf16(mem::temp());
|
||||
|
||||
fn int128! String.to_int128(String s) => s.to_integer(int128);
|
||||
fn long! String.to_long(String s) => s.to_integer(long);
|
||||
fn int! String.to_int(String s) => s.to_integer(int);
|
||||
fn short! String.to_short(String s) => s.to_integer(short);
|
||||
fn ichar! String.to_ichar(String s) => s.to_integer(ichar);
|
||||
|
||||
fn uint128! String.to_uint128(String s) => s.to_integer(uint128);
|
||||
fn ulong! String.to_ulong(String s) => s.to_integer(ulong);
|
||||
fn uint! String.to_uint(String s) => s.to_integer(uint);
|
||||
fn ushort! String.to_ushort(String s) => s.to_integer(ushort);
|
||||
fn char! String.to_uchar(String s) => s.to_integer(char);
|
||||
|
||||
fn double! String.to_double(String s) => s.to_real(double);
|
||||
fn float! String.to_float(String s) => s.to_real(float);
|
||||
23
lib/std/core/string_iterator.c3
Normal file
23
lib/std/core/string_iterator.c3
Normal file
@@ -0,0 +1,23 @@
|
||||
module std::core::string::iterator;
|
||||
|
||||
struct StringIterator
|
||||
{
|
||||
String utf8;
|
||||
usz current;
|
||||
}
|
||||
|
||||
fn void StringIterator.reset(StringIterator* this)
|
||||
{
|
||||
this.current = 0;
|
||||
}
|
||||
|
||||
fn Char32! StringIterator.next(StringIterator* this)
|
||||
{
|
||||
usz len = this.utf8.len;
|
||||
usz current = this.current;
|
||||
if (current >= len) return IteratorResult.NO_MORE_ELEMENT?;
|
||||
usz read = (len - current < 4 ? len - current : 4);
|
||||
Char32 res = conv::utf8_to_char32(&this.utf8[current], &read)!;
|
||||
this.current += read;
|
||||
return res;
|
||||
}
|
||||
487
lib/std/core/string_to_real.c3
Normal file
487
lib/std/core/string_to_real.c3
Normal file
@@ -0,0 +1,487 @@
|
||||
module std::core::string;
|
||||
import std::math;
|
||||
|
||||
// Float parsing based on code in Musl floatscan.c by Rich Felker.
|
||||
// Musl uses the MIT license, copied below:
|
||||
// ----------------------------------------------------------------------
|
||||
// Copyright © 2005-2014 Rich Felker, et al.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const KMAX = 128;
|
||||
const MASK = KMAX - 1;
|
||||
const B1B_DIG = 2;
|
||||
const uint[2] B1B_MAX = { 9007199, 254740991 };
|
||||
|
||||
/**
|
||||
* @require chars.len > 0
|
||||
**/
|
||||
macro double! decfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
{
|
||||
uint[KMAX] x;
|
||||
const uint[2] TH = B1B_MAX;
|
||||
int emax = - $emin - $bits + 3;
|
||||
|
||||
const int[*] P10S = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
|
||||
usz index;
|
||||
bool got_digit = chars[0] == '0';
|
||||
bool got_rad;
|
||||
long lrp, dc;
|
||||
int k, j, lnz;
|
||||
usz len = chars.len;
|
||||
usz last_char = len - 1;
|
||||
|
||||
assert(len);
|
||||
|
||||
char c @noinit;
|
||||
// Skip past first characters
|
||||
while ((c = chars[index]) == '0')
|
||||
{
|
||||
if (index == last_char) return sign * 0.0;
|
||||
index++;
|
||||
}
|
||||
|
||||
if (c == '.')
|
||||
{
|
||||
got_rad = true;
|
||||
if (index == last_char)
|
||||
{
|
||||
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
|
||||
return sign * 0.0;
|
||||
}
|
||||
if (index != last_char && (c = chars[++index]) == '0')
|
||||
{
|
||||
lrp--;
|
||||
got_digit = true;
|
||||
while (last_char != index && (c = chars[++index]) == '0')
|
||||
{
|
||||
lrp--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (c - '0' < 10u || c == '.')
|
||||
{
|
||||
switch
|
||||
{
|
||||
case c == '.':
|
||||
if (got_rad) return NumberConversion.MALFORMED_FLOAT?;
|
||||
got_rad = true;
|
||||
lrp = dc;
|
||||
case k < KMAX - 3:
|
||||
dc++;
|
||||
if (c != '0') lnz = (int)dc;
|
||||
if (j)
|
||||
{
|
||||
x[k] = x[k] * 10 + c - '0';
|
||||
}
|
||||
else
|
||||
{
|
||||
x[k] = c - '0';
|
||||
}
|
||||
if (++j == 9)
|
||||
{
|
||||
k++;
|
||||
j = 0;
|
||||
}
|
||||
got_digit = true;
|
||||
default:
|
||||
dc++;
|
||||
if (c != '0') x[KMAX - 4] |= 1;
|
||||
|
||||
}
|
||||
if (index == last_char) break;
|
||||
assert(index < last_char);
|
||||
c = chars[++index];
|
||||
}
|
||||
if (!got_rad) lrp = dc;
|
||||
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
|
||||
if ((c | 32) == 'e')
|
||||
{
|
||||
if (last_char == index) return NumberConversion.MALFORMED_FLOAT?;
|
||||
long e10 = String.to_long((String)chars[index + 1..]) ?? NumberConversion.MALFORMED_FLOAT?!;
|
||||
lrp += e10;
|
||||
}
|
||||
else if (index != last_char)
|
||||
{
|
||||
return NumberConversion.MALFORMED_FLOAT?;
|
||||
}
|
||||
// Handle zero specially to avoid nasty special cases later
|
||||
if (!x[0]) return sign * 0.0;
|
||||
|
||||
// Optimize small integers (w/no exponent) and over/under-flow
|
||||
if (lrp == dc && dc < 10 && ($bits > 30 || (ulong)x[0] >> $bits == 0)) return sign * (double)x[0];
|
||||
if (lrp > - $emin / 2) return NumberConversion.FLOAT_OUT_OF_RANGE?;
|
||||
if (lrp < $emin - 2 * math::DOUBLE_MANT_DIG) return NumberConversion.FLOAT_OUT_OF_RANGE?;
|
||||
|
||||
// Align incomplete final B1B digit
|
||||
if (j)
|
||||
{
|
||||
for (; j < 9; j++) x[k] *= 10;
|
||||
k++;
|
||||
j = 0;
|
||||
}
|
||||
|
||||
int a;
|
||||
int z = k;
|
||||
int e2;
|
||||
long rp = lrp;
|
||||
|
||||
// Optimize small to mid-size integers (even in exp. notation)
|
||||
if (lnz < 9 && lnz <= rp && rp < 18)
|
||||
{
|
||||
if (rp == 9) return sign * (double)x[0];
|
||||
if (rp < 9) return sign * (double)x[0] / P10S[8 - rp];
|
||||
int bitlim = $bits - 3 * (int)(rp - 9);
|
||||
if (bitlim > 30 || x[0] >> bitlim == 0) return sign * (double)x[0] * P10S[rp - 10];
|
||||
}
|
||||
|
||||
// Align radix point to B1B digit boundary
|
||||
if (rp % 9)
|
||||
{
|
||||
long rpm9 = rp >= 0 ? rp % 9 : rp % 9 + 9;
|
||||
int p10 = P10S[8 - rpm9];
|
||||
uint carry = 0;
|
||||
for (k = a; k != z; k++)
|
||||
{
|
||||
uint tmp = x[k] % p10;
|
||||
x[k] = x[k] / p10 + carry;
|
||||
carry = 1000000000 / p10 * tmp;
|
||||
if (k == a && !x[k])
|
||||
{
|
||||
a = (a + 1) & MASK;
|
||||
rp -= 9;
|
||||
}
|
||||
}
|
||||
if (carry) x[z++] = carry;
|
||||
rp += 9 - rpm9;
|
||||
}
|
||||
|
||||
// Upscale until desired number of bits are left of radix point
|
||||
while (rp < 9 * B1B_DIG || (rp == 9 * B1B_DIG && x[a] < TH[0]))
|
||||
{
|
||||
uint carry = 0;
|
||||
e2 -= 29;
|
||||
for (k = (z - 1) & MASK; ; k = (k - 1) & MASK)
|
||||
{
|
||||
ulong tmp = (ulong)x[k] << 29 + carry;
|
||||
if (tmp > 1000000000)
|
||||
{
|
||||
carry = (uint)(tmp / 1000000000);
|
||||
x[k] = (uint)(tmp % 1000000000);
|
||||
}
|
||||
else
|
||||
{
|
||||
carry = 0;
|
||||
x[k] = (uint)tmp;
|
||||
}
|
||||
if (k == (z - 1) & MASK && k != a && !x[k]) z = k;
|
||||
if (k == a) break;
|
||||
}
|
||||
if (carry)
|
||||
{
|
||||
rp += 9;
|
||||
a = (a - 1) & MASK;
|
||||
if (a == z)
|
||||
{
|
||||
z = (z - 1) & MASK;
|
||||
x[(z - 1) & MASK] |= x[z];
|
||||
}
|
||||
x[a] = carry;
|
||||
}
|
||||
}
|
||||
|
||||
// Downscale until exactly number of bits are left of radix point
|
||||
while (true)
|
||||
{
|
||||
uint carry = 0;
|
||||
int sh = 1;
|
||||
int i;
|
||||
for (i = 0; i < B1B_DIG; i++)
|
||||
{
|
||||
k = (a + i) & MASK;
|
||||
if (k == z || x[k] < TH[i])
|
||||
{
|
||||
i = B1B_DIG;
|
||||
break;
|
||||
}
|
||||
if (x[(a + i) & MASK] > TH[i]) break;
|
||||
}
|
||||
if (i == B1B_DIG && rp == 9 * B1B_DIG) break;
|
||||
if (rp > 9 + 9 * B1B_DIG) sh = 9;
|
||||
e2 += sh;
|
||||
for (k = a; k != z; k = (k+1) & MASK)
|
||||
{
|
||||
uint tmp = x[k] & (1 << sh - 1);
|
||||
x[k] = x[k] >> sh + carry;
|
||||
carry = (1000000000 >> sh) * tmp;
|
||||
if (k == a && !x[k])
|
||||
{
|
||||
a = (a + 1) & MASK;
|
||||
i--;
|
||||
rp -= 9;
|
||||
}
|
||||
}
|
||||
if (carry)
|
||||
{
|
||||
if ((z + 1) & MASK != a)
|
||||
{
|
||||
x[z] = carry;
|
||||
z = (z + 1) & MASK;
|
||||
}
|
||||
else
|
||||
{
|
||||
x[(z - 1) & MASK] |= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assemble desired bits into floating point variable
|
||||
double y;
|
||||
int i;
|
||||
for (i = 0; i < B1B_DIG; i++)
|
||||
{
|
||||
if ((a + i) & MASK == z) x[(z = (z + 1) & MASK) - 1] = 0;
|
||||
y = 1000000000.0 * y + x[(a + i) & MASK];
|
||||
}
|
||||
|
||||
y *= sign;
|
||||
|
||||
bool denormal;
|
||||
/* Limit precision for denormal results */
|
||||
uint bits = $bits;
|
||||
if (bits > math::DOUBLE_MANT_DIG + e2 - $emin)
|
||||
{
|
||||
bits = math::DOUBLE_MANT_DIG + e2 - $emin;
|
||||
if (bits < 0) bits = 0;
|
||||
denormal = true;
|
||||
}
|
||||
|
||||
// Calculate bias term to force rounding, move out lower bits
|
||||
double bias;
|
||||
double frac;
|
||||
if (bits < math::DOUBLE_MANT_DIG)
|
||||
{
|
||||
bias = math::copysign(math::scalbn(1, 2 * math::DOUBLE_MANT_DIG - bits - 1), y);
|
||||
frac = y % math::scalbn(1, math::DOUBLE_MANT_DIG - bits);
|
||||
y -= frac;
|
||||
y += bias;
|
||||
}
|
||||
|
||||
// Process tail of decimal input so it can affect rounding
|
||||
if ((a + i) & MASK != z)
|
||||
{
|
||||
uint t = x[(a + i) & MASK];
|
||||
switch
|
||||
{
|
||||
case t < 500000000 && (t || (a + i + 1) & MASK != z):
|
||||
frac += 0.25 * sign;
|
||||
case t > 500000000:
|
||||
frac += 0.75 * sign;
|
||||
case t == 500000000:
|
||||
if ((a + i + 1) & MASK == z)
|
||||
{
|
||||
frac += 0.5 * sign;
|
||||
}
|
||||
else
|
||||
{
|
||||
frac += 0.75 * sign;
|
||||
}
|
||||
}
|
||||
if (math::DOUBLE_MANT_DIG - bits >= 2 && !(frac % 1)) frac++;
|
||||
}
|
||||
|
||||
y += frac;
|
||||
y -= bias;
|
||||
|
||||
if (((e2 + math::DOUBLE_MANT_DIG) & int.max) > emax - 5)
|
||||
{
|
||||
if (math::abs(y) >= 0x1p53)
|
||||
{
|
||||
if (denormal && bits == math::DOUBLE_MANT_DIG + e2 - $emin) denormal = false;
|
||||
y *= 0.5;
|
||||
e2++;
|
||||
}
|
||||
if (e2 + math::DOUBLE_MANT_DIG > emax || (denormal && frac)) return NumberConversion.MALFORMED_FLOAT?;
|
||||
}
|
||||
return math::scalbn(y, e2);
|
||||
}
|
||||
|
||||
macro double! hexfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
{
|
||||
double scale = 1;
|
||||
uint x;
|
||||
long rp;
|
||||
long dc;
|
||||
char c @noinit;
|
||||
bool got_rad;
|
||||
bool got_digit;
|
||||
bool got_tail;
|
||||
usz len = chars.len;
|
||||
usz last_char = len - 1;
|
||||
usz index;
|
||||
double y;
|
||||
|
||||
// Skip past first characters
|
||||
while ((c = chars[index]) == '0')
|
||||
{
|
||||
if (index == last_char) return 0.0;
|
||||
index++;
|
||||
}
|
||||
if (c == '.')
|
||||
{
|
||||
got_rad = true;
|
||||
if (index == last_char)
|
||||
{
|
||||
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
|
||||
return sign * 0.0;
|
||||
}
|
||||
if (index != last_char && (c = chars[++index]) == '0')
|
||||
{
|
||||
rp--;
|
||||
got_digit = true;
|
||||
while (last_char != index && (c = chars[++index]) == '0')
|
||||
{
|
||||
rp--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while ((c - '0') < 10u || ((c | 32) - 'a') < 6u || c == '.')
|
||||
{
|
||||
if (c == '.')
|
||||
{
|
||||
if (got_rad) return NumberConversion.MALFORMED_FLOAT?;
|
||||
got_rad = true;
|
||||
rp = dc;
|
||||
}
|
||||
else
|
||||
{
|
||||
got_digit = true;
|
||||
int d = {|
|
||||
if (c > '9') return (c | 32) + 10 - 'a';
|
||||
return c - '0';
|
||||
|};
|
||||
switch
|
||||
{
|
||||
case dc < 8:
|
||||
x = x * 16 + d;
|
||||
case dc < math::DOUBLE_MANT_DIG / 4 + 1:
|
||||
y += d * (scale /= 16);
|
||||
got_tail = true;
|
||||
case d && !got_tail:
|
||||
y += 0.5 * scale;
|
||||
got_tail = true;
|
||||
}
|
||||
dc++;
|
||||
}
|
||||
if (index == last_char) break;
|
||||
c = chars[++index];
|
||||
}
|
||||
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
|
||||
if (!got_rad) rp = dc;
|
||||
for (; dc < 8; dc++) x *= 16;
|
||||
|
||||
long e2;
|
||||
if ((c | 32) == 'p')
|
||||
{
|
||||
long e2val = String.to_long((String)chars[index + 1..]) ?? (NumberConversion.MALFORMED_FLOAT?)!;
|
||||
e2 = e2val;
|
||||
}
|
||||
e2 += 4 * rp - 32;
|
||||
if (!x) return sign * 0.0;
|
||||
if (e2 > -$emin) return NumberConversion.FLOAT_OUT_OF_RANGE?;
|
||||
if (e2 < $emin - 2 * math::DOUBLE_MANT_DIG) return NumberConversion.FLOAT_OUT_OF_RANGE?;
|
||||
|
||||
while (x < 0x80000000)
|
||||
{
|
||||
if (y >= 0.5)
|
||||
{
|
||||
x += x + 1;
|
||||
y += y - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
x += x;
|
||||
y += y;
|
||||
}
|
||||
e2--;
|
||||
}
|
||||
int bits = $bits;
|
||||
if ($bits > 32 + e2 - $emin)
|
||||
{
|
||||
bits = (int)(32 + e2 - $emin);
|
||||
if (bits < 0) bits = 0;
|
||||
}
|
||||
double bias;
|
||||
if (bits < math::DOUBLE_MANT_DIG)
|
||||
{
|
||||
bias = math::copysign(math::scalbn(1, 32 + math::DOUBLE_MANT_DIG - bits - 1), (double)sign);
|
||||
}
|
||||
|
||||
if (bits < 32 && y && !(x & 1))
|
||||
{
|
||||
x++;
|
||||
y = 0;
|
||||
}
|
||||
y = bias + sign * (double)x + sign * y;
|
||||
y -= bias;
|
||||
if (!y) return NumberConversion.FLOAT_OUT_OF_RANGE?;
|
||||
|
||||
return math::scalbn(y, (int)e2);
|
||||
}
|
||||
|
||||
macro String.to_real(String chars, $Type) @private
|
||||
{
|
||||
int sign = 1;
|
||||
$switch ($Type)
|
||||
$case float:
|
||||
const int BITS = math::FLOAT_MANT_DIG;
|
||||
const int EMIN = math::FLOAT_MIN_EXP - BITS;
|
||||
$case double:
|
||||
const int BITS = math::DOUBLE_MANT_DIG;
|
||||
const int EMIN = math::DOUBLE_MIN_EXP - BITS;
|
||||
$case float128:
|
||||
$error "Not yet supported";
|
||||
$default:
|
||||
$error "Unexpected type";
|
||||
$endswitch
|
||||
|
||||
while (chars.len && chars[0] == ' ') chars = chars[1..];
|
||||
if (!chars.len) return NumberConversion.MALFORMED_FLOAT?;
|
||||
switch (chars[0])
|
||||
{
|
||||
case '-':
|
||||
sign = -1;
|
||||
nextcase;
|
||||
case '+':
|
||||
chars = chars[1..];
|
||||
}
|
||||
if (chars == "infinity" || chars == "INFINITY") return sign * $Type.inf;
|
||||
if (chars == "NAN" || chars == "nan") return $Type.nan;
|
||||
|
||||
if (chars.len > 2 && chars[0] == '0' && (chars[1] | 32) == 'x')
|
||||
{
|
||||
return ($Type)hexfloat((char[])chars[2..], BITS, EMIN, sign);
|
||||
}
|
||||
return ($Type)decfloat((char[])chars, BITS, EMIN, sign);
|
||||
}
|
||||
|
||||
275
lib/std/core/types.c3
Normal file
275
lib/std/core/types.c3
Normal file
@@ -0,0 +1,275 @@
|
||||
|
||||
module std::core::types;
|
||||
import libc;
|
||||
|
||||
|
||||
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"
|
||||
**/
|
||||
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;
|
||||
switch (any_type)
|
||||
{
|
||||
case ichar:
|
||||
ichar c = *(char*)v.ptr;
|
||||
if (is_mixed_signed && c < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
|
||||
return ($Type)c;
|
||||
case short:
|
||||
short s = *(short*)v.ptr;
|
||||
if (is_mixed_signed && s < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
|
||||
if (s > max || s < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
|
||||
return ($Type)s;
|
||||
case int:
|
||||
int i = *(int*)v.ptr;
|
||||
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
|
||||
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
|
||||
return ($Type)i;
|
||||
case long:
|
||||
long l = *(long*)v.ptr;
|
||||
if (is_mixed_signed && l < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
|
||||
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
|
||||
return ($Type)l;
|
||||
case int128:
|
||||
int128 i = *(int128*)v.ptr;
|
||||
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
|
||||
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
|
||||
return ($Type)i;
|
||||
case char:
|
||||
char c = *(char*)v.ptr;
|
||||
if (c > max) return ConversionResult.VALUE_OUT_OF_RANGE?;
|
||||
return ($Type)c;
|
||||
case ushort:
|
||||
ushort s = *(ushort*)v.ptr;
|
||||
if (s > max || s < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
|
||||
return ($Type)s;
|
||||
case uint:
|
||||
uint i = *(uint*)v.ptr;
|
||||
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
|
||||
return ($Type)i;
|
||||
case ulong:
|
||||
ulong l = *(ulong*)v.ptr;
|
||||
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
|
||||
return ($Type)l;
|
||||
case uint128:
|
||||
uint128 i = *(uint128*)v.ptr;
|
||||
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
|
||||
return ($Type)i;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
macro bool is_numerical($Type)
|
||||
{
|
||||
var $kind = $Type.kindof;
|
||||
$if $kind == TypeKind.DISTINCT:
|
||||
return is_numerical($Type.inner);
|
||||
$else
|
||||
return $kind == TypeKind.SIGNED_INT || $kind == TypeKind.UNSIGNED_INT || $kind == TypeKind.FLOAT
|
||||
|| $kind == TypeKind.VECTOR;
|
||||
$endif
|
||||
}
|
||||
|
||||
fn bool TypeKind.is_int(TypeKind kind) @inline
|
||||
{
|
||||
return kind == TypeKind.SIGNED_INT || kind == TypeKind.UNSIGNED_INT;
|
||||
}
|
||||
|
||||
macro bool is_indexable($Type)
|
||||
{
|
||||
return $checks($Type t, int i, t[i]);
|
||||
}
|
||||
|
||||
macro bool is_comparable($Type)
|
||||
{
|
||||
var $kind = $Type.kindof;
|
||||
$if $kind == TypeKind.DISTINCT:
|
||||
return is_comparable($Type.inner);
|
||||
$else
|
||||
return $kind == TypeKind.SIGNED_INT || $kind == TypeKind.UNSIGNED_INT || $kind == TypeKind.FLOAT
|
||||
|| $kind == TypeKind.VECTOR || $kind == TypeKind.BOOL || $kind == TypeKind.POINTER
|
||||
|| $kind == TypeKind.ENUM;
|
||||
$endif
|
||||
}
|
||||
|
||||
macro bool is_equatable($Type)
|
||||
{
|
||||
return $checks($Type a, a == a);
|
||||
}
|
||||
|
||||
macro bool is_subarray_convertable($Type)
|
||||
{
|
||||
$switch ($Type.kindof)
|
||||
$case SUBARRAY:
|
||||
return true;
|
||||
$case POINTER:
|
||||
return $Type.inner.kindof == TypeKind.ARRAY;
|
||||
$default:
|
||||
return false;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
macro bool is_bool($Type) => $Type.kindof == TypeKind.BOOL;
|
||||
macro bool is_int($Type) => $Type.kindof == TypeKind.SIGNED_INT || $Type.kindof == TypeKind.UNSIGNED_INT;
|
||||
|
||||
macro bool is_intlike($Type)
|
||||
{
|
||||
$switch ($Type.kindof)
|
||||
$case SIGNED_INT:
|
||||
$case UNSIGNED_INT:
|
||||
return true;
|
||||
$case VECTOR:
|
||||
return $Type.inner.kindof == TypeKind.SIGNED_INT || $Type.inner.kindof == TypeKind.UNSIGNED_INT;
|
||||
$default:
|
||||
return false;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
|
||||
macro bool is_float($Type) => $Type.kindof == TypeKind.FLOAT;
|
||||
|
||||
macro bool is_floatlike($Type)
|
||||
{
|
||||
$switch ($Type.kindof)
|
||||
$case FLOAT:
|
||||
return true;
|
||||
$case VECTOR:
|
||||
return $Type.inner.kindof == TypeKind.FLOAT;
|
||||
$default:
|
||||
return false;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
macro bool is_vector($Type)
|
||||
{
|
||||
return $Type.kindof == TypeKind.VECTOR;
|
||||
}
|
||||
|
||||
macro TypeKind inner_kind($Type)
|
||||
{
|
||||
$if $Type.kindof == TypeKind.DISTINCT:
|
||||
return inner_kind($typefrom($Type.inner));
|
||||
$else
|
||||
return $Type.kindof;
|
||||
$endif
|
||||
}
|
||||
|
||||
macro bool @convertable(#a, $TypeB) @builtin
|
||||
{
|
||||
return $checks($TypeB x = #a);
|
||||
}
|
||||
|
||||
macro bool is_same($TypeA, $TypeB)
|
||||
{
|
||||
return $TypeA.typeid == $TypeB.typeid;
|
||||
}
|
||||
|
||||
macro bool @has_same(#a, #b, ...)
|
||||
{
|
||||
var $type_a = $typeof(#a).typeid;
|
||||
$if $type_a != $typeof(#b).typeid:
|
||||
return false;
|
||||
$endif
|
||||
$for (var $i = 0; $i < $vacount; $i++)
|
||||
$if $typeof($vaexpr($i)).typeid != $type_a:
|
||||
return false;
|
||||
$endif
|
||||
$endfor
|
||||
return true;
|
||||
}
|
||||
|
||||
macro bool may_load_atomic($Type)
|
||||
{
|
||||
$switch ($Type.kindof)
|
||||
$case SIGNED_INT:
|
||||
$case UNSIGNED_INT:
|
||||
$case POINTER:
|
||||
$case FLOAT:
|
||||
return true;
|
||||
$case DISTINCT:
|
||||
return may_load_atomic($Type.inner);
|
||||
$default:
|
||||
return false;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
macro bool is_promotable_to_floatlike($Type) => types::is_floatlike($Type) || types::is_int($Type);
|
||||
macro bool is_promotable_to_float($Type) => types::is_float($Type) || types::is_int($Type);
|
||||
|
||||
macro bool is_same_vector_type($Type1, $Type2)
|
||||
{
|
||||
$if $Type1.kindof != TypeKind.VECTOR:
|
||||
return $Type2.kindof != TypeKind.VECTOR;
|
||||
$else
|
||||
return $Type1.inner == $Type2.inner && $Type1.len == $Type2.len;
|
||||
$endif
|
||||
}
|
||||
|
||||
macro bool is_equatable_type($Type)
|
||||
{
|
||||
$if $defined($Type.less) || $defined($Type.compare_to) || $defined($Type.equals):
|
||||
return true;
|
||||
$else
|
||||
return is_equatable($Type);
|
||||
$endif
|
||||
}
|
||||
|
||||
macro bool is_equatable_value(value)
|
||||
{
|
||||
return is_equatable_type($typeof(value));
|
||||
}
|
||||
|
||||
macro bool is_comparable_value(value)
|
||||
{
|
||||
$if $defined(value.less) || $defined(value.compare_to):
|
||||
return true;
|
||||
$else
|
||||
return is_comparable($typeof(value));
|
||||
$endif
|
||||
}
|
||||
|
||||
enum TypeKind : char
|
||||
{
|
||||
VOID,
|
||||
BOOL,
|
||||
SIGNED_INT,
|
||||
UNSIGNED_INT,
|
||||
FLOAT,
|
||||
TYPEID,
|
||||
ANYFAULT,
|
||||
ANY,
|
||||
ENUM,
|
||||
FAULT,
|
||||
STRUCT,
|
||||
UNION,
|
||||
BITSTRUCT,
|
||||
FUNC,
|
||||
OPTIONAL,
|
||||
ARRAY,
|
||||
SUBARRAY,
|
||||
VECTOR,
|
||||
DISTINCT,
|
||||
POINTER,
|
||||
}
|
||||
|
||||
struct TypeEnum
|
||||
{
|
||||
TypeKind type;
|
||||
usz elements;
|
||||
}
|
||||
|
||||
23
lib/std/core/values.c3
Normal file
23
lib/std/core/values.c3
Normal file
@@ -0,0 +1,23 @@
|
||||
module std::core::values;
|
||||
|
||||
macro TypeKind @typekind(#value) @builtin => $typeof(#value).kindof;
|
||||
macro bool @typeis(#value, $Type) @builtin => $typeof(#value).typeid == $Type.typeid;
|
||||
macro bool @is_bool(#value) => types::is_bool($typeof(#value));
|
||||
macro bool @is_int(#value) => types::is_int($typeof(#value));
|
||||
macro bool @convertable_to(#a, #b) => $checks($typeof(#b) x = #a);
|
||||
macro bool @is_floatlike(#value) => types::is_floatlike($typeof(#value));
|
||||
macro bool @is_float(#value) => types::is_float($typeof(#value));
|
||||
macro bool @is_promotable_to_floatlike(#value) => types::is_promotable_to_floatlike($typeof(#value));
|
||||
macro bool @is_promotable_to_float(#value) => types::is_promotable_to_float($typeof(#value));
|
||||
macro bool @is_same_vector_type(#value1, #value2) => types::is_same_vector_type($typeof(#value1), $typeof(#value2));
|
||||
macro promote_int(x)
|
||||
{
|
||||
$if @is_int(x):
|
||||
return (double)x;
|
||||
$else
|
||||
return x;
|
||||
$endif
|
||||
}
|
||||
|
||||
macro TypeKind @inner_kind(#value) => types::inner_kind($typeof(#value));
|
||||
|
||||
2
lib/std/crypto/crypto.c3
Normal file
2
lib/std/crypto/crypto.c3
Normal file
@@ -0,0 +1,2 @@
|
||||
module std::crypto;
|
||||
|
||||
65
lib/std/crypto/rc4.c3
Normal file
65
lib/std/crypto/rc4.c3
Normal file
@@ -0,0 +1,65 @@
|
||||
module std::crypto::rc4;
|
||||
// Copyright (c) 2021 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.
|
||||
|
||||
struct Rc4
|
||||
{
|
||||
uint i, j;
|
||||
char[256] state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the RC4 state.
|
||||
*
|
||||
* @param [inout] this "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(Rc4* this, char[] key)
|
||||
{
|
||||
// Init the state matrix
|
||||
foreach (char i, &c : this.state) *c = i;
|
||||
for (int i = 0, int j = 0; i < 256; i++)
|
||||
{
|
||||
j = (j + this.state[i] + key[i % key.len]) & 0xFF;
|
||||
@swap(this.state[i], this.state[j]);
|
||||
}
|
||||
this.i = 0;
|
||||
this.j = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt or decrypt a sequence of bytes.
|
||||
*
|
||||
* @param [inout] this "The RC4 State"
|
||||
* @param [in] in "The input"
|
||||
* @param [out] out "The output"
|
||||
* @require in.len <= out.len "Output would overflow"
|
||||
**/
|
||||
fn void Rc4.crypt(Rc4* this, char[] in, char[] out)
|
||||
{
|
||||
uint i = this.i;
|
||||
uint j = this.j;
|
||||
char* state = &this.state;
|
||||
isz len = in.len;
|
||||
foreach (idx, c : in)
|
||||
{
|
||||
i = (i + 1) & 0xFF;
|
||||
j = (j + state[i]) & 0xFF;
|
||||
@swap(state[i], state[j]);
|
||||
out[idx] = in[idx] ^ state[(state[i] + state[j]) & 0xFF];
|
||||
}
|
||||
this.i = i;
|
||||
this.j = j;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the rc4 state.
|
||||
*
|
||||
* @param [out] this "The RC4 State"
|
||||
**/
|
||||
fn void Rc4.destroy(Rc4* this)
|
||||
{
|
||||
*this = {};
|
||||
}
|
||||
353
lib/std/encoding/json.c3
Normal file
353
lib/std/encoding/json.c3
Normal file
@@ -0,0 +1,353 @@
|
||||
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::encoding::json;
|
||||
import std::io;
|
||||
import std::ascii;
|
||||
import std::collections::object;
|
||||
|
||||
enum JsonTokenType
|
||||
{
|
||||
NO_TOKEN,
|
||||
LBRACE,
|
||||
LBRACKET,
|
||||
COMMA,
|
||||
COLON,
|
||||
RBRACE,
|
||||
RBRACKET,
|
||||
STRING,
|
||||
NUMBER,
|
||||
TRUE,
|
||||
FALSE,
|
||||
NULL,
|
||||
EOF,
|
||||
}
|
||||
|
||||
struct JsonParser
|
||||
{
|
||||
uint line;
|
||||
Stream stream;
|
||||
Allocator* allocator;
|
||||
JsonTokenType token;
|
||||
DString last_string;
|
||||
double last_number;
|
||||
char current;
|
||||
anyfault current_err;
|
||||
bool skip_comments;
|
||||
bool reached_end;
|
||||
}
|
||||
|
||||
fault JsonParsingError
|
||||
{
|
||||
EOF,
|
||||
UNEXPECTED_CHARACTER,
|
||||
INVALID_ESCAPE_SEQUENCE,
|
||||
DUPLICATE_MEMBERS,
|
||||
INVALID_NUMBER,
|
||||
}
|
||||
|
||||
fn void JsonParser.init(JsonParser* parser, Stream s, Allocator* using = mem::heap())
|
||||
{
|
||||
*parser = { .last_string = dstring::new_with_capacity(64, using), .stream = s, .allocator = using };
|
||||
}
|
||||
|
||||
fn Object*! JsonParser.parse_from_token(JsonParser* this, JsonTokenType token)
|
||||
{
|
||||
switch (token)
|
||||
{
|
||||
case NO_TOKEN: unreachable();
|
||||
case LBRACE: return this.parse_map();
|
||||
case LBRACKET: return this.parse_array();
|
||||
case COMMA:
|
||||
case RBRACE:
|
||||
case RBRACKET:
|
||||
case COLON: return JsonParsingError.UNEXPECTED_CHARACTER?;
|
||||
case STRING: return object::new_string(this.last_string.str(), this.allocator);
|
||||
case NUMBER: return object::new_float(this.last_number, this.allocator);
|
||||
case TRUE: return object::new_bool(true);
|
||||
case FALSE: return object::new_bool(false);
|
||||
case NULL: return object::new_null();
|
||||
case EOF: return JsonParsingError.EOF?;
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
fn Object*! JsonParser.parse_any(JsonParser* this)
|
||||
{
|
||||
return this.parse_from_token(this.advance());
|
||||
}
|
||||
|
||||
fn JsonTokenType! JsonParser.lex_number(JsonParser* this, char c)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
DString t = dstring::tnew_with_capacity(32);
|
||||
bool negate = c == '-';
|
||||
if (negate)
|
||||
{
|
||||
t.append(c);
|
||||
c = this.read_next()!;
|
||||
}
|
||||
while (c >= '0' && c <= '9')
|
||||
{
|
||||
t.append(c);
|
||||
c = this.read_next()!;
|
||||
}
|
||||
if (c == '.')
|
||||
{
|
||||
t.append(c);
|
||||
while (c = this.read_next()!, c >= '0' && c <= '9')
|
||||
{
|
||||
t.append(c);
|
||||
}
|
||||
}
|
||||
if ((c | 32) == 'e')
|
||||
{
|
||||
t.append(c);
|
||||
c = this.read_next()!;
|
||||
switch (c)
|
||||
{
|
||||
case '-':
|
||||
case '+':
|
||||
t.append(c);
|
||||
c = this.read_next()!;
|
||||
}
|
||||
if (c < '0' || c > '9') return JsonParsingError.INVALID_NUMBER?;
|
||||
while (c >= '0' && c <= '9')
|
||||
{
|
||||
t.append(c);
|
||||
c = this.read_next()!;
|
||||
}
|
||||
}
|
||||
this.pushback();
|
||||
double! d = t.str().to_double() ?? JsonParsingError.INVALID_NUMBER?;
|
||||
this.last_number = d!;
|
||||
return NUMBER;
|
||||
};
|
||||
}
|
||||
|
||||
fn Object*! JsonParser.parse_map(JsonParser* this)
|
||||
{
|
||||
Object* map = object::new_obj(this.allocator);
|
||||
JsonTokenType token = this.advance()!;
|
||||
defer catch map.free();
|
||||
|
||||
DString temp_key = dstring::new_with_capacity(32, this.allocator);
|
||||
defer temp_key.free();
|
||||
while (token != JsonTokenType.RBRACE)
|
||||
{
|
||||
if (token != JsonTokenType.STRING) return JsonParsingError.UNEXPECTED_CHARACTER?;
|
||||
DString string = this.last_string;
|
||||
if (map.has_key(string.str())) 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);
|
||||
this.parse_expected(COLON)!;
|
||||
Object* element = this.parse_any()!;
|
||||
map.set(temp_key.str(), element);
|
||||
token = this.advance()!;
|
||||
if (token == JsonTokenType.COMMA)
|
||||
{
|
||||
token = this.advance()!;
|
||||
continue;
|
||||
}
|
||||
if (token != JsonTokenType.RBRACE) return JsonParsingError.UNEXPECTED_CHARACTER?;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
fn Object*! JsonParser.parse_array(JsonParser* this)
|
||||
{
|
||||
Object* list = object::new_obj(this.allocator);
|
||||
defer catch list.free();
|
||||
JsonTokenType token = this.advance()!;
|
||||
while (token != JsonTokenType.RBRACKET)
|
||||
{
|
||||
Object* element = this.parse_from_token(token)!;
|
||||
list.append(element);
|
||||
token = this.advance()!;
|
||||
if (token == JsonTokenType.COMMA)
|
||||
{
|
||||
token = this.advance()!;
|
||||
continue;
|
||||
}
|
||||
if (token != JsonTokenType.RBRACKET) return JsonParsingError.UNEXPECTED_CHARACTER?;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
fn void JsonParser.pushback(JsonParser* this)
|
||||
{
|
||||
if (!this.reached_end) this.stream.pushback_byte()!!;
|
||||
}
|
||||
|
||||
fn char! JsonParser.read_next(JsonParser* this)
|
||||
{
|
||||
if (this.reached_end) return '\0';
|
||||
char! c = this.stream.read_byte();
|
||||
if (catch err = c)
|
||||
{
|
||||
case IoError.EOF:
|
||||
this.reached_end = true;
|
||||
return '\0';
|
||||
default:
|
||||
return err?;
|
||||
}
|
||||
if (c == 0)
|
||||
{
|
||||
this.reached_end = true;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
fn JsonTokenType! JsonParser.advance(JsonParser* this)
|
||||
{
|
||||
char c;
|
||||
// Skip whitespace
|
||||
while WS: (c = this.read_next()!)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '\n':
|
||||
this.line++;
|
||||
nextcase;
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\v':
|
||||
continue;
|
||||
case '/':
|
||||
if (!this.skip_comments) break;
|
||||
c = this.read_next()!;
|
||||
if (c != '*')
|
||||
{
|
||||
this.pushback();
|
||||
break WS;
|
||||
}
|
||||
while COMMENT: (1)
|
||||
{
|
||||
// Skip to */
|
||||
while (c = this.read_next()!)
|
||||
{
|
||||
if (c == '\n') this.line++;
|
||||
if (c != '*') continue;
|
||||
// Skip through all the '*'
|
||||
while (c = this.read_next()!)
|
||||
{
|
||||
if (c == '\n') this.line++;
|
||||
if (c != '*') break;
|
||||
}
|
||||
if (c == '/') break COMMENT;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
default:
|
||||
break WS;
|
||||
}
|
||||
}
|
||||
switch (c)
|
||||
{
|
||||
case '\0':
|
||||
return IoError.EOF?;
|
||||
case '{':
|
||||
return LBRACE;
|
||||
case '}':
|
||||
return RBRACE;
|
||||
case '[':
|
||||
return LBRACKET;
|
||||
case ']':
|
||||
return RBRACKET;
|
||||
case ':':
|
||||
return COLON;
|
||||
case ',':
|
||||
return COMMA;
|
||||
case '"':
|
||||
return this.lex_string();
|
||||
case '-':
|
||||
case '0'..'9':
|
||||
return this.lex_number(c);
|
||||
case 't':
|
||||
this.match("rue")!;
|
||||
return TRUE;
|
||||
case 'f':
|
||||
this.match("alse")!;
|
||||
return FALSE;
|
||||
case 'n':
|
||||
this.match("ull")!;
|
||||
return NULL;
|
||||
default:
|
||||
return JsonParsingError.UNEXPECTED_CHARACTER?;
|
||||
}
|
||||
}
|
||||
|
||||
fn void! JsonParser.match(JsonParser* this, String str)
|
||||
{
|
||||
foreach (c : str)
|
||||
{
|
||||
char l = this.read_next()!;
|
||||
if (l != c) return JsonParsingError.UNEXPECTED_CHARACTER?;
|
||||
}
|
||||
}
|
||||
|
||||
fn void! JsonParser.parse_expected(JsonParser* this, JsonTokenType token) @local
|
||||
{
|
||||
if (this.advance()! != token) return JsonParsingError.UNEXPECTED_CHARACTER?;
|
||||
}
|
||||
|
||||
fn JsonTokenType! JsonParser.lex_string(JsonParser *this)
|
||||
{
|
||||
this.last_string.clear();
|
||||
while LOOP: (1)
|
||||
{
|
||||
char c = this.read_next()!;
|
||||
switch (c)
|
||||
{
|
||||
case '\0':
|
||||
return JsonParsingError.EOF?;
|
||||
case 1..31:
|
||||
return JsonParsingError.UNEXPECTED_CHARACTER?;
|
||||
case '"':
|
||||
break LOOP;
|
||||
case '\\':
|
||||
break;
|
||||
default:
|
||||
this.last_string.append(c);
|
||||
continue;
|
||||
}
|
||||
c = this.read_next()!;
|
||||
switch (c)
|
||||
{
|
||||
case '\0':
|
||||
return JsonParsingError.EOF?;
|
||||
case 1..31:
|
||||
return JsonParsingError.UNEXPECTED_CHARACTER?;
|
||||
case '"':
|
||||
case '\\':
|
||||
case '/':
|
||||
break;
|
||||
case 'b':
|
||||
c = '\b';
|
||||
case 'f':
|
||||
c = '\f';
|
||||
case 'n':
|
||||
c = '\n';
|
||||
case 'r':
|
||||
c = '\r';
|
||||
case 't':
|
||||
c = '\t';
|
||||
case 'u':
|
||||
uint val;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
c = this.read_next()!;
|
||||
if (!c.is_xdigit()) return JsonParsingError.INVALID_ESCAPE_SEQUENCE?;
|
||||
val = val << 4 + (c > '9' ? (c | 32) - 'a' + 10 : c - '0');
|
||||
}
|
||||
this.last_string.append_char32(val);
|
||||
continue;
|
||||
default:
|
||||
return JsonParsingError.INVALID_ESCAPE_SEQUENCE?;
|
||||
}
|
||||
}
|
||||
return STRING;
|
||||
}
|
||||
53
lib/std/hash/adler32.c3
Normal file
53
lib/std/hash/adler32.c3
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2021 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::hash::adler32;
|
||||
|
||||
const uint ADLER_CONST @private = 65521;
|
||||
|
||||
struct Adler32
|
||||
{
|
||||
uint a;
|
||||
uint b;
|
||||
}
|
||||
|
||||
fn void Adler32.init(Adler32 *this)
|
||||
{
|
||||
*this = { 1, 0 };
|
||||
}
|
||||
|
||||
fn void Adler32.updatec(Adler32* this, char c)
|
||||
{
|
||||
this.a = (this.a + c) % ADLER_CONST;
|
||||
this.b = (this.b + this.a) % ADLER_CONST;
|
||||
}
|
||||
|
||||
fn void Adler32.update(Adler32* this, char[] data)
|
||||
{
|
||||
uint a = this.a;
|
||||
uint b = this.b;
|
||||
foreach (char x : data)
|
||||
{
|
||||
a = (a + x) % ADLER_CONST;
|
||||
b = (b + a) % ADLER_CONST;
|
||||
}
|
||||
*this = { a, b };
|
||||
}
|
||||
|
||||
fn uint Adler32.final(Adler32* this)
|
||||
{
|
||||
return (this.b << 16) | this.a;
|
||||
}
|
||||
|
||||
fn uint encode(char[] data)
|
||||
{
|
||||
uint a = 1;
|
||||
uint b = 0;
|
||||
foreach (char x : data)
|
||||
{
|
||||
a = (a + x) % ADLER_CONST;
|
||||
b = (b + a) % ADLER_CONST;
|
||||
}
|
||||
return (b << 16) | a;
|
||||
}
|
||||
111
lib/std/hash/crc32.c3
Normal file
111
lib/std/hash/crc32.c3
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright (c) 2021 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::hash::crc32;
|
||||
|
||||
struct Crc32
|
||||
{
|
||||
uint result;
|
||||
}
|
||||
|
||||
fn void Crc32.init(Crc32* this, uint seed = 0)
|
||||
{
|
||||
this.result = ~seed;
|
||||
}
|
||||
|
||||
fn void Crc32.updatec(Crc32* this, char c)
|
||||
{
|
||||
this.result = (this.result >> 8) ^ CRC32_TABLE[(this.result ^ c) & 0xFF];
|
||||
}
|
||||
|
||||
fn void Crc32.update(Crc32* this, char[] data)
|
||||
{
|
||||
uint result = this.result;
|
||||
foreach (char x : data)
|
||||
{
|
||||
result = (result >> 8) ^ CRC32_TABLE[(result ^ x) & 0xFF];
|
||||
}
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
fn uint Crc32.final(Crc32* this)
|
||||
{
|
||||
return ~this.result;
|
||||
}
|
||||
|
||||
fn uint encode(char[] data)
|
||||
{
|
||||
uint result = ~(uint)(0);
|
||||
foreach (char x : data)
|
||||
{
|
||||
result = (result >> 8) ^ CRC32_TABLE[(result ^ x) & 0xFF];
|
||||
}
|
||||
return ~result;
|
||||
}
|
||||
|
||||
const uint[256] CRC32_TABLE @private = {
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
|
||||
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
|
||||
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
|
||||
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
|
||||
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
|
||||
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
|
||||
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
|
||||
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
|
||||
0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
|
||||
0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
|
||||
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
|
||||
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
|
||||
0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
|
||||
0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
|
||||
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
|
||||
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
|
||||
0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
|
||||
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
|
||||
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
|
||||
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
|
||||
0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
|
||||
0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
|
||||
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
|
||||
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
|
||||
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
|
||||
0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
|
||||
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
|
||||
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
|
||||
0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
|
||||
0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
|
||||
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
|
||||
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
|
||||
0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
|
||||
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
|
||||
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
|
||||
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
|
||||
0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
|
||||
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
|
||||
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
|
||||
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
|
||||
0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
|
||||
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
|
||||
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
|
||||
};
|
||||
111
lib/std/hash/crc64.c3
Normal file
111
lib/std/hash/crc64.c3
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright (c) 2021 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::hash::crc64;
|
||||
|
||||
struct Crc64
|
||||
{
|
||||
ulong result;
|
||||
}
|
||||
|
||||
fn void Crc64.init(Crc64* this, uint seed = 0)
|
||||
{
|
||||
this.result = seed;
|
||||
}
|
||||
|
||||
fn void Crc64.updatec(Crc64* this, char c)
|
||||
{
|
||||
this.result = (this.result << 8) ^ CRC64_TABLE[(char)((this.result >> 56) ^ c)];
|
||||
}
|
||||
|
||||
fn void Crc64.update(Crc64* this, char[] data)
|
||||
{
|
||||
ulong result = this.result;
|
||||
foreach (char x : data)
|
||||
{
|
||||
result = (result << 8) ^ CRC64_TABLE[(char)((result >> 56) ^ x)];
|
||||
}
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
fn ulong Crc64.final(Crc64* this)
|
||||
{
|
||||
return this.result;
|
||||
}
|
||||
|
||||
fn ulong encode(char[] data)
|
||||
{
|
||||
ulong result = (ulong)(0);
|
||||
foreach (char x : data)
|
||||
{
|
||||
result = (result << 8) ^ CRC64_TABLE[(char)((result >> 56) ^ x)];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const ulong[256] CRC64_TABLE @private = {
|
||||
0x0000000000000000, 0x42f0e1eba9ea3693, 0x85e1c3d753d46d26, 0xc711223cfa3e5bb5,
|
||||
0x493366450e42ecdf, 0x0bc387aea7a8da4c, 0xccd2a5925d9681f9, 0x8e224479f47cb76a,
|
||||
0x9266cc8a1c85d9be, 0xd0962d61b56fef2d, 0x17870f5d4f51b498, 0x5577eeb6e6bb820b,
|
||||
0xdb55aacf12c73561, 0x99a54b24bb2d03f2, 0x5eb4691841135847, 0x1c4488f3e8f96ed4,
|
||||
0x663d78ff90e185ef, 0x24cd9914390bb37c, 0xe3dcbb28c335e8c9, 0xa12c5ac36adfde5a,
|
||||
0x2f0e1eba9ea36930, 0x6dfeff5137495fa3, 0xaaefdd6dcd770416, 0xe81f3c86649d3285,
|
||||
0xf45bb4758c645c51, 0xb6ab559e258e6ac2, 0x71ba77a2dfb03177, 0x334a9649765a07e4,
|
||||
0xbd68d2308226b08e, 0xff9833db2bcc861d, 0x388911e7d1f2dda8, 0x7a79f00c7818eb3b,
|
||||
0xcc7af1ff21c30bde, 0x8e8a101488293d4d, 0x499b3228721766f8, 0x0b6bd3c3dbfd506b,
|
||||
0x854997ba2f81e701, 0xc7b97651866bd192, 0x00a8546d7c558a27, 0x4258b586d5bfbcb4,
|
||||
0x5e1c3d753d46d260, 0x1cecdc9e94ace4f3, 0xdbfdfea26e92bf46, 0x990d1f49c77889d5,
|
||||
0x172f5b3033043ebf, 0x55dfbadb9aee082c, 0x92ce98e760d05399, 0xd03e790cc93a650a,
|
||||
0xaa478900b1228e31, 0xe8b768eb18c8b8a2, 0x2fa64ad7e2f6e317, 0x6d56ab3c4b1cd584,
|
||||
0xe374ef45bf6062ee, 0xa1840eae168a547d, 0x66952c92ecb40fc8, 0x2465cd79455e395b,
|
||||
0x3821458aada7578f, 0x7ad1a461044d611c, 0xbdc0865dfe733aa9, 0xff3067b657990c3a,
|
||||
0x711223cfa3e5bb50, 0x33e2c2240a0f8dc3, 0xf4f3e018f031d676, 0xb60301f359dbe0e5,
|
||||
0xda050215ea6c212f, 0x98f5e3fe438617bc, 0x5fe4c1c2b9b84c09, 0x1d14202910527a9a,
|
||||
0x93366450e42ecdf0, 0xd1c685bb4dc4fb63, 0x16d7a787b7faa0d6, 0x5427466c1e109645,
|
||||
0x4863ce9ff6e9f891, 0x0a932f745f03ce02, 0xcd820d48a53d95b7, 0x8f72eca30cd7a324,
|
||||
0x0150a8daf8ab144e, 0x43a04931514122dd, 0x84b16b0dab7f7968, 0xc6418ae602954ffb,
|
||||
0xbc387aea7a8da4c0, 0xfec89b01d3679253, 0x39d9b93d2959c9e6, 0x7b2958d680b3ff75,
|
||||
0xf50b1caf74cf481f, 0xb7fbfd44dd257e8c, 0x70eadf78271b2539, 0x321a3e938ef113aa,
|
||||
0x2e5eb66066087d7e, 0x6cae578bcfe24bed, 0xabbf75b735dc1058, 0xe94f945c9c3626cb,
|
||||
0x676dd025684a91a1, 0x259d31cec1a0a732, 0xe28c13f23b9efc87, 0xa07cf2199274ca14,
|
||||
0x167ff3eacbaf2af1, 0x548f120162451c62, 0x939e303d987b47d7, 0xd16ed1d631917144,
|
||||
0x5f4c95afc5edc62e, 0x1dbc74446c07f0bd, 0xdaad56789639ab08, 0x985db7933fd39d9b,
|
||||
0x84193f60d72af34f, 0xc6e9de8b7ec0c5dc, 0x01f8fcb784fe9e69, 0x43081d5c2d14a8fa,
|
||||
0xcd2a5925d9681f90, 0x8fdab8ce70822903, 0x48cb9af28abc72b6, 0x0a3b7b1923564425,
|
||||
0x70428b155b4eaf1e, 0x32b26afef2a4998d, 0xf5a348c2089ac238, 0xb753a929a170f4ab,
|
||||
0x3971ed50550c43c1, 0x7b810cbbfce67552, 0xbc902e8706d82ee7, 0xfe60cf6caf321874,
|
||||
0xe224479f47cb76a0, 0xa0d4a674ee214033, 0x67c58448141f1b86, 0x253565a3bdf52d15,
|
||||
0xab1721da49899a7f, 0xe9e7c031e063acec, 0x2ef6e20d1a5df759, 0x6c0603e6b3b7c1ca,
|
||||
0xf6fae5c07d3274cd, 0xb40a042bd4d8425e, 0x731b26172ee619eb, 0x31ebc7fc870c2f78,
|
||||
0xbfc9838573709812, 0xfd39626eda9aae81, 0x3a28405220a4f534, 0x78d8a1b9894ec3a7,
|
||||
0x649c294a61b7ad73, 0x266cc8a1c85d9be0, 0xe17dea9d3263c055, 0xa38d0b769b89f6c6,
|
||||
0x2daf4f0f6ff541ac, 0x6f5faee4c61f773f, 0xa84e8cd83c212c8a, 0xeabe6d3395cb1a19,
|
||||
0x90c79d3fedd3f122, 0xd2377cd44439c7b1, 0x15265ee8be079c04, 0x57d6bf0317edaa97,
|
||||
0xd9f4fb7ae3911dfd, 0x9b041a914a7b2b6e, 0x5c1538adb04570db, 0x1ee5d94619af4648,
|
||||
0x02a151b5f156289c, 0x4051b05e58bc1e0f, 0x87409262a28245ba, 0xc5b073890b687329,
|
||||
0x4b9237f0ff14c443, 0x0962d61b56fef2d0, 0xce73f427acc0a965, 0x8c8315cc052a9ff6,
|
||||
0x3a80143f5cf17f13, 0x7870f5d4f51b4980, 0xbf61d7e80f251235, 0xfd913603a6cf24a6,
|
||||
0x73b3727a52b393cc, 0x31439391fb59a55f, 0xf652b1ad0167feea, 0xb4a25046a88dc879,
|
||||
0xa8e6d8b54074a6ad, 0xea16395ee99e903e, 0x2d071b6213a0cb8b, 0x6ff7fa89ba4afd18,
|
||||
0xe1d5bef04e364a72, 0xa3255f1be7dc7ce1, 0x64347d271de22754, 0x26c49cccb40811c7,
|
||||
0x5cbd6cc0cc10fafc, 0x1e4d8d2b65facc6f, 0xd95caf179fc497da, 0x9bac4efc362ea149,
|
||||
0x158e0a85c2521623, 0x577eeb6e6bb820b0, 0x906fc95291867b05, 0xd29f28b9386c4d96,
|
||||
0xcedba04ad0952342, 0x8c2b41a1797f15d1, 0x4b3a639d83414e64, 0x09ca82762aab78f7,
|
||||
0x87e8c60fded7cf9d, 0xc51827e4773df90e, 0x020905d88d03a2bb, 0x40f9e43324e99428,
|
||||
0x2cffe7d5975e55e2, 0x6e0f063e3eb46371, 0xa91e2402c48a38c4, 0xebeec5e96d600e57,
|
||||
0x65cc8190991cb93d, 0x273c607b30f68fae, 0xe02d4247cac8d41b, 0xa2dda3ac6322e288,
|
||||
0xbe992b5f8bdb8c5c, 0xfc69cab42231bacf, 0x3b78e888d80fe17a, 0x7988096371e5d7e9,
|
||||
0xf7aa4d1a85996083, 0xb55aacf12c735610, 0x724b8ecdd64d0da5, 0x30bb6f267fa73b36,
|
||||
0x4ac29f2a07bfd00d, 0x08327ec1ae55e69e, 0xcf235cfd546bbd2b, 0x8dd3bd16fd818bb8,
|
||||
0x03f1f96f09fd3cd2, 0x41011884a0170a41, 0x86103ab85a2951f4, 0xc4e0db53f3c36767,
|
||||
0xd8a453a01b3a09b3, 0x9a54b24bb2d03f20, 0x5d45907748ee6495, 0x1fb5719ce1045206,
|
||||
0x919735e51578e56c, 0xd367d40ebc92d3ff, 0x1476f63246ac884a, 0x568617d9ef46bed9,
|
||||
0xe085162ab69d5e3c, 0xa275f7c11f7768af, 0x6564d5fde549331a, 0x279434164ca30589,
|
||||
0xa9b6706fb8dfb2e3, 0xeb46918411358470, 0x2c57b3b8eb0bdfc5, 0x6ea7525342e1e956,
|
||||
0x72e3daa0aa188782, 0x30133b4b03f2b111, 0xf7021977f9cceaa4, 0xb5f2f89c5026dc37,
|
||||
0x3bd0bce5a45a6b5d, 0x79205d0e0db05dce, 0xbe317f32f78e067b, 0xfcc19ed95e6430e8,
|
||||
0x86b86ed5267cdbd3, 0xc4488f3e8f96ed40, 0x0359ad0275a8b6f5, 0x41a94ce9dc428066,
|
||||
0xcf8b0890283e370c, 0x8d7be97b81d4019f, 0x4a6acb477bea5a2a, 0x089a2aacd2006cb9,
|
||||
0x14dea25f3af9026d, 0x562e43b4931334fe, 0x913f6188692d6f4b, 0xd3cf8063c0c759d8,
|
||||
0x5dedc41a34bbeeb2, 0x1f1d25f19d51d821, 0xd80c07cd676f8394, 0x9afce626ce85b507,
|
||||
};
|
||||
41
lib/std/hash/fnv32a.c3
Normal file
41
lib/std/hash/fnv32a.c3
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2021 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::hash::fnv32a;
|
||||
|
||||
def Fnv32a = distinct uint;
|
||||
|
||||
const FNV32A_START @private = 0x811c9dc5;
|
||||
const FNV32A_MUL @private = 0x01000193;
|
||||
|
||||
macro void @update(uint &h, char x) @private => h = (h * FNV32A_MUL) ^ x;
|
||||
|
||||
fn void Fnv32a.init(Fnv32a* this)
|
||||
{
|
||||
*this = FNV32A_START;
|
||||
}
|
||||
|
||||
fn void Fnv32a.update(Fnv32a* this, char[] data)
|
||||
{
|
||||
uint h = (uint)*this;
|
||||
foreach (char x : data)
|
||||
{
|
||||
@update(h, x);
|
||||
}
|
||||
*this = (Fnv32a)h;
|
||||
}
|
||||
|
||||
macro void Fnv32a.update_char(Fnv32a* this, char c)
|
||||
{
|
||||
@update(*this, x);
|
||||
}
|
||||
|
||||
fn uint encode(char[] data)
|
||||
{
|
||||
uint h = FNV32A_START;
|
||||
foreach (char x : data)
|
||||
{
|
||||
@update(h, x);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
237
lib/std/hash/sha1.c3
Normal file
237
lib/std/hash/sha1.c3
Normal file
@@ -0,0 +1,237 @@
|
||||
// Copyright (c) 2021 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.
|
||||
//
|
||||
// Implementation was off Steve Reid's SHA-1 C implementation
|
||||
|
||||
module std::hash::sha1;
|
||||
import std::bits;
|
||||
|
||||
struct Sha1
|
||||
{
|
||||
uint[5] state;
|
||||
uint[2] count;
|
||||
char[64] buffer;
|
||||
}
|
||||
|
||||
fn void Sha1.init(Sha1* this)
|
||||
{
|
||||
// SHA1 initialization constants
|
||||
*this = {
|
||||
.state = {
|
||||
0x67452301,
|
||||
0xEFCDAB89,
|
||||
0x98BADCFE,
|
||||
0x10325476,
|
||||
0xC3D2E1F0
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] this
|
||||
* @param [in] data
|
||||
* @require data.len <= uint.max
|
||||
**/
|
||||
fn void Sha1.update(Sha1* this, char[] data)
|
||||
{
|
||||
uint j = this.count[0];
|
||||
uint len = data.len;
|
||||
if ((this.count[0] += len << 3) < j) this.count[1]++;
|
||||
this.count[1] += len >> 29;
|
||||
j = (j >> 3) & 63;
|
||||
uint i;
|
||||
if (j + len > 63)
|
||||
{
|
||||
i = 64 - j;
|
||||
this.buffer[j..] = data[:i];
|
||||
sha1_transform(&this.state, &this.buffer);
|
||||
for (; i + 63 < len; i += 64)
|
||||
{
|
||||
sha1_transform(&this.state, &data[i]);
|
||||
}
|
||||
j = 0;
|
||||
}
|
||||
this.buffer[j:len - i] = data[i..];
|
||||
}
|
||||
|
||||
|
||||
fn char[20] Sha1.final(Sha1* this)
|
||||
{
|
||||
char[8] finalcount;
|
||||
for (uint i = 0; i < 8; i++)
|
||||
{
|
||||
finalcount[i] = (char)((this.count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 0xFF);
|
||||
}
|
||||
this.update(char[] { 0o200 });
|
||||
while ((this.count[0] & 504) != 448)
|
||||
{
|
||||
this.update(char[] { 0 });
|
||||
}
|
||||
|
||||
this.update(&finalcount);
|
||||
char[20] digest;
|
||||
for (uint i = 0; i < 20; i++)
|
||||
{
|
||||
digest[i] = (char)((this.state[i >> 2] >> ((3 - (i & 3)) * 8)) & 0xFF);
|
||||
}
|
||||
|
||||
// Clear mem
|
||||
mem::clear(this, Sha1.sizeof);
|
||||
finalcount = {};
|
||||
return digest;
|
||||
}
|
||||
|
||||
union Long16 @local
|
||||
{
|
||||
char[64] c;
|
||||
uint[16] l;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
$if env::BIG_ENDIAN:
|
||||
return block.l[i];
|
||||
$else
|
||||
return block.l[i] = (block.l[i].rotl(24) & 0xFF00FF00)
|
||||
| (block.l[i].rotl(8) & 0x00FF00FF);
|
||||
$endif
|
||||
}
|
||||
|
||||
macro @r0(&block, v, &w, x, y, &z, i) @local
|
||||
{
|
||||
z += ((w & (x ^ y)) ^ y) + @blk0(block, i) + 0x5A827999 + v.rotl(5);
|
||||
w = w.rotl(30);
|
||||
}
|
||||
|
||||
macro @r1(&block, v, &w, x, y, &z, i) @local
|
||||
{
|
||||
z += ((w & (x ^ y)) ^ y) + @blk(block, i) + 0x5A827999 + v.rotl(5);
|
||||
w = w.rotl(30);
|
||||
}
|
||||
|
||||
macro @r2(&block, v, &w, x, y, &z, i) @local
|
||||
{
|
||||
z += (w ^ x ^ y) + @blk(block, i) + 0x6ED9EBA1 + v.rotl(5);
|
||||
w = w.rotl(30);
|
||||
}
|
||||
|
||||
macro @r3(&block, v, &w, x, y, &z, i) @local
|
||||
{
|
||||
z += (((w | x) &y) | (w & x)) + @blk(block, i) + 0x8F1BBCDC + v.rotl(5);
|
||||
w = w.rotl(30);
|
||||
}
|
||||
|
||||
macro @r4(&block, v, &w, x, y, &z, i) @local
|
||||
{
|
||||
z += (w ^ x ^ y) + @blk(block, i) + 0xCA62C1D6 + v.rotl(5);
|
||||
w = w.rotl(30);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] state
|
||||
* @param [&in] buffer
|
||||
**/
|
||||
fn void sha1_transform(uint* state, char* buffer) @local
|
||||
{
|
||||
Long16 block;
|
||||
block.c[..] = buffer[:64];
|
||||
uint a = state[0];
|
||||
uint b = state[1];
|
||||
uint c = state[2];
|
||||
uint d = state[3];
|
||||
uint e = state[4];
|
||||
@r0(block, a, b, c, d, e, 0);
|
||||
@r0(block, e, a, b, c, d, 1);
|
||||
@r0(block, d, e, a, b, c, 2);
|
||||
@r0(block, c, d, e, a, b, 3);
|
||||
@r0(block, b, c, d, e, a, 4);
|
||||
@r0(block, a, b, c, d, e, 5);
|
||||
@r0(block, e, a, b, c, d, 6);
|
||||
@r0(block, d, e, a, b, c, 7);
|
||||
@r0(block, c, d, e, a, b, 8);
|
||||
@r0(block, b, c, d, e, a, 9);
|
||||
@r0(block, a, b, c, d, e, 10);
|
||||
@r0(block, e, a, b, c, d, 11);
|
||||
@r0(block, d, e, a, b, c, 12);
|
||||
@r0(block, c, d, e, a, b, 13);
|
||||
@r0(block, b, c, d, e, a, 14);
|
||||
@r0(block, a, b, c, d, e, 15);
|
||||
@r1(block, e, a, b, c, d, 16);
|
||||
@r1(block, d, e, a, b, c, 17);
|
||||
@r1(block, c, d, e, a, b, 18);
|
||||
@r1(block, b, c, d, e, a, 19);
|
||||
@r2(block, a, b, c, d, e, 20);
|
||||
@r2(block, e, a, b, c, d, 21);
|
||||
@r2(block, d, e, a, b, c, 22);
|
||||
@r2(block, c, d, e, a, b, 23);
|
||||
@r2(block, b, c, d, e, a, 24);
|
||||
@r2(block, a, b, c, d, e, 25);
|
||||
@r2(block, e, a, b, c, d, 26);
|
||||
@r2(block, d, e, a, b, c, 27);
|
||||
@r2(block, c, d, e, a, b, 28);
|
||||
@r2(block, b, c, d, e, a, 29);
|
||||
@r2(block, a, b, c, d, e, 30);
|
||||
@r2(block, e, a, b, c, d, 31);
|
||||
@r2(block, d, e, a, b, c, 32);
|
||||
@r2(block, c, d, e, a, b, 33);
|
||||
@r2(block, b, c, d, e, a, 34);
|
||||
@r2(block, a, b, c, d, e, 35);
|
||||
@r2(block, e, a, b, c, d, 36);
|
||||
@r2(block, d, e, a, b, c, 37);
|
||||
@r2(block, c, d, e, a, b, 38);
|
||||
@r2(block, b, c, d, e, a, 39);
|
||||
@r3(block, a, b, c, d, e, 40);
|
||||
@r3(block, e, a, b, c, d, 41);
|
||||
@r3(block, d, e, a, b, c, 42);
|
||||
@r3(block, c, d, e, a, b, 43);
|
||||
@r3(block, b, c, d, e, a, 44);
|
||||
@r3(block, a, b, c, d, e, 45);
|
||||
@r3(block, e, a, b, c, d, 46);
|
||||
@r3(block, d, e, a, b, c, 47);
|
||||
@r3(block, c, d, e, a, b, 48);
|
||||
@r3(block, b, c, d, e, a, 49);
|
||||
@r3(block, a, b, c, d, e, 50);
|
||||
@r3(block, e, a, b, c, d, 51);
|
||||
@r3(block, d, e, a, b, c, 52);
|
||||
@r3(block, c, d, e, a, b, 53);
|
||||
@r3(block, b, c, d, e, a, 54);
|
||||
@r3(block, a, b, c, d, e, 55);
|
||||
@r3(block, e, a, b, c, d, 56);
|
||||
@r3(block, d, e, a, b, c, 57);
|
||||
@r3(block, c, d, e, a, b, 58);
|
||||
@r3(block, b, c, d, e, a, 59);
|
||||
@r4(block, a, b, c, d, e, 60);
|
||||
@r4(block, e, a, b, c, d, 61);
|
||||
@r4(block, d, e, a, b, c, 62);
|
||||
@r4(block, c, d, e, a, b, 63);
|
||||
@r4(block, b, c, d, e, a, 64);
|
||||
@r4(block, a, b, c, d, e, 65);
|
||||
@r4(block, e, a, b, c, d, 66);
|
||||
@r4(block, d, e, a, b, c, 67);
|
||||
@r4(block, c, d, e, a, b, 68);
|
||||
@r4(block, b, c, d, e, a, 69);
|
||||
@r4(block, a, b, c, d, e, 70);
|
||||
@r4(block, e, a, b, c, d, 71);
|
||||
@r4(block, d, e, a, b, c, 72);
|
||||
@r4(block, c, d, e, a, b, 73);
|
||||
@r4(block, b, c, d, e, a, 74);
|
||||
@r4(block, a, b, c, d, e, 75);
|
||||
@r4(block, e, a, b, c, d, 76);
|
||||
@r4(block, d, e, a, b, c, 77);
|
||||
@r4(block, c, d, e, a, b, 78);
|
||||
@r4(block, b, c, d, e, a, 79);
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
a = b = c = d = e = 0;
|
||||
buffer[:64] = 0;
|
||||
}
|
||||
3
lib/std/io/dir.c3
Normal file
3
lib/std/io/dir.c3
Normal file
@@ -0,0 +1,3 @@
|
||||
module std::io::dir;
|
||||
import std::io::os;
|
||||
|
||||
135
lib/std/io/io.c3
Normal file
135
lib/std/io/io.c3
Normal file
@@ -0,0 +1,135 @@
|
||||
// Copyright (c) 2021-2022 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::io;
|
||||
import libc;
|
||||
|
||||
struct File
|
||||
{
|
||||
CFile file;
|
||||
}
|
||||
|
||||
enum Seek
|
||||
{
|
||||
SET,
|
||||
CURSOR,
|
||||
END
|
||||
}
|
||||
|
||||
fault IoError
|
||||
{
|
||||
FILE_NOT_FOUND,
|
||||
FILE_NOT_VALID,
|
||||
INVALID_POSITION,
|
||||
OVERFLOW,
|
||||
FILE_IS_PIPE,
|
||||
FILE_EOF,
|
||||
INCOMPLETE_WRITE,
|
||||
BUSY,
|
||||
NO_PERMISSION,
|
||||
OUT_OF_SPACE,
|
||||
INVALID_PUSHBACK,
|
||||
EOF,
|
||||
CANNOT_READ_DIR,
|
||||
TOO_MANY_DESCRIPTORS,
|
||||
FILE_IS_DIR,
|
||||
READ_ONLY,
|
||||
FILE_NOT_DIR,
|
||||
SYMLINK_FAILED,
|
||||
ALREADY_EXISTS,
|
||||
NOT_SEEKABLE,
|
||||
NAME_TOO_LONG,
|
||||
WOULD_BLOCK,
|
||||
DIR_NOT_EMPTY,
|
||||
INTERRUPTED,
|
||||
GENERAL_ERROR,
|
||||
UNKNOWN_ERROR,
|
||||
UNSUPPORTED_OPERATION,
|
||||
}
|
||||
|
||||
fn void putchar(char c) @inline
|
||||
{
|
||||
libc::putchar(c);
|
||||
}
|
||||
|
||||
macro void print(x)
|
||||
{
|
||||
var $Type = $typeof(x);
|
||||
$switch ($Type)
|
||||
$case String:
|
||||
(void)stdout().print(x);
|
||||
$case ZString:
|
||||
(void)stdout().print(x.as_str());
|
||||
$case DString:
|
||||
(void)stdout().print(x.str());
|
||||
$default:
|
||||
$if @convertible(x, String):
|
||||
(void)stdout().print((String)x);
|
||||
$else
|
||||
(void)stdout().printf("%s", x);
|
||||
$endif
|
||||
$endswitch
|
||||
}
|
||||
|
||||
macro void printn(x = "")
|
||||
{
|
||||
var $Type = $typeof(x);
|
||||
$switch ($Type)
|
||||
$case String:
|
||||
(void)stdout().printn(x);
|
||||
$case ZString:
|
||||
(void)stdout().printn(x.as_str());
|
||||
$case DString:
|
||||
(void)stdout().printn(x.str());
|
||||
$default:
|
||||
$if @convertible(x, String):
|
||||
(void)stdout().printn((String)x);
|
||||
$else
|
||||
(void)stdout().printfn("%s", x);
|
||||
$endif
|
||||
$endswitch
|
||||
}
|
||||
|
||||
fn File stdout()
|
||||
{
|
||||
return { libc::stdout() };
|
||||
}
|
||||
|
||||
fn File stderr()
|
||||
{
|
||||
return { libc::stderr() };
|
||||
}
|
||||
|
||||
fn File stdin()
|
||||
{
|
||||
return { libc::stdin() };
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
|
||||
error FileError
|
||||
{
|
||||
ulong errno;
|
||||
}
|
||||
|
||||
fn FileError errorFromErrno()
|
||||
{
|
||||
return FileError { };
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
pubic fn void! File.clearerr(File *file) @inline
|
||||
{
|
||||
clearerr(file->file);
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn void File.error(File *file) @inline
|
||||
{
|
||||
int err = ferror
|
||||
}
|
||||
*/
|
||||
171
lib/std/io/io_file.c3
Normal file
171
lib/std/io/io_file.c3
Normal file
@@ -0,0 +1,171 @@
|
||||
module std::io::file;
|
||||
import libc;
|
||||
|
||||
fn File! open(String filename, String mode)
|
||||
{
|
||||
return { .file = os::native_fopen(filename, mode) };
|
||||
}
|
||||
|
||||
fn File! open_path(Path path, String mode)
|
||||
{
|
||||
return { .file = os::native_fopen(path.as_str(), mode) };
|
||||
}
|
||||
|
||||
/**
|
||||
* @require file.file != null
|
||||
**/
|
||||
fn void! File.reopen(File* file, String filename, String mode)
|
||||
{
|
||||
file.file = os::native_freopen(file.file, filename, mode)!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require file.file != null
|
||||
**/
|
||||
fn usz! File.seek(File file, isz offset, Seek seek_mode = Seek.SET)
|
||||
{
|
||||
os::native_fseek(file.file, offset, seek_mode)!;
|
||||
return os::native_ftell(file.file);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Implement later
|
||||
/**
|
||||
* @require file.file == null
|
||||
**/
|
||||
fn void! File.memopen(File* file, char[] data, String mode)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
file.file = libc::memopen(data.ptr, data.len, mode.zstr_tcopy(), file.file);
|
||||
// TODO errors
|
||||
};
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @require file && file.file != null
|
||||
*/
|
||||
fn void! File.putc(File *file, char c)
|
||||
{
|
||||
if (!libc::fputc(c, file.file)) return IoError.FILE_EOF?;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require file != null
|
||||
*/
|
||||
fn void! File.close(File *file) @inline
|
||||
{
|
||||
if (file.file && libc::fclose(file.file))
|
||||
{
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::ECONNRESET:
|
||||
case errno::EBADF: return IoError.FILE_NOT_VALID?;
|
||||
case errno::EINTR: return IoError.INTERRUPTED?;
|
||||
case errno::EDQUOT:
|
||||
case errno::EFAULT:
|
||||
case errno::EAGAIN:
|
||||
case errno::EFBIG:
|
||||
case errno::ENETDOWN:
|
||||
case errno::ENETUNREACH:
|
||||
case errno::ENOSPC:
|
||||
case errno::EIO: return IoError.INCOMPLETE_WRITE?;
|
||||
default: return IoError.UNKNOWN_ERROR?;
|
||||
}
|
||||
}
|
||||
file.file = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require file && file.file
|
||||
*/
|
||||
fn bool File.eof(File* file) @inline
|
||||
{
|
||||
return libc::feof(file.file) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [in] buffer
|
||||
*/
|
||||
fn usz! File.read(File* file, char[] buffer)
|
||||
{
|
||||
return os::native_fread(file.file, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] file
|
||||
* @param [&out] buffer
|
||||
* @require file.file `File must be initialized`
|
||||
*/
|
||||
fn usz! File.write(File file, char[] buffer)
|
||||
{
|
||||
return os::native_fwrite(file.file, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] file
|
||||
* @require file.file `File must be initialized`
|
||||
*/
|
||||
fn usz! File.printn(File file, String string = "")
|
||||
{
|
||||
usz len = file.print(string)!;
|
||||
if (!libc::putc('\n', file.file)) return IoError.UNKNOWN_ERROR?;
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] file
|
||||
* @require file.file `File must be initialized`
|
||||
*/
|
||||
fn usz! File.print(File file, String string)
|
||||
{
|
||||
usz len = string.len;
|
||||
if (len != file.write((char[])string)!) return IoError.UNKNOWN_ERROR?;
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] file
|
||||
* @require file.file `File must be initialized`
|
||||
*/
|
||||
fn DString File.getline(File* file, Allocator* using = mem::heap())
|
||||
{
|
||||
DString s = dstring::new_with_capacity(120, using);
|
||||
while (!file.eof())
|
||||
{
|
||||
int c = libc::fgetc(file.file);
|
||||
if (c == -1) break;
|
||||
if (c == '\n') break;
|
||||
s.append_char((char)c);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] file
|
||||
* @require file.file `File must be initialized`
|
||||
* @return "a zero terminated String (the pointer may be safely cast into a ZString)"
|
||||
*/
|
||||
fn String File.tgetline(File* file)
|
||||
{
|
||||
return file.getline(mem::temp()).zstr().as_str();
|
||||
}
|
||||
|
||||
fn char! File.getc(File* file)
|
||||
{
|
||||
int c = libc::fgetc(file.file);
|
||||
if (c == -1) return IoError.FILE_EOF?;
|
||||
return (char)c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] file
|
||||
* @require file.file `File must be initialized`
|
||||
*/
|
||||
fn void File.flush(File* file)
|
||||
{
|
||||
libc::fflush(file.file);
|
||||
}
|
||||
14
lib/std/io/io_fileinfo.c3
Normal file
14
lib/std/io/io_fileinfo.c3
Normal file
@@ -0,0 +1,14 @@
|
||||
module std::io::file;
|
||||
import libc;
|
||||
|
||||
|
||||
|
||||
fn bool is_file(String path)
|
||||
{
|
||||
return os::native_is_file(path);
|
||||
}
|
||||
|
||||
fn usz! get_size(String path)
|
||||
{
|
||||
return os::native_file_size(path);
|
||||
}
|
||||
667
lib/std/io/io_formatter_private.c3
Normal file
667
lib/std/io/io_formatter_private.c3
Normal file
@@ -0,0 +1,667 @@
|
||||
module std::io;
|
||||
|
||||
const char[16] XDIGITS_H = "0123456789ABCDEF";
|
||||
const char[16] XDIGITS_L = "0123456789abcdef";
|
||||
|
||||
fn void! Formatter.left_adjust(Formatter* this, usz len) @local
|
||||
{
|
||||
if (!this.flags.left) return;
|
||||
for (usz l = len; l < this.width; l++) this.out(' ')!;
|
||||
}
|
||||
|
||||
fn void! Formatter.right_adjust(Formatter* this, usz len) @local
|
||||
{
|
||||
if (this.flags.left) return;
|
||||
for (usz l = len; l < this.width; l++) this.out(' ')!;
|
||||
}
|
||||
|
||||
|
||||
fn uint128! int_from_any(any arg, bool *is_neg) @private
|
||||
{
|
||||
*is_neg = false;
|
||||
if (arg.type.kindof == TypeKind.POINTER)
|
||||
{
|
||||
return (uint128)(uptr)*(void**)arg.ptr;
|
||||
}
|
||||
if (arg.type.kindof == TypeKind.DISTINCT)
|
||||
{
|
||||
return int_from_any(any { arg.ptr, arg.type.inner }, is_neg);
|
||||
}
|
||||
switch (arg)
|
||||
{
|
||||
case bool:
|
||||
return (uint128)*arg;
|
||||
case ichar:
|
||||
int val = *arg;
|
||||
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
|
||||
case short:
|
||||
int val = *arg;
|
||||
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
|
||||
case int:
|
||||
int val = *arg;
|
||||
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
|
||||
case long:
|
||||
long val = *arg;
|
||||
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
|
||||
case int128:
|
||||
int128 val = *arg;
|
||||
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
|
||||
case char:
|
||||
return *arg;
|
||||
case ushort:
|
||||
return *arg;
|
||||
case uint:
|
||||
return *arg;
|
||||
case ulong:
|
||||
return *arg;
|
||||
case uint128:
|
||||
return *arg;
|
||||
case float:
|
||||
float f = *arg;
|
||||
return (uint128)((*is_neg = f < 0) ? -f : f);
|
||||
case double:
|
||||
double d = *arg;
|
||||
return (uint128)((*is_neg = d < 0) ? -d : d);
|
||||
default:
|
||||
return PrintFault.INVALID_ARGUMENT_TYPE?;
|
||||
}
|
||||
}
|
||||
|
||||
fn FloatType! float_from_any(any arg) @private
|
||||
{
|
||||
$if env::F128_SUPPORT:
|
||||
if (arg.type == float128.typeid) return (FloatType)*((float128*)arg.ptr);
|
||||
$endif
|
||||
$if env::F16_SUPPORT:
|
||||
if (arg.type == float16.typeid) return *((float16*)arg.ptr);
|
||||
$endif
|
||||
if (arg.type.kindof == TypeKind.DISTINCT)
|
||||
{
|
||||
return float_from_any(any { arg.ptr, arg.type.inner });
|
||||
}
|
||||
switch (arg)
|
||||
{
|
||||
case bool:
|
||||
return (FloatType)*arg;
|
||||
case ichar:
|
||||
return *arg;
|
||||
case short:
|
||||
return *arg;
|
||||
case int:
|
||||
return *arg;
|
||||
case long:
|
||||
return *arg;
|
||||
case int128:
|
||||
return *arg;
|
||||
case char:
|
||||
return *arg;
|
||||
case ushort:
|
||||
return *arg;
|
||||
case uint:
|
||||
return *arg;
|
||||
case ulong:
|
||||
return *arg;
|
||||
case uint128:
|
||||
return *arg;
|
||||
case float:
|
||||
return (FloatType)*arg;
|
||||
case double:
|
||||
return (FloatType)*arg;
|
||||
default:
|
||||
return PrintFault.INVALID_ARGUMENT_TYPE?;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
usz len = *len_ptr;
|
||||
while (len < maxlen)
|
||||
{
|
||||
char c = buf[len];
|
||||
if (c < '0' || c > '9') break;
|
||||
i = i * 10 + c - '0';
|
||||
len++;
|
||||
}
|
||||
*len_ptr = len;
|
||||
return i;
|
||||
}
|
||||
|
||||
fn void! Formatter.out_substr(Formatter *this, String str) @private
|
||||
{
|
||||
usz l = conv::utf8_codepoints(str);
|
||||
uint prec = this.prec;
|
||||
if (this.flags.precision && l < prec) l = prec;
|
||||
this.right_adjust(' ')!;
|
||||
usz index = 0;
|
||||
usz chars = str.len;
|
||||
char* ptr = str.ptr;
|
||||
while (index < chars)
|
||||
{
|
||||
char c = ptr[index];
|
||||
// Break if we have precision set and we ran out...
|
||||
if (c & 0xC0 != 0x80 && this.flags.precision && !prec--) break;
|
||||
this.out(c)!;
|
||||
index++;
|
||||
}
|
||||
return this.left_adjust(l);
|
||||
}
|
||||
|
||||
fn void! Formatter.pad(Formatter* this, char c, isz width, isz len) @inline
|
||||
{
|
||||
for (isz i = len; i < width; i++) this.out(c)!;
|
||||
}
|
||||
|
||||
fn char* fmt_u(uint128 x, char* s)
|
||||
{
|
||||
for (; x > ulong.max; x /= 10) *--s = '0' + (char)(x % 10);
|
||||
for (ulong y = (ulong)x; y; y /= 10) *--s = '0' + (char)(y % 10);
|
||||
return s;
|
||||
}
|
||||
|
||||
fn void! Formatter.out_chars(Formatter* this, char[] s)
|
||||
{
|
||||
foreach (c : s) this.out(c)!;
|
||||
}
|
||||
|
||||
enum FloatFormatting
|
||||
{
|
||||
FLOAT,
|
||||
EXPONENTIAL,
|
||||
ADAPTIVE,
|
||||
HEX
|
||||
}
|
||||
|
||||
fn void! Formatter.etoa(Formatter* this, double y) => this.floatformat(EXPONENTIAL, y);
|
||||
fn void! Formatter.ftoa(Formatter* this, double y) => this.floatformat(FLOAT, y);
|
||||
fn void! Formatter.gtoa(Formatter* this, double y) => this.floatformat(ADAPTIVE, y);
|
||||
fn void! Formatter.atoa(Formatter* this, double y) => this.floatformat(HEX, y);
|
||||
|
||||
fn void! Formatter.floatformat(Formatter* this, FloatFormatting formatting, double y) @private
|
||||
{
|
||||
// This code is heavily based on musl's printf code
|
||||
const BUF_SIZE = (math::DOUBLE_MANT_DIG + 28) / 29 + 1
|
||||
+ (math::DOUBLE_MAX_EXP + math::DOUBLE_MANT_DIG + 28 + 8) / 9;
|
||||
uint[BUF_SIZE] big;
|
||||
bool is_neg = false;
|
||||
if (math::signbit(y))
|
||||
{
|
||||
is_neg = true;
|
||||
y = -y;
|
||||
}
|
||||
int pl = is_neg || this.flags.plus ? 1 : 0;
|
||||
// Print inf/nan
|
||||
if (!math::is_finite(y))
|
||||
{
|
||||
// Add padding
|
||||
if (!this.flags.left) this.pad(' ', this.width, 3 + pl)!;
|
||||
String s = this.flags.uppercase ? "INF" : "inf";
|
||||
if (y != y) this.flags.uppercase ? "NAN" : "nan";
|
||||
if (pl) this.out(is_neg ? '-' : '+')!;
|
||||
this.out_chars(s)!;
|
||||
if (this.flags.left) this.pad(' ', this.width, 3 + pl)!;
|
||||
return;
|
||||
}
|
||||
// Rescale
|
||||
int e2;
|
||||
|
||||
y = math::frexp(y, &e2) * 2;
|
||||
if (y) e2--;
|
||||
char[12] ebuf0;
|
||||
char* ebuf = 12 + (char*)&ebuf0;
|
||||
char[9 + math::DOUBLE_MANT_DIG / 4] buf_array;
|
||||
char* buf = &buf_array;
|
||||
isz p = this.flags.precision ? this.prec : -1;
|
||||
if (formatting == HEX)
|
||||
{
|
||||
double round = 8.0;
|
||||
// 0x / 0X
|
||||
pl += 2;
|
||||
if (p > 0 && p < math::DOUBLE_MANT_DIG / 4 - 1)
|
||||
{
|
||||
int re = math::DOUBLE_MANT_DIG / 4 - 1 - (int)p;
|
||||
round *= 1 << (math::DOUBLE_MANT_DIG % 4);
|
||||
while (re--) round *= 16;
|
||||
if (is_neg)
|
||||
{
|
||||
y = -y;
|
||||
y -= round;
|
||||
y += round;
|
||||
y = -y;
|
||||
}
|
||||
else
|
||||
{
|
||||
y += round;
|
||||
y -= round;
|
||||
}
|
||||
}
|
||||
// Reverse print
|
||||
char* estr = fmt_u(e2 < 0 ? (int128)-e2 : (int128)e2, ebuf);
|
||||
if (estr == ebuf) *--estr = '0';
|
||||
*--estr = (e2 < 0 ? '-' : '+');
|
||||
*--estr = this.flags.uppercase ? 'P' : 'p';
|
||||
char* s = buf;
|
||||
char* xdigits = this.flags.uppercase ? &XDIGITS_H : &XDIGITS_L;
|
||||
do
|
||||
{
|
||||
int x = (int)y;
|
||||
*s++ = xdigits[x];
|
||||
y = 16 * (y - x);
|
||||
if (s - buf == 1 && (y || p > 0 || this.flags.hash)) *s++ = '.';
|
||||
} while (y);
|
||||
isz outlen = s - buf;
|
||||
isz explen = ebuf - estr;
|
||||
if (p > int.max - 2 - explen - pl) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
usz l = p && outlen - 2 < p
|
||||
? p + 2 + explen
|
||||
: outlen + explen;
|
||||
if (!this.flags.left && !this.flags.zeropad) this.pad(' ', this.width, pl + l)!;
|
||||
if (is_neg || this.flags.plus) this.out(is_neg ? '-' : '+')!;
|
||||
this.out_chars(this.flags.uppercase ? "0X" : "0x")!;
|
||||
if (this.flags.zeropad) this.pad('0', this.width, pl + l)!;
|
||||
this.out_chars(buf[:outlen])!;
|
||||
this.pad('0', l - outlen - explen, 0)!;
|
||||
this.out_chars(estr[:explen])!;
|
||||
if (this.flags.left) this.pad(' ', this.width, pl + l)!;
|
||||
return;
|
||||
}
|
||||
if (p < 0) p = 6;
|
||||
if (y)
|
||||
{
|
||||
y *= 0x1p28;
|
||||
e2 -= 28;
|
||||
}
|
||||
|
||||
uint* a, z, r;
|
||||
if (e2 < 0)
|
||||
{
|
||||
a = r = z = &big;
|
||||
}
|
||||
else
|
||||
{
|
||||
a = r = z = (uint*)&big + big.len - math::DOUBLE_MANT_DIG - 1;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
uint v = z++[0] = (uint)y;
|
||||
y = 1000000000 * (y - v);
|
||||
} while (y);
|
||||
|
||||
while (e2 > 0)
|
||||
{
|
||||
uint carry = 0;
|
||||
int sh = math::min(29, e2);
|
||||
for (uint* d = z - 1; d >= a; d--)
|
||||
{
|
||||
ulong x = (ulong)*d << sh + carry;
|
||||
*d = (uint)(x % 1000000000);
|
||||
carry = (uint)(x / 1000000000);
|
||||
}
|
||||
if (carry) *--a = carry;
|
||||
while (z > a && !z[-1]) z--;
|
||||
e2 -= sh;
|
||||
}
|
||||
|
||||
while (e2 < 0)
|
||||
{
|
||||
uint carry = 0;
|
||||
uint* b;
|
||||
int sh = math::min(9, -e2);
|
||||
int need = (int)(1 + (p + math::DOUBLE_MANT_DIG / 3u + 8) / 9);
|
||||
for (uint* d = a; d < z; d++)
|
||||
{
|
||||
// CHECK THIS
|
||||
uint rm = *d & ((1 << sh) - 1);
|
||||
*d = (*d >> sh) + carry;
|
||||
carry = (1000000000 >> sh) * rm;
|
||||
}
|
||||
if (!a[0]) a++;
|
||||
if (carry) z++[0] = carry;
|
||||
// Avoid (slow!) computation past requested precision
|
||||
b = formatting == FLOAT ? r : a;
|
||||
if (z - b > need) z = b + need;
|
||||
e2 += sh;
|
||||
}
|
||||
|
||||
int e;
|
||||
if (a < z)
|
||||
{
|
||||
for (int i = 10, e = (int)(9 * (r - a)); *a >= i; i *= 10, e++);
|
||||
}
|
||||
|
||||
// Perform rounding: j is precision after the radix (possibly neg)
|
||||
int j = (int)(p - (isz)(formatting == FLOAT ? 0 : e - (int)(formatting == ADAPTIVE && p)));
|
||||
if (j < 9 * (z - r - 1))
|
||||
{
|
||||
uint x;
|
||||
// We avoid C's broken division of negative numbers
|
||||
uint* d = r + 1 + ((j + 9 * math::DOUBLE_MAX_EXP) / 9 - math::DOUBLE_MAX_EXP);
|
||||
j += 9 * math::DOUBLE_MAX_EXP;
|
||||
j %= 9;
|
||||
int i;
|
||||
for (i = 10, j++; j < 9; i *= 10, j++);
|
||||
x = *d % i;
|
||||
// Are there any significant digits past j?
|
||||
if (x || (d + 1) != z)
|
||||
{
|
||||
double round = 2 / math::DOUBLE_EPSILON;
|
||||
double small;
|
||||
if (((*d / i) & 1) || (i == 1000000000 && d > a && (d[-1] & 1)))
|
||||
{
|
||||
round += 2;
|
||||
}
|
||||
switch
|
||||
{
|
||||
case x < i / 2:
|
||||
small = 0x0.8p0;
|
||||
case x == i / 2 && d + 1 == z:
|
||||
small = 0x1.0p0;
|
||||
default:
|
||||
small = 0x1.8p0;
|
||||
}
|
||||
if (pl && is_neg)
|
||||
{
|
||||
round *= -1;
|
||||
small *= -1;
|
||||
}
|
||||
*d -= x;
|
||||
// Decide whether to round by probing round+small
|
||||
if (round + small != round)
|
||||
{
|
||||
*d = *d + i;
|
||||
while (*d > 999999999)
|
||||
{
|
||||
*d-- = 0;
|
||||
if (d < a) *--a = 0;
|
||||
(*d)++;
|
||||
}
|
||||
for (i = 10, e = (int)(9 * (r - a)); *a >= i; i *= 10, e++);
|
||||
}
|
||||
}
|
||||
if (z > d + 1) z = d + 1;
|
||||
}
|
||||
for (; z>a && !z[-1]; z--);
|
||||
|
||||
if (formatting == ADAPTIVE)
|
||||
{
|
||||
if (!p) p++;
|
||||
if (p > e && e >= -4)
|
||||
{
|
||||
formatting = FLOAT;
|
||||
p -= (isz)e + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
formatting = EXPONENTIAL;
|
||||
p--;
|
||||
}
|
||||
if (!this.flags.hash)
|
||||
{
|
||||
// Count trailing zeros in last place
|
||||
if (z > a && z[-1])
|
||||
{
|
||||
for (int i = 10, j = 0; z[-1] % i == 0; i *= 10, j++);
|
||||
}
|
||||
else
|
||||
{
|
||||
j = 9;
|
||||
}
|
||||
if (formatting == FLOAT)
|
||||
{
|
||||
p = math::min(p, math::max((isz)0, 9 * (z - r - 1) - j));
|
||||
}
|
||||
else
|
||||
{
|
||||
p = math::min(p, math::max((isz)0, 9 * (z - r - 1) + e - j));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if (p > int.max - 1 - (isz)(p || this.flags.hash)) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
int l = (int)(1 + p + (isz)(p || this.flags.hash));
|
||||
char* estr @noinit;
|
||||
if (formatting == FLOAT)
|
||||
{
|
||||
if (e > int.max - l) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
if (e > 0) l += e;
|
||||
}
|
||||
else
|
||||
{
|
||||
estr = fmt_u((uint128)(e < 0 ? -e : e), ebuf);
|
||||
while (ebuf - estr < 2) (--estr)[0] = '0';
|
||||
*--estr = (e < 0 ? '-' : '+');
|
||||
*--estr = this.flags.uppercase ? 'E' : 'e';
|
||||
if (ebuf - estr > (isz)int.max - l) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
l += (int)(ebuf - estr);
|
||||
}
|
||||
if (l > int.max - pl) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
if (!this.flags.left && !this.flags.zeropad) this.pad(' ', this.width, pl + l)!;
|
||||
if (is_neg || this.flags.plus) this.out(is_neg ? '-' : '+')!;
|
||||
if (this.flags.zeropad) this.pad('0', this.width, pl + l)!;
|
||||
if (formatting == FLOAT)
|
||||
{
|
||||
if (a > r) a = r;
|
||||
uint* d = a;
|
||||
for (; d <= r; d++)
|
||||
{
|
||||
char* s = fmt_u(*d, buf + 9);
|
||||
switch
|
||||
{
|
||||
case d != a:
|
||||
while (s > buf) (--s)[0] = '0';
|
||||
case s == buf + 9:
|
||||
*--s = '0';
|
||||
}
|
||||
this.out_chars(s[:buf + 9 - s])!;
|
||||
}
|
||||
if (p || this.flags.hash) this.out('.')!;
|
||||
for (; d < z && p > 0; d++, p -= 9)
|
||||
{
|
||||
char* s = fmt_u(*d, buf + 9);
|
||||
while (s > buf) *--s = '0';
|
||||
this.out_chars(s[:math::min((isz)9, p)])!;
|
||||
}
|
||||
this.pad('0', p + 9, 9)!;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (z <= a) z = a + 1;
|
||||
for (uint* d = a; d < z && p >= 0; d++)
|
||||
{
|
||||
char* s = fmt_u(*d, buf + 9);
|
||||
if (s == buf + 9) (--s)[0] = '0';
|
||||
if (d != a)
|
||||
{
|
||||
while (s > buf) (--s)[0] = '0';
|
||||
}
|
||||
else
|
||||
{
|
||||
this.out(s++[0])!;
|
||||
if (p > 0 || this.flags.hash) this.out('.')!;
|
||||
}
|
||||
this.out_chars(s[:math::min(buf + 9 - s, p)])!;
|
||||
p -= buf + 9 - s;
|
||||
}
|
||||
this.pad('0', p + 18, 18)!;
|
||||
this.out_chars(estr[:ebuf - estr])!;
|
||||
}
|
||||
|
||||
if (this.flags.left) this.pad(' ', this.width, pl + l)!;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
fn void! Formatter.ntoa(Formatter* this, uint128 value, bool negative, uint base) @private
|
||||
{
|
||||
char[PRINTF_NTOA_BUFFER_SIZE] buf @noinit;
|
||||
usz len = 0;
|
||||
|
||||
// no hash for 0 values
|
||||
if (!value) this.flags.hash = false;
|
||||
|
||||
// write if precision != 0 or value is != 0
|
||||
if (!this.flags.precision || value)
|
||||
{
|
||||
char past_10 = (this.flags.uppercase ? 'A' : 'a') - 10;
|
||||
do
|
||||
{
|
||||
if (len >= PRINTF_NTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
char digit = (char)(value % base);
|
||||
buf[len++] = digit + (digit < 10 ? '0' : past_10);
|
||||
value /= base;
|
||||
}
|
||||
while (value);
|
||||
}
|
||||
return this.ntoa_format((String)buf[:PRINTF_NTOA_BUFFER_SIZE], len, negative, base);
|
||||
}
|
||||
|
||||
fn void! Formatter.ntoa_format(Formatter* this, String buf, usz len, bool negative, uint base) @private
|
||||
{
|
||||
// pad leading zeros
|
||||
if (!this.flags.left)
|
||||
{
|
||||
if (this.width && this.flags.zeropad && (negative || this.flags.plus || this.flags.space)) this.width--;
|
||||
while (len < this.prec)
|
||||
{
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
buf[len++] = '0';
|
||||
}
|
||||
while (this.flags.zeropad && len < this.width)
|
||||
{
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
buf[len++] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
// handle hash
|
||||
if (this.flags.hash && base != 10)
|
||||
{
|
||||
if (!this.flags.precision && len && len == this.prec && len == this.width)
|
||||
{
|
||||
len--;
|
||||
if (len) len--;
|
||||
}
|
||||
if (base != 10)
|
||||
{
|
||||
if (len + 1 >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
switch (base)
|
||||
{
|
||||
case 16:
|
||||
buf[len++] = this.flags.uppercase ? 'X' : 'x';
|
||||
case 8:
|
||||
buf[len++] = this.flags.uppercase ? 'O' : 'o';
|
||||
case 2:
|
||||
buf[len++] = this.flags.uppercase ? 'B' : 'b';
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
buf[len++] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
switch (true)
|
||||
{
|
||||
case negative:
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
buf[len++] = '-';
|
||||
case this.flags.plus:
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
buf[len++] = '+';
|
||||
case this.flags.space:
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
|
||||
buf[len++] = ' ';
|
||||
}
|
||||
if (!len) return;
|
||||
return this.out_reverse(buf[:len]);
|
||||
}
|
||||
|
||||
|
||||
fn void! Formatter.ntoa_any(Formatter* this, any arg, uint base) @private
|
||||
{
|
||||
bool is_neg;
|
||||
uint128 val = int_from_any(arg, &is_neg)!!;
|
||||
return this.ntoa(val, is_neg, base) @inline;
|
||||
}
|
||||
|
||||
fn void! Formatter.out_char(Formatter* this, any arg) @private
|
||||
{
|
||||
uint l = 1;
|
||||
// pre padding
|
||||
this.right_adjust(l)!;
|
||||
// char output
|
||||
Char32 c = types::any_to_int(arg, uint) ?? 0xFFFD;
|
||||
switch (true)
|
||||
{
|
||||
case c < 0x7f:
|
||||
this.out((char)c)!;
|
||||
case c < 0x7ff:
|
||||
this.out((char)(0xC0 | c >> 6))!;
|
||||
this.out((char)(0x80 | (c & 0x3F)))!;
|
||||
case c < 0xffff:
|
||||
this.out((char)(0xE0 | c >> 12))!;
|
||||
this.out((char)(0x80 | (c >> 6 & 0x3F)))!;
|
||||
this.out((char)(0x80 | (c & 0x3F)))!;
|
||||
default:
|
||||
this.out((char)(0xF0 | c >> 18))!;
|
||||
this.out((char)(0x80 | (c >> 12 & 0x3F)))!;
|
||||
this.out((char)(0x80 | (c >> 6 & 0x3F)))!;
|
||||
this.out((char)(0x80 | (c & 0x3F)))!;
|
||||
}
|
||||
return this.left_adjust(l);
|
||||
}
|
||||
|
||||
|
||||
fn void! Formatter.out_reverse(Formatter* this, char[] buf) @private
|
||||
{
|
||||
usz buffer_start_idx = this.idx;
|
||||
usz len = buf.len;
|
||||
// pad spaces up to given width
|
||||
if (!this.flags.left && !this.flags.zeropad)
|
||||
{
|
||||
for (usz i = len; i < this.width; i++)
|
||||
{
|
||||
this.out(' ')!;
|
||||
}
|
||||
}
|
||||
// reverse string
|
||||
while (len) this.out(buf[--len])!;
|
||||
|
||||
// append pad spaces up to given width
|
||||
return this.left_adjust(this.idx - buffer_start_idx);
|
||||
}
|
||||
|
||||
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,
|
||||
char* format_ptr, usz format_len, usz* index_ptr) @inline @private
|
||||
{
|
||||
char c = format_ptr[*index_ptr];
|
||||
if (c >= '0' && c <= '9') 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?;
|
||||
uint! intval = types::any_to_int(val, int);
|
||||
return intval ?? FormattingFault.INVALID_WIDTH_ARG?;
|
||||
}
|
||||
427
lib/std/io/io_printf.c3
Normal file
427
lib/std/io/io_printf.c3
Normal file
@@ -0,0 +1,427 @@
|
||||
module std::io;
|
||||
import std::collections::map;
|
||||
import libc;
|
||||
|
||||
const int PRINTF_NTOA_BUFFER_SIZE = 256;
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
def OutputFn = fn void!(char c, void* buffer);
|
||||
def FloatType = double;
|
||||
|
||||
fn String any.to_string(void* value, Allocator *using) @interface;
|
||||
fn void! any.to_format(void* value, Formatter* formatter) @interface;
|
||||
|
||||
fn usz! printf(String format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_putchar_fn);
|
||||
return formatter.vprintf(format, args);
|
||||
}
|
||||
|
||||
fn usz! printfn(String format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_putchar_fn);
|
||||
usz len = formatter.vprintf(format, args)!;
|
||||
putchar('\n');
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
fn char[]! bprintf(char[] buffer, String format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
BufferData data = { .buffer = buffer };
|
||||
formatter.init(&out_buffer_fn, &data);
|
||||
usz size = formatter.vprintf(format, args)!;
|
||||
return buffer[:data.written];
|
||||
}
|
||||
|
||||
fn usz! File.printf(File file, String format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_fputchar_fn, &file);
|
||||
return formatter.vprintf(format, args)!;
|
||||
}
|
||||
|
||||
fn usz! File.printfn(File file, String format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_fputchar_fn, &file);
|
||||
usz len = formatter.vprintf(format, args)!;
|
||||
file.putc('\n')!;
|
||||
file.flush();
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
fn usz! Formatter.printf(Formatter* this, String format, args...)
|
||||
{
|
||||
return this.vprintf(format, args) @inline;
|
||||
}
|
||||
|
||||
struct Formatter
|
||||
{
|
||||
void *data;
|
||||
OutputFn out_fn;
|
||||
struct
|
||||
{
|
||||
PrintFlags flags;
|
||||
uint width;
|
||||
uint prec;
|
||||
usz idx;
|
||||
}
|
||||
}
|
||||
|
||||
bitstruct PrintFlags : uint
|
||||
{
|
||||
bool zeropad : 0;
|
||||
bool left : 1;
|
||||
bool plus : 2;
|
||||
bool space : 3;
|
||||
bool hash : 4;
|
||||
bool uppercase : 5;
|
||||
bool precision : 6;
|
||||
}
|
||||
|
||||
fn void Formatter.init(Formatter* this, OutputFn out_fn, void* data = null)
|
||||
{
|
||||
*this = { .data = data, .out_fn = out_fn};
|
||||
}
|
||||
|
||||
fn void! Formatter.out(Formatter* this, char c) @private
|
||||
{
|
||||
this.out_fn(c, this.data)!;
|
||||
}
|
||||
|
||||
macro bool! Formatter.print_with_function(Formatter* this, any arg)
|
||||
{
|
||||
if (&arg.to_format)
|
||||
{
|
||||
PrintFlags old = this.flags;
|
||||
uint old_width = this.width;
|
||||
uint old_prec = this.prec;
|
||||
defer
|
||||
{
|
||||
this.flags = old;
|
||||
this.width = old_width;
|
||||
this.prec = old_prec;
|
||||
}
|
||||
arg.to_format(this)!;
|
||||
return true;
|
||||
}
|
||||
if (&arg.to_string)
|
||||
{
|
||||
PrintFlags old = this.flags;
|
||||
uint old_width = this.width;
|
||||
uint old_prec = this.prec;
|
||||
defer
|
||||
{
|
||||
this.flags = old;
|
||||
this.width = old_width;
|
||||
this.prec = old_prec;
|
||||
}
|
||||
@stack_mem(512; Allocator* mem)
|
||||
{
|
||||
this.out_substr(arg.to_string(mem))!;
|
||||
return true;
|
||||
};
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void! Formatter.out_str(Formatter* this, any arg) @private
|
||||
{
|
||||
switch (arg.type.kindof)
|
||||
{
|
||||
case TYPEID:
|
||||
return this.out_substr("typeid");
|
||||
case VOID:
|
||||
return this.out_substr("void");
|
||||
case ANYFAULT:
|
||||
case FAULT:
|
||||
return this.out_substr((*(anyfault*)arg.ptr).nameof);
|
||||
case ANY:
|
||||
return this.out_str(*(any*)arg);
|
||||
case ENUM:
|
||||
if (this.print_with_function(arg)!) return;
|
||||
return this.out_substr(arg.type.names[types::any_to_int(arg, usz)!!]);
|
||||
case STRUCT:
|
||||
if (this.print_with_function(arg)!) return;
|
||||
return this.out_substr("<struct>");
|
||||
case UNION:
|
||||
if (this.print_with_function(arg)!) return;
|
||||
return this.out_substr("<union>");
|
||||
case BITSTRUCT:
|
||||
if (this.print_with_function(arg)!) return;
|
||||
return this.out_substr("<bitstruct>");
|
||||
case FUNC:
|
||||
if (this.print_with_function(arg)!) return;
|
||||
return this.out_substr("<function>");
|
||||
case OPTIONAL:
|
||||
unreachable();
|
||||
case DISTINCT:
|
||||
if (this.print_with_function(arg)!) return;
|
||||
if (arg.type == DString.typeid)
|
||||
{
|
||||
return this.out_substr(((DString*)arg).str());
|
||||
}
|
||||
return this.out_str(any { arg.ptr, arg.type.inner });
|
||||
case POINTER:
|
||||
if (this.print_with_function(arg)!) return;
|
||||
return this.ntoa_any(arg, 16);
|
||||
case SIGNED_INT:
|
||||
case UNSIGNED_INT:
|
||||
return this.ntoa_any(arg, 10);
|
||||
case FLOAT:
|
||||
return this.ftoa(float_from_any(arg)!!);
|
||||
case ARRAY:
|
||||
if (this.print_with_function(arg)!) return;
|
||||
// this is SomeType[*] so grab the "SomeType"
|
||||
typeid inner = arg.type.inner;
|
||||
usz size = inner.sizeof;
|
||||
usz len = arg.type.len;
|
||||
// Pretend this is a String
|
||||
void* ptr = (void*)arg.ptr;
|
||||
this.out('[')!;
|
||||
for (usz i = 0; i < len; i++)
|
||||
{
|
||||
if (i != 0) this.out_substr(", ")!;
|
||||
this.out_str(any { ptr, inner })!;
|
||||
ptr += size;
|
||||
}
|
||||
return this.out(']');
|
||||
case VECTOR:
|
||||
if (this.print_with_function(arg)!) return;
|
||||
// this is SomeType[*] so grab the "SomeType"
|
||||
typeid inner = arg.type.inner;
|
||||
usz size = inner.sizeof;
|
||||
usz len = arg.type.len;
|
||||
// Pretend this is a String
|
||||
void* ptr = (void*)arg.ptr;
|
||||
this.out_substr("[<")!;
|
||||
for (usz i = 0; i < len; i++)
|
||||
{
|
||||
if (i != 0) this.out_substr(", ")!;
|
||||
this.out_str(any { ptr, inner })!;
|
||||
ptr += size;
|
||||
}
|
||||
return this.out_substr(">]");
|
||||
case SUBARRAY:
|
||||
if (this.print_with_function(arg)!) return;
|
||||
// this is SomeType[] so grab the "SomeType"
|
||||
typeid inner = arg.type.inner;
|
||||
if (inner == char.typeid)
|
||||
{
|
||||
return this.out_substr(*(String*)arg);
|
||||
}
|
||||
usz size = inner.sizeof;
|
||||
// Pretend this is a String
|
||||
String* temp = (void*)arg.ptr;
|
||||
void* ptr = (void*)temp.ptr;
|
||||
usz len = temp.len;
|
||||
this.out('[')!;
|
||||
for (usz i = 0; i < len; i++)
|
||||
{
|
||||
if (i != 0) this.out_substr(", ")!;
|
||||
this.out_str(any { ptr, inner })!;
|
||||
ptr += size;
|
||||
}
|
||||
this.out(']')!;
|
||||
case BOOL:
|
||||
return this.out_substr(*(bool*)arg.ptr ? "true" : "false");
|
||||
default:
|
||||
if (this.print_with_function(arg)!) return;
|
||||
return this.out_substr("Invalid type");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn void! out_buffer_fn(char c, void *data) @private
|
||||
{
|
||||
BufferData *buffer_data = data;
|
||||
if (buffer_data.written >= buffer_data.buffer.len) return PrintFault.BUFFER_EXCEEDED?;
|
||||
buffer_data.buffer[buffer_data.written++] = c;
|
||||
}
|
||||
|
||||
fn void! out_null_fn(char c @unused, void* data @unused) @private
|
||||
{
|
||||
}
|
||||
|
||||
fn void! out_putchar_fn(char c, void* data @unused) @private
|
||||
{
|
||||
libc::putchar(c);
|
||||
}
|
||||
|
||||
fn void! out_fputchar_fn(char c, void* data) @private
|
||||
{
|
||||
File* f = data;
|
||||
f.putc(c)!;
|
||||
}
|
||||
|
||||
struct BufferData @private
|
||||
{
|
||||
char[] buffer;
|
||||
usz written;
|
||||
}
|
||||
|
||||
|
||||
fn usz! Formatter.vprintf(Formatter* this, String format, any[] anys)
|
||||
{
|
||||
if (!this.out_fn)
|
||||
{
|
||||
// use null output function
|
||||
this.out_fn = &out_null_fn;
|
||||
}
|
||||
usz format_len = format.len;
|
||||
usz variant_index = 0;
|
||||
for (usz i = 0; i < format_len; i++)
|
||||
{
|
||||
// format specifier? %[flags][width][.precision][length]
|
||||
char c = format[i];
|
||||
if (c != '%')
|
||||
{
|
||||
// no
|
||||
this.out(c)!;
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
if (i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
|
||||
c = format[i];
|
||||
if (c == '%')
|
||||
{
|
||||
this.out(c)!;
|
||||
continue;
|
||||
}
|
||||
// evaluate flags
|
||||
this.flags = {};
|
||||
while FLAG_EVAL: (true)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '0': this.flags.zeropad = true;
|
||||
case '-': this.flags.left = true;
|
||||
case '+': this.flags.plus = true;
|
||||
case ' ': this.flags.space = true;
|
||||
case '#': this.flags.hash = true;
|
||||
default: break FLAG_EVAL;
|
||||
}
|
||||
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
|
||||
c = format[i];
|
||||
}
|
||||
// evaluate width field
|
||||
int w = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i)!;
|
||||
c = format[i];
|
||||
if (w < 0)
|
||||
{
|
||||
this.flags.left = true;
|
||||
w = -w;
|
||||
}
|
||||
this.width = w;
|
||||
// evaluate precision field
|
||||
this.prec = 0;
|
||||
if (c == '.')
|
||||
{
|
||||
this.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)!;
|
||||
this.prec = prec < 0 ? 0 : prec;
|
||||
c = format[i];
|
||||
}
|
||||
|
||||
// evaluate specifier
|
||||
uint base = 0;
|
||||
if (variant_index >= anys.len) return PrintFault.MISSING_ARG?;
|
||||
any current = anys[variant_index++];
|
||||
switch (c)
|
||||
{
|
||||
case 'd':
|
||||
base = 10;
|
||||
this.flags.hash = false;
|
||||
case 'X' :
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'x' :
|
||||
base = 16;
|
||||
case 'O':
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'o' :
|
||||
base = 8;
|
||||
case 'B':
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'b' :
|
||||
base = 2;
|
||||
case 'A':
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'a':
|
||||
this.atoa(float_from_any(current)!!)!;
|
||||
continue;
|
||||
case 'F' :
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'f':
|
||||
this.ftoa(float_from_any(current)!!)!;
|
||||
continue;
|
||||
case 'E':
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'e':
|
||||
this.etoa(float_from_any(current)!!)!;
|
||||
continue;
|
||||
case 'G':
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'g':
|
||||
this.gtoa(float_from_any(current)!!)!;
|
||||
continue;
|
||||
case 'c':
|
||||
this.out_char(current)!;
|
||||
continue;
|
||||
case 's':
|
||||
this.out_str(current)!;
|
||||
continue;
|
||||
case 'p':
|
||||
this.flags.zeropad = true;
|
||||
this.flags.hash = true;
|
||||
base = 16;
|
||||
default:
|
||||
return PrintFault.INVALID_FORMAT_STRING?;
|
||||
}
|
||||
if (base != 10)
|
||||
{
|
||||
this.flags.plus = false;
|
||||
this.flags.space = false;
|
||||
}
|
||||
// ignore '0' flag when precision is given
|
||||
if (this.flags.precision) this.flags.zeropad = false;
|
||||
|
||||
bool is_neg;
|
||||
uint128 v = int_from_any(current, &is_neg)!!;
|
||||
|
||||
this.ntoa(v, is_neg, base)!;
|
||||
}
|
||||
// termination
|
||||
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
|
||||
|
||||
// return written chars without terminating \0
|
||||
return this.idx;
|
||||
}
|
||||
|
||||
205
lib/std/io/io_stream.c3
Normal file
205
lib/std/io/io_stream.c3
Normal file
@@ -0,0 +1,205 @@
|
||||
module std::io;
|
||||
|
||||
def CloseStreamFn = fn void!(Stream*);
|
||||
def FlushStreamFn = fn void!(Stream*);
|
||||
def SeekStreamFn = fn usz!(Stream*, isz offset, Seek seek);
|
||||
def LenStreamFn = fn usz(Stream*);
|
||||
def AvailableStreamFn = fn usz(Stream*);
|
||||
def ReadStreamFn = fn usz!(Stream*, char[] bytes);
|
||||
def ReadFromStreamFn = fn usz!(Stream*, Stream*);
|
||||
def ReadByteStreamFn = fn char!(Stream*);
|
||||
def PushbackByteStreamFn = fn void!(Stream*);
|
||||
def WriteStreamFn = fn usz!(Stream*, char[] bytes);
|
||||
def WriteToStreamFn = fn usz!(Stream*, Stream* out);
|
||||
def WriteByteStreamFn = fn void!(Stream*, char c);
|
||||
def DestroyStreamFn = fn void!(Stream*);
|
||||
|
||||
struct StreamInterface
|
||||
{
|
||||
CloseStreamFn close_fn;
|
||||
FlushStreamFn flush_fn;
|
||||
SeekStreamFn seek_fn;
|
||||
LenStreamFn len_fn;
|
||||
AvailableStreamFn available_fn;
|
||||
ReadStreamFn read_fn;
|
||||
ReadFromStreamFn read_stream_fn;
|
||||
ReadByteStreamFn read_byte_fn;
|
||||
PushbackByteStreamFn pushback_byte_fn;
|
||||
WriteStreamFn write_fn;
|
||||
WriteToStreamFn write_stream_fn;
|
||||
WriteByteStreamFn write_byte_fn;
|
||||
DestroyStreamFn destroy_fn;
|
||||
}
|
||||
|
||||
struct Stream
|
||||
{
|
||||
StreamInterface *fns;
|
||||
void* data;
|
||||
}
|
||||
|
||||
fn bool Stream.supports_seek(Stream* s) @inline => (bool)s.fns.seek_fn;
|
||||
fn bool Stream.supports_available(Stream* s) @inline => s.fns.available_fn || s.fns.seek_fn;
|
||||
fn bool Stream.supports_len(Stream* s) @inline => s.fns.len_fn || s.fns.seek_fn;
|
||||
fn bool Stream.supports_read(Stream* s) @inline => s.fns.read_fn || s.fns.read_byte_fn;
|
||||
fn bool Stream.supports_read_from(Stream* s) @inline => (bool)s.fns.read_stream_fn;
|
||||
fn bool Stream.supports_write_to(Stream* s) @inline => (bool)s.fns.write_stream_fn;
|
||||
fn bool Stream.supports_pushback_byte(Stream* s) @inline => s.fns.pushback_byte_fn || s.fns.seek_fn;
|
||||
fn bool Stream.supports_write(Stream* s) @inline => s.fns.write_fn || s.fns.write_byte_fn;
|
||||
|
||||
fn void! Stream.destroy(Stream* s) @inline @maydiscard
|
||||
{
|
||||
if (s.fns.destroy_fn) return s.fns.destroy_fn(s);
|
||||
return s.close();
|
||||
}
|
||||
|
||||
fn void! Stream.close(Stream* s) @inline @maydiscard
|
||||
{
|
||||
if (CloseStreamFn func = s.fns.close_fn) return func(s);
|
||||
}
|
||||
|
||||
fn usz! Stream.seek(Stream* s, isz offset, Seek seek) @inline
|
||||
{
|
||||
if (SeekStreamFn func = s.fns.seek_fn) return func(s, offset, seek);
|
||||
return IoError.NOT_SEEKABLE?;
|
||||
}
|
||||
|
||||
fn usz! Stream.available(Stream* s) @inline
|
||||
{
|
||||
if (AvailableStreamFn func = s.fns.available_fn) return func(s);
|
||||
if (SeekStreamFn func = s.fns.seek_fn)
|
||||
{
|
||||
usz curr = func(s, 0, Seek.CURSOR)!;
|
||||
usz len = func(s, 0, Seek.END)!;
|
||||
func(s, curr, Seek.SET)!;
|
||||
return len - curr;
|
||||
}
|
||||
return IoError.NOT_SEEKABLE?;
|
||||
}
|
||||
|
||||
fn usz! Stream.read(Stream* s, char[] buffer)
|
||||
{
|
||||
if (ReadStreamFn func = s.fns.read_fn) return func(s, buffer);
|
||||
if (ReadByteStreamFn func = s.fns.read_byte_fn)
|
||||
{
|
||||
usz len = 0;
|
||||
foreach (&cptr : buffer)
|
||||
{
|
||||
char! c = func(s);
|
||||
if (catch err = c)
|
||||
{
|
||||
case IoError.EOF: return len;
|
||||
default: return err?;
|
||||
}
|
||||
*cptr = c;
|
||||
len++;
|
||||
}
|
||||
}
|
||||
return IoError.UNSUPPORTED_OPERATION?;
|
||||
}
|
||||
|
||||
fn char! Stream.read_byte(Stream* s) @inline
|
||||
{
|
||||
if (ReadByteStreamFn func = s.fns.read_byte_fn) return func(s);
|
||||
return IoError.UNSUPPORTED_OPERATION?;
|
||||
}
|
||||
|
||||
fn usz! Stream.write(Stream* s, char[] bytes) @inline
|
||||
{
|
||||
if (WriteStreamFn func = s.fns.write_fn) return func(s, bytes);
|
||||
if (WriteByteStreamFn func = s.fns.write_byte_fn)
|
||||
{
|
||||
foreach (c : bytes) func(s, c)!;
|
||||
return bytes.len;
|
||||
}
|
||||
return IoError.UNSUPPORTED_OPERATION?;
|
||||
}
|
||||
|
||||
fn void! Stream.write_byte(Stream* s, char b) @inline
|
||||
{
|
||||
if (WriteByteStreamFn func = s.fns.write_byte_fn) return func(s, b);
|
||||
return IoError.UNSUPPORTED_OPERATION?;
|
||||
}
|
||||
|
||||
fn usz! Stream.write_to(Stream* s, Stream* to) @inline
|
||||
{
|
||||
if (WriteToStreamFn func = s.fns.write_stream_fn) return func(s, to);
|
||||
return IoError.UNSUPPORTED_OPERATION?;
|
||||
}
|
||||
|
||||
fn usz! Stream.read_from(Stream* s, Stream* from) @inline
|
||||
{
|
||||
if (ReadFromStreamFn func = s.fns.read_stream_fn) return func(s, from);
|
||||
return IoError.UNSUPPORTED_OPERATION?;
|
||||
}
|
||||
|
||||
fn void! Stream.flush(Stream* s) @inline @maydiscard
|
||||
{
|
||||
if (FlushStreamFn func = s.fns.flush_fn) return func(s);
|
||||
return IoError.UNSUPPORTED_OPERATION?;
|
||||
}
|
||||
|
||||
fn usz! Stream.len(Stream* s) @inline
|
||||
{
|
||||
if (LenStreamFn func = s.fns.len_fn) return func(s);
|
||||
if (SeekStreamFn func = s.fns.seek_fn)
|
||||
{
|
||||
usz curr = func(s, 0, Seek.CURSOR)!;
|
||||
usz len = func(s, 0, Seek.END)!;
|
||||
func(s, curr, Seek.SET)!;
|
||||
return len;
|
||||
}
|
||||
return IoError.NOT_SEEKABLE?;
|
||||
}
|
||||
|
||||
fn void! Stream.pushback_byte(Stream* s) @inline
|
||||
{
|
||||
if (PushbackByteStreamFn func = s.fns.pushback_byte_fn) return func(s);
|
||||
if (SeekStreamFn func = s.fns.seek_fn)
|
||||
{
|
||||
func(s, -1, CURSOR)!;
|
||||
return;
|
||||
}
|
||||
return IoError.UNSUPPORTED_OPERATION?;
|
||||
}
|
||||
|
||||
fn void! Stream.write_string(Stream* s, String str) @inline => (void)(s.write((char[])str)!);
|
||||
|
||||
fn usz! Stream.copy_to(Stream* s, Stream* dst, char[] buffer = {})
|
||||
{
|
||||
if (buffer.len) return copy_through_buffer(s, dst, buffer);
|
||||
if (WriteToStreamFn func = s.fns.write_stream_fn) return func(s, dst);
|
||||
if (ReadFromStreamFn func = dst.fns.read_stream_fn) return func(dst, s);
|
||||
$switch (env::MEMORY_ENV)
|
||||
$case NORMAL:
|
||||
@pool()
|
||||
{
|
||||
return copy_through_buffer(s, dst, tmalloc(char, 4096));
|
||||
};
|
||||
$case SMALL:
|
||||
@pool()
|
||||
{
|
||||
return copy_through_buffer(s, dst, tmalloc(char, 1024));
|
||||
};
|
||||
$case TINY:
|
||||
$case NONE:
|
||||
return copy_through_buffer(s, dst, &&(char[256]{}));
|
||||
$endswitch
|
||||
}
|
||||
|
||||
macro usz! copy_through_buffer(Stream* s, Stream* dst, char[] buffer) @local
|
||||
{
|
||||
usz total_copied;
|
||||
while (true)
|
||||
{
|
||||
usz! len = s.read(buffer);
|
||||
if (catch err = len)
|
||||
{
|
||||
case IoError.EOF: return total_copied;
|
||||
default: return err?;
|
||||
}
|
||||
if (!len) return total_copied;
|
||||
usz written = dst.write(buffer[:len])!;
|
||||
total_copied += len;
|
||||
if (written != len) return IoError.INCOMPLETE_WRITE?;
|
||||
}
|
||||
}
|
||||
44
lib/std/io/os/chdir.c3
Normal file
44
lib/std/io/os/chdir.c3
Normal file
@@ -0,0 +1,44 @@
|
||||
module std::io::os;
|
||||
import libc;
|
||||
|
||||
|
||||
$switch
|
||||
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_posix():
|
||||
|
||||
macro void! native_chdir(Path p)
|
||||
{
|
||||
if (posix::chdir(p.as_zstr()))
|
||||
{
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EACCES: return IoError.NO_PERMISSION?;
|
||||
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG?;
|
||||
case errno::ENOTDIR: return IoError.FILE_NOT_DIR?;
|
||||
case errno::ENOENT: return IoError.FILE_NOT_FOUND?;
|
||||
case errno::ELOOP: return IoError.SYMLINK_FAILED?;
|
||||
default: return IoError.GENERAL_ERROR?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_win32():
|
||||
|
||||
macro void! native_chdir(Path path)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
// TODO improve with better error handling.
|
||||
if (win32::win32_SetCurrentDirectoryW(path.as_str().to_temp_utf16()!!)) return;
|
||||
};
|
||||
return IoError.GENERAL_ERROR?;
|
||||
}
|
||||
|
||||
$default:
|
||||
|
||||
fn void! native_chdir(Path path)
|
||||
{
|
||||
unreachable("'getcwd' not available");
|
||||
}
|
||||
|
||||
$endswitch
|
||||
187
lib/std/io/os/file.c3
Normal file
187
lib/std/io/os/file.c3
Normal file
@@ -0,0 +1,187 @@
|
||||
module std::io::os;
|
||||
import libc;
|
||||
|
||||
def FopenFn = fn void*!(String, String);
|
||||
def FreopenFn = fn void*!(void*, String, String);
|
||||
def FcloseFn = fn void!(void*);
|
||||
def FseekFn = fn void!(void*, isz, Seek);
|
||||
def FtellFn = fn usz!(void*);
|
||||
def FwriteFn = fn usz!(void*, char[] buffer);
|
||||
def FreadFn = fn usz!(void*, char[] buffer);
|
||||
|
||||
$if !$defined(native_fopen_fn):
|
||||
FopenFn native_fopen_fn @weak;
|
||||
$endif
|
||||
$if !$defined(native_fclose_fn):
|
||||
FcloseFn native_fclose_fn @weak;
|
||||
$endif
|
||||
$if !$defined(native_freopen_fn):
|
||||
FreopenFn native_freopen_fn @weak;
|
||||
$endif
|
||||
$if !$defined(native_fseek_fn):
|
||||
FseekFn native_fseek_fn @weak;
|
||||
$endif
|
||||
$if !$defined(native_ftell_fn):
|
||||
FtellFn native_ftell_fn @weak;
|
||||
$endif
|
||||
$if !$defined(native_fwrite_fn):
|
||||
FwriteFn native_fwrite_fn @weak;
|
||||
$endif
|
||||
$if !$defined(native_fread_fn):
|
||||
FreadFn native_fread_fn @weak;
|
||||
$endif
|
||||
|
||||
|
||||
/**
|
||||
* @require mode.len > 0
|
||||
* @require filename.len > 0
|
||||
**/
|
||||
fn void*! native_fopen(String filename, String mode) @inline
|
||||
{
|
||||
$if !env::COMPILER_LIBC_AVAILABLE:
|
||||
if (native_fopen_fn) return native_fopen_fn(filename, mode);
|
||||
unreachable("Tried to call fopen without support.");
|
||||
$else
|
||||
@pool()
|
||||
{
|
||||
$if env::os_is_win32():
|
||||
void* file = (CFile)_wfopen(filename.to_temp_utf16(), filename.to_temp_utf16())!;
|
||||
$else
|
||||
void* file = libc::fopen(filename.zstr_tcopy(), mode.zstr_tcopy());
|
||||
$endif
|
||||
return file ?: file_open_errno()?;
|
||||
};
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @require mode.len > 0
|
||||
* @require filename.len > 0
|
||||
**/
|
||||
fn void*! native_freopen(void* file, String filename, String mode) @inline
|
||||
{
|
||||
$if !env::COMPILER_LIBC_AVAILABLE:
|
||||
if (native_freopen_fn) return native_freopen_fn(file, filename, mode);
|
||||
unreachable("Tried to call freopen without support.");
|
||||
$else
|
||||
@pool()
|
||||
{
|
||||
$if env::os_is_win32():
|
||||
file = _wfreopen(filename.to_temp_utf16(), mode.to_temp_utf16(), file)!;
|
||||
$else
|
||||
file = libc::freopen(filename.zstr_tcopy(), mode.zstr_tcopy(), file);
|
||||
$endif
|
||||
return file ?: file_open_errno()?;
|
||||
};
|
||||
$endif
|
||||
}
|
||||
|
||||
fn void! native_fseek(void* file, isz offset, Seek seek_mode) @inline
|
||||
{
|
||||
$if !env::COMPILER_LIBC_AVAILABLE:
|
||||
if (native_fseek_fn) return native_fseek_fn(file, offset, seek_mode);
|
||||
unreachable("Tried to call fseek without support.");
|
||||
$else
|
||||
$if env::os_is_win32():
|
||||
bool success = _fseeki64(file, (long)offset, (int)seek_mode) == 0;
|
||||
$else
|
||||
bool success = libc::fseek(file, (SeekIndex)offset, (CInt)seek_mode) == 0;
|
||||
$endif
|
||||
if (!success) return file_seek_errno()?;
|
||||
$endif
|
||||
}
|
||||
|
||||
fn usz! native_ftell(CFile file) @inline
|
||||
{
|
||||
$if !env::COMPILER_LIBC_AVAILABLE:
|
||||
if (native_ftell_fn) return native_ftell_fn(file);
|
||||
unreachable("Tried to call ftell without support.");
|
||||
$else
|
||||
$if env::os_is_win32():
|
||||
long index = _ftelli64(file);
|
||||
return index >= 0 ? index : file_seek_errno()?;
|
||||
$else
|
||||
SeekIndex index = libc::ftell(file);
|
||||
return index >= 0 ? index : file_seek_errno()?;
|
||||
$endif
|
||||
$endif
|
||||
}
|
||||
|
||||
fn usz! native_fwrite(CFile file, char[] buffer) @inline
|
||||
{
|
||||
$if !env::COMPILER_LIBC_AVAILABLE:
|
||||
if (native_fwrite_fn) return native_fwrite_fn(file, buffer);
|
||||
unreachable("Tried to call fwrite without support.");
|
||||
$else
|
||||
return libc::fwrite(buffer.ptr, 1, buffer.len, file);
|
||||
$endif
|
||||
}
|
||||
|
||||
fn usz! native_fread(CFile file, char[] buffer) @inline
|
||||
{
|
||||
$if !env::COMPILER_LIBC_AVAILABLE:
|
||||
if (native_fread_fn) return native_fread_fn(file, buffer);
|
||||
unreachable("Tried to call fread without support.");
|
||||
$else
|
||||
return libc::fread(buffer.ptr, 1, buffer.len, file);
|
||||
$endif
|
||||
}
|
||||
|
||||
macro anyfault file_open_errno() @local
|
||||
{
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EACCES: return IoError.NO_PERMISSION;
|
||||
case errno::EDQUOT: return IoError.OUT_OF_SPACE;
|
||||
case errno::EBADF: return IoError.FILE_NOT_VALID;
|
||||
case errno::EEXIST: return IoError.ALREADY_EXISTS;
|
||||
case errno::EINTR: return IoError.INTERRUPTED;
|
||||
case errno::EFAULT: return IoError.GENERAL_ERROR;
|
||||
case errno::EISDIR: return IoError.FILE_IS_DIR;
|
||||
case errno::ELOOP: return IoError.SYMLINK_FAILED;
|
||||
case errno::EMFILE: return IoError.TOO_MANY_DESCRIPTORS;
|
||||
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG;
|
||||
case errno::ENFILE: return IoError.OUT_OF_SPACE;
|
||||
case errno::ENOTDIR: return IoError.FILE_NOT_DIR;
|
||||
case errno::ENOENT: return IoError.FILE_NOT_FOUND;
|
||||
case errno::ENOSPC: return IoError.OUT_OF_SPACE;
|
||||
case errno::ENXIO: return IoError.FILE_NOT_FOUND;
|
||||
case errno::EOVERFLOW: return IoError.OVERFLOW;
|
||||
case errno::EROFS: return IoError.READ_ONLY;
|
||||
case errno::EOPNOTSUPP: return IoError.UNSUPPORTED_OPERATION;
|
||||
case errno::EIO: return IoError.INCOMPLETE_WRITE;
|
||||
case errno::EWOULDBLOCK: return IoError.WOULD_BLOCK;
|
||||
default: return IoError.UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
macro anyfault file_seek_errno() @local
|
||||
{
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::ESPIPE: return IoError.FILE_IS_PIPE;
|
||||
case errno::EPIPE: return IoError.FILE_IS_PIPE;
|
||||
case errno::EOVERFLOW: return IoError.OVERFLOW;
|
||||
case errno::ENXIO: return IoError.FILE_NOT_FOUND;
|
||||
case errno::ENOSPC: return IoError.OUT_OF_SPACE;
|
||||
case errno::EIO: return IoError.INCOMPLETE_WRITE;
|
||||
case errno::EINVAL: return IoError.INVALID_POSITION;
|
||||
case errno::EINTR: return IoError.INTERRUPTED;
|
||||
case errno::EFBIG: return IoError.OUT_OF_SPACE;
|
||||
case errno::EBADF: return IoError.FILE_NOT_VALID;
|
||||
case errno::EAGAIN: return IoError.WOULD_BLOCK;
|
||||
default: return IoError.UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// Win functions
|
||||
$if env::os_is_win32():
|
||||
extern fn void* _wfopen(Char16*, Char16*) @local;
|
||||
extern fn void* _wfreopen(Char16*, Char16*, CFile) @local;
|
||||
extern fn int _fseeki64(CFile, long, int) @local;
|
||||
extern fn long _ftelli64(CFile) @local;
|
||||
$endif
|
||||
|
||||
$if env::os_is_posix():
|
||||
extern fn CInt access(ZString path, CInt mode);
|
||||
$endif
|
||||
101
lib/std/io/os/fileinfo_darwin.c3
Normal file
101
lib/std/io/os/fileinfo_darwin.c3
Normal file
@@ -0,0 +1,101 @@
|
||||
module std::io::file::os;
|
||||
import libc;
|
||||
|
||||
$if env::os_is_darwin():
|
||||
|
||||
struct DarwinTimespec @private
|
||||
{
|
||||
long tv_sec;
|
||||
long tv_nsec;
|
||||
}
|
||||
struct Darwin64Stat @private
|
||||
{
|
||||
int st_dev;
|
||||
ushort st_mode;
|
||||
ushort st_nlink;
|
||||
ulong st_ino;
|
||||
uint st_uid;
|
||||
uint st_gid;
|
||||
int st_rdev;
|
||||
DarwinTimespec st_atimespec; // time of last access
|
||||
DarwinTimespec st_mtimespec; // time of last data modification
|
||||
DarwinTimespec st_ctimespec; // time of last status change
|
||||
DarwinTimespec st_birthtimespec; // time of file creation(birth)
|
||||
long st_size;
|
||||
long st_blocks;
|
||||
int st_blocksize;
|
||||
uint st_flags;
|
||||
uint st_gen;
|
||||
int st_lspare;
|
||||
long[2] st_qspare;
|
||||
}
|
||||
extern fn int _stat(ZString str, Darwin64Stat* stat) @extern("stat64");
|
||||
|
||||
const S_IFMT = 0o170000; // type of file mask
|
||||
const S_IFIFO = 0o010000; // named pipe (fifo)
|
||||
const S_IFCHR = 0o020000; // character special
|
||||
const S_IFDIR = 0o040000; // directory
|
||||
const S_IFBLK = 0o060000; // block special
|
||||
const S_IFREG = 0o100000; // regular
|
||||
const S_IFLNK = 0o120000; // symbolic link
|
||||
const S_IFSOCK = 0o140000; // socket
|
||||
|
||||
fn usz! native_file_size(String path)
|
||||
{
|
||||
Darwin64Stat stat;
|
||||
read_stat(&stat, path)!;
|
||||
return stat.st_size;
|
||||
}
|
||||
|
||||
fn bool native_file_or_dir_exists(String path)
|
||||
{
|
||||
Darwin64Stat stat;
|
||||
return @ok(read_stat(&stat, path));
|
||||
}
|
||||
|
||||
fn bool native_is_file(String path)
|
||||
{
|
||||
Darwin64Stat stat;
|
||||
return @ok(read_stat(&stat, path)) && stat.st_mode & S_IFREG;
|
||||
}
|
||||
|
||||
fn bool native_is_dir(String path)
|
||||
{
|
||||
Darwin64Stat stat;
|
||||
return @ok(read_stat(&stat, path)) && stat.st_mode & S_IFDIR;
|
||||
}
|
||||
|
||||
fn void! read_stat(Darwin64Stat* stat, String path) @local
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
int res = _stat(path.zstr_tcopy(), stat);
|
||||
if (res != 0)
|
||||
{
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EBADF:
|
||||
return IoError.FILE_NOT_VALID?;
|
||||
case errno::EFAULT:
|
||||
unreachable("Invalid stat");
|
||||
case errno::EIO:
|
||||
return IoError.GENERAL_ERROR?;
|
||||
case errno::EACCES:
|
||||
return IoError.NO_PERMISSION?;
|
||||
case errno::ELOOP:
|
||||
return IoError.NO_PERMISSION?;
|
||||
case errno::ENAMETOOLONG:
|
||||
return IoError.NAME_TOO_LONG?;
|
||||
case errno::ENOENT:
|
||||
return IoError.FILE_NOT_FOUND?;
|
||||
case errno::ENOTDIR:
|
||||
return IoError.FILE_NOT_DIR?;
|
||||
case errno::EOVERFLOW:
|
||||
return IoError.GENERAL_ERROR?;
|
||||
default:
|
||||
return IoError.UNKNOWN_ERROR?;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
$endif
|
||||
181
lib/std/io/os/fileinfo_other.c3
Normal file
181
lib/std/io/os/fileinfo_other.c3
Normal file
@@ -0,0 +1,181 @@
|
||||
module std::io::file::os;
|
||||
|
||||
// native_temp_directory, for non Win32
|
||||
$if !env::os_is_win32():
|
||||
|
||||
fn Path! native_temp_directory(Allocator* using = mem::heap())
|
||||
{
|
||||
foreach (String env : { "TMPDIR", "TMP", "TEMP", "TEMPDIR" })
|
||||
{
|
||||
String tmpdir = env::get_var(env) ?? "";
|
||||
if (tmpdir) return path::new(tmpdir, using);
|
||||
}
|
||||
return path::new("/tmp", using);
|
||||
}
|
||||
|
||||
$if env::COMPILER_LIBC_AVAILABLE:
|
||||
|
||||
extern fn void* opendir(ZString);
|
||||
extern fn void closedir(void*);
|
||||
extern fn int remove(ZString);
|
||||
|
||||
const DT_UNKNOWN = 0;
|
||||
const DT_FIFO = 1;
|
||||
const DT_CHR = 2;
|
||||
const DT_DIR = 4;
|
||||
const DT_BLK = 6;
|
||||
const DT_REG = 8;
|
||||
const DT_LNK = 10;
|
||||
const DT_SOCK = 12;
|
||||
const DT_WHT = 14;
|
||||
|
||||
fn PathList! native_readdir(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator* using)
|
||||
{
|
||||
PathList list;
|
||||
list.init(.using = using);
|
||||
void* directory = opendir(dir.as_str() ? dir.as_zstr() : (ZString)".");
|
||||
defer if (directory) closedir(directory);
|
||||
if (!directory) return (path::is_dir(dir) ? IoError.CANNOT_READ_DIR : IoError.FILE_NOT_DIR)?;
|
||||
NativeDirentry* entry;
|
||||
while ((entry = readdir(directory)))
|
||||
{
|
||||
String name = ((ZString)&entry.name).as_str();
|
||||
if (!name || name == "." || name == "..") continue;
|
||||
if (entry.type == DT_LNK && no_symlinks) continue;
|
||||
if (entry.type == DT_DIR && no_dirs) continue;
|
||||
Path path = path::new(name.copy(using), using)!!;
|
||||
list.append(path);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require dir.as_str()
|
||||
**/
|
||||
fn void! native_rmtree(Path dir)
|
||||
{
|
||||
void* directory = opendir(dir.as_zstr());
|
||||
defer if (directory) closedir(directory);
|
||||
if (!directory) return path::is_dir(dir) ? IoError.CANNOT_READ_DIR? : IoError.FILE_NOT_DIR?;
|
||||
NativeDirentry* entry;
|
||||
while ((entry = readdir(directory)))
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
String name = ((ZString)&entry.name).as_str();
|
||||
if (!name || name == "." || name == "..") continue;
|
||||
Path new_path = dir.tappend(name)!;
|
||||
if (entry.type == DT_DIR)
|
||||
{
|
||||
native_rmtree(new_path)!;
|
||||
continue;
|
||||
}
|
||||
if (remove(new_path.as_zstr()))
|
||||
{
|
||||
// TODO improve
|
||||
return IoError.GENERAL_ERROR?;
|
||||
}
|
||||
};
|
||||
}
|
||||
os::native_rmdir(dir)!;
|
||||
}
|
||||
|
||||
$endif
|
||||
|
||||
$endif
|
||||
|
||||
$if !env::os_is_darwin() && !env::os_is_win32():
|
||||
|
||||
fn usz! native_file_size(String path)
|
||||
{
|
||||
File f = file::open(path, "r")!;
|
||||
defer (void)f.close();
|
||||
return f.seek(0, Seek.END)!;
|
||||
}
|
||||
|
||||
$if env::os_is_posix() && env::COMPILER_LIBC_AVAILABLE:
|
||||
|
||||
fn bool native_file_or_dir_exists(String path)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
return os::access(path.zstr_tcopy(), 0 /* F_OK */) != -1;
|
||||
};
|
||||
}
|
||||
|
||||
fn bool native_is_file(String path)
|
||||
{
|
||||
File! f = file::open(path, "r");
|
||||
defer (void)f.close();
|
||||
return @ok(f);
|
||||
}
|
||||
|
||||
fn bool native_is_dir(String path)
|
||||
{
|
||||
return native_file_or_dir_exists(path) && !native_is_file(path);
|
||||
}
|
||||
|
||||
$else
|
||||
|
||||
fn bool native_file_or_dir_exists(String path)
|
||||
{
|
||||
unreachable("Tried to call file_or_dir_exists without support.");
|
||||
}
|
||||
|
||||
fn bool native_is_dir(String path)
|
||||
{
|
||||
unreachable("Tried to call is_dir without support.");
|
||||
}
|
||||
|
||||
fn bool native_is_file(String path)
|
||||
{
|
||||
unreachable("Tried to call is_file without support.");
|
||||
}
|
||||
|
||||
$endif
|
||||
|
||||
$endif
|
||||
|
||||
$switch (env::OS_TYPE)
|
||||
$case IOS:
|
||||
$case MACOS:
|
||||
$case TVOS:
|
||||
$case WATCHOS:
|
||||
|
||||
$if env::ARCH_TYPE == X86_64:
|
||||
extern fn NativeDirentry* readdir(void*) @extern("readdir$INODE64");
|
||||
$else
|
||||
extern fn NativeDirentry* readdir(void*) @extern("readdir");
|
||||
$endif
|
||||
|
||||
struct NativeDirentry
|
||||
{
|
||||
usz ino;
|
||||
usz seekoff;
|
||||
ushort reclen;
|
||||
ushort namelen;
|
||||
char type;
|
||||
char[1024] name;
|
||||
}
|
||||
$case LINUX:
|
||||
extern fn NativeDirentry* readdir(void*);
|
||||
struct NativeDirentry
|
||||
{
|
||||
usz ino;
|
||||
isz seekoff;
|
||||
ushort reclen;
|
||||
char type;
|
||||
char[*] name;
|
||||
}
|
||||
$default:
|
||||
// Fix this as we go along.
|
||||
extern fn NativeDirentry* readdir(void*);
|
||||
struct NativeDirentry
|
||||
{
|
||||
usz ino;
|
||||
isz seekoff;
|
||||
ushort reclen;
|
||||
char type;
|
||||
char[*] name;
|
||||
}
|
||||
$endswitch
|
||||
113
lib/std/io/os/fileinfo_win32.c3
Normal file
113
lib/std/io/os/fileinfo_win32.c3
Normal file
@@ -0,0 +1,113 @@
|
||||
module std::io::file::os;
|
||||
import std::os::win32;
|
||||
|
||||
$if env::os_is_win32():
|
||||
|
||||
const Win32_DWORD FILE_ATTRIBUTE_READONLY = 0x01;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_HIDDEN = 0x02;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_SYSTEM = 0x04;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_DIRECTORY = 0x10;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_ARCHIVE = 0x20;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_DEVICE = 0x40;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_NORMAL = 0x80;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_TEMPORARY = 0x100;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_SPARSE_FILE = 0x200;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_REPARSE_POINT = 0x400;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_COMPRESSED = 0x800;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_OFFLINE = 0x1000;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_ENCRYPTED = 0x4000;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_VIRTUAL = 0x10000;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_EA = 0x40000;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_PINNED = 0x80000;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_UNPINNED = 0x100000;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x40000;
|
||||
const Win32_DWORD FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x400000;
|
||||
|
||||
fn usz! native_file_size(String path)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
Char16[] path16 = path.to_temp_utf16()!;
|
||||
Win32_FILE_ATTRIBUTE_DATA data;
|
||||
win32::win32_GetFileAttributesExW(path16, Win32_GET_FILEEX_INFO_LEVELS.STANDARD, &data);
|
||||
Win32_LARGE_INTEGER size;
|
||||
size.lowPart = data.nFileSizeLow;
|
||||
size.highPart = data.nFileSizeHigh;
|
||||
return (usz)size.quadPart;
|
||||
};
|
||||
}
|
||||
|
||||
fn bool native_file_or_dir_exists(String path)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
return (bool)win32::win32_PathFileExistsW(path.to_temp_utf16()) ?? false;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
fn bool native_is_file(String path)
|
||||
{
|
||||
File! f = file::open(path, "r");
|
||||
defer (void)f.close();
|
||||
return @ok(f);
|
||||
}
|
||||
|
||||
fn bool native_is_dir(String path)
|
||||
{
|
||||
return native_file_or_dir_exists(path) && !native_is_file(path);
|
||||
}
|
||||
|
||||
fn void! native_rmtree(Path path)
|
||||
{
|
||||
Win32_WIN32_FIND_DATAW find_data;
|
||||
|
||||
String s = path.as_str().tconcat("\\*");
|
||||
Win32_HANDLE find = win32::win32_FindFirstFileW(s.to_utf16(mem::temp()), &find_data)!;
|
||||
|
||||
if (find == win32::INVALID_HANDLE_VALUE) return IoError.CANNOT_READ_DIR?;
|
||||
|
||||
defer win32::win32_FindClose(find);
|
||||
do
|
||||
{
|
||||
String filename = string::from_zutf16(&find_data.cFileName, mem::temp())!;
|
||||
if (filename == "." || filename == "..") continue;
|
||||
Path file_path = path.tappend(filename)!;
|
||||
if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
native_rmtree(file_path)!;
|
||||
}
|
||||
else
|
||||
{
|
||||
win32::win32_DeleteFileW(file_path.as_str().to_utf16(mem::temp()));
|
||||
}
|
||||
} while (win32::win32_FindNextFileW(find, &find_data) != 0);
|
||||
os::native_rmdir(path)!;
|
||||
}
|
||||
|
||||
fn Path! native_temp_directory(Allocator* using = mem::heap())
|
||||
{
|
||||
@stack_mem(256; Allocator* mem)
|
||||
{
|
||||
Win32_DWORD len = win32::win32_GetTempPathW(0, null);
|
||||
if (!len) return IoError.GENERAL_ERROR?;
|
||||
Char16[] buff = malloc(Char16, len + 1, .using = mem);
|
||||
if (!win32::win32_GetTempPathW(len, buff)) return IoError.GENERAL_ERROR?;
|
||||
return path::new(string::from_utf16(buff[:len], .using = mem), using);
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
}else if(method == file_size_methods::get_attributes){
|
||||
WIN32_FILE_ATTRIBUTE_DATA file_attr_data;
|
||||
if(GetFileAttributesEx(path, GetFileExInfoStandard, &file_attr_data)){
|
||||
file_size.LowPart = file_attr_data.nFileSizeLow;
|
||||
file_size.HighPart = file_attr_data.nFileSizeHigh;
|
||||
}
|
||||
}
|
||||
*/
|
||||
$endif
|
||||
51
lib/std/io/os/getcwd.c3
Normal file
51
lib/std/io/os/getcwd.c3
Normal file
@@ -0,0 +1,51 @@
|
||||
module std::io::os;
|
||||
import libc;
|
||||
|
||||
$switch
|
||||
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_win32():
|
||||
|
||||
macro String! getcwd(Allocator* using = mem::heap())
|
||||
{
|
||||
const DEFAULT_BUFFER = 256;
|
||||
Char16[DEFAULT_BUFFER] buffer;
|
||||
Char16 *res = win32::_wgetcwd(&buffer, DEFAULT_BUFFER);
|
||||
bool free = false;
|
||||
defer if (free) libc::free(res);
|
||||
if (!res)
|
||||
{
|
||||
if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR?;
|
||||
res = win32::_wgetcwd(null, 0);
|
||||
free = true;
|
||||
}
|
||||
Char16[] str16 = res[:win32::wcslen(res)];
|
||||
return string::from_utf16(str16, using);
|
||||
}
|
||||
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_posix():
|
||||
|
||||
macro String! getcwd(Allocator* using = mem::heap())
|
||||
{
|
||||
const usz DEFAULT_BUFFER = 256;
|
||||
char[DEFAULT_BUFFER] buffer;
|
||||
ZString res = posix::getcwd(&buffer, DEFAULT_BUFFER);
|
||||
bool free = false;
|
||||
if (!res)
|
||||
{
|
||||
// Improve error
|
||||
if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR?;
|
||||
res = posix::getcwd(null, 0);
|
||||
free = true;
|
||||
}
|
||||
defer if (free) libc::free((void*)res);
|
||||
return res.copy(using);
|
||||
}
|
||||
|
||||
$default:
|
||||
|
||||
fn String! getcwd(Allocator* using = mem::heap())
|
||||
{
|
||||
unreachable("'getcwd' not available");
|
||||
}
|
||||
|
||||
$endswitch
|
||||
63
lib/std/io/os/mkdir.c3
Normal file
63
lib/std/io/os/mkdir.c3
Normal file
@@ -0,0 +1,63 @@
|
||||
module std::io::os;
|
||||
import libc;
|
||||
import std::io::path;
|
||||
import std::os::win32;
|
||||
import std::os::posix;
|
||||
|
||||
$switch
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_posix():
|
||||
|
||||
macro bool! native_mkdir(Path path, MkdirPermissions permissions)
|
||||
{
|
||||
if (!posix::mkdir(path.as_zstr(), permissions == NORMAL ? 0o777 : 0o700)) return true;
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EACCES:
|
||||
case errno::EPERM:
|
||||
case errno::EROFS:
|
||||
case errno::EFAULT: return IoError.NO_PERMISSION?;
|
||||
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG?;
|
||||
case errno::EDQUOT:
|
||||
case errno::ENOSPC: return IoError.OUT_OF_SPACE?;
|
||||
case errno::EISDIR:
|
||||
case errno::EEXIST: return false;
|
||||
case errno::ELOOP: return IoError.SYMLINK_FAILED?;
|
||||
case errno::ENOTDIR: return IoError.FILE_NOT_FOUND?;
|
||||
default: return IoError.GENERAL_ERROR?;
|
||||
}
|
||||
}
|
||||
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_win32():
|
||||
|
||||
macro bool! native_mkdir(Path path, MkdirPermissions permissions)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
// TODO security attributes
|
||||
if (win32::win32_CreateDirectoryW(path.as_str().to_temp_utf16()!!, null)) return true;
|
||||
switch (win32::win32_GetLastError())
|
||||
{
|
||||
case win32::ERROR_ACCESS_DENIED:
|
||||
return IoError.NO_PERMISSION?;
|
||||
case win32::ERROR_DISK_FULL:
|
||||
return IoError.OUT_OF_SPACE?;
|
||||
case win32::ERROR_ALREADY_EXISTS:
|
||||
return false;
|
||||
case win32::ERROR_PATH_NOT_FOUND:
|
||||
return IoError.FILE_NOT_FOUND?;
|
||||
default:
|
||||
return IoError.GENERAL_ERROR?;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
$default:
|
||||
|
||||
fn bool! native_mkdir(Path path, MkdirPermissions permissions)
|
||||
{
|
||||
unreachable("'mkdir' not available");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$endswitch
|
||||
61
lib/std/io/os/rmdir.c3
Normal file
61
lib/std/io/os/rmdir.c3
Normal file
@@ -0,0 +1,61 @@
|
||||
module std::io::os;
|
||||
import libc;
|
||||
import std::io::path;
|
||||
import std::os::win32;
|
||||
import std::os::posix;
|
||||
|
||||
$switch
|
||||
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_posix():
|
||||
|
||||
macro bool! native_rmdir(Path path)
|
||||
{
|
||||
if (!posix::rmdir(path.as_zstr())) return true;
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EBUSY: return IoError.BUSY?;
|
||||
case errno::EACCES:
|
||||
case errno::EPERM:
|
||||
case errno::EROFS:
|
||||
case errno::EFAULT: return IoError.NO_PERMISSION?;
|
||||
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG?;
|
||||
case errno::ENOTDIR:
|
||||
case errno::ENOENT: return false;
|
||||
case errno::ENOTEMPTY: return IoError.DIR_NOT_EMPTY?;
|
||||
case errno::ELOOP: return IoError.SYMLINK_FAILED?;
|
||||
default: return IoError.GENERAL_ERROR?;
|
||||
}
|
||||
}
|
||||
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_win32():
|
||||
|
||||
macro bool! native_rmdir(Path path)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
if (win32::win32_RemoveDirectoryW(path.as_str().to_temp_utf16()!!)) return true;
|
||||
switch (win32::win32_GetLastError())
|
||||
{
|
||||
case win32::ERROR_ACCESS_DENIED:
|
||||
return IoError.NO_PERMISSION?;
|
||||
case win32::ERROR_CURRENT_DIRECTORY:
|
||||
return IoError.BUSY?;
|
||||
case win32::ERROR_DIR_NOT_EMPTY:
|
||||
return IoError.DIR_NOT_EMPTY?;
|
||||
case win32::ERROR_DIRECTORY:
|
||||
case win32::ERROR_PATH_NOT_FOUND:
|
||||
return false;
|
||||
default:
|
||||
return IoError.GENERAL_ERROR?;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
$default:
|
||||
|
||||
fn bool! native_rmdir(Path path)
|
||||
{
|
||||
unreachable("'rmdir' not available");
|
||||
}
|
||||
|
||||
$endswitch
|
||||
413
lib/std/io/path.c3
Normal file
413
lib/std/io/path.c3
Normal file
@@ -0,0 +1,413 @@
|
||||
module std::io::path;
|
||||
import std::collections::list;
|
||||
|
||||
const PathEnv DEFAULT_PATH_ENV = env::os_is_win32() ? PathEnv.WIN32 : PathEnv.POSIX;
|
||||
const char PREFERRED_SEPARATOR_WIN32 = '\\';
|
||||
const char PREFERRED_SEPARATOR_POSIX = '/';
|
||||
const char PREFERRED_SEPARATOR = env::os_is_win32() ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX;
|
||||
|
||||
def PathList = List<Path>;
|
||||
|
||||
fault PathResult
|
||||
{
|
||||
INVALID_PATH,
|
||||
NO_PARENT,
|
||||
}
|
||||
|
||||
struct Path
|
||||
{
|
||||
String path_string;
|
||||
PathEnv env;
|
||||
}
|
||||
|
||||
enum PathEnv
|
||||
{
|
||||
WIN32,
|
||||
POSIX
|
||||
}
|
||||
|
||||
|
||||
fn Path! getcwd(Allocator* using = mem::heap())
|
||||
{
|
||||
@stack_mem(256; Allocator* mem)
|
||||
{
|
||||
return new(os::getcwd(mem), using);
|
||||
};
|
||||
}
|
||||
|
||||
fn bool is_dir(Path path) => os::native_is_dir(path.as_str());
|
||||
fn bool is_file(Path path) => os::native_is_file(path.as_str());
|
||||
fn usz! file_size(Path path) => os::native_file_size(path.as_str());
|
||||
fn bool exists(Path path) => os::native_file_or_dir_exists(path.as_str());
|
||||
fn Path! tgetcwd() => getcwd(mem::temp()) @inline;
|
||||
fn void! chdir(Path path) => os::native_chdir(path) @inline;
|
||||
fn Path! temp_directory(Allocator* using = mem::heap()) => os::native_temp_directory(using);
|
||||
|
||||
macro bool is_separator(char c, PathEnv path_env = DEFAULT_PATH_ENV)
|
||||
{
|
||||
return c == '/' || (c == '\\' && path_env == PathEnv.WIN32);
|
||||
}
|
||||
|
||||
macro bool is_posix_separator(char c)
|
||||
{
|
||||
return c == '/' || c == '\\';
|
||||
}
|
||||
|
||||
macro bool is_win32_separator(char c)
|
||||
{
|
||||
return c == '/' || c == '\\';
|
||||
}
|
||||
|
||||
fn Path[]! ls(Path path)
|
||||
{
|
||||
unreachable();
|
||||
}
|
||||
|
||||
enum MkdirPermissions
|
||||
{
|
||||
NORMAL,
|
||||
USER_ONLY,
|
||||
USER_AND_ADMIN
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn bool! mkdir(Path path, bool recursive = false, MkdirPermissions permissions = NORMAL)
|
||||
{
|
||||
if (!path.path_string.len) return PathResult.INVALID_PATH?;
|
||||
if (is_dir(path)) return false;
|
||||
if (exists(path)) return IoError.FILE_NOT_DIR?;
|
||||
|
||||
if (recursive)
|
||||
{
|
||||
if (try parent = path.parent()) mkdir(parent, true, permissions)!;
|
||||
}
|
||||
if (!is_dir(path.parent()) ?? false) return IoError.CANNOT_READ_DIR?;
|
||||
|
||||
return os::native_mkdir(path, permissions);
|
||||
}
|
||||
|
||||
fn bool! rmdir(Path path)
|
||||
{
|
||||
if (!path.path_string.len) return PathResult.INVALID_PATH?;
|
||||
return os::native_rmdir(path);
|
||||
}
|
||||
|
||||
fn void! rmtree(Path path)
|
||||
{
|
||||
if (!path.path_string.len) return PathResult.INVALID_PATH?;
|
||||
$if $defined(os::native_rmtree):
|
||||
return os::native_rmtree(path);
|
||||
$else
|
||||
assert(false, "rmtree is not available");
|
||||
$endif
|
||||
}
|
||||
|
||||
fn Path! new(String path, Allocator* using = mem::heap(), PathEnv path_env = DEFAULT_PATH_ENV)
|
||||
{
|
||||
return { normalize(path.copy(using), path_env), path_env };
|
||||
}
|
||||
|
||||
fn Path! new_windows(String path, Allocator* using = mem::heap())
|
||||
{
|
||||
return new(path, using, WIN32);
|
||||
}
|
||||
|
||||
fn Path! new_posix(String path, Allocator* using = mem::heap())
|
||||
{
|
||||
return new(path, using, POSIX);
|
||||
}
|
||||
|
||||
fn bool Path.equals(Path p1, Path p2)
|
||||
{
|
||||
return p1.env == p2.env && p1.path_string == p2.path_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the string to the current path.
|
||||
*
|
||||
* @param [in] path
|
||||
* @param [in] filename
|
||||
* @ensure return.path_string.len >= path.path_string.len
|
||||
**/
|
||||
fn Path! Path.append(Path path, String filename, Allocator* using = mem::heap())
|
||||
{
|
||||
if (!path.path_string.len) return new(filename, using, path.env)!;
|
||||
assert(!is_separator(path.path_string[^1], path.env));
|
||||
|
||||
@stack_mem(256; Allocator* mem)
|
||||
{
|
||||
DString dstr = dstring::new_with_capacity(path.path_string.len + 1 + filename.len, .using = mem);
|
||||
dstr.append(path.path_string);
|
||||
dstr.append(PREFERRED_SEPARATOR);
|
||||
dstr.append(filename);
|
||||
return { normalize(dstr.copy_str(using), path.env), path.env };
|
||||
};
|
||||
}
|
||||
|
||||
fn Path! Path.tappend(Path path, String filename) => path.append(filename, mem::temp());
|
||||
|
||||
fn usz Path.start_of_base_name(Path path) @local
|
||||
{
|
||||
String path_str = path.path_string;
|
||||
if (!path_str.len) return 0;
|
||||
if (path.env == PathEnv.WIN32)
|
||||
{
|
||||
return path_str.rindex_of(`\`) + 1 ?? volume_name_len(path_str, path.env)!!;
|
||||
}
|
||||
return path_str.rindex_of("/") + 1 ?? 0;
|
||||
}
|
||||
|
||||
fn String Path.basename(Path path)
|
||||
{
|
||||
usz basename_start = path.start_of_base_name();
|
||||
String path_str = path.path_string;
|
||||
if (basename_start == path_str.len) return "";
|
||||
return path_str[basename_start..];
|
||||
}
|
||||
|
||||
fn String Path.dirname(Path path)
|
||||
{
|
||||
usz basename_start = path.start_of_base_name();
|
||||
String path_str = path.path_string;
|
||||
if (basename_start == 0) return "";
|
||||
usz start = volume_name_len(path_str, path.env)!!;
|
||||
if (basename_start <= start + 1) return path_str[:basename_start];
|
||||
return path_str[:basename_start - 1];
|
||||
}
|
||||
|
||||
fn String! Path.extension(Path path)
|
||||
{
|
||||
String basename = path.basename();
|
||||
usz index = basename.rindex_of(".")!;
|
||||
// Plain ".foo" does not have an
|
||||
if (index == 0) return SearchResult.MISSING?;
|
||||
if (index == basename.len) return "";
|
||||
return basename[index + 1..];
|
||||
}
|
||||
|
||||
fn String Path.volume_name(Path path)
|
||||
{
|
||||
usz len = volume_name_len(path.as_str(), path.env)!!;
|
||||
if (!len) return "";
|
||||
return path.path_string[:len];
|
||||
}
|
||||
|
||||
fn usz! volume_name_len(String path, PathEnv path_env) @local
|
||||
{
|
||||
usz len = path.len;
|
||||
if (len < 2 || path_env != PathEnv.WIN32) return 0;
|
||||
switch (path[0])
|
||||
{
|
||||
case '\\':
|
||||
// "\\" paths.. must be longer than 2
|
||||
if (len == 2) return 0;
|
||||
int count = 1;
|
||||
while (count < len && path[count] == '\\') count++;
|
||||
// Not 2 => folded paths
|
||||
if (count != 2) return 0;
|
||||
// Check that we have a name followed by '/'
|
||||
for (usz i = 2; i < len; i++)
|
||||
{
|
||||
char c = path[i];
|
||||
if (is_win32_separator(c)) return i;
|
||||
if (is_reserved_win32_path_char(c)) return PathResult.INVALID_PATH?;
|
||||
}
|
||||
return PathResult.INVALID_PATH?;
|
||||
case 'A'..'Z':
|
||||
case 'a'..'z':
|
||||
return path[1] == ':' ? 2 : 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
fn Path! Path.parent(Path path)
|
||||
{
|
||||
if (path.path_string.len == 1 && is_separator(path.path_string[0], path.env)) return PathResult.NO_PARENT?;
|
||||
foreach_r(i, c : path.path_string)
|
||||
{
|
||||
if (is_separator(c, path.env))
|
||||
{
|
||||
return { path.path_string[:i], path.env };
|
||||
}
|
||||
}
|
||||
return PathResult.NO_PARENT?;
|
||||
}
|
||||
|
||||
fn String! normalize(String path_str, PathEnv path_env = DEFAULT_PATH_ENV)
|
||||
{
|
||||
if (!path_str.len) return path_str;
|
||||
usz path_start = volume_name_len(path_str, path_env)!;
|
||||
usz path_len = path_str.len;
|
||||
if (path_start == path_len) return path_str;
|
||||
char path_separator = path_env == PathEnv.WIN32 ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX;
|
||||
usz len = path_start;
|
||||
bool has_root = is_separator(path_str[path_start], path_env);
|
||||
if (has_root)
|
||||
{
|
||||
path_str[len++] = path_separator;
|
||||
path_start++;
|
||||
}
|
||||
// It is safe to write it as true, since we already dealt with /foo.
|
||||
// This allows us to avoid checking whether it is the start of the path.
|
||||
bool previous_was_separator = true;
|
||||
|
||||
for (usz i = path_start; i < path_len; i++)
|
||||
{
|
||||
char c = path_str[i];
|
||||
// Fold foo///bar into foo/bar
|
||||
if (is_separator(c, path_env))
|
||||
{
|
||||
// Fold //
|
||||
if (previous_was_separator) continue;
|
||||
|
||||
// New /, so mark and rewrite
|
||||
path_str.ptr[len++] = path_separator;
|
||||
previous_was_separator = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// The rest are names of the path elements, so check that the
|
||||
// characters are valid.
|
||||
if (is_reserved_path_char(c, path_env)) return PathResult.INVALID_PATH?;
|
||||
|
||||
// If we have '.' after a separator
|
||||
if (c == '.' && previous_was_separator)
|
||||
{
|
||||
// Get the number of dots until next separator, expecting 1 or 2
|
||||
bool is_last = i == path_len - 1;
|
||||
int dots = 1;
|
||||
if (!is_last && path_str[i + 1] == '.')
|
||||
{
|
||||
dots = 2;
|
||||
is_last = i == path_len - 2;
|
||||
if (!is_last && !is_separator(path_str[i + 2], path_env))
|
||||
{
|
||||
dots = 0;
|
||||
}
|
||||
}
|
||||
switch (dots)
|
||||
{
|
||||
case 1:
|
||||
// /./abc -> skip to /./abc
|
||||
// ^ ^
|
||||
i++;
|
||||
continue;
|
||||
case 2:
|
||||
// This is an error: /a/../..
|
||||
if (len == path_start && has_root) return PathResult.INVALID_PATH?;
|
||||
|
||||
// If this .. at the start, or after ../? If so, we just copy ..
|
||||
if (len == path_start ||
|
||||
(len - path_start >= 3 && path_str[len - 1] == path_separator
|
||||
&& path_str[len - 3] == '.' && path_str[len - 3] == '.' &&
|
||||
(len - 3 == 0 || path_str[len - 4] == path_separator)))
|
||||
{
|
||||
if (i != len)
|
||||
{
|
||||
path_str[len] = '.';
|
||||
path_str[len + 1] = '.';
|
||||
}
|
||||
len += 2;
|
||||
if (len < path_len) path_str[len++] = path_separator;
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
// Step back, now looking at '/' abc/def/. -> abc/def/
|
||||
len--;
|
||||
// Step back until finding a separator or the start.
|
||||
while (len > path_start && !is_separator(path_str[len - 1], path_env))
|
||||
{
|
||||
len--;
|
||||
}
|
||||
// Reading, we go from /../abc to /../abc
|
||||
// ^ ^
|
||||
i += 2;
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
if (i != len) path_str[len] = c;
|
||||
previous_was_separator = false;
|
||||
len++;
|
||||
}
|
||||
if (len > path_start + 1 && is_separator(path_str[len - 1], path_env)) len--;
|
||||
path_str.ptr[len] = 0;
|
||||
return path_str[:len];
|
||||
}
|
||||
|
||||
fn ZString Path.as_zstr(Path path) => (ZString)path.path_string.ptr;
|
||||
|
||||
fn String Path.root_directory(Path path)
|
||||
{
|
||||
String path_str = path.as_str();
|
||||
usz len = path_str.len;
|
||||
if (!len) return "";
|
||||
if (path.env == PathEnv.WIN32)
|
||||
{
|
||||
usz root_len = volume_name_len(path_str, path.env)!!;
|
||||
if (root_len == len || !is_win32_separator(path_str[root_len])) return "";
|
||||
return path_str[root_len..root_len];
|
||||
}
|
||||
if (!is_posix_separator(path_str[0])) return "";
|
||||
for (usz i = 1; i < len; i++)
|
||||
{
|
||||
if (is_posix_separator(path_str[i]))
|
||||
{
|
||||
return path_str[:i];
|
||||
}
|
||||
}
|
||||
return path_str;
|
||||
}
|
||||
|
||||
|
||||
fn String Path.as_str(Path path)
|
||||
{
|
||||
return path.path_string;
|
||||
}
|
||||
|
||||
|
||||
fn bool Path.has_suffix(Path path, String str)
|
||||
{
|
||||
return path.as_str().ends_with(str);
|
||||
}
|
||||
|
||||
|
||||
fn void Path.free(Path path)
|
||||
{
|
||||
free(path.path_string.ptr);
|
||||
}
|
||||
|
||||
|
||||
const bool[256] RESERVED_PATH_CHAR_POSIX = {
|
||||
[0] = true,
|
||||
['/'] = true,
|
||||
};
|
||||
const bool[256] RESERVED_PATH_CHAR_WIN32 = {
|
||||
[0..31] = true,
|
||||
['>'] = true,
|
||||
['<'] = true,
|
||||
[':'] = true,
|
||||
['\"'] = true,
|
||||
['/'] = true,
|
||||
['\\'] = true,
|
||||
['|'] = true,
|
||||
['?'] = true,
|
||||
['*'] = true,
|
||||
};
|
||||
|
||||
macro bool is_reserved_win32_path_char(char c)
|
||||
{
|
||||
return RESERVED_PATH_CHAR_WIN32[c];
|
||||
}
|
||||
|
||||
macro bool is_reserved_path_char(char c, PathEnv path_env = DEFAULT_PATH_ENV)
|
||||
{
|
||||
return path_env == PathEnv.WIN32
|
||||
? RESERVED_PATH_CHAR_WIN32[c]
|
||||
: RESERVED_PATH_CHAR_POSIX[c];
|
||||
}
|
||||
|
||||
80
lib/std/io/stream/bytereader.c3
Normal file
80
lib/std/io/stream/bytereader.c3
Normal file
@@ -0,0 +1,80 @@
|
||||
module std::io;
|
||||
import std::math;
|
||||
|
||||
struct ByteReader
|
||||
{
|
||||
char[] bytes;
|
||||
usz index;
|
||||
}
|
||||
|
||||
fn void ByteReader.init(ByteReader* reader, char[] bytes)
|
||||
{
|
||||
*reader = { .bytes = bytes };
|
||||
}
|
||||
|
||||
fn Stream ByteReader.as_stream(ByteReader* reader)
|
||||
{
|
||||
return { .fns = &bytereader_interface, .data = reader };
|
||||
}
|
||||
|
||||
fn usz! ByteReader.read(ByteReader* reader, char[] bytes)
|
||||
{
|
||||
if (reader.index >= reader.bytes.len) return IoError.EOF?;
|
||||
usz len = math::min(reader.bytes.len - reader.index, bytes.len);
|
||||
if (len == 0) return 0;
|
||||
mem::copy(bytes.ptr, &reader.bytes[reader.index], len);
|
||||
reader.index += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
fn char! ByteReader.read_byte(ByteReader* reader)
|
||||
{
|
||||
if (reader.index >= reader.bytes.len) return IoError.EOF?;
|
||||
return reader.bytes[reader.index++];
|
||||
}
|
||||
|
||||
fn void! ByteReader.pushback_byte(ByteReader* reader)
|
||||
{
|
||||
if (!reader.index) return IoError.INVALID_PUSHBACK?;
|
||||
reader.index--;
|
||||
}
|
||||
|
||||
fn usz! ByteReader.seek(ByteReader* reader, isz offset, Seek seek)
|
||||
{
|
||||
isz new_index;
|
||||
switch (seek)
|
||||
{
|
||||
case SET: new_index = offset;
|
||||
case CURSOR: new_index = reader.index + offset;
|
||||
case END: new_index = reader.bytes.len + offset;
|
||||
}
|
||||
if (new_index < 0) return IoError.INVALID_POSITION?;
|
||||
reader.index = new_index;
|
||||
return new_index;
|
||||
}
|
||||
|
||||
fn usz! ByteReader.write_stream(ByteReader* reader, Stream* writer)
|
||||
{
|
||||
if (reader.index >= reader.bytes.len) return 0;
|
||||
usz written = writer.write(reader.bytes[reader.index..])!;
|
||||
reader.index += written;
|
||||
assert(reader.index <= reader.bytes.len);
|
||||
return written;
|
||||
}
|
||||
|
||||
fn usz ByteReader.available(ByteReader* reader)
|
||||
{
|
||||
return math::max((isz)0, (isz)reader.bytes.len - reader.index);
|
||||
}
|
||||
|
||||
StreamInterface bytereader_interface = {
|
||||
.len_fn = fn (s) => ((ByteReader*)s.data).bytes.len,
|
||||
.read_fn = fn (s, char[] bytes) => ((ByteReader*)s.data).read(bytes) @inline,
|
||||
.read_byte_fn = fn (s) => ((ByteReader*)s.data).read_byte() @inline,
|
||||
.pushback_byte_fn = fn (s) => ((ByteReader*)s.data).pushback_byte() @inline,
|
||||
.seek_fn = fn (s, offset, seek) => ((ByteReader*)s.data).seek(offset, seek) @inline,
|
||||
.write_stream_fn = fn (s, writer) => ((ByteReader*)s.data).write_stream(writer) @inline,
|
||||
.available_fn = fn (s) => ((ByteReader*)s.data).available() @inline,
|
||||
};
|
||||
|
||||
|
||||
119
lib/std/io/stream/bytewriter.c3
Normal file
119
lib/std/io/stream/bytewriter.c3
Normal file
@@ -0,0 +1,119 @@
|
||||
module std::io;
|
||||
|
||||
struct ByteWriter
|
||||
{
|
||||
char[] bytes;
|
||||
usz index;
|
||||
Allocator* allocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] writer
|
||||
* @param [&in] using
|
||||
* @require writer.bytes.len == 0 "Init may not run on on already initialized data"
|
||||
* @ensure using != null, index == 0
|
||||
**/
|
||||
fn void ByteWriter.init(ByteWriter* writer, Allocator* using = mem::heap())
|
||||
{
|
||||
*writer = { .bytes = {}, .allocator = using };
|
||||
}
|
||||
|
||||
fn void ByteWriter.init_buffer(ByteWriter* writer, char[] data)
|
||||
{
|
||||
*writer = { .bytes = data, .allocator = null };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] writer
|
||||
* @require writer.bytes.len == 0 "Init may not run on on already initialized data"
|
||||
**/
|
||||
fn void ByteWriter.tinit(ByteWriter* writer)
|
||||
{
|
||||
*writer = { .bytes = {}, .allocator = mem::temp() };
|
||||
}
|
||||
|
||||
fn Stream ByteWriter.as_stream(ByteWriter* writer)
|
||||
{
|
||||
return { .fns = &bytewriter_interface, .data = writer };
|
||||
}
|
||||
|
||||
fn void ByteWriter.destroy(ByteWriter* writer)
|
||||
{
|
||||
if (!writer.allocator) return;
|
||||
if (void* ptr = writer.bytes.ptr) free(ptr, .using = writer.allocator);
|
||||
*writer = { };
|
||||
}
|
||||
|
||||
fn String ByteWriter.as_str(ByteWriter* writer)
|
||||
{
|
||||
return (String)writer.bytes[:writer.index];
|
||||
}
|
||||
|
||||
fn void! ByteWriter.ensure_capacity(ByteWriter* writer, usz len) @inline
|
||||
{
|
||||
if (writer.bytes.len > len) return;
|
||||
if (!writer.allocator) return IoError.OUT_OF_SPACE?;
|
||||
if (len < 16) len = 16;
|
||||
usz new_capacity = math::next_power_of_2(len);
|
||||
char* new_ptr = realloc_checked(writer.bytes.ptr, new_capacity, .using = writer.allocator)!;
|
||||
writer.bytes = new_ptr[:new_capacity];
|
||||
}
|
||||
|
||||
fn usz! ByteWriter.write(ByteWriter* writer, char[] bytes)
|
||||
{
|
||||
writer.ensure_capacity(writer.index + bytes.len)!;
|
||||
mem::copy(&writer.bytes[writer.index], bytes.ptr, bytes.len);
|
||||
writer.index += bytes.len;
|
||||
return bytes.len;
|
||||
}
|
||||
|
||||
fn void! ByteWriter.write_byte(ByteWriter* writer, char c)
|
||||
{
|
||||
writer.ensure_capacity(writer.index + 1)!;
|
||||
writer.bytes[writer.index++] = c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] writer
|
||||
* @param [&inout] reader
|
||||
**/
|
||||
fn usz! ByteWriter.read_from(ByteWriter* writer, Stream* reader)
|
||||
{
|
||||
if (reader.supports_available())
|
||||
{
|
||||
usz total_read = 0;
|
||||
while (usz available = reader.available()!)
|
||||
{
|
||||
writer.ensure_capacity(writer.index + available)!;
|
||||
usz len = reader.read(writer.bytes[writer.index..])!;
|
||||
total_read += len;
|
||||
writer.index += len;
|
||||
}
|
||||
return total_read;
|
||||
}
|
||||
usz total_read = 0;
|
||||
while (true)
|
||||
{
|
||||
// See how much we can read.
|
||||
usz len_to_read = writer.bytes.len - writer.index;
|
||||
// Less than 16 bytes? Double the capacity
|
||||
if (len_to_read < 16)
|
||||
{
|
||||
writer.ensure_capacity(writer.bytes.len * 2)!;
|
||||
}
|
||||
// Read into the rest of the buffer
|
||||
usz read = reader.read(writer.bytes[writer.index..])!;
|
||||
writer.index += read;
|
||||
// Ok, we reached the end.
|
||||
if (read < len_to_read) return total_read;
|
||||
// Otherwise go another round
|
||||
}
|
||||
}
|
||||
|
||||
StreamInterface bytewriter_interface = {
|
||||
.destroy_fn = fn (s) => ((ByteWriter*)s.data).destroy(),
|
||||
.len_fn = fn (s) => ((ByteWriter*)s.data).bytes.len,
|
||||
.write_fn = fn (s, char[] bytes) => ((ByteWriter*)s.data).write(bytes),
|
||||
.write_byte_fn = fn (s, char c) => ((ByteWriter*)s.data).write_byte(c),
|
||||
.read_stream_fn = fn (s, reader) => ((ByteWriter*)s.data).read_from(reader),
|
||||
};
|
||||
14
lib/std/io/stream/dstringwriter.c3
Normal file
14
lib/std/io/stream/dstringwriter.c3
Normal file
@@ -0,0 +1,14 @@
|
||||
module std::io;
|
||||
|
||||
fn Stream DString.as_stream(DString* dstring)
|
||||
{
|
||||
return { .fns = &dstring_interface, .data = dstring };
|
||||
}
|
||||
|
||||
StreamInterface dstring_interface = {
|
||||
.destroy_fn = fn (s) => ((DString*)s.data).free(),
|
||||
.len_fn = fn (s) => ((DString*)s.data).len(),
|
||||
.write_fn = fn (s, char[] bytes) { ((DString*)s.data).append_chars((String)bytes); return bytes.len; },
|
||||
.write_byte_fn = fn (s, char c) => ((DString*)s.data).append_char(c),
|
||||
.read_stream_fn = fn (s, reader) => ((DString*)s.data).read_from_stream(reader),
|
||||
};
|
||||
18
lib/std/io/stream/filestream.c3
Normal file
18
lib/std/io/stream/filestream.c3
Normal file
@@ -0,0 +1,18 @@
|
||||
module std::io;
|
||||
|
||||
fn Stream File.as_stream(File* file)
|
||||
{
|
||||
return { .fns = &filestream_interface, .data = file };
|
||||
}
|
||||
|
||||
StreamInterface filestream_interface = {
|
||||
.close_fn = fn (s) => ((File*)s.data).close(),
|
||||
.seek_fn = fn (s, offset, seek) => ((File*)s.data).seek(offset, seek) @inline,
|
||||
.read_fn = fn (s, char[] bytes) => ((File*)s.data).read(bytes) @inline,
|
||||
.write_fn = fn (s, char[] bytes) => ((File*)s.data).write(bytes) @inline,
|
||||
.write_byte_fn = fn (s, char c) => ((File*)s.data).putc(c) @inline,
|
||||
.read_byte_fn = fn (s) => ((File*)s.data).getc() @inline,
|
||||
.flush_fn = fn (s) => ((File*)s.data).flush() @inline,
|
||||
};
|
||||
|
||||
|
||||
640
lib/std/libc/libc.c3
Normal file
640
lib/std/libc/libc.c3
Normal file
@@ -0,0 +1,640 @@
|
||||
// Copyright (c) 2021 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 libc;
|
||||
|
||||
|
||||
// Constants need to be per os/arch
|
||||
const int EXIT_FAILURE = 1;
|
||||
const int EXIT_SUCCESS = 0;
|
||||
const int RAND_MAX = 0x7fffffff;
|
||||
|
||||
struct DivResult
|
||||
{
|
||||
int quot;
|
||||
int rem;
|
||||
}
|
||||
|
||||
struct LongDivResult
|
||||
{
|
||||
long quot;
|
||||
long rem;
|
||||
}
|
||||
|
||||
fn Errno errno()
|
||||
{
|
||||
return (Errno)os::errno();
|
||||
}
|
||||
|
||||
fn void errno_set(Errno e)
|
||||
{
|
||||
os::errno_set((int)e);
|
||||
}
|
||||
|
||||
def TerminateFunction = fn void();
|
||||
def CompareFunction = fn int(void*, void*);
|
||||
def JmpBuf = uptr[$$JMP_BUF_SIZE];
|
||||
|
||||
$if env::COMPILER_LIBC_AVAILABLE:
|
||||
|
||||
extern fn double atof(char* str);
|
||||
extern fn int atoi(char* str);
|
||||
extern fn CLongLong atoll(char* str);
|
||||
extern fn double strtod(char* str, char** endptr);
|
||||
extern fn CLong strtol(char* str, char** endptr, int base);
|
||||
extern fn CULong stroul(char* str, char** endptr, int base);
|
||||
extern fn void abort();
|
||||
extern fn void atexit(TerminateFunction f);
|
||||
extern fn void exit(int status);
|
||||
extern fn ZString getenv(ZString name);
|
||||
extern fn int setenv(ZString name, ZString value, int overwrite);
|
||||
extern fn int unsetenv(ZString name);
|
||||
extern fn int system(char* str);
|
||||
extern fn void bsearch(void* key, void *base, usz items, usz size, CompareFunction compare);
|
||||
extern fn void qsort(void* base, usz items, usz size, CompareFunction compare);
|
||||
extern fn DivResult div(int numer, int denom);
|
||||
extern fn long labs(long x);
|
||||
extern fn LongDivResult ldiv(long number, long denom);
|
||||
extern fn int rand();
|
||||
extern fn void srand(uint seed);
|
||||
|
||||
extern fn void longjmp(JmpBuf* buffer, CInt value);
|
||||
$if env::os_is_win32():
|
||||
// TODO win32 aarch64
|
||||
extern fn CInt _setjmp(void* frameptr, JmpBuf* buffer);
|
||||
macro CInt setjmp(JmpBuf* buffer) => _setjmp($$frameaddress(), buffer);
|
||||
$else
|
||||
extern fn CInt setjmp(JmpBuf* buffer);
|
||||
$endif
|
||||
// MB functions omitted
|
||||
|
||||
// string
|
||||
extern fn void* memchr(void* str, int c, usz n);
|
||||
extern fn int memcmp(void* str1, void* str2, usz n);
|
||||
extern fn void* memcpy(void* dest, void* src, usz n);
|
||||
extern fn void* memmove(void* dest, void* src, usz n);
|
||||
extern fn void* memset(void* dest, CInt value, usz n);
|
||||
extern fn char* strcat(char* dest, char* src);
|
||||
extern fn char* strncat(char* dest, char* src, usz n);
|
||||
extern fn char* strchr(char* str, int c);
|
||||
extern fn int strcmp(char* str1, char* str2);
|
||||
extern fn int strncmp(char* str1, char* str2, usz n);
|
||||
extern fn int strcoll(char* str1, char* str2);
|
||||
extern fn char* strcpy(char* dst, char* src);
|
||||
extern fn char* strncpy(char* dst, char* src, usz n);
|
||||
extern fn usz strcspn(char* str1, char* str2);
|
||||
extern fn char* strerror(int errn);
|
||||
extern fn usz strlen(char* str);
|
||||
extern fn char* strpbrk(char* str1, char* str2);
|
||||
extern fn usz strspn(char* str1, char* str2);
|
||||
extern fn char* strstr(char* haystack, char* needle);
|
||||
extern fn char* strtok(char* str, char* delim);
|
||||
extern fn usz strxfrm(char* dest, char* src, usz n);
|
||||
|
||||
// malloc
|
||||
extern fn void* malloc(usz size);
|
||||
extern fn void* calloc(usz count, usz size);
|
||||
extern fn void* free(void*);
|
||||
extern fn void* realloc(void* ptr, usz size);
|
||||
|
||||
$else
|
||||
|
||||
fn void longjmp(JmpBuf* buffer, CInt value) @weak @extern("longjmp") @nostrip
|
||||
{
|
||||
unreachable("longjmp unavailable");
|
||||
}
|
||||
|
||||
fn CInt setjmp(JmpBuf* buffer) @weak @extern("setjmp") @nostrip
|
||||
{
|
||||
unreachable("setjmp unavailable");
|
||||
}
|
||||
|
||||
fn void* malloc(usz size) @weak @extern("malloc") @nostrip
|
||||
{
|
||||
unreachable("malloc unavailable");
|
||||
}
|
||||
fn void* calloc(usz count, usz size) @weak @extern("calloc") @nostrip
|
||||
{
|
||||
unreachable("calloc unavailable");
|
||||
}
|
||||
fn void* free(void*) @weak @extern("free")
|
||||
{
|
||||
unreachable("free unavailable");
|
||||
}
|
||||
|
||||
fn void* realloc(void* ptr, usz size) @weak @extern("realloc") @nostrip
|
||||
{
|
||||
unreachable("realloc unavailable");
|
||||
}
|
||||
|
||||
fn void* memcpy(void* dest, void* src, usz n) @weak @extern("memcpy") @nostrip
|
||||
{
|
||||
for (usz i = 0; i < n; i++) ((char*)dest)[i] = ((char*)src)[i];
|
||||
return dest;
|
||||
}
|
||||
|
||||
fn void* memmove(void* dest, void* src, usz n) @weak @extern("memmove") @nostrip
|
||||
{
|
||||
return memcpy(dest, src, n) @inline;
|
||||
}
|
||||
|
||||
fn void* memset(void* dest, CInt value, usz n) @weak @extern("memset") @nostrip
|
||||
{
|
||||
for (usz i = 0; i < n; i++) ((char*)dest)[i] = (char)value;
|
||||
return dest;
|
||||
}
|
||||
|
||||
$endif
|
||||
|
||||
// stdio
|
||||
|
||||
def Fpos = long;
|
||||
def CFile = void*;
|
||||
|
||||
$switch
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == LINUX:
|
||||
extern CFile __stdin @extern("stdin");
|
||||
extern CFile __stdout @extern("stdout");
|
||||
extern CFile __stderr @extern("stderr");
|
||||
extern fn usz malloc_usable_size(void* ptr);
|
||||
macro usz malloc_size(void* ptr) { return malloc_usable_size(ptr); }
|
||||
extern fn void* aligned_alloc(usz align, usz size);
|
||||
macro CFile stdin() { return __stdin; }
|
||||
macro CFile stdout() { return __stdout; }
|
||||
macro CFile stderr() { return __stderr; }
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_darwin():
|
||||
extern CFile __stdinp;
|
||||
extern CFile __stdoutp;
|
||||
extern CFile __stderrp;
|
||||
extern fn usz malloc_size(void* ptr);
|
||||
extern fn void* aligned_alloc(usz align, usz size);
|
||||
macro CFile stdin() { return __stdinp; }
|
||||
macro CFile stdout() { return __stdoutp; }
|
||||
macro CFile stderr() { return __stderrp; }
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_win32():
|
||||
extern fn CFile __acrt_iob_func(CInt c);
|
||||
extern fn usz _msize(void* ptr);
|
||||
macro usz malloc_size(void* ptr) { return _msize(ptr); }
|
||||
macro CFile stdin() { return __acrt_iob_func(0); }
|
||||
macro CFile stdout() { return __acrt_iob_func(1); }
|
||||
macro CFile stderr() { return __acrt_iob_func(2); }
|
||||
$default:
|
||||
macro CFile stdin() { return (CFile*)(uptr)0; }
|
||||
macro CFile stdout() { return (CFile*)(uptr)1; }
|
||||
macro CFile stderr() { return (CFile*)(uptr)2; }
|
||||
$endswitch
|
||||
|
||||
const HAS_MALLOC_SIZE =
|
||||
env::OS_TYPE == LINUX
|
||||
|| env::os_is_win32()
|
||||
|| env::os_is_darwin();
|
||||
|
||||
// The following needs to be set per arch+os
|
||||
// For now I have simply pulled the defaults from MacOS
|
||||
const int SEEK_SET = 0;
|
||||
const int SEEK_CUR = 1;
|
||||
const int SEEK_END = 2;
|
||||
const int _IOFBF = 0; // Fully buffered
|
||||
const int _IOLBF = 1; // Line buffered
|
||||
const int _IONBF = 2; // Unbuffered
|
||||
const int BUFSIZ = 1024;
|
||||
const int EOF = -1;
|
||||
const int FOPEN_MAX = 20;
|
||||
const int FILENAME_MAX = 1024;
|
||||
|
||||
def Errno = distinct CInt;
|
||||
def SeekIndex = CLong;
|
||||
|
||||
$if env::COMPILER_LIBC_AVAILABLE:
|
||||
|
||||
extern fn int fclose(CFile stream);
|
||||
extern fn void clearerr(CFile stream);
|
||||
extern fn int feof(CFile stream);
|
||||
extern fn int ferror(CFile stream);
|
||||
extern fn int fflush(CFile stream);
|
||||
extern fn int fgetpos(CFile stream, Fpos* pos);
|
||||
extern fn CFile fopen(ZString filename, ZString mode);
|
||||
extern fn usz fread(void* ptr, usz size, usz nmemb, CFile stream);
|
||||
extern fn CFile freopen(ZString filename, ZString mode, CFile stream);
|
||||
extern fn CFile fmemopen(void* ptr, usz size, ZString mode);
|
||||
extern fn int fseek(CFile stream, SeekIndex offset, int whence);
|
||||
extern fn int fsetpos(CFile stream, Fpos* pos);
|
||||
extern fn SeekIndex ftell(CFile stream);
|
||||
extern fn usz fwrite(void* ptr, usz size, usz nmemb, CFile stream);
|
||||
extern fn int remove(char* filename);
|
||||
extern fn int rename(char* old_name, char* new_name);
|
||||
extern fn void rewind(CFile stream);
|
||||
extern fn void setbuf(CFile stream, char* buffer);
|
||||
extern fn void setvbuf(CFile stream, char* buffer, int mode, usz size);
|
||||
extern fn CFile tmpnam(char* str);
|
||||
extern fn int fprintf(CFile stream, char* format, ...);
|
||||
extern fn int printf(char* format, ...);
|
||||
extern fn int sprintf(char* str, char* format, ...);
|
||||
extern fn int snprintf(char* str, usz size, char* format, ...);
|
||||
extern fn int fscanf(CFile stream, char* format, ...);
|
||||
extern fn int scanf(char* format, ...);
|
||||
extern fn int sscanf(char* str, char* format, ...);
|
||||
extern fn int fgetc(CFile stream);
|
||||
extern fn char* fgets(char* str, int n, CFile stream);
|
||||
extern fn int fputc(int c, CFile stream);
|
||||
extern fn int getc(CFile stream);
|
||||
extern fn int getchar();
|
||||
extern fn int putc(int c, CFile stream);
|
||||
extern fn int putchar(int c);
|
||||
extern fn int puts(char* str);
|
||||
extern fn int ungetc(int c, CFile stream);
|
||||
extern fn void perror(char* str);
|
||||
extern fn isz getline(char** linep, usz* linecapp, CFile stream);
|
||||
|
||||
$else
|
||||
|
||||
fn int fseek(CFile stream, SeekIndex offset, int whence) @weak @extern("fseek") @nostrip
|
||||
{
|
||||
unreachable("'fseek' not available.");
|
||||
}
|
||||
fn CFile fopen(ZString filename, ZString mode) @weak @extern("fopen") @nostrip
|
||||
{
|
||||
unreachable("'fopen' not available.");
|
||||
}
|
||||
|
||||
fn CFile freopen(ZString filename, ZString mode, CFile stream) @weak @extern("fopen") @nostrip
|
||||
{
|
||||
unreachable("'freopen' not available.");
|
||||
}
|
||||
|
||||
fn usz fwrite(void* ptr, usz size, usz nmemb, CFile stream) @weak @extern("fwrite") @nostrip
|
||||
{
|
||||
unreachable("'fwrite' not available.");
|
||||
}
|
||||
|
||||
fn usz fread(void* ptr, usz size, usz nmemb, CFile stream) @weak @extern("fread") @nostrip
|
||||
{
|
||||
unreachable("'fread' not available.");
|
||||
}
|
||||
|
||||
fn CFile fclose(CFile) @weak @extern("fclose") @nostrip
|
||||
{
|
||||
unreachable("'fclose' not available.");
|
||||
}
|
||||
|
||||
fn int fflush(CFile stream) @weak @extern("fflush") @nostrip
|
||||
{
|
||||
unreachable("'fflush' not available.");
|
||||
}
|
||||
|
||||
fn int fputc(int c, CFile stream) @weak @extern("fputc") @nostrip
|
||||
{
|
||||
unreachable("'fputc' not available.");
|
||||
}
|
||||
|
||||
fn char* fgets(ZString str, int n, CFile stream) @weak @extern("fgets") @nostrip
|
||||
{
|
||||
unreachable("'fgets' not available.");
|
||||
}
|
||||
|
||||
fn int fgetc(CFile stream) @weak @extern("fgetc") @nostrip
|
||||
{
|
||||
unreachable("'fgetc' not available.");
|
||||
}
|
||||
|
||||
fn int feof(CFile stream) @weak @extern("feof") @nostrip
|
||||
{
|
||||
unreachable("'feof' not available.");
|
||||
}
|
||||
|
||||
fn int putc(int c, CFile stream) @weak @extern("putc") @nostrip
|
||||
{
|
||||
unreachable("'putc' not available.");
|
||||
}
|
||||
fn int putchar(int c) @weak @extern("putchar") @nostrip
|
||||
{
|
||||
unreachable("'putchar' not available.");
|
||||
}
|
||||
fn int puts(ZString str) @weak @extern("puts") @nostrip
|
||||
{
|
||||
unreachable("'puts' not available.");
|
||||
}
|
||||
|
||||
$endif
|
||||
// vsprintf vprintf not supported
|
||||
|
||||
// time.h
|
||||
|
||||
struct TmCommon @private
|
||||
{
|
||||
int tm_sec; /* seconds after the minute [0-60] */
|
||||
int tm_min; /* minutes after the hour [0-59] */
|
||||
int tm_hour; /* hours since midnight [0-23] */
|
||||
int tm_mday; /* day of the month [1-31] */
|
||||
int tm_mon; /* months since January [0-11] */
|
||||
int tm_year; /* years since 1900 */
|
||||
int tm_wday; /* days since Sunday [0-6] */
|
||||
int tm_yday; /* days since January 1 [0-365] */
|
||||
int tm_isdst; /* Daylight Savings Time flag */
|
||||
}
|
||||
|
||||
|
||||
$switch (env::OS_TYPE)
|
||||
|
||||
$case WIN32:
|
||||
|
||||
def Tm = TmCommon;
|
||||
|
||||
$case WASI:
|
||||
|
||||
def TimeOffset = int;
|
||||
struct Tm
|
||||
{
|
||||
inline TmCommon common;
|
||||
TimeOffset tm_gmtoff; /* offset from UTC in seconds */
|
||||
char *tm_zone; /* timezone abbreviation */
|
||||
int tm_nsec;
|
||||
}
|
||||
|
||||
$case MACOS:
|
||||
$case IOS:
|
||||
$case TVOS:
|
||||
$case WATCHOS:
|
||||
$case OPENBSD:
|
||||
$case FREEBSD:
|
||||
$default:
|
||||
|
||||
def TimeOffset = CLong;
|
||||
struct Tm
|
||||
{
|
||||
inline TmCommon common;
|
||||
TimeOffset tm_gmtoff; /* offset from UTC in seconds */
|
||||
char *tm_zone; /* timezone abbreviation */
|
||||
}
|
||||
|
||||
$endswitch
|
||||
|
||||
|
||||
$if env::os_is_win32():
|
||||
|
||||
struct TimeSpec
|
||||
{
|
||||
Time_t s;
|
||||
ulong ns;
|
||||
}
|
||||
|
||||
def Time_t = long;
|
||||
def Clock_t = ulong;
|
||||
|
||||
$else
|
||||
|
||||
struct TimeSpec
|
||||
{
|
||||
Time_t s;
|
||||
CLong ns;
|
||||
}
|
||||
|
||||
def Time_t = CLong;
|
||||
def Clock_t = CULong;
|
||||
|
||||
$endif
|
||||
|
||||
const int TIME_UTC = 1;
|
||||
|
||||
extern fn int timespec_get(TimeSpec* ts, int base);
|
||||
extern fn int nanosleep(TimeSpec* req, TimeSpec* remaining);
|
||||
|
||||
// Likely wrong, must be per platform.
|
||||
const CLOCKS_PER_SEC = 1000000;
|
||||
|
||||
|
||||
extern fn ZString asctime(Tm *timeptr);
|
||||
extern fn Clock_t clock();
|
||||
extern fn ZString ctime(Time_t *timer);
|
||||
extern fn double difftime(Time_t time1, Time_t time2);
|
||||
extern fn Tm* gmtime(Time_t *timer);
|
||||
|
||||
extern fn Tm* localtime(Time_t *timer);
|
||||
|
||||
$if env::os_is_win32():
|
||||
extern fn Tm* _gmtime64_s(Tm* buf, Time_t *timer);
|
||||
extern fn Tm* _localtime64_s(Tm* buf, Time_t *timer);
|
||||
extern fn void _get_timezone(CLong *timezone);
|
||||
|
||||
macro Tm* gmtime_r(Time_t *timer, Tm* buf) => _gmtime64_s(buf, timer);
|
||||
macro Tm* localtime_r(Time_t *timer, Tm* buf) => _localtime64_s(buf, timer);
|
||||
|
||||
extern fn Time_t mktime(Tm *timeptr) @extern("_mktime64");
|
||||
extern fn Time_t timegm(Tm *timeptr) @extern("_mkgmtime64");
|
||||
$else
|
||||
extern fn Tm* gmtime_r(Time_t *timer, Tm* buf);
|
||||
extern fn Tm* localtime_r(Time_t *timer, Tm* buf);
|
||||
extern fn Time_t mktime(Tm *timeptr);
|
||||
extern fn Time_t timegm(Tm *timeptr);
|
||||
$endif
|
||||
|
||||
extern fn usz strftime(char* str, usz maxsize, char* format, Tm *timeptr);
|
||||
extern fn Time_t time(Time_t *timer);
|
||||
|
||||
// signal
|
||||
def SignalFunction = fn void(int);
|
||||
extern fn SignalFunction signal(int sig, SignalFunction function);
|
||||
// Incomplete
|
||||
|
||||
|
||||
module libc::errno;
|
||||
|
||||
const Errno OK = 0;
|
||||
const Errno EPERM = 1; // Operation not permitted
|
||||
const Errno ENOENT = 2; // No such file or directory
|
||||
const Errno ESRCH = 3; // No such process
|
||||
const Errno EINTR = 4; // Interrupted system call
|
||||
const Errno EIO = 5; // I/O error
|
||||
const Errno ENXIO = 6; // No such device or address
|
||||
const Errno E2BIG = 7; // Argument list too long
|
||||
const Errno ENOEXEC = 8; // Exec format error
|
||||
const Errno EBADF = 9; // Bad file number
|
||||
const Errno ECHILD = 10; // No child processes
|
||||
|
||||
$if env::os_is_darwin():
|
||||
const Errno EAGAIN = 35; // Try again Macos
|
||||
$else
|
||||
const Errno EAGAIN = 11; // Try again
|
||||
$endif
|
||||
|
||||
const Errno ENOMEM = 12; // Out of memory
|
||||
const Errno EACCES = 13; // Permission denied
|
||||
const Errno EFAULT = 14; // Bad address
|
||||
const Errno ENOTBLK = 15; // Block device required, not on Win32
|
||||
const Errno EBUSY = 16; // Device or resource busy
|
||||
const Errno EEXIST = 17; // File exists
|
||||
const Errno EXDEV = 18; // Cross-device link
|
||||
const Errno ENODEV = 19; // No such device
|
||||
const Errno ENOTDIR = 20; // Not a directory
|
||||
const Errno EISDIR = 21; // Is a directory
|
||||
const Errno EINVAL = 22; // Invalid argument
|
||||
const Errno ENFILE = 23; // File table overflow
|
||||
const Errno EMFILE = 24; // Too many open files
|
||||
const Errno ENOTTY = 25; // Not a typewriter
|
||||
const Errno ETXTBSY = 26; // Text file busy, not on Win32
|
||||
const Errno EFBIG = 27; // File too large
|
||||
const Errno ENOSPC = 28; // No space left on device
|
||||
const Errno ESPIPE = 29; // Illegal seek
|
||||
const Errno EROFS = 30; // Read-only file system
|
||||
const Errno EMLINK = 31; // Too many links
|
||||
const Errno EPIPE = 32; // Broken pipe
|
||||
const Errno EDOM = 33; // Math argument out of domain of func
|
||||
const Errno ERANGE = 34; // Math result not representable
|
||||
|
||||
$switch (env::OS_TYPE)
|
||||
|
||||
$case MACOS:
|
||||
const Errno EDEADLK = 11; // Resource deadlock would occur MacOS
|
||||
const Errno ENAMETOOLONG = 63; // File name too long MacOS
|
||||
const Errno ELOOP = 62; // Too many symbolic links encountered
|
||||
const Errno EOVERFLOW = 84; // Value too large for defined data type Macos
|
||||
const Errno ECONNRESET = 54; // Connection reset by peer Macos
|
||||
const Errno ENETDOWN = 50; // Network is down MacOS
|
||||
const Errno ENETUNREACH = 51; // Network is unreachable MacOS
|
||||
const Errno ENETRESET = 52; // Network dropped connection because of reset MacOS
|
||||
const Errno EOPNOTSUPP = 45; // Operation not supported on transport endpoint
|
||||
const Errno ENOTEMPTY = 66; // Directory not empty
|
||||
|
||||
$case WIN32:
|
||||
const Errno EDEADLK = 36; // Resource deadlock would occur Win32
|
||||
const Errno ENAMETOOLONG = 38; // File name too long Win32
|
||||
const Errno ELOOP = 114; // Too many symbolic links encountered
|
||||
const Errno EOVERFLOW = 132; // Value too large for defined data type
|
||||
const Errno ENETDOWN = 116; // Network is down
|
||||
const Errno ECONNRESET = 108; // Connection reset by peer
|
||||
const Errno ENETUNREACH = 118; // Network is unreachable
|
||||
const Errno ENETRESET = 117; // Network dropped connection because of reset
|
||||
const Errno EOPNOTSUPP = 130; // Operation not supported on transport endpoint
|
||||
const Errno ENOTEMPTY = 41; // Directory not empty
|
||||
|
||||
$default:
|
||||
const Errno EDEADLK = 35; // Resource deadlock would occur Linux (others?)
|
||||
const Errno ENAMETOOLONG = 36; // File name too long Linux (others?)
|
||||
const Errno ELOOP = 40; // Too many symbolic links encountered
|
||||
const Errno EOVERFLOW = 75; // Value too large for defined data type
|
||||
const Errno ENETDOWN = 100; // Network is down
|
||||
const Errno ECONNRESET = 104; // Connection reset by peer
|
||||
const Errno ENETUNREACH = 101; // Network is unreachable
|
||||
const Errno ENETRESET = 102; // Network dropped connection because of reset
|
||||
const Errno EOPNOTSUPP = 95; // Operation not supported on transport endpoint
|
||||
const Errno ENOTEMPTY = 39; // Directory not empty
|
||||
|
||||
$endswitch
|
||||
|
||||
|
||||
|
||||
/*
|
||||
const Errno ENOLCK = 37; /* No record locks available */
|
||||
const Errno ENOSYS = 38; /* Function not implemented */
|
||||
|
||||
const Errno ENOMSG = 42; /* No message of desired type */
|
||||
const Errno EIDRM = 43; /* Identifier removed */
|
||||
const Errno ECHRNG = 44; /* Channel number out of range */
|
||||
const Errno EL2NSYNC = 45; /* Level 2 not synchronized */
|
||||
const Errno EL3HLT = 46; /* Level 3 halted */
|
||||
const Errno EL3RST = 47; /* Level 3 reset */
|
||||
const Errno ELNRNG = 48; /* Link number out of range */
|
||||
const Errno EUNATCH = 49; /* Protocol driver not attached */
|
||||
const Errno ENOCSI = 50; /* No CSI structure available */
|
||||
const Errno EL2HLT = 51; /* Level 2 halted */
|
||||
const Errno EBADE = 52; /* Invalid exchange */
|
||||
const Errno EBADR = 53; /* Invalid request descriptor */
|
||||
const Errno EXFULL = 54; /* Exchange full */
|
||||
const Errno ENOANO = 55; /* No anode */
|
||||
const Errno EBADRQC = 56; /* Invalid request code */
|
||||
const Errno EBADSLT = 57; /* Invalid slot */
|
||||
|
||||
const Errno EBFONT = 59; /* Bad font file format */
|
||||
const Errno ENOSTR = 60; /* Device not a stream */
|
||||
const Errno ENODATA = 61; /* No data available */
|
||||
const Errno ETIME = 62; /* Timer expired */
|
||||
const Errno ENOSR = 63; /* Out of streams resources */
|
||||
const Errno ENONET = 64; /* Machine is not on the network */
|
||||
const Errno ENOPKG = 65; /* Package not installed */
|
||||
const Errno EREMOTE = 66; /* Object is remote */
|
||||
const Errno ENOLINK = 67; /* Link has been severed */
|
||||
const Errno EADV = 68; /* Advertise error */
|
||||
const Errno ESRMNT = 69; /* Srmount error */
|
||||
const Errno ECOMM = 70; /* Communication error on send */
|
||||
const Errno EPROTO = 71; /* Protocol error */
|
||||
const Errno EMULTIHOP = 72; /* Multihop attempted */
|
||||
const Errno EDOTDOT = 73; /* RFS specific error */
|
||||
const Errno EBADMSG = 74; /* Not a data message */
|
||||
const Errno ENOTUNIQ = 76; /* Name not unique on network */
|
||||
const Errno EBADFD = 77; /* File descriptor in bad state */
|
||||
const Errno EREMCHG = 78; /* Remote address changed */
|
||||
const Errno ELIBACC = 79; /* Can not access a needed shared library */
|
||||
const Errno ELIBBAD = 80; /* Accessing a corrupted shared library */
|
||||
const Errno ELIBSCN = 81; /* .lib section in a.out corrupted */
|
||||
const Errno ELIBMAX = 82; /* Attempting to link in too many shared libraries */
|
||||
const Errno ELIBEXEC = 83; /* Cannot exec a shared library directly */
|
||||
const Errno EILSEQ = 84; /* Illegal byte sequence */
|
||||
const Errno ERESTART = 85; /* Interrupted system call should be restarted */
|
||||
const Errno ESTRPIPE = 86; /* Streams pipe error */
|
||||
const Errno EUSERS = 87; /* Too many users */
|
||||
const Errno ENOTSOCK = 88; /* Socket operation on non-socket */
|
||||
const Errno EDESTADDRREQ = 89; /* Destination address required */
|
||||
const Errno EMSGSIZE = 90; /* Message too long */
|
||||
const Errno EPROTOTYPE = 91; /* Protocol wrong type for socket */
|
||||
const Errno ENOPROTOOPT = 92; /* Protocol not available */
|
||||
const Errno EPROTONOSUPPORT = 93; /* Protocol not supported */
|
||||
const Errno ESOCKTNOSUPPORT = 94; /* Socket type not supported */
|
||||
const Errno EPFNOSUPPORT = 96; /* Protocol family not supported */
|
||||
const Errno EAFNOSUPPORT = 97; /* Address family not supported by protocol */
|
||||
const Errno EADDRINUSE = 98; /* Address already in use */
|
||||
const Errno EADDRNOTAVAIL = 99; /* Cannot assign requested address */
|
||||
const Errno ECONNABORTED = 103; /* Software caused connection abort */
|
||||
const Errno ENOBUFS = 105; /* No buffer space available */
|
||||
const Errno EISCONN = 106; /* Transport endpoint is already connected */
|
||||
const Errno ENOTCONN = 107; /* Transport endpoint is not connected */
|
||||
const Errno ESHUTDOWN = 108; /* Cannot send after transport endpoint shutdown */
|
||||
const Errno ETOOMANYREFS = 109; /* Too many references: cannot splice */
|
||||
const Errno ECONNREFUSED = 111; /* Connection refused */
|
||||
const Errno EHOSTDOWN = 112; /* Host is down */
|
||||
const Errno EHOSTUNREACH = 113; /* No route to host */
|
||||
*/
|
||||
|
||||
$switch (env::OS_TYPE)
|
||||
|
||||
$case MACOS:
|
||||
const Errno ETIMEDOUT = 60; // Connection timed out
|
||||
const Errno EINPROGRESS = 36; // Operation now in progress MacOS
|
||||
const Errno EALREADY = 37; // Operation already in progress MacOS
|
||||
const Errno EDQUOT = 69; // Quota exceeded, MacOS
|
||||
const Errno EWOULDBLOCK = 35; // Operation would block
|
||||
|
||||
$case WIN32:
|
||||
const Errno ETIMEDOUT = 138; // Connection timed out
|
||||
const Errno EALREADY = 103; // Operation already in progress
|
||||
const Errno EINPROGRESS = 112; // Operation now in progress Win32
|
||||
const Errno EDQUOT = -122; // Quota exceeded, not in Win32
|
||||
const Errno EWOULDBLOCK = 140; // Operation would block
|
||||
|
||||
$default:
|
||||
const Errno ETIMEDOUT = 110; // Connection timed out
|
||||
const Errno EALREADY = 114; // Operation already in progress
|
||||
const Errno EINPROGRESS = 115; // Operation now in progress
|
||||
const Errno EDQUOT = 122; // Quota exceeded
|
||||
const Errno EWOULDBLOCK = 41; // Operation would block
|
||||
$endswitch
|
||||
|
||||
/*
|
||||
const Errno ESTALE = 116; /* Stale NFS file handle */
|
||||
const Errno EUCLEAN = 117; /* Structure needs cleaning */
|
||||
const Errno ENOTNAM = 118; /* Not a XENIX named type file */
|
||||
const Errno ENAVAIL = 119; /* No XENIX semaphores available */
|
||||
const Errno EISNAM = 120; /* Is a named type file */
|
||||
const Errno EREMOTEIO = 121; /* Remote I/O error */
|
||||
|
||||
const Errno ENOMEDIUM = 123; /* No medium found */
|
||||
const Errno EMEDIUMTYPE = 124; /* Wrong medium type */
|
||||
const Errno ECANCELED = 125; /* Operation Canceled */
|
||||
const Errno ENOKEY = 126; /* Required key not available */
|
||||
const Errno EKEYEXPIRED = 127; /* Key has expired */
|
||||
const Errno EKEYREVOKED = 128; /* Key has been revoked */
|
||||
const Errno EKEYREJECTED = 129; /* Key was rejected by service */
|
||||
|
||||
const Errno EOWNERDEAD = 130; /* Owner died */
|
||||
const Errno ENOTRECOVERABLE = 131; /* State not recoverable */
|
||||
*/
|
||||
|
||||
38
lib/std/libc/os/errno.c3
Normal file
38
lib/std/libc/os/errno.c3
Normal file
@@ -0,0 +1,38 @@
|
||||
module libc::os;
|
||||
|
||||
|
||||
$switch
|
||||
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == LINUX:
|
||||
|
||||
extern fn int* __errno_location();
|
||||
macro int errno() => *__errno_location();
|
||||
macro void errno_set(int err) => *(__errno_location()) = err;
|
||||
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == MACOS:
|
||||
|
||||
extern fn int* __error();
|
||||
macro int errno() => *__error();
|
||||
macro void errno_set(int err) => *(__error()) = err;
|
||||
|
||||
$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == WIN32:
|
||||
|
||||
macro int errno()
|
||||
{
|
||||
int holder;
|
||||
_get_errno(&holder);
|
||||
return holder;
|
||||
}
|
||||
|
||||
macro void errno_set(int err) => _set_errno(err);
|
||||
|
||||
extern fn void _get_errno(int* result);
|
||||
extern fn void _set_errno(int err);
|
||||
|
||||
$default:
|
||||
|
||||
tlocal int _errno_c3 = 0;
|
||||
fn void errno_set(int err) => _errno_c3 = err;
|
||||
fn int errno() => _errno_c3;
|
||||
|
||||
$endswitch
|
||||
950
lib/std/math/math.c3
Normal file
950
lib/std/math/math.c3
Normal file
@@ -0,0 +1,950 @@
|
||||
// Copyright (c) 2021 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::math;
|
||||
import std::math::complex;
|
||||
import std::math::matrix;
|
||||
import std::math::quaternion;
|
||||
|
||||
// TODO Define these using quad precision.
|
||||
const E = 2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466;
|
||||
const LOG2E = 1.44269504088896340735992468100189214; // log2(e)
|
||||
const LOG10E = 0.434294481903251827651128918916605082; // log10(e)
|
||||
const LN2 = 0.693147180559945309417232121458176568; // ln(2)
|
||||
const LN10 = 2.30258509299404568401799145468436421; // ln(10)
|
||||
const PI = 3.14159265358979323846264338327950288419716939937510; // pi
|
||||
const PI_2 = 1.57079632679489661923132169163975144; // pi / 2
|
||||
const PI_4 = 0.785398163397448309615660845819875721; // pi / 4
|
||||
const DIV_PI = 0.318309886183790671537767526745028724; // 1 / pi
|
||||
const DIV_2_PI = 0.636619772367581343075535053490057448; // 2 / pi
|
||||
const DIV_2_SQRTPI = 1.12837916709551257389615890312154517; // 2/sqrt(pi)
|
||||
const SQRT2 = 1.41421356237309504880168872420969808; // sqrt(2)
|
||||
const double DIV_1_SQRT2 = 0.707106781186547524400844362104849039; // 1 / sqrt(2)
|
||||
|
||||
const HALF_MAX = 6.5504e+4;
|
||||
const HALF_MIN = 6.103515625e-5;
|
||||
const HALF_DENORM_MIN = 5.9604644775390625e-8;
|
||||
const HALF_DIG = 3;
|
||||
const HALF_DEC_DIGITS = 5;
|
||||
const HALF_MANT_DIG = 11;
|
||||
const HALF_MAX_10_EXP = 4;
|
||||
const HALF_MIN_10_EXP = -4;
|
||||
const HALF_MAX_EXP = 16;
|
||||
const HALF_MIN_EXP = -13;
|
||||
const HALF_EPSILON = 9.765625e-4;
|
||||
|
||||
const FLOAT_MAX = 0x1.fffffep+127;
|
||||
const FLOAT_MIN = 1.17549435e-38;
|
||||
const FLOAT_DENORM_MIN = 1.40129846432481707092e-45;
|
||||
const FLOAT_DIG = 6;
|
||||
const FLOAT_DEC_DIGITS = 9;
|
||||
const FLOAT_MANT_DIG = 24;
|
||||
const FLOAT_MAX_10_EXP = 38;
|
||||
const FLOAT_MIN_10_EXP = -37;
|
||||
const FLOAT_MAX_EXP = 128;
|
||||
const FLOAT_MIN_EXP = -125;
|
||||
const FLOAT_EPSILON = 1.1920928955078125e-07;
|
||||
|
||||
const DOUBLE_MAX = 1.79769313486231570815e+308;
|
||||
const DOUBLE_MIN = 2.2250738585072014e-308;
|
||||
const DOUBLE_DENORM_MIN = 4.94065645841246544177e-324;
|
||||
const DOUBLE_DIG = 15;
|
||||
const DOUBLE_DEC_DIGITS = 17;
|
||||
const DOUBLE_MANT_DIG = 53;
|
||||
const DOUBLE_MAX_10_EXP = 308;
|
||||
const DOUBLE_MIN_10_EXP = -307;
|
||||
const DOUBLE_MAX_EXP = 1024;
|
||||
const DOUBLE_MIN_EXP = -1021;
|
||||
const DOUBLE_EPSILON = 2.22044604925031308085e-16;
|
||||
|
||||
const QUAD_MANT_DIG = 113;
|
||||
|
||||
/*
|
||||
const QUAD_MAX = 1.18973149535723176508575932662800702e+4932;
|
||||
const QUAD_MIN = 3.36210314311209350626267781732175260e-4932;
|
||||
const QUAD_DENORM_MIN = 6.47517511943802511092443895822764655e-4966;
|
||||
const QUAD_DIG = 33;
|
||||
const QUAD_DEC_DIGITS = 36;
|
||||
const QUAD_MAX_10_EXP = 4932;
|
||||
const QUAD_MIN_10_EXP = -4931;
|
||||
const QUAD_MAX_EXP = 16384;
|
||||
const QUAD_MIN_EXP = -16481;
|
||||
const QUAD_EPSILON = 1.92592994438723585305597794258492732e-34;
|
||||
*/
|
||||
|
||||
enum RoundingMode : int
|
||||
{
|
||||
TOWARD_ZERO,
|
||||
TO_NEAREST,
|
||||
TOWARD_INFINITY,
|
||||
TOWARD_NEG_INFINITY
|
||||
}
|
||||
|
||||
fault MatrixError
|
||||
{
|
||||
MATRIX_INVERSE_DOESNT_EXIST,
|
||||
}
|
||||
|
||||
def Complexf = Complex<float>;
|
||||
def Complex = Complex<double>;
|
||||
def complexf_identity = complex::identity<float>;
|
||||
def complex_identity = complex::identity<double>;
|
||||
|
||||
def Quaternionf = Quaternion<float>;
|
||||
def Quaternion = Quaternion<double>;
|
||||
def quaternionf_identity = quaternion::identity<float>;
|
||||
def quaternion_identity = quaternion::identity<double>;
|
||||
|
||||
def Matrix2f = Matrix2x2<float>;
|
||||
def Matrix2 = Matrix2x2<double>;
|
||||
def Matrix3f = Matrix3x3<float>;
|
||||
def Matrix3 = Matrix3x3<double>;
|
||||
def Matrix4f = Matrix4x4<float>;
|
||||
def Matrix4 = Matrix4x4<double>;
|
||||
def matrix4_ortho = matrix::ortho<double>;
|
||||
def matrix4_perspective = matrix::perspective<double>;
|
||||
def matrix4f_ortho = matrix::ortho<float>;
|
||||
def matrix4f_perspective = matrix::perspective<float>;
|
||||
|
||||
def MATRIX2_IDENTITY = matrix::IDENTITY2<double>;
|
||||
def MATRIX2F_IDENTITY = matrix::IDENTITY2<float>;
|
||||
def MATRIX3_IDENTITY = matrix::IDENTITY3<double>;
|
||||
def MATRIX3F_IDENTITY = matrix::IDENTITY3<float>;
|
||||
def MATRIX4_IDENTITY = matrix::IDENTITY4<double>;
|
||||
def MATRIX4F_IDENTITY = matrix::IDENTITY4<float>;
|
||||
|
||||
/**
|
||||
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
|
||||
**/
|
||||
macro abs(x) => $$abs(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_int(x) `The input must be an integer`
|
||||
**/
|
||||
macro sign(x)
|
||||
{
|
||||
var $Type = $typeof(x);
|
||||
$if $Type.kindof == TypeKind.UNSIGNED_INT:
|
||||
return ($Type)(x > 0);
|
||||
$else
|
||||
return ($Type)(x > 0) - ($Type)(x < 0);
|
||||
$endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
|
||||
* @checked x + y
|
||||
**/
|
||||
macro atan2(x, y)
|
||||
{
|
||||
$if @typeis(x, float) && @typeis(y, float):
|
||||
return _atan2f(x, y);
|
||||
$else
|
||||
return _atan2(x, y);
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
|
||||
* @checked (*y)[0] = x, y.len
|
||||
* @require y.len == 2
|
||||
**/
|
||||
macro sincos(x, y)
|
||||
{
|
||||
$if $typeof(y[0]).typeid == float.typeid:
|
||||
return _sincosf(x, y);
|
||||
$else
|
||||
return _sincos(x, y);
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
|
||||
* @checked x
|
||||
**/
|
||||
macro atan(x)
|
||||
{
|
||||
$if $typeof(x).typeid == float.typeid:
|
||||
return _atanf(x);
|
||||
$else
|
||||
return _atan(x);
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
|
||||
**/
|
||||
macro atanh(x)
|
||||
{
|
||||
$if $typeof(x).typeid == float.typeid:
|
||||
return _atanhf(x);
|
||||
$else
|
||||
return _atanh(x);
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
|
||||
**/
|
||||
macro acos(x)
|
||||
{
|
||||
$if $typeof(x).typeid == float.typeid:
|
||||
return _acosf(x);
|
||||
$else
|
||||
return _acos(x);
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
|
||||
**/
|
||||
macro acosh(x)
|
||||
{
|
||||
$if $typeof(x).typeid == float.typeid:
|
||||
return _acoshf(x);
|
||||
$else
|
||||
return _acosh(x);
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
|
||||
**/
|
||||
macro asin(x)
|
||||
{
|
||||
$if $typeof(x).typeid == float.typeid:
|
||||
return _asinf(x);
|
||||
$else
|
||||
return _asin(x);
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
|
||||
**/
|
||||
macro asinh(x)
|
||||
{
|
||||
$if $typeof(x).typeid == float.typeid:
|
||||
return _asinhf(x);
|
||||
$else
|
||||
return _asinh(x);
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro ceil(x) => $$ceil(x);
|
||||
|
||||
/**
|
||||
* Constrain the value to lie within the given interval.
|
||||
*
|
||||
* @param x "the value to clamp, may be a number or a numerical vector."
|
||||
* @param lower "the lower bounds"
|
||||
* @param upper "the upper bounds"
|
||||
* @return "lower if x < lower, upper if x > upper, otherwise return x."
|
||||
*
|
||||
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
|
||||
* @checked $typeof(x) z = lower `The lower bound must be convertable to the value type.`
|
||||
* @checked $typeof(x) z = upper `The upper bound must be convertable to the value type.`
|
||||
**/
|
||||
macro clamp(x, lower, upper) => $$max(($typeof(x))lower, $$min(x, ($typeof(x))upper));
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(mag) `The input must be a number value or float vector`
|
||||
* @require @convertable(sgn, $typeof(values::promote_int(mag)))
|
||||
**/
|
||||
macro copysign(mag, sgn) => $$copysign(values::promote_int(mag), ($typeof(values::promote_int(mag)))sgn);
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
|
||||
**/
|
||||
macro cos(x) => $$cos(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
|
||||
**/
|
||||
macro cosec(x) => 1 / sin(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
|
||||
**/
|
||||
macro cosech(x) => 2 / (exp(x) - exp(-x));
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
|
||||
**/
|
||||
macro cosh(x) => (exp(x) + exp(-x)) / 2.0;
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
|
||||
**/
|
||||
macro cotan(x) => cos(x) / sin(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
|
||||
**/
|
||||
macro cotanh(x) => (exp(2.0 * x) + 1.0) / (exp(2.0 * x) - 1.0);
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
|
||||
**/
|
||||
macro exp(x) => $$exp(values::promote_int(x));
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
|
||||
**/
|
||||
macro exp2(x) => $$exp2(values::promote_int(x));
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
|
||||
**/
|
||||
macro floor(x) => $$floor(values::promote_int(x));
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(a) `The input must be a number or float vector`
|
||||
* @require values::@is_promotable_to_floatlike(b) `The input must be a number or float vector`
|
||||
* @require values::@is_promotable_to_floatlike(c) `The input must be a number or float vector`
|
||||
* @require types::@is_same_vector_type(a, b) `The input types must be equal`
|
||||
* @require types::@is_same_vector_type(a, c) `The input types must match`
|
||||
**/
|
||||
macro fma(a, b, c) => $$fma(a, b, c);
|
||||
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
|
||||
* @require values::@is_promotable_to_floatlike(y) `The input must be a number or a float vector`
|
||||
* @require types::@is_same_vector_type(x, y) `The input types must match`
|
||||
**/
|
||||
macro hypot(x, y) => sqrt(sqr(x) + sqr(y));
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
|
||||
**/
|
||||
macro log(x) => $$log(values::promote_int(x));
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
|
||||
**/
|
||||
macro log2(x) => $$log2(values::promote_int(x));
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
|
||||
**/
|
||||
macro log10(x) => $$log10(values::promote_int(x));
|
||||
|
||||
/**
|
||||
* @require types::is_numerical($typeof(x)) `The input must be a floating point value or float vector`
|
||||
* @require types::is_same($typeof(x), $typeof(y)) `The input types must be equal`
|
||||
**/
|
||||
macro max(x, y, ...)
|
||||
{
|
||||
$if $vacount == 0:
|
||||
return $$max(x, y);
|
||||
$else
|
||||
var m = $$max(x, y);
|
||||
$for (var $i = 0; $i < $vacount; $i++)
|
||||
m = $$max(m, $vaarg($i));
|
||||
$endfor
|
||||
return m;
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
|
||||
* @require types::is_same($typeof(x), $typeof(y)) `The input types must be equal`
|
||||
**/
|
||||
macro min(x, y, ...)
|
||||
{
|
||||
$if $vacount == 0:
|
||||
return $$min(x, y);
|
||||
$else
|
||||
var m = $$min(x, y);
|
||||
$for (var $i = 0; $i < $vacount; $i++)
|
||||
m = $$min(m, $vaarg($i));
|
||||
$endfor
|
||||
return m;
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @require types::@is_float(a) `The input must be a floating point value`
|
||||
* @require types::@has_same(a, b, c) `The input types must be equal`
|
||||
**/
|
||||
macro muladd(a, b, c) => $$fmuladd(a, b, c);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro nearbyint(x) => $$nearbyint(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
|
||||
* @require values::@convertable_to(exp, x) || values::@is_int(exp) `The input must be an integer, castable to the type of x`
|
||||
**/
|
||||
macro pow(x, exp)
|
||||
{
|
||||
$if types::is_floatlike($typeof(exp)):
|
||||
return $$pow(x, ($typeof(x))exp);
|
||||
$else
|
||||
return $$pow_int(x, exp);
|
||||
$endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_float(x) : `The input must be integer or floating type`
|
||||
**/
|
||||
macro frexp(x, int* e)
|
||||
{
|
||||
$switch ($typeof(x))
|
||||
$case float:
|
||||
$case float16:
|
||||
return _frexpf((float)x, e);
|
||||
$default:
|
||||
return _frexp((double)x, e);
|
||||
$endswitch
|
||||
}
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_float(x) : `The input must be integer or floating type`
|
||||
**/
|
||||
macro int signbit(x)
|
||||
{
|
||||
$switch ($typeof(x))
|
||||
$case float:
|
||||
$case float16:
|
||||
return bitcast((float)x, uint) >> 31;
|
||||
$default:
|
||||
return (int)(bitcast((double)x, ulong) >> 63);
|
||||
$endswitch
|
||||
}
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a number or a float vector`
|
||||
**/
|
||||
macro rint(x) => $$rint(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro round(x) => $$round(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro roundeven(x) => $$roundeven(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
|
||||
**/
|
||||
macro sec(x) => 1 / cos(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
|
||||
**/
|
||||
macro sech(x) => 2 / (exp(x) + exp(-x));
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
|
||||
**/
|
||||
macro sin(x) => $$sin(values::promote_int(x));
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
|
||||
**/
|
||||
macro sinh(x) => (exp(x) - exp(-x)) / 2.0;
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
|
||||
**/
|
||||
macro sqr(x) => values::promote_int(x) * values::promote_int(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
|
||||
**/
|
||||
macro sqrt(x) => $$sqrt(values::promote_int(x));
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
|
||||
**/
|
||||
macro tan(x)
|
||||
{
|
||||
var $Type = $typeof(x);
|
||||
$switch
|
||||
$case types::is_vector($Type):
|
||||
return $$sin(x) / $$cos(x);
|
||||
$case $Type.typeid == float.typeid:
|
||||
return _tanf(x);
|
||||
$default:
|
||||
return _tan(x);
|
||||
$endswitch
|
||||
}
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_float(x) `The input must be a float`
|
||||
**/
|
||||
macro bool is_finite(x)
|
||||
{
|
||||
$switch ($typeof(x))
|
||||
$case float:
|
||||
$case float16:
|
||||
return bitcast((float)x, uint) & 0x7fffffff < 0x7f800000;
|
||||
$default:
|
||||
return bitcast((double)x, ulong) & (~0u64 >> 1) < 0x7ffu64 << 52;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_float(x) `The input must be a float`
|
||||
**/
|
||||
macro is_nan(x)
|
||||
{
|
||||
$switch ($typeof(x))
|
||||
$case float:
|
||||
$case float16:
|
||||
return bitcast((float)x, uint) & 0x7fffffff > 0x7f800000;
|
||||
$default:
|
||||
return bitcast((double)x, ulong) & (~0u64 >> 1) > 0x7ffu64 << 52;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_float(x) `The input must be a float`
|
||||
**/
|
||||
macro is_inf(x)
|
||||
{
|
||||
$switch ($typeof(x))
|
||||
$case float:
|
||||
$case float16:
|
||||
return bitcast((float)x, uint) & 0x7fffffff == 0x7f800000;
|
||||
$default:
|
||||
return bitcast((double)x, ulong) & (~0u64 >> 1) == 0x7ffu64 << 52;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
/**
|
||||
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
|
||||
**/
|
||||
macro tanh(x) => (exp(2.0 * x) - 1.0) / (exp(2.0 * x) + 1.0);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro trunc(x) => $$trunc(x);
|
||||
|
||||
macro lerp(x, y, amount) @private => x + (y - x) * amount;
|
||||
macro reflect(x, y) @private
|
||||
{
|
||||
var dot = x.dot(y);
|
||||
return x - 2 * y * dot;
|
||||
}
|
||||
macro normalize(x) @private
|
||||
{
|
||||
var len = x.length();
|
||||
if (len == 0) return x;
|
||||
return x * (1 / len);
|
||||
}
|
||||
|
||||
macro float float.ceil(float x) => $$ceil(x);
|
||||
macro float float.clamp(float x, float lower, float upper) => $$max(lower, $$min(x, upper));
|
||||
macro float float.copysign(float mag, float sgn) => $$copysign(mag, sgn);
|
||||
macro float float.floor(float x) => $$floor(x);
|
||||
macro float float.fma(float a, float b, float c) => $$fma(a, b, c);
|
||||
macro float float.muladd(float a, float b, float c) => $$fmuladd(a, b, c);
|
||||
macro float float.nearbyint(float x) => $$nearbyint(x);
|
||||
macro float float.pow(float x, exp) => pow(x, exp);
|
||||
macro float float.rint(float x) => $$rint(x);
|
||||
macro float float.round(float x) => $$round(x);
|
||||
macro float float.roundeven(float x) => $$roundeven(x);
|
||||
macro float float.trunc(float x) => $$trunc(x);
|
||||
|
||||
macro float float[<*>].sum(float[<*>] x, float start = 0.0) => $$reduce_fadd(x, start);
|
||||
macro float float[<*>].product(float[<*>] x, float start = 1.0) => $$reduce_fmul(x, start);
|
||||
macro float float[<*>].max(float[<*>] x) => $$reduce_max(x);
|
||||
macro float float[<*>].min(float[<*>] x) => $$reduce_min(x);
|
||||
macro float[<*>] float[<*>].ceil(float[<*>] x) => $$ceil(x);
|
||||
macro float[<*>] float[<*>].clamp(float[<*>] x, float[<*>] lower, float[<*>] upper) => $$max(lower, $$min(x, upper));
|
||||
macro float[<*>] float[<*>].copysign(float[<*>] mag, float[<*>] sgn) => $$copysign(mag, sgn);
|
||||
macro float[<*>] float[<*>].fma(float[<*>] a, float[<*>] b, float[<*>] c) => $$fma(a, b, c);
|
||||
macro float[<*>] float[<*>].floor(float[<*>] x) => $$floor(x);
|
||||
macro float[<*>] float[<*>].nearbyint(float[<*>] x) => $$nearbyint(x);
|
||||
macro float[<*>] float[<*>].pow(float[<*>] x, exp) => pow(x, exp);
|
||||
macro float[<*>] float[<*>].rint(float[<*>] x) => $$rint(x);
|
||||
macro float[<*>] float[<*>].round(float[<*>] x) => $$round(x);
|
||||
macro float[<*>] float[<*>].roundeven(float[<*>] x) => $$roundeven(x);
|
||||
macro float[<*>] float[<*>].trunc(float[<*>] x) => $$trunc(x);
|
||||
macro float float[<*>].dot(float[<*>] x, float[<*>] y) => (x * y).sum();
|
||||
macro float float[<*>].length(float[<*>] x) => $$sqrt(x.dot(x));
|
||||
macro float float[<*>].distance(float[<*>] x, float[<*>] y) => (x - y).length();
|
||||
macro float[<*>] float[<*>].normalize(float[<*>] x) => normalize(x);
|
||||
macro float[<*>] float[<*>].lerp(float[<*>] x, float[<*>] y, float amount) => lerp(x, y, amount);
|
||||
macro float[<*>] float[<*>].reflect(float[<*>] x, float[<*>] y) => reflect(x, y);
|
||||
macro bool float[<*>].equals(float[<*>] x, float[<*>] y) => equals_vec(x, y);
|
||||
|
||||
macro bool[<*>] float[<*>].comp_lt(float[<*>] x, float[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] float[<*>].comp_le(float[<*>] x, float[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] float[<*>].comp_eq(float[<*>] x, float[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] float[<*>].comp_gt(float[<*>] x, float[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] float[<*>].comp_ge(float[<*>] x, float[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] float[<*>].comp_ne(float[<*>] x, float[<*>] y) => $$veccompne(x, y);
|
||||
|
||||
macro double double.ceil(double x) => $$ceil(x);
|
||||
macro double double.clamp(double x, double lower, double upper) => $$max(lower, $$min(x, upper));
|
||||
macro double double.copysign(double mag, double sgn) => $$copysign(mag, sgn);
|
||||
macro double double.floor(double x) => $$floor(x);
|
||||
macro double double.fma(double a, double b, double c) => $$fma(a, b, c);
|
||||
macro double double.muladd(double a, double b, double c) => $$fmuladd(a, b, c);
|
||||
macro double double.nearbyint(double x) => $$nearbyint(x);
|
||||
macro double double.pow(double x, exp) => pow(x, exp);
|
||||
macro double double.rint(double x) => $$rint(x);
|
||||
macro double double.round(double x) => $$round(x);
|
||||
macro double double.roundeven(double x) => $$roundeven(x);
|
||||
macro double double.trunc(double x) => $$trunc(x);
|
||||
|
||||
macro double double[<*>].sum(double[<*>] x, double start = 0.0) => $$reduce_fadd(x, start);
|
||||
macro double double[<*>].product(double[<*>] x, double start = 1.0) => $$reduce_fmul(x, start);
|
||||
macro double double[<*>].max(double[<*>] x) => $$reduce_fmax(x);
|
||||
macro double double[<*>].min(double[<*>] x) => $$reduce_fmin(x);
|
||||
macro double[<*>] double[<*>].ceil(double[<*>] x) => $$ceil(x);
|
||||
macro double[<*>] double[<*>].clamp(double[<*>] x, double[<*>] lower, double[<*>] upper) => $$max(lower, $$min(x, upper));
|
||||
macro double[<*>] double[<*>].copysign(double[<*>] mag, double[<*>] sgn) => $$copysign(mag, sgn);
|
||||
macro double[<*>] double[<*>].floor(double[<*>] x) => $$floor(x);
|
||||
macro double[<*>] double[<*>].fma(double[<*>] a, double[<*>] b, double[<*>] c) => $$fma(a, b, c);
|
||||
macro double[<*>] double[<*>].nearbyint(double[<*>] x) => $$nearbyint(x);
|
||||
macro double[<*>] double[<*>].pow(double[<*>] x, exp) => pow(x, exp);
|
||||
macro double[<*>] double[<*>].rint(double[<*>] x) => $$rint(x);
|
||||
macro double[<*>] double[<*>].round(double[<*>] x) => $$round(x);
|
||||
macro double[<*>] double[<*>].roundeven(double[<*>] x) => $$roundeven(x);
|
||||
macro double[<*>] double[<*>].trunc(double[<*>] x) => $$trunc(x);
|
||||
macro double double[<*>].dot(double[<*>] x, double[<*>] y) => (x * y).sum();
|
||||
macro double double[<*>].length(double[<*>] x) => $$sqrt(x.dot(x));
|
||||
macro double double[<*>].distance(double[<*>] x, double[<*>] y) => (x - y).length();
|
||||
macro double[<*>] double[<*>].normalize(double[<*>] x) => normalize(x);
|
||||
macro double[<*>] double[<*>].reflect(double[<*>] x, double[<*>] y) => reflect(x, y);
|
||||
macro double[<*>] double[<*>].lerp(double[<*>] x, double[<*>] y, double amount) => lerp(x, y, amount);
|
||||
macro bool double[<*>].equals(double[<*>] x, double[<*>] y) => equals_vec(x, y);
|
||||
|
||||
macro bool[<*>] double[<*>].comp_lt(double[<*>] x, double[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] double[<*>].comp_le(double[<*>] x, double[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] double[<*>].comp_eq(double[<*>] x, double[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] double[<*>].comp_gt(double[<*>] x, double[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] double[<*>].comp_ge(double[<*>] x, double[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] double[<*>].comp_ne(double[<*>] x, double[<*>] y) => $$veccompne(x, y);
|
||||
|
||||
macro bool[<*>] ichar[<*>].comp_lt(ichar[<*>] x, ichar[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] ichar[<*>].comp_le(ichar[<*>] x, ichar[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] ichar[<*>].comp_eq(ichar[<*>] x, ichar[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] ichar[<*>].comp_gt(ichar[<*>] x, ichar[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] ichar[<*>].comp_ge(ichar[<*>] x, ichar[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] ichar[<*>].comp_ne(ichar[<*>] x, ichar[<*>] y) => $$veccompne(x, y);
|
||||
|
||||
macro ichar ichar[<*>].sum(ichar[<*>] x) => $$reduce_add(x);
|
||||
macro ichar ichar[<*>].product(ichar[<*>] x) => $$reduce_mul(x);
|
||||
macro ichar ichar[<*>].and(ichar[<*>] x) => $$reduce_and(x);
|
||||
macro ichar ichar[<*>].or(ichar[<*>] x) => $$reduce_or(x);
|
||||
macro ichar ichar[<*>].xor(ichar[<*>] x) => $$reduce_xor(x);
|
||||
macro ichar ichar[<*>].max(ichar[<*>] x) => $$reduce_max(x);
|
||||
macro ichar ichar[<*>].min(ichar[<*>] x) => $$reduce_min(x);
|
||||
|
||||
macro bool[<*>] short[<*>].comp_lt(short[<*>] x, short[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] short[<*>].comp_le(short[<*>] x, short[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] short[<*>].comp_eq(short[<*>] x, short[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] short[<*>].comp_gt(short[<*>] x, short[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] short[<*>].comp_ge(short[<*>] x, short[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] short[<*>].comp_ne(short[<*>] x, short[<*>] y) => $$veccompne(x, y);
|
||||
|
||||
macro short short[<*>].sum(short[<*>] x) => $$reduce_add(x);
|
||||
macro short short[<*>].product(short[<*>] x) => $$reduce_mul(x);
|
||||
macro short short[<*>].and(short[<*>] x) => $$reduce_and(x);
|
||||
macro short short[<*>].or(short[<*>] x) => $$reduce_or(x);
|
||||
macro short short[<*>].xor(short[<*>] x) => $$reduce_xor(x);
|
||||
macro short short[<*>].max(short[<*>] x) => $$reduce_max(x);
|
||||
macro short short[<*>].min(short[<*>] x) => $$reduce_min(x);
|
||||
|
||||
macro bool[<*>] int[<*>].comp_lt(int[<*>] x, int[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] int[<*>].comp_le(int[<*>] x, int[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] int[<*>].comp_eq(int[<*>] x, int[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] int[<*>].comp_gt(int[<*>] x, int[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] int[<*>].comp_ge(int[<*>] x, int[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] int[<*>].comp_ne(int[<*>] x, int[<*>] y) => $$veccompne(x, y);
|
||||
|
||||
macro int int[<*>].sum(int[<*>] x) => $$reduce_add(x);
|
||||
macro int int[<*>].product(int[<*>] x) => $$reduce_mul(x);
|
||||
macro int int[<*>].and(int[<*>] x) => $$reduce_and(x);
|
||||
macro int int[<*>].or(int[<*>] x) => $$reduce_or(x);
|
||||
macro int int[<*>].xor(int[<*>] x) => $$reduce_xor(x);
|
||||
macro int int[<*>].max(int[<*>] x) => $$reduce_max(x);
|
||||
macro int int[<*>].min(int[<*>] x) => $$reduce_min(x);
|
||||
|
||||
macro bool[<*>] long[<*>].comp_lt(long[<*>] x, long[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] long[<*>].comp_le(long[<*>] x, long[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] long[<*>].comp_eq(long[<*>] x, long[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] long[<*>].comp_gt(long[<*>] x, long[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] long[<*>].comp_ge(long[<*>] x, long[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] long[<*>].comp_ne(long[<*>] x, long[<*>] y) => $$veccompne(x, y);
|
||||
macro long long[<*>].sum(long[<*>] x) => $$reduce_add(x);
|
||||
macro long long[<*>].product(long[<*>] x) => $$reduce_mul(x);
|
||||
macro long long[<*>].and(long[<*>] x) => $$reduce_and(x);
|
||||
macro long long[<*>].or(long[<*>] x) => $$reduce_or(x);
|
||||
macro long long[<*>].xor(long[<*>] x) => $$reduce_xor(x);
|
||||
macro long long[<*>].max(long[<*>] x) => $$reduce_max(x);
|
||||
macro long long[<*>].min(long[<*>] x) => $$reduce_min(x);
|
||||
|
||||
macro bool[<*>] int128[<*>].comp_lt(int128[<*>] x, int128[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] int128[<*>].comp_le(int128[<*>] x, int128[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] int128[<*>].comp_eq(int128[<*>] x, int128[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] int128[<*>].comp_gt(int128[<*>] x, int128[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] int128[<*>].comp_ge(int128[<*>] x, int128[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] int128[<*>].comp_ne(int128[<*>] x, int128[<*>] y) => $$veccompne(x, y);
|
||||
macro int128 int128[<*>].sum(int128[<*>] x) => $$reduce_add(x);
|
||||
macro int128 int128[<*>].product(int128[<*>] x) => $$reduce_mul(x);
|
||||
macro int128 int128[<*>].and(int128[<*>] x) => $$reduce_and(x);
|
||||
macro int128 int128[<*>].or(int128[<*>] x) => $$reduce_or(x);
|
||||
macro int128 int128[<*>].xor(int128[<*>] x) => $$reduce_xor(x);
|
||||
macro int128 int128[<*>].max(int128[<*>] x) => $$reduce_max(x);
|
||||
macro int128 int128[<*>].min(int128[<*>] x) => $$reduce_min(x);
|
||||
|
||||
macro bool[<*>] bool[<*>].comp_lt(bool[<*>] x, bool[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] bool[<*>].comp_le(bool[<*>] x, bool[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] bool[<*>].comp_eq(bool[<*>] x, bool[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] bool[<*>].comp_gt(bool[<*>] x, bool[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] bool[<*>].comp_ge(bool[<*>] x, bool[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] bool[<*>].comp_ne(bool[<*>] x, bool[<*>] y) => $$veccompne(x, y);
|
||||
|
||||
macro bool bool[<*>].sum(bool[<*>] x) => $$reduce_add(x);
|
||||
macro bool bool[<*>].product(bool[<*>] x) => $$reduce_mul(x);
|
||||
macro bool bool[<*>].and(bool[<*>] x) => $$reduce_and(x);
|
||||
macro bool bool[<*>].or(bool[<*>] x) => $$reduce_or(x);
|
||||
macro bool bool[<*>].xor(bool[<*>] x) => $$reduce_xor(x);
|
||||
macro bool bool[<*>].max(bool[<*>] x) => $$reduce_max(x);
|
||||
macro bool bool[<*>].min(bool[<*>] x) => $$reduce_min(x);
|
||||
|
||||
macro bool[<*>] char[<*>].comp_lt(char[<*>] x, char[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] char[<*>].comp_le(char[<*>] x, char[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] char[<*>].comp_eq(char[<*>] x, char[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] char[<*>].comp_gt(char[<*>] x, char[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] char[<*>].comp_ge(char[<*>] x, char[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] char[<*>].comp_ne(char[<*>] x, char[<*>] y) => $$veccompne(x, y);
|
||||
|
||||
macro char char[<*>].sum(char[<*>] x) => $$reduce_add(x);
|
||||
macro char char[<*>].product(char[<*>] x) => $$reduce_mul(x);
|
||||
macro char char[<*>].and(char[<*>] x) => $$reduce_and(x);
|
||||
macro char char[<*>].or(char[<*>] x) => $$reduce_or(x);
|
||||
macro char char[<*>].xor(char[<*>] x) => $$reduce_xor(x);
|
||||
macro char char[<*>].max(char[<*>] x) => $$reduce_max(x);
|
||||
macro char char[<*>].min(char[<*>] x) => $$reduce_min(x);
|
||||
|
||||
macro bool[<*>] ushort[<*>].comp_lt(ushort[<*>] x, ushort[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] ushort[<*>].comp_le(ushort[<*>] x, ushort[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] ushort[<*>].comp_eq(ushort[<*>] x, ushort[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] ushort[<*>].comp_gt(ushort[<*>] x, ushort[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] ushort[<*>].comp_ge(ushort[<*>] x, ushort[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] ushort[<*>].comp_ne(ushort[<*>] x, ushort[<*>] y) => $$veccompne(x, y);
|
||||
|
||||
macro ushort ushort[<*>].sum(ushort[<*>] x) => $$reduce_add(x);
|
||||
macro ushort ushort[<*>].product(ushort[<*>] x) => $$reduce_mul(x);
|
||||
macro ushort ushort[<*>].and(ushort[<*>] x) => $$reduce_and(x);
|
||||
macro ushort ushort[<*>].or(ushort[<*>] x) => $$reduce_or(x);
|
||||
macro ushort ushort[<*>].xor(ushort[<*>] x) => $$reduce_xor(x);
|
||||
macro ushort ushort[<*>].max(ushort[<*>] x) => $$reduce_max(x);
|
||||
macro ushort ushort[<*>].min(ushort[<*>] x) => $$reduce_min(x);
|
||||
|
||||
macro bool[<*>] uint[<*>].comp_lt(uint[<*>] x, uint[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] uint[<*>].comp_le(uint[<*>] x, uint[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] uint[<*>].comp_eq(uint[<*>] x, uint[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] uint[<*>].comp_gt(uint[<*>] x, uint[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] uint[<*>].comp_ge(uint[<*>] x, uint[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] uint[<*>].comp_ne(uint[<*>] x, uint[<*>] y) => $$veccompne(x, y);
|
||||
|
||||
macro uint uint[<*>].sum(uint[<*>] x) => $$reduce_add(x);
|
||||
macro uint uint[<*>].product(uint[<*>] x) => $$reduce_mul(x);
|
||||
macro uint uint[<*>].and(uint[<*>] x) => $$reduce_and(x);
|
||||
macro uint uint[<*>].or(uint[<*>] x) => $$reduce_or(x);
|
||||
macro uint uint[<*>].xor(uint[<*>] x) => $$reduce_xor(x);
|
||||
macro uint uint[<*>].max(uint[<*>] x) => $$reduce_max(x);
|
||||
macro uint uint[<*>].min(uint[<*>] x) => $$reduce_min(x);
|
||||
|
||||
macro bool[<*>] ulong[<*>].comp_lt(ulong[<*>] x, ulong[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] ulong[<*>].comp_le(ulong[<*>] x, ulong[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] ulong[<*>].comp_eq(ulong[<*>] x, ulong[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] ulong[<*>].comp_gt(ulong[<*>] x, ulong[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] ulong[<*>].comp_ge(ulong[<*>] x, ulong[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] ulong[<*>].comp_ne(ulong[<*>] x, ulong[<*>] y) => $$veccompne(x, y);
|
||||
|
||||
macro ulong ulong[<*>].sum(ulong[<*>] x) => $$reduce_add(x);
|
||||
macro ulong ulong[<*>].product(ulong[<*>] x) => $$reduce_mul(x);
|
||||
macro ulong ulong[<*>].and(ulong[<*>] x) => $$reduce_and(x);
|
||||
macro ulong ulong[<*>].or(ulong[<*>] x) => $$reduce_or(x);
|
||||
macro ulong ulong[<*>].xor(ulong[<*>] x) => $$reduce_xor(x);
|
||||
macro ulong ulong[<*>].max(ulong[<*>] x) => $$reduce_max(x);
|
||||
macro ulong ulong[<*>].min(ulong[<*>] x) => $$reduce_min(x);
|
||||
|
||||
macro bool[<*>] uint128[<*>].comp_lt(uint128[<*>] x, uint128[<*>] y) => $$veccomplt(x, y);
|
||||
macro bool[<*>] uint128[<*>].comp_le(uint128[<*>] x, uint128[<*>] y) => $$veccomple(x, y);
|
||||
macro bool[<*>] uint128[<*>].comp_eq(uint128[<*>] x, uint128[<*>] y) => $$veccompeq(x, y);
|
||||
macro bool[<*>] uint128[<*>].comp_gt(uint128[<*>] x, uint128[<*>] y) => $$veccompgt(x, y);
|
||||
macro bool[<*>] uint128[<*>].comp_ge(uint128[<*>] x, uint128[<*>] y) => $$veccompge(x, y);
|
||||
macro bool[<*>] uint128[<*>].comp_ne(uint128[<*>] x, uint128[<*>] y) => $$veccompne(x, y);
|
||||
|
||||
macro uint128 uint128[<*>].sum(uint128[<*>] x) => $$reduce_add(x);
|
||||
macro uint128 uint128[<*>].product(uint128[<*>] x) => $$reduce_mul(x);
|
||||
macro uint128 uint128[<*>].and(uint128[<*>] x) => $$reduce_and(x);
|
||||
macro uint128 uint128[<*>].or(uint128[<*>] x) => $$reduce_or(x);
|
||||
macro uint128 uint128[<*>].xor(uint128[<*>] x) => $$reduce_xor(x);
|
||||
macro uint128 uint128[<*>].max(uint128[<*>] x) => $$reduce_max(x);
|
||||
macro uint128 uint128[<*>].min(uint128[<*>] x) => $$reduce_min(x);
|
||||
|
||||
macro char char.sat_add(char x, char y) => $$sat_add(x, y);
|
||||
macro char char.sat_sub(char x, char y) => $$sat_sub(x, y);
|
||||
macro char char.sat_mul(char x, char y) => $$sat_mul(x, y);
|
||||
macro char char.sat_shl(char x, char y) => $$sat_shl(x, y);
|
||||
|
||||
macro ichar ichar.sat_add(ichar x, ichar y) => $$sat_add(x, y);
|
||||
macro ichar ichar.sat_sub(ichar x, ichar y) => $$sat_sub(x, y);
|
||||
macro ichar ichar.sat_mul(ichar x, ichar y) => $$sat_mul(x, y);
|
||||
macro ichar ichar.sat_shl(ichar x, ichar y) => $$sat_shl(x, y);
|
||||
|
||||
macro ushort ushort.sat_add(ushort x, ushort y) => $$sat_add(x, y);
|
||||
macro ushort ushort.sat_sub(ushort x, ushort y) => $$sat_sub(x, y);
|
||||
macro ushort ushort.sat_mul(ushort x, ushort y) => $$sat_mul(x, y);
|
||||
macro ushort ushort.sat_shl(ushort x, ushort y) => $$sat_shl(x, y);
|
||||
|
||||
macro short short.sat_add(short x, short y) => $$sat_add(x, y);
|
||||
macro short short.sat_sub(short x, short y) => $$sat_sub(x, y);
|
||||
macro short short.sat_mul(short x, short y) => $$sat_mul(x, y);
|
||||
macro short short.sat_shl(short x, short y) => $$sat_shl(x, y);
|
||||
|
||||
macro uint uint.sat_add(uint x, uint y) => $$sat_add(x, y);
|
||||
macro uint uint.sat_sub(uint x, uint y) => $$sat_sub(x, y);
|
||||
macro uint uint.sat_mul(uint x, uint y) => $$sat_mul(x, y);
|
||||
macro uint uint.sat_shl(uint x, uint y) => $$sat_shl(x, y);
|
||||
|
||||
macro int int.sat_add(int x, int y) => $$sat_add(x, y);
|
||||
macro int int.sat_sub(int x, int y) => $$sat_sub(x, y);
|
||||
macro int int.sat_mul(int x, int y) => $$sat_mul(x, y);
|
||||
macro int int.sat_shl(int x, int y) => $$sat_shl(x, y);
|
||||
|
||||
macro ulong ulong.sat_add(ulong x, ulong y) => $$sat_add(x, y);
|
||||
macro ulong ulong.sat_sub(ulong x, ulong y) => $$sat_sub(x, y);
|
||||
macro ulong ulong.sat_mul(ulong x, ulong y) => $$sat_mul(x, y);
|
||||
macro ulong ulong.sat_shl(ulong x, ulong y) => $$sat_shl(x, y);
|
||||
|
||||
macro long long.sat_add(long x, long y) => $$sat_add(x, y);
|
||||
macro long long.sat_sub(long x, long y) => $$sat_sub(x, y);
|
||||
macro long long.sat_mul(long x, long y) => $$sat_mul(x, y);
|
||||
macro long long.sat_shl(long x, long y) => $$sat_shl(x, y);
|
||||
|
||||
macro uint128 uint128.sat_add(uint128 x, uint128 y) => $$sat_add(x, y);
|
||||
macro uint128 uint128.sat_sub(uint128 x, uint128 y) => $$sat_sub(x, y);
|
||||
macro uint128 uint128.sat_mul(uint128 x, uint128 y) => $$sat_mul(x, y);
|
||||
macro uint128 uint128.sat_shl(uint128 x, uint128 y) => $$sat_shl(x, y);
|
||||
|
||||
macro int128 int128.sat_add(int128 x, int128 y) => $$sat_add(x, y);
|
||||
macro int128 int128.sat_sub(int128 x, int128 y) => $$sat_sub(x, y);
|
||||
macro int128 int128.sat_mul(int128 x, int128 y) => $$sat_mul(x, y);
|
||||
macro int128 int128.sat_shl(int128 x, int128 y) => $$sat_shl(x, y);
|
||||
|
||||
/**
|
||||
* @checked x & 1
|
||||
*/
|
||||
macro bool is_power_of_2(x)
|
||||
{
|
||||
return x != 0 && (x & (x - 1)) == 0;
|
||||
}
|
||||
|
||||
macro next_power_of_2(x)
|
||||
{
|
||||
$typeof(x) y = 1;
|
||||
while (y < x) y += y;
|
||||
return y;
|
||||
}
|
||||
|
||||
macro equals_vec(v1, v2) @private
|
||||
{
|
||||
var $elements = v1.len;
|
||||
var abs_diff = math::abs(v1 - v2);
|
||||
var abs_v1 = math::abs(v1);
|
||||
var abs_v2 = math::abs(v2);
|
||||
$typeof(abs_v2) eps = 1;
|
||||
return abs_diff.comp_le(FLOAT_EPSILON * math::max(abs_v1, abs_v2, eps)).and();
|
||||
}
|
||||
|
||||
macro uint double.high_word(double d) => (uint)(bitcast(d, ulong) >> 32);
|
||||
macro uint double.low_word(double d) => (uint)bitcast(d, ulong);
|
||||
macro uint float.word(float d) => bitcast(d, uint);
|
||||
|
||||
|
||||
macro double scalbn(double x, int n) => _scalbn(x, n);
|
||||
|
||||
extern fn double _atan(double x) @extern("atan");
|
||||
extern fn float _atanf(float x) @extern("atanf");
|
||||
extern fn double _atan2(double, double) @extern("atan2");
|
||||
extern fn float _atan2f(float, float) @extern("atan2f");
|
||||
extern fn void _sincos(double, double*) @extern("sincos");
|
||||
extern fn void _sincosf(float, float*) @extern("sincosf");
|
||||
extern fn double _tan(double x) @extern("tan");
|
||||
extern fn float _tanf(float x) @extern("tanf");
|
||||
extern fn double _scalbn(double x, int n) @extern("scalbn");
|
||||
extern fn double _acos(double x) @extern("acos");
|
||||
extern fn double _asin(double x) @extern("asin");
|
||||
extern fn double _acosh(double x) @extern("acosh");
|
||||
extern fn double _asinh(double x) @extern("asinh");
|
||||
extern fn double _atanh(double x) @extern("atanh");
|
||||
extern fn float _acosf(float x) @extern("acosf");
|
||||
extern fn float _asinf(float x) @extern("asinf");
|
||||
extern fn float _acoshf(float x) @extern("acoshf");
|
||||
extern fn float _asinhf(float x) @extern("asinhf");
|
||||
extern fn float _atanhf(float x) @extern("atanhf");
|
||||
|
||||
|
||||
fn double _frexp(double x, int* e)
|
||||
{
|
||||
ulong i = bitcast(x, ulong);
|
||||
int ee = (int)((i >> 52) & 0x7ff);
|
||||
switch
|
||||
{
|
||||
case !ee:
|
||||
if (!x)
|
||||
{
|
||||
*e = 0;
|
||||
return x;
|
||||
}
|
||||
x = _frexp(x * 0x1p64, e);
|
||||
*e -= 64;
|
||||
return x;
|
||||
case ee == 0x7ff:
|
||||
return x;
|
||||
default:
|
||||
*e = ee - 0x3fe;
|
||||
i &= 0x800fffffffffffffu64;
|
||||
i |= 0x3fe0000000000000u64;
|
||||
return bitcast(i, double);
|
||||
}
|
||||
}
|
||||
|
||||
fn float _frexpf(float x, int* e)
|
||||
{
|
||||
uint i = bitcast(x, uint);
|
||||
int ee = (i >> 23) & 0xff;
|
||||
|
||||
switch
|
||||
{
|
||||
case !ee:
|
||||
if (!x)
|
||||
{
|
||||
*e = 0;
|
||||
return x;
|
||||
}
|
||||
x = _frexpf(x * 0x1p64, e);
|
||||
*e -= 64;
|
||||
return x;
|
||||
case ee == 0xff:
|
||||
return x;
|
||||
default:
|
||||
*e = ee - 0x7e;
|
||||
i &= 0x807fffffu32;
|
||||
i |= 0x3f000000u32;
|
||||
return bitcast(i, float);
|
||||
}
|
||||
}
|
||||
24
lib/std/math/math.complex.c3
Normal file
24
lib/std/math/math.complex.c3
Normal file
@@ -0,0 +1,24 @@
|
||||
module std::math::complex<Real>;
|
||||
|
||||
union Complex
|
||||
{
|
||||
struct
|
||||
{
|
||||
Real r, c;
|
||||
}
|
||||
Real[<2>] v;
|
||||
}
|
||||
|
||||
|
||||
macro Complex identity() => { 1, 0 };
|
||||
macro Complex Complex.add(Complex a, Complex b) => Complex { .v = a.v + b.v };
|
||||
macro Complex Complex.add_each(Complex a, Real b) => Complex { .v = a.v + b };
|
||||
macro Complex Complex.sub(Complex a, Complex b) => Complex { .v = a.v - b.v };
|
||||
macro Complex Complex.sub_each(Complex a, Real b) => Complex { .v = a.v - b };
|
||||
macro Complex Complex.scale(Complex a, Real s) => Complex { .v = a.v * s };
|
||||
macro Complex Complex.mul(Complex a, Complex b) => { a.r * b.r - a.c * b.c, a.r * b.c + b.r * a.c };
|
||||
macro Complex Complex.div(Complex a, Complex b)
|
||||
{
|
||||
Real div = b.v.dot(b.v);
|
||||
return Complex{ (a.r * b.r + a.c * b.c) / div, (a.c * b.r - a.r * b.c) / div };
|
||||
}
|
||||
434
lib/std/math/math.matrix.c3
Normal file
434
lib/std/math/math.matrix.c3
Normal file
@@ -0,0 +1,434 @@
|
||||
module std::math::matrix<Real>;
|
||||
|
||||
struct Matrix2x2
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
Real m00, m01;
|
||||
Real m10, m11;
|
||||
}
|
||||
Real[4] m;
|
||||
}
|
||||
}
|
||||
|
||||
struct Matrix3x3
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
Real m00, m01, m02;
|
||||
Real m10, m11, m12;
|
||||
Real m20, m21, m22;
|
||||
}
|
||||
Real[9] m;
|
||||
}
|
||||
}
|
||||
|
||||
struct Matrix4x4
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
Real m00, m01, m02, m03;
|
||||
Real m10, m11, m12, m13;
|
||||
Real m20, m21, m22, m23;
|
||||
Real m30, m31, m32, m33;
|
||||
}
|
||||
Real[16] m;
|
||||
}
|
||||
}
|
||||
|
||||
fn Real[<2>] Matrix2x2.apply(Matrix2x2* mat, Real[<2>] vec)
|
||||
{
|
||||
return Real[<2>] {
|
||||
mat.m00 * vec[0] + mat.m01 * vec[1],
|
||||
mat.m10 * vec[0] + mat.m11 * vec[1],
|
||||
};
|
||||
}
|
||||
|
||||
fn Real[<3>] Matrix3x3.apply(Matrix3x3* mat, Real[<3>] vec)
|
||||
{
|
||||
return Real[<3>] {
|
||||
mat.m00 * vec[0] + mat.m01 * vec[1] + mat.m02 * vec[2],
|
||||
mat.m10 * vec[0] + mat.m11 * vec[1] + mat.m12 * vec[2],
|
||||
mat.m20 * vec[0] + mat.m21 * vec[1] + mat.m22 * vec[2],
|
||||
};
|
||||
}
|
||||
|
||||
fn Real[<4>] Matrix4x4.apply(Matrix4x4* mat, Real[<4>] vec)
|
||||
{
|
||||
return Real[<4>] {
|
||||
mat.m00 * vec[0] + mat.m01 * vec[1] + mat.m02 * vec[2] + mat.m03 * vec[3],
|
||||
mat.m10 * vec[0] + mat.m11 * vec[1] + mat.m12 * vec[2] + mat.m13 * vec[3],
|
||||
mat.m20 * vec[0] + mat.m21 * vec[1] + mat.m22 * vec[2] + mat.m23 * vec[3],
|
||||
mat.m30 * vec[0] + mat.m31 * vec[1] + mat.m32 * vec[2] + mat.m33 * vec[3],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
fn Matrix2x2 Matrix2x2.mul(Matrix2x2* a, Matrix2x2 b)
|
||||
{
|
||||
return Matrix2x2 {
|
||||
a.m00 * b.m00 + a.m01 * b.m10, a.m00 * b.m01 + a.m01 * b.m11,
|
||||
a.m10 * b.m00 + a.m11 * b.m10, a.m10 * b.m01 + a.m11 * b.m11,
|
||||
};
|
||||
}
|
||||
|
||||
fn Matrix3x3 Matrix3x3.mul(Matrix3x3* a, Matrix3x3 b)
|
||||
{
|
||||
return Matrix3x3 {
|
||||
a.m00 * b.m00 + a.m01 * b.m10 + a.m02 * b.m20,
|
||||
a.m00 * b.m01 + a.m01 * b.m11 + a.m02 * b.m21,
|
||||
a.m00 * b.m02 + a.m01 * b.m12 + a.m02 * b.m22,
|
||||
|
||||
a.m10 * b.m00 + a.m11 * b.m10 + a.m12 * b.m20,
|
||||
a.m10 * b.m01 + a.m11 * b.m11 + a.m12 * b.m21,
|
||||
a.m10 * b.m02 + a.m11 * b.m12 + a.m12 * b.m22,
|
||||
|
||||
a.m20 * b.m00 + a.m21 * b.m10 + a.m22 * b.m20,
|
||||
a.m20 * b.m01 + a.m21 * b.m11 + a.m22 * b.m21,
|
||||
a.m20 * b.m02 + a.m21 * b.m12 + a.m22 * b.m22,
|
||||
};
|
||||
}
|
||||
|
||||
fn Matrix4x4 Matrix4x4.mul(Matrix4x4* a, Matrix4x4 b)
|
||||
{
|
||||
return Matrix4x4 {
|
||||
a.m00 * b.m00 + a.m01 * b.m10 + a.m02 * b.m20 + a.m03 * b.m30,
|
||||
a.m00 * b.m01 + a.m01 * b.m11 + a.m02 * b.m21 + a.m03 * b.m31,
|
||||
a.m00 * b.m02 + a.m01 * b.m12 + a.m02 * b.m22 + a.m03 * b.m32,
|
||||
a.m00 * b.m03 + a.m01 * b.m13 + a.m02 * b.m23 + a.m03 * b.m33,
|
||||
|
||||
a.m10 * b.m00 + a.m11 * b.m10 + a.m12 * b.m20 + a.m13 * b.m30,
|
||||
a.m10 * b.m01 + a.m11 * b.m11 + a.m12 * b.m21 + a.m13 * b.m31,
|
||||
a.m10 * b.m02 + a.m11 * b.m12 + a.m12 * b.m22 + a.m13 * b.m32,
|
||||
a.m10 * b.m03 + a.m11 * b.m13 + a.m12 * b.m23 + a.m13 * b.m33,
|
||||
|
||||
a.m20 * b.m00 + a.m21 * b.m10 + a.m22 * b.m20 + a.m23 * b.m30,
|
||||
a.m20 * b.m01 + a.m21 * b.m11 + a.m22 * b.m21 + a.m23 * b.m31,
|
||||
a.m20 * b.m02 + a.m21 * b.m12 + a.m22 * b.m22 + a.m23 * b.m32,
|
||||
a.m20 * b.m03 + a.m21 * b.m13 + a.m22 * b.m23 + a.m23 * b.m33,
|
||||
|
||||
a.m30 * b.m00 + a.m31 * b.m10 + a.m32 * b.m20 + a.m33 * b.m30,
|
||||
a.m30 * b.m01 + a.m31 * b.m11 + a.m32 * b.m21 + a.m33 * b.m31,
|
||||
a.m30 * b.m02 + a.m31 * b.m12 + a.m32 * b.m22 + a.m33 * b.m32,
|
||||
a.m30 * b.m03 + a.m31 * b.m13 + a.m32 * b.m23 + a.m33 * b.m33,
|
||||
};
|
||||
}
|
||||
|
||||
fn Matrix2x2 Matrix2x2.component_mul(Matrix2x2* mat, Real s) => matrix_component_mul(mat, s);
|
||||
fn Matrix3x3 Matrix3x3.component_mul(Matrix3x3* mat, Real s) => matrix_component_mul(mat, s);
|
||||
fn Matrix4x4 Matrix4x4.component_mul(Matrix4x4* mat, Real s) => matrix_component_mul(mat, s);
|
||||
|
||||
fn Matrix2x2 Matrix2x2.add(Matrix2x2* mat, Matrix2x2 mat2) => matrix_add(mat, mat2);
|
||||
fn Matrix3x3 Matrix3x3.add(Matrix3x3* mat, Matrix3x3 mat2) => matrix_add(mat, mat2);
|
||||
fn Matrix4x4 Matrix4x4.add(Matrix4x4* mat, Matrix4x4 mat2) => matrix_add(mat, mat2);
|
||||
|
||||
fn Matrix2x2 Matrix2x2.sub(Matrix2x2* mat, Matrix2x2 mat2) => matrix_sub(mat, mat2);
|
||||
fn Matrix3x3 Matrix3x3.sub(Matrix3x3* mat, Matrix3x3 mat2) => matrix_sub(mat, mat2);
|
||||
fn Matrix4x4 Matrix4x4.sub(Matrix4x4* mat, Matrix4x4 mat2) => matrix_sub(mat, mat2);
|
||||
|
||||
|
||||
|
||||
fn Matrix2x2 Matrix2x2.transpose(Matrix2x2* mat)
|
||||
{
|
||||
return Matrix2x2 {
|
||||
mat.m00, mat.m10,
|
||||
mat.m01, mat.m11
|
||||
};
|
||||
}
|
||||
|
||||
fn Matrix3x3 Matrix3x3.transpose(Matrix3x3* mat)
|
||||
{
|
||||
return Matrix3x3 {
|
||||
mat.m00, mat.m10, mat.m20,
|
||||
mat.m01, mat.m11, mat.m21,
|
||||
mat.m02, mat.m12, mat.m22,
|
||||
};
|
||||
}
|
||||
|
||||
fn Matrix4x4 Matrix4x4.transpose(Matrix4x4* mat)
|
||||
{
|
||||
return Matrix4x4 {
|
||||
mat.m00, mat.m10, mat.m20, mat.m30,
|
||||
mat.m01, mat.m11, mat.m21, mat.m31,
|
||||
mat.m02, mat.m12, mat.m22, mat.m32,
|
||||
mat.m03, mat.m13, mat.m23, mat.m33,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
fn Real Matrix2x2.determinant(Matrix2x2* mat)
|
||||
{
|
||||
return mat.m00 * mat.m11 - mat.m01 * mat.m10;
|
||||
}
|
||||
|
||||
fn Real Matrix3x3.determinant(Matrix3x3* mat)
|
||||
{
|
||||
return
|
||||
mat.m00 * (mat.m11 * mat.m22 - mat.m21 * mat.m12) -
|
||||
mat.m01 * (mat.m10 * mat.m22 - mat.m20 * mat.m12) +
|
||||
mat.m02 * (mat.m10 * mat.m21 - mat.m20 * mat.m11);
|
||||
}
|
||||
|
||||
fn Real Matrix4x4.determinant(Matrix4x4* mat)
|
||||
{
|
||||
return
|
||||
mat.m00 * (mat.m11 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
|
||||
mat.m12 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) +
|
||||
mat.m13 * (mat.m21 * mat.m32 - mat.m31 * mat.m22) ) -
|
||||
mat.m01 * (mat.m10 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
|
||||
mat.m12 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
|
||||
mat.m13 * (mat.m20 * mat.m32 - mat.m30 * mat.m22) ) +
|
||||
mat.m02 * (mat.m10 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) -
|
||||
mat.m11 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
|
||||
mat.m13 * (mat.m20 * mat.m31 - mat.m30 * mat.m21) ) -
|
||||
mat.m03 * (mat.m10 * (mat.m21 * mat.m32 - mat.m31 * mat.m22) -
|
||||
mat.m11 * (mat.m20 * mat.m32 - mat.m30 * mat.m22) +
|
||||
mat.m12 * (mat.m20 * mat.m31 - mat.m30 * mat.m21) );
|
||||
}
|
||||
|
||||
|
||||
fn Matrix2x2 Matrix2x2.adjoint(Matrix2x2* mat)
|
||||
{
|
||||
return Matrix2x2 { mat.m00, -mat.m01, -mat.m10, mat.m11 };
|
||||
}
|
||||
|
||||
fn Matrix3x3 Matrix3x3.adjoint(Matrix3x3* mat)
|
||||
{
|
||||
return Matrix3x3 {
|
||||
(mat.m11 * mat.m22 - mat.m21 * mat.m12),
|
||||
-(mat.m10 * mat.m22 - mat.m20 * mat.m12),
|
||||
(mat.m10 * mat.m21 - mat.m20 * mat.m11),
|
||||
|
||||
-(mat.m01 * mat.m22 - mat.m21 * mat.m02),
|
||||
(mat.m00 * mat.m22 - mat.m20 * mat.m02),
|
||||
-(mat.m00 * mat.m21 - mat.m20 * mat.m01),
|
||||
|
||||
(mat.m01 * mat.m12 - mat.m11 * mat.m02),
|
||||
-(mat.m00 * mat.m12 - mat.m10 * mat.m02),
|
||||
(mat.m00 * mat.m11 - mat.m10 * mat.m01),
|
||||
};
|
||||
}
|
||||
|
||||
fn Matrix4x4 Matrix4x4.adjoint(Matrix4x4* mat)
|
||||
{
|
||||
return Matrix4x4 {
|
||||
(mat.m11 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
|
||||
mat.m12 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) +
|
||||
mat.m13 * (mat.m21 * mat.m32 - mat.m31 * mat.m22)),
|
||||
-(mat.m10 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
|
||||
mat.m12 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
|
||||
mat.m13 * (mat.m20 * mat.m32 - mat.m30 * mat.m22)),
|
||||
(mat.m10 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) -
|
||||
mat.m11 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
|
||||
mat.m13 * (mat.m20 * mat.m31 - mat.m30 * mat.m21)),
|
||||
-(mat.m10 * (mat.m21 * mat.m32 - mat.m31 * mat.m22) -
|
||||
mat.m11 * (mat.m20 * mat.m32 - mat.m30 * mat.m22) +
|
||||
mat.m12 * (mat.m20 * mat.m31 - mat.m30 * mat.m21)),
|
||||
|
||||
-(mat.m01 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
|
||||
mat.m02 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) +
|
||||
mat.m03 * (mat.m21 * mat.m32 - mat.m31 * mat.m22)),
|
||||
(mat.m00 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
|
||||
mat.m02 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
|
||||
mat.m03 * (mat.m20 * mat.m32 - mat.m30 * mat.m22)),
|
||||
-(mat.m00 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) -
|
||||
mat.m01 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
|
||||
mat.m03 * (mat.m20 * mat.m31 - mat.m30 * mat.m21)),
|
||||
(mat.m00 * (mat.m21 * mat.m32 - mat.m31 * mat.m22) -
|
||||
mat.m01 * (mat.m20 * mat.m32 - mat.m30 * mat.m22) +
|
||||
mat.m02 * (mat.m20 * mat.m31 - mat.m30 * mat.m21)),
|
||||
|
||||
(mat.m01 * (mat.m12 * mat.m33 - mat.m32 * mat.m13) -
|
||||
mat.m02 * (mat.m11 * mat.m33 - mat.m31 * mat.m13) +
|
||||
mat.m03 * (mat.m11 * mat.m32 - mat.m31 * mat.m12)),
|
||||
-(mat.m00 * (mat.m12 * mat.m33 - mat.m32 * mat.m13) -
|
||||
mat.m02 * (mat.m10 * mat.m33 - mat.m30 * mat.m13) +
|
||||
mat.m03 * (mat.m10 * mat.m32 - mat.m30 * mat.m12)),
|
||||
(mat.m00 * (mat.m11 * mat.m33 - mat.m31 * mat.m13) -
|
||||
mat.m01 * (mat.m10 * mat.m33 - mat.m30 * mat.m13) +
|
||||
mat.m03 * (mat.m10 * mat.m31 - mat.m30 * mat.m11)),
|
||||
-(mat.m00 * (mat.m11 * mat.m32 - mat.m31 * mat.m12) -
|
||||
mat.m01 * (mat.m10 * mat.m32 - mat.m30 * mat.m12) +
|
||||
mat.m02 * (mat.m10 * mat.m31 - mat.m30 * mat.m11)),
|
||||
|
||||
-(mat.m01 * (mat.m12 * mat.m23 - mat.m22 * mat.m13) -
|
||||
mat.m02 * (mat.m11 * mat.m23 - mat.m21 * mat.m13) +
|
||||
mat.m03 * (mat.m11 * mat.m22 - mat.m21 * mat.m12)),
|
||||
(mat.m00 * (mat.m12 * mat.m23 - mat.m22 * mat.m13) -
|
||||
mat.m02 * (mat.m10 * mat.m23 - mat.m20 * mat.m13) +
|
||||
mat.m03 * (mat.m10 * mat.m22 - mat.m20 * mat.m12)),
|
||||
-(mat.m00 * (mat.m11 * mat.m23 - mat.m21 * mat.m13) -
|
||||
mat.m01 * (mat.m10 * mat.m23 - mat.m20 * mat.m13) +
|
||||
mat.m03 * (mat.m10 * mat.m21 - mat.m20 * mat.m11)),
|
||||
(mat.m00 * (mat.m11 * mat.m22 - mat.m21 * mat.m12) -
|
||||
mat.m01 * (mat.m10 * mat.m22 - mat.m20 * mat.m12) +
|
||||
mat.m02 * (mat.m10 * mat.m21 - mat.m20 * mat.m11)),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
fn Matrix2x2! Matrix2x2.inverse(Matrix2x2* m)
|
||||
{
|
||||
Real det = m.determinant();
|
||||
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST?;
|
||||
Matrix2x2 adj = m.adjoint();
|
||||
return adj.component_mul(1 / det).transpose();
|
||||
}
|
||||
|
||||
fn Matrix3x3! Matrix3x3.inverse(Matrix3x3* m)
|
||||
{
|
||||
Real det = m.determinant();
|
||||
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST?;
|
||||
Matrix3x3 adj = m.adjoint();
|
||||
return adj.component_mul(1 / det).transpose();
|
||||
}
|
||||
|
||||
fn Matrix4x4! Matrix4x4.inverse(Matrix4x4* m)
|
||||
{
|
||||
Real det = m.determinant();
|
||||
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST?;
|
||||
Matrix4x4 adj = m.adjoint();
|
||||
return adj.component_mul(1 / det).transpose();
|
||||
}
|
||||
|
||||
|
||||
fn Matrix3x3 Matrix3x3.translate(Matrix3x3* m, Real[<2>] v)
|
||||
{
|
||||
return m.mul(Matrix3x3 {
|
||||
1, 0, v[0],
|
||||
0, 1, v[1],
|
||||
0, 0, 1,
|
||||
});
|
||||
}
|
||||
|
||||
fn Matrix4x4 Matrix4x4.translate(Matrix4x4* m, Real[<3>] v)
|
||||
{
|
||||
return m.mul(Matrix4x4 {
|
||||
1, 0, 0, v[0],
|
||||
0, 1, 0, v[1],
|
||||
0, 0, 1, v[2],
|
||||
0, 0, 0, 1,
|
||||
});
|
||||
}
|
||||
|
||||
// r in radians
|
||||
fn Matrix3x3 Matrix3x3.rotate(Matrix3x3* m, Real r)
|
||||
{
|
||||
return m.mul(Matrix3x3 {
|
||||
math::cos(r), -math::sin(r), 0,
|
||||
math::sin(r), math::cos(r), 0,
|
||||
0, 0, 1,
|
||||
});
|
||||
}
|
||||
|
||||
// r in radians
|
||||
fn Matrix4x4 Matrix4x4.rotate_z(Matrix4x4* m, Real r)
|
||||
{
|
||||
return m.mul(Matrix4x4 {
|
||||
math::cos(r), -math::sin(r), 0, 0,
|
||||
math::sin(r), math::cos(r), 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1,
|
||||
});
|
||||
}
|
||||
|
||||
// r in radians
|
||||
fn Matrix4x4 Matrix4x4.rotate_y(Matrix4x4* m, Real r)
|
||||
{
|
||||
return m.mul(Matrix4x4 {
|
||||
math::cos(r), 0, -math::sin(r), 0,
|
||||
0, 1, 0, 0,
|
||||
math::sin(r), 0, math::cos(r), 0,
|
||||
0, 0, 0, 1,
|
||||
});
|
||||
}
|
||||
|
||||
// r in radians
|
||||
fn Matrix4x4 Matrix4x4.rotate_x(Matrix4x4* m, Real r)
|
||||
{
|
||||
return m.mul(Matrix4x4 {
|
||||
1, 0, 0, 0,
|
||||
0, math::cos(r), -math::sin(r), 0,
|
||||
0, math::sin(r), math::cos(r), 0,
|
||||
0, 0, 0, 1,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
fn Matrix3x3 Matrix3x3.scale(Matrix3x3* m, Real[<2>] v)
|
||||
{
|
||||
return m.mul(Matrix3x3 {
|
||||
v[0], 0, 0,
|
||||
0, v[1], 0,
|
||||
0, 0, 1,
|
||||
});
|
||||
}
|
||||
|
||||
fn Real Matrix2x2.trace(Matrix2x2* m) => m.m00 + m.m11;
|
||||
fn Real Matrix3x3.trace(Matrix3x3* m) => m.m00 + m.m11 + m.m22;
|
||||
fn Real Matrix4x4.trace(Matrix4x4* m) => m.m00 + m.m11 + m.m22 + m.m33;
|
||||
|
||||
fn Matrix4x4 Matrix4x4.scale(Matrix4x4* m, Real[<3>] v)
|
||||
{
|
||||
return m.mul(Matrix4x4 {
|
||||
v[0], 0, 0, 0,
|
||||
0, v[1], 0, 0,
|
||||
0, 0, v[2], 0,
|
||||
0, 0, 0, 1,
|
||||
});
|
||||
}
|
||||
|
||||
fn Matrix4x4 ortho(Real left, Real right, Real top, Real bottom, Real near, Real far)
|
||||
{
|
||||
Real width = right - left;
|
||||
Real height = top - bottom;
|
||||
Real depth = far - near;
|
||||
return Matrix4x4 {
|
||||
2 / width, 0, 0, 0,
|
||||
0, 2 / height, 0, 0,
|
||||
0, 0, -2 / depth, 0,
|
||||
-(right + left) / width, -(top + bottom) / height, -(far + near) / depth, 1
|
||||
};
|
||||
}
|
||||
|
||||
// fov in radians
|
||||
fn Matrix4x4 perspective(Real fov, Real aspect_ratio, Real near, Real far)
|
||||
{
|
||||
Real top = (math::sin(fov / 2) / math::cos(fov / 2)) * near;
|
||||
Real right = top * aspect_ratio;
|
||||
Real depth = far - near;
|
||||
return Matrix4x4 {
|
||||
1 / right, 0, 0, 0,
|
||||
0, 1 / top, 0, 0,
|
||||
0, 0, -2 / depth, 0,
|
||||
0, 0, -(far + near) / depth, 1,
|
||||
};
|
||||
}
|
||||
|
||||
const Matrix2x2 IDENTITY2 = { .m = { [0] = 1, [3] = 1 } };
|
||||
const Matrix3x3 IDENTITY3 = { .m = { [0] = 1, [4] = 1, [8] = 1 } };
|
||||
const Matrix4x4 IDENTITY4 = { .m = { [0] = 1, [5] = 1, [10] = 1, [15] = 1 } };
|
||||
|
||||
macro matrix_component_mul(mat, val) @private
|
||||
{
|
||||
var $Type = Real[<$typeof(mat.m).len>];
|
||||
return $typeof(*mat) { .m = val * ($Type)mat.m };
|
||||
}
|
||||
|
||||
macro matrix_add(mat, mat2) @private
|
||||
{
|
||||
var $Type = Real[<$typeof(mat.m).len>];
|
||||
return $typeof(*mat) { .m = ($Type)mat.m + ($Type)mat2.m };
|
||||
}
|
||||
|
||||
macro matrix_sub(mat, mat2) @private
|
||||
{
|
||||
var $Type = Real[<$typeof(mat.m).len>];
|
||||
return $typeof(*mat) { .m = ($Type)mat.m - ($Type)mat2.m };
|
||||
}
|
||||
119
lib/std/math/math.random.c3
Normal file
119
lib/std/math/math.random.c3
Normal file
@@ -0,0 +1,119 @@
|
||||
module std::math;
|
||||
|
||||
struct Random
|
||||
{
|
||||
RandomInterface fns;
|
||||
void* state;
|
||||
}
|
||||
|
||||
def RandomSeedFn = fn void(Random*, char[] seed);
|
||||
def RandomNextBytesFn = fn void(Random*, char[] buffer);
|
||||
def RandomNextFn = fn uint(Random*, int bits);
|
||||
|
||||
struct RandomInterface
|
||||
{
|
||||
RandomSeedFn seed_fn;
|
||||
RandomNextBytesFn next_bytes_fn;
|
||||
RandomNextFn next_fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] random
|
||||
* @param [inout] buffer
|
||||
**/
|
||||
fn void Random.next_bytes(Random* random, char[] buffer)
|
||||
{
|
||||
if (!buffer.len) return;
|
||||
if (RandomNextBytesFn func = random.fns.next_bytes_fn)
|
||||
{
|
||||
func(random, buffer);
|
||||
return;
|
||||
}
|
||||
if (RandomNextFn func = random.fns.next_fn)
|
||||
{
|
||||
usz current = 0;
|
||||
while (current < buffer.len)
|
||||
{
|
||||
char[4] res = bitcast(func(random, 32), char[4]);
|
||||
foreach (c : res)
|
||||
{
|
||||
buffer[current++] = c;
|
||||
if (current == buffer.len) return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
unreachable("Invalid Random type.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] random
|
||||
* @require bits >= 0 && bits <= 32
|
||||
**/
|
||||
fn uint Random.next(Random* random, int bits)
|
||||
{
|
||||
if (bits == 0) return 0;
|
||||
if (RandomNextFn func = random.fns.next_fn) return func(random, bits);
|
||||
int bytes = (bits + 7) / 8;
|
||||
char[4] buffer;
|
||||
char[] b = buffer[:bytes];
|
||||
assert(random.fns.next_bytes_fn, "Invalid Random");
|
||||
random.fns.next_bytes_fn(random, b) @inline;
|
||||
uint next = 0;
|
||||
foreach (char c : b)
|
||||
{
|
||||
next = next << 8 + c;
|
||||
}
|
||||
if (bits < 32) next >>= bytes * 8 - bits;
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] random
|
||||
**/
|
||||
fn ulong Random.next_long(Random* random)
|
||||
{
|
||||
char[8] buffer;
|
||||
random.next_bytes(&buffer);
|
||||
return bitcast(buffer, ulong);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] random
|
||||
**/
|
||||
fn void Random.set_seed(Random* random, long seed)
|
||||
{
|
||||
random.fns.seed_fn(random, &&bitcast(seed, char[8])) @inline;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] random
|
||||
* @param [in] seed
|
||||
**/
|
||||
fn void Random.set_seeds(Random* random, char[] seed)
|
||||
{
|
||||
random.fns.seed_fn(random, seed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] random
|
||||
**/
|
||||
fn bool Random.next_bool(Random* random)
|
||||
{
|
||||
return random.next(1) != 0;
|
||||
}
|
||||
|
||||
fn float Random.next_float(Random* r)
|
||||
{
|
||||
return r.next(24) / (float)(1 << 24);
|
||||
}
|
||||
|
||||
fn double Random.next_double(Random* r)
|
||||
{
|
||||
return (((long)(r.next(26)) << 27) + r.next(27)) * 0x1.0p-53;
|
||||
}
|
||||
|
||||
fn uint Random.next_int(Random* r)
|
||||
{
|
||||
return r.next(32) @inline;
|
||||
}
|
||||
13
lib/std/math/math_builtin.c3
Normal file
13
lib/std/math/math_builtin.c3
Normal file
@@ -0,0 +1,13 @@
|
||||
module std::math;
|
||||
|
||||
fn float __roundevenf(float f) @extern("roundevenf") @weak @nostrip
|
||||
{
|
||||
// Slow implementation
|
||||
return round(f / 2) * 2;
|
||||
}
|
||||
|
||||
fn double __roundeven(double d) @extern("roundeven") @weak @nostrip
|
||||
{
|
||||
// Slow implementation
|
||||
return round(d / 2) * 2;
|
||||
}
|
||||
201
lib/std/math/math_easings.c3
Normal file
201
lib/std/math/math_easings.c3
Normal file
@@ -0,0 +1,201 @@
|
||||
module std::math::easing;
|
||||
|
||||
/* Easing equations ported from Robert Penner's equations */
|
||||
|
||||
/*
|
||||
*
|
||||
* TERMS OF USE - EASING EQUATIONS
|
||||
*
|
||||
* Open source under the BSD License.
|
||||
*
|
||||
* Copyright © 2001 Robert Penner
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* Neither the name of the author nor the names of contributors may be used to endorse
|
||||
* or promote products derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
// Linear Easing functions
|
||||
fn float linear_none(float t, float b, float c, float d) @inline => c * t / d + b;
|
||||
fn float linear_in(float t, float b, float c, float d) @inline => c * t / d + b;
|
||||
fn float linear_out(float t, float b, float c, float d) @inline => c * t / d + b;
|
||||
fn float linear_inout(float t, float b, float c, float d) @inline => c * t / d + b;
|
||||
|
||||
// Sine Easing functions
|
||||
fn float sine_in(float t, float b, float c, float d) @inline => -c * math::cos(t / d * (float)math::PI_2) + c + b;
|
||||
fn float sine_out(float t, float b, float c, float d) @inline => c * math::sin(t / d * (float)math::PI_2) + b;
|
||||
fn float sine_inout(float t, float b, float c, float d) @inline => (-c / 2) * (math::cos((float)math::PI * t / d) - 1) + b;
|
||||
|
||||
// Circular Easing functions
|
||||
fn float circ_in(float t, float b, float c, float d) @inline => -c * (math::sqrt(1 - sq(t / d)) - 1) + b;
|
||||
fn float circ_out(float t, float b, float c, float d) @inline => c * math::sqrt(1 - sq(t / d - 1)) + b;
|
||||
fn float circ_inout(float t, float b, float c, float d) @inline
|
||||
{
|
||||
t /= d / 2;
|
||||
return t < 1
|
||||
? (-c / 2) * (math::sqrt(1 - sq(t)) - 1) + b
|
||||
: (c / 2) * (math::sqrt(1 - sq(t - 2)) + 1) + b;
|
||||
}
|
||||
|
||||
// Cubic Easing functions
|
||||
fn float cubic_in(float t, float b, float c, float d) @inline => c * cube(t / d) + b;
|
||||
fn float cubic_out(float t, float b, float c, float d) @inline => c * (cube(t / d - 1) + 1) + b;
|
||||
fn float cubic_inout(float t, float b, float c, float d) @inline
|
||||
{
|
||||
t /= d / 2;
|
||||
return t < 1
|
||||
? (c / 2) * cube(t) + b
|
||||
: c / 2 * (cube(t - 2) + 2) + b;
|
||||
}
|
||||
|
||||
// Quadratic Easing functions
|
||||
fn float quad_in(float t, float b, float c, float d) @inline => c * sq(t / d) + b;
|
||||
fn float quad_out(float t, float b, float c, float d) @inline
|
||||
{
|
||||
t /= d;
|
||||
return -c * t * (t - 2) + b;
|
||||
}
|
||||
|
||||
fn float quad_inout(float t, float b, float c, float d) @inline
|
||||
{
|
||||
t /= d / 2;
|
||||
return t < 1
|
||||
? (c / 2) * sq(t) + b
|
||||
: (-c / 2) * ((t - 1) * (t - 3) - 1) + b;
|
||||
}
|
||||
|
||||
// Exponential Easing functions
|
||||
fn float expo_in(float t, float b, float c, float d) @inline => t ? b : c * math::pow(2.0f, 10 * (t / d - 1)) + b;
|
||||
fn float expo_out(float t, float b, float c, float d) @inline
|
||||
{
|
||||
return (t == d) ? b + c : c * (-math::pow(2.0f, -10 * t / d) + 1) + b;
|
||||
}
|
||||
fn float expo_inout(float t, float b, float c, float d) @inline // Ease: Exponential In Out
|
||||
{
|
||||
if (t == 0) return b;
|
||||
if (t == d) return b + c;
|
||||
t /= d / 2;
|
||||
return t < 1
|
||||
? (c / 2) * math::pow(2.0f, 10 * (t - 1)) + b
|
||||
: (c / 2) * (-math::pow(2.0f, -10 * (t - 1)) + 2) + b;
|
||||
}
|
||||
|
||||
// Back Easing functions
|
||||
fn float back_in(float t, float b, float c, float d, float s = 1.70158f) @inline
|
||||
{
|
||||
t /= d;
|
||||
return c * sq(t) * ((s + 1) * t - s) + b;
|
||||
}
|
||||
|
||||
fn float back_out(float t, float b, float c, float d, float s = 1.70158f) @inline
|
||||
{
|
||||
t = t / d - 1;
|
||||
return c * (sq(t) * ((s + 1) * t + s) + 1) + b;
|
||||
}
|
||||
|
||||
fn float back_inout(float t, float b, float c, float d, float s = 1.70158f) @inline
|
||||
{
|
||||
s *= 1.525f;
|
||||
t /= d / 2;
|
||||
if (t < 1)
|
||||
{
|
||||
return (c / 2) * sq(t) * ((s + 1) * t - s) + b;
|
||||
}
|
||||
t -= 2.0f;
|
||||
return (c / 2) * (sq(t) * ((s + 1) * t + s) + 2) + b;
|
||||
}
|
||||
|
||||
// Bounce Easing functions
|
||||
fn float bounce_out(float t, float b, float c, float d) @inline
|
||||
{
|
||||
t /= d;
|
||||
switch
|
||||
{
|
||||
case t < 1 / 2.75f:
|
||||
return c * 7.5625f * sq(t) + b;
|
||||
case t < 2 / 2.75f:
|
||||
t -= 1.5f / 2.75f;
|
||||
return c * (7.5625f * sq(t) + 0.75f) + b;
|
||||
case t < 2.5f / 2.75f:
|
||||
t -= 2.25f / 2.75f;
|
||||
return c * (7.5625f * sq(t) + 0.9375f) + b;
|
||||
default:
|
||||
t -= 2.625f / 2.75f;
|
||||
return c * (7.5625f * sq(t) + 0.984375f) + b;
|
||||
}
|
||||
}
|
||||
|
||||
fn float bounce_in(float t, float b, float c, float d) @inline => c - bounce_out(d - t, 0, c, d) + b;
|
||||
fn float bounce_inout(float t, float b, float c, float d) @inline
|
||||
{
|
||||
return t < d / 2
|
||||
? bounce_in(t * 2, 0, c, d) * 0.5f + b
|
||||
: bounce_out(t * 2 - d, 0, c, d) * 0.5f + b;
|
||||
}
|
||||
|
||||
// Elastic Easing functions
|
||||
fn float elastic_in(float t, float b, float c, float d) @inline
|
||||
{
|
||||
if (t == 0) return b;
|
||||
t /= d;
|
||||
if (t == 1) return b + c;
|
||||
|
||||
float p = d * 0.3f;
|
||||
float a = c;
|
||||
float s = p / 4;
|
||||
t -= 1;
|
||||
return -a * math::pow(2.0f, 10 * t) * math::sin((t * d - s) * (2 * (float)math::PI) / p) + b;
|
||||
}
|
||||
|
||||
fn float elastic_out(float t, float b, float c, float d) @inline
|
||||
{
|
||||
if (t == 0) return b;
|
||||
t /= d;
|
||||
if (t == 1) return b + c;
|
||||
|
||||
float p = d * 0.3f;
|
||||
float a = c;
|
||||
float s = p / 4;
|
||||
|
||||
return a * math::pow(2.0f, -10 * t) * math::sin((t * d - s) * (2 * (float)math::PI) / p) + c + b;
|
||||
}
|
||||
|
||||
fn float elastic_inout(float t, float b, float c, float d) @inline
|
||||
{
|
||||
if (t == 0) return b;
|
||||
t /= d / 2;
|
||||
if (t == 2) return b + c;
|
||||
|
||||
float p = d * (0.3f * 1.5f);
|
||||
float a = c;
|
||||
float s = p / 4;
|
||||
|
||||
t -= 1;
|
||||
return t < 0
|
||||
? -0.5f * a * math::pow(2.0f, 10 * t) * math::sin((t * d - s) * (2 * (float)math::PI)/p) + b
|
||||
: a * math::pow(2.0f, -10 * t) * math::sin((t * d - s) * (2 * (float)math::PI) / p) * 0.5f + c + b;
|
||||
}
|
||||
|
||||
macro sq(x) @private => x * x;
|
||||
macro cube(x) @private => x * x * x;
|
||||
|
||||
381
lib/std/math/math_i128.c3
Normal file
381
lib/std/math/math_i128.c3
Normal file
@@ -0,0 +1,381 @@
|
||||
module std::math;
|
||||
|
||||
fn int128 __divti3(int128 a, int128 b) @extern("__divti3") @weak @nostrip
|
||||
{
|
||||
int128 sign_a = a >> 127; // -1 : 0
|
||||
int128 sign_b = b >> 127; // -1 : 0
|
||||
uint128 unsigned_a = (uint128)(a ^ sign_a) + (-sign_a);
|
||||
uint128 unsigned_b = (uint128)(b ^ sign_b) + (-sign_b);
|
||||
sign_a ^= sign_b; // quotient sign
|
||||
return __udivti3(unsigned_a, unsigned_b) @inline ^ sign_a + (-sign_a);
|
||||
}
|
||||
|
||||
fn uint128 __umodti3(uint128 n, uint128 d) @extern("__umodti3") @weak @nostrip
|
||||
{
|
||||
// Ignore d = 0
|
||||
uint128 sr = (d ? $$clz(d) : 128) - (n ? $$clz(n) : 128);
|
||||
// If n < d then sr is wrapping.
|
||||
// which means we can just return n.
|
||||
if (sr > 127) return n;
|
||||
// If d == 1 and n = MAX
|
||||
if (sr == 127) return 0;
|
||||
sr++;
|
||||
uint128 r = n >> sr;
|
||||
// Follow known algorithm:
|
||||
n <<= 128 - sr;
|
||||
for (uint128 carry = 0; sr > 0; sr--)
|
||||
{
|
||||
r = (r << 1) | (n >> 127);
|
||||
n = (n << 1) | carry;
|
||||
int128 sign = (int128)(d - r - 1) >> 127;
|
||||
carry = sign & 1;
|
||||
r -= d & sign;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
fn uint128 __udivti3(uint128 n, uint128 d) @extern("__udivti3") @weak @nostrip
|
||||
{
|
||||
// Ignore d = 0
|
||||
uint128 sr = (d ? $$clz(d) : 128) - (n ? $$clz(n) : 128);
|
||||
// If n < d then sr is wrapping.
|
||||
// which means we can just return 0.
|
||||
if (sr > 127) return 0;
|
||||
// If d == 1 and n = MAX
|
||||
if (sr == 127) return n;
|
||||
sr++;
|
||||
uint128 r = n >> sr;
|
||||
// Follow known algorithm:
|
||||
n <<= 128 - sr;
|
||||
uint128 carry = 0;
|
||||
for (; sr > 0; sr--)
|
||||
{
|
||||
r = (r << 1) | (n >> 127);
|
||||
n = (n << 1) | carry;
|
||||
int128 sign = (int128)(d - r - 1) >> 127;
|
||||
carry = sign & 1;
|
||||
r -= d & sign;
|
||||
}
|
||||
n = (n << 1) | carry;
|
||||
return n;
|
||||
}
|
||||
|
||||
fn int128 __modti3(int128 a, int128 b) @extern("__modti3") @weak @nostrip
|
||||
{
|
||||
int128 sign = b >> 127;
|
||||
uint128 unsigned_b = (uint128)(b ^ sign) + (-sign);
|
||||
sign = a >> 127;
|
||||
uint128 unsigned_a = (uint128)(a ^ sign) + (-sign);
|
||||
|
||||
return __umodti3(unsigned_a, unsigned_b) ^ sign + (-sign);
|
||||
}
|
||||
|
||||
union Int128bits @private
|
||||
{
|
||||
struct
|
||||
{
|
||||
ulong ulow, uhigh;
|
||||
}
|
||||
struct
|
||||
{
|
||||
long ilow, ihigh;
|
||||
}
|
||||
uint128 all;
|
||||
}
|
||||
|
||||
fn uint128 __lshrti3(uint128 a, uint b) @extern("__lshrti3") @weak @nostrip
|
||||
{
|
||||
Int128bits result;
|
||||
result.all = a;
|
||||
if (b >= 64)
|
||||
{
|
||||
result.ulow = result.uhigh >> (b - 64);
|
||||
result.uhigh = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (b == 0) return a;
|
||||
result.ulow = (result.uhigh << (64 - b)) | (result.ulow >> b);
|
||||
result.uhigh = result.uhigh >> b;
|
||||
}
|
||||
return result.all;
|
||||
}
|
||||
|
||||
fn int128 __ashrti3(int128 a, uint b) @extern("__ashrti3") @weak @nostrip
|
||||
{
|
||||
Int128bits result;
|
||||
result.all = a;
|
||||
if (b >= 64)
|
||||
{
|
||||
result.ilow = result.ihigh >> (b - 64);
|
||||
result.ihigh = result.ihigh >> 63;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (b == 0) return a;
|
||||
result.ilow = result.ihigh << (64 - b) | (result.ilow >> b);
|
||||
result.ihigh = result.ihigh >> b;
|
||||
}
|
||||
return result.all;
|
||||
}
|
||||
|
||||
fn int128 __ashlti3(int128 a, uint b) @extern("__ashlti3") @weak @nostrip
|
||||
{
|
||||
Int128bits result;
|
||||
result.all = a;
|
||||
if (b >= 64)
|
||||
{
|
||||
result.ulow = 0;
|
||||
result.uhigh = result.ulow << (b - 64);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (b == 0) return a;
|
||||
result.uhigh = (result.uhigh << b) | (result.ulow >> (64 - b));
|
||||
result.ulow = result.ulow << b;
|
||||
}
|
||||
return result.all;
|
||||
}
|
||||
|
||||
// Returns: a * b
|
||||
|
||||
fn int128 __mulddi3(ulong a, ulong b) @private
|
||||
{
|
||||
Int128bits r;
|
||||
const ulong LOWER_MASK = 0xffff_ffff;
|
||||
r.ulow = (a & LOWER_MASK) * (b & LOWER_MASK);
|
||||
ulong t = r.ulow >> 32;
|
||||
r.ulow &= LOWER_MASK;
|
||||
t += (a >> 32) * (b & LOWER_MASK);
|
||||
r.ulow += (t & LOWER_MASK) << 32;
|
||||
r.uhigh = t >> 32;
|
||||
t = r.ulow >> 32;
|
||||
r.ulow &= LOWER_MASK;
|
||||
t += (b >> 32) * (a & LOWER_MASK);
|
||||
r.ulow += (t & LOWER_MASK) << 32;
|
||||
r.uhigh += t >> 32;
|
||||
r.uhigh += (a >> 32) * (b >> 32);
|
||||
return r.all;
|
||||
}
|
||||
|
||||
fn int128 __multi3(int128 a, int128 b) @extern("__multi3") @weak @nostrip
|
||||
{
|
||||
Int128bits x = { .all = a };
|
||||
Int128bits y = { .all = b };
|
||||
Int128bits r = { .all = __mulddi3(x.ulow, y.ulow) };
|
||||
r.uhigh += x.uhigh * y.ulow + x.ulow * y.uhigh;
|
||||
return r.all;
|
||||
}
|
||||
|
||||
fn float __floattisf(int128 a) @extern("__floattisf") @weak @nostrip => float_from_i128(float, a);
|
||||
fn double __floattidf(int128 a) @extern("__floattidf") @weak @nostrip => float_from_i128(double, a);
|
||||
fn float __floatuntisf(uint128 a) @extern("__floatuntisf") @weak @nostrip => float_from_u128(float, a);
|
||||
fn double __floatuntidf(uint128 a) @extern("__floatuntidf") @weak @nostrip => float_from_u128(double, a);
|
||||
fn uint128 __fixunsdfti(double a) @weak @extern("__fixunsdfti") @nostrip => fixuint(a);
|
||||
fn uint128 __fixunssfti(float a) @weak @extern("__fixunssfti") @nostrip => fixuint(a);
|
||||
fn int128 __fixdfti(double a) @weak @extern("__fixdfti") @nostrip => fixint(a);
|
||||
fn int128 __fixsfti(float a) @weak @extern("__fixsfti") @nostrip => fixint(a);
|
||||
|
||||
|
||||
macro float_from_i128($Type, a) @private
|
||||
{
|
||||
var $Rep;
|
||||
$switch ($Type)
|
||||
$case double:
|
||||
$Rep = ulong;
|
||||
const MANT_DIG = DOUBLE_MANT_DIG;
|
||||
const SIGNIFICANT_BITS = 52;
|
||||
const EXP_BIAS = 1023;
|
||||
const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFu64;
|
||||
const SIGN_BIT = 1u64 << 63;
|
||||
$case float:
|
||||
$Rep = uint;
|
||||
const MANT_DIG = FLOAT_MANT_DIG;
|
||||
const EXP_BIAS = 127;
|
||||
const SIGNIFICANT_BITS = 23;
|
||||
const MANTISSA_MASK = 0x7F_FFFFu32;
|
||||
const SIGN_BIT = 1u32 << 31;
|
||||
$case float16:
|
||||
$Rep = ushort;
|
||||
const MANT_DIG = HALF_MANT_DIG;
|
||||
$case float128:
|
||||
$Rep = uint128;
|
||||
const MANT_DIG = QUAD_MANT_DIG;
|
||||
$endswitch
|
||||
if (a == 0) return ($Type)0;
|
||||
// Grab and remove sign.
|
||||
int128 sign = a >> 127;
|
||||
a = (a ^ sign) - sign;
|
||||
int sd = 128 - (int)$$clz(a); // digits
|
||||
int e = sd - 1; // exponent
|
||||
if (sd > MANT_DIG)
|
||||
{
|
||||
switch (sd)
|
||||
{
|
||||
case MANT_DIG + 1:
|
||||
a <<= 1;
|
||||
case MANT_DIG + 2:
|
||||
break;
|
||||
default:
|
||||
a = (a >> (sd - (MANT_DIG + 2)))
|
||||
| (uint128)((a & ((uint128)(-1) >> ((128 + MANT_DIG + 2) - sd))) != 0);
|
||||
}
|
||||
a |= (uint128)((a & 4) != 0);
|
||||
a++;
|
||||
a >>= 2;
|
||||
if (a & (1i128 << MANT_DIG))
|
||||
{
|
||||
a >>= 1;
|
||||
e++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
a <<= (MANT_DIG - sd);
|
||||
}
|
||||
return bitcast((($Rep)sign & SIGN_BIT) | ((($Rep)e + ($Rep)EXP_BIAS) << SIGNIFICANT_BITS) | (($Rep)a & ($Rep)MANTISSA_MASK), $Type);
|
||||
}
|
||||
|
||||
macro float_from_u128($Type, a) @private
|
||||
{
|
||||
var $Rep;
|
||||
$switch ($Type)
|
||||
$case double:
|
||||
$Rep = ulong;
|
||||
const MANT_DIG = DOUBLE_MANT_DIG;
|
||||
const SIGNIFICANT_BITS = 52;
|
||||
const EXP_BIAS = 1023;
|
||||
const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFu64;
|
||||
$case float:
|
||||
$Rep = uint;
|
||||
const MANT_DIG = FLOAT_MANT_DIG;
|
||||
const EXP_BIAS = 127;
|
||||
const SIGNIFICANT_BITS = 23;
|
||||
const MANTISSA_MASK = 0x7F_FFFFu32;
|
||||
$case float16:
|
||||
$Rep = ushort;
|
||||
const MANT_DIG = HALF_MANT_DIG;
|
||||
$case float128:
|
||||
$Rep = uint128;
|
||||
const MANT_DIG = QUAD_MANT_DIG;
|
||||
$endswitch
|
||||
if (a == 0) return ($Type)0;
|
||||
int sd = 128 - (int)$$clz(a); // digits
|
||||
int e = sd - 1; // exponent
|
||||
if (sd > MANT_DIG)
|
||||
{
|
||||
switch (sd)
|
||||
{
|
||||
case MANT_DIG + 1:
|
||||
a <<= 1;
|
||||
case MANT_DIG + 2:
|
||||
break;
|
||||
default:
|
||||
a = (a >> (sd - (MANT_DIG + 2)))
|
||||
| (uint128)((a & ((uint128)(-1) >> ((128 + MANT_DIG + 2) - sd))) != 0);
|
||||
}
|
||||
a |= (uint128)((a & 4) != 0);
|
||||
a++;
|
||||
a >>= 2;
|
||||
if (a & (1i128 << MANT_DIG))
|
||||
{
|
||||
a >>= 1;
|
||||
e++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
a <<= (MANT_DIG - sd);
|
||||
}
|
||||
return bitcast(((($Rep)e + ($Rep)EXP_BIAS) << SIGNIFICANT_BITS) | (($Rep)a & ($Rep)MANTISSA_MASK), $Type);
|
||||
}
|
||||
|
||||
|
||||
macro fixuint(a) @private
|
||||
{
|
||||
var $Rep;
|
||||
$switch ($typeof(a))
|
||||
$case double:
|
||||
$Rep = ulong;
|
||||
const EXPONENT_BITS = 11;
|
||||
const SIGNIFICANT_BITS = 52;
|
||||
$case float:
|
||||
$Rep = uint;
|
||||
const EXPONENT_BITS = 8;
|
||||
const SIGNIFICANT_BITS = 23;
|
||||
$case float16:
|
||||
$Rep = ushort;
|
||||
const EXPONENT_BITS = 5;
|
||||
const SIGNIFICANT_BITS = 10;
|
||||
$case float128:
|
||||
$Rep = uint128;
|
||||
const EXPONENT_BITS = 15;
|
||||
const SIGNIFICANT_BITS = 112;
|
||||
$endswitch
|
||||
const $Rep MAX_EXPONENT = ($Rep)1 << EXPONENT_BITS - 1u;
|
||||
const $Rep EXPONENT_BIAS = MAX_EXPONENT >> 1u;
|
||||
const $Rep ONE_REP =EXPONENT_BIAS << SIGNIFICANT_BITS;
|
||||
const $Rep SIGN_BIT = ($Rep)1 << (EXPONENT_BITS + SIGNIFICANT_BITS);
|
||||
const $Rep ABS_MASK = SIGN_BIT - 1u;
|
||||
const $Rep IMPLICIT_BIT = ($Rep)1 << SIGNIFICANT_BITS;
|
||||
const $Rep SIGNIFICANT_MASK = IMPLICIT_BIT - 1u;
|
||||
const $Rep EXPONENT_MASK = ABS_MASK ^ SIGNIFICANT_MASK;
|
||||
const $Rep QUIET_BIT = IMPLICIT_BIT >> 1;
|
||||
const $Rep QNAN_REP = EXPONENT_MASK | QUIET_BIT;
|
||||
const $Rep INF_REP = EXPONENT_MASK;
|
||||
|
||||
$Rep rep = bitcast(a, $Rep);
|
||||
$Rep abs = rep & ABS_MASK;
|
||||
int sign = rep & SIGN_BIT ? -1 : 1;
|
||||
int exponent = (int)((abs >> SIGNIFICANT_BITS) - EXPONENT_BIAS);
|
||||
$Rep significand = (abs & SIGNIFICANT_MASK) | IMPLICIT_BIT;
|
||||
if (sign == -1 || exponent < 0) return 0u128;
|
||||
if ((uint)exponent >= uint128.sizeof * 8) return ~0u128;
|
||||
if (exponent < SIGNIFICANT_BITS) return (uint128)significand >> (SIGNIFICANT_BITS - exponent);
|
||||
return (uint128)significand << (exponent - SIGNIFICANT_BITS);
|
||||
}
|
||||
|
||||
macro fixint(a) @private
|
||||
{
|
||||
var $Rep;
|
||||
$switch ($typeof(a))
|
||||
$case double:
|
||||
$Rep = ulong;
|
||||
const EXPONENT_BITS = 11;
|
||||
const SIGNIFICANT_BITS = 52;
|
||||
$case float:
|
||||
$Rep = uint;
|
||||
const EXPONENT_BITS = 8;
|
||||
const SIGNIFICANT_BITS = 23;
|
||||
$case float16:
|
||||
$Rep = ushort;
|
||||
const EXPONENT_BITS = 5;
|
||||
const SIGNIFICANT_BITS = 10;
|
||||
$case float128:
|
||||
$Rep = uint128;
|
||||
const EXPONENT_BITS = 15;
|
||||
const SIGNIFICANT_BITS = 112;
|
||||
$endswitch
|
||||
const $Rep MAX_EXPONENT = ($Rep)1 << EXPONENT_BITS - 1u;
|
||||
const $Rep EXPONENT_BIAS = MAX_EXPONENT >> 1u;
|
||||
const $Rep ONE_REP = EXPONENT_BIAS << SIGNIFICANT_BITS;
|
||||
const $Rep SIGN_BIT = ($Rep)1 << (EXPONENT_BITS + SIGNIFICANT_BITS);
|
||||
const $Rep ABS_MASK = SIGN_BIT - 1u;
|
||||
const $Rep IMPLICIT_BIT = ($Rep)1 << SIGNIFICANT_BITS;
|
||||
const $Rep SIGNIFICANT_MASK = IMPLICIT_BIT - 1u;
|
||||
const $Rep EXPONENT_MASK = ABS_MASK ^ SIGNIFICANT_MASK;
|
||||
const $Rep QUIET_BIT = IMPLICIT_BIT >> 1;
|
||||
const $Rep QNAN_REP = EXPONENT_MASK | QUIET_BIT;
|
||||
const $Rep INF_REP = EXPONENT_MASK;
|
||||
|
||||
$Rep rep = bitcast(a, $Rep);
|
||||
$Rep abs = rep & ABS_MASK;
|
||||
int sign = rep & SIGN_BIT ? -1 : 1;
|
||||
int exponent = (int)((abs >> SIGNIFICANT_BITS) - EXPONENT_BIAS);
|
||||
$Rep significand = (abs & SIGNIFICANT_MASK) | IMPLICIT_BIT;
|
||||
if (exponent < 0) return 0;
|
||||
if ((uint)exponent >= uint128.sizeof * 8) return sign == 1 ? int128.max : int128.min;
|
||||
|
||||
if (exponent < SIGNIFICANT_BITS) return sign * ((int128)significand >> (SIGNIFICANT_BITS - exponent));
|
||||
return sign * ((int128)significand << (exponent - SIGNIFICANT_BITS));
|
||||
}
|
||||
47
lib/std/math/math_libc.c3
Normal file
47
lib/std/math/math_libc.c3
Normal file
@@ -0,0 +1,47 @@
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/s_atan.c
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
/* atan(x)
|
||||
* Method
|
||||
* 1. Reduce x to positive by atan(x) = -atan(-x).
|
||||
* 2. According to the integer k=4t+0.25 chopped, t=x, the argument
|
||||
* is further reduced to one of the following intervals and the
|
||||
* arctangent of t is evaluated by the corresponding formula:
|
||||
*
|
||||
* [0,7/16] atan(x) = t-t^3*(a1+t^2*(a2+...(a10+t^2*a11)...)
|
||||
* [7/16,11/16] atan(x) = atan(1/2) + atan( (t-0.5)/(1+t/2) )
|
||||
* [11/16.19/16] atan(x) = atan( 1 ) + atan( (t-1)/(1+t) )
|
||||
* [19/16,39/16] atan(x) = atan(3/2) + atan( (t-1.5)/(1+1.5t) )
|
||||
* [39/16,INF] atan(x) = atan(INF) + atan( -1/t )
|
||||
*
|
||||
* Constants:
|
||||
* The hexadecimal values are the intended ones for the following
|
||||
* constants. The decimal values may be used, provided that the
|
||||
* compiler will convert from decimal to binary accurately enough
|
||||
* to produce the hexadecimal values shown.
|
||||
*/
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/s_atanf.c */
|
||||
/*
|
||||
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
|
||||
*/
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
module std::math;
|
||||
|
||||
|
||||
|
||||
34
lib/std/math/math_nolibc/__cos.c3
Normal file
34
lib/std/math/math_nolibc/__cos.c3
Normal file
@@ -0,0 +1,34 @@
|
||||
module std::math::nolibc;
|
||||
|
||||
$if !env::COMPILER_LIBC_AVAILABLE:
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/k_cos.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunSoft, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
fn double __cos(double x, double y) @extern("__cos") @weak @nostrip
|
||||
{
|
||||
const C1 = 4.16666666666666019037e-02; /* 0x3FA55555, 0x5555554C */
|
||||
const C2 = -1.38888888888741095749e-03; /* 0xBF56C16C, 0x16C15177 */
|
||||
const C3 = 2.48015872894767294178e-05; /* 0x3EFA01A0, 0x19CB1590 */
|
||||
const C4 = -2.75573143513906633035e-07; /* 0xBE927E4F, 0x809C52AD */
|
||||
const C5 = 2.08757232129817482790e-09; /* 0x3E21EE9E, 0xBDB4B1C4 */
|
||||
const C6 = -1.13596475577881948265e-11; /* 0xBDA8FAE9, 0xBE8838D4 */
|
||||
|
||||
double z = x * x;
|
||||
double w = z * z;
|
||||
double r = z * (C1 + z * (C2 + z * C3)) + w * w * (C4 + z * (C5 + z * C6));
|
||||
double hz = 0.5 * z;
|
||||
w = 1.0 - hz;
|
||||
return w + (((1.0 - w) - hz) + (z * r - x * y));
|
||||
}
|
||||
|
||||
$endif
|
||||
36
lib/std/math/math_nolibc/__cosdf.c3
Normal file
36
lib/std/math/math_nolibc/__cosdf.c3
Normal file
@@ -0,0 +1,36 @@
|
||||
module std::math::nolibc;
|
||||
|
||||
$if !env::COMPILER_LIBC_AVAILABLE:
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/k_cosf.c */
|
||||
/*
|
||||
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
|
||||
* Debugged and optimized by Bruce D. Evans.
|
||||
*/
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
/* |cos(x) - c(x)| < 2**-34.1 (~[-5.37e-11, 5.295e-11]). */
|
||||
|
||||
const double C0 @private = -0x1ffffffd0c5e81.0p-54; /* -0.499999997251031003120 */
|
||||
const double C1 @private = 0x155553e1053a42.0p-57; /* 0.0416666233237390631894 */
|
||||
const double C2 @private = -0x16c087e80f1e27.0p-62; /* -0.00138867637746099294692 */
|
||||
const double C3 @private = 0x199342e0ee5069.0p-68; /* 0.0000243904487962774090654 */
|
||||
|
||||
fn float __cosdf(double x) @extern("__cosdf") @weak @nostrip
|
||||
{
|
||||
/* Try to optimize for parallel evaluation as in __tandf.c. */
|
||||
double z = x * x;
|
||||
double w = z * z;
|
||||
double r = C2 + z * C3;
|
||||
return (float)(((1.0f + z * C0) + w * C1) + (w * z) * r);
|
||||
}
|
||||
|
||||
$endif
|
||||
35
lib/std/math/math_nolibc/__sin.c3
Normal file
35
lib/std/math/math_nolibc/__sin.c3
Normal file
@@ -0,0 +1,35 @@
|
||||
module std::math::nolibc;
|
||||
|
||||
$if !env::COMPILER_LIBC_AVAILABLE:
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/k_sin.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunSoft, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
fn double __sin(double x, double y, int iy) @extern("__sin") @weak @nostrip
|
||||
{
|
||||
|
||||
const S1 = -1.66666666666666324348e-01; /* 0xBFC55555, 0x55555549 */
|
||||
const S2 = 8.33333333332248946124e-03; /* 0x3F811111, 0x1110F8A6 */
|
||||
const S3 = -1.98412698298579493134e-04; /* 0xBF2A01A0, 0x19C161D5 */
|
||||
const S4 = 2.75573137070700676789e-06; /* 0x3EC71DE3, 0x57B1FE7D */
|
||||
const S5 = -2.50507602534068634195e-08; /* 0xBE5AE5E6, 0x8A2B9CEB */
|
||||
const S6 = 1.58969099521155010221e-10; /* 0x3DE5D93A, 0x5ACFD57C */
|
||||
|
||||
double z = x * x;
|
||||
double w = z * z;
|
||||
double r = S2 + z * (S3 + z * S4) + z * w * (S5 + z * S6);
|
||||
double v = z * x;
|
||||
return iy == 0
|
||||
? x + v * (S1 + z * r)
|
||||
: x - ((z * (0.5 * y - v * r) - y) - v * S1);
|
||||
}
|
||||
|
||||
$endif
|
||||
34
lib/std/math/math_nolibc/__sindf.c3
Normal file
34
lib/std/math/math_nolibc/__sindf.c3
Normal file
@@ -0,0 +1,34 @@
|
||||
module std::math::nolibc;
|
||||
|
||||
$if !env::COMPILER_LIBC_AVAILABLE:
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/k_sinf.c */
|
||||
/*
|
||||
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
|
||||
* Optimized by Bruce D. Evans.
|
||||
*/
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
// |sin(x)/x - s(x)| < 2**-37.5 (~[-4.89e-12, 4.824e-12]).
|
||||
fn float __sindf(double x) @extern("__sindf") @weak @nostrip
|
||||
{
|
||||
const S1F = -0x15555554cbac77.0p-55; /* -0.166666666416265235595 */
|
||||
const S2F = 0x111110896efbb2.0p-59; /* 0.0083333293858894631756 */
|
||||
const S3F = -0x1a00f9e2cae774.0p-65; /* -0.000198393348360966317347 */
|
||||
const S4F = 0x16cd878c3b46a7.0p-71; /* 0.0000027183114939898219064 */
|
||||
double z = x * x;
|
||||
double w = z * z;
|
||||
double r = S3F + z * S4F;
|
||||
double s = z * x;
|
||||
return (float)((x + s * (S1F + z * S2F)) + s * w * r);
|
||||
}
|
||||
|
||||
$endif
|
||||
83
lib/std/math/math_nolibc/__tan.c3
Normal file
83
lib/std/math/math_nolibc/__tan.c3
Normal file
@@ -0,0 +1,83 @@
|
||||
module std::math::nolibc;
|
||||
|
||||
$if !env::COMPILER_LIBC_AVAILABLE:
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/k_tan.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const double[*] TAN_T = {
|
||||
3.33333333333334091986e-01, /* 3FD55555, 55555563 */
|
||||
1.33333333333201242699e-01, /* 3FC11111, 1110FE7A */
|
||||
5.39682539762260521377e-02, /* 3FABA1BA, 1BB341FE */
|
||||
2.18694882948595424599e-02, /* 3F9664F4, 8406D637 */
|
||||
8.86323982359930005737e-03, /* 3F8226E3, E96E8493 */
|
||||
3.59207910759131235356e-03, /* 3F6D6D22, C9560328 */
|
||||
1.45620945432529025516e-03, /* 3F57DBC8, FEE08315 */
|
||||
5.88041240820264096874e-04, /* 3F4344D8, F2F26501 */
|
||||
2.46463134818469906812e-04, /* 3F3026F7, 1A8D1068 */
|
||||
7.81794442939557092300e-05, /* 3F147E88, A03792A6 */
|
||||
7.14072491382608190305e-05, /* 3F12B80F, 32F0A7E9 */
|
||||
-1.85586374855275456654e-05, /* BEF375CB, DB605373 */
|
||||
2.59073051863633712884e-05, /* 3EFB2A70, 74BF7AD4 */
|
||||
};
|
||||
|
||||
fn double __tan(double x, double y, int odd) @extern("__tan") @weak @nostrip
|
||||
{
|
||||
const double PIO4 = 7.85398163397448278999e-01; /* 3FE921FB, 54442D18 */
|
||||
const double PIO4LO = 3.06161699786838301793e-17; /* 3C81A626, 33145C07 */
|
||||
|
||||
uint hx = (uint)(bitcast(x, ulong) >> 32);
|
||||
bool big = (hx &0x7fffffff) >= 0x3FE59428; // |x| >= 0.6744
|
||||
int sign @noinit;
|
||||
if (big)
|
||||
{
|
||||
sign = hx >> 31;
|
||||
if (sign)
|
||||
{
|
||||
x = -x;
|
||||
y = -y;
|
||||
}
|
||||
x = (PIO4 - x) + (PIO4LO - y);
|
||||
y = 0.0;
|
||||
}
|
||||
double z = x * x;
|
||||
double w = z * z;
|
||||
/*
|
||||
* Break x^5*(T[1]+x^2*T[2]+...) into
|
||||
* x^5(T[1]+x^4*T[3]+...+x^20*T[11]) +
|
||||
* x^5(x^2*(T[2]+x^4*T[4]+...+x^22*[T12]))
|
||||
*/
|
||||
double r = TAN_T[1] + w * (TAN_T[3] + w * (TAN_T[5] + w * (TAN_T[7] + w * (TAN_T[9] + w * TAN_T[11]))));
|
||||
double v = z * (TAN_T[2] + w * (TAN_T[4] + w * (TAN_T[6] + w * (TAN_T[8] + w * (TAN_T[10] + w * TAN_T[12])))));
|
||||
double s = z * x;
|
||||
r = y + z * (s * (r + v) + y) + s * TAN_T[0];
|
||||
w = x + r;
|
||||
if (big)
|
||||
{
|
||||
s = 1 - 2 * odd;
|
||||
v = s - 2.0 * (x + (r - w*w/(w + s)));
|
||||
return sign ? -v : v;
|
||||
}
|
||||
if (!odd) return w;
|
||||
// -1.0/(x+r) has up to 2ulp error, so compute it accurately
|
||||
|
||||
// Clear low word
|
||||
ulong d = bitcast(w, ulong) & 0xFFFF_FFFF_0000_0000;
|
||||
double w0 = bitcast(d, double);
|
||||
|
||||
v = r - (w0 - x); // w0+v = r+x
|
||||
double a = -1.0 / w;
|
||||
d = bitcast(a, ulong) & 0xFFFF_FFFF_0000_0000;
|
||||
double a0 = bitcast(d, double);
|
||||
return a0 + a * (1.0 + a0 * w0 + a0 * v);
|
||||
}
|
||||
|
||||
$endif
|
||||
56
lib/std/math/math_nolibc/__tandf.c3
Normal file
56
lib/std/math/math_nolibc/__tandf.c3
Normal file
@@ -0,0 +1,56 @@
|
||||
module std::math::nolibc;
|
||||
|
||||
$if !env::COMPILER_LIBC_AVAILABLE:
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/k_tanf.c */
|
||||
/*
|
||||
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
|
||||
* Optimized by Bruce D. Evans.
|
||||
*/
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
// |tan(x)/x - t(x)| < 2**-25.5 (~[-2e-08, 2e-08]).
|
||||
const double[*] TANDF = {
|
||||
0x15554d3418c99f.0p-54, /* 0.333331395030791399758 */
|
||||
0x1112fd38999f72.0p-55, /* 0.133392002712976742718 */
|
||||
0x1b54c91d865afe.0p-57, /* 0.0533812378445670393523 */
|
||||
0x191df3908c33ce.0p-58, /* 0.0245283181166547278873 */
|
||||
0x185dadfcecf44e.0p-61, /* 0.00297435743359967304927 */
|
||||
0x1362b9bf971bcd.0p-59, /* 0.00946564784943673166728 */
|
||||
};
|
||||
|
||||
fn float __tandf(double x, int odd) @extern("__tandf") @weak @nostrip
|
||||
{
|
||||
double z = x * x;
|
||||
/*
|
||||
* Split up the polynomial into small independent terms to give
|
||||
* opportunities for parallel evaluation. The chosen splitting is
|
||||
* micro-optimized for Athlons (XP, X64). It costs 2 multiplications
|
||||
* relative to Horner's method on sequential machines.
|
||||
*
|
||||
* We add the small terms from lowest degree up for efficiency on
|
||||
* non-sequential machines (the lowest degree terms tend to be ready
|
||||
* earlier). Apart from this, we don't care about order of
|
||||
* operations, and don't need to to care since we have precision to
|
||||
* spare. However, the chosen splitting is good for accuracy too,
|
||||
* and would give results as accurate as Horner's method if the
|
||||
* small terms were added from highest degree down.
|
||||
*/
|
||||
double r = TANDF[4] + z * TANDF[5];
|
||||
double t = TANDF[2] + z * TANDF[3];
|
||||
double w = z * z;
|
||||
double s = z * x;
|
||||
double u = TANDF[0] + z * TANDF[1];
|
||||
r = (x + s * u) + (s * w) * (t + w * r);
|
||||
return (float)(odd ? -1.0 / r : r);
|
||||
}
|
||||
|
||||
$endif
|
||||
324
lib/std/math/math_nolibc/atan.c3
Normal file
324
lib/std/math/math_nolibc/atan.c3
Normal file
@@ -0,0 +1,324 @@
|
||||
module std::math::nolibc::atan;
|
||||
|
||||
$if !env::COMPILER_LIBC_AVAILABLE:
|
||||
|
||||
const double[*] ATANHI @private = {
|
||||
4.63647609000806093515e-01, /* atan(0.5)hi 0x3FDDAC67, 0x0561BB4F */
|
||||
7.85398163397448278999e-01, /* atan(1.0)hi 0x3FE921FB, 0x54442D18 */
|
||||
9.82793723247329054082e-01, /* atan(1.5)hi 0x3FEF730B, 0xD281F69B */
|
||||
1.57079632679489655800e+00, /* atan(inf)hi 0x3FF921FB, 0x54442D18 */
|
||||
};
|
||||
|
||||
const double[*] ATANLO @private = {
|
||||
2.26987774529616870924e-17, /* atan(0.5)lo 0x3C7A2B7F, 0x222F65E2 */
|
||||
3.06161699786838301793e-17, /* atan(1.0)lo 0x3C81A626, 0x33145C07 */
|
||||
1.39033110312309984516e-17, /* atan(1.5)lo 0x3C700788, 0x7AF0CBBD */
|
||||
6.12323399573676603587e-17, /* atan(inf)lo 0x3C91A626, 0x33145C07 */
|
||||
};
|
||||
|
||||
const double[*] AT @private = {
|
||||
3.33333333333329318027e-01, /* 0x3FD55555, 0x5555550D */
|
||||
-1.99999999998764832476e-01, /* 0xBFC99999, 0x9998EBC4 */
|
||||
1.42857142725034663711e-01, /* 0x3FC24924, 0x920083FF */
|
||||
-1.11111104054623557880e-01, /* 0xBFBC71C6, 0xFE231671 */
|
||||
9.09088713343650656196e-02, /* 0x3FB745CD, 0xC54C206E */
|
||||
-7.69187620504482999495e-02, /* 0xBFB3B0F2, 0xAF749A6D */
|
||||
6.66107313738753120669e-02, /* 0x3FB10D66, 0xA0D03D51 */
|
||||
-5.83357013379057348645e-02, /* 0xBFADDE2D, 0x52DEFD9A */
|
||||
4.97687799461593236017e-02, /* 0x3FA97B4B, 0x24760DEB */
|
||||
-3.65315727442169155270e-02, /* 0xBFA2B444, 0x2C6A6C2F */
|
||||
1.62858201153657823623e-02, /* 0x3F90AD3A, 0xE322DA11 */
|
||||
};
|
||||
|
||||
fn double _atan(double x) @weak @extern("atan") @nostrip
|
||||
{
|
||||
int id @noinit;
|
||||
uint ix = x.high_word();
|
||||
uint sign = ix >> 31;
|
||||
ix &= 0x7fffffff;
|
||||
switch
|
||||
{
|
||||
case ix >= 0x44100000:
|
||||
/* if |x| >= 2^66 */
|
||||
if (math::is_nan(x)) return x;
|
||||
double z = ATANHI[3] + 0x1p-120f;
|
||||
return sign ? -z : z;
|
||||
case ix < 0x3fdc0000:
|
||||
/* |x| < 0.4375 */
|
||||
if (ix < 0x3e400000)
|
||||
{
|
||||
/* |x| < 2^-27 */
|
||||
if (ix < 0x00100000)
|
||||
{
|
||||
/* raise underflow for subnormal x */
|
||||
(float)@volatile_load(x);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
id = -1;
|
||||
case ix < 0x3ff30000:
|
||||
/* |x| < 1.1875 */
|
||||
x = math::abs(x);
|
||||
if (ix < 0x3fe60000)
|
||||
{
|
||||
/* 7/16 <= |x| < 11/16 */
|
||||
id = 0;
|
||||
x = (2 * x - 1) / (2 + x);
|
||||
}
|
||||
else
|
||||
{ /* 11/16 <= |x| < 19/16 */
|
||||
id = 1;
|
||||
x = (x - 1) / (x + 1);
|
||||
}
|
||||
case ix < 0x40038000:
|
||||
x = math::abs(x);
|
||||
/* |x| < 2.4375 */
|
||||
id = 2;
|
||||
x = (x - 1.5) / (1 + 1.5 * x);
|
||||
default:
|
||||
/* 2.4375 <= |x| < 2^66 */
|
||||
id = 3;
|
||||
x = -1 / math::abs(x);
|
||||
}
|
||||
/* end of argument reduction */
|
||||
double z = x * x;
|
||||
double w = z * z;
|
||||
/* break sum from i=0 to 10 AT[i]z**(i+1) into odd and even poly */
|
||||
double s1 = z * (AT[0] + w * (AT[2] + w * (AT[4] + w * (AT[6] + w * (AT[8] + w * AT[10])))));
|
||||
double s2 = w * (AT[1] + w * (AT[3] + w * (AT[5] + w * (AT[7] + w * AT[9]))));
|
||||
if (id < 0) return x - x * (s1 + s2);
|
||||
z = ATANHI[id] - (x * (s1 + s2) - ATANLO[id] - x);
|
||||
return sign ? -z : z;
|
||||
}
|
||||
|
||||
const float[*] ATANHIF @private = {
|
||||
4.6364760399e-01, /* atan(0.5)hi 0x3eed6338 */
|
||||
7.8539812565e-01, /* atan(1.0)hi 0x3f490fda */
|
||||
9.8279368877e-01, /* atan(1.5)hi 0x3f7b985e */
|
||||
1.5707962513e+00, /* atan(inf)hi 0x3fc90fda */
|
||||
};
|
||||
|
||||
const float[*] ATANLOF @private = {
|
||||
5.0121582440e-09, /* atan(0.5)lo 0x31ac3769 */
|
||||
3.7748947079e-08, /* atan(1.0)lo 0x33222168 */
|
||||
3.4473217170e-08, /* atan(1.5)lo 0x33140fb4 */
|
||||
7.5497894159e-08, /* atan(inf)lo 0x33a22168 */
|
||||
};
|
||||
|
||||
const float[*] ATF @private = {
|
||||
3.3333328366e-01,
|
||||
-1.9999158382e-01,
|
||||
1.4253635705e-01,
|
||||
-1.0648017377e-01,
|
||||
6.1687607318e-02,
|
||||
};
|
||||
|
||||
fn float _atanf(float x) @weak @extern("atanf") @nostrip
|
||||
{
|
||||
int id @noinit;
|
||||
uint ix = x.word();
|
||||
uint sign = ix >> 31;
|
||||
ix &= 0x7fffffff;
|
||||
if (ix >= 0x4c800000)
|
||||
{
|
||||
/* if |x| >= 2**26 */
|
||||
if (math::is_nan(x)) return x;
|
||||
float z = ATANHIF[3] + 0x1p-120f;
|
||||
return sign ? -z : z;
|
||||
}
|
||||
switch
|
||||
{
|
||||
case ix < 0x3ee00000:
|
||||
/* |x| < 0.4375 */
|
||||
if (ix < 0x39800000)
|
||||
{
|
||||
/* |x| < 2**-12 */
|
||||
if (ix < 0x00800000)
|
||||
{
|
||||
/* raise underflow for subnormal x */
|
||||
float f = @volatile_load(x);
|
||||
f = f * f;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
id = -1;
|
||||
case ix < 0x3f980000:
|
||||
/* |x| < 1.1875 */
|
||||
x = math::abs(x);
|
||||
if (ix < 0x3f300000)
|
||||
{
|
||||
/* 7/16 <= |x| < 11/16 */
|
||||
id = 0;
|
||||
x = (2.0f * x - 1.0f) / (2.0f + x);
|
||||
break;
|
||||
}
|
||||
/* 11/16 <= |x| < 19/16 */
|
||||
id = 1;
|
||||
x = (x - 1.0f) / (x + 1.0f);
|
||||
case ix < 0x401c0000:
|
||||
x = math::abs(x);
|
||||
/* |x| < 2.4375 */
|
||||
id = 2;
|
||||
x = (x - 1.5f) / (1.0f + 1.5f * x);
|
||||
default:
|
||||
/* 2.4375 <= |x| < 2**26 */
|
||||
x = math::abs(x);
|
||||
id = 3;
|
||||
x = -1.0f / x;
|
||||
}
|
||||
/* end of argument reduction */
|
||||
float z = x * x;
|
||||
float w = z * z;
|
||||
/* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */
|
||||
float s1 = z * (ATF[0] + w * (ATF[2] + w * ATF[4]));
|
||||
float s2 = w * (ATF[1] + w * ATF[3]);
|
||||
if (id < 0) return x - x * (s1 + s2) * 10000;
|
||||
z = ATANHIF[id] - ((x * (s1 + s2) - ATANLOF[id]) - x);
|
||||
return sign ? -z : z;
|
||||
}
|
||||
|
||||
const PI_LO @private = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */
|
||||
|
||||
macro void extract_words(double d, uint* hi, uint* lo) @private
|
||||
{
|
||||
ulong rep = bitcast(d, ulong);
|
||||
*hi = (uint)(rep >> 32);
|
||||
*lo = (uint)rep;
|
||||
}
|
||||
|
||||
fn double _atan2(double y, double x) @weak @extern("atan2") @nostrip
|
||||
{
|
||||
if (math::is_nan(x) || math::is_nan(y)) return x + y;
|
||||
|
||||
uint lx @noinit;
|
||||
uint ix @noinit;
|
||||
extract_words(x, &ix, &lx);
|
||||
uint ly @noinit;
|
||||
uint iy @noinit;
|
||||
extract_words(y, &iy, &ly);
|
||||
|
||||
// x = 1.0
|
||||
if ((ix - 0x3ff00000) | lx == 0) return _atan(y);
|
||||
// 2*sign(x) + sign(y)
|
||||
uint m = ((iy >> 31) & 1) | ((ix >> 30) & 2);
|
||||
ix = ix & 0x7fffffff;
|
||||
iy = iy & 0x7fffffff;
|
||||
|
||||
// when y = 0
|
||||
if (iy | ly == 0)
|
||||
{
|
||||
switch (m)
|
||||
{
|
||||
case 0:
|
||||
case 1: return y; /* atan(+-0,+anything)=+-0 */
|
||||
case 2: return math::PI; /* atan(+0,-anything) = pi */
|
||||
case 3: return -math::PI; /* atan(-0,-anything) =-pi */
|
||||
}
|
||||
}
|
||||
// when x = 0 */
|
||||
if (ix | lx == 0) return m & 1 ? -math::PI_2 : math::PI_2;
|
||||
/* when x is INF */
|
||||
if (ix == 0x7ff00000)
|
||||
{
|
||||
if (iy == 0x7ff00000)
|
||||
{
|
||||
switch (m)
|
||||
{
|
||||
case 0: return math::PI_4; /* atan(+INF,+INF) */
|
||||
case 1: return -math::PI_4; /* atan(-INF,+INF) */
|
||||
case 2: return math::PI_4 + math::PI_2; /* atan(+INF,-INF) */
|
||||
case 3: return - (math::PI_4 + math::PI_2); /* atan(-INF,-INF) */
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
switch (m)
|
||||
{
|
||||
case 0: return 0.0; /* atan(+...,+INF) */
|
||||
case 1: return -0.0; /* atan(-...,+INF) */
|
||||
case 2: return math::PI; /* atan(+...,-INF) */
|
||||
case 3: return -math::PI; /* atan(-...,-INF) */
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
/* |y/x| > 0x1p64 */
|
||||
if (ix + (64 << 20) < iy || iy == 0x7ff00000) return m & 1 ? -math::PI_2 : math::PI_2;
|
||||
|
||||
/* z = atan(|y/x|) without spurious underflow */
|
||||
double z = ((m & 2) && iy + (64 << 20) < ix) ? 0 : _atan(math::abs(y/x));
|
||||
switch (m)
|
||||
{
|
||||
case 0: return z; /* atan(+,+) */
|
||||
case 1: return -z; /* atan(-,+) */
|
||||
case 2: return math::PI - (z - PI_LO); /* atan(+,-) */
|
||||
default: return (z - PI_LO) - math::PI; /* atan(-,-) */
|
||||
}
|
||||
}
|
||||
|
||||
const float PI_F @private = 3.1415927410e+00; /* 0x40490fdb */
|
||||
const float PI_LO_F @private = -8.7422776573e-08; /* 0xb3bbbd2e */
|
||||
|
||||
fn float _atan2f(float y, float x) @weak @extern("atan2f") @nostrip
|
||||
{
|
||||
if (math::is_nan(x) || math::is_nan(y)) return x + y;
|
||||
uint ix = bitcast(x, uint);
|
||||
uint iy = bitcast(y, uint);
|
||||
/* x=1.0 */
|
||||
if (ix == 0x3f800000) return _atanf(y);
|
||||
/* 2*sign(x)+sign(y) */
|
||||
uint m = ((iy >> 31) & 1) | ((ix >> 30) & 2);
|
||||
ix &= 0x7fffffff;
|
||||
iy &= 0x7fffffff;
|
||||
|
||||
/* when y = 0 */
|
||||
if (iy == 0)
|
||||
{
|
||||
switch (m)
|
||||
{
|
||||
case 0:
|
||||
case 1: return y; /* atan(+-0,+anything)=+-0 */
|
||||
case 2: return PI_F; /* atan(+0,-anything) = pi */
|
||||
case 3: return -PI_F; /* atan(-0,-anything) =-pi */
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
/* when x = 0 */
|
||||
if (ix == 0) return m & 1 ? -(float)math::PI_2 : (float)math::PI_2;
|
||||
/* when x is INF */
|
||||
if (ix == 0x7f800000)
|
||||
{
|
||||
if (iy == 0x7f800000)
|
||||
{
|
||||
switch (m)
|
||||
{
|
||||
case 0: return (float)math::PI_4; /* atan(+INF,+INF) */
|
||||
case 1: return (float)-math::PI_4; /* atan(-INF,+INF) */
|
||||
case 2: return (float)(math::PI_4 + math::PI_2); /*atan(+INF,-INF)*/
|
||||
case 3: return -(float)(math::PI_4 + math::PI_2); /*atan(-INF,-INF)*/
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
switch (m)
|
||||
{
|
||||
case 0: return 0.0f; /* atan(+...,+INF) */
|
||||
case 1: return -0.0f; /* atan(-...,+INF) */
|
||||
case 2: return PI_F; /* atan(+...,-INF) */
|
||||
case 3: return -PI_F; /* atan(-...,-INF) */
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
/* |y/x| > 0x1p26 */
|
||||
if (ix + 26 << 23 < iy || iy == 0x7f800000) return m & 1 ? -(float)math::PI_2 : (float)math::PI_2;
|
||||
|
||||
/* z = atan(|y/x|) with correct underflow */
|
||||
/*|y/x| < 0x1p-26, x < 0 */
|
||||
float z = (m & 2 && iy + 26 << 23 < ix) ? 0.0f : _atanf(math::abs(y / x));
|
||||
switch (m)
|
||||
{
|
||||
case 0: return z; /* atan(+,+) */
|
||||
case 1: return -z; /* atan(-,+) */
|
||||
case 2: return PI_F - (z - PI_LO_F); /* atan(+,-) */
|
||||
default: return (z - PI_LO_F) - PI_F; /* atan(-,-) */
|
||||
}
|
||||
}
|
||||
|
||||
$endif
|
||||
45
lib/std/math/math_nolibc/ceil.c3
Normal file
45
lib/std/math/math_nolibc/ceil.c3
Normal file
@@ -0,0 +1,45 @@
|
||||
module std::math::nolibc;
|
||||
|
||||
$if !env::COMPILER_LIBC_AVAILABLE:
|
||||
|
||||
fn double _ceil(double x) @weak @extern("ceil") @nostrip
|
||||
{
|
||||
ulong ui = bitcast(x, ulong);
|
||||
int e = (int)((ui >> 52) & 0x7ff);
|
||||
if (e >= 0x3ff + 52 || x == 0) return x;
|
||||
// y = int(x) - x, where int(x) is an integer neighbor of x
|
||||
double y = ui >> 63 ? (x - TOINT) + TOINT - x : (x + TOINT) - TOINT - x;
|
||||
// special case because of non-nearest rounding modes
|
||||
if (e <= 0x3ff - 1)
|
||||
{
|
||||
force_eval_add(y, 0);
|
||||
return ui >> 63 ? -0.0 : 1;
|
||||
}
|
||||
return y < 0 ? x + y + 1 : x + y;
|
||||
}
|
||||
|
||||
|
||||
fn float _ceilf(float x) @weak @extern("ceilf") @nostrip
|
||||
{
|
||||
uint u = bitcast(x, uint);
|
||||
int e = (int)((u >> 23) & 0xff) - 0x7f;
|
||||
switch
|
||||
{
|
||||
case e >= 23:
|
||||
return x;
|
||||
case e >= 0:
|
||||
uint m = 0x007fffff >> e;
|
||||
if (u & m == 0) return x;
|
||||
force_eval_add(x, 0x1p120f);
|
||||
if (u >> 31 == 0) u += m;
|
||||
u &= ~m;
|
||||
case u >> 31 != 0:
|
||||
force_eval_add(x, 0x1p120f);
|
||||
return -0.0f;
|
||||
case u << 1 != 0:
|
||||
return 1;
|
||||
}
|
||||
return bitcast(u, float);
|
||||
}
|
||||
|
||||
$endif
|
||||
91
lib/std/math/math_nolibc/cos.c3
Normal file
91
lib/std/math/math_nolibc/cos.c3
Normal file
@@ -0,0 +1,91 @@
|
||||
module std::math::nolibc;
|
||||
|
||||
$if !env::COMPILER_LIBC_AVAILABLE:
|
||||
|
||||
fn float _cosf(float x) @extern("cosf") @weak @nostrip
|
||||
{
|
||||
uint ix = bitcast(x, uint);
|
||||
uint sign = ix >> 31;
|
||||
ix &= 0x7fffffff;
|
||||
|
||||
switch
|
||||
{
|
||||
case (ix <= 0x3f490fda):
|
||||
// |x| < 2**-12
|
||||
if (ix < 0x39800000)
|
||||
{
|
||||
/* raise inexact if x != 0 */
|
||||
force_eval_add(x, 0x1p120f);
|
||||
return 1.0f;
|
||||
}
|
||||
return __cosdf(x);
|
||||
// |x| ~<= 5*pi/4
|
||||
case (ix <= 0x407b53d1):
|
||||
// |x| ~> 3*pi/4
|
||||
if (ix > 0x4016cbe3) return -__cosdf(sign ? x + S2PI2 : x - S2PI2);
|
||||
return sign ? __sindf(x + S1PI2) : __sindf(S1PI2 - x);
|
||||
// |x| ~<= 9*pi/4
|
||||
case (ix <= 0x40e231d5):
|
||||
if (ix > 0x40afeddf) return __cosdf(sign ? x + S4PI2 : x - S4PI2);
|
||||
return sign ? __sindf((double)(-x) - S3PI2) : __sindf(x - S3PI2);
|
||||
}
|
||||
|
||||
// general argument reduction needed
|
||||
double y @noinit;
|
||||
switch (__rem_pio2f(x, &y) & 3)
|
||||
{
|
||||
case 0: return __cosdf(y);
|
||||
case 1: return __sindf(-y);
|
||||
case 2: return -__cosdf(y);
|
||||
default: return __sindf(y);
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/s_cos.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
fn double _cos(double x) @extern("cos") @weak @nostrip
|
||||
{
|
||||
// High word of x.
|
||||
uint ix = (uint)(bitcast(x, ulong) >> 32);
|
||||
ix &= 0x7fffffff;
|
||||
|
||||
switch
|
||||
{
|
||||
// |x| ~< pi/4
|
||||
case ix <= 0x3fe921fb:
|
||||
if (ix < 0x3e46a09e)
|
||||
{
|
||||
// |x| < 2**-27 * sqrt(2)
|
||||
// raise inexact if x!=0
|
||||
force_eval_add(x, 0x1p120f);
|
||||
return 1.0;
|
||||
}
|
||||
return __cos(x, 0);
|
||||
case ix >= 0x7ff00000:
|
||||
// cos(Inf or NaN) is NaN
|
||||
return x - x;
|
||||
default:
|
||||
// argument reduction
|
||||
double[2] y @noinit;
|
||||
switch (__rem_pio2(x, &y) & 3)
|
||||
{
|
||||
case 0: return __cos(y[0], y[1]);
|
||||
case 1: return -__sin(y[0], y[1], 1);
|
||||
case 2: return -__cos(y[0], y[1]);
|
||||
default: return __sin(y[0], y[1], 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$endif
|
||||
144
lib/std/math/math_nolibc/exp2.c3
Normal file
144
lib/std/math/math_nolibc/exp2.c3
Normal file
@@ -0,0 +1,144 @@
|
||||
module std::math::nolibc;
|
||||
|
||||
$if !env::COMPILER_LIBC_AVAILABLE:
|
||||
|
||||
macro uint _top12f(float x) @private => bitcast(x, uint) >> 20;
|
||||
|
||||
|
||||
fn float _exp2f(float x) @extern("exp2f") @weak @nostrip
|
||||
{
|
||||
double xd = x;
|
||||
uint abstop = _top12f(x) & 0x7ff;
|
||||
if (abstop >= _top12f(128.0f)) /* @unlikely */
|
||||
{
|
||||
switch
|
||||
{
|
||||
// |x| >= 128 or x is nan.
|
||||
case bitcast(x, uint) == bitcast(-float.inf, uint):
|
||||
return 0;
|
||||
case abstop >= _top12f(float.inf):
|
||||
return x + x;
|
||||
case x > 0:
|
||||
return __math_oflowf(0);
|
||||
case x <= -150.0f:
|
||||
return __math_uflowf(0);
|
||||
}
|
||||
}
|
||||
const SHIFT = __EXP2F_DATA.shift_scaled;
|
||||
const uint N = 1U << EXP2F_TABLE_BITS;
|
||||
// x = k/N + r with r in [-1/(2N), 1/(2N)] and int k.
|
||||
double kd = xd + SHIFT;
|
||||
ulong ki = bitcast(kd, ulong);
|
||||
kd -= SHIFT; /* k/N for int k. */
|
||||
double r = xd - kd;
|
||||
|
||||
// exp2(x) = 2^(k/N) * 2^r ~= s * (C0*r^3 + C1*r^2 + C2*r + 1)
|
||||
ulong t = __EXP2F_DATA.tab[ki % N];
|
||||
t += ki << (52 - EXP2F_TABLE_BITS);
|
||||
double s = bitcast(t, double);
|
||||
double z = __EXP2F_DATA.poly[0] * r + __EXP2F_DATA.poly[1];
|
||||
double r2 = r * r;
|
||||
double y = __EXP2F_DATA.poly[2] * r + 1;
|
||||
y = z * r2 + y;
|
||||
y = y * s;
|
||||
return (float)y;
|
||||
}
|
||||
|
||||
fn double _exp2_specialcase(double tmp, ulong sbits, ulong ki) @private
|
||||
{
|
||||
if (ki & 0x80000000 == 0)
|
||||
{
|
||||
// k > 0, the exponent of scale might have overflowed by 1.
|
||||
sbits -= 1u64 << 52;
|
||||
double scale = bitcast(sbits, double);
|
||||
double y = 2 * (scale + scale * tmp);
|
||||
return y;
|
||||
}
|
||||
// k < 0, need special care in the subnormal range.
|
||||
sbits += 1022u64 << 52;
|
||||
double scale = bitcast(sbits, double);
|
||||
double y = scale + scale * tmp;
|
||||
if (y >= 1.0)
|
||||
{
|
||||
return 0x1p-1022 * y;
|
||||
}
|
||||
// Round y to the right precision before scaling it into the subnormal
|
||||
// range to avoid double rounding that can cause 0.5+E/2 ulp error where
|
||||
// E is the worst-case ulp error outside the subnormal range. So this
|
||||
// is only useful if the goal is better than 1 ulp worst-case error.
|
||||
double lo = scale - y + scale * tmp;
|
||||
double hi = 1.0 + y;
|
||||
lo = 1.0 - hi + y + lo;
|
||||
y = hi + lo - 1.0;
|
||||
/* Avoid -0.0 with downward rounding. */
|
||||
if (WANT_ROUNDING && y == 0.0) y = 0.0;
|
||||
/* The underflow exception needs to be signaled explicitly. */
|
||||
return __math_xflow(0, 0x1p-1022);
|
||||
}
|
||||
|
||||
|
||||
macro uint _top12d(double x) @private
|
||||
{
|
||||
return (uint)(bitcast(x, ulong) >> 52);
|
||||
}
|
||||
|
||||
fn double _exp2(double x) @extern("exp2") @weak @nostrip
|
||||
{
|
||||
uint abstop = _top12d(x) & 0x7ff;
|
||||
ulong u = bitcast(x, ulong);
|
||||
if (abstop - _top12d(0x1p-54) >= _top12d(512.0) - _top12d(0x1p-54)) /* @unlikely */
|
||||
{
|
||||
if (abstop - _top12d(0x1p-54) >= 0x80000000)
|
||||
{
|
||||
// Avoid spurious underflow for tiny x. */
|
||||
// Note: 0 is common input. */
|
||||
return WANT_ROUNDING ? 1.0 + x : 1.0;
|
||||
}
|
||||
if (abstop >= _top12d(1024.0))
|
||||
{
|
||||
switch
|
||||
{
|
||||
case u == bitcast(-double.inf, ulong):
|
||||
return 0.0;
|
||||
case abstop >= _top12d(double.inf):
|
||||
return 1.0 + x;
|
||||
case !(u >> 63):
|
||||
return __math_oflow(0);
|
||||
case u >= bitcast(-1075.0, ulong):
|
||||
return __math_uflow(0);
|
||||
}
|
||||
}
|
||||
// Large x is special cased below.
|
||||
if (2 * u > 2 * bitcast(928.0, ulong)) abstop = 0;
|
||||
}
|
||||
const SHIFT = __EXP2_DATA.exp2_shift;
|
||||
// exp2(x) = 2^(k/N) * 2^r, with 2^r in [2^(-1/2N),2^(1/2N)]. */
|
||||
// x = k/N + r, with int k and r in [-1/2N, 1/2N]. */
|
||||
double kd = x + SHIFT;
|
||||
ulong ki = bitcast(kd, ulong); /* k. */
|
||||
kd -= SHIFT; /* k/N for int k. */
|
||||
double r = x - kd;
|
||||
/* 2^(k/N) ~= scale * (1 + tail). */
|
||||
ulong idx = 2 * (ki % EXP_DATA_WIDTH);
|
||||
ulong top = ki << (52 - EXP_TABLE_BITS);
|
||||
double tail = __EXP2_DATA.tab[idx];
|
||||
/* This is only a valid scale when -1023*N < k < 1024*N. */
|
||||
ulong sbits = __EXP2_DATA.tab[idx + 1] + top;
|
||||
/* exp2(x) = 2^(k/N) * 2^r ~= scale + scale * (tail + 2^r - 1). */
|
||||
/* Evaluation is optimized assuming superscalar pipelined execution. */
|
||||
double r2 = r * r;
|
||||
const C1 = __EXP2_DATA.exp2_poly[0];
|
||||
const C2 = __EXP2_DATA.exp2_poly[1];
|
||||
const C3 = __EXP2_DATA.exp2_poly[2];
|
||||
const C4 = __EXP2_DATA.exp2_poly[3];
|
||||
const C5 = __EXP2_DATA.exp2_poly[4];
|
||||
/* Without fma the worst case error is 0.5/N ulp larger. */
|
||||
/* Worst case error is less than 0.5+0.86/N+(abs poly error * 2^53) ulp. */
|
||||
double tmp = tail + r * C1 + r2 * (C2 + r * C3) + r2 * r2 * (C4 + r * C5);
|
||||
if (abstop == 0 /* @unlikely */) return _exp2_specialcase(tmp, sbits, ki);
|
||||
double scale = bitcast(sbits, double);
|
||||
/* Note: tmp == 0 or |tmp| > 2^-65 and scale > 2^-928, so there
|
||||
is no spurious underflow here even without fma. */
|
||||
return scale + scale * tmp;
|
||||
}
|
||||
$endif
|
||||
45
lib/std/math/math_nolibc/floor.c3
Normal file
45
lib/std/math/math_nolibc/floor.c3
Normal file
@@ -0,0 +1,45 @@
|
||||
module std::math::nolibc;
|
||||
|
||||
$if !env::COMPILER_LIBC_AVAILABLE:
|
||||
|
||||
fn double _floor(double x) @weak @extern("floor") @nostrip
|
||||
{
|
||||
ulong ui = bitcast(x, ulong);
|
||||
int e = (int)((ui >> 52) & 0x7ff);
|
||||
if (e >= 0x3ff + 52 || x == 0) return x;
|
||||
// y = int(x) - x, where int(x) is an integer neighbor of x
|
||||
double y = ui >> 63 ? (x - TOINT) + TOINT - x : (x + TOINT) - TOINT - x;
|
||||
// special case because of non-nearest rounding modes
|
||||
if (e <= 0x3ff - 1)
|
||||
{
|
||||
force_eval_add(y, 0);
|
||||
return ui >> 63 ? -1 : 0;
|
||||
}
|
||||
return y > 0 ? x + y - 1 : x + y;
|
||||
}
|
||||
|
||||
|
||||
fn float _floorf(float x) @weak @extern("floorf") @nostrip
|
||||
{
|
||||
uint u = bitcast(x, uint);
|
||||
int e = (int)((u >> 23) & 0xff) - 0x7f;
|
||||
switch
|
||||
{
|
||||
case e >= 23:
|
||||
return x;
|
||||
case e >= 0:
|
||||
uint m = 0x007fffff >> e;
|
||||
if (u & m == 0) return x;
|
||||
force_eval_add(x, 0x1p120f);
|
||||
if (u >> 31) u += m;
|
||||
u &= ~m;
|
||||
case u >> 31 == 0:
|
||||
force_eval_add(x, 0x1p120f);
|
||||
u = 0;
|
||||
case (u << 1 != 0):
|
||||
return -1;
|
||||
}
|
||||
return bitcast(u, float);
|
||||
}
|
||||
|
||||
$endif
|
||||
256
lib/std/math/math_nolibc/math_nolibc.c3
Normal file
256
lib/std/math/math_nolibc/math_nolibc.c3
Normal file
@@ -0,0 +1,256 @@
|
||||
module std::math::nolibc;
|
||||
|
||||
const double TOINT = 1 / math::DOUBLE_EPSILON;
|
||||
const double TOINT15 = 1.5 / math::DOUBLE_EPSILON;
|
||||
const float TOINTF = (float)(1 / math::FLOAT_EPSILON);
|
||||
const double S1PI2 @private = math::PI_2; /* 0x3FF921FB, 0x54442D18 */
|
||||
const double S2PI2 @private = math::PI; /* 0x400921FB, 0x54442D18 */
|
||||
const double S3PI2 @private = math::PI + math::PI_2; /* 0x4012D97C, 0x7F3321D2 */
|
||||
const double S4PI2 @private = math::PI + math::PI; /* 0x401921FB, 0x54442D18 */
|
||||
|
||||
// Shared between expf, exp2f and powf.
|
||||
const EXP2F_TABLE_BITS @private = 5;
|
||||
const EXP2F_POLY_ORDER @private = 3;
|
||||
struct Exp2fData @private
|
||||
{
|
||||
ulong[1 << EXP2F_TABLE_BITS] tab;
|
||||
double shift_scaled;
|
||||
double[EXP2F_POLY_ORDER] poly;
|
||||
double shift;
|
||||
double invln2_scaled;
|
||||
double[EXP2F_POLY_ORDER] poly_scaled;
|
||||
}
|
||||
|
||||
const Exp2fData __EXP2F_DATA @private = {
|
||||
.tab = {
|
||||
0x3ff0000000000000, 0x3fefd9b0d3158574, 0x3fefb5586cf9890f, 0x3fef9301d0125b51,
|
||||
0x3fef72b83c7d517b, 0x3fef54873168b9aa, 0x3fef387a6e756238, 0x3fef1e9df51fdee1,
|
||||
0x3fef06fe0a31b715, 0x3feef1a7373aa9cb, 0x3feedea64c123422, 0x3feece086061892d,
|
||||
0x3feebfdad5362a27, 0x3feeb42b569d4f82, 0x3feeab07dd485429, 0x3feea47eb03a5585,
|
||||
0x3feea09e667f3bcd, 0x3fee9f75e8ec5f74, 0x3feea11473eb0187, 0x3feea589994cce13,
|
||||
0x3feeace5422aa0db, 0x3feeb737b0cdc5e5, 0x3feec49182a3f090, 0x3feed503b23e255d,
|
||||
0x3feee89f995ad3ad, 0x3feeff76f2fb5e47, 0x3fef199bdd85529c, 0x3fef3720dcef9069,
|
||||
0x3fef5818dcfba487, 0x3fef7c97337b9b5f, 0x3fefa4afa2a490da, 0x3fefd0765b6e4540,
|
||||
},
|
||||
.shift_scaled = 0x1.8p+52 / (1 << 5),
|
||||
.poly = {
|
||||
0x1.c6af84b912394p-5, 0x1.ebfce50fac4f3p-3, 0x1.62e42ff0c52d6p-1,
|
||||
},
|
||||
.shift = 0x1.8p+52,
|
||||
.invln2_scaled = 0x1.71547652b82fep+0 * (1 << 5),
|
||||
.poly_scaled = {
|
||||
0x1.c6af84b912394p-5 / (1 << 5) / (1 << 5) / (1 << 5),
|
||||
0x1.ebfce50fac4f3p-3 / (1 << 5) / (1 << 5),
|
||||
0x1.62e42ff0c52d6p-1 / (1 << 5),
|
||||
},
|
||||
};
|
||||
|
||||
const EXP_TABLE_BITS = 7;
|
||||
const EXP_POLY_ORDER = 5;
|
||||
const EXP2_POLY_ORDER = 5;
|
||||
const EXP_DATA_WIDTH = 1 << EXP_TABLE_BITS;
|
||||
struct Exp2Data @private
|
||||
{
|
||||
double invln2N;
|
||||
double shift;
|
||||
double negln2hiN;
|
||||
double negln2loN;
|
||||
double[4] poly; // Last four coefficients.
|
||||
double exp2_shift;
|
||||
double[EXP2_POLY_ORDER] exp2_poly;
|
||||
ulong[2 * EXP_DATA_WIDTH] tab;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shared data between exp, exp2 and pow.
|
||||
*
|
||||
* Copyright (c) 2018, Arm Limited.
|
||||
* SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception
|
||||
*/
|
||||
|
||||
|
||||
const Exp2Data __EXP2_DATA = {
|
||||
// N/ln2
|
||||
.invln2N = 0x1.71547652b82fep0 * EXP_DATA_WIDTH,
|
||||
// -ln2/N
|
||||
.negln2hiN = -0x1.62e42fefa0000p-8,
|
||||
.negln2loN = -0x1.cf79abc9e3b3ap-47,
|
||||
.shift = 0x1.8p52,
|
||||
// exp polynomial coefficients.
|
||||
.poly = {
|
||||
// abs error: 1.555*2^-66
|
||||
// ulp error: 0.509 (0.511 without fma)
|
||||
// if |x| < ln2/256+eps
|
||||
// abs error if |x| < ln2/256+0x1p-15: 1.09*2^-65
|
||||
// abs error if |x| < ln2/128: 1.7145*2^-56
|
||||
0x1.ffffffffffdbdp-2,
|
||||
0x1.555555555543cp-3,
|
||||
0x1.55555cf172b91p-5,
|
||||
0x1.1111167a4d017p-7,
|
||||
},
|
||||
.exp2_shift = 0x1.8p52 / EXP_DATA_WIDTH,
|
||||
// exp2 polynomial coefficients.
|
||||
.exp2_poly = {
|
||||
// abs error: 1.2195*2^-65
|
||||
// ulp error: 0.507 (0.511 without fma)
|
||||
// if |x| < 1/256
|
||||
// abs error if |x| < 1/128: 1.9941*2^-56
|
||||
0x1.62e42fefa39efp-1,
|
||||
0x1.ebfbdff82c424p-3,
|
||||
0x1.c6b08d70cf4b5p-5,
|
||||
0x1.3b2abd24650ccp-7,
|
||||
0x1.5d7e09b4e3a84p-10,
|
||||
},
|
||||
// 2^(k/N) ~= H[k]*(1 + T[k]) for int k in [0,N)
|
||||
// tab[2*k] = asuint64(T[k])
|
||||
// tab[2*k+1] = asuint64(H[k]) - (k << 52)/N
|
||||
.tab = {
|
||||
0x0, 0x3ff0000000000000,
|
||||
0x3c9b3b4f1a88bf6e, 0x3feff63da9fb3335,
|
||||
0xbc7160139cd8dc5d, 0x3fefec9a3e778061,
|
||||
0xbc905e7a108766d1, 0x3fefe315e86e7f85,
|
||||
0x3c8cd2523567f613, 0x3fefd9b0d3158574,
|
||||
0xbc8bce8023f98efa, 0x3fefd06b29ddf6de,
|
||||
0x3c60f74e61e6c861, 0x3fefc74518759bc8,
|
||||
0x3c90a3e45b33d399, 0x3fefbe3ecac6f383,
|
||||
0x3c979aa65d837b6d, 0x3fefb5586cf9890f,
|
||||
0x3c8eb51a92fdeffc, 0x3fefac922b7247f7,
|
||||
0x3c3ebe3d702f9cd1, 0x3fefa3ec32d3d1a2,
|
||||
0xbc6a033489906e0b, 0x3fef9b66affed31b,
|
||||
0xbc9556522a2fbd0e, 0x3fef9301d0125b51,
|
||||
0xbc5080ef8c4eea55, 0x3fef8abdc06c31cc,
|
||||
0xbc91c923b9d5f416, 0x3fef829aaea92de0,
|
||||
0x3c80d3e3e95c55af, 0x3fef7a98c8a58e51,
|
||||
0xbc801b15eaa59348, 0x3fef72b83c7d517b,
|
||||
0xbc8f1ff055de323d, 0x3fef6af9388c8dea,
|
||||
0x3c8b898c3f1353bf, 0x3fef635beb6fcb75,
|
||||
0xbc96d99c7611eb26, 0x3fef5be084045cd4,
|
||||
0x3c9aecf73e3a2f60, 0x3fef54873168b9aa,
|
||||
0xbc8fe782cb86389d, 0x3fef4d5022fcd91d,
|
||||
0x3c8a6f4144a6c38d, 0x3fef463b88628cd6,
|
||||
0x3c807a05b0e4047d, 0x3fef3f49917ddc96,
|
||||
0x3c968efde3a8a894, 0x3fef387a6e756238,
|
||||
0x3c875e18f274487d, 0x3fef31ce4fb2a63f,
|
||||
0x3c80472b981fe7f2, 0x3fef2b4565e27cdd,
|
||||
0xbc96b87b3f71085e, 0x3fef24dfe1f56381,
|
||||
0x3c82f7e16d09ab31, 0x3fef1e9df51fdee1,
|
||||
0xbc3d219b1a6fbffa, 0x3fef187fd0dad990,
|
||||
0x3c8b3782720c0ab4, 0x3fef1285a6e4030b,
|
||||
0x3c6e149289cecb8f, 0x3fef0cafa93e2f56,
|
||||
0x3c834d754db0abb6, 0x3fef06fe0a31b715,
|
||||
0x3c864201e2ac744c, 0x3fef0170fc4cd831,
|
||||
0x3c8fdd395dd3f84a, 0x3feefc08b26416ff,
|
||||
0xbc86a3803b8e5b04, 0x3feef6c55f929ff1,
|
||||
0xbc924aedcc4b5068, 0x3feef1a7373aa9cb,
|
||||
0xbc9907f81b512d8e, 0x3feeecae6d05d866,
|
||||
0xbc71d1e83e9436d2, 0x3feee7db34e59ff7,
|
||||
0xbc991919b3ce1b15, 0x3feee32dc313a8e5,
|
||||
0x3c859f48a72a4c6d, 0x3feedea64c123422,
|
||||
0xbc9312607a28698a, 0x3feeda4504ac801c,
|
||||
0xbc58a78f4817895b, 0x3feed60a21f72e2a,
|
||||
0xbc7c2c9b67499a1b, 0x3feed1f5d950a897,
|
||||
0x3c4363ed60c2ac11, 0x3feece086061892d,
|
||||
0x3c9666093b0664ef, 0x3feeca41ed1d0057,
|
||||
0x3c6ecce1daa10379, 0x3feec6a2b5c13cd0,
|
||||
0x3c93ff8e3f0f1230, 0x3feec32af0d7d3de,
|
||||
0x3c7690cebb7aafb0, 0x3feebfdad5362a27,
|
||||
0x3c931dbdeb54e077, 0x3feebcb299fddd0d,
|
||||
0xbc8f94340071a38e, 0x3feeb9b2769d2ca7,
|
||||
0xbc87deccdc93a349, 0x3feeb6daa2cf6642,
|
||||
0xbc78dec6bd0f385f, 0x3feeb42b569d4f82,
|
||||
0xbc861246ec7b5cf6, 0x3feeb1a4ca5d920f,
|
||||
0x3c93350518fdd78e, 0x3feeaf4736b527da,
|
||||
0x3c7b98b72f8a9b05, 0x3feead12d497c7fd,
|
||||
0x3c9063e1e21c5409, 0x3feeab07dd485429,
|
||||
0x3c34c7855019c6ea, 0x3feea9268a5946b7,
|
||||
0x3c9432e62b64c035, 0x3feea76f15ad2148,
|
||||
0xbc8ce44a6199769f, 0x3feea5e1b976dc09,
|
||||
0xbc8c33c53bef4da8, 0x3feea47eb03a5585,
|
||||
0xbc845378892be9ae, 0x3feea34634ccc320,
|
||||
0xbc93cedd78565858, 0x3feea23882552225,
|
||||
0x3c5710aa807e1964, 0x3feea155d44ca973,
|
||||
0xbc93b3efbf5e2228, 0x3feea09e667f3bcd,
|
||||
0xbc6a12ad8734b982, 0x3feea012750bdabf,
|
||||
0xbc6367efb86da9ee, 0x3fee9fb23c651a2f,
|
||||
0xbc80dc3d54e08851, 0x3fee9f7df9519484,
|
||||
0xbc781f647e5a3ecf, 0x3fee9f75e8ec5f74,
|
||||
0xbc86ee4ac08b7db0, 0x3fee9f9a48a58174,
|
||||
0xbc8619321e55e68a, 0x3fee9feb564267c9,
|
||||
0x3c909ccb5e09d4d3, 0x3feea0694fde5d3f,
|
||||
0xbc7b32dcb94da51d, 0x3feea11473eb0187,
|
||||
0x3c94ecfd5467c06b, 0x3feea1ed0130c132,
|
||||
0x3c65ebe1abd66c55, 0x3feea2f336cf4e62,
|
||||
0xbc88a1c52fb3cf42, 0x3feea427543e1a12,
|
||||
0xbc9369b6f13b3734, 0x3feea589994cce13,
|
||||
0xbc805e843a19ff1e, 0x3feea71a4623c7ad,
|
||||
0xbc94d450d872576e, 0x3feea8d99b4492ed,
|
||||
0x3c90ad675b0e8a00, 0x3feeaac7d98a6699,
|
||||
0x3c8db72fc1f0eab4, 0x3feeace5422aa0db,
|
||||
0xbc65b6609cc5e7ff, 0x3feeaf3216b5448c,
|
||||
0x3c7bf68359f35f44, 0x3feeb1ae99157736,
|
||||
0xbc93091fa71e3d83, 0x3feeb45b0b91ffc6,
|
||||
0xbc5da9b88b6c1e29, 0x3feeb737b0cdc5e5,
|
||||
0xbc6c23f97c90b959, 0x3feeba44cbc8520f,
|
||||
0xbc92434322f4f9aa, 0x3feebd829fde4e50,
|
||||
0xbc85ca6cd7668e4b, 0x3feec0f170ca07ba,
|
||||
0x3c71affc2b91ce27, 0x3feec49182a3f090,
|
||||
0x3c6dd235e10a73bb, 0x3feec86319e32323,
|
||||
0xbc87c50422622263, 0x3feecc667b5de565,
|
||||
0x3c8b1c86e3e231d5, 0x3feed09bec4a2d33,
|
||||
0xbc91bbd1d3bcbb15, 0x3feed503b23e255d,
|
||||
0x3c90cc319cee31d2, 0x3feed99e1330b358,
|
||||
0x3c8469846e735ab3, 0x3feede6b5579fdbf,
|
||||
0xbc82dfcd978e9db4, 0x3feee36bbfd3f37a,
|
||||
0x3c8c1a7792cb3387, 0x3feee89f995ad3ad,
|
||||
0xbc907b8f4ad1d9fa, 0x3feeee07298db666,
|
||||
0xbc55c3d956dcaeba, 0x3feef3a2b84f15fb,
|
||||
0xbc90a40e3da6f640, 0x3feef9728de5593a,
|
||||
0xbc68d6f438ad9334, 0x3feeff76f2fb5e47,
|
||||
0xbc91eee26b588a35, 0x3fef05b030a1064a,
|
||||
0x3c74ffd70a5fddcd, 0x3fef0c1e904bc1d2,
|
||||
0xbc91bdfbfa9298ac, 0x3fef12c25bd71e09,
|
||||
0x3c736eae30af0cb3, 0x3fef199bdd85529c,
|
||||
0x3c8ee3325c9ffd94, 0x3fef20ab5fffd07a,
|
||||
0x3c84e08fd10959ac, 0x3fef27f12e57d14b,
|
||||
0x3c63cdaf384e1a67, 0x3fef2f6d9406e7b5,
|
||||
0x3c676b2c6c921968, 0x3fef3720dcef9069,
|
||||
0xbc808a1883ccb5d2, 0x3fef3f0b555dc3fa,
|
||||
0xbc8fad5d3ffffa6f, 0x3fef472d4a07897c,
|
||||
0xbc900dae3875a949, 0x3fef4f87080d89f2,
|
||||
0x3c74a385a63d07a7, 0x3fef5818dcfba487,
|
||||
0xbc82919e2040220f, 0x3fef60e316c98398,
|
||||
0x3c8e5a50d5c192ac, 0x3fef69e603db3285,
|
||||
0x3c843a59ac016b4b, 0x3fef7321f301b460,
|
||||
0xbc82d52107b43e1f, 0x3fef7c97337b9b5f,
|
||||
0xbc892ab93b470dc9, 0x3fef864614f5a129,
|
||||
0x3c74b604603a88d3, 0x3fef902ee78b3ff6,
|
||||
0x3c83c5ec519d7271, 0x3fef9a51fbc74c83,
|
||||
0xbc8ff7128fd391f0, 0x3fefa4afa2a490da,
|
||||
0xbc8dae98e223747d, 0x3fefaf482d8e67f1,
|
||||
0x3c8ec3bc41aa2008, 0x3fefba1bee615a27,
|
||||
0x3c842b94c3a9eb32, 0x3fefc52b376bba97,
|
||||
0x3c8a64a931d185ee, 0x3fefd0765b6e4540,
|
||||
0xbc8e37bae43be3ed, 0x3fefdbfdad9cbe14,
|
||||
0x3c77893b4d91cd9d, 0x3fefe7c1819e90d8,
|
||||
0x3c5305c14160cc89, 0x3feff3c22b8f71f1,
|
||||
}
|
||||
};
|
||||
|
||||
const bool WANT_ROUNDING = true;
|
||||
macro float __math_uflowf(uint sign) => __math_xflow(sign, 0x1p97f);
|
||||
macro double __math_uflow(ulong sign) => __math_xflow(sign, 0x1p769);
|
||||
macro float __math_oflowf(uint sign) => __math_xflow(sign, 0x1p-95f);
|
||||
macro double __math_oflow(ulong sign) => __math_xflow(sign, 0x1p-767);
|
||||
|
||||
macro __math_xflow(sign, v)
|
||||
{
|
||||
$typeof(v) temp;
|
||||
@volatile_store(temp, (sign ? -v : v) * v);
|
||||
return temp;
|
||||
}
|
||||
|
||||
macro force_eval_add(x, v)
|
||||
{
|
||||
$typeof(x) temp;
|
||||
@volatile_store(temp, x + v);
|
||||
}
|
||||
15
lib/std/math/math_nolibc/pow.c3
Normal file
15
lib/std/math/math_nolibc/pow.c3
Normal file
@@ -0,0 +1,15 @@
|
||||
module std::math::nolibc;
|
||||
|
||||
$if !env::COMPILER_LIBC_AVAILABLE:
|
||||
|
||||
fn float powf_broken(float x, float f) @extern("powf") @weak @nostrip
|
||||
{
|
||||
unreachable("'powf' not supported");
|
||||
}
|
||||
|
||||
fn double pow_broken(double x, double y) @extern("pow") @weak @nostrip
|
||||
{
|
||||
unreachable("'pow' not supported");
|
||||
}
|
||||
|
||||
$endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user