Compare commits
645
Commits
master
..
ba6de21396
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba6de21396 | ||
|
|
b75e0a8a55 | ||
|
|
9a64fd8e01 | ||
|
|
b0a0fca8ee | ||
|
|
710269a876 | ||
|
|
4a410c3c1a | ||
|
|
4fa1a49765 | ||
|
|
9614f01e20 | ||
|
|
36839df841 | ||
|
|
2b1eab54b3 | ||
|
|
8128c2932e | ||
|
|
851eac0ec2 | ||
|
|
df963b0893 | ||
|
|
84c992c574 | ||
|
|
1ce128a5d5 | ||
|
|
372287f557 | ||
|
|
8ecfc82d8f | ||
|
|
9d60b110f5 | ||
|
|
70f28de0e1 | ||
|
|
76e279920f | ||
|
|
37cb6248a5 | ||
|
|
37154a2067 | ||
|
|
e52450a3df | ||
|
|
6fc5f0538f | ||
|
|
c970c1c7d6 | ||
|
|
01488e4eca | ||
|
|
ce1fe5feb1 | ||
|
|
270109a56b | ||
|
|
663286b45c | ||
|
|
d5c3794a93 | ||
|
|
dcda334767 | ||
|
|
69c9f7a336 | ||
|
|
ca3d087aa3 | ||
|
|
d2be40a282 | ||
|
|
6c200f7764 | ||
|
|
8de20eb0e2 | ||
|
|
86c91668e1 | ||
|
|
1e4e9fbccc | ||
|
|
36180c5c90 | ||
|
|
5ab20ce8ab | ||
|
|
61fc0cd339 | ||
|
|
3864060699 | ||
|
|
db06c783a9 | ||
|
|
f33c9873d0 | ||
|
|
66483f6c99 | ||
|
|
da821cb581 | ||
|
|
f85697d037 | ||
|
|
02c45e0dfd | ||
|
|
05d5de4b7d | ||
|
|
e575569d8a | ||
|
|
4c9c0e5210 | ||
|
|
507bd57854 | ||
|
|
0dd4641044 | ||
|
|
57c179970e | ||
|
|
005a22a93d | ||
|
|
de06ea05cc | ||
|
|
5fecc74193 | ||
|
|
101fed6d7d | ||
|
|
239126db70 | ||
|
|
59320a749a | ||
|
|
a43b1db424 | ||
|
|
3ba9a9380e | ||
|
|
a96cead179 | ||
|
|
73a19e2260 | ||
|
|
94b3bc832b | ||
|
|
ecbc800fdd | ||
|
|
753c9199b6 | ||
|
|
eb19ba6037 | ||
|
|
1a6159e0ec | ||
|
|
c90857eb46 | ||
|
|
146273afd8 | ||
|
|
8f4d3a218f | ||
|
|
8c0c9c82f1 | ||
|
|
d694a374d6 | ||
|
|
f646a6d22c | ||
|
|
c0d00a6211 | ||
|
|
96d9847639 | ||
|
|
233ebe0eba | ||
|
|
80ea09b3ea | ||
|
|
77c6bf3da9 | ||
|
|
565a005dcb | ||
|
|
40b15ec7aa | ||
|
|
9073909cfc | ||
|
|
cc57da69fd | ||
|
|
31ebf0bb14 | ||
|
|
a7afdac4fa | ||
|
|
04e5642ce3 | ||
|
|
f059c33940 | ||
|
|
1b8f0b12e8 | ||
|
|
bfcab9b3c3 | ||
|
|
da96fb847c | ||
|
|
1767b2767e | ||
|
|
215dd4a03f | ||
|
|
9730aa944d | ||
|
|
bedc2b15e2 | ||
|
|
ee1ba86f74 | ||
|
|
82e1c33db5 | ||
|
|
78917e28fb | ||
|
|
505b2d0ffa | ||
|
|
dd7e2ff655 | ||
|
|
9affcd546a | ||
|
|
e4f2401633 | ||
|
|
65a6c372e0 | ||
|
|
bbab59fa0a | ||
|
|
6cf9731959 | ||
|
|
c4b4460cbd | ||
|
|
5b50a49416 | ||
|
|
c6bff29bef | ||
|
|
193c3b36ef | ||
|
|
1d1e865745 | ||
|
|
9758d83856 | ||
|
|
59081eb4bd | ||
|
|
a40797abb2 | ||
|
|
6c0a4d0633 | ||
|
|
edafe05b5b | ||
|
|
b40873d54c | ||
|
|
0921054b6e | ||
|
|
f8f9e33032 | ||
|
|
beaf072740 | ||
|
|
e5a028ff93 | ||
|
|
8270fe2542 | ||
|
|
5214ce61ac | ||
|
|
ebf0f5de27 | ||
|
|
ca464c314b | ||
|
|
b140d9c3f1 | ||
|
|
a57ebb49be | ||
|
|
187bbcc5fc | ||
|
|
50ac0600cb | ||
|
|
9da85fadbd | ||
|
|
ab8558ef43 | ||
|
|
c5afe78b91 | ||
|
|
c1828a51c4 | ||
|
|
97ba82ab7d | ||
|
|
cde431549c | ||
|
|
ccf55d6d74 | ||
|
|
0f03f248c7 | ||
|
|
35d68bda61 | ||
|
|
1ae20a46b2 | ||
|
|
10fb5d77a1 | ||
|
|
19d5a26966 | ||
|
|
e9db3d7386 | ||
|
|
2acf2c189c | ||
|
|
06cb073832 | ||
|
|
00442da5dd | ||
|
|
1011c63494 | ||
|
|
28d8348b89 | ||
|
|
e4b84dde90 | ||
|
|
b6f3a1ebec | ||
|
|
9ae73e5c04 | ||
|
|
6f9299e557 | ||
|
|
29ad3a8f08 | ||
|
|
44bf7c68ff | ||
|
|
4221aa8fb7 | ||
|
|
509db7af46 | ||
|
|
5c46844747 | ||
|
|
2f638c731a | ||
|
|
b98d14f9dd | ||
|
|
ceb6747fcd | ||
|
|
938edcc321 | ||
|
|
28308f58b2 | ||
|
|
74ae806978 | ||
|
|
c67db4e495 | ||
|
|
edc941bf53 | ||
|
|
edb2379bce | ||
|
|
05652240f1 | ||
|
|
af1d88b07c | ||
|
|
6243cace7d | ||
|
|
3ba7eeb1f2 | ||
|
|
32efabac6a | ||
|
|
725cdde007 | ||
|
|
79d709d391 | ||
|
|
c8a7c75dbe | ||
|
|
c99c3881ed | ||
|
|
868c95f7d6 | ||
|
|
0a315760f7 | ||
|
|
56b8b09f35 | ||
|
|
266378154a | ||
|
|
f637352669 | ||
|
|
bb9809b956 | ||
|
|
ce93a769f6 | ||
|
|
7720176e60 | ||
|
|
29002c965c | ||
|
|
5e7acd4a20 | ||
|
|
4e5a0f6a9b | ||
|
|
f297006dcb | ||
|
|
85ca1a6a9e | ||
|
|
5a12ab8d8c | ||
|
|
2d79790d83 | ||
|
|
a351d2eb41 | ||
|
|
0424ca62f2 | ||
|
|
83d5ddff9d | ||
|
|
b600361278 | ||
|
|
9d29a1c187 | ||
|
|
20207e6040 | ||
|
|
ef6a7be285 | ||
|
|
df84d1605d | ||
|
|
88a5adaa0a | ||
|
|
e6e1ae0223 | ||
|
|
05f0197d50 | ||
|
|
e34c2b0cef | ||
|
|
c2f4686bc4 | ||
|
|
c19c60fe22 | ||
|
|
d72485a137 | ||
|
|
e9e8751981 | ||
|
|
781c2dc53c | ||
|
|
38b9b7df10 | ||
|
|
a2cf5f2284 | ||
|
|
38a20bd97a | ||
|
|
fcefe9f827 | ||
|
|
56cdb9557d | ||
|
|
166f06d1fb | ||
|
|
24533dfe7f | ||
|
|
8408f400c8 | ||
|
|
48574d634c | ||
|
|
ad3e58d455 | ||
|
|
0dc020c429 | ||
|
|
2e70d33364 | ||
|
|
b0f314c9e3 | ||
|
|
1276c565da | ||
|
|
955d92bcc7 | ||
|
|
4e330de1d5 | ||
|
|
77b337aed8 | ||
|
|
b6ef63e817 | ||
|
|
e6414908b7 | ||
|
|
55eecf3177 | ||
|
|
12ac285456 | ||
|
|
a911707613 | ||
|
|
3e7f09f33f | ||
|
|
4b55faa41e | ||
|
|
720fdd9f34 | ||
|
|
76d52d9e1c | ||
|
|
c74c256968 | ||
|
|
aa5b51b88c | ||
|
|
cf72182d56 | ||
|
|
2e9147c47d | ||
|
|
f7f2aafc56 | ||
|
|
7eec390752 | ||
|
|
0c7a424f5e | ||
|
|
6aaff7573a | ||
|
|
70858fdec5 | ||
|
|
a94b914e06 | ||
|
|
fd842a1f70 | ||
|
|
872c46c43d | ||
|
|
a79f11d137 | ||
|
|
3b48954bc1 | ||
|
|
9884ba7dab | ||
|
|
78ea184868 | ||
|
|
a2b081ec15 | ||
|
|
c139c44503 | ||
|
|
8370a2ae67 | ||
|
|
1590f12b43 | ||
|
|
3180625a41 | ||
|
|
d2ca655178 | ||
|
|
c57eedce49 | ||
|
|
bc9be4927b | ||
|
|
a1282af0b5 | ||
|
|
46c109dce4 | ||
|
|
abb2cfb1c4 | ||
|
|
9ddfed92ff | ||
|
|
c4419490b6 | ||
|
|
20422d9023 | ||
|
|
2c29df0171 | ||
|
|
8fc1081cef | ||
|
|
556b05cb78 | ||
|
|
4a1554ce28 | ||
|
|
542b0f0a0a | ||
|
|
101e844ac5 | ||
|
|
dec70c10e7 | ||
|
|
7a5d949a12 | ||
|
|
93c1ba606a | ||
|
|
4871494c50 | ||
|
|
d7908bee54 | ||
|
|
fd8a319b18 | ||
|
|
d1d9b39200 | ||
|
|
6374f3cd1a | ||
|
|
e652d5b0e2 | ||
|
|
dc3861adfe | ||
|
|
a00f8acd9a | ||
|
|
46dcd1cc4e | ||
|
|
a5d99ffb9a | ||
|
|
daa0d2d4c4 | ||
|
|
92139cf799 | ||
|
|
ed9ec06eee | ||
|
|
f1e674ca37 | ||
|
|
35cc5ae48d | ||
|
|
9ea1e254e0 | ||
|
|
1c0063e834 | ||
|
|
afd4e1d21c | ||
|
|
2065c9ded7 | ||
|
|
bafe0af407 | ||
|
|
d0bc7bdc4a | ||
|
|
fbe34b2d40 | ||
|
|
e49838b974 | ||
|
|
7ed46b9724 | ||
|
|
41a05fb08c | ||
|
|
8f09e25710 | ||
|
|
a28c172ac1 | ||
|
|
b5772fa52c | ||
|
|
d4ac1dd817 | ||
|
|
5e51e2fbad | ||
|
|
a3e87b2508 | ||
|
|
eeb6caa58d | ||
|
|
212392386c | ||
|
|
a84a7073dc | ||
|
|
8cd754c923 | ||
|
|
225d1ffc06 | ||
|
|
f38c20b975 | ||
|
|
a0315f161c | ||
|
|
cac092a344 | ||
|
|
5fd790bd54 | ||
|
|
b60f846209 | ||
|
|
2b4a43ddc1 | ||
|
|
f125a38c50 | ||
|
|
40cb8582fd | ||
|
|
7db1fc7bd6 | ||
|
|
50e642483d | ||
|
|
915679b87b | ||
|
|
2ada434925 | ||
|
|
26c1180fad | ||
|
|
9cac98d961 | ||
|
|
c0877bfe1d | ||
|
|
5ce418995f | ||
|
|
7b21cba834 | ||
|
|
b3e95a705f | ||
|
|
da80e2c731 | ||
|
|
fcabf96347 | ||
|
|
2b48f30cfe | ||
|
|
3251df6068 | ||
|
|
d6dd7c9eab | ||
|
|
3fd728543c | ||
|
|
b4bc061b76 | ||
|
|
f10212f4a0 | ||
|
|
08a385b050 | ||
|
|
d5f903eaa2 | ||
|
|
26b0ba4828 | ||
|
|
90aaa375d0 | ||
|
|
a12a10ee0b | ||
|
|
97b1cf9e73 | ||
|
|
3aa4806515 | ||
|
|
4e1ae87a8e | ||
|
|
6bc8c549fc | ||
|
|
2137a0990e | ||
|
|
7f8b22ff43 | ||
|
|
d1e645b17e | ||
|
|
8bf32a557b | ||
|
|
93cfe71c21 | ||
|
|
65ef0b1b0f | ||
|
|
d98086d869 | ||
|
|
00c1f405c6 | ||
|
|
19546eea4d | ||
|
|
6d9792b1a6 | ||
|
|
47ec2b55c7 | ||
|
|
68ff6dc2b5 | ||
|
|
ad847a3eaa | ||
|
|
4564e7bbc2 | ||
|
|
3fb0c131f1 | ||
|
|
91ba202562 | ||
|
|
93530bd089 | ||
|
|
a7dc343fd6 | ||
|
|
4c179e3965 | ||
|
|
a5756ab2f5 | ||
|
|
95b7ded031 | ||
|
|
a004976c6b | ||
|
|
eecff81093 | ||
|
|
d47482bc04 | ||
|
|
4434cd5764 | ||
|
|
3488c71155 | ||
|
|
ad2efab7c8 | ||
|
|
28a9656db1 | ||
|
|
58db05fcb3 | ||
|
|
bfff188726 | ||
|
|
080669b5e9 | ||
|
|
161a9e3965 | ||
|
|
bb44eab406 | ||
|
|
b3b50715b4 | ||
|
|
2565cefe70 | ||
|
|
8e73057cfb | ||
|
|
74ab76344b | ||
|
|
38afbe8816 | ||
|
|
6d66986b5b | ||
|
|
327f352872 | ||
|
|
7b8b8c751d | ||
|
|
fe20d05e13 | ||
|
|
b036e48cee | ||
|
|
4aaae7773d | ||
|
|
1145a13987 | ||
|
|
f8c9e8fd3e | ||
|
|
f419ffd1ca | ||
|
|
fa99475912 | ||
|
|
6e61816e1c | ||
|
|
b552f73ac9 | ||
|
|
f0df0b69f0 | ||
|
|
f7100ae8ee | ||
|
|
e514c29de4 | ||
|
|
d81c403a5d | ||
|
|
956f2319b5 | ||
|
|
bb2c9a2f32 | ||
|
|
3928e2eb96 | ||
|
|
c4bc48bba1 | ||
|
|
8eb58e7dd8 | ||
|
|
e4af56e3eb | ||
|
|
331f75c7ab | ||
|
|
55465ad6de | ||
|
|
10f0d7dca2 | ||
|
|
c9aabdec68 | ||
|
|
da1def101b | ||
|
|
4d22995ea3 | ||
|
|
d3b89d22fb | ||
|
|
9215205aba | ||
|
|
7fc071154f | ||
|
|
e3c1bcb56e | ||
|
|
7e35e0cbed | ||
|
|
d59a8d9c8b | ||
|
|
605f3aa68d | ||
|
|
c6a415f642 | ||
|
|
edff7a4267 | ||
|
|
48bbef0b10 | ||
|
|
b267000394 | ||
|
|
c3bd96d5d6 | ||
|
|
28dbac4a35 | ||
|
|
22dc4be100 | ||
|
|
ffb38664b8 | ||
|
|
3f16888fe0 | ||
|
|
2b5f441e92 | ||
|
|
e761761108 | ||
|
|
324e5e1ea1 | ||
|
|
cc04fec1de | ||
|
|
b68b80f317 | ||
|
|
cfad821e8e | ||
|
|
815e3c9670 | ||
|
|
0c4dc62170 | ||
|
|
7655ece178 | ||
|
|
22899c2523 | ||
|
|
7f85f8348e | ||
|
|
ee863e3b09 | ||
|
|
1ebe8d9929 | ||
|
|
6c975b98a1 | ||
|
|
e4a248bb60 | ||
|
|
d382735b80 | ||
|
|
a42a4e435d | ||
|
|
f79fce83ca | ||
|
|
4c7f6340d6 | ||
|
|
f3059ec8ea | ||
|
|
4f12887a91 | ||
|
|
03b6b80568 | ||
|
|
cbf1cb8c4a | ||
|
|
7fc8e6e8bb | ||
|
|
48dc808dcb | ||
|
|
95dc31be91 | ||
|
|
55223b763a | ||
|
|
cff301499f | ||
|
|
4cdb8a986a | ||
|
|
0ae0c03775 | ||
|
|
d6378922cf | ||
|
|
6ce5fd59a3 | ||
|
|
b93cdd8f4c | ||
|
|
cadf7742ab | ||
|
|
5f8021c570 | ||
|
|
1b0d0520bc | ||
|
|
be99b0aabb | ||
|
|
2ab1917b73 | ||
|
|
111781dc71 | ||
|
|
7df09a9ebd | ||
|
|
7b13dff29c | ||
|
|
c48abbf5c9 | ||
|
|
12b459507a | ||
|
|
77a6cc5e53 | ||
|
|
978d2d603a | ||
|
|
01a0d5f05f | ||
|
|
0569d64cc9 | ||
|
|
f55f895cfd | ||
|
|
de7f084633 | ||
|
|
0c2683afaa | ||
|
|
d6b850178e | ||
|
|
a486a09d9e | ||
|
|
07a41e0266 | ||
|
|
052894a628 | ||
|
|
60f26ca40e | ||
|
|
d5e392c3bc | ||
|
|
8b00f15710 | ||
|
|
0ebe63eff3 | ||
|
|
9955181b0d | ||
|
|
477b485a34 | ||
|
|
03eece4e41 | ||
|
|
a43fe1789b | ||
|
|
1e0c5dac83 | ||
|
|
680c782a56 | ||
|
|
dfe8aa56e9 | ||
|
|
71494984de | ||
|
|
8117d043aa | ||
|
|
9d02d5ab78 | ||
|
|
7803bc87a6 | ||
|
|
27b322bf25 | ||
|
|
def8b6bdc2 | ||
|
|
ee6df45e2d | ||
|
|
a74409c3ec | ||
|
|
672a0b3561 | ||
|
|
48b918a551 | ||
|
|
4aa738fcc9 | ||
|
|
7eefa2e6c2 | ||
|
|
d4136ef0e3 | ||
|
|
469cf3dd24 | ||
|
|
2d60e795c0 | ||
|
|
754d6cd3bd | ||
|
|
d1b4053205 | ||
|
|
1cd5e71904 | ||
|
|
cb4f380313 | ||
|
|
93e0104c1e | ||
|
|
c9aed019d5 | ||
|
|
8b8229f73a | ||
|
|
039d2f9350 | ||
|
|
bb3044d168 | ||
|
|
8dace27f56 | ||
|
|
30932b0d7c | ||
|
|
afdd3834b9 | ||
|
|
02b63bf8d2 | ||
|
|
fb19abb69f | ||
|
|
1528ecac52 | ||
|
|
4cb892133f | ||
|
|
5864b060a9 | ||
|
|
0ebfedbf4d | ||
|
|
45c6495121 | ||
|
|
24ad9464b5 | ||
|
|
0f34416726 | ||
|
|
7249bbf92b | ||
|
|
5f18779f1d | ||
|
|
740b0f217f | ||
|
|
bc4fbfd28a | ||
|
|
37a0c73dc3 | ||
|
|
53e153a696 | ||
|
|
06082f2273 | ||
|
|
b1c123977d | ||
|
|
535b6d00c4 | ||
|
|
5594b55e5c | ||
|
|
9229135588 | ||
|
|
9f2aafb95e | ||
|
|
ce20a1d940 | ||
|
|
c2b2c2a6ae | ||
|
|
b2d9688fbe | ||
|
|
254407e89e | ||
|
|
a5069cd148 | ||
|
|
91d2db6116 | ||
|
|
4c9366d495 | ||
|
|
915620da34 | ||
|
|
5d3f1fcf08 | ||
|
|
9554160edc | ||
|
|
fc928da75b | ||
|
|
8c1118b429 | ||
|
|
846d4e7ab5 | ||
|
|
816c24c1b6 | ||
|
|
6ac1c8711c | ||
|
|
c9cd2e6c1e | ||
|
|
4c1630d61d | ||
|
|
ddbee2f0ec | ||
|
|
8de8938be1 | ||
|
|
675e4a10dd | ||
|
|
2b2cc9cf6c | ||
|
|
0b74fd81e3 | ||
|
|
941652b4bc | ||
|
|
2ba9d991bf | ||
|
|
ba5930b585 | ||
|
|
3868afce93 | ||
|
|
ba4ed9afff | ||
|
|
adeb2de366 | ||
|
|
0071136146 | ||
|
|
cd48094a18 | ||
|
|
e0ac9654c3 | ||
|
|
196d5353c8 | ||
|
|
e9783b2317 | ||
|
|
47d92dba14 | ||
|
|
24eeca8f79 | ||
|
|
f7ac92cd01 | ||
|
|
63bd416e7c | ||
|
|
e6b23b2a98 | ||
|
|
83bc33015d | ||
|
|
c660118f8b | ||
|
|
1ec217905d | ||
|
|
bd97ecb9eb | ||
|
|
9891a302ac | ||
|
|
37982e0448 | ||
|
|
22828c9d23 | ||
|
|
e3a176b2f9 | ||
|
|
30fa22aed5 | ||
|
|
0dd6289509 | ||
|
|
e23a7b9c02 | ||
|
|
b48cffd880 | ||
|
|
2f52c018cd | ||
|
|
143a88ac1d | ||
|
|
21df381d12 | ||
|
|
e7f53d2c07 | ||
|
|
a77a149664 | ||
|
|
0a75cde80c | ||
|
|
8250058296 | ||
|
|
d3c4c4cd02 | ||
|
|
aa41d9e15b | ||
|
|
186ec3c11d | ||
|
|
620954631a | ||
|
|
e0505dc974 | ||
|
|
ce13f83d46 | ||
|
|
1a420e2f22 | ||
|
|
d1f83fa669 | ||
|
|
4425db7ca3 | ||
|
|
ceecb6cc81 | ||
|
|
c8ad9222fd | ||
|
|
7e68e09b22 | ||
|
|
e3150f8203 | ||
|
|
93ef620448 | ||
|
|
ba711b7f14 | ||
|
|
0d3cb029d8 | ||
|
|
2807573012 | ||
|
|
8528bfefc1 | ||
|
|
d561883144 | ||
|
|
2dacc969a3 | ||
|
|
63adca1fac | ||
|
|
4dfa9b432e | ||
|
|
b8cdb49ea5 | ||
|
|
ffe1ca643e | ||
|
|
95cafa3200 | ||
|
|
784028c7a9 | ||
|
|
fcb4d361bc | ||
|
|
ceb9985242 | ||
|
|
03644c0561 | ||
|
|
7ef502f3b0 | ||
|
|
84f0b65fa9 | ||
|
|
66b9a5d15f | ||
|
|
e82edb89ae | ||
|
|
37518ed7b0 | ||
|
|
b76d825f55 | ||
|
|
10afbd3e4f | ||
|
|
c0d69521fe | ||
|
|
e54596809e | ||
|
|
6347b56751 | ||
|
|
48eca55a4c | ||
|
|
5ba9975c10 | ||
|
|
7b818c5cde | ||
|
|
443e88b4a9 | ||
|
|
64540d2d36 | ||
|
|
94555d2a60 | ||
|
|
a329bc2424 | ||
|
|
b3b8a54f7f | ||
|
|
5539bbd3b9 | ||
|
|
b919678913 | ||
|
|
7fe6aa2a97 | ||
|
|
055be9b058 | ||
|
|
a75c8a2e4f |
+18
-14
@@ -1,16 +1,20 @@
|
||||
build/
|
||||
backup
|
||||
subl.bat
|
||||
multimedia.c
|
||||
multimedia.h
|
||||
playground.py
|
||||
ui_notes.txt
|
||||
run_linux.bat
|
||||
l.log
|
||||
vendor
|
||||
x64/Debug
|
||||
x64/Release
|
||||
.vs/
|
||||
|
||||
*.10x
|
||||
src/external/SDL/
|
||||
build/
|
||||
package/
|
||||
|
||||
project.te
|
||||
*.rdbg
|
||||
*.spall
|
||||
*.sublime-workspace
|
||||
*.pdb
|
||||
te
|
||||
te_debug
|
||||
*.wasm
|
||||
*.o
|
||||
*sublime-project*
|
||||
*sublime-workspace*
|
||||
*.html
|
||||
text_editor.js
|
||||
*.map
|
||||
*.exe
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# te, a text editor
|
||||
|
||||

|
||||
|
||||
In order to build on windows you need to call build.bat from
|
||||
the x64 Native Command Tools prompt that ships with Visual Studio.
|
||||
|
||||
For Linux you will need clang, SDL3 and libbacktrace libraries to be installed and
|
||||
available on your system. Just call build.sh when these are available.
|
||||
@@ -1,19 +1,45 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
cd /D "%~dp0"
|
||||
|
||||
for %%a in (%*) do set "%%~a=1"
|
||||
if not "%release%"=="1" set debug=1
|
||||
if "%debug%"=="1" set release=0 && echo [debug mode]
|
||||
if "%release%"=="1" set debug=0 && echo [release mode]
|
||||
if "%package%"=="1" echo [package zip]
|
||||
|
||||
set common=-I ../src/ -g -fdiagnostics-absolute-paths -Wno-unsequenced -Wno-single-bit-bitfield-constant-conversion -Wall -Wno-missing-braces -Wextra -Wno-missing-field-initializers
|
||||
set wasm_flags=--target=wasm32 -nostdlib -mbulk-memory -msimd128 -Wl,-export-dynamic,--allow-undefined,--import-memory,--no-entry,--initial-memory=131072000,--max-memory=4294967296
|
||||
|
||||
if not exist build (
|
||||
mkdir build
|
||||
if not exist "src\external\SDL" (
|
||||
pushd src\external
|
||||
git clone https://github.com/libsdl-org/SDL.git
|
||||
pushd SDL
|
||||
git checkout release-3.4.0
|
||||
cmake -S . -B build_win32_static -DCMAKE_BUILD_TYPE=Release -DSDL_STATIC=ON
|
||||
pushd build_win32_static
|
||||
msbuild SDL3.sln -maxCpuCount:16 -p:Configuration=Release
|
||||
popd
|
||||
popd
|
||||
popd
|
||||
)
|
||||
set sdl=..\src\external\SDL
|
||||
set sdllib=%sdl%\build_win32_static\Release
|
||||
|
||||
mkdir build
|
||||
pushd build
|
||||
if "%testing%"=="1" echo [testing] && set didbuild=1 && clang ../src/testing/testing_main.c -o testing.exe %common% || exit 1 /b
|
||||
if "%scratch%"=="1" echo [scratch] && set didbuild=1 && clang ../src/scratch/scratch_main.c -o ../src/scratch/main.wasm %common% %wasm_flags% || exit 1 /b
|
||||
|
||||
set flags=/EHsc- /MT /Zi /FC /nologo /WX /W3 /wd4200 /wd4334 /diagnostics:column
|
||||
if "%release%"=="1" set flags=%flags% -O2 -DDEBUG_BUILD=0
|
||||
|
||||
set libs=%sdllib%/SDL3-static.lib %sdllib%/SDL_uclibc.lib kernel32.lib gdi32.lib user32.lib Imm32.lib ole32.lib Shell32.lib OleAut32.lib Cfgmgr32.lib Setupapi.lib Advapi32.lib version.lib winmm.lib
|
||||
rem rc ..\data\icon.rc
|
||||
cl %flags% ../src/text_editor/text_editor.cpp -Fe:te.exe -I%sdl%/include -I../src/external/glad -I../src/ %libs% -link /SUBSYSTEM:WINDOWS /NODEFAULTLIB:LIBCMT /NODEFAULTLIB:MSVCRTD ..\data\icon.res
|
||||
|
||||
:: small util when working on the editor using the editor
|
||||
copy te.exe te2.exe
|
||||
|
||||
popd
|
||||
|
||||
if "%didbuild%"=="" (
|
||||
echo [WARNING] no valid build target specified
|
||||
exit /b 1
|
||||
)
|
||||
if "%package%"=="1" (
|
||||
copy build\te.exe .
|
||||
"C:\Program Files\7-Zip\7z.exe" a te.zip te.exe
|
||||
del te.exe
|
||||
)
|
||||
@@ -1,9 +1,10 @@
|
||||
cd "$(dirname "$0")"
|
||||
#!/usr/bin/bash
|
||||
|
||||
if [ ! -d build ]; then
|
||||
mkdir build
|
||||
fi
|
||||
mkdir build
|
||||
cd build
|
||||
# clang ../src/testing/testing_main.c -o testing_main -I../src/ -ldl -lm -lbacktrace -g -Wall -Wextra -Wno-missing-braces -Wno-missing-field-initializers -Wno-single-bit-bitfield-constant-conversion -fdiagnostics-absolute-paths -Wno-writable-strings -Wno-unsequenced
|
||||
tcc $(realpath ../src/testing/testing_main.c) -o testing_main -I$(realpath ../src/) -b -g -ldl -lm -Wall -Wextra
|
||||
./testing_main
|
||||
|
||||
FLAGS="-Wall -Wno-missing-braces -Wno-writable-strings -nostdlib++ -fsanitize=address -fno-exceptions -fdiagnostics-absolute-paths -g -I../src -DDEBUG_BUILD=1"
|
||||
I="-I../src/external/SDL/include -I../src/external/lua/src -I../src/external/glad"
|
||||
clang -o te $FLAGS ../src/text_editor/text_editor.cpp $I -lSDL3 -lm -lbacktrace
|
||||
|
||||
# -v -Wl,--verbose
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
@echo off
|
||||
|
||||
set incs=-Isrc/external/SDL/include -Isrc/external/lua/src -Isrc/external/glad -Lsrc/external/SDL/build_web -Isrc
|
||||
set wasmflags=-sALLOW_MEMORY_GROWTH=1 -sMAXIMUM_MEMORY=1gb -msimd128 -sTOTAL_STACK=5MB -sINITIAL_MEMORY=256mb -sUSE_WEBGL2 -sFULL_ES3=1 -sASYNCIFY -sASSERTIONS=2
|
||||
set flags=-Wall -Wno-missing-braces -Wno-writable-strings -nostdlib++ -fno-exceptions -fdiagnostics-absolute-paths
|
||||
set dbg=-g -DDEBUG_BUILD=1 -gsource-map
|
||||
set rel=-O3 -DDEBUG_BUILD=0
|
||||
|
||||
emcc -o text_editor.html --shell-file=data/shell.html %flags% %incs% %wasmflags% %dbg% -lm -lSDL3 src/text_editor/text_editor.cpp
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
incs="-Isrc/external/SDL/include -Isrc/external/lua/src -Isrc/external/glad -Lsrc/external/SDL/build_web -Isrc"
|
||||
wasmflags="-sALLOW_MEMORY_GROWTH=1 -sMAXIMUM_MEMORY=1gb -msimd128 -sTOTAL_STACK=5MB -sINITIAL_MEMORY=256mb -sUSE_WEBGL2 -sFULL_ES3=1 -sUSE_SDL=3 -sASYNCIFY -sASSERTIONS=2"
|
||||
flags="-Wall -Wno-missing-braces -Wno-writable-strings -nostdlib++ -fno-exceptions -fdiagnostics-absolute-paths"
|
||||
dbg="-g -DDEBUG_BUILD=1 -gsource-map"
|
||||
rel="-O3 -DDEBUG_BUILD=0"
|
||||
|
||||
emcc -o text_editor.html --shell-file=data/shell.html $flags $incs $wasmflags $dbg -lm -lSDL3 src/text_editor/text_editor.cpp
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1 @@
|
||||
id ICON "icon.ico"
|
||||
Binary file not shown.
@@ -0,0 +1,19 @@
|
||||
left_mouse :: ctrl add cursor, selections work, alt load word
|
||||
right_mouse :: ctrl exec word,alt go back
|
||||
ctrl+q exec alt+q load
|
||||
alt+w go back
|
||||
|
||||
|
||||
ctrl mleft - add cursor
|
||||
ctrl shift mleft - add cursor and set selection
|
||||
alt mleft -
|
||||
alt ctrl mleft -
|
||||
alt shift mleft -
|
||||
mright - destroy pointed at selection and copy | copy line
|
||||
alt mright - go back
|
||||
ctrl mright - load word
|
||||
shift mright -
|
||||
alt shift mright -
|
||||
alt ctrl mright - go forward
|
||||
ctrl shift mright - exec word
|
||||
mmiddle -
|
||||
@@ -0,0 +1,26 @@
|
||||
https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-peeknamedpipe
|
||||
https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile
|
||||
https://github.com/sheredom/subprocess.h
|
||||
https://github.com/4coder-archive/4coder/blob/master/platform_win32/win32_4ed.cpp :486
|
||||
|
||||
https://stackoverflow.com/questions/67551213/read-processs-stdout-output-in-win32-c-desktop-application
|
||||
https://handmade.network/forums/t/1219-win32_asynchronous_pipes_question
|
||||
|
||||
|
||||
|
||||
- I guess you can just do color matching in rendering, have a table and try matching words etc.
|
||||
- maybe I should disallow somehow taking pointers to dynamic array items? Just references!
|
||||
|
||||
|
||||
- create process with buffer (append to end)
|
||||
- create process and put it at specific cursor position in buffer?
|
||||
- create process without buffer attached (maybe for error?) (we want some notification that it failed or something)
|
||||
|
||||
- code indexing: maybe we can do that line based since you want to jump to line not a particular place.
|
||||
- on edit we could reparse the line for symbol
|
||||
- I think 4coder first tries to offset the index file on immediate edit, on frame tick it tries to reparse the modified file in entirety
|
||||
- offseting things doesn't seem that hard since we already figured it out with cursors
|
||||
- would it be fast enough to just reparse the entire file of certain size every time?
|
||||
- coloring seems even more hairy to do correctly because there are multiline comments etc.
|
||||
- it honestly seems no matter what you do it's a very hairy problem, text was not meant for this
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
absolute path C:/notes
|
||||
lower case c:/notes
|
||||
backwards slash C:\
|
||||
go back ../notes
|
||||
current dir ./notes
|
||||
file without slash notes
|
||||
allows spaces C:\Program Files/
|
||||
multiple slashes C:\\\\\\\\\\\\\\\\\\\\\\\\\thing
|
||||
you can combine the slashes C:\/Work
|
||||
disallowed characters for names * \ / ? > < | : "
|
||||
|
||||
There is literally no way to consistently skip the windows path, you always have to relay on heuristics
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 302 KiB |
@@ -0,0 +1,56 @@
|
||||
! What precise workflow do I need for me to be viable to use this?
|
||||
! From a user (novice) point of view, how does it look like?
|
||||
|
||||
- Buffer edit history separate from caret history (tagging history blocks with carets???)
|
||||
- We need regex for: [FormatCode matching, IsCode matching,
|
||||
- Macros
|
||||
- Window position: vscode opens in fullscreen and then remembers what position it was close in (could be a config option)
|
||||
- On Linux: Try to implement is_debugger_present()
|
||||
- Maybe IPC for te.exe when it's already open and file arguments are passed it should perhaps open a buffer in current window??
|
||||
- Add <<File>> <<ProjectDirectory>> template strings to Open (Then remove SEtWorkdirhere)
|
||||
- :Set Filename to name current buffer ??? :O and others like that!!
|
||||
- Make a fuzzy command !> grep and fuzzy over it??? (doesn't seem very useful for grep)
|
||||
- Make the equivalent of SearchOpenBuffers but for cmds like !@git grep -n "@>"
|
||||
- Add Bool variable
|
||||
- Guide on the first page for new users with links to configs, tutorials
|
||||
|
||||
Debug session:
|
||||
- Report errorf - use coroutine dialogs
|
||||
- Replace in render layer also
|
||||
- BlockAllocator something is not working there which only showed after executing OpenCode on many files
|
||||
- Some bad allocating happening in Clipboard for sure OR MAYBE BLOCK ALLOCATOR ACTION
|
||||
|
||||
New UI Session
|
||||
- Uneditable buffers ?
|
||||
- Maybe marked allocations??? So that we can associate allocations with a buffer or view and then dealloc all at the same time
|
||||
|
||||
- Open with seek string (open at pattern) filename:32 filename:/^Window$/
|
||||
- Show what process/coroutines are running and allow to kill (active process buffer?)
|
||||
- Database idea: use special buffers to store information
|
||||
- Editing the buffer doesn't seem to be the slow part rather, accessing the data and putting it into the buffer (potentially hitting many different memory locations) I have a crazy idea to use buffers in order to store the names in a serialized format
|
||||
- non editable buffers (raw ops ok, non-raw no op)
|
||||
- DBBuffer
|
||||
|
||||
- Try to add Tracking Allocator and rewrite the app, free all memory at the end of the app and check all is well
|
||||
|
||||
FEATURE Some decl/function indexing in fuzzy format
|
||||
FEATURE dump text editor state to file, restore state
|
||||
- Search and replace
|
||||
- word complete
|
||||
- escapeing multiple cursor after ctrl + d should put the cursor where it was (probably will need to swap secondary and primary cursor for new cursor
|
||||
- draw indentation levels like in sublime (those lines) - we render chars one by one so seems relatively easy to figure out if whitespace belongs to beginning of line (make sure to add max value like 40 because of big files)
|
||||
- code sections, visual demarkation if beginning of line has a very specific text + goto next / goto prev section hotkey!
|
||||
- combine glyph and selection rendering
|
||||
|
||||
- Test stdin writing code
|
||||
- Implement shell interaction (the valid cmd lines should start with '>' or '$', user can add more lines like this to expand the command size maybe?, if we have a case where we have a line with '>' but the last line doesn't have (just a space) then it should execute?)
|
||||
- drop text into window
|
||||
- I think the way sublime text and we display line highlights is confusing with multiple cursors (line highlight can be confused with selection)
|
||||
- make the editor replayable, store events and then replay, be careful about globals
|
||||
- maybe open should return multiple options if there are many more? (like in sublime if many symbols you get a window and you choose and it automatically jumps you to the symbol in the background)
|
||||
- I want a way to assign flags to buffers/views/windows from user perspective so that console window concept can be created from user space
|
||||
- font cache and on demand unicode loads
|
||||
- color parens, braces
|
||||
- gap buffer
|
||||
- optimize rendering - command buffer, and vertice buffer instead of vertice buffer with scissor
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
Basic Latin
|
||||
! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~
|
||||
Latin-1 Supplement
|
||||
¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ® ¯ ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ
|
||||
Latin Extended-A
|
||||
Ā ā Ă ă Ą ą Ć ć Ĉ ĉ Ċ ċ Č č Ď ď Đ đ Ē ē Ĕ ĕ Ė ė Ę ę Ě ě Ĝ ĝ Ğ ğ Ġ ġ Ģ ģ Ĥ ĥ Ħ ħ Ĩ ĩ Ī ī Ĭ ĭ Į į İ ı IJ ij Ĵ ĵ Ķ ķ ĸ Ĺ ĺ Ļ ļ Ľ ľ Ŀ ŀ Ł ł Ń ń Ņ ņ Ň ň ʼn Ŋ ŋ Ō ō Ŏ ŏ Ő ő Œ œ Ŕ ŕ Ŗ ŗ Ř ř Ś ś Ŝ ŝ Ş ş Š š Ţ ţ Ť ť Ŧ ŧ Ũ ũ Ū ū Ŭ ŭ Ů ů Ű ű Ų ų Ŵ ŵ Ŷ ŷ Ÿ Ź ź Ż ż Ž ž ſ
|
||||
Latin Extended-B
|
||||
ƀ Ɓ Ƃ ƃ Ƅ ƅ Ɔ Ƈ ƈ Ɖ Ɗ Ƌ ƌ ƍ Ǝ Ə Ɛ Ƒ ƒ Ɠ Ɣ ƕ Ɩ Ɨ Ƙ ƙ ƚ ƛ Ɯ Ɲ ƞ Ɵ Ơ ơ Ƣ ƣ Ƥ ƥ Ʀ Ƨ ƨ Ʃ ƪ ƫ Ƭ ƭ Ʈ Ư ư Ʊ Ʋ Ƴ ƴ Ƶ ƶ Ʒ Ƹ ƹ ƺ ƻ Ƽ ƽ ƾ ƿ ǀ ǁ ǂ ǃ DŽ Dž dž LJ Lj lj NJ Nj nj Ǎ ǎ Ǐ ǐ Ǒ ǒ Ǔ ǔ Ǖ ǖ Ǘ ǘ Ǚ ǚ Ǜ ǜ ǝ Ǟ ǟ Ǡ ǡ Ǣ ǣ Ǥ ǥ Ǧ ǧ Ǩ ǩ Ǫ ǫ Ǭ ǭ Ǯ ǯ ǰ DZ Dz dz Ǵ ǵ Ǻ ǻ Ǽ ǽ Ǿ ǿ Ȁ ȁ Ȃ ȃ ...
|
||||
IPA Extensions
|
||||
ɐ ɑ ɒ ɓ ɔ ɕ ɖ ɗ ɘ ə ɚ ɛ ɜ ɝ ɞ ɟ ɠ ɡ ɢ ɣ ɤ ɥ ɦ ɧ ɨ ɩ ɪ ɫ ɬ ɭ ɮ ɯ ɰ ɱ ɲ ɳ ɴ ɵ ɶ ɷ ɸ ɹ ɺ ɻ ɼ ɽ ɾ ɿ ʀ ʁ ʂ ʃ ʄ ʅ ʆ ʇ ʈ ʉ ʊ ʋ ʌ ʍ ʎ ʏ ʐ ʑ ʒ ʓ ʔ ʕ ʖ ʗ ʘ ʙ ʚ ʛ ʜ ʝ ʞ ʟ ʠ ʡ ʢ ʣ ʤ ʥ ʦ ʧ ʨ
|
||||
Spacing Modifier Letters
|
||||
ʰ ʱ ʲ ʳ ʴ ʵ ʶ ʷ ʸ ʹ ʺ ʻ ʼ ʽ ʾ ʿ ˀ ˁ ˂ ˃ ˄ ˅ ˆ ˇ ˈ ˉ ˊ ˋ ˌ ˍ ˎ ˏ ː ˑ ˒ ˓ ˔ ˕ ˖ ˗ ˘ ˙ ˚ ˛ ˜ ˝ ˞ ˠ ˡ ˢ ˣ ˤ ˥ ˦ ˧ ˨ ˩
|
||||
Combining Diacritical Marks
|
||||
̀ ́ ̂ ̃ ̄ ̅ ̆ ̇ ̈ ̉ ̊ ̋ ̌ ̍ ̎ ̏ ̐ ̑ ̒ ̓ ̔ ̕ ̖ ̗ ̘ ̙ ̚ ̛ ̜ ̝ ̞ ̟ ̠ ̡ ̢ ̣ ̤ ̥ ̦ ̧ ̨ ̩ ̪ ̫ ̬ ̭ ̮ ̯ ̰ ̱ ̲ ̳ ̴ ̵ ̶ ̷ ̸ ̹ ̺ ̻ ̼ ̽ ̾ ̿ ̀ ́ ͂ ̓ ̈́ ͅ ͠ ͡
|
||||
Greek
|
||||
ʹ ͵ ͺ ; ΄ ΅ Ά · Έ Ή Ί Ό Ύ Ώ ΐ Α Β Γ Δ Ε Ζ Η Θ Ι Κ Λ Μ Ν Ξ Ο Π Ρ Σ Τ Υ Φ Χ Ψ Ω Ϊ Ϋ ά έ ή ί ΰ α β γ δ ε ζ η θ ι κ λ μ ν ξ ο π ρ ς σ τ υ φ χ ψ ω ϊ ϋ ό ύ ώ ϐ ϑ ϒ ϓ ϔ ϕ ϖ Ϛ Ϝ Ϟ Ϡ Ϣ ϣ Ϥ ϥ Ϧ ϧ Ϩ ϩ Ϫ ϫ Ϭ ϭ Ϯ ϯ ϰ ϱ ϲ ϳ
|
||||
Cyrillic
|
||||
Ё Ђ Ѓ Є Ѕ І Ї Ј Љ Њ Ћ Ќ Ў Џ А Б В Г Д Е Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Ъ Ы Ь Э Ю Я а б в г д е ж з и й к л м н о п р с т у ф х ц ч ш щ ъ ы ь э ю я ё ђ ѓ є ѕ і ї ј љ њ ћ ќ ў џ Ѡ ѡ Ѣ ѣ Ѥ ѥ Ѧ ѧ Ѩ ѩ Ѫ ѫ Ѭ ѭ Ѯ ѯ Ѱ ѱ Ѳ ѳ Ѵ ѵ Ѷ ѷ Ѹ ѹ Ѻ ѻ Ѽ ѽ Ѿ ѿ Ҁ ҁ ҂ ҃ ...
|
||||
Armenian
|
||||
Ա Բ Գ Դ Ե Զ Է Ը Թ Ժ Ի Լ Խ Ծ Կ Հ Ձ Ղ Ճ Մ Յ Ն Շ Ո Չ Պ Ջ Ռ Ս Վ Տ Ր Ց Ւ Փ Ք Օ Ֆ ՙ ՚ ՛ ՜ ՝ ՞ ՟ ա բ գ դ ե զ է ը թ ժ ի լ խ ծ կ հ ձ ղ ճ մ յ ն շ ո չ պ ջ ռ ս վ տ ր ց ւ փ ք օ ֆ և ։
|
||||
Hebrew
|
||||
֑ ֒ ֓ ֔ ֕ ֖ ֗ ֘ ֙ ֚ ֛ ֜ ֝ ֞ ֟ ֠ ֡ ֣ ֤ ֥ ֦ ֧ ֨ ֩ ֪ ֫ ֬ ֭ ֮ ֯ ְ ֱ ֲ ֳ ִ ֵ ֶ ַ ָ ֹ ֻ ּ ֽ ־ ֿ ׀ ׁ ׂ ׃ ׄ א ב ג ד ה ו ז ח ט י ך כ ל ם מ ן נ ס ע ף פ ץ צ ק ר ש ת װ ױ ײ ׳ ״
|
||||
Arabic
|
||||
، ؛ ؟ ء آ أ ؤ إ ئ ا ب ة ت ث ج ح خ د ذ ر ز س ش ص ض ط ظ ع غ ـ ف ق ك ل م ن ه و ى ي ً ٌ ٍ َ ُ ِ ّ ْ ٠ ١ ٢ ٣ ٤ ٥ ٦ ٧ ٨ ٩ ٪ ٫ ٬ ٭ ٰ ٱ ٲ ٳ ٴ ٵ ٶ ٷ ٸ ٹ ٺ ٻ ټ ٽ پ ٿ ڀ ځ ڂ ڃ ڄ څ چ ڇ ڈ ډ ڊ ڋ ڌ ڍ ڎ ڏ ڐ ڑ ڒ ړ ڔ ڕ ږ ڗ ژ ڙ ښ ڛ ڜ ڝ ڞ ڟ ڠ ڡ ڢ ڣ ڤ ڥ ڦ ڧ ڨ ک ڪ ګ ڬ ڭ ڮ گ ڰ ڱ ...
|
||||
Devanagari
|
||||
ँ ं ः अ आ इ ई उ ऊ ऋ ऌ ऍ ऎ ए ऐ ऑ ऒ ओ औ क ख ग घ ङ च छ ज झ ञ ट ठ ड ढ ण त थ द ध न ऩ प फ ब भ म य र ऱ ल ळ ऴ व श ष स ह ़ ऽ ा ि ी ु ू ृ ॄ ॅ ॆ े ै ॉ ॊ ो ौ ् ॐ ॑ ॒ ॓ ॔ क़ ख़ ग़ ज़ ड़ ढ़ फ़ य़ ॠ ॡ ॢ ॣ । ॥ ० १ २ ३ ४ ५ ६ ७ ८ ९ ॰
|
||||
Bengali
|
||||
ঁ ং ঃ অ আ ই ঈ উ ঊ ঋ ঌ এ ঐ ও ঔ ক খ গ ঘ ঙ চ ছ জ ঝ ঞ ট ঠ ড ঢ ণ ত থ দ ধ ন প ফ ব ভ ম য র ল শ ষ স হ ় া ি ী ু ূ ৃ ৄ ে ৈ ো ৌ ্ ৗ ড় ঢ় য় ৠ ৡ ৢ ৣ ০ ১ ২ ৩ ৪ ৫ ৬ ৭ ৮ ৯ ৰ ৱ ৲ ৳ ৴ ৵ ৶ ৷ ৸ ৹ ৺
|
||||
Gurmukhi
|
||||
ਂ ਅ ਆ ਇ ਈ ਉ ਊ ਏ ਐ ਓ ਔ ਕ ਖ ਗ ਘ ਙ ਚ ਛ ਜ ਝ ਞ ਟ ਠ ਡ ਢ ਣ ਤ ਥ ਦ ਧ ਨ ਪ ਫ ਬ ਭ ਮ ਯ ਰ ਲ ਲ਼ ਵ ਸ਼ ਸ ਹ ਼ ਾ ਿ ੀ ੁ ੂ ੇ ੈ ੋ ੌ ੍ ਖ਼ ਗ਼ ਜ਼ ੜ ਫ਼ ੦ ੧ ੨ ੩ ੪ ੫ ੬ ੭ ੮ ੯ ੰ ੱ ੲ ੳ ੴ
|
||||
Gujarati
|
||||
ઁ ં ઃ અ આ ઇ ઈ ઉ ઊ ઋ ઍ એ ઐ ઑ ઓ ઔ ક ખ ગ ઘ ઙ ચ છ જ ઝ ઞ ટ ઠ ડ ઢ ણ ત થ દ ધ ન પ ફ બ ભ મ ય ર લ ળ વ શ ષ સ હ ઼ ઽ ા િ ી ુ ૂ ૃ ૄ ૅ ે ૈ ૉ ો ૌ ્ ૐ ૠ ૦ ૧ ૨ ૩ ૪ ૫ ૬ ૭ ૮ ૯
|
||||
Oriya
|
||||
ଁ ଂ ଃ ଅ ଆ ଇ ଈ ଉ ଊ ଋ ଌ ଏ ଐ ଓ ଔ କ ଖ ଗ ଘ ଙ ଚ ଛ ଜ ଝ ଞ ଟ ଠ ଡ ଢ ଣ ତ ଥ ଦ ଧ ନ ପ ଫ ବ ଭ ମ ଯ ର ଲ ଳ ଶ ଷ ସ ହ ଼ ଽ ା ି ୀ ୁ ୂ ୃ େ ୈ ୋ ୌ ୍ ୖ ୗ ଡ଼ ଢ଼ ୟ ୠ ୡ ୦ ୧ ୨ ୩ ୪ ୫ ୬ ୭ ୮ ୯ ୰
|
||||
Tamil
|
||||
ஂ ஃ அ ஆ இ ஈ உ ஊ எ ஏ ஐ ஒ ஓ ஔ க ங ச ஜ ஞ ட ண த ந ன ப ம ய ர ற ல ள ழ வ ஷ ஸ ஹ ா ி ீ ு ூ ெ ே ை ொ ோ ௌ ் ௗ ௧ ௨ ௩ ௪ ௫ ௬ ௭ ௮ ௯ ௰ ௱ ௲
|
||||
Telugu
|
||||
ఁ ం ః అ ఆ ఇ ఈ ఉ ఊ ఋ ఌ ఎ ఏ ఐ ఒ ఓ ఔ క ఖ గ ఘ ఙ చ ఛ జ ఝ ఞ ట ఠ డ ఢ ణ త థ ద ధ న ప ఫ బ భ మ య ర ఱ ల ళ వ శ ష స హ ా ి ీ ు ూ ృ ౄ ె ే ై ొ ో ౌ ్ ౕ ౖ ౠ ౡ ౦ ౧ ౨ ౩ ౪ ౫ ౬ ౭ ౮ ౯
|
||||
Kannada
|
||||
ಂ ಃ ಅ ಆ ಇ ಈ ಉ ಊ ಋ ಌ ಎ ಏ ಐ ಒ ಓ ಔ ಕ ಖ ಗ ಘ ಙ ಚ ಛ ಜ ಝ ಞ ಟ ಠ ಡ ಢ ಣ ತ ಥ ದ ಧ ನ ಪ ಫ ಬ ಭ ಮ ಯ ರ ಱ ಲ ಳ ವ ಶ ಷ ಸ ಹ ಾ ಿ ೀ ು ೂ ೃ ೄ ೆ ೇ ೈ ೊ ೋ ೌ ್ ೕ ೖ ೞ ೠ ೡ ೦ ೧ ೨ ೩ ೪ ೫ ೬ ೭ ೮ ೯
|
||||
Malayalam
|
||||
ം ഃ അ ആ ഇ ഈ ഉ ഊ ഋ ഌ എ ഏ ഐ ഒ ഓ ഔ ക ഖ ഗ ഘ ങ ച ഛ ജ ഝ ഞ ട ഠ ഡ ഢ ണ ത ഥ ദ ധ ന പ ഫ ബ ഭ മ യ ര റ ല ള ഴ വ ശ ഷ സ ഹ ാ ി ീ ു ൂ ൃ െ േ ൈ ൊ ോ ൌ ് ൗ ൠ ൡ ൦ ൧ ൨ ൩ ൪ ൫ ൬ ൭ ൮ ൯
|
||||
Thai
|
||||
ก ข ฃ ค ฅ ฆ ง จ ฉ ช ซ ฌ ญ ฎ ฏ ฐ ฑ ฒ ณ ด ต ถ ท ธ น บ ป ผ ฝ พ ฟ ภ ม ย ร ฤ ล ฦ ว ศ ษ ส ห ฬ อ ฮ ฯ ะ ั า ำ ิ ี ึ ื ุ ู ฺ ฿ เ แ โ ใ ไ ๅ ๆ ็ ่ ้ ๊ ๋ ์ ํ ๎ ๏ ๐ ๑ ๒ ๓ ๔ ๕ ๖ ๗ ๘ ๙ ๚ ๛
|
||||
Lao
|
||||
ກ ຂ ຄ ງ ຈ ຊ ຍ ດ ຕ ຖ ທ ນ ບ ປ ຜ ຝ ພ ຟ ມ ຢ ຣ ລ ວ ສ ຫ ອ ຮ ຯ ະ ັ າ ຳ ິ ີ ຶ ື ຸ ູ ົ ຼ ຽ ເ ແ ໂ ໃ ໄ ໆ ່ ້ ໊ ໋ ໌ ໍ ໐ ໑ ໒ ໓ ໔ ໕ ໖ ໗ ໘ ໙ ໜ ໝ
|
||||
Tibetan
|
||||
ༀ ༁ ༂ ༃ ༄ ༅ ༆ ༇ ༈ ༉ ༊ ་ ༌ ། ༎ ༏ ༐ ༑ ༒ ༓ ༔ ༕ ༖ ༗ ༘ ༙ ༚ ༛ ༜ ༝ ༞ ༟ ༠ ༡ ༢ ༣ ༤ ༥ ༦ ༧ ༨ ༩ ༪ ༫ ༬ ༭ ༮ ༯ ༰ ༱ ༲ ༳ ༴ ༵ ༶ ༷ ༸ ༹ ༺ ༻ ༼ ༽ ༾ ༿ ཀ ཁ ག གྷ ང ཅ ཆ ཇ ཉ ཊ ཋ ཌ ཌྷ ཎ ཏ ཐ ད དྷ ན པ ཕ བ བྷ མ ཙ ཚ ཛ ཛྷ ཝ ཞ ཟ འ ཡ ར ལ ཤ ཥ ས ཧ ཨ ཀྵ ཱ ི ཱི ུ ཱུ ྲྀ ཷ ླྀ ཹ ེ ཻ ོ ཽ ཾ ཿ ྀ ཱྀ ྂ ྃ ྄ ྅ ྆ ྇ ...
|
||||
Georgian
|
||||
Ⴀ Ⴁ Ⴂ Ⴃ Ⴄ Ⴅ Ⴆ Ⴇ Ⴈ Ⴉ Ⴊ Ⴋ Ⴌ Ⴍ Ⴎ Ⴏ Ⴐ Ⴑ Ⴒ Ⴓ Ⴔ Ⴕ Ⴖ Ⴗ Ⴘ Ⴙ Ⴚ Ⴛ Ⴜ Ⴝ Ⴞ Ⴟ Ⴠ Ⴡ Ⴢ Ⴣ Ⴤ Ⴥ ა ბ გ დ ე ვ ზ თ ი კ ლ მ ნ ო პ ჟ რ ს ტ უ ფ ქ ღ ყ შ ჩ ც ძ წ ჭ ხ ჯ ჰ ჱ ჲ ჳ ჴ ჵ ჶ ჻
|
||||
Hangul Jamo
|
||||
ᄀ ᄁ ᄂ ᄃ ᄄ ᄅ ᄆ ᄇ ᄈ ᄉ ᄊ ᄋ ᄌ ᄍ ᄎ ᄏ ᄐ ᄑ ᄒ ᄓ ᄔ ᄕ ᄖ ᄗ ᄘ ᄙ ᄚ ᄛ ᄜ ᄝ ᄞ ᄟ ᄠ ᄡ ᄢ ᄣ ᄤ ᄥ ᄦ ᄧ ᄨ ᄩ ᄪ ᄫ ᄬ ᄭ ᄮ ᄯ ᄰ ᄱ ᄲ ᄳ ᄴ ᄵ ᄶ ᄷ ᄸ ᄹ ᄺ ᄻ ᄼ ᄽ ᄾ ᄿ ᅀ ᅁ ᅂ ᅃ ᅄ ᅅ ᅆ ᅇ ᅈ ᅉ ᅊ ᅋ ᅌ ᅍ ᅎ ᅏ ᅐ ᅑ ᅒ ᅓ ᅔ ᅕ ᅖ ᅗ ᅘ ᅙ ᅟ ᅠ ᅡ ᅢ ᅣ ᅤ ᅥ ᅦ ᅧ ᅨ ᅩ ᅪ ᅫ ᅬ ᅭ ᅮ ᅯ ᅰ ᅱ ᅲ ᅳ ᅴ ᅵ ᅶ ᅷ ᅸ ᅹ ᅺ ᅻ ᅼ ᅽ ᅾ ᅿ ᆀ ᆁ ᆂ ᆃ ᆄ ...
|
||||
Latin Extended Additional
|
||||
Ḁ ḁ Ḃ ḃ Ḅ ḅ Ḇ ḇ Ḉ ḉ Ḋ ḋ Ḍ ḍ Ḏ ḏ Ḑ ḑ Ḓ ḓ Ḕ ḕ Ḗ ḗ Ḙ ḙ Ḛ ḛ Ḝ ḝ Ḟ ḟ Ḡ ḡ Ḣ ḣ Ḥ ḥ Ḧ ḧ Ḩ ḩ Ḫ ḫ Ḭ ḭ Ḯ ḯ Ḱ ḱ Ḳ ḳ Ḵ ḵ Ḷ ḷ Ḹ ḹ Ḻ ḻ Ḽ ḽ Ḿ ḿ Ṁ ṁ Ṃ ṃ Ṅ ṅ Ṇ ṇ Ṉ ṉ Ṋ ṋ Ṍ ṍ Ṏ ṏ Ṑ ṑ Ṓ ṓ Ṕ ṕ Ṗ ṗ Ṙ ṙ Ṛ ṛ Ṝ ṝ Ṟ ṟ Ṡ ṡ Ṣ ṣ Ṥ ṥ Ṧ ṧ Ṩ ṩ Ṫ ṫ Ṭ ṭ Ṯ ṯ Ṱ ṱ Ṳ ṳ Ṵ ṵ Ṷ ṷ Ṹ ṹ Ṻ ṻ Ṽ ṽ Ṿ ṿ ...
|
||||
Greek Extended
|
||||
ἀ ἁ ἂ ἃ ἄ ἅ ἆ ἇ Ἀ Ἁ Ἂ Ἃ Ἄ Ἅ Ἆ Ἇ ἐ ἑ ἒ ἓ ἔ ἕ Ἐ Ἑ Ἒ Ἓ Ἔ Ἕ ἠ ἡ ἢ ἣ ἤ ἥ ἦ ἧ Ἠ Ἡ Ἢ Ἣ Ἤ Ἥ Ἦ Ἧ ἰ ἱ ἲ ἳ ἴ ἵ ἶ ἷ Ἰ Ἱ Ἲ Ἳ Ἴ Ἵ Ἶ Ἷ ὀ ὁ ὂ ὃ ὄ ὅ Ὀ Ὁ Ὂ Ὃ Ὄ Ὅ ὐ ὑ ὒ ὓ ὔ ὕ ὖ ὗ Ὑ Ὓ Ὕ Ὗ ὠ ὡ ὢ ὣ ὤ ὥ ὦ ὧ Ὠ Ὡ Ὢ Ὣ Ὤ Ὥ Ὦ Ὧ ὰ ά ὲ έ ὴ ή ὶ ί ὸ ό ὺ ύ ὼ ώ ᾀ ᾁ ᾂ ᾃ ᾄ ᾅ ᾆ ᾇ ᾈ ᾉ ᾊ ᾋ ᾌ ᾍ ...
|
||||
General Punctuation
|
||||
‐ ‑ ‒ – — ― ‖ ‗ ‘ ’ ‚ ‛ “ ” „ ‟ † ‡ • ‣ ․ ‥ … ‧
‰ ‱ ′ ″ ‴ ‵ ‶ ‷ ‸ ‹ › ※ ‼ ‽ ‾ ‿ ⁀ ⁁ ⁂ ⁃ ⁄ ⁅ ⁆
|
||||
Superscripts and Subscripts
|
||||
⁰ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ⁺ ⁻ ⁼ ⁽ ⁾ ⁿ ₀ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ₊ ₋ ₌ ₍ ₎
|
||||
Currency Symbols
|
||||
₠ ₡ ₢ ₣ ₤ ₥ ₦ ₧ ₨ ₩ ₪ ₫
|
||||
Combining Marks for Symbols
|
||||
⃐ ⃑ ⃒ ⃓ ⃔ ⃕ ⃖ ⃗ ⃘ ⃙ ⃚ ⃛ ⃜ ⃝ ⃞ ⃟ ⃠ ⃡
|
||||
Letterlike Symbols
|
||||
℀ ℁ ℂ ℃ ℄ ℅ ℆ ℇ ℈ ℉ ℊ ℋ ℌ ℍ ℎ ℏ ℐ ℑ ℒ ℓ ℔ ℕ № ℗ ℘ ℙ ℚ ℛ ℜ ℝ ℞ ℟ ℠ ℡ ™ ℣ ℤ ℥ Ω ℧ ℨ ℩ K Å ℬ ℭ ℮ ℯ ℰ ℱ Ⅎ ℳ ℴ ℵ ℶ ℷ ℸ
|
||||
Number Forms
|
||||
⅓ ⅔ ⅕ ⅖ ⅗ ⅘ ⅙ ⅚ ⅛ ⅜ ⅝ ⅞ ⅟ Ⅰ Ⅱ Ⅲ Ⅳ Ⅴ Ⅵ Ⅶ Ⅷ Ⅸ Ⅹ Ⅺ Ⅻ Ⅼ Ⅽ Ⅾ Ⅿ ⅰ ⅱ ⅲ ⅳ ⅴ ⅵ ⅶ ⅷ ⅸ ⅹ ⅺ ⅻ ⅼ ⅽ ⅾ ⅿ ↀ ↁ ↂ
|
||||
Arrows
|
||||
← ↑ → ↓ ↔ ↕ ↖ ↗ ↘ ↙ ↚ ↛ ↜ ↝ ↞ ↟ ↠ ↡ ↢ ↣ ↤ ↥ ↦ ↧ ↨ ↩ ↪ ↫ ↬ ↭ ↮ ↯ ↰ ↱ ↲ ↳ ↴ ↵ ↶ ↷ ↸ ↹ ↺ ↻ ↼ ↽ ↾ ↿ ⇀ ⇁ ⇂ ⇃ ⇄ ⇅ ⇆ ⇇ ⇈ ⇉ ⇊ ⇋ ⇌ ⇍ ⇎ ⇏ ⇐ ⇑ ⇒ ⇓ ⇔ ⇕ ⇖ ⇗ ⇘ ⇙ ⇚ ⇛ ⇜ ⇝ ⇞ ⇟ ⇠ ⇡ ⇢ ⇣ ⇤ ⇥ ⇦ ⇧ ⇨ ⇩ ⇪
|
||||
Mathematical Operators
|
||||
∀ ∁ ∂ ∃ ∄ ∅ ∆ ∇ ∈ ∉ ∊ ∋ ∌ ∍ ∎ ∏ ∐ ∑ − ∓ ∔ ∕ ∖ ∗ ∘ ∙ √ ∛ ∜ ∝ ∞ ∟ ∠ ∡ ∢ ∣ ∤ ∥ ∦ ∧ ∨ ∩ ∪ ∫ ∬ ∭ ∮ ∯ ∰ ∱ ∲ ∳ ∴ ∵ ∶ ∷ ∸ ∹ ∺ ∻ ∼ ∽ ∾ ∿ ≀ ≁ ≂ ≃ ≄ ≅ ≆ ≇ ≈ ≉ ≊ ≋ ≌ ≍ ≎ ≏ ≐ ≑ ≒ ≓ ≔ ≕ ≖ ≗ ≘ ≙ ≚ ≛ ≜ ≝ ≞ ≟ ≠ ≡ ≢ ≣ ≤ ≥ ≦ ≧ ≨ ≩ ≪ ≫ ≬ ≭ ≮ ≯ ≰ ≱ ≲ ≳ ≴ ≵ ≶ ≷ ≸ ≹ ≺ ≻ ≼ ≽ ≾ ≿ ...
|
||||
Miscellaneous Technical
|
||||
⌀ ⌂ ⌃ ⌄ ⌅ ⌆ ⌇ ⌈ ⌉ ⌊ ⌋ ⌌ ⌍ ⌎ ⌏ ⌐ ⌑ ⌒ ⌓ ⌔ ⌕ ⌖ ⌗ ⌘ ⌙ ⌚ ⌛ ⌜ ⌝ ⌞ ⌟ ⌠ ⌡ ⌢ ⌣ ⌤ ⌥ ⌦ ⌧ ⌨ 〈 〉 ⌫ ⌬ ⌭ ⌮ ⌯ ⌰ ⌱ ⌲ ⌳ ⌴ ⌵ ⌶ ⌷ ⌸ ⌹ ⌺ ⌻ ⌼ ⌽ ⌾ ⌿ ⍀ ⍁ ⍂ ⍃ ⍄ ⍅ ⍆ ⍇ ⍈ ⍉ ⍊ ⍋ ⍌ ⍍ ⍎ ⍏ ⍐ ⍑ ⍒ ⍓ ⍔ ⍕ ⍖ ⍗ ⍘ ⍙ ⍚ ⍛ ⍜ ⍝ ⍞ ⍟ ⍠ ⍡ ⍢ ⍣ ⍤ ⍥ ⍦ ⍧ ⍨ ⍩ ⍪ ⍫ ⍬ ⍭ ⍮ ⍯ ⍰ ⍱ ⍲ ⍳ ⍴ ⍵ ⍶ ⍷ ⍸ ⍹ ⍺
|
||||
Control Pictures
|
||||
␀ ␁ ␂ ␃ ␄ ␅ ␆ ␇ ␈ ␉ ␊ ␋ ␌ ␍ ␎ ␏ ␐ ␑ ␒ ␓ ␔ ␕ ␖ ␗ ␘ ␙ ␚ ␛ ␜ ␝ ␞ ␟ ␠ ␡ ␢ ␣ 
|
||||
Optical Character Recognition
|
||||
⑀ ⑁ ⑂ ⑃ ⑄ ⑅ ⑆ ⑇ ⑈ ⑉ ⑊
|
||||
Enclosed Alphanumerics
|
||||
① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳ ⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾ ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇ ⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖ ⒗ ⒘ ⒙ ⒚ ⒛ ⒜ ⒝ ⒞ ⒟ ⒠ ⒡ ⒢ ⒣ ⒤ ⒥ ⒦ ⒧ ⒨ ⒩ ⒪ ⒫ ⒬ ⒭ ⒮ ⒯ ⒰ ⒱ ⒲ ⒳ ⒴ ⒵ Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ ⓐ ⓑ ⓒ ⓓ ⓔ ⓕ ⓖ ⓗ ⓘ ⓙ ⓚ ⓛ ⓜ ⓝ ⓞ ⓟ ...
|
||||
Box Drawing
|
||||
─ ━ │ ┃ ┄ ┅ ┆ ┇ ┈ ┉ ┊ ┋ ┌ ┍ ┎ ┏ ┐ ┑ ┒ ┓ └ ┕ ┖ ┗ ┘ ┙ ┚ ┛ ├ ┝ ┞ ┟ ┠ ┡ ┢ ┣ ┤ ┥ ┦ ┧ ┨ ┩ ┪ ┫ ┬ ┭ ┮ ┯ ┰ ┱ ┲ ┳ ┴ ┵ ┶ ┷ ┸ ┹ ┺ ┻ ┼ ┽ ┾ ┿ ╀ ╁ ╂ ╃ ╄ ╅ ╆ ╇ ╈ ╉ ╊ ╋ ╌ ╍ ╎ ╏ ═ ║ ╒ ╓ ╔ ╕ ╖ ╗ ╘ ╙ ╚ ╛ ╜ ╝ ╞ ╟ ╠ ╡ ╢ ╣ ╤ ╥ ╦ ╧ ╨ ╩ ╪ ╫ ╬ ╭ ╮ ╯ ╰ ╱ ╲ ╳ ╴ ╵ ╶ ╷ ╸ ╹ ╺ ╻ ╼ ╽ ╾ ╿
|
||||
Block Elements
|
||||
▀ ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ ▉ ▊ ▋ ▌ ▍ ▎ ▏ ▐ ░ ▒ ▓ ▔ ▕
|
||||
Geometric Shapes
|
||||
■ □ ▢ ▣ ▤ ▥ ▦ ▧ ▨ ▩ ▪ ▫ ▬ ▭ ▮ ▯ ▰ ▱ ▲ △ ▴ ▵ ▶ ▷ ▸ ▹ ► ▻ ▼ ▽ ▾ ▿ ◀ ◁ ◂ ◃ ◄ ◅ ◆ ◇ ◈ ◉ ◊ ○ ◌ ◍ ◎ ● ◐ ◑ ◒ ◓ ◔ ◕ ◖ ◗ ◘ ◙ ◚ ◛ ◜ ◝ ◞ ◟ ◠ ◡ ◢ ◣ ◤ ◥ ◦ ◧ ◨ ◩ ◪ ◫ ◬ ◭ ◮ ◯
|
||||
Miscellaneous Symbols
|
||||
☀ ☁ ☂ ☃ ☄ ★ ☆ ☇ ☈ ☉ ☊ ☋ ☌ ☍ ☎ ☏ ☐ ☑ ☒ ☓ ☚ ☛ ☜ ☝ ☞ ☟ ☠ ☡ ☢ ☣ ☤ ☥ ☦ ☧ ☨ ☩ ☪ ☫ ☬ ☭ ☮ ☯ ☰ ☱ ☲ ☳ ☴ ☵ ☶ ☷ ☸ ☹ ☺ ☻ ☼ ☽ ☾ ☿ ♀ ♁ ♂ ♃ ♄ ♅ ♆ ♇ ♈ ♉ ♊ ♋ ♌ ♍ ♎ ♏ ♐ ♑ ♒ ♓ ♔ ♕ ♖ ♗ ♘ ♙ ♚ ♛ ♜ ♝ ♞ ♟ ♠ ♡ ♢ ♣ ♤ ♥ ♦ ♧ ♨ ♩ ♪ ♫ ♬ ♭ ♮ ♯
|
||||
Dingbats
|
||||
✁ ✂ ✃ ✄ ✆ ✇ ✈ ✉ ✌ ✍ ✎ ✏ ✐ ✑ ✒ ✓ ✔ ✕ ✖ ✗ ✘ ✙ ✚ ✛ ✜ ✝ ✞ ✟ ✠ ✡ ✢ ✣ ✤ ✥ ✦ ✧ ✩ ✪ ✫ ✬ ✭ ✮ ✯ ✰ ✱ ✲ ✳ ✴ ✵ ✶ ✷ ✸ ✹ ✺ ✻ ✼ ✽ ✾ ✿ ❀ ❁ ❂ ❃ ❄ ❅ ❆ ❇ ❈ ❉ ❊ ❋ ❍ ❏ ❐ ❑ ❒ ❖ ❘ ❙ ❚ ❛ ❜ ❝ ❞ ❡ ❢ ❣ ❤ ❥ ❦ ❧ ❶ ❷ ❸ ❹ ❺ ❻ ❼ ❽ ❾ ❿ ➀ ➁ ➂ ➃ ➄ ➅ ➆ ➇ ➈ ➉ ➊ ➋ ➌ ➍ ➎ ➏ ➐ ➑ ➒ ➓ ➔ ➘ ➙ ➚ ➛ ➜ ➝ ...
|
||||
CJK Symbols and Punctuation
|
||||
、 。 〃 〄 々 〆 〇 〈 〉 《 》 「 」 『 』 【 】 〒 〓 〔 〕 〖 〗 〘 〙 〚 〛 〜 〝 〞 〟 〠 〡 〢 〣 〤 〥 〦 〧 〨 〩 〪 〫 〬 〭 〮 〯 〰 〱 〲 〳 〴 〵 〶 〷 〿
|
||||
Hiragana
|
||||
ぁ あ ぃ い ぅ う ぇ え ぉ お か が き ぎ く ぐ け げ こ ご さ ざ し じ す ず せ ぜ そ ぞ た だ ち ぢ っ つ づ て で と ど な に ぬ ね の は ば ぱ ひ び ぴ ふ ぶ ぷ へ べ ぺ ほ ぼ ぽ ま み む め も ゃ や ゅ ゆ ょ よ ら り る れ ろ ゎ わ ゐ ゑ を ん ゔ ゙ ゚ ゛ ゜ ゝ ゞ
|
||||
Katakana
|
||||
ァ ア ィ イ ゥ ウ ェ エ ォ オ カ ガ キ ギ ク グ ケ ゲ コ ゴ サ ザ シ ジ ス ズ セ ゼ ソ ゾ タ ダ チ ヂ ッ ツ ヅ テ デ ト ド ナ ニ ヌ ネ ノ ハ バ パ ヒ ビ ピ フ ブ プ ヘ ベ ペ ホ ボ ポ マ ミ ム メ モ ャ ヤ ュ ユ ョ ヨ ラ リ ル レ ロ ヮ ワ ヰ ヱ ヲ ン ヴ ヵ ヶ ヷ ヸ ヹ ヺ ・ ー ヽ ヾ
|
||||
Bopomofo
|
||||
ㄅ ㄆ ㄇ ㄈ ㄉ ㄊ ㄋ ㄌ ㄍ ㄎ ㄏ ㄐ ㄑ ㄒ ㄓ ㄔ ㄕ ㄖ ㄗ ㄘ ㄙ ㄚ ㄛ ㄜ ㄝ ㄞ ㄟ ㄠ ㄡ ㄢ ㄣ ㄤ ㄥ ㄦ ㄧ ㄨ ㄩ ㄪ ㄫ ㄬ
|
||||
@@ -0,0 +1,9 @@
|
||||
#define STB_SPRINTF_IMPLEMENTATION
|
||||
#include "stb_sprintf.h"
|
||||
#include "basic_string.cpp"
|
||||
#include "basic_string16.cpp"
|
||||
#include "basic_unicode.cpp"
|
||||
#include "basic_alloc.cpp"
|
||||
#include "basic_os.cpp"
|
||||
#include "math.cpp"
|
||||
#include "math_int.cpp"
|
||||
@@ -0,0 +1,10 @@
|
||||
#include "basic_head.h"
|
||||
#include "basic_alloc.h"
|
||||
#include "basic_array.h"
|
||||
#include "basic_string.h"
|
||||
#include "basic_table.h"
|
||||
#include "basic_string16.h"
|
||||
#include "basic_unicode.h"
|
||||
#include "basic_os.h"
|
||||
#include "basic_list.h"
|
||||
#include "basic_math.h"
|
||||
@@ -0,0 +1,437 @@
|
||||
#if defined(USE_ADDRESS_SANITIZER)
|
||||
#include <sanitizer/asan_interface.h>
|
||||
#endif
|
||||
|
||||
#if !defined(ASAN_POISON_MEMORY_REGION)
|
||||
#define MA_ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
|
||||
#define MA_ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
|
||||
#else
|
||||
#define MA_ASAN_POISON_MEMORY_REGION(addr, size) ASAN_POISON_MEMORY_REGION(addr, size)
|
||||
#define MA_ASAN_UNPOISON_MEMORY_REGION(addr, size) ASAN_UNPOISON_MEMORY_REGION(addr, size)
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#if OS_WINDOWS
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
API void *VReserve(size_t size) {
|
||||
void *result = (uint8_t *)VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE);
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool VCommit(void *p, size_t size) {
|
||||
void *result = VirtualAlloc(p, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
return result ? true : false;
|
||||
}
|
||||
|
||||
API bool VRelease(void *p, size_t size) {
|
||||
BOOL result = VirtualFree(p, 0, MEM_RELEASE);
|
||||
return result ? true : false;
|
||||
}
|
||||
|
||||
API bool VDecommit(void *p, size_t size) {
|
||||
BOOL result = VirtualFree(p, size, MEM_DECOMMIT);
|
||||
return result ? true : false;
|
||||
}
|
||||
|
||||
#elif OS_LINUX || OS_MAC
|
||||
#include <sys/mman.h>
|
||||
|
||||
|
||||
API void *VReserve(size_t size) {
|
||||
void *result = mmap(0, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, (off_t)0);
|
||||
return result == (void *)-1 ? 0 : result;
|
||||
}
|
||||
|
||||
API bool VCommit(void *p, size_t size) {
|
||||
int result = mprotect(p, size, PROT_READ | PROT_WRITE);
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
API bool VRelease(void *p, size_t size) {
|
||||
int result = munmap(p, size);
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
API bool VDecommit(void *p, size_t size) {
|
||||
mprotect(p, size, PROT_NONE);
|
||||
madvise(p, size, MADV_DONTNEED);
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
API void *VReserve(size_t size) {
|
||||
InvalidCodepath();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
API bool VCommit(void *p, size_t size) {
|
||||
InvalidCodepath();
|
||||
return false;
|
||||
}
|
||||
|
||||
API bool VRelease(void *p, size_t size) {
|
||||
InvalidCodepath();
|
||||
return false;
|
||||
}
|
||||
|
||||
API bool VDecommit(void *p, size_t size) {
|
||||
InvalidCodepath();
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
API void *SystemAllocatorProc(void *object, int kind, void *p, size_t size) {
|
||||
void *result = NULL;
|
||||
if (kind == AllocatorKind_Allocate) {
|
||||
result = malloc(size);
|
||||
Assert(result);
|
||||
} else if (kind == AllocatorKind_Deallocate) {
|
||||
free(p);
|
||||
} else {
|
||||
InvalidCodepath();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
API Allocator GetSystemAllocator() {
|
||||
Allocator result = {SystemAllocatorProc};
|
||||
return result;
|
||||
}
|
||||
|
||||
API void *_AllocSize_(Allocator alo, size_t size) {
|
||||
void *result = alo.proc(alo.object, AllocatorKind_Allocate, NULL, size);
|
||||
memset(result, 0, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
struct MemoryRecord {
|
||||
size_t size;
|
||||
void *addr;
|
||||
char *file;
|
||||
int line;
|
||||
};
|
||||
thread_local Array<MemoryRecord> MemoryTrackingRecord;
|
||||
|
||||
|
||||
API void *TrackingAllocatorProc(void *object, int kind, void *p, size_t size) {
|
||||
void *result = NULL;
|
||||
|
||||
if (kind == AllocatorKind_Allocate) {
|
||||
result = malloc(size);
|
||||
Add(&MemoryTrackingRecord, {size, result, LocationTraceO.file, LocationTraceO.line});
|
||||
Assert(result);
|
||||
} else if (kind == AllocatorKind_Deallocate) {
|
||||
free(p);
|
||||
|
||||
bool found = false;
|
||||
For(IterateInReverse(&MemoryTrackingRecord)) {
|
||||
if (it.addr == p) {
|
||||
found = true;
|
||||
UnorderedRemove(&MemoryTrackingRecord, it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Assert(found);
|
||||
} else {
|
||||
InvalidCodepath();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
API Allocator GetTrackingAllocator() {
|
||||
Allocator result = {TrackingAllocatorProc};
|
||||
return result;
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
// Virtual Arena
|
||||
|
||||
API void InitArena(VirtualArena *arena, size_t reserve) {
|
||||
reserve = AlignUp(reserve, PAGE_SIZE);
|
||||
arena->align = DEFAULT_ALIGNMENT;
|
||||
arena->data = (uint8_t *)VReserve(reserve);
|
||||
if (arena->data) {
|
||||
arena->reserve = reserve;
|
||||
}
|
||||
}
|
||||
|
||||
API VirtualArena *AllocArena(Allocator allocator, size_t size) {
|
||||
VirtualArena *result = AllocType(allocator, VirtualArena);
|
||||
result->data = (uint8_t *)AllocSize(allocator, size);
|
||||
result->reserve = size;
|
||||
result->commit = size;
|
||||
result->align = DEFAULT_ALIGNMENT;
|
||||
return result;
|
||||
}
|
||||
|
||||
API VirtualArena *AllocArena(size_t reserve) {
|
||||
VirtualArena *result = NULL;
|
||||
|
||||
void *data = VReserve(reserve);
|
||||
if (!data) return result;
|
||||
|
||||
bool success = VCommit(data, PAGE_SIZE);
|
||||
if (!success) {
|
||||
VRelease(data, reserve);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = (VirtualArena *)data;
|
||||
result->data = (uint8_t *)data;
|
||||
result->reserve = reserve;
|
||||
result->commit = PAGE_SIZE;
|
||||
result->len = result->base_len = sizeof(VirtualArena);
|
||||
result->align = DEFAULT_ALIGNMENT;
|
||||
return result;
|
||||
}
|
||||
|
||||
API void *PushSize(VirtualArena *arena, size_t size) {
|
||||
// base_len is used for bootstraping arenas, it denotes the
|
||||
// space occupied by the arena. If len is smaller then base_len then
|
||||
// we start to overwrite the arena itself - pure barbarism.
|
||||
Assert(arena->len >= arena->base_len);
|
||||
|
||||
size_t align_offset = 0;
|
||||
if (arena->align) {
|
||||
align_offset = GetAlignOffset((uintptr_t)arena->data + arena->len, arena->align);
|
||||
}
|
||||
size_t size_with_alignment = size + align_offset;
|
||||
size_t new_len = arena->len + size_with_alignment;
|
||||
if (new_len > arena->commit) {
|
||||
size_t new_len_aligned_to_page_size = AlignUp(new_len, PAGE_SIZE);
|
||||
size_t to_commit = new_len_aligned_to_page_size - arena->commit;
|
||||
size_t to_commit_clamped = ClampTop(to_commit, arena->reserve);
|
||||
if (to_commit_clamped > 0) {
|
||||
bool success = VCommit(arena->data + arena->commit, to_commit_clamped);
|
||||
if (success) {
|
||||
MA_ASAN_POISON_MEMORY_REGION(arena->data + arena->commit, to_commit_clamped);
|
||||
arena->commit += to_commit_clamped;
|
||||
}
|
||||
}
|
||||
if (new_len > arena->commit) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
uint8_t *result = arena->data + arena->len + align_offset;
|
||||
arena->len = new_len;
|
||||
MA_ASAN_UNPOISON_MEMORY_REGION(result, size);
|
||||
return (void *)result;
|
||||
}
|
||||
|
||||
API void Release(VirtualArena *arena) {
|
||||
if (arena == NULL || arena->data == NULL) return;
|
||||
bool zero_memory = (uint8_t *)arena != arena->data;
|
||||
VRelease(arena->data, arena->reserve);
|
||||
if (zero_memory) MemoryZero(arena, sizeof(VirtualArena));
|
||||
}
|
||||
|
||||
API void PopToPos(VirtualArena *arena, size_t pos) {
|
||||
// base_len is used for bootstraping arenas, it denotes the
|
||||
// space occupied by the arena. If len is smaller then base_len then
|
||||
// we start to overwrite the arena itself - pure barbarism.
|
||||
Assert(arena->len >= arena->base_len);
|
||||
|
||||
pos = Clamp(pos, arena->base_len, arena->len);
|
||||
size_t size = arena->len - pos;
|
||||
arena->len = pos;
|
||||
MA_ASAN_POISON_MEMORY_REGION(arena->data + arena->len, size);
|
||||
}
|
||||
|
||||
API void *ArenaAllocatorProc(void *object, int kind, void *p, size_t size) {
|
||||
if (kind == AllocatorKind_Allocate) {
|
||||
return PushSize((VirtualArena *)object, size);
|
||||
} else if (AllocatorKind_Deallocate) {
|
||||
} else {
|
||||
Assert(!"invalid codepath");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
// Block Arena
|
||||
|
||||
void AddBlock(BlockArena *arena, size_t size = MiB(1)) {
|
||||
size_t block_size = MiB(1);
|
||||
if (size > block_size) {
|
||||
block_size = size;
|
||||
}
|
||||
if (arena->allocator.proc == NULL) {
|
||||
arena->allocator = GetSystemAllocator();
|
||||
}
|
||||
BlockArenaNode *new_block = (BlockArenaNode *)AllocSize(arena->allocator, block_size + sizeof(BlockArenaNode));
|
||||
Assert(GetAlignOffset((size_t)new_block->start, DEFAULT_ALIGNMENT) == 0);
|
||||
arena->start = new_block->start;
|
||||
new_block->end = arena->end = new_block->start + block_size;
|
||||
SLL_STACK_ADD(arena->blocks, new_block);
|
||||
}
|
||||
|
||||
API void Init(BlockArena *arena) {
|
||||
AddBlock(arena);
|
||||
}
|
||||
|
||||
API void *PushSize(BlockArena *arena, size_t size) {
|
||||
if (size > (size_t)(arena->end - arena->start)) {
|
||||
AddBlock(arena, size);
|
||||
}
|
||||
U8 *result = arena->start;
|
||||
Assert(GetAlignOffset((size_t)result, DEFAULT_ALIGNMENT) == 0);
|
||||
arena->start = (U8 *)AlignUp((size_t)(arena->start + size), DEFAULT_ALIGNMENT);
|
||||
return result;
|
||||
}
|
||||
|
||||
API void Release(BlockArena *arena) {
|
||||
for (BlockArenaNode *it = arena->blocks, *next = NULL; it; it = next) {
|
||||
next = it->next;
|
||||
Dealloc(arena->allocator, it);
|
||||
}
|
||||
MemoryZero(arena, sizeof(BlockArena));
|
||||
}
|
||||
|
||||
API void Unwind(BlockArena *arena, U8 *pos) {
|
||||
bool contains = false;
|
||||
for (BlockArenaNode *it = arena->blocks, *next = NULL; it; it = next) {
|
||||
next = it->next;
|
||||
if ((pos >= it->start) && (pos <= it->end)) {
|
||||
contains = true;
|
||||
break;
|
||||
} else {
|
||||
arena->blocks = arena->blocks->next;
|
||||
Dealloc(arena->allocator, it);
|
||||
}
|
||||
}
|
||||
Assert(contains || pos == NULL);
|
||||
arena->start = pos;
|
||||
arena->end = arena->blocks ? arena->blocks->end : NULL;
|
||||
}
|
||||
|
||||
API void *BlockArenaAllocatorProc(void *object, int kind, void *p, size_t size) {
|
||||
BlockArena *arena = (BlockArena *)object;
|
||||
if (kind == AllocatorKind_Allocate) {
|
||||
return PushSize(arena, size);
|
||||
} else if (AllocatorKind_Deallocate) {
|
||||
} else {
|
||||
Assert(!"invalid codepath");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if OS_WASM
|
||||
API void InitScratch() {
|
||||
}
|
||||
#else
|
||||
thread_local VirtualArena ScratchArenas[4];
|
||||
API void InitScratch() {
|
||||
for (int i = 0; i < 4; i += 1) {
|
||||
InitArena(&ScratchArenas[i]);
|
||||
}
|
||||
}
|
||||
|
||||
API VirtualArena *GetScratchEx(VirtualArena **conflicts, int conflict_count) {
|
||||
VirtualArena *unoccupied = 0;
|
||||
for (int i = 0; i < Lengthof(ScratchArenas); i += 1) {
|
||||
VirtualArena *from_pool = &ScratchArenas[i];
|
||||
unoccupied = from_pool;
|
||||
for (int conflict_i = 0; conflict_i < conflict_count; conflict_i += 1) {
|
||||
VirtualArena *from_conflict = conflicts[conflict_i];
|
||||
if (from_pool == from_conflict) {
|
||||
unoccupied = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (unoccupied) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Failed to get free scratch memory, this is a fatal error, this shouldnt happen
|
||||
Assert(unoccupied);
|
||||
return unoccupied;
|
||||
}
|
||||
#endif
|
||||
|
||||
void RunArenaTest() {
|
||||
Allocator memory_tracking_allocator = GetTrackingAllocator();
|
||||
{
|
||||
BlockArena arena = {};
|
||||
arena.allocator = memory_tracking_allocator;
|
||||
for (int i = 0; i < 10000; i += 1) {
|
||||
int *vals = (int *)PushSize(&arena, sizeof(int)*i);
|
||||
for (int j = 0; j < i; j += 1) {
|
||||
vals[j] = j;
|
||||
}
|
||||
}
|
||||
Release(&arena);
|
||||
Assert(MemoryTrackingRecord.len == 0);
|
||||
}
|
||||
|
||||
{
|
||||
BlockArena arena = {};
|
||||
arena.allocator = memory_tracking_allocator;
|
||||
int *vals = (int *)PushSize(&arena, sizeof(int) * 32);
|
||||
for (int i = 0; i < 32; i += 1) vals[i] = i;
|
||||
Unwind(&arena, (U8 *)vals);
|
||||
Assert(arena.blocks);
|
||||
Assert(arena.blocks->next == NULL);
|
||||
Assert(arena.start == (U8 *)vals);
|
||||
Assert(arena.blocks[0].start == (U8 *)vals);
|
||||
Unwind(&arena, NULL);
|
||||
Dealloc(arena.allocator, arena.blocks);
|
||||
Assert(MemoryTrackingRecord.len == 0);
|
||||
}
|
||||
|
||||
{
|
||||
BlockArena arena = {};
|
||||
arena.allocator = memory_tracking_allocator;
|
||||
int *vals = (int *)PushSize(&arena, sizeof(int) * 32);
|
||||
for (int i = 0; i < 32; i += 1) vals[i] = i;
|
||||
|
||||
U8 *p = arena.start;
|
||||
U8 *a = (U8 *)PushSize(&arena, KiB(32));
|
||||
PushSize(&arena, KiB(1000));
|
||||
Assert(arena.blocks);
|
||||
Assert(arena.blocks->next);
|
||||
Assert(arena.blocks->next->next == NULL);
|
||||
Unwind(&arena, a);
|
||||
Assert(arena.blocks);
|
||||
Assert(arena.blocks->next == NULL);
|
||||
Assert(arena.start == p);
|
||||
|
||||
Release(&arena);
|
||||
Assert(MemoryTrackingRecord.len == 0);
|
||||
}
|
||||
|
||||
{
|
||||
BlockArena arena = {};
|
||||
arena.allocator = memory_tracking_allocator;
|
||||
PushSize(&arena, KiB(2000));
|
||||
Assert((size_t)(arena.blocks[0].end - arena.blocks[0].start) == KiB(2000));
|
||||
Release(&arena);
|
||||
Assert(MemoryTrackingRecord.len == 0);
|
||||
}
|
||||
|
||||
{
|
||||
BlockArena arena = {};
|
||||
arena.allocator = memory_tracking_allocator;
|
||||
PushSize(&arena, KiB(2000));
|
||||
Unwind(&arena, 0);
|
||||
Assert(MemoryTrackingRecord.len == 0);
|
||||
PushSize(&arena, MiB(1));
|
||||
PushSize(&arena, MiB(3));
|
||||
Assert(MemoryTrackingRecord.len == 2);
|
||||
Unwind(&arena, 0);
|
||||
Assert(MemoryTrackingRecord.len == 0);
|
||||
Release(&arena);
|
||||
Assert(MemoryTrackingRecord.len == 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
#pragma once
|
||||
|
||||
struct LocationTrace {
|
||||
char *file;
|
||||
int line;
|
||||
};
|
||||
|
||||
thread_local LocationTrace LocationTraceO;
|
||||
#define LOCATION_TRACE (LocationTraceO.file = __FILE__, LocationTraceO.line = __LINE__)
|
||||
|
||||
const int AllocatorKind_Allocate = 1;
|
||||
const int AllocatorKind_Deallocate = 2;
|
||||
|
||||
struct Allocator {
|
||||
void *(*proc)(void *object, int kind, void *p, size_t size);
|
||||
void *object;
|
||||
};
|
||||
|
||||
#define AllocType(alo, Type) (Type *)AllocSize(alo, sizeof(Type))
|
||||
#define AllocArray(alo, Type, count) (Type *)AllocSize(alo, sizeof(Type) * (count))
|
||||
#define AllocSize(alo, size) (LOCATION_TRACE, _AllocSize_(alo, size))
|
||||
API void *_AllocSize_(Allocator alo, size_t size);
|
||||
inline void Dealloc(Allocator alo, void *p) { if (p) (alo).proc((alo).object, AllocatorKind_Deallocate, (p), 0); }
|
||||
|
||||
API Allocator GetSystemAllocator();
|
||||
API void TrackingAllocatorCheck();
|
||||
API Allocator GetTrackingAllocator();
|
||||
|
||||
#define MemoryZero(x, size) memset(x, 0, size)
|
||||
#define MemoryZeroStruct(x) memset(x, 0, sizeof(*x))
|
||||
#define MemoryCopy(dst, src, size) memcpy(dst, src, size)
|
||||
#define MemoryMove(dst, src, size) memmove(dst, src, size)
|
||||
|
||||
///////////////////
|
||||
// Block Arena
|
||||
///////////////////
|
||||
API void *BlockArenaAllocatorProc(void *object, int kind, void *p, size_t size);
|
||||
|
||||
struct BlockArenaNode {
|
||||
BlockArenaNode *next;
|
||||
U8 *end;
|
||||
U8 start[0];
|
||||
};
|
||||
|
||||
struct BlockArena {
|
||||
U8 *start;
|
||||
U8 *end;
|
||||
BlockArenaNode *blocks;
|
||||
Allocator allocator;
|
||||
int refs; // :CoroutineLeakCheck
|
||||
|
||||
operator Allocator() { return {BlockArenaAllocatorProc, this}; }
|
||||
};
|
||||
|
||||
API void *PushSize(BlockArena *arena, size_t size);
|
||||
API void Release(BlockArena *arena);
|
||||
API void Unwind(BlockArena *arena, U8 *pos);
|
||||
|
||||
///////////////////
|
||||
// Virtual Arena
|
||||
///////////////////
|
||||
|
||||
struct VirtualArena {
|
||||
uint8_t *data;
|
||||
size_t len;
|
||||
size_t base_len; // to prevent self deleting the arena
|
||||
size_t reserve;
|
||||
size_t commit;
|
||||
size_t align;
|
||||
int refs; // :CoroutineLeakCheck
|
||||
|
||||
operator Allocator() {
|
||||
void *ArenaAllocatorProc(void *object, int kind, void *p, size_t size);
|
||||
return {ArenaAllocatorProc, this};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
inline void SetLen(VirtualArena *arena, size_t len) { arena->len = Clamp(len, arena->base_len, arena->len); }
|
||||
inline void Pop(VirtualArena *arena, size_t size) { SetLen(arena, arena->len - size); }
|
||||
inline void Clear(VirtualArena *arena) { SetLen(arena, 0); }
|
||||
|
||||
API void *VReserve(size_t size);
|
||||
API bool VCommit(void *p, size_t size);
|
||||
API bool VRelease(void *p, size_t size);
|
||||
API bool VDecommit(void *p, size_t size);
|
||||
|
||||
API void InitArena(VirtualArena *arena, size_t reserve = MiB(256));
|
||||
API VirtualArena *AllocArena(size_t reserve = MiB(256));
|
||||
API VirtualArena *AllocArena(Allocator allocator, size_t size);
|
||||
API void *PushSize(VirtualArena *arena, size_t size);
|
||||
API void Release(VirtualArena *arena);
|
||||
|
||||
///////////////////////////////
|
||||
// Scratch
|
||||
#if OS_WASM
|
||||
struct Scratch {
|
||||
BlockArena arena = {};
|
||||
Scratch() {}
|
||||
Scratch(BlockArena *conflict) {}
|
||||
Scratch(BlockArena *c1, BlockArena *c2) {}
|
||||
Scratch(Allocator conflict) {}
|
||||
Scratch(Allocator c1, Allocator c2) {}
|
||||
~Scratch() { Release(&arena); }
|
||||
operator BlockArena *() { return &arena; }
|
||||
operator Allocator() { return arena; }
|
||||
private: // @Note: Disable copy constructors, cause its error prone
|
||||
Scratch(Scratch &arena);
|
||||
Scratch(Scratch &arena, Scratch &a2);
|
||||
};
|
||||
#else
|
||||
extern thread_local VirtualArena ScratchArenas[4];
|
||||
API VirtualArena *GetScratchEx(VirtualArena **conflicts, int conflict_count);
|
||||
struct Scratch {
|
||||
VirtualArena *arena;
|
||||
U64 p;
|
||||
Scratch() {arena = &ScratchArenas[0]; p = arena->len; arena->refs += 1;}
|
||||
Scratch(VirtualArena *conflict) {
|
||||
VirtualArena *conf[] = {conflict};
|
||||
arena = GetScratchEx(conf, Lengthof(conf));
|
||||
p = arena->len;
|
||||
arena->refs += 1;
|
||||
}
|
||||
Scratch(VirtualArena *c1, VirtualArena *c2) {
|
||||
VirtualArena *conf[] = {c1, c2};
|
||||
arena = GetScratchEx(conf, Lengthof(conf));
|
||||
p = arena->len;
|
||||
arena->refs += 1;
|
||||
}
|
||||
Scratch(Allocator conflict) {
|
||||
VirtualArena *conf[] = {(VirtualArena *)conflict.object};
|
||||
arena = GetScratchEx(conf, Lengthof(conf));
|
||||
p = arena->len;
|
||||
arena->refs += 1;
|
||||
}
|
||||
Scratch(Allocator c1, Allocator c2) {
|
||||
VirtualArena *conf[] = {(VirtualArena *)c1.object, (VirtualArena *)c2.object};
|
||||
arena = GetScratchEx(conf, Lengthof(conf));
|
||||
p = arena->len;
|
||||
arena->refs += 1;
|
||||
}
|
||||
~Scratch() { SetLen(arena, p); arena->refs -= 1; }
|
||||
operator VirtualArena *() { return arena; }
|
||||
operator Allocator() { return *arena; }
|
||||
|
||||
private: // @Note: Disable copy constructors, cause its error prone
|
||||
Scratch(Scratch &arena);
|
||||
Scratch(Scratch &arena, Scratch &a2);
|
||||
};
|
||||
#endif
|
||||
|
||||
const int PAGE_SIZE = 4096;
|
||||
const int DEFAULT_ALIGNMENT = sizeof(void *);
|
||||
@@ -0,0 +1,498 @@
|
||||
#pragma once
|
||||
/*
|
||||
// Iterating and removing elements
|
||||
for (int i = 0; i < array.len; i += 1) {
|
||||
auto &it = array[i];
|
||||
bool remove_item = false;
|
||||
defer {
|
||||
if (remove_item) {
|
||||
array.ordered_remove(it);
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Simple delete
|
||||
IterRemove(arr) {
|
||||
IterRemovePrepare(arr);
|
||||
|
||||
remove_item = true;
|
||||
}
|
||||
|
||||
// Deleting backwards
|
||||
For(arr.reverse_iter()) {
|
||||
defer{ arr.unordered_remove(it); };
|
||||
}
|
||||
*/
|
||||
#define IterRemove(a) for (int i = 0; i < (a).len; i += 1)
|
||||
#define IterRemovePrepare(a) \
|
||||
auto &it = (a)[i]; \
|
||||
bool remove_item = false; \
|
||||
defer { \
|
||||
if (remove_item) { \
|
||||
Remove(&(a), it); \
|
||||
i -= 1; \
|
||||
} \
|
||||
}
|
||||
#define ForItem(it, array) for (auto &it : (array))
|
||||
#define For(array) ForItem(it, array)
|
||||
|
||||
template <class T>
|
||||
struct Slice {
|
||||
T *data;
|
||||
int64_t len;
|
||||
|
||||
Slice() = default;
|
||||
Slice(T *s, int64_t l) : data(s), len(l) {}
|
||||
|
||||
T &operator[](int64_t index) {
|
||||
Assert(index < len);
|
||||
return data[index];
|
||||
}
|
||||
T *begin() { return data; }
|
||||
T *end() { return data + len; }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
Slice<T> Copy(Allocator alo, Slice<T> array) {
|
||||
Slice<T> result = {};
|
||||
result.data = AllocArray(alo, T, array.len);
|
||||
memcpy(result.data, array.data, sizeof(T) * array.len);
|
||||
result.len = array.len;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T Pop(Slice<T> *arr) {
|
||||
Assert(arr->len > 0);
|
||||
return arr->data[--arr->len];
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool Contains(Slice<T> &arr, T item) {
|
||||
For(arr) if (it == item) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
int64_t GetIndex(Slice<T> &arr, const T &item) {
|
||||
ptrdiff_t index = (ptrdiff_t)(&item - arr.data);
|
||||
Assert(index >= 0 && index < arr.len);
|
||||
return (int64_t)index;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T Get(Slice<T> &arr, int64_t i, T default_value = {}) {
|
||||
T result = default_value;
|
||||
if (i >= 0 && i < arr.len) result = arr[i];
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool IsLast(Slice<T> &arr, T &item) {
|
||||
bool result = arr.last() == &item;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool IsFirst(Slice<T> &arr, T &item) {
|
||||
bool result = arr.first() == &item;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T *GetFirst(Slice<T> &arr) {
|
||||
Assert(arr.len > 0);
|
||||
return arr.data;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T *GetLast(Slice<T> &arr) {
|
||||
Assert(arr.len > 0);
|
||||
return arr.data + arr.len - 1;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Slice<T> Chop(Slice<T> &arr, int64_t len) {
|
||||
len = ClampTop(len, arr.len);
|
||||
Slice<T> result = {arr.data, arr.len - len};
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Slice<T> Skip(Slice<T> &arr, int64_t len) {
|
||||
len = ClampTop(len, arr.len);
|
||||
Slice<T> result = {arr.data + len, arr.len - len};
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Slice<T> GetPostfix(Slice<T> &arr, int64_t len) {
|
||||
len = ClampTop(len, arr.len);
|
||||
int64_t remain_len = arr.len - len;
|
||||
Slice<T> result = {arr.data + remain_len, len};
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Slice<T> GetPrefix(Slice<T> &arr, int64_t len) {
|
||||
len = ClampTop(len, arr.len);
|
||||
Slice<T> result = {arr.data, len};
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Slice<T> GetSlice(Slice<T> &arr, int64_t first_index = 0, int64_t one_past_last_index = SLICE_LAST) {
|
||||
// Negative indexes work in python style, they return you the index counting from end of list
|
||||
if (one_past_last_index == SLICE_LAST) one_past_last_index = arr.len;
|
||||
if (one_past_last_index < 0) one_past_last_index = arr.len + one_past_last_index;
|
||||
|
||||
if (first_index == SLICE_LAST) first_index = arr.len;
|
||||
if (first_index < 0) first_index = arr.len + first_index;
|
||||
|
||||
Slice<T> result = {arr.data, arr.len};
|
||||
if (arr.len > 0) {
|
||||
if (one_past_last_index > first_index) {
|
||||
first_index = ClampTop(first_index, arr.len - 1);
|
||||
one_past_last_index = ClampTop(one_past_last_index, arr.len);
|
||||
result.data += first_index;
|
||||
result.len = one_past_last_index - first_index;
|
||||
} else {
|
||||
result.len = 0;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Make arrays resize on every item
|
||||
#define ARRAY_DEBUG 0
|
||||
#if ARRAY_DEBUG
|
||||
#define ARRAY_IF_DEBUG_ELSE(IF, ELSE) IF
|
||||
#else
|
||||
#define ARRAY_IF_DEBUG_ELSE(IF, ELSE) ELSE
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
struct Array {
|
||||
Allocator allocator;
|
||||
int64_t cap;
|
||||
union {
|
||||
Slice<T> slice;
|
||||
struct {
|
||||
T *data;
|
||||
int64_t len;
|
||||
};
|
||||
};
|
||||
|
||||
T &operator[](int64_t index) {
|
||||
Assert(index >= 0 && index < len);
|
||||
return data[index];
|
||||
}
|
||||
T *begin() { return data; }
|
||||
T *end() { return data + len; }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
T *GetFirst(Array<T> &arr) {
|
||||
Assert(arr.len > 0);
|
||||
return arr.data;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T *GetLast(Array<T> &arr) {
|
||||
Assert(arr.len > 0);
|
||||
return arr.data + arr.len - 1;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Reserve(Array<T> *arr, int64_t size) {
|
||||
if (size > arr->cap) {
|
||||
if (!arr->allocator.proc) arr->allocator = GetSystemAllocator();
|
||||
|
||||
T *new_data = AllocArray(arr->allocator, T, size);
|
||||
Assert(new_data);
|
||||
memcpy(new_data, arr->data, arr->len * sizeof(T));
|
||||
Dealloc(arr->allocator, arr->data);
|
||||
|
||||
arr->data = new_data;
|
||||
arr->cap = size;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void TryGrowing(Array<T> *arr) {
|
||||
if (arr->len + 1 > arr->cap) {
|
||||
int64_t initial_size = (int64_t)ARRAY_IF_DEBUG_ELSE(1, 16);
|
||||
int64_t new_size = ClampBottom(initial_size, arr->cap ARRAY_IF_DEBUG_ELSE(+1, *2));
|
||||
Reserve(arr, new_size);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void TryGrowing(Array<T> *arr, int64_t item_count) {
|
||||
if (arr->len + item_count > arr->cap) {
|
||||
int64_t initial_size = (int64_t)ARRAY_IF_DEBUG_ELSE(1, 16);
|
||||
int64_t new_size = ClampBottom(initial_size, (arr->cap + item_count) ARRAY_IF_DEBUG_ELSE(+1, *2));
|
||||
Reserve(arr, new_size);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Add(Array<T> *arr, T item) {
|
||||
TryGrowing(arr);
|
||||
arr->data[arr->len++] = item;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Add(Array<T> *arr, Array<T> &another) {
|
||||
For(another) Add(arr, it);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Add(Array<T> *arr, T *items, int64_t item_count) {
|
||||
for (int64_t i = 0; i < item_count; i += 1) Add(arr, items[i]);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void BoundedAdd(Array<T> *arr, T item) {
|
||||
if (arr->len + 1 <= arr->cap) arr->data[arr->len++] = item;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void BoundedAddError(Array<T> *arr, T item) {
|
||||
Assert(arr->len + 1 <= arr->cap);
|
||||
if (arr->len + 1 <= arr->cap) arr->data[arr->len++] = item;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Insert(Array<T> *arr, T item, int64_t index) {
|
||||
if (index == arr->len) {
|
||||
Add(arr, item);
|
||||
return;
|
||||
}
|
||||
|
||||
Assert(index < arr->len);
|
||||
Assert(index >= 0);
|
||||
TryGrowing(arr);
|
||||
int64_t right_len = arr->len - index;
|
||||
memmove(arr->data + index + 1, arr->data + index, sizeof(T) * right_len);
|
||||
arr->data[index] = item;
|
||||
arr->len += 1;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Array<T> Copy(Allocator alo, Array<T> array) {
|
||||
Array<T> result = {alo};
|
||||
Reserve(&result, array.cap);
|
||||
memcpy(result.data, array.data, sizeof(T) * array.len);
|
||||
result.len = array.len;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Array<T> TightCopy(Allocator alo, Array<T> array) {
|
||||
Array<T> result = {alo};
|
||||
Reserve(&result, array.len);
|
||||
memcpy(result.data, array.data, sizeof(T) * array.len);
|
||||
result.len = array.len;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T Pop(Array<T> *arr) {
|
||||
Assert(arr->len > 0);
|
||||
return arr->data[--arr->len];
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool Contains(Array<T> &arr, T item) {
|
||||
For(arr) if (it == item) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
int64_t GetIndex(Array<T> &arr, const T &item) {
|
||||
ptrdiff_t index = (ptrdiff_t)(&item - arr.data);
|
||||
Assert(index >= 0 && index < arr.len);
|
||||
return (int64_t)index;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void RemoveByIndex(Array<T> *arr, int64_t index) {
|
||||
Assert(index >= 0 && index < arr->len);
|
||||
int64_t right_len = arr->len - index - 1;
|
||||
memmove(arr->data + index, arr->data + index + 1, right_len * sizeof(T));
|
||||
arr->len -= 1;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void RemoveManyByIndex(Array<T> *arr, int64_t index, int64_t count) {
|
||||
if (count == 0) return;
|
||||
Assert(index >= 0 && index < arr->len);
|
||||
Assert((index + count) > 0 && (index + count) <= arr->len);
|
||||
int64_t right_len = arr->len - index - count;
|
||||
memmove(arr->data + index, arr->data + index + count, right_len * sizeof(T));
|
||||
arr->len -= count;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void RemoveMany(Array<T> *arr, T &item, int64_t count) {
|
||||
Assert(arr->len > 0);
|
||||
Assert(&item >= arr->begin() && &item < arr->end());
|
||||
int64_t index = GetIndex(*arr, item);
|
||||
RemoveManyByIndex(arr, index, count);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Remove(Array<T> *arr, T &item) {
|
||||
Assert(arr->len > 0);
|
||||
Assert(&item >= arr->begin() && &item < arr->end());
|
||||
int64_t index = GetIndex(*arr, item);
|
||||
RemoveByIndex(arr, index);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void UnorderedRemove(Array<T> *arr, T &item) {
|
||||
Assert(arr->len > 0);
|
||||
Assert((&item >= arr->begin()) && (&item < arr->end()));
|
||||
item = arr->data[--arr->len];
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void UnorderedRemoveByIndex(Array<T> *arr, int64_t index) {
|
||||
Assert(arr->len > 0);
|
||||
Assert(index >= 0 && index < arr->len);
|
||||
arr->data[index] = arr->data[--arr->len];
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void InsertArray(Array<T> *arr, T *items, int64_t count, int64_t index) {
|
||||
if (index == arr->len) {
|
||||
Add(arr, items, count);
|
||||
return;
|
||||
}
|
||||
Assert(index < arr->len);
|
||||
|
||||
TryGrowing(arr, count);
|
||||
T *gap_begin = arr->data + index;
|
||||
T *gap_end = gap_begin + count;
|
||||
int64_t item_count = arr->len - index;
|
||||
memmove(gap_end, gap_begin, item_count * sizeof(T));
|
||||
for (int64_t i = 0; i < count; i += 1) arr->data[index + i] = items[i];
|
||||
arr->len += count;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T Get(Array<T> &arr, int64_t i, T default_value = {}) {
|
||||
T result = default_value;
|
||||
if (i >= 0 && i < arr.len) result = arr[i];
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Dealloc(Array<T> *arr) {
|
||||
if (arr->data) {
|
||||
Dealloc(arr->allocator, arr->data);
|
||||
arr->data = NULL;
|
||||
}
|
||||
arr->len = arr->cap = 0;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool IsLast(Array<T> &arr, T &item) {
|
||||
bool result = GetLast(arr) == &item;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool IsFirst(Array<T> &arr, T &item) {
|
||||
bool result = GetFirst(arr) == &item;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Slice<T> Chop(Array<T> &arr, int64_t len) {
|
||||
len = ClampTop(len, arr.len);
|
||||
Slice<T> result = {arr.data, arr.len - len};
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Slice<T> Skip(Array<T> &arr, int64_t len) {
|
||||
len = ClampTop(len, arr.len);
|
||||
Slice<T> result = {arr.data + len, arr.len - len};
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Slice<T> GetPostfix(Array<T> &arr, int64_t len) {
|
||||
len = ClampTop(len, arr.len);
|
||||
int64_t remain_len = arr.len - len;
|
||||
Slice<T> result = {arr.data + remain_len, len};
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Slice<T> GetPrefix(Array<T> &arr, int64_t len) {
|
||||
len = ClampTop(len, arr.len);
|
||||
Slice<T> result = {arr.data, len};
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Slice<T> GetSlice(Array<T> &arr, int64_t first_index = 0, int64_t one_past_last_index = SLICE_LAST) {
|
||||
// Negative indexes work in python style, they return you the index counting from end of list
|
||||
if (one_past_last_index == SLICE_LAST) one_past_last_index = arr.len;
|
||||
if (one_past_last_index < 0) one_past_last_index = arr.len + one_past_last_index;
|
||||
|
||||
if (first_index == SLICE_LAST) first_index = arr.len;
|
||||
if (first_index < 0) first_index = arr.len + first_index;
|
||||
|
||||
Slice<T> result = {arr.data, arr.len};
|
||||
if (arr.len > 0) {
|
||||
if (one_past_last_index > first_index) {
|
||||
first_index = ClampTop(first_index, arr.len - 1);
|
||||
one_past_last_index = ClampTop(one_past_last_index, arr.len);
|
||||
result.data += first_index;
|
||||
result.len = one_past_last_index - first_index;
|
||||
} else {
|
||||
result.len = 0;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
struct ReverseIter {
|
||||
T *data;
|
||||
Slice<T> *arr;
|
||||
|
||||
ReverseIter operator++(int) {
|
||||
ReverseIter ret = *this;
|
||||
data -= 1;
|
||||
return ret;
|
||||
}
|
||||
ReverseIter &operator++() {
|
||||
data -= 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
T &operator*() { return data[0]; }
|
||||
T *operator->() { return data; }
|
||||
|
||||
friend bool operator==(const ReverseIter &a, const ReverseIter &b) { return a.data == b.data; };
|
||||
friend bool operator!=(const ReverseIter &a, const ReverseIter &b) { return a.data != b.data; };
|
||||
|
||||
ReverseIter begin() { return ReverseIter{arr->end() - 1, arr}; }
|
||||
ReverseIter end() { return ReverseIter{arr->begin() - 1, arr}; }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
ReverseIter<T> IterateInReverse(Array<T> *arr) {
|
||||
return {arr->end() - 1, &arr->slice};
|
||||
}
|
||||
template <class T>
|
||||
ReverseIter<T> IterateInReverse(Slice<T> *slice) {
|
||||
return {slice->end() - 1, slice};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
#pragma once
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#define OS_POSIX 1
|
||||
#define OS_MAC 1
|
||||
#elif defined(_WIN32)
|
||||
#define OS_WINDOWS 1
|
||||
#elif defined(__linux__)
|
||||
#define OS_POSIX 1
|
||||
#define OS_LINUX 1
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
#define OS_WASM 1
|
||||
#else
|
||||
#error Unsupported platform
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
#define COMPILER_CLANG 1
|
||||
#elif defined(__GNUC__) || defined(__GNUG__)
|
||||
#define COMPILER_GCC 1
|
||||
#elif defined(_MSC_VER)
|
||||
#define COMPILER_MSVC 1
|
||||
#else
|
||||
#error Unsupported compiler
|
||||
#endif
|
||||
|
||||
#ifndef OS_WASM
|
||||
#define OS_WASM 0
|
||||
#endif
|
||||
|
||||
#ifndef OS_MAC
|
||||
#define OS_MAC 0
|
||||
#endif
|
||||
|
||||
#ifndef OS_WINDOWS
|
||||
#define OS_WINDOWS 0
|
||||
#endif
|
||||
|
||||
#ifndef OS_LINUX
|
||||
#define OS_LINUX 0
|
||||
#endif
|
||||
|
||||
#ifndef OS_POSIX
|
||||
#define OS_POSIX 0
|
||||
#endif
|
||||
|
||||
#ifndef COMPILER_MSVC
|
||||
#define COMPILER_MSVC 0
|
||||
#endif
|
||||
|
||||
#ifndef COMPILER_CLANG
|
||||
#define COMPILER_CLANG 0
|
||||
#endif
|
||||
|
||||
#ifndef COMPILER_GCC
|
||||
#define COMPILER_GCC 0
|
||||
#endif
|
||||
|
||||
#ifndef DEBUG_BUILD
|
||||
#define DEBUG_BUILD 1
|
||||
#endif
|
||||
|
||||
#if DEBUG_BUILD
|
||||
#define IF_DEBUG(x) x
|
||||
#else
|
||||
#define IF_DEBUG(x)
|
||||
#endif
|
||||
|
||||
#if OS_WINDOWS
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#define BREAK() if (IsDebuggerPresent()) {__debugbreak();}
|
||||
#elif OS_LINUX
|
||||
#define BREAK() raise(SIGTRAP)
|
||||
#elif OS_WASM
|
||||
#include <emscripten.h>
|
||||
EM_JS(void, JS_Breakpoint, (), {
|
||||
debugger;
|
||||
})
|
||||
#define BREAK() JS_Breakpoint()
|
||||
#endif
|
||||
|
||||
#define API
|
||||
#define Assert(x) \
|
||||
if (!(x)) { \
|
||||
BREAK(); \
|
||||
}
|
||||
#define InvalidCodepath() Assert(!"invalid codepath")
|
||||
#define ElseInvalidCodepath() else {InvalidCodepath();}
|
||||
|
||||
#define KiB(x) ((x##ull) * 1024ull)
|
||||
#define MiB(x) (KiB(x) * 1024ull)
|
||||
#define GiB(x) (MiB(x) * 1024ull)
|
||||
#define TiB(x) (GiB(x) * 1024ull)
|
||||
#define Lengthof(x) ((int64_t)((sizeof(x) / sizeof((x)[0]))))
|
||||
#define SLICE_LAST INT64_MAX
|
||||
|
||||
using U8 = uint8_t;
|
||||
using U16 = uint16_t;
|
||||
using U32 = uint32_t;
|
||||
using U64 = uint64_t;
|
||||
using S8 = int8_t;
|
||||
using S16 = int16_t;
|
||||
using S32 = int32_t;
|
||||
using S64 = int64_t;
|
||||
using Int = S64;
|
||||
using UInt = U64;
|
||||
using Float = double;
|
||||
|
||||
template <class T>
|
||||
T Min(T a, T b) {
|
||||
if (a > b) return b;
|
||||
return a;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T ClampTop(T a, T top) {
|
||||
return Min(a, top);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T Max(T a, T b) {
|
||||
if (a > b) return a;
|
||||
return b;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T ClampBottom(T bottom, T b) {
|
||||
return Max(bottom, b);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T Clamp(T value, T min, T max) {
|
||||
if (value < min) return min;
|
||||
if (value > max) return max;
|
||||
return value;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Swap(T *a, T *b) {
|
||||
T temp = *a;
|
||||
*a = *b;
|
||||
*b = temp;
|
||||
}
|
||||
|
||||
inline bool IsPowerOf2(size_t x) {
|
||||
size_t result = (((x) & ((x)-1)) == 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline size_t WrapAroundPowerOf2(size_t x, size_t pow2) {
|
||||
Assert(IsPowerOf2(pow2));
|
||||
size_t result = (((x) & ((pow2)-1llu)));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline uint64_t HashBytes(void *data, int64_t size) {
|
||||
uint8_t *data8 = (uint8_t *)data;
|
||||
uint64_t hash = (uint64_t)14695981039346656037ULL;
|
||||
for (int64_t i = 0; i < size; i++) {
|
||||
hash = hash ^ (uint64_t)(data8[i]);
|
||||
hash = hash * (uint64_t)1099511628211ULL;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
inline size_t GetAlignOffset(size_t size, size_t align) {
|
||||
Assert(IsPowerOf2(align));
|
||||
size_t mask = align - 1;
|
||||
size_t val = size & mask;
|
||||
if (val) {
|
||||
val = align - val;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
inline size_t AlignUp(size_t size, size_t align) {
|
||||
size_t result = size + GetAlignOffset(size, align);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline size_t AlignDown(size_t size, size_t align) {
|
||||
size += 1; // Make sure when align is 8 doesn't get rounded down to 0
|
||||
size_t result = size - (align - GetAlignOffset(size, align));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct DEFER_ExitScope {
|
||||
T lambda;
|
||||
DEFER_ExitScope(T lambda) : lambda(lambda) {}
|
||||
~DEFER_ExitScope() { lambda(); }
|
||||
DEFER_ExitScope(const DEFER_ExitScope &i) : lambda(i.lambda){};
|
||||
|
||||
private:
|
||||
DEFER_ExitScope &operator=(const DEFER_ExitScope &);
|
||||
};
|
||||
|
||||
class DEFER_ExitScopeHelp {
|
||||
public:
|
||||
template <typename T>
|
||||
DEFER_ExitScope<T> operator+(T t) { return t; }
|
||||
};
|
||||
|
||||
#define DEFER_CONCAT_INTERNAL(x, y) x##y
|
||||
#define DEFER_CONCAT(x, y) DEFER_CONCAT_INTERNAL(x, y)
|
||||
#define defer const auto DEFER_CONCAT(defer__, __LINE__) = DEFER_ExitScopeHelp() + [&]()
|
||||
|
||||
struct RandomSeed {
|
||||
uint64_t a;
|
||||
};
|
||||
|
||||
inline uint64_t GetRandomU64(RandomSeed *state) {
|
||||
uint64_t x = state->a;
|
||||
x ^= x << 13;
|
||||
x ^= x >> 7;
|
||||
x ^= x << 17;
|
||||
return state->a = x;
|
||||
}
|
||||
|
||||
#define STRINGIFY_(x) x
|
||||
#define STRINGIFY(x) STRINGIFY_(x)
|
||||
#define CONCAT_(a, b) a ## b
|
||||
#define CONCAT(a, b) CONCAT_(a, b)
|
||||
@@ -0,0 +1,117 @@
|
||||
#pragma once
|
||||
#define SLL_QUEUE_ADD_MOD(f, l, n, next) \
|
||||
do { \
|
||||
(n)->next = 0; \
|
||||
if ((f) == 0) { \
|
||||
(f) = (l) = (n); \
|
||||
} else { \
|
||||
(l) = (l)->next = (n); \
|
||||
} \
|
||||
} while (0)
|
||||
#define SLL_QUEUE_ADD(f, l, n) SLL_QUEUE_ADD_MOD(f, l, n, next)
|
||||
|
||||
#define SLL_QUEUE_POP_FIRST_MOD(f, l, next) \
|
||||
do { \
|
||||
if ((f) == (l)) { \
|
||||
(f) = (l) = 0; \
|
||||
} else { \
|
||||
(f) = (f)->next; \
|
||||
} \
|
||||
} while (0)
|
||||
#define SLL_QUEUE_POP_FIRST(f, l) SLL_QUEUE_POP_FIRST_MOD(f, l, next)
|
||||
|
||||
#define SLL_STACK_ADD_MOD(stack_base, new_stack_base, next) \
|
||||
do { \
|
||||
(new_stack_base)->next = (stack_base); \
|
||||
(stack_base) = (new_stack_base); \
|
||||
} while (0)
|
||||
#define SLL_STACK_ADD(stack_base, new_stack_base) \
|
||||
SLL_STACK_ADD_MOD(stack_base, new_stack_base, next)
|
||||
|
||||
#define SLL_STACK_POP_AND_STORE(stack_base, out_node) \
|
||||
do { \
|
||||
if (stack_base) { \
|
||||
(out_node) = (stack_base); \
|
||||
(stack_base) = (stack_base)->next; \
|
||||
(out_node)->next = 0; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DLL_QUEUE_ADD_MOD(f, l, node, next, prev) \
|
||||
do { \
|
||||
if ((f) == 0) { \
|
||||
(f) = (l) = (node); \
|
||||
(node)->prev = 0; \
|
||||
(node)->next = 0; \
|
||||
} else { \
|
||||
(l)->next = (node); \
|
||||
(node)->prev = (l); \
|
||||
(node)->next = 0; \
|
||||
(l) = (node); \
|
||||
} \
|
||||
} while (0)
|
||||
#define DLL_QUEUE_ADD(f, l, node) DLL_QUEUE_ADD_MOD(f, l, node, next, prev)
|
||||
#define DLL_QUEUE_ADD_FRONT(f, l, node) DLL_QUEUE_ADD_MOD(l, f, node, prev, next)
|
||||
#define DLL_QUEUE_REMOVE_MOD(first, last, node, next, prev) \
|
||||
do { \
|
||||
if ((first) == (last)) { \
|
||||
(first) = (last) = 0; \
|
||||
} else if ((last) == (node)) { \
|
||||
(last) = (last)->prev; \
|
||||
(last)->next = 0; \
|
||||
} else if ((first) == (node)) { \
|
||||
(first) = (first)->next; \
|
||||
(first)->prev = 0; \
|
||||
} else { \
|
||||
(node)->prev->next = (node)->next; \
|
||||
(node)->next->prev = (node)->prev; \
|
||||
} \
|
||||
if (node) { \
|
||||
(node)->prev = 0; \
|
||||
(node)->next = 0; \
|
||||
} \
|
||||
} while (0)
|
||||
#define DLL_QUEUE_REMOVE(first, last, node) DLL_QUEUE_REMOVE_MOD(first, last, node, next, prev)
|
||||
|
||||
#define DLL_STACK_ADD_MOD(first, node, next, prev) \
|
||||
do { \
|
||||
(node)->next = (first); \
|
||||
if ((first)) \
|
||||
(first)->prev = (node); \
|
||||
(first) = (node); \
|
||||
(node)->prev = 0; \
|
||||
} while (0)
|
||||
#define DLL_STACK_ADD(first, node) DLL_STACK_ADD_MOD(first, node, next, prev)
|
||||
#define DLL_STACK_REMOVE_MOD(first, node, next, prev) \
|
||||
do { \
|
||||
if ((node) == (first)) { \
|
||||
(first) = (first)->next; \
|
||||
if ((first)) \
|
||||
(first)->prev = 0; \
|
||||
} else { \
|
||||
(node)->prev->next = (node)->next; \
|
||||
if ((node)->next) \
|
||||
(node)->next->prev = (node)->prev; \
|
||||
} \
|
||||
if (node) { \
|
||||
(node)->prev = 0; \
|
||||
(node)->next = 0; \
|
||||
} \
|
||||
} while (0)
|
||||
#define DLL_STACK_REMOVE(first, node) DLL_STACK_REMOVE_MOD(first, node, next, prev)
|
||||
|
||||
#define DLL_INSERT_NEXT_MOD(base, new, next, prev) \
|
||||
do { \
|
||||
if ((base) == 0) { \
|
||||
(base) = (new); \
|
||||
(new)->next = 0; \
|
||||
(new)->prev = 0; \
|
||||
} else { \
|
||||
(new)->next = (base)->next; \
|
||||
(base)->next = (new); \
|
||||
(new)->prev = (base); \
|
||||
if ((new)->next) (new)->next->prev = (new); \
|
||||
} \
|
||||
} while (0)
|
||||
#define DLL_INSERT_NEXT(base, new) DLL_INSERT_NEXT_MOD(base, new, next, prev)
|
||||
#define DLL_INSERT_PREV(base, new) DLL_INSERT_NEXT_MOD(base, new, next, prev)
|
||||
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
#include <math.h>
|
||||
|
||||
struct Vec2 {
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
union Vec4 {
|
||||
struct {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float w;
|
||||
};
|
||||
struct {
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
float a;
|
||||
};
|
||||
};
|
||||
|
||||
union Vec3 {
|
||||
struct {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
};
|
||||
struct {
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
};
|
||||
};
|
||||
|
||||
struct Rect2 {
|
||||
Vec2 min;
|
||||
Vec2 max;
|
||||
};
|
||||
|
||||
struct Vec2I {
|
||||
Int x;
|
||||
Int y;
|
||||
};
|
||||
|
||||
struct Rect2I {
|
||||
Vec2I min;
|
||||
Vec2I max;
|
||||
};
|
||||
|
||||
union Color {
|
||||
struct {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
uint8_t a;
|
||||
};
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
#define PI32 3.14159265359f
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
struct FileIter {
|
||||
bool is_valid;
|
||||
bool is_directory;
|
||||
String absolute_path;
|
||||
String relative_path;
|
||||
String filename;
|
||||
|
||||
String path;
|
||||
Allocator allocator;
|
||||
union {
|
||||
struct Win32_FileIter *w32;
|
||||
void *dir;
|
||||
};
|
||||
};
|
||||
|
||||
String ReadFile(Allocator arena, String path);
|
||||
bool WriteFile(String path, String data);
|
||||
String GetAbsolutePath(Allocator arena, String relative);
|
||||
bool IsValid(const FileIter &it);
|
||||
void Advance(FileIter *it);
|
||||
FileIter IterateFiles(Allocator allocator, String path);
|
||||
void InitOS(void (*error_proc)(const char *, ...));
|
||||
|
||||
bool DeleteFile(String path);
|
||||
String GetExePath(Allocator allocator);
|
||||
String GetExeDir(Allocator allocator);
|
||||
bool FileExists(String path);
|
||||
bool IsDir(String path);
|
||||
bool IsFile(String path);
|
||||
String GetWorkingDir(Allocator arena);
|
||||
bool IsAbsolute(String path);
|
||||
int64_t GetFileModTime(String file);
|
||||
|
||||
struct Process {
|
||||
bool is_valid;
|
||||
int exit_code;
|
||||
char platform[6 * 8];
|
||||
int64_t id;
|
||||
|
||||
int64_t view_id; // text editor view
|
||||
bool scroll_to_end;
|
||||
};
|
||||
|
||||
Process SpawnProcess(String command_line, String working_dir, String write_stdin = {}, Array<String> enviroment = {});
|
||||
bool IsValid(Process *process);
|
||||
void KillProcess(Process *process);
|
||||
String PollStdout(Allocator allocator, Process *process, bool force_read);
|
||||
void WriteStdin(Process *process, String string);
|
||||
void CloseStdin(Process *process);
|
||||
double GetTimeMicros(void);
|
||||
|
||||
enum MakeDirResult {
|
||||
MakeDirResult_Success,
|
||||
MakeDirResult_Exists,
|
||||
MakeDirResult_NotFound,
|
||||
MakeDirResult_ErrorOther,
|
||||
};
|
||||
|
||||
MakeDirResult MakeDir(String path);
|
||||
@@ -0,0 +1,515 @@
|
||||
API char ToLowerCase(char a) {
|
||||
if (a >= 'A' && a <= 'Z') a += 32;
|
||||
return a;
|
||||
}
|
||||
|
||||
API char ToUpperCase(char a) {
|
||||
if (a >= 'a' && a <= 'z') a -= 32;
|
||||
return a;
|
||||
}
|
||||
|
||||
API bool IsWhitespace(char w) {
|
||||
bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r';
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool IsAlphabetic(char a) {
|
||||
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool IsIdent(char a) {
|
||||
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || a == '_';
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool IsDigit(char a) {
|
||||
bool result = a >= '0' && a <= '9';
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool IsAlphanumeric(char a) {
|
||||
bool result = IsDigit(a) || IsAlphabetic(a);
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool IsSymbol(char w) {
|
||||
bool result = (w >= '!' && w <= '/') || (w >= ':' && w <= '@') || (w >= '[' && w <= '`') || (w >= '{' && w <= '~');
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool IsNonWord(char w) {
|
||||
if (w == '_') return false;
|
||||
bool result = IsSymbol(w) || IsWhitespace(w);
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool IsWord(char w) {
|
||||
bool result = !IsNonWord(w);
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool IsBrace(char c) {
|
||||
bool result = c == '{' || c == '}';
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool IsParen(char c) {
|
||||
bool result = c == '(' || c == ')';
|
||||
return result;
|
||||
}
|
||||
|
||||
API String Chop(String a, int64_t len) {
|
||||
len = ClampTop(len, a.len);
|
||||
String result = {a.data, a.len - len};
|
||||
return result;
|
||||
}
|
||||
|
||||
API String Skip(String a, int64_t len) {
|
||||
len = ClampTop(len, a.len);
|
||||
String result = {a.data + len, a.len - len};
|
||||
return result;
|
||||
}
|
||||
|
||||
API String GetPostfix(String a, int64_t len) {
|
||||
len = ClampTop(len, a.len);
|
||||
int64_t remain_len = a.len - len;
|
||||
String result = {a.data + remain_len, len};
|
||||
return result;
|
||||
}
|
||||
|
||||
API String GetPrefix(String a, int64_t len) {
|
||||
len = ClampTop(len, a.len);
|
||||
String result = {a.data, len};
|
||||
return result;
|
||||
}
|
||||
|
||||
API char At(String a, int64_t idx) {
|
||||
if (idx < a.len) {
|
||||
return a.data[idx];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
API String GetSlice(String arr, int64_t first_index, int64_t one_past_last_index) {
|
||||
// Negative indexes work in python style, they return you the index counting from end of list
|
||||
if (one_past_last_index == SLICE_LAST) one_past_last_index = arr.len;
|
||||
if (one_past_last_index < 0) one_past_last_index = arr.len + one_past_last_index;
|
||||
|
||||
if (first_index == SLICE_LAST) first_index = arr.len;
|
||||
if (first_index < 0) first_index = arr.len + first_index;
|
||||
|
||||
String result = {arr.data, arr.len};
|
||||
if (arr.len > 0) {
|
||||
if (one_past_last_index > first_index) {
|
||||
first_index = ClampTop(first_index, arr.len - 1);
|
||||
one_past_last_index = ClampTop(one_past_last_index, arr.len);
|
||||
result.data += first_index;
|
||||
result.len = one_past_last_index - first_index;
|
||||
} else {
|
||||
result.len = 0;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool AreEqual(String a, String b, unsigned ignore_case) {
|
||||
if (a.len != b.len) return false;
|
||||
for (int64_t i = 0; i < a.len; i++) {
|
||||
char A = a.data[i];
|
||||
char B = b.data[i];
|
||||
if (ignore_case) {
|
||||
A = ToLowerCase(A);
|
||||
B = ToLowerCase(B);
|
||||
}
|
||||
if (A != B)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
API bool EndsWith(String a, String end, unsigned ignore_case) {
|
||||
String a_end = GetPostfix(a, end.len);
|
||||
bool result = AreEqual(end, a_end, ignore_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool StartsWith(String a, String start, unsigned ignore_case) {
|
||||
String a_start = GetPrefix(a, start.len);
|
||||
bool result = AreEqual(start, a_start, ignore_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
API String Trim(String string) {
|
||||
if (string.len == 0)
|
||||
return string;
|
||||
|
||||
int64_t whitespace_begin = 0;
|
||||
for (; whitespace_begin < string.len; whitespace_begin++) {
|
||||
if (!IsWhitespace(string.data[whitespace_begin])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t whitespace_end = string.len;
|
||||
for (; whitespace_end != whitespace_begin; whitespace_end--) {
|
||||
if (!IsWhitespace(string.data[whitespace_end - 1])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (whitespace_begin == whitespace_end) {
|
||||
string.len = 0;
|
||||
} else {
|
||||
string = GetSlice(string, whitespace_begin, whitespace_end);
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
API String TrimEnd(String string) {
|
||||
int64_t whitespace_end = string.len;
|
||||
for (; whitespace_end != 0; whitespace_end--) {
|
||||
if (!IsWhitespace(string.data[whitespace_end - 1])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String result = GetPrefix(string, whitespace_end);
|
||||
return result;
|
||||
}
|
||||
|
||||
API String Copy(Allocator allocator, String string) {
|
||||
char *copy = (char *)AllocSize(allocator, sizeof(char) * (string.len + 1));
|
||||
memcpy(copy, string.data, string.len);
|
||||
copy[string.len] = 0;
|
||||
String result = {copy, string.len};
|
||||
return result;
|
||||
}
|
||||
|
||||
API String Copy(Allocator allocator, char *string) {
|
||||
return Copy(allocator, {string, (int64_t)strlen(string)});
|
||||
}
|
||||
|
||||
API void NormalizePathInPlace(String s) {
|
||||
for (int64_t i = 0; i < s.len; i++) {
|
||||
if (s.data[i] == '\\')
|
||||
s.data[i] = '/';
|
||||
}
|
||||
}
|
||||
|
||||
API String NormalizePath(Allocator allocator, String s) {
|
||||
String copy = Copy(allocator, s);
|
||||
NormalizePathInPlace(copy);
|
||||
return copy;
|
||||
}
|
||||
|
||||
API bool Seek(String string, String find, int64_t *index_out, SeekFlag flags) {
|
||||
bool ignore_case = flags & SeekFlag_IgnoreCase ? true : false;
|
||||
bool result = false;
|
||||
if (flags & SeekFlag_MatchFindLast) {
|
||||
for (int64_t i = string.len; i != 0; i--) {
|
||||
int64_t index = i - 1;
|
||||
String substring = GetSlice(string, index, index + find.len);
|
||||
if (AreEqual(substring, find, ignore_case)) {
|
||||
bool ok_boundary = true;
|
||||
if (flags & SeekFlag_WordBoundary) {
|
||||
String left = GetSlice(string, i - 1, i);
|
||||
String right = GetSlice(string, i + find.len, i + find.len + 1);
|
||||
if (left.len != 0 && !IsNonWord(left.data[0])) {
|
||||
ok_boundary = false;
|
||||
}
|
||||
if (right.len != 0 && !IsNonWord(right.data[0])) {
|
||||
ok_boundary = false;
|
||||
}
|
||||
}
|
||||
if (ok_boundary) {
|
||||
if (index_out) {
|
||||
*index_out = index;
|
||||
}
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int64_t i = 0; i < string.len; i++) {
|
||||
String substring = GetSlice(string, i, i + find.len);
|
||||
if (AreEqual(substring, find, ignore_case)) {
|
||||
bool ok_boundary = true;
|
||||
if (flags & SeekFlag_WordBoundary) {
|
||||
String left = GetSlice(string, i - 1, i);
|
||||
String right = GetSlice(string, i + find.len, i + find.len + 1);
|
||||
if (left.len != 0 && !IsNonWord(left.data[0])) {
|
||||
ok_boundary = false;
|
||||
}
|
||||
if (right.len != 0 && !IsNonWord(right.data[0])) {
|
||||
ok_boundary = false;
|
||||
}
|
||||
}
|
||||
if (ok_boundary) {
|
||||
if (index_out) {
|
||||
*index_out = i;
|
||||
}
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
API String CutLastSlash(String *s) {
|
||||
String result = *s;
|
||||
Seek(*s, "/", &s->len, SeekFlag_MatchFindLast);
|
||||
result = Skip(result, s->len);
|
||||
return result;
|
||||
}
|
||||
|
||||
API String ChopLastSlash(String s) {
|
||||
String result = s;
|
||||
Seek(s, "/", &result.len, SeekFlag_MatchFindLast);
|
||||
return result;
|
||||
}
|
||||
|
||||
API String ChopLastPeriod(String s) {
|
||||
String result = s;
|
||||
Seek(s, ".", &result.len, SeekFlag_MatchFindLast);
|
||||
return result;
|
||||
}
|
||||
|
||||
API String SkipToLastSlash(String s) {
|
||||
int64_t pos;
|
||||
String result = s;
|
||||
if (Seek(s, "/", &pos, SeekFlag_MatchFindLast)) {
|
||||
result = Skip(result, pos + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
API String SkipToLastPeriod(String s) {
|
||||
int64_t pos;
|
||||
String result = s;
|
||||
if (Seek(s, ".", &pos, SeekFlag_MatchFindLast)) {
|
||||
result = Skip(result, pos + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
API String CutPrefix(String *string, int64_t len) {
|
||||
String result = GetPrefix(*string, len);
|
||||
*string = Skip(*string, len);
|
||||
return result;
|
||||
}
|
||||
|
||||
API String CutPostfix(String *string, int64_t len) {
|
||||
String result = GetPostfix(*string, len);
|
||||
*string = Chop(*string, len);
|
||||
return result;
|
||||
}
|
||||
|
||||
API String FormatV(Allocator allocator, const char *data, va_list args1) {
|
||||
va_list args2;
|
||||
va_copy(args2, args1);
|
||||
int64_t len = stbsp_vsnprintf(0, 0, data, args2);
|
||||
va_end(args2);
|
||||
|
||||
char *result = (char *)AllocSize(allocator, sizeof(char) * (len + 1));
|
||||
stbsp_vsnprintf(result, (int)(len + 1), data, args1);
|
||||
String res = {result, len};
|
||||
return res;
|
||||
}
|
||||
|
||||
API String Format(Allocator allocator, const char *data, ...) {
|
||||
STRING_FORMAT(allocator, data, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
API Array<String> Split(Allocator allocator, String string, String delimiter) {
|
||||
Array<String> result = {allocator};
|
||||
int64_t index = 0;
|
||||
while (Seek(string, delimiter, &index)) {
|
||||
String before_match = {string.data, index};
|
||||
Add(&result, before_match);
|
||||
string = Skip(string, index + delimiter.len);
|
||||
}
|
||||
Add(&result, string);
|
||||
return result;
|
||||
}
|
||||
|
||||
API Array<String> SplitWhitespace(Allocator allocator, String string) {
|
||||
Array<String> result = {allocator};
|
||||
String it = {string.data, 0};
|
||||
for (int64_t i = 0; i < string.len; i += 1) {
|
||||
if (IsWhitespace(string[i])) {
|
||||
if (it.len) Add(&result, it);
|
||||
it.len = 0;
|
||||
it.data = string.data + (i + 1);
|
||||
} else {
|
||||
it.len += 1;
|
||||
}
|
||||
}
|
||||
if (it.len) Add(&result, it);
|
||||
return result;
|
||||
}
|
||||
|
||||
API String Merge(Allocator allocator, Array<String> list, String separator) {
|
||||
int64_t char_count = 0;
|
||||
For(list) char_count += it.len;
|
||||
if (char_count == 0) return {};
|
||||
int64_t node_count = list.len;
|
||||
|
||||
int64_t base_size = (char_count + 1);
|
||||
int64_t sep_size = (node_count - 1) * separator.len;
|
||||
int64_t size = base_size + sep_size;
|
||||
char *buff = (char *)AllocSize(allocator, sizeof(char) * (size + 1));
|
||||
String string = {buff, 0};
|
||||
For(list) {
|
||||
Assert(string.len + it.len <= size);
|
||||
memcpy(string.data + string.len, it.data, it.len);
|
||||
string.len += it.len;
|
||||
if (!IsLast(list, it)) {
|
||||
memcpy(string.data + string.len, separator.data, separator.len);
|
||||
string.len += separator.len;
|
||||
}
|
||||
}
|
||||
Assert(string.len == size - 1);
|
||||
string.data[size] = 0;
|
||||
return string;
|
||||
}
|
||||
|
||||
API Int GetSize(Array<String> array) {
|
||||
Int result = 0;
|
||||
For (array) result += it.len;
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool Chop(String *string, String ending) {
|
||||
if (EndsWith(*string, ending)) {
|
||||
*string = Chop(*string, ending.len);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
API String SkipIntEx(String *string) {
|
||||
String col = {string->data, 0};
|
||||
for (int64_t i = 0; i < string->len; i += 1) {
|
||||
if (IsDigit(string->data[i])) {
|
||||
col.len += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*string = Skip(*string, col.len);
|
||||
return col;
|
||||
}
|
||||
|
||||
API Int SkipInt(String *string) {
|
||||
String col = SkipIntEx(string);
|
||||
if (col.len == 0) return -1;
|
||||
Int result = strtoll(col.data, NULL, 10);
|
||||
return result;
|
||||
}
|
||||
|
||||
API String SkipUntil(String *string, String str) {
|
||||
String begin = *string;
|
||||
begin.len = 0;
|
||||
for (; string->len; begin.len += 1) {
|
||||
String match = GetPrefix(*string, str.len);
|
||||
if (StartsWith(match, str)) break;
|
||||
*string = Skip(*string, 1);
|
||||
}
|
||||
return begin;
|
||||
|
||||
}
|
||||
API String SkipWhitespace(String *string) {
|
||||
String begin = {string->data, 0};
|
||||
for (;string->len;) {
|
||||
if (!IsWhitespace(At(*string, 0))) {
|
||||
break;
|
||||
}
|
||||
*string = Skip(*string, 1);
|
||||
begin.len += 1;
|
||||
}
|
||||
return begin;
|
||||
}
|
||||
|
||||
API String ChopNumberEx(String *string) {
|
||||
String col = {};
|
||||
for (int64_t i = string->len - 1; i >= 0; i -= 1) {
|
||||
if (IsDigit(string->data[i])) {
|
||||
col.data = string->data + i;
|
||||
col.len += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*string = Chop(*string, col.len);
|
||||
return col;
|
||||
}
|
||||
|
||||
API Int ChopNumber(String *string) {
|
||||
Scratch scratch;
|
||||
String col = ChopNumberEx(string);
|
||||
if (col.len == 0) return -1;
|
||||
Int result = strtoll(col.data, NULL, 10) - 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
String SkipIdent(String *string) {
|
||||
String begin = {string->data, 0};
|
||||
if (IsIdent(At(*string, 0))) {
|
||||
for (;string->len;) {
|
||||
char c = At(*string, 0);
|
||||
if (!IsIdent(c) && !IsDigit(c)) {
|
||||
break;
|
||||
}
|
||||
*string = Skip(*string, 1);
|
||||
begin.len += 1;
|
||||
}
|
||||
}
|
||||
return begin;
|
||||
}
|
||||
|
||||
String SkipString(String *string) {
|
||||
String saved_string = *string;
|
||||
char c = At(*string, 0);
|
||||
String q = {&c, 1};
|
||||
if (c == '"' || c == '\'') {
|
||||
*string = Skip(*string, 1);
|
||||
String quote = SkipUntil(string, q);
|
||||
if (At(*string, 0) != c) {
|
||||
*string = saved_string;
|
||||
return {};
|
||||
}
|
||||
return quote;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
API String SkipFloatEx(String *string) {
|
||||
String col = {string->data, 0};
|
||||
if (At(*string, 0) == '-') {
|
||||
col.len += 1;
|
||||
*string = Skip(*string, 1);
|
||||
}
|
||||
for (int64_t i = 0; i < string->len; i += 1) {
|
||||
if (IsDigit(string->data[i]) || string->data[i] == u'.') {
|
||||
col.len += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*string = Skip(*string, col.len);
|
||||
return col;
|
||||
}
|
||||
|
||||
API Float SkipFloat(String *string) {
|
||||
String col = SkipFloatEx(string);
|
||||
if (col.len == 0) return 0;
|
||||
Float result = strtod(string->data, NULL);
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
|
||||
struct String {
|
||||
char *data;
|
||||
int64_t len;
|
||||
|
||||
String() = default;
|
||||
String(char *s) : data(s), len(strlen(s)) {}
|
||||
String(char *s, int64_t l) : data((char *)s), len(l) {}
|
||||
String(const char *s) : data((char *)s), len(strlen((char *)s)) {}
|
||||
String(const char *s, int64_t l) : data((char *)s), len(l) {}
|
||||
|
||||
|
||||
char &operator[](int64_t index) {
|
||||
Assert(index < len);
|
||||
return data[index];
|
||||
}
|
||||
char *begin() { return data; }
|
||||
char *end() { return data + len; }
|
||||
|
||||
struct ReverseIter {
|
||||
char *data;
|
||||
String *arr;
|
||||
|
||||
ReverseIter operator++(int) {
|
||||
ReverseIter ret = *this;
|
||||
data -= 1;
|
||||
return ret;
|
||||
}
|
||||
ReverseIter &operator++() {
|
||||
data -= 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
char &operator*() { return data[0]; }
|
||||
char *operator->() { return data; }
|
||||
|
||||
friend bool operator==(const ReverseIter &a, const ReverseIter &b) { return a.data == b.data; };
|
||||
friend bool operator!=(const ReverseIter &a, const ReverseIter &b) { return a.data != b.data; };
|
||||
|
||||
ReverseIter begin() { return ReverseIter{arr->end() - 1, arr}; }
|
||||
ReverseIter end() { return ReverseIter{arr->begin() - 1, arr}; }
|
||||
};
|
||||
ReverseIter reverse() { return {this->end() - 1, this}; }
|
||||
};
|
||||
|
||||
API char ToLowerCase(char a);
|
||||
API char ToUpperCase(char a);
|
||||
API bool IsWhitespace(char w);
|
||||
API bool IsAlphabetic(char a);
|
||||
API bool IsIdent(char a);
|
||||
API bool IsDigit(char a);
|
||||
API bool IsAlphanumeric(char a);
|
||||
API bool IsSymbol(char w);
|
||||
API bool IsNonWord(char w);
|
||||
API bool IsWord(char w);
|
||||
API bool IsBrace(char c);
|
||||
API bool IsParen(char c);
|
||||
|
||||
API bool EndsWith(String a, String end, unsigned ignore_case = false);
|
||||
API bool StartsWith(String a, String start, unsigned ignore_case = false);
|
||||
API bool AreEqual(String a, String b, unsigned ignore_case = false);
|
||||
inline bool operator==(String a, String b) { return AreEqual(a, b); }
|
||||
inline bool operator!=(String a, String b) { return !AreEqual(a, b); }
|
||||
|
||||
API String Trim(String string);
|
||||
API String TrimEnd(String string);
|
||||
API String Chop(String a, int64_t len);
|
||||
API String Skip(String a, int64_t len);
|
||||
API String GetPostfix(String a, int64_t len);
|
||||
API String GetPrefix(String a, int64_t len);
|
||||
API String GetSlice(String arr, int64_t first_index = 0, int64_t one_past_last_index = SLICE_LAST);
|
||||
API String CutLastSlash(String *s);
|
||||
API String ChopLastSlash(String s);
|
||||
API String ChopLastPeriod(String s);
|
||||
API String SkipToLastSlash(String s);
|
||||
API String SkipToLastPeriod(String s);
|
||||
API String CutPrefix(String *string, int64_t len);
|
||||
API String CutPostfix(String *string, int64_t len);
|
||||
|
||||
API String Copy(Allocator allocator, String string);
|
||||
API String Copy(Allocator allocator, char *string);
|
||||
API void NormalizePathInPlace(String s);
|
||||
API String NormalizePath(Allocator allocator, String s);
|
||||
|
||||
API String FormatV(Allocator allocator, const char *data, va_list args1);
|
||||
API String Format(Allocator allocator, const char *data, ...);
|
||||
#define STRING_FORMAT(allocator, data, result) \
|
||||
va_list args1; \
|
||||
va_start(args1, data); \
|
||||
String result = FormatV(allocator, data, args1); \
|
||||
va_end(args1)
|
||||
|
||||
|
||||
typedef int SeekFlag;
|
||||
enum {
|
||||
SeekFlag_None = 0,
|
||||
SeekFlag_IgnoreCase = 1,
|
||||
SeekFlag_MatchFindLast = 2,
|
||||
SeekFlag_WordBoundary = 4,
|
||||
};
|
||||
API bool Seek(String string, String find, int64_t *index_out = NULL, SeekFlag flags = SeekFlag_None);
|
||||
|
||||
API Array<String> Split(Allocator allocator, String string, String delimiter = " ");
|
||||
API String Merge(Allocator allocator, Array<String> list, String separator);
|
||||
API Int GetSize(Array<String> array);
|
||||
@@ -0,0 +1,547 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
API int64_t WideLength(char16_t *string) {
|
||||
if (!string) return 0;
|
||||
int64_t len = 0;
|
||||
while (*string++ != 0)
|
||||
len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
API char16_t ToLowerCase(char16_t a) {
|
||||
if (a >= u'A' && a <= u'Z') a += 32;
|
||||
return a;
|
||||
}
|
||||
|
||||
API char16_t ToUpperCase(char16_t a) {
|
||||
if (a >= u'a' && a <= u'z') a -= 32;
|
||||
return a;
|
||||
}
|
||||
|
||||
API bool IsWhitespace(char16_t w) {
|
||||
bool result = w == u'\n' || w == u' ' || w == u'\t' || w == u'\v' || w == u'\r';
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool IsAlphabetic(char16_t a) {
|
||||
bool result = (a >= u'a' && a <= u'z') || (a >= u'A' && a <= u'Z');
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool IsIdent(char16_t a) {
|
||||
bool result = (a >= u'a' && a <= u'z') || (a >= u'A' && a <= u'Z') || a == u'_';
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool IsDigit(char16_t a) {
|
||||
bool result = a >= u'0' && a <= u'9';
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool IsHexDigit(char16_t a) {
|
||||
bool result = a >= u'0' && a <= u'9' || a == 'a' || a == 'b' || a == 'c' || a == 'd' || a == 'e' || a == 'f';
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool IsAlphanumeric(char16_t a) {
|
||||
bool result = IsDigit(a) || IsAlphabetic(a);
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool IsSymbol(char16_t w) {
|
||||
bool result = (w >= u'!' && w <= u'/') || (w >= u':' && w <= u'@') || (w >= u'[' && w <= u'`') || (w >= u'{' && w <= u'~');
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool IsNonWord(char16_t w) {
|
||||
if (w == u'_') return false;
|
||||
bool result = IsSymbol(w) || IsWhitespace(w);
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool IsWord(char16_t w) {
|
||||
bool result = !IsNonWord(w);
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool IsBrace(char16_t c) {
|
||||
bool result = c == u'{' || c == u'}';
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool IsParen(char16_t c) {
|
||||
bool result = c == u'(' || c == u')';
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 Chop(String16 a, int64_t len) {
|
||||
len = ClampTop(len, a.len);
|
||||
String16 result = {a.data, a.len - len};
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 Skip(String16 a, int64_t len) {
|
||||
len = ClampTop(len, a.len);
|
||||
String16 result = {a.data + len, a.len - len};
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 GetPostfix(String16 a, int64_t len) {
|
||||
len = ClampTop(len, a.len);
|
||||
int64_t remain_len = a.len - len;
|
||||
String16 result = {a.data + remain_len, len};
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 GetPrefix(String16 a, int64_t len) {
|
||||
len = ClampTop(len, a.len);
|
||||
String16 result = {a.data, len};
|
||||
return result;
|
||||
}
|
||||
|
||||
API char16_t At(String16 a, int64_t idx) {
|
||||
if (idx < a.len) {
|
||||
return a.data[idx];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
API String16 GetSlice(String16 arr, int64_t first_index, int64_t one_past_last_index) {
|
||||
// Negative indexes work in python style, they return you the index counting from end of list
|
||||
if (one_past_last_index == SLICE_LAST) one_past_last_index = arr.len;
|
||||
if (one_past_last_index < 0) one_past_last_index = arr.len + one_past_last_index;
|
||||
|
||||
if (first_index == SLICE_LAST) first_index = arr.len;
|
||||
if (first_index < 0) first_index = arr.len + first_index;
|
||||
|
||||
String16 result = {arr.data, arr.len};
|
||||
if (arr.len > 0) {
|
||||
if (one_past_last_index > first_index) {
|
||||
first_index = ClampTop(first_index, arr.len - 1);
|
||||
one_past_last_index = ClampTop(one_past_last_index, arr.len);
|
||||
result.data += first_index;
|
||||
result.len = one_past_last_index - first_index;
|
||||
} else {
|
||||
result.len = 0;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool AreEqual(String16 a, String16 b, unsigned ignore_case) {
|
||||
if (a.len != b.len) return false;
|
||||
for (int64_t i = 0; i < a.len; i++) {
|
||||
char16_t A = a.data[i];
|
||||
char16_t B = b.data[i];
|
||||
if (ignore_case) {
|
||||
A = ToLowerCase(A);
|
||||
B = ToLowerCase(B);
|
||||
}
|
||||
if (A != B)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
API bool EndsWith(String16 a, String16 end, unsigned ignore_case) {
|
||||
String16 a_end = GetPostfix(a, end.len);
|
||||
bool result = AreEqual(end, a_end, ignore_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
API bool StartsWith(String16 a, String16 start, unsigned ignore_case) {
|
||||
String16 a_start = GetPrefix(a, start.len);
|
||||
bool result = AreEqual(start, a_start, ignore_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 Trim(String16 string) {
|
||||
if (string.len == 0)
|
||||
return string;
|
||||
|
||||
int64_t whitespace_begin = 0;
|
||||
for (; whitespace_begin < string.len; whitespace_begin++) {
|
||||
if (!IsWhitespace(string.data[whitespace_begin])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t whitespace_end = string.len;
|
||||
for (; whitespace_end != whitespace_begin; whitespace_end--) {
|
||||
if (!IsWhitespace(string.data[whitespace_end - 1])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (whitespace_begin == whitespace_end) {
|
||||
string.len = 0;
|
||||
} else {
|
||||
string = GetSlice(string, whitespace_begin, whitespace_end);
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
API String16 TrimEnd(String16 string) {
|
||||
int64_t whitespace_end = string.len;
|
||||
for (; whitespace_end != 0; whitespace_end--) {
|
||||
if (!IsWhitespace(string.data[whitespace_end - 1])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String16 result = GetPrefix(string, whitespace_end);
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 Copy16(Allocator allocator, String16 string) {
|
||||
char16_t *copy = (char16_t *)AllocSize(allocator, sizeof(char16_t) * (string.len + 1));
|
||||
memcpy(copy, string.data, string.len * sizeof(char16_t));
|
||||
copy[string.len] = 0;
|
||||
String16 result = {copy, string.len};
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 Copy16(Allocator allocator, char16_t *string) {
|
||||
return Copy16(allocator, {string, (int64_t)WideLength(string)});
|
||||
}
|
||||
|
||||
API void NormalizePathInPlace(String16 s) {
|
||||
for (int64_t i = 0; i < s.len; i++) {
|
||||
if (s.data[i] == u'\\')
|
||||
s.data[i] = u'/';
|
||||
}
|
||||
}
|
||||
|
||||
API String16 NormalizePath(Allocator allocator, String16 s) {
|
||||
String16 copy = Copy16(allocator, s);
|
||||
NormalizePathInPlace(copy);
|
||||
return copy;
|
||||
}
|
||||
|
||||
API bool Seek(String16 string, String16 find, int64_t *index_out, SeekFlag flags) {
|
||||
bool ignore_case = flags & SeekFlag_IgnoreCase ? true : false;
|
||||
bool result = false;
|
||||
if (flags & SeekFlag_MatchFindLast) {
|
||||
for (int64_t i = string.len; i != 0; i--) {
|
||||
int64_t index = i - 1;
|
||||
String16 substring = GetSlice(string, index, index + find.len);
|
||||
if (AreEqual(substring, find, ignore_case)) {
|
||||
bool ok_boundary = true;
|
||||
if (flags & SeekFlag_WordBoundary) {
|
||||
String16 left = GetSlice(string, i - 1, i);
|
||||
String16 right = GetSlice(string, i + find.len, i + find.len + 1);
|
||||
if (left.len != 0 && !IsNonWord(left.data[0])) {
|
||||
ok_boundary = false;
|
||||
}
|
||||
if (right.len != 0 && !IsNonWord(right.data[0])) {
|
||||
ok_boundary = false;
|
||||
}
|
||||
}
|
||||
if (ok_boundary) {
|
||||
if (index_out) {
|
||||
*index_out = index;
|
||||
}
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int64_t i = 0; i < string.len; i++) {
|
||||
String16 substring = GetSlice(string, i, i + find.len);
|
||||
if (AreEqual(substring, find, ignore_case)) {
|
||||
bool ok_boundary = true;
|
||||
if (flags & SeekFlag_WordBoundary) {
|
||||
String16 left = GetSlice(string, i - 1, i);
|
||||
String16 right = GetSlice(string, i + find.len, i + find.len + 1);
|
||||
if (left.len != 0 && !IsNonWord(left.data[0])) {
|
||||
ok_boundary = false;
|
||||
}
|
||||
if (right.len != 0 && !IsNonWord(right.data[0])) {
|
||||
ok_boundary = false;
|
||||
}
|
||||
}
|
||||
if (ok_boundary) {
|
||||
if (index_out) {
|
||||
*index_out = i;
|
||||
}
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 CutLastSlash(String16 *s) {
|
||||
String16 result = *s;
|
||||
Seek(*s, u"/", &s->len, SeekFlag_MatchFindLast);
|
||||
result = Skip(result, s->len);
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 ChopLastSlash(String16 s) {
|
||||
String16 result = s;
|
||||
Seek(s, u"/", &result.len, SeekFlag_MatchFindLast);
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 ChopLastPeriod(String16 s) {
|
||||
String16 result = s;
|
||||
Seek(s, u".", &result.len, SeekFlag_MatchFindLast);
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 SkipToLastSlash(String16 s) {
|
||||
int64_t pos;
|
||||
String16 result = s;
|
||||
if (Seek(s, u"/", &pos, SeekFlag_MatchFindLast)) {
|
||||
result = Skip(result, pos + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 SkipToLastPeriod(String16 s) {
|
||||
int64_t pos;
|
||||
String16 result = s;
|
||||
if (Seek(s, u".", &pos, SeekFlag_MatchFindLast)) {
|
||||
result = Skip(result, pos + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 CutPrefix(String16 *string, int64_t len) {
|
||||
String16 result = GetPrefix(*string, len);
|
||||
*string = Skip(*string, len);
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 CutPostfix(String16 *string, int64_t len) {
|
||||
String16 result = GetPostfix(*string, len);
|
||||
*string = Chop(*string, len);
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 Format16V(Allocator allocator, const char *data, va_list args1) {
|
||||
Scratch scratch(allocator);
|
||||
va_list args2;
|
||||
va_copy(args2, args1);
|
||||
int64_t len = stbsp_vsnprintf(0, 0, data, args2);
|
||||
va_end(args2);
|
||||
|
||||
char *result = (char *)AllocSize(scratch, sizeof(char) * (len + 1));
|
||||
stbsp_vsnprintf(result, (int)(len + 1), data, args1);
|
||||
String res = {result, len};
|
||||
String16 res16 = ToString16(allocator, res);
|
||||
return res16;
|
||||
}
|
||||
|
||||
API String16 Format16(Allocator allocator, const char *data, ...) {
|
||||
Scratch scratch(allocator);
|
||||
STRING_FORMAT(scratch, data, result);
|
||||
String16 result16 = ToString16(allocator, result);
|
||||
return result16;
|
||||
}
|
||||
|
||||
API Array<String16> Split(Allocator allocator, String16 string, String16 delimiter) {
|
||||
Array<String16> result = {allocator};
|
||||
int64_t index = 0;
|
||||
while (Seek(string, delimiter, &index)) {
|
||||
String16 before_match = {string.data, index};
|
||||
Add(&result, before_match);
|
||||
string = Skip(string, index + delimiter.len);
|
||||
}
|
||||
Add(&result, string);
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 Merge(Allocator allocator, Array<String16> list, String16 separator) {
|
||||
int64_t char_count = 0;
|
||||
For(list) char_count += it.len;
|
||||
if (char_count == 0) return {};
|
||||
int64_t node_count = list.len;
|
||||
|
||||
int64_t base_size = (char_count + 1);
|
||||
int64_t sep_size = (node_count - 1) * separator.len;
|
||||
int64_t size = base_size + sep_size;
|
||||
char16_t *buff = (char16_t *)AllocSize(allocator, sizeof(char16_t) * (size + 1));
|
||||
String16 string = {buff, 0};
|
||||
For(list) {
|
||||
Assert(string.len + it.len <= size);
|
||||
memcpy(string.data + string.len, it.data, it.len * sizeof(char16_t));
|
||||
string.len += it.len;
|
||||
if (!IsLast(list, it)) {
|
||||
memcpy(string.data + string.len, separator.data, separator.len * sizeof(char16_t));
|
||||
string.len += separator.len;
|
||||
}
|
||||
}
|
||||
Assert(string.len == size - 1);
|
||||
string.data[size] = 0;
|
||||
return string;
|
||||
}
|
||||
|
||||
API Int GetSize(Array<String16> array) {
|
||||
Int result = 0;
|
||||
For (array) result += it.len;
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 SkipIntEx(String16 *string) {
|
||||
String16 col = {string->data, 0};
|
||||
if (At(*string, 0) == '-') {
|
||||
col.len += 1;
|
||||
*string = Skip(*string, 1);
|
||||
}
|
||||
for (int64_t i = 0; i < string->len; i += 1) {
|
||||
if (IsDigit(string->data[i])) {
|
||||
col.len += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*string = Skip(*string, col.len);
|
||||
return col;
|
||||
}
|
||||
|
||||
API Int SkipInt(String16 *string) {
|
||||
String16 col = SkipIntEx(string);
|
||||
if (col.len == 0) return 0;
|
||||
Scratch scratch;
|
||||
String num_string = ToString(scratch, col);
|
||||
Int result = strtoll(num_string.data, NULL, 10);
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 SkipFloatEx(String16 *string) {
|
||||
String16 col = {string->data, 0};
|
||||
if (At(*string, 0) == '-') {
|
||||
col.len += 1;
|
||||
*string = Skip(*string, 1);
|
||||
}
|
||||
for (int64_t i = 0; i < string->len; i += 1) {
|
||||
if (IsDigit(string->data[i]) || string->data[i] == u'.') {
|
||||
col.len += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*string = Skip(*string, col.len);
|
||||
return col;
|
||||
}
|
||||
|
||||
API Float SkipFloat(String16 *string) {
|
||||
String16 col = SkipFloatEx(string);
|
||||
if (col.len == 0) return 0;
|
||||
Scratch scratch;
|
||||
String num_string = ToString(scratch, col);
|
||||
Float result = strtod(num_string.data, NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 SkipUntil(String16 *string, String16 str) {
|
||||
String16 begin = *string;
|
||||
begin.len = 0;
|
||||
for (; string->len; begin.len += 1) {
|
||||
String16 match = GetPrefix(*string, str.len);
|
||||
if (StartsWith(match, str)) break;
|
||||
*string = Skip(*string, 1);
|
||||
}
|
||||
return begin;
|
||||
}
|
||||
|
||||
API String16 SkipWhitespace(String16 *string) {
|
||||
String16 begin = {string->data, 0};
|
||||
for (;string->len;) {
|
||||
if (!IsWhitespace(At(*string, 0))) {
|
||||
break;
|
||||
}
|
||||
*string = Skip(*string, 1);
|
||||
begin.len += 1;
|
||||
}
|
||||
return begin;
|
||||
}
|
||||
|
||||
String16 SkipIdent(String16 *string) {
|
||||
String16 begin = {string->data, 0};
|
||||
if (IsIdent(At(*string, 0))) {
|
||||
for (;string->len;) {
|
||||
char16_t c = At(*string, 0);
|
||||
if (!IsIdent(c) && !IsDigit(c)) {
|
||||
break;
|
||||
}
|
||||
*string = Skip(*string, 1);
|
||||
begin.len += 1;
|
||||
}
|
||||
}
|
||||
return begin;
|
||||
}
|
||||
|
||||
String16 SkipString(String16 *string) {
|
||||
String16 saved_string = *string;
|
||||
char16_t c = At(*string, 0);
|
||||
String16 q = {&c, 1};
|
||||
if (c == u'"' || c == u'\'') {
|
||||
*string = Skip(*string, 1);
|
||||
String16 quote = SkipUntil(string, q);
|
||||
if (At(*string, 0) != c) {
|
||||
*string = saved_string;
|
||||
return {};
|
||||
}
|
||||
return quote;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool MatchIdent(String16 *string, String16 expect) {
|
||||
String16 copy = *string;
|
||||
String16 ident = SkipIdent(©);
|
||||
if (ident == expect) {
|
||||
*string = copy;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
API bool Chop(String16 *string, String16 ending) {
|
||||
if (EndsWith(*string, ending)) {
|
||||
*string = Chop(*string, ending.len);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// chop this - 324
|
||||
API String16 ChopNumberEx(String16 *string) {
|
||||
String16 col = {};
|
||||
for (int64_t i = string->len - 1; i >= 0; i -= 1) {
|
||||
if (IsDigit(string->data[i])) {
|
||||
col.data = string->data + i;
|
||||
col.len += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*string = Chop(*string, col.len);
|
||||
return col;
|
||||
}
|
||||
|
||||
API Int ChopNumber(String16 *string) {
|
||||
Scratch scratch;
|
||||
String16 col = ChopNumberEx(string);
|
||||
if (col.len == 0) return -1;
|
||||
String num_string = ToString(scratch, col);
|
||||
Int result = strtoll(num_string.data, NULL, 10) - 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 Concat(Allocator allocator, String16 a, String16 b) {
|
||||
char16_t *p = AllocArray(allocator, char16_t, a.len + b.len + 1);
|
||||
MemoryCopy(p, a.data, sizeof(char16_t) * a.len);
|
||||
MemoryCopy(p + a.len, b.data, sizeof(char16_t) * b.len);
|
||||
String16 result = {p, a.len + b.len};
|
||||
result.data[result.len] = 0;
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
#pragma once
|
||||
|
||||
API int64_t WideLength(char16_t *string);
|
||||
|
||||
struct String16 {
|
||||
char16_t *data;
|
||||
int64_t len;
|
||||
|
||||
String16() = default;
|
||||
String16(char16_t *s) : data(s), len(WideLength(s)) {}
|
||||
String16(char16_t *s, int64_t l) : data((char16_t *)s), len(l) {}
|
||||
String16(const char16_t *s) : data((char16_t *)s), len(WideLength((char16_t *)s)) {}
|
||||
String16(const char16_t *s, int64_t l) : data((char16_t *)s), len(l) {}
|
||||
|
||||
|
||||
char16_t &operator[](int64_t index) {
|
||||
Assert(index < len);
|
||||
return data[index];
|
||||
}
|
||||
char16_t *begin() { return data; }
|
||||
char16_t *end() { return data + len; }
|
||||
|
||||
struct ReverseIter {
|
||||
char16_t *data;
|
||||
String16 *arr;
|
||||
|
||||
ReverseIter operator++(int) {
|
||||
ReverseIter ret = *this;
|
||||
data -= 1;
|
||||
return ret;
|
||||
}
|
||||
ReverseIter &operator++() {
|
||||
data -= 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
char16_t &operator*() { return data[0]; }
|
||||
char16_t *operator->() { return data; }
|
||||
|
||||
friend bool operator==(const ReverseIter &a, const ReverseIter &b) { return a.data == b.data; };
|
||||
friend bool operator!=(const ReverseIter &a, const ReverseIter &b) { return a.data != b.data; };
|
||||
|
||||
ReverseIter begin() { return ReverseIter{arr->end() - 1, arr}; }
|
||||
ReverseIter end() { return ReverseIter{arr->begin() - 1, arr}; }
|
||||
};
|
||||
ReverseIter reverse() { return {this->end() - 1, this}; }
|
||||
};
|
||||
|
||||
API char16_t ToLowerCase(char16_t a);
|
||||
API char16_t ToUpperCase(char16_t a);
|
||||
API bool IsWhitespace(char16_t w);
|
||||
API bool IsAlphabetic(char16_t a);
|
||||
API bool IsIdent(char16_t a);
|
||||
API bool IsDigit(char16_t a);
|
||||
API bool IsAlphanumeric(char16_t a);
|
||||
API bool IsSymbol(char16_t w);
|
||||
API bool IsNonWord(char16_t w);
|
||||
API bool IsWord(char16_t w);
|
||||
API bool IsBrace(char16_t c);
|
||||
API bool IsParen(char16_t c);
|
||||
|
||||
API bool EndsWith(String16 a, String16 end, unsigned ignore_case = false);
|
||||
API bool StartsWith(String16 a, String16 start, unsigned ignore_case = false);
|
||||
API bool AreEqual(String16 a, String16 b, unsigned ignore_case = false);
|
||||
inline bool operator==(String16 a, String16 b) { return AreEqual(a, b); }
|
||||
inline bool operator!=(String16 a, String16 b) { return !AreEqual(a, b); }
|
||||
|
||||
API String16 Trim(String16 string);
|
||||
API String16 TrimEnd(String16 string);
|
||||
API String16 Chop(String16 a, int64_t len);
|
||||
API String16 Skip(String16 a, int64_t len);
|
||||
API String16 GetPostfix(String16 a, int64_t len);
|
||||
API String16 GetPrefix(String16 a, int64_t len);
|
||||
API String16 GetSlice(String16 arr, int64_t first_index = 0, int64_t one_past_last_index = SLICE_LAST);
|
||||
API String16 CutLastSlash(String16 *s);
|
||||
API String16 ChopLastSlash(String16 s);
|
||||
API String16 ChopLastPeriod(String16 s);
|
||||
API String16 SkipToLastSlash(String16 s);
|
||||
API String16 SkipToLastPeriod(String16 s);
|
||||
API String16 CutPrefix(String16 *string, int64_t len);
|
||||
API String16 CutPostfix(String16 *string, int64_t len);
|
||||
|
||||
// @todo: think about this API, for parsing, maybe redesign or something
|
||||
API String16 SkipIntEx(String16 *string);
|
||||
API Int SkipInt(String16 *string);
|
||||
API String16 SkipUntil(String16 *string, String16 str);
|
||||
API String16 SkipWhitespace(String16 *string);
|
||||
API String16 ChopNumberEx(String16 *string);
|
||||
API Int ChopNumber(String16 *string);
|
||||
|
||||
API String16 Copy16(Allocator allocator, String16 string);
|
||||
API String16 Copy16(Allocator allocator, char16_t *string, int64_t len);
|
||||
API String16 Copy16(Allocator allocator, char *string);
|
||||
API void NormalizePathInPlace(String16 s);
|
||||
API String16 NormalizePath(Allocator allocator, String16 s);
|
||||
|
||||
API String16 Format16V(Allocator allocator, const char *data, va_list args1);
|
||||
API String16 Format16(Allocator allocator, const char *data, ...);
|
||||
|
||||
API bool Seek(String16 string, String16 find, int64_t *index_out = NULL, SeekFlag flags = SeekFlag_None);
|
||||
|
||||
API Array<String16> Split(Allocator allocator, String16 string, String16 delimiter = u" ");
|
||||
API String16 Merge(Allocator allocator, Array<String16> list, String16 separator);
|
||||
API Int GetSize(Array<String16> array);
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
Hash table implementation:
|
||||
Pointers to values
|
||||
Open adressing
|
||||
Linear Probing
|
||||
Power of 2
|
||||
Robin Hood hashing
|
||||
Resizes on high probe count (min max load factor)
|
||||
|
||||
Hash 0 is reserved for empty hash table entry
|
||||
*/
|
||||
template <class Value>
|
||||
struct HashTable {
|
||||
struct Entry {
|
||||
uint64_t hash;
|
||||
uint64_t key;
|
||||
size_t distance;
|
||||
Value value;
|
||||
};
|
||||
|
||||
Allocator allocator;
|
||||
size_t len, cap;
|
||||
Entry *values;
|
||||
|
||||
static const size_t max_load_factor = 80;
|
||||
static const size_t min_load_factor = 50;
|
||||
static const size_t significant_distance = 8;
|
||||
|
||||
// load factor calculation was rearranged
|
||||
// to get rid of division:
|
||||
//> 100 * len / cap = load_factor
|
||||
//> len * 100 = load_factor * cap
|
||||
inline bool reached_load_factor(size_t lfactor) {
|
||||
return (len + 1) * 100 >= lfactor * cap;
|
||||
}
|
||||
|
||||
inline bool is_empty(Entry *entry) { return entry->hash == 0; }
|
||||
inline bool is_occupied(Entry *entry) { return entry->hash != 0; }
|
||||
|
||||
void reserve(size_t size) {
|
||||
Assert(size > cap && "New size is smaller then original size");
|
||||
Assert(IsPowerOf2(size));
|
||||
if (!allocator.proc) allocator = GetSystemAllocator();
|
||||
|
||||
Entry *old_values = values;
|
||||
size_t old_cap = cap;
|
||||
|
||||
values = (Entry *)AllocSize(allocator, sizeof(Entry) * size);
|
||||
for (size_t i = 0; i < size; i += 1) values[i] = {};
|
||||
cap = size;
|
||||
|
||||
Assert(!(old_values == 0 && len != 0));
|
||||
if (len == 0) {
|
||||
if (old_values) {
|
||||
Dealloc(allocator, old_values);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
len = 0;
|
||||
for (size_t i = 0; i < old_cap; i += 1) {
|
||||
Entry *it = old_values + i;
|
||||
if (is_occupied(it)) {
|
||||
insert(it->key, it->value);
|
||||
}
|
||||
}
|
||||
Dealloc(allocator, old_values);
|
||||
}
|
||||
|
||||
Entry *get_table_entry(uint64_t key) {
|
||||
if (len == 0) return 0;
|
||||
uint64_t hash = HashBytes(&key, sizeof(key));
|
||||
if (hash == 0) hash += 1;
|
||||
uint64_t index = WrapAroundPowerOf2(hash, cap);
|
||||
uint64_t i = index;
|
||||
uint64_t distance = 0;
|
||||
for (;;) {
|
||||
Entry *it = values + i;
|
||||
if (distance > it->distance) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (it->hash == hash && it->key == key) {
|
||||
return it;
|
||||
}
|
||||
|
||||
distance += 1;
|
||||
i = WrapAroundPowerOf2(i + 1, cap);
|
||||
if (i == index) return 0;
|
||||
}
|
||||
Assert(!"Invalid codepath");
|
||||
}
|
||||
|
||||
void insert(uint64_t key, const Value &value) {
|
||||
if (reached_load_factor(max_load_factor)) {
|
||||
if (cap == 0) cap = 16; // 32 cause cap*2
|
||||
reserve(cap * 2);
|
||||
}
|
||||
|
||||
uint64_t hash = HashBytes(&key, sizeof(key));
|
||||
if (hash == 0) hash += 1;
|
||||
uint64_t index = WrapAroundPowerOf2(hash, cap);
|
||||
uint64_t i = index;
|
||||
Entry to_insert = {hash, key, 0, value};
|
||||
for (;;) {
|
||||
Entry *it = values + i;
|
||||
if (is_empty(it)) {
|
||||
*it = to_insert;
|
||||
len += 1;
|
||||
// If we have more then 8 consecutive items we try to resize
|
||||
if (to_insert.distance > 8 && reached_load_factor(min_load_factor)) {
|
||||
reserve(cap * 2);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (it->hash == hash && it->key == key) {
|
||||
*it = to_insert;
|
||||
// If we have more then 8 consecutive items we try to resize
|
||||
if (to_insert.distance > 8 && reached_load_factor(min_load_factor)) {
|
||||
reserve(cap * 2);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Robin hood hashing
|
||||
if (to_insert.distance > it->distance) {
|
||||
Entry temp = to_insert;
|
||||
to_insert = *it;
|
||||
*it = temp;
|
||||
}
|
||||
|
||||
to_insert.distance += 1;
|
||||
i = WrapAroundPowerOf2(i + 1, cap);
|
||||
Assert(i != index && "Did a full 360 through a hash table, no good :( that shouldnt be possible");
|
||||
}
|
||||
Assert(!"Invalid codepath");
|
||||
}
|
||||
|
||||
void remove(uint64_t key) {
|
||||
Entry *entry = get_table_entry(key);
|
||||
entry->hash = 0;
|
||||
entry->distance = 0;
|
||||
len -= 1;
|
||||
}
|
||||
|
||||
Value *get(uint64_t key) {
|
||||
Entry *v = get_table_entry(key);
|
||||
if (!v) return 0;
|
||||
return &v->value;
|
||||
}
|
||||
|
||||
Value get(uint64_t key, Value default_value) {
|
||||
Entry *v = get_table_entry(key);
|
||||
if (!v) return default_value;
|
||||
return v->value;
|
||||
}
|
||||
|
||||
Value *get(String s) {
|
||||
uint64_t hash = HashBytes(s.data, (unsigned)s.len);
|
||||
return get(hash);
|
||||
}
|
||||
|
||||
Value get(String s, Value default_value) {
|
||||
uint64_t hash = HashBytes(s.data, (unsigned)s.len);
|
||||
return get(hash, default_value);
|
||||
}
|
||||
|
||||
void put(String s, const Value &value) {
|
||||
uint64_t hash = HashBytes(s.data, (unsigned)s.len);
|
||||
insert(hash, value);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
len = 0;
|
||||
for (size_t i = 0; i < cap; i += 1) {
|
||||
Entry *it = values + i;
|
||||
it->hash = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void dealloc() {
|
||||
Dealloc(allocator, values);
|
||||
values = NULL;
|
||||
len = 0;
|
||||
cap = 0;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,237 @@
|
||||
API UTF32Result UTF16ToUTF32(uint16_t *c, int64_t max_advance) {
|
||||
UTF32Result result;
|
||||
MemoryZero(&result, sizeof(result));
|
||||
if (max_advance >= 1) {
|
||||
result.advance = 1;
|
||||
result.out_str = c[0];
|
||||
if (c[0] >= 0xD800 && c[0] <= 0xDBFF && c[1] >= 0xDC00 && c[1] <= 0xDFFF) {
|
||||
if (max_advance >= 2) {
|
||||
result.out_str = 0x10000;
|
||||
result.out_str += (uint32_t)(c[0] & 0x03FF) << 10u | (c[1] & 0x03FF);
|
||||
result.advance = 2;
|
||||
} else
|
||||
result.error = 2;
|
||||
}
|
||||
} else {
|
||||
result.error = 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
API UTF8Result UTF32ToUTF8(uint32_t codepoint) {
|
||||
UTF8Result result;
|
||||
MemoryZero(&result, sizeof(result));
|
||||
|
||||
if (codepoint <= 0x7F) {
|
||||
result.len = 1;
|
||||
result.out_str[0] = (char)codepoint;
|
||||
} else if (codepoint <= 0x7FF) {
|
||||
result.len = 2;
|
||||
result.out_str[0] = 0xc0 | (0x1f & (codepoint >> 6));
|
||||
result.out_str[1] = 0x80 | (0x3f & codepoint);
|
||||
} else if (codepoint <= 0xFFFF) { // 16 bit word
|
||||
result.len = 3;
|
||||
result.out_str[0] = 0xe0 | (0xf & (codepoint >> 12)); // 4 bits
|
||||
result.out_str[1] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits
|
||||
result.out_str[2] = 0x80 | (0x3f & codepoint); // 6 bits
|
||||
} else if (codepoint <= 0x10FFFF) { // 21 bit word
|
||||
result.len = 4;
|
||||
result.out_str[0] = 0xf0 | (0x7 & (codepoint >> 18)); // 3 bits
|
||||
result.out_str[1] = 0x80 | (0x3f & (codepoint >> 12)); // 6 bits
|
||||
result.out_str[2] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits
|
||||
result.out_str[3] = 0x80 | (0x3f & codepoint); // 6 bits
|
||||
} else {
|
||||
result.error = 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
API UTF32Result UTF8ToUTF32(uint8_t *c, int64_t max_advance) {
|
||||
UTF32Result result;
|
||||
MemoryZero(&result, sizeof(result));
|
||||
|
||||
if ((c[0] & 0x80) == 0) { // Check if leftmost zero of first byte is unset
|
||||
if (max_advance >= 1) {
|
||||
result.out_str = c[0];
|
||||
result.advance = 1;
|
||||
} else result.error = 1;
|
||||
}
|
||||
|
||||
else if ((c[0] & 0xe0) == 0xc0) {
|
||||
if ((c[1] & 0xc0) == 0x80) { // Continuation byte required
|
||||
if (max_advance >= 2) {
|
||||
result.out_str = (uint32_t)(c[0] & 0x1f) << 6u | (c[1] & 0x3f);
|
||||
result.advance = 2;
|
||||
} else result.error = 2;
|
||||
} else result.error = 2;
|
||||
}
|
||||
|
||||
else if ((c[0] & 0xf0) == 0xe0) {
|
||||
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80) { // Two continuation bytes required
|
||||
if (max_advance >= 3) {
|
||||
result.out_str = (uint32_t)(c[0] & 0xf) << 12u | (uint32_t)(c[1] & 0x3f) << 6u | (c[2] & 0x3f);
|
||||
result.advance = 3;
|
||||
} else result.error = 3;
|
||||
} else result.error = 3;
|
||||
}
|
||||
|
||||
else if ((c[0] & 0xf8) == 0xf0) {
|
||||
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80 && (c[3] & 0xc0) == 0x80) { // Three continuation bytes required
|
||||
if (max_advance >= 4) {
|
||||
result.out_str = (uint32_t)(c[0] & 0xf) << 18u | (uint32_t)(c[1] & 0x3f) << 12u | (uint32_t)(c[2] & 0x3f) << 6u | (uint32_t)(c[3] & 0x3f);
|
||||
result.advance = 4;
|
||||
} else result.error = 4;
|
||||
} else result.error = 4;
|
||||
} else result.error = 4;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
API UTF16Result UTF32ToUTF16(uint32_t codepoint) {
|
||||
UTF16Result result;
|
||||
MemoryZero(&result, sizeof(result));
|
||||
if (codepoint < 0x10000) {
|
||||
result.out_str[0] = (uint16_t)codepoint;
|
||||
result.out_str[1] = 0;
|
||||
result.len = 1;
|
||||
} else if (codepoint <= 0x10FFFF) {
|
||||
uint32_t code = (codepoint - 0x10000);
|
||||
result.out_str[0] = (uint16_t)(0xD800 | (code >> 10));
|
||||
result.out_str[1] = (uint16_t)(0xDC00 | (code & 0x3FF));
|
||||
result.len = 2;
|
||||
} else {
|
||||
result.error = 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#define UTF__HANDLE_DECODE_ERROR(question_mark, I) \
|
||||
{ \
|
||||
if (outlen < buffer_size - 1) buffer[outlen++] = (question_mark); \
|
||||
i += I; \
|
||||
}
|
||||
|
||||
API int64_t CreateCharFromWidechar(char *buffer, int64_t buffer_size, char16_t *in, int64_t inlen) {
|
||||
int64_t outlen = 0;
|
||||
for (int64_t i = 0; i < inlen && in[i];) {
|
||||
UTF32Result decode = UTF16ToUTF32((uint16_t *)(in + i), (int64_t)(inlen - i));
|
||||
if (!decode.error) {
|
||||
i += decode.advance;
|
||||
UTF8Result encode = UTF32ToUTF8(decode.out_str);
|
||||
if (!encode.error) {
|
||||
for (int64_t j = 0; j < encode.len; j++) {
|
||||
if (outlen < buffer_size - 1) {
|
||||
buffer[outlen++] = encode.out_str[j];
|
||||
}
|
||||
}
|
||||
} else UTF__HANDLE_DECODE_ERROR('?', 0);
|
||||
} else UTF__HANDLE_DECODE_ERROR('?', 1);
|
||||
}
|
||||
|
||||
buffer[outlen] = 0;
|
||||
return outlen;
|
||||
}
|
||||
|
||||
API int64_t CreateWidecharFromChar(char16_t *buffer, int64_t buffer_size, char *in, int64_t inlen) {
|
||||
int64_t outlen = 0;
|
||||
for (int64_t i = 0; i < inlen;) {
|
||||
UTF32Result decode = UTF8ToUTF32((uint8_t *)(in + i), (int64_t)(inlen - i));
|
||||
if (!decode.error) {
|
||||
i += decode.advance;
|
||||
UTF16Result encode = UTF32ToUTF16(decode.out_str);
|
||||
if (!encode.error) {
|
||||
for (int64_t j = 0; j < encode.len; j++) {
|
||||
if (outlen < buffer_size - 1) {
|
||||
buffer[outlen++] = encode.out_str[j];
|
||||
}
|
||||
}
|
||||
} else UTF__HANDLE_DECODE_ERROR(0x003f, 0);
|
||||
} else UTF__HANDLE_DECODE_ERROR(0x003f, 1);
|
||||
}
|
||||
|
||||
buffer[outlen] = 0;
|
||||
return outlen;
|
||||
}
|
||||
|
||||
API bool IsValid(UTF8Iter &iter) {
|
||||
return iter.item;
|
||||
}
|
||||
|
||||
API void Advance(UTF8Iter *iter) {
|
||||
iter->i += iter->utf8_codepoint_byte_size;
|
||||
UTF32Result r = UTF8ToUTF32((uint8_t *)(iter->data + iter->i), iter->len - iter->i);
|
||||
if (r.error) {
|
||||
iter->item = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
iter->utf8_codepoint_byte_size = r.advance;
|
||||
iter->item = r.out_str;
|
||||
}
|
||||
|
||||
API UTF8Iter IterateUTF8Ex(char *data, int64_t len) {
|
||||
UTF8Iter result;
|
||||
MemoryZero(&result, sizeof(result));
|
||||
result.data = data;
|
||||
result.len = len;
|
||||
if (len) Advance(&result);
|
||||
return result;
|
||||
}
|
||||
|
||||
API UTF8Iter IterateUTF8(char *data) {
|
||||
int64_t length = 0;
|
||||
while (data[length]) length += 1;
|
||||
return IterateUTF8Ex(data, length);
|
||||
}
|
||||
|
||||
API UTF8Iter IterateUTF8(String string) {
|
||||
return IterateUTF8Ex(string.data, string.len);
|
||||
}
|
||||
|
||||
API bool IsUTF8ContinuationByte(char c) {
|
||||
char result = (c & 0b11000000) == 0b10000000;
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 ToString16(Allocator allocator, String string) {
|
||||
char16_t *buffer = (char16_t *)AllocSize(allocator, sizeof(char16_t) * (string.len + 1));
|
||||
int64_t size = CreateWidecharFromChar(buffer, string.len + 1, string.data, string.len);
|
||||
String16 result = {buffer, size};
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 ToString16(Allocator allocator, char *in_string) {
|
||||
String string(in_string);
|
||||
String16 result = ToString16(allocator, string);
|
||||
return result;
|
||||
}
|
||||
|
||||
API char16_t *ToWidechar(Allocator allocator, String string) {
|
||||
String16 result = ToString16(allocator, string);
|
||||
return result.data;
|
||||
}
|
||||
|
||||
API String ToString(Allocator allocator, String16 string) {
|
||||
Assert(sizeof(char16_t) == 2);
|
||||
|
||||
int64_t buffer_size = (string.len + 1) * 2;
|
||||
char *buffer = (char *)AllocSize(allocator, buffer_size);
|
||||
int64_t size = CreateCharFromWidechar(buffer, buffer_size, string.data, string.len);
|
||||
String result = {buffer, size};
|
||||
|
||||
Assert(size < buffer_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
API String ToString(Allocator allocator, char16_t *string, int64_t len) {
|
||||
return ToString(allocator, {string, len});
|
||||
}
|
||||
|
||||
API String ToString(Allocator allocator, char16_t *wstring) {
|
||||
int64_t size = WideLength(wstring);
|
||||
String result = ToString(allocator, {wstring, size});
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
struct UTF32Result {
|
||||
uint32_t out_str;
|
||||
int64_t advance;
|
||||
int64_t error;
|
||||
};
|
||||
|
||||
struct UTF8Result {
|
||||
uint8_t out_str[4];
|
||||
int64_t len;
|
||||
int64_t error;
|
||||
};
|
||||
|
||||
struct UTF16Result {
|
||||
uint16_t out_str[2];
|
||||
int64_t len;
|
||||
int64_t error;
|
||||
};
|
||||
|
||||
struct UTF8Iter {
|
||||
char *data;
|
||||
int64_t len;
|
||||
int64_t utf8_codepoint_byte_size;
|
||||
int64_t i;
|
||||
uint32_t item;
|
||||
|
||||
UTF8Iter &operator++() {
|
||||
void Advance(UTF8Iter * iter);
|
||||
Advance(this);
|
||||
return *this;
|
||||
}
|
||||
friend bool operator!=(const UTF8Iter &a, const UTF8Iter &b) { return a.item != b.item; }
|
||||
UTF8Iter &operator*() { return *this; }
|
||||
UTF8Iter begin() {
|
||||
UTF8Iter IterateUTF8Ex(char *data, int64_t len);
|
||||
return {IterateUTF8Ex(data, len)};
|
||||
}
|
||||
UTF8Iter end() { return {}; }
|
||||
};
|
||||
|
||||
API UTF32Result UTF16ToUTF32(uint16_t *c, int64_t max_advance);
|
||||
API UTF8Result UTF32ToUTF8(uint32_t codepoint);
|
||||
API UTF32Result UTF8ToUTF32(uint8_t *c, int64_t max_advance);
|
||||
API UTF16Result UTF32ToUTF16(uint32_t codepoint);
|
||||
|
||||
API int64_t CreateCharFromWidechar(char *buffer, int64_t buffer_size, char16_t *in, int64_t inlen);
|
||||
API int64_t CreateWidecharFromChar(char16_t *buffer, int64_t buffer_size, char *in, int64_t inlen);
|
||||
API char16_t *ToWidechar(Allocator allocator, String string);
|
||||
API String16 ToString16(Allocator allocator, String string);
|
||||
API String ToString(Allocator allocator, String16 string);
|
||||
API String ToString(Allocator allocator, char16_t *string, int64_t len);
|
||||
API String ToString(Allocator allocator, char16_t *wstring);
|
||||
|
||||
API bool IsValid(UTF8Iter &iter);
|
||||
API void Advance(UTF8Iter *iter);
|
||||
API UTF8Iter IterateUTF8Ex(char *data, int64_t len);
|
||||
API UTF8Iter IterateUTF8(char *data);
|
||||
API UTF8Iter IterateUTF8(String string);
|
||||
API bool IsUTF8ContinuationByte(char c);
|
||||
@@ -0,0 +1,186 @@
|
||||
Vec2 GetSize(Rect2 r) {
|
||||
Vec2 result = {r.max.x - r.min.x, r.max.y - r.min.y};
|
||||
return result;
|
||||
}
|
||||
|
||||
Vec2 GetSizeF(Rect2I r) {
|
||||
Vec2 result = {(float)(r.max.x - r.min.x), (float)(r.max.y - r.min.y)};
|
||||
return result;
|
||||
}
|
||||
|
||||
Vec2 ToVec2(Vec2I v) { return {(float)v.x, (float)v.y}; }
|
||||
Vec2I ToVec2I(Vec2 v) { return {(Int)v.x, (Int)v.y}; }
|
||||
Rect2I ToRect2I(Rect2 r) { return {ToVec2I(r.min), ToVec2I(r.max)}; }
|
||||
Rect2 ToRect2(Rect2I r) { return {ToVec2(r.min), ToVec2(r.max)}; }
|
||||
|
||||
// clang-format off
|
||||
Rect2 operator-(Rect2 r, Rect2 value) { return { {r.min.x - value.min.x, r.min.y - value.min.y}, {r.max.x - value.max.x, r.max.y - value.max.y} }; }
|
||||
Rect2 operator+(Rect2 r, Rect2 value) { return { {r.min.x + value.min.x, r.min.y + value.min.y}, {r.max.x + value.max.x, r.max.y + value.max.y} }; }
|
||||
Rect2 operator*(Rect2 r, Rect2 value) { return { {r.min.x * value.min.x, r.min.y * value.min.y}, {r.max.x * value.max.x, r.max.y * value.max.y} }; }
|
||||
Rect2 operator/(Rect2 r, Rect2 value) { return { {r.min.x / value.min.x, r.min.y / value.min.y}, {r.max.x / value.max.x, r.max.y / value.max.y} }; }
|
||||
|
||||
Rect2 operator-(Rect2 r, Vec2 value) { return { {r.min.x - value.x, r.min.y - value.y}, {r.max.x - value.x, r.max.y - value.y} }; }
|
||||
Rect2 operator+(Rect2 r, Vec2 value) { return { {r.min.x + value.x, r.min.y + value.y}, {r.max.x + value.x, r.max.y + value.y} }; }
|
||||
Rect2 operator*(Rect2 r, Vec2 value) { return { {r.min.x * value.x, r.min.y * value.y}, {r.max.x * value.x, r.max.y * value.y} }; }
|
||||
Rect2 operator/(Rect2 r, Vec2 value) { return { {r.min.x / value.x, r.min.y / value.y}, {r.max.x / value.x, r.max.y / value.y} }; }
|
||||
Rect2 operator-(Vec2 value, Rect2 r) { return { {r.min.x - value.x, r.min.y - value.y}, {r.max.x - value.x, r.max.y - value.y} }; }
|
||||
Rect2 operator+(Vec2 value, Rect2 r) { return { {r.min.x + value.x, r.min.y + value.y}, {r.max.x + value.x, r.max.y + value.y} }; }
|
||||
Rect2 operator*(Vec2 value, Rect2 r) { return { {r.min.x * value.x, r.min.y * value.y}, {r.max.x * value.x, r.max.y * value.y} }; }
|
||||
Rect2 operator/(Vec2 value, Rect2 r) { return { {r.min.x / value.x, r.min.y / value.y}, {r.max.x / value.x, r.max.y / value.y} }; }
|
||||
|
||||
Rect2 operator-(Rect2 r, float value) { return { {r.min.x - value, r.min.y - value}, {r.max.x - value, r.max.y - value} }; }
|
||||
Rect2 operator+(Rect2 r, float value) { return { {r.min.x + value, r.min.y + value}, {r.max.x + value, r.max.y + value} }; }
|
||||
Rect2 operator*(Rect2 r, float value) { return { {r.min.x * value, r.min.y * value}, {r.max.x * value, r.max.y * value} }; }
|
||||
Rect2 operator/(Rect2 r, float value) { return { {r.min.x / value, r.min.y / value}, {r.max.x / value, r.max.y / value} }; }
|
||||
Rect2 operator-(float value, Rect2 r) { return { {r.min.x - value, r.min.y - value}, {r.max.x - value, r.max.y - value} }; }
|
||||
Rect2 operator+(float value, Rect2 r) { return { {r.min.x + value, r.min.y + value}, {r.max.x + value, r.max.y + value} }; }
|
||||
Rect2 operator*(float value, Rect2 r) { return { {r.min.x * value, r.min.y * value}, {r.max.x * value, r.max.y * value} }; }
|
||||
Rect2 operator/(float value, Rect2 r) { return { {r.min.x / value, r.min.y / value}, {r.max.x / value, r.max.y / value} }; }
|
||||
|
||||
Rect2 operator-=(Rect2 &r, Rect2 value) { r = r - value; return r; }
|
||||
Rect2 operator+=(Rect2 &r, Rect2 value) { r = r + value; return r; }
|
||||
Rect2 operator*=(Rect2 &r, Rect2 value) { r = r * value; return r; }
|
||||
Rect2 operator/=(Rect2 &r, Rect2 value) { r = r / value; return r; }
|
||||
Rect2 operator-=(Rect2 &r, Vec2 value) { r = r - value; return r; }
|
||||
Rect2 operator+=(Rect2 &r, Vec2 value) { r = r + value; return r; }
|
||||
Rect2 operator*=(Rect2 &r, Vec2 value) { r = r * value; return r; }
|
||||
Rect2 operator/=(Rect2 &r, Vec2 value) { r = r / value; return r; }
|
||||
Rect2 operator-=(Rect2 &r, float value) { r = r - value; return r; }
|
||||
Rect2 operator+=(Rect2 &r, float value) { r = r + value; return r; }
|
||||
Rect2 operator*=(Rect2 &r, float value) { r = r * value; return r; }
|
||||
Rect2 operator/=(Rect2 &r, float value) { r = r / value; return r; }
|
||||
|
||||
bool operator==(Rect2 a, Rect2 b) {
|
||||
bool result = a.min.x == b.min.x && a.min.y == b.min.y && a.max.x == b.max.x && a.max.y == b.max.y;
|
||||
return result;
|
||||
}
|
||||
bool operator!=(Rect2 a, Rect2 b) {
|
||||
bool result = !(a == b);
|
||||
return result;
|
||||
}
|
||||
|
||||
Vec2 operator-(Vec2 a, Vec2 b) { return {a.x - b.x, a.y - b.y}; }
|
||||
Vec2 operator+(Vec2 a, Vec2 b) { return {a.x + b.x, a.y + b.y}; }
|
||||
Vec2 operator*(Vec2 a, Vec2 b) { return {a.x * b.x, a.y * b.y}; }
|
||||
Vec2 operator/(Vec2 a, Vec2 b) { return {a.x / b.x, a.y / b.y}; }
|
||||
|
||||
Vec2 operator-(Vec2 a, float b) { return {a.x - b, a.y - b}; }
|
||||
Vec2 operator+(Vec2 a, float b) { return {a.x + b, a.y + b}; }
|
||||
Vec2 operator*(Vec2 a, float b) { return {a.x * b, a.y * b}; }
|
||||
Vec2 operator/(Vec2 a, float b) { return {a.x / b, a.y / b}; }
|
||||
Vec2 operator-(float b, Vec2 a) { return {a.x - b, a.y - b}; }
|
||||
Vec2 operator+(float b, Vec2 a) { return {a.x + b, a.y + b}; }
|
||||
Vec2 operator*(float b, Vec2 a) { return {a.x * b, a.y * b}; }
|
||||
Vec2 operator/(float b, Vec2 a) { return {a.x / b, a.y / b}; }
|
||||
|
||||
Vec2 operator-=(Vec2 &a, Vec2 b) { a = a - b; return a; }
|
||||
Vec2 operator+=(Vec2 &a, Vec2 b) { a = a + b; return a; }
|
||||
Vec2 operator*=(Vec2 &a, Vec2 b) { a = a * b; return a; }
|
||||
Vec2 operator/=(Vec2 &a, Vec2 b) { a = a / b; return a; }
|
||||
Vec2 operator-=(Vec2 &a, float b) { a = a - b; return a; }
|
||||
Vec2 operator+=(Vec2 &a, float b) { a = a + b; return a; }
|
||||
Vec2 operator*=(Vec2 &a, float b) { a = a * b; return a; }
|
||||
Vec2 operator/=(Vec2 &a, float b) { a = a / b; return a; }
|
||||
|
||||
// clang-format on
|
||||
|
||||
Rect2 Rect0Size(float x, float y) {
|
||||
Rect2 rect = {0, 0, x, y};
|
||||
return rect;
|
||||
}
|
||||
|
||||
Rect2 Rect2FromSize(Vec2 pos, Vec2 size) {
|
||||
Rect2 result = {};
|
||||
result.min = pos;
|
||||
result.max = pos + size;
|
||||
return result;
|
||||
}
|
||||
|
||||
Rect2 Rect2MidHalf(Vec2 mid, Vec2 half_size) {
|
||||
Rect2 result = {};
|
||||
result.min = mid - half_size;
|
||||
result.max = mid + half_size;
|
||||
return result;
|
||||
}
|
||||
|
||||
Rect2 Shrink(Rect2 result, float v) {
|
||||
result.min.x += v;
|
||||
result.max.x -= v;
|
||||
result.min.y += v;
|
||||
result.max.y -= v;
|
||||
return result;
|
||||
}
|
||||
|
||||
Rect2 ShrinkVertical(Rect2 result, float v) {
|
||||
result.min.y += v;
|
||||
result.max.y -= v;
|
||||
return result;
|
||||
}
|
||||
|
||||
Rect2 ShrinkHorizontal(Rect2 result, float v) {
|
||||
result.min.x += v;
|
||||
result.max.x -= v;
|
||||
return result;
|
||||
}
|
||||
|
||||
Vec2 GetMid(Rect2 r) {
|
||||
Vec2 size = GetSize(r);
|
||||
size.x /= 2.f;
|
||||
size.y /= 2.f;
|
||||
Vec2 result = r.min + size;
|
||||
return result;
|
||||
}
|
||||
|
||||
Rect2 CutLeft(Rect2 *r, float value) {
|
||||
float minx = r->min.x;
|
||||
r->min.x = Min(r->min.x + value, r->max.x);
|
||||
Rect2 result = {
|
||||
{ minx, r->min.y},
|
||||
{r->min.x, r->max.y}
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
Rect2 CutRight(Rect2 *r, float value) {
|
||||
float maxx = r->max.x;
|
||||
r->max.x = Max(r->min.x, r->max.x - value);
|
||||
Rect2 result = {
|
||||
{r->max.x, r->min.y},
|
||||
{ maxx, r->max.y},
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
// Y is up
|
||||
Rect2 CutBottom(Rect2 *r, float value) {
|
||||
float maxy = r->max.y;
|
||||
r->max.y = Max(r->min.y, r->max.y - value);
|
||||
Rect2 result = {
|
||||
{r->min.x, r->max.y},
|
||||
{r->max.x, maxy},
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
// Y is up
|
||||
Rect2 CutTop(Rect2 *r, float value) {
|
||||
float miny = r->min.y;
|
||||
r->min.y = Min(r->min.y + value, r->max.y);
|
||||
Rect2 result = {
|
||||
{r->min.x, miny},
|
||||
{r->max.x, r->min.y},
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
float SafeDivide(float a, float b) {
|
||||
if (b == 0.f) {
|
||||
return 0.f;
|
||||
}
|
||||
return a / b;
|
||||
}
|
||||
|
||||
bool AreOverlapping(Vec2 point, Rect2 rec) {
|
||||
bool result = (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y);
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
Rect2I operator-(Rect2I r, Rect2I value) { return { {r.min.x - value.min.x, r.min.y - value.min.y}, {r.max.x - value.max.x, r.max.y - value.max.y} }; }
|
||||
Rect2I operator+(Rect2I r, Rect2I value) { return { {r.min.x + value.min.x, r.min.y + value.min.y}, {r.max.x + value.max.x, r.max.y + value.max.y} }; }
|
||||
Rect2I operator*(Rect2I r, Rect2I value) { return { {r.min.x * value.min.x, r.min.y * value.min.y}, {r.max.x * value.max.x, r.max.y * value.max.y} }; }
|
||||
Rect2I operator/(Rect2I r, Rect2I value) { return { {r.min.x / value.min.x, r.min.y / value.min.y}, {r.max.x / value.max.x, r.max.y / value.max.y} }; }
|
||||
|
||||
Rect2I operator-(Rect2I r, Vec2I value) { return { {r.min.x - value.x, r.min.y - value.y}, {r.max.x - value.x, r.max.y - value.y} }; }
|
||||
Rect2I operator+(Rect2I r, Vec2I value) { return { {r.min.x + value.x, r.min.y + value.y}, {r.max.x + value.x, r.max.y + value.y} }; }
|
||||
Rect2I operator*(Rect2I r, Vec2I value) { return { {r.min.x * value.x, r.min.y * value.y}, {r.max.x * value.x, r.max.y * value.y} }; }
|
||||
Rect2I operator/(Rect2I r, Vec2I value) { return { {r.min.x / value.x, r.min.y / value.y}, {r.max.x / value.x, r.max.y / value.y} }; }
|
||||
Rect2I operator-(Vec2I value, Rect2I r) { return { {r.min.x - value.x, r.min.y - value.y}, {r.max.x - value.x, r.max.y - value.y} }; }
|
||||
Rect2I operator+(Vec2I value, Rect2I r) { return { {r.min.x + value.x, r.min.y + value.y}, {r.max.x + value.x, r.max.y + value.y} }; }
|
||||
Rect2I operator*(Vec2I value, Rect2I r) { return { {r.min.x * value.x, r.min.y * value.y}, {r.max.x * value.x, r.max.y * value.y} }; }
|
||||
Rect2I operator/(Vec2I value, Rect2I r) { return { {r.min.x / value.x, r.min.y / value.y}, {r.max.x / value.x, r.max.y / value.y} }; }
|
||||
|
||||
Rect2I operator-(Int value, Rect2I r) { return { {r.min.x - value, r.min.y - value}, {r.max.x - value, r.max.y - value} }; }
|
||||
Rect2I operator+(Int value, Rect2I r) { return { {r.min.x + value, r.min.y + value}, {r.max.x + value, r.max.y + value} }; }
|
||||
Rect2I operator*(Int value, Rect2I r) { return { {r.min.x * value, r.min.y * value}, {r.max.x * value, r.max.y * value} }; }
|
||||
Rect2I operator/(Int value, Rect2I r) { return { {r.min.x / value, r.min.y / value}, {r.max.x / value, r.max.y / value} }; }
|
||||
Rect2I operator-(Rect2I r, Int value) { return { {r.min.x - value, r.min.y - value}, {r.max.x - value, r.max.y - value} }; }
|
||||
Rect2I operator+(Rect2I r, Int value) { return { {r.min.x + value, r.min.y + value}, {r.max.x + value, r.max.y + value} }; }
|
||||
Rect2I operator*(Rect2I r, Int value) { return { {r.min.x * value, r.min.y * value}, {r.max.x * value, r.max.y * value} }; }
|
||||
Rect2I operator/(Rect2I r, Int value) { return { {r.min.x / value, r.min.y / value}, {r.max.x / value, r.max.y / value} }; }
|
||||
|
||||
Rect2I operator-=(Rect2I &r, Rect2I value) { r = r - value; return r; }
|
||||
Rect2I operator+=(Rect2I &r, Rect2I value) { r = r + value; return r; }
|
||||
Rect2I operator*=(Rect2I &r, Rect2I value) { r = r * value; return r; }
|
||||
Rect2I operator/=(Rect2I &r, Rect2I value) { r = r / value; return r; }
|
||||
Rect2I operator-=(Rect2I &r, Vec2I value) { r = r - value; return r; }
|
||||
Rect2I operator+=(Rect2I &r, Vec2I value) { r = r + value; return r; }
|
||||
Rect2I operator*=(Rect2I &r, Vec2I value) { r = r * value; return r; }
|
||||
Rect2I operator/=(Rect2I &r, Vec2I value) { r = r / value; return r; }
|
||||
Rect2I operator-=(Rect2I &r, Int value) { r = r - value; return r; }
|
||||
Rect2I operator+=(Rect2I &r, Int value) { r = r + value; return r; }
|
||||
Rect2I operator*=(Rect2I &r, Int value) { r = r * value; return r; }
|
||||
Rect2I operator/=(Rect2I &r, Int value) { r = r / value; return r; }
|
||||
|
||||
Vec2I operator-(Vec2I a, Vec2I b) { return {a.x - b.x, a.y - b.y}; }
|
||||
Vec2I operator+(Vec2I a, Vec2I b) { return {a.x + b.x, a.y + b.y}; }
|
||||
Vec2I operator*(Vec2I a, Vec2I b) { return {a.x * b.x, a.y * b.y}; }
|
||||
Vec2I operator/(Vec2I a, Vec2I b) { return {a.x / b.x, a.y / b.y}; }
|
||||
|
||||
Vec2I operator-(Vec2I a, Int b) { return {a.x - b, a.y - b}; }
|
||||
Vec2I operator+(Vec2I a, Int b) { return {a.x + b, a.y + b}; }
|
||||
Vec2I operator*(Vec2I a, Int b) { return {a.x * b, a.y * b}; }
|
||||
Vec2I operator/(Vec2I a, Int b) { return {a.x / b, a.y / b}; }
|
||||
Vec2I operator-(Int b, Vec2I a) { return {a.x - b, a.y - b}; }
|
||||
Vec2I operator+(Int b, Vec2I a) { return {a.x + b, a.y + b}; }
|
||||
Vec2I operator*(Int b, Vec2I a) { return {a.x * b, a.y * b}; }
|
||||
Vec2I operator/(Int b, Vec2I a) { return {a.x / b, a.y / b}; }
|
||||
|
||||
Vec2I operator-=(Vec2I &a, Vec2I b) { a = a - b; return a; }
|
||||
Vec2I operator+=(Vec2I &a, Vec2I b) { a = a + b; return a; }
|
||||
Vec2I operator*=(Vec2I &a, Vec2I b) { a = a * b; return a; }
|
||||
Vec2I operator/=(Vec2I &a, Vec2I b) { a = a / b; return a; }
|
||||
Vec2I operator-=(Vec2I &a, Int b) { a = a - b; return a; }
|
||||
Vec2I operator+=(Vec2I &a, Int b) { a = a + b; return a; }
|
||||
Vec2I operator*=(Vec2I &a, Int b) { a = a * b; return a; }
|
||||
Vec2I operator/=(Vec2I &a, Int b) { a = a / b; return a; }
|
||||
|
||||
Vec2I GetSize(Rect2I r) {
|
||||
Vec2I result = {r.max.x - r.min.x, r.max.y - r.min.y};
|
||||
return result;
|
||||
}
|
||||
|
||||
Rect2I RectI0Size(Int x, Int y) {
|
||||
Rect2I rect = {0, 0, x, y};
|
||||
return rect;
|
||||
}
|
||||
|
||||
Rect2I Rect2IFromSize(Vec2I pos, Vec2I size) {
|
||||
Rect2I result = {};
|
||||
result.min = pos;
|
||||
result.max = pos + size;
|
||||
return result;
|
||||
}
|
||||
|
||||
Rect2I Shrink(Rect2I result, Int v) {
|
||||
result.min.x += v;
|
||||
result.max.x -= v;
|
||||
result.min.y += v;
|
||||
result.max.y -= v;
|
||||
return result;
|
||||
}
|
||||
|
||||
Vec2I GetMid(Rect2I r) {
|
||||
Vec2I size = GetSize(r);
|
||||
size.x /= 2;
|
||||
size.y /= 2;
|
||||
Vec2I result = r.min + size;
|
||||
return result;
|
||||
}
|
||||
|
||||
Rect2I CutLeft(Rect2I *r, Int value) {
|
||||
Int minx = r->min.x;
|
||||
r->min.x = Min(r->min.x + value, r->max.x);
|
||||
Rect2I result = {
|
||||
{ minx, r->min.y},
|
||||
{r->min.x, r->max.y}
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
Rect2I CutRight(Rect2I *r, Int value) {
|
||||
Int maxx = r->max.x;
|
||||
r->max.x = Max(r->min.x, r->max.x - value);
|
||||
Rect2I result = {
|
||||
{r->max.x, r->min.y},
|
||||
{ maxx, r->max.y},
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
// Y is up
|
||||
Rect2I CutBottom(Rect2I *r, Int value) {
|
||||
Int maxy = r->max.y;
|
||||
r->max.y = Max(r->min.y, r->max.y - value);
|
||||
Rect2I result = {
|
||||
{r->min.x, r->max.y},
|
||||
{r->max.x, maxy},
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
// Y is up
|
||||
Rect2I CutTop(Rect2I *r, Int value) {
|
||||
Int miny = r->min.y;
|
||||
r->min.y = Min(r->min.y + value, r->max.y);
|
||||
Rect2I result = {
|
||||
{r->min.x, miny},
|
||||
{r->max.x, r->min.y},
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
Int SafeDivide(Int a, Int b) {
|
||||
if (b == 0) {
|
||||
return 0;
|
||||
}
|
||||
return a / b;
|
||||
}
|
||||
|
||||
bool AreOverlapping(Vec2I point, Rect2I rec) {
|
||||
bool result = (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y);
|
||||
return result;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,15 +0,0 @@
|
||||
#include "core_platform.c"
|
||||
#include "core_intrin.c"
|
||||
#include "core_unicode.c"
|
||||
#include "core_math.c"
|
||||
#include "core_arena.c"
|
||||
#include "stb_sprintf.c"
|
||||
#include "core_string.c"
|
||||
#include "core_string16.c"
|
||||
#include "core_log.c"
|
||||
#include "core_lexer.c"
|
||||
#include "core_type_info.c"
|
||||
#include "core_hash_table.c"
|
||||
#include "core_array.c"
|
||||
#include "core_math.gen.c"
|
||||
#include "core_ctx.c"
|
||||
@@ -1,14 +0,0 @@
|
||||
#include "core_basic.h"
|
||||
#include "core_unicode.h"
|
||||
#include "core_arena.h"
|
||||
#include "core_string.h"
|
||||
#include "core_string16.h"
|
||||
#include "core_math.h"
|
||||
#include "core_type_info.h"
|
||||
#include "core_lexer.h"
|
||||
#include "core_log.h"
|
||||
#include "core_intrin.h"
|
||||
#include "core_platform.h"
|
||||
#include "core_hash_table.h"
|
||||
#include "core_ctx.h"
|
||||
#include "core_array.h"
|
||||
@@ -1,202 +0,0 @@
|
||||
#include "core_arena.h"
|
||||
#include "core_platform.h"
|
||||
#include "core_intrin.h"
|
||||
#include "core_ctx.h"
|
||||
|
||||
#if PLATFORM_ADDRESS_SANITIZER
|
||||
#include <sanitizer/asan_interface.h>
|
||||
#endif
|
||||
|
||||
#if !defined(ASAN_POISON_MEMORY_REGION)
|
||||
#define MA_ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
|
||||
#define MA_ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
|
||||
#else
|
||||
#define MA_ASAN_POISON_MEMORY_REGION(addr, size) ASAN_POISON_MEMORY_REGION(addr, size)
|
||||
#define MA_ASAN_UNPOISON_MEMORY_REGION(addr, size) ASAN_UNPOISON_MEMORY_REGION(addr, size)
|
||||
#endif
|
||||
|
||||
fn b32 is_pow2(usize x) {
|
||||
b32 result = (((x) & ((x)-1)) == 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn usize get_align_offset(usize size, usize align) {
|
||||
assert(is_pow2(align));
|
||||
if (align == 0) return 0;
|
||||
|
||||
usize mask = align - 1;
|
||||
usize val = size & mask;
|
||||
if (val) {
|
||||
val = align - val;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
fn usize align_up(usize size, usize align) {
|
||||
usize result = size + get_align_offset(size, align);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn usize align_down(usize size, usize align) {
|
||||
size += 1; // Make sure when align is 8 doesn't get rounded down to 0
|
||||
usize result = size - (align - get_align_offset(size, align));
|
||||
return result;
|
||||
}
|
||||
|
||||
fn void ma_init(ma_arena_t *arena, usize reserve) {
|
||||
reserve = align_up(reserve, ma_page_size);
|
||||
arena->align = ma_default_alignment;
|
||||
arena->data = (u8 *)os_vmem_reserve(reserve);
|
||||
if (arena->data) {
|
||||
arena->reserve = reserve;
|
||||
}
|
||||
}
|
||||
|
||||
fn ma_arena_t *ma_create(usize reserve) {
|
||||
ma_arena_t *result = NULL;
|
||||
|
||||
void *data = os_vmem_reserve(reserve);
|
||||
if (!data) return result;
|
||||
|
||||
b32 success = os_vmem_commit(data, ma_page_size);
|
||||
if (!success) {
|
||||
os_vmem_release(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = (ma_arena_t *)data;
|
||||
result->data = (u8 *)data;
|
||||
result->reserve = reserve;
|
||||
result->commit = ma_page_size;
|
||||
result->len = result->base_len = sizeof(ma_arena_t);
|
||||
result->align = ma_default_alignment;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn void *ma_push_size_ex(ma_arena_t *arena, usize size) {
|
||||
// base_len is used for bootstraping arenas, it denotes the
|
||||
// space occupied by the arena. If len is smaller then base_len then
|
||||
// we start to overwrite the arena itself - pure barbarism.
|
||||
assert(arena->len >= arena->base_len);
|
||||
|
||||
usize align_offset = 0;
|
||||
if (arena->align) {
|
||||
align_offset = get_align_offset((usize)arena->data + arena->len, arena->align);
|
||||
}
|
||||
usize size_with_alignment = size + align_offset;
|
||||
usize new_len = arena->len + size_with_alignment;
|
||||
if (new_len > arena->commit) {
|
||||
if (arena->reserve == 0) {
|
||||
ma_init(arena, ma_default_reserve_size);
|
||||
}
|
||||
usize new_len_aligned_to_page_size = align_up(new_len, ma_page_size);
|
||||
usize to_commit = new_len_aligned_to_page_size - arena->commit;
|
||||
usize to_commit_clamped = CLAMP_TOP(to_commit, arena->reserve);
|
||||
if (to_commit_clamped > 0) {
|
||||
b32 success = os_vmem_commit(arena->data + arena->commit, to_commit_clamped);
|
||||
if (success) {
|
||||
MA_ASAN_UNPOISON_MEMORY_REGION(arena->data + arena->commit, to_commit_clamped);
|
||||
arena->commit += to_commit_clamped;
|
||||
}
|
||||
}
|
||||
if (new_len > arena->commit) {
|
||||
invalid_codepath;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
u8 *result = arena->data + arena->len + align_offset;
|
||||
arena->len = new_len;
|
||||
MA_ASAN_UNPOISON_MEMORY_REGION(result, size);
|
||||
return (void *)result;
|
||||
}
|
||||
|
||||
fn void *ma_push_size(ma_arena_t *arena, usize size) {
|
||||
void *result = ma_push_size_ex(arena, size);
|
||||
if (result) memory_zero(result, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn void ma_push_arena_ex(ma_arena_t *allocator, ma_arena_t *result, usize size) {
|
||||
result->data = (u8 *)ma_push_size(allocator, size);
|
||||
result->reserve = size;
|
||||
result->commit = size;
|
||||
result->align = ma_default_alignment;
|
||||
}
|
||||
|
||||
fn ma_arena_t *ma_push_arena(ma_arena_t *allocator, usize size) {
|
||||
ma_arena_t *result = ma_push_type(allocator, ma_arena_t);
|
||||
ma_push_arena_ex(allocator, result, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn void ma_destroy(ma_arena_t *arena) {
|
||||
if (arena == NULL || arena->data == NULL) return;
|
||||
b32 zero_memory = (u8 *)arena != arena->data;
|
||||
os_vmem_release(arena->data);
|
||||
if (zero_memory) memory_zero(arena, sizeof(ma_arena_t));
|
||||
}
|
||||
|
||||
fn void ma_set_len(ma_arena_t *arena, usize pos) {
|
||||
// base_len is used for bootstraping arenas, it denotes the
|
||||
// space occupied by the arena. If len is smaller then base_len then
|
||||
// we start to overwrite the arena itself - pure barbarism.
|
||||
assert(arena->len >= arena->base_len);
|
||||
|
||||
pos = CLAMP(pos, arena->base_len, arena->len);
|
||||
usize size = arena->len - pos;
|
||||
arena->len = pos;
|
||||
MA_ASAN_POISON_MEMORY_REGION(arena->data + arena->len, size);
|
||||
}
|
||||
|
||||
fn void ma_pop(ma_arena_t *arena, usize size) { ma_set_len(arena, arena->len - size); }
|
||||
fn ma_temp_t ma_begin_temp(ma_arena_t *arena) { return (ma_temp_t){arena, arena->len}; }
|
||||
fn void ma_end_temp(ma_temp_t temp) { ma_set_len(temp.arena, temp.len); }
|
||||
fn void ma_set0(ma_arena_t *arena) { ma_set_len(arena, 0); }
|
||||
|
||||
//
|
||||
// Scratch arena
|
||||
fn ma_temp_t ma_begin_scratch_ex(ma_arena_t **conflicts, int conflict_count) {
|
||||
ma_arena_t *unoccupied = 0;
|
||||
for (int i = 0; i < lengthof(tcx->scratch); i += 1) {
|
||||
ma_arena_t *from_pool = tcx->scratch + i;
|
||||
unoccupied = from_pool;
|
||||
for (int conflict_i = 0; conflict_i < conflict_count; conflict_i += 1) {
|
||||
ma_arena_t *from_conflict = conflicts[conflict_i];
|
||||
if (from_pool == from_conflict) {
|
||||
unoccupied = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (unoccupied) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(unoccupied); // failed to get free scratch memory, this is a fatal error, this shouldnt happen
|
||||
ma_temp_t result = ma_begin_temp(unoccupied);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn ma_temp_t ma_begin_scratch(void) {
|
||||
ma_temp_t result = ma_begin_temp(tcx->scratch + 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn ma_temp_t ma_begin_scratch1(ma_arena_t *conflict) {
|
||||
ma_arena_t *conflicts[] = {conflict};
|
||||
return ma_begin_scratch_ex(conflicts, 1);
|
||||
}
|
||||
|
||||
fn ma_temp_t ma_begin_scratch2(ma_arena_t *a, ma_arena_t *b) {
|
||||
ma_arena_t *conflicts[] = {a, b};
|
||||
return ma_begin_scratch_ex(conflicts, 2);
|
||||
}
|
||||
|
||||
fn void *ma_push__copy(ma_arena_t *arena, void *data, u64 size) {
|
||||
void *result = ma_push_size_ex(arena, size);
|
||||
if (result) {
|
||||
memory_copy(result, data, size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
#pragma once
|
||||
#include "core_basic.h"
|
||||
|
||||
static const usize ma_page_size = 4096;
|
||||
static const usize ma_default_alignment = sizeof(void *);
|
||||
static const usize ma_default_reserve_size = mib(256);
|
||||
|
||||
typedef struct ma_arena_t ma_arena_t;
|
||||
struct ma_arena_t {
|
||||
u8 *data;
|
||||
usize len;
|
||||
usize base_len; // to prevent self deleting the arena
|
||||
usize reserve;
|
||||
usize commit;
|
||||
usize align;
|
||||
};
|
||||
|
||||
typedef struct ma_temp_t ma_temp_t;
|
||||
struct ma_temp_t {
|
||||
ma_arena_t *arena;
|
||||
usize len;
|
||||
};
|
||||
|
||||
//
|
||||
// constructors
|
||||
fn void ma_init(ma_arena_t *arena, usize reserve);
|
||||
fn ma_arena_t *ma_create(usize reserve);
|
||||
fn void ma_destroy(ma_arena_t *arena);
|
||||
|
||||
//
|
||||
// push
|
||||
fn void *ma_push_size(ma_arena_t *arena, usize size);
|
||||
fn void *ma_push_size_ex(ma_arena_t *arena, usize size);
|
||||
fn ma_arena_t *ma_push_arena(ma_arena_t *allocator, usize size);
|
||||
fn void ma_push_arena_ex(ma_arena_t *allocator, ma_arena_t *result, usize size);
|
||||
#define ma_push_type(arena, Type) (Type *)ma_push_size((arena), sizeof(Type))
|
||||
#define ma_push_array(arena, Type, count) (Type *)ma_push_size((arena), sizeof(Type) * (count))
|
||||
#define ma_push_type_copy(ma, data) ma_push__copy((ma), (data), sizeof(*(data)))
|
||||
#define ma_push_array_copy(ma, data, count) ma_push__copy((ma), (data), (count) * sizeof(*(data)))
|
||||
|
||||
//
|
||||
// pop
|
||||
fn void ma_set_len(ma_arena_t *arena, usize pos);
|
||||
fn void ma_pop(ma_arena_t *arena, usize size);
|
||||
fn void ma_set0(ma_arena_t *arena);
|
||||
|
||||
//
|
||||
// temp
|
||||
fn ma_temp_t ma_begin_temp(ma_arena_t *arena);
|
||||
fn void ma_end_temp(ma_temp_t temp);
|
||||
#define ma_temp_scope(name, InArena) for (ma_temp_t name = ma_begin_temp(InArena); name.arena; (ma_end_temp(name), name.arena = 0))
|
||||
|
||||
//
|
||||
// scratch
|
||||
fn ma_temp_t ma_begin_scratch_ex(ma_arena_t **conflicts, int conflict_count);
|
||||
fn ma_temp_t ma_begin_scratch(void);
|
||||
fn ma_temp_t ma_begin_scratch1(ma_arena_t *conflict);
|
||||
#define ma_end_scratch(x) ma_end_temp(x)
|
||||
#define ma_scratch_scope(x) for (ma_temp_t x = ma_begin_scratch(); x.arena; (ma_end_temp(x), x.arena = 0))
|
||||
@@ -1,122 +0,0 @@
|
||||
#include "core_array.h"
|
||||
#include "core_basic.h"
|
||||
#include "core_platform.h"
|
||||
#include "core_ctx.h"
|
||||
#include "core_intrin.h"
|
||||
|
||||
fn void array__init(alo_t alo, array_void_t *this, size_t item_size, i64 count) {
|
||||
assert_expr(this->data == NULL);
|
||||
this->data = alloc_size(alo, item_size * count);
|
||||
this->cap = count;
|
||||
this->len = 0;
|
||||
this->alo = alo;
|
||||
}
|
||||
|
||||
fn void array__copy(alo_t alo, array_void_t *dst, array_void_t *src, size_t item_size) {
|
||||
dst->data = alloc_size(alo, item_size * src->cap);
|
||||
memory_copy(dst->data, src->data, item_size * src->len);
|
||||
dst->cap = src->cap;
|
||||
dst->len = src->len;
|
||||
dst->alo = alo;
|
||||
}
|
||||
|
||||
fn void array__grow(array_void_t *this, size_t item_size, i64 item_count) {
|
||||
if (this->len + 1 > this->cap) {
|
||||
i64 new_cap = this->cap == 0 ? 16 : (this->cap + item_count - 1) * 2;
|
||||
void *new_data = alloc_size(this->alo, new_cap * item_size);
|
||||
if (this->data) {
|
||||
memory_copy(new_data, this->data, this->len*item_size);
|
||||
dealloc(this->alo, this->data);
|
||||
}
|
||||
this->data = new_data;
|
||||
this->cap = new_cap;
|
||||
}
|
||||
}
|
||||
|
||||
fn void array__del(array_void_t *this, size_t item_size, i64 idx, i64 count) {
|
||||
if (count == 0) return;
|
||||
assert(idx >= 0 && idx < this->len);
|
||||
assert((idx + count) > 0 && (idx + count) <= this->len);
|
||||
i64 right_len = this->len - idx - count;
|
||||
u8 *data = (u8 *)this->data;
|
||||
memory_move(data + idx * item_size, data + (idx + count) * item_size, right_len * item_size);
|
||||
this->len -= count;
|
||||
}
|
||||
|
||||
fn void array__insert(array_void_t *this, size_t item_size, i64 idx) {
|
||||
array__grow(this, item_size, 1);
|
||||
assert(idx >= 0);
|
||||
assert(idx <= this->len);
|
||||
assert(this->alo.object);
|
||||
|
||||
u8 *data = (u8 *)this->data;
|
||||
i64 right_len = this->len - idx;
|
||||
memory_move(data + (idx + 1) * item_size, data + idx * item_size, item_size * right_len);
|
||||
this->len += 1;
|
||||
}
|
||||
|
||||
fn_test void test_array(void) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
{
|
||||
array_i64_t arr = (array_i64_t){.alo = malot(scratch)};
|
||||
for (int i = 0; i < 512; i += 1) {
|
||||
array_add(&arr, i);
|
||||
}
|
||||
for (int i = 0; i < 512; i += 1) {
|
||||
assert(arr.data[i] == i);
|
||||
}
|
||||
assert(arr.len == 512);
|
||||
assert(arr.cap == 512);
|
||||
for (int i = 511; i >= 0; i -= 1) {
|
||||
i64 a = array_pop(&arr);
|
||||
assert(a == i);
|
||||
}
|
||||
assert(arr.len == 0);
|
||||
array_dealloc(&arr);
|
||||
}
|
||||
|
||||
{
|
||||
array_i64_t arr = (array_i64_t){.alo = malot(scratch)};
|
||||
i64 *i = array_addn(&arr, 10);
|
||||
assert(arr.len == 10 && arr.cap == 16);
|
||||
i64 *j = array_addn(&arr, 2);
|
||||
assert(i == arr.data);
|
||||
assert(j == arr.data + 10);
|
||||
array_dealloc(&arr);
|
||||
}
|
||||
|
||||
{
|
||||
array_i64_t arr = (array_i64_t){.alo = malot(scratch)};
|
||||
for (int i = 0; i < 512; i += 1) {
|
||||
array_add(&arr, i);
|
||||
}
|
||||
|
||||
array_insert(&arr, 256, 111);
|
||||
assert(arr.data[256] == 111);
|
||||
assert(arr.data[255] == 255);
|
||||
assert(arr.data[257] == 256);
|
||||
assert(arr.len == 513);
|
||||
|
||||
array_insert(&arr, 4, 222);
|
||||
assert(arr.len == 514);
|
||||
assert(arr.data[4] == 222);
|
||||
assert(arr.data[3] == 3);
|
||||
assert(arr.data[5] == 4);
|
||||
|
||||
array_swapdel(&arr, 2);
|
||||
assert(arr.len == 513);
|
||||
assert(arr.data[2] == 511);
|
||||
assert(arr.data[1] == 1);
|
||||
assert(arr.data[3] == 3);
|
||||
|
||||
array_i64_t copy = {0};
|
||||
array_copy(malot(scratch), ©, &arr);
|
||||
for (i64 i = 0; i < arr.len; i += 1) {
|
||||
assert(arr.data[i] == copy.data[i]);
|
||||
}
|
||||
assert(copy.len == arr.len);
|
||||
|
||||
}
|
||||
|
||||
ma_end_scratch(scratch);
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
#include "core_basic.h"
|
||||
|
||||
#define array(type) struct { alo_t alo; type *data; i64 len; i64 cap; }
|
||||
typedef array(void) array_void_t;
|
||||
typedef array(i64) array_i64_t;
|
||||
|
||||
#define arrisz(x) sizeof((x)->data[0])
|
||||
#define arrcst(x) ((array_void_t *)(x))
|
||||
#define array_init(a, this, count) (array__init((a), arrcst(this), arrisz(this), (count)))
|
||||
#define array_add(this, ...) (array__grow(arrcst(this), arrisz(this), 1), (this)->data[(this)->len++] = __VA_ARGS__)
|
||||
#define array_pop(this) (assert_expr((this)->len > 0), (this)->data[--(this)->len])
|
||||
#define array_dealloc(this) (dealloc((this)->alo, (this)->data))
|
||||
#define array_addn(this, n) (array__grow(arrcst(this), arrisz(this), (n)), (this)->len += (n), &(this)->data[(this)->len - (n)])
|
||||
#define array_insert(this, i, item) (array__insert(arrcst(this), arrisz(this), (i)), (this)->data[(i)] = (item))
|
||||
#define array_swapdel(this, i) (assert_expr((i) < (this)->len), (this)->data[(i)] = (this)->data[--(this)->len])
|
||||
#define array_del(this, i) (array__del(arrcst(this), arrisz(this), (i), 1))
|
||||
#define array_deln(this, i, count) (array__del(arrcst(this), arrisz(this), (i), (count)))
|
||||
#define array_copy(alo, dst, src) (array__copy((alo), arrcst(dst), (array_void_t *)(src), arrisz(dst)))
|
||||
#define array_last(x) ((x)->data[(x)->len - 1])
|
||||
#define array_for(type, it, array) for (type *it = (array)->data; it < (array)->data + (array)->len; it += 1)
|
||||
|
||||
fn void array__init(alo_t alo, array_void_t *, size_t item_size, i64 count);
|
||||
fn void array__copy(alo_t alo, array_void_t *dst, array_void_t *src, size_t item_size);
|
||||
fn void array__grow(array_void_t *, size_t item_size, i64 item_count);
|
||||
fn void array__del(array_void_t *, size_t item_size, i64 idx, i64 count);
|
||||
fn void array__insert(array_void_t *, size_t item_size, i64 idx);
|
||||
@@ -1,427 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#define PLATFORM_MAC_OS 1
|
||||
#define PLATFORM_POSIX 1
|
||||
#elif defined(_WIN32)
|
||||
#define PLATFORM_WINDOWS 1
|
||||
#elif defined(__linux__)
|
||||
#define PLATFORM_POSIX 1
|
||||
#define PLATFORM_LINUX 1
|
||||
#elif defined(__wasm)
|
||||
#define PLATFORM_WASM 1
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
#define PLATFORM_CLANG 1
|
||||
#elif defined(__GNUC__) || defined(__GNUG__)
|
||||
#define PLATFORM_GCC 1
|
||||
#elif defined(_MSC_VER)
|
||||
#define PLATFORM_CL 1
|
||||
#elif defined(__TINYC__)
|
||||
#define PLATFORM_TCC 1
|
||||
#endif
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
#define PLATFORM_EMSCRIPTEN 1
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_EMSCRIPTEN
|
||||
#define PLATFORM_EMSCRIPTEN 0
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_WASM
|
||||
#define PLATFORM_WASM 0
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_WINDOWS
|
||||
#define PLATFORM_WINDOWS 0
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_LINUX
|
||||
#define PLATFORM_LINUX 0
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_POSIX
|
||||
#define PLATFORM_POSIX 0
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_MAC_OS
|
||||
#define PLATFORM_MAC_OS 0
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_CLANG
|
||||
#define PLATFORM_CLANG 0
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_GCC
|
||||
#define PLATFORM_GCC 0
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_CL
|
||||
#define PLATFORM_CL 0
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_TCC
|
||||
#define PLATFORM_TCC 0
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_ADDRESS_SANITIZER
|
||||
#define PLATFORM_ADDRESS_SANITIZER 0
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_DEBUG_ASSERT
|
||||
#define PLATFORM_DEBUG_ASSERT 1
|
||||
#endif
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#define SWITCH_PLATFORM_WINDOWS_WASM_ELSE(WINDOWS, WASM, ELSE) WINDOWS
|
||||
#elif PLATFORM_WASM
|
||||
#define SWITCH_PLATFORM_WINDOWS_WASM_ELSE(WINDOWS, WASM, ELSE) WASM
|
||||
#else
|
||||
#define SWITCH_PLATFORM_WINDOWS_WASM_ELSE(WINDOWS, WASM, ELSE) ELSE
|
||||
#endif
|
||||
|
||||
#if PLATFORM_CLANG
|
||||
#define IF_PLATFORM_CLANG_ELSE(IF, ELSE) IF
|
||||
#else
|
||||
#define IF_PLATFORM_CLANG_ELSE(IF, ELSE) ELSE
|
||||
#endif
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#define IF_PLATFORM_WINDOWS(x) x
|
||||
#else
|
||||
#define IF_PLATFORM_WINDOWS(x)
|
||||
#endif
|
||||
|
||||
#if PLATFORM_LINUX && !defined(_GNU_SOURCE)
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
typedef uintptr_t usize;
|
||||
typedef uint64_t u64;
|
||||
typedef uint32_t u32;
|
||||
typedef uint16_t u16;
|
||||
typedef uint8_t u8;
|
||||
|
||||
typedef intptr_t isize;
|
||||
typedef int64_t i64;
|
||||
typedef int32_t i32;
|
||||
typedef int16_t i16;
|
||||
typedef int8_t i8;
|
||||
|
||||
typedef int64_t b64;
|
||||
typedef int32_t b32;
|
||||
typedef int16_t b16;
|
||||
typedef int8_t b8;
|
||||
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
|
||||
#define f32_max FLT_MAX
|
||||
#define f32_min FLT_MIN
|
||||
#define f64_max DBL_MAX
|
||||
#define f64_min DBL_MIN
|
||||
|
||||
#define i8_max INT8_MAX
|
||||
#define i8_min INT8_MIN
|
||||
#define i16_max INT16_MAX
|
||||
#define i16_min INT16_MIN
|
||||
#define i32_max INT32_MAX
|
||||
#define i32_min INT32_MIN
|
||||
#define i64_max INT64_MAX
|
||||
#define i64_min INT64_MIN
|
||||
|
||||
#define u8_max UINT8_MAX
|
||||
#define u8_min 0
|
||||
#define u16_max UINT16_MAX
|
||||
#define u16_min 0
|
||||
#define u32_max UINT32_MAX
|
||||
#define u32_min 0
|
||||
#define u64_max UINT64_MAX
|
||||
#define u64_min 0
|
||||
|
||||
#ifndef true
|
||||
#define true 1
|
||||
#endif
|
||||
#ifndef false
|
||||
#define false 0
|
||||
#endif
|
||||
|
||||
#define fn
|
||||
#define fn_test
|
||||
#define gb
|
||||
#define locl static
|
||||
|
||||
typedef union convert_f64_u64_t convert_f64_u64_t;
|
||||
union convert_f64_u64_t { f64 f; u64 i; };
|
||||
typedef union convert_f64_i64_t convert_f64_i64_t;
|
||||
union convert_f64_i64_t { f64 f; i64 i; };
|
||||
typedef union convert_f32_u32_t convert_f32_u32_t;
|
||||
union convert_f32_u32_t { f32 f; u32 i; };
|
||||
typedef union convert_f32_i32_t convert_f32_i32_t;
|
||||
union convert_f32_i32_t { f32 f; i32 i; };
|
||||
|
||||
#define U64_TO_F64(x) (((convert_f64_u64_t) { .i = (x) }).f)
|
||||
#define U32_TO_F32(x) (((convert_f32_u32_t) { .i = (x) }).f)
|
||||
#define F64_TO_U64(x) (((convert_f64_u64_t) { .f = (x) }).i)
|
||||
#define F32_TO_U32(x) (((convert_f32_u32_t) { .f = (x) }).i)
|
||||
|
||||
#define I64_TO_F64(x) (((convert_f64_i64_t) { .i = (x) }).f)
|
||||
#define I32_TO_F32(x) (((convert_f32_i32_t) { .i = (x) }).f)
|
||||
#define F64_TO_I64(x) (((convert_f64_i64_t) { .f = (x) }).i)
|
||||
#define F32_TO_I32(x) (((convert_f32_i32_t) { .f = (x) }).i)
|
||||
|
||||
#define MIN(x,y) ((x) > (y) ? (y) : (x))
|
||||
#define MAX(x,y) ((x) > (y) ? (x) : (y))
|
||||
|
||||
#define CLAMP_TOP(A,X) MIN(A,X)
|
||||
#define CLAMP_BOT(X,B) MAX(X,B)
|
||||
#define CLAMP(x,a,b) (((x)<(a))?(a):((x)>(b))?(b):(x))
|
||||
|
||||
#define set_bit(x) (1ULL << (x))
|
||||
#define lengthof(x) ((int)(sizeof((x))/sizeof((x)[0])))
|
||||
#ifndef offsetof
|
||||
#define offsetof(st, m) ((usize)&(((st *)0)->m))
|
||||
#endif
|
||||
#define expect(x) if (!(x))
|
||||
#define unused(x) (void)(x)
|
||||
|
||||
#define kib(x) (1024ULL * (x##ULL))
|
||||
#define mib(x) (1024ULL * kib(x))
|
||||
#define gib(x) (1024ULL * mib(x))
|
||||
#define thousand(n) ((n)*1000)
|
||||
#define million(n) ((n)*1000000)
|
||||
#define billion(n) ((n)*1000000000)
|
||||
|
||||
#define zero_struct(x) memory_zero((x), sizeof(*(x)))
|
||||
|
||||
|
||||
#define defer_if(begin, cond_end) for (b32 PASTE(_i_, __LINE__) = !!begin; PASTE(_i_, __LINE__); PASTE(_i_, __LINE__) = (cond_end, 0))
|
||||
#define defer_block(begin, end) for (i32 PASTE(_i_, __LINE__) = (begin, 0); !PASTE(_i_, __LINE__); PASTE(_i_, __LINE__) += (end, 1))
|
||||
|
||||
#define stack_t(type, size) struct { type data[size]; i32 len; }
|
||||
#define STACK_CAP(stack) (lengthof((stack).data))
|
||||
#define STACK_EMPTY(stack) ((stack).len == 0)
|
||||
#define STACK_FULL(stack) ((stack).len == STACK_CAP(stack))
|
||||
#define STACK_PUSH(stack, ...) (assert_expr(!STACK_FULL(stack)), (stack).data[(stack).len++] = __VA_ARGS__)
|
||||
#define STACK_POP(stack) (assert_expr(!STACK_EMPTY(stack)), (stack).data[--(stack).len])
|
||||
#define STACK_TOP(stack) (assert_expr(!STACK_EMPTY(stack)), (stack).data[((stack).len-1)])
|
||||
|
||||
#define STRINGIFY_(S) #S
|
||||
#define STRINGIFY(S) STRINGIFY_(S)
|
||||
#define PASTE_(a, b) a##b
|
||||
#define PASTE(a, b) PASTE_(a, b)
|
||||
#define SWAP(t, a, b) do { t PASTE(temp__, __LINE__) = a; a = b; b = PASTE(temp__, __LINE__); } while(0)
|
||||
#define SWAP_PTR(t, a, b) do { t PASTE(temp__, __LINE__) = *a; *a = *b; *b = PASTE(temp__, __LINE__); } while(0)
|
||||
#define CODE(...) #__VA_ARGS__
|
||||
|
||||
#if PLATFORM_CL || (PLATFORM_CLANG && PLATFORM_WINDOWS)
|
||||
#pragma section(".rdata$", read)
|
||||
#define gb_read_only __declspec(allocate(".rdata$"))
|
||||
#elif PLATFORM_CLANG && PLATFORM_LINUX
|
||||
#define gb_read_only __attribute__((section(".rodata")))
|
||||
#else
|
||||
#define gb_read_only
|
||||
#endif
|
||||
|
||||
#if PLATFORM_CL
|
||||
#define fn_inline __forceinline
|
||||
#elif PLATFORM_CLANG || PLATFORM_GCC
|
||||
#define fn_inline __attribute__((always_inline))
|
||||
#else
|
||||
#define fn_inline
|
||||
#endif
|
||||
|
||||
#ifndef FILE_AND_LINE_GCC_FORMAT
|
||||
#define FILE_AND_LINE __FILE__ "(" STRINGIFY(__LINE__) ")"
|
||||
#else
|
||||
#define FILE_AND_LINE __FILE__ ":" STRINGIFY(__LINE__)
|
||||
#endif
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
int IsDebuggerPresent(void);
|
||||
#define debug__break() (IsDebuggerPresent() && (__debugbreak(), 1))
|
||||
#elif PLATFORM_UNIX
|
||||
// https://github.com/r-lyeh/tinybits/blob/master/tinyassert.c
|
||||
#include <signal.h>
|
||||
static void os_unix_sigtrap(int signum) { signal(SIGTRAP, SIG_DFL); }
|
||||
#define debug__break() (signal(SIGTRAP, os_unix_sigtrap), raise(SIGTRAP))
|
||||
#elif PLATFORM_WASM
|
||||
void wasm_trap(void);
|
||||
#define debug__break() wasm_trap()
|
||||
#elif PLATFORM_TCC
|
||||
#define debug__break() raise(SIGTRAP)
|
||||
#else
|
||||
#define debug__break() __builtin_trap()
|
||||
#endif
|
||||
#define debug_break() (debug__break(), 1)
|
||||
|
||||
#if PLATFORM_WASM | PLATFORM_TCC
|
||||
#define gb_thread
|
||||
#elif PLATFORM_GCC | PLATFORM_CLANG
|
||||
#define gb_thread __thread
|
||||
#elif PLATFORM_CL
|
||||
#define gb_thread __declspec(thread)
|
||||
#else
|
||||
#define gb_thread _Thread_local
|
||||
#endif
|
||||
|
||||
#if PLATFORM_CL
|
||||
#pragma warning(disable: 4116)
|
||||
#endif
|
||||
|
||||
// Single linked list Queue
|
||||
#define SLLQ_APPEND_EX(f, l, n, next) \
|
||||
do { \
|
||||
assert_expr((n)->next == NULL); \
|
||||
if ((f) == 0) { \
|
||||
(f) = (l) = (n); \
|
||||
} else { \
|
||||
(l) = (l)->next = (n); \
|
||||
} \
|
||||
} while (0)
|
||||
#define SLLQ_APPEND(f, l, n) SLLQ_APPEND_EX(f, l, n, next)
|
||||
|
||||
#define SLLQ_PREPEND_EX(f, l, n, next) \
|
||||
do { \
|
||||
assert_expr((n)->next == NULL); \
|
||||
if ((f) == 0) { \
|
||||
(f) = (l) = (n); \
|
||||
} else { \
|
||||
(n)->next = (f); \
|
||||
(f) = (n); \
|
||||
} \
|
||||
} while (0)
|
||||
#define SLLQ_PREPEND(f, l, n) SLLQ_PREPEND_EX(f, l, n, next)
|
||||
|
||||
#define SLLQ_REMOVE_FIRST_EX(f, l, next) \
|
||||
do { \
|
||||
if ((f) == (l)) { \
|
||||
(f) = (l) = 0; \
|
||||
} else { \
|
||||
(f) = (f)->next; \
|
||||
} \
|
||||
} while (0)
|
||||
#define SLLQ_REMOVE_FIRST(f, l) SLLQ_REMOVE_FIRST_EX(f, l, next)
|
||||
|
||||
// Singly linked list stack
|
||||
#define SLLS_PUSH_EX(stack_base, new_stack_base, next) \
|
||||
do { \
|
||||
(new_stack_base)->next = (stack_base); \
|
||||
(stack_base) = (new_stack_base); \
|
||||
} while (0)
|
||||
#define SLLS_PUSH(stack_base, new_stack_base) \
|
||||
SLLS_PUSH_EX(stack_base, new_stack_base, next)
|
||||
|
||||
#define SLLS_POP(stack_base) ((stack_base) = (stack_base)->next)
|
||||
#define SLLS_POP_AND_STORE(stack_base, out_node) \
|
||||
do { \
|
||||
if (stack_base) { \
|
||||
(out_node) = (stack_base); \
|
||||
(stack_base) = (stack_base)->next; \
|
||||
(out_node)->next = 0; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// Doubly linked list Queue
|
||||
#define DLLQ_APPEND_EX(f, l, node, next, prev) \
|
||||
do { \
|
||||
assert_expr((node)->next == NULL); \
|
||||
assert_expr((node)->prev == NULL); \
|
||||
if ((f) == 0) { \
|
||||
(f) = (l) = (node); \
|
||||
} else { \
|
||||
(l)->next = (node); \
|
||||
(node)->prev = (l); \
|
||||
(l) = (node); \
|
||||
} \
|
||||
} while (0)
|
||||
#define DLLQ_APPEND(f, l, node) DLLQ_APPEND_EX(f, l, node, next, prev)
|
||||
|
||||
#define DLLQ_PREPEND_EX(f, l, node, next, prev) \
|
||||
do { \
|
||||
assert_expr((node)->next == NULL); \
|
||||
assert_expr((node)->prev == NULL); \
|
||||
if ((f) == 0) { \
|
||||
(f) = (l) = (node); \
|
||||
} else { \
|
||||
(node)->next = (f); \
|
||||
(f)->prev = (node); \
|
||||
(f) = (node); \
|
||||
} \
|
||||
} while (0)
|
||||
#define DLLQ_PREPEND(f, l, node) DLLQ_PREPEND_EX(f, l, node, next, prev)
|
||||
|
||||
#define DLLQ_CONTAINS(f, l, n, next, prev) for (
|
||||
|
||||
#define DLLQ_REMOVE_EX(first, last, node, next, prev) \
|
||||
do { \
|
||||
if ((first) == (last)) { \
|
||||
assert_expr((node) == (first)); \
|
||||
(first) = (last) = 0; \
|
||||
} else if ((last) == (node)) { \
|
||||
(last) = (last)->prev; \
|
||||
(last)->next = 0; \
|
||||
} else if ((first) == (node)) { \
|
||||
(first) = (first)->next; \
|
||||
(first)->prev = 0; \
|
||||
} else { \
|
||||
(node)->prev->next = (node)->next; \
|
||||
(node)->next->prev = (node)->prev; \
|
||||
} \
|
||||
if (node) { \
|
||||
(node)->prev = 0; \
|
||||
(node)->next = 0; \
|
||||
} \
|
||||
} while (0)
|
||||
#define DLLQ_REMOVE(first, last, node) DLLQ_REMOVE_EX(first, last, node, next, prev)
|
||||
|
||||
// Doubly linked list Stack
|
||||
#define DLLS_PUSH_EX(first, node, next, prev) \
|
||||
do { \
|
||||
assert_expr((node)->next == NULL); \
|
||||
assert_expr((node)->prev == NULL); \
|
||||
(node)->next = (first); \
|
||||
if ((first)) \
|
||||
(first)->prev = (node); \
|
||||
(first) = (node); \
|
||||
} while (0)
|
||||
#define DLLS_PUSH(first, node) DLLS_PUSH_EX(first, node, next, prev)
|
||||
#define DLLS_REMOVE_EX(first, node, next, prev) \
|
||||
do { \
|
||||
if ((node) == (first)) { \
|
||||
(first) = (first)->next; \
|
||||
if ((first)) \
|
||||
(first)->prev = 0; \
|
||||
} else { \
|
||||
(node)->prev->next = (node)->next; \
|
||||
if ((node)->next) \
|
||||
(node)->next->prev = (node)->prev; \
|
||||
} \
|
||||
if (node) { \
|
||||
(node)->prev = 0; \
|
||||
(node)->next = 0; \
|
||||
} \
|
||||
} while (0)
|
||||
#define DLLS_REMOVE(first, node) DLLS_REMOVE_EX(first, node, next, prev)
|
||||
|
||||
typedef enum {
|
||||
alokind_alloc,
|
||||
alokind_dealloc,
|
||||
} alokind_t;
|
||||
|
||||
typedef struct alo_t alo_t;
|
||||
struct alo_t {
|
||||
void *object;
|
||||
void *(*proc)(alo_t alo, alokind_t kind, void *ptr, size_t size);
|
||||
};
|
||||
@@ -1,30 +0,0 @@
|
||||
#include "core_ctx.h"
|
||||
#include "core_platform.h"
|
||||
|
||||
gb_thread thread_ctx_t *tcx;
|
||||
|
||||
fn void *ma_arena_alo_proc(alo_t alo, alokind_t kind, void *ptr, size_t size) {
|
||||
unused(ptr);
|
||||
if (kind == alokind_alloc) {
|
||||
return ma_push_size(alo.object, size);
|
||||
} else if (kind == alokind_dealloc) {
|
||||
return NULL;
|
||||
} else_is_invalid;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fn alo_t malo(ma_arena_t *arena) {
|
||||
return (alo_t){arena, ma_arena_alo_proc};
|
||||
}
|
||||
|
||||
fn alo_t malot(ma_temp_t temp) {
|
||||
return malo(temp.arena);
|
||||
}
|
||||
|
||||
fn void dealloc(alo_t alo, void *ptr) {
|
||||
alo.proc(alo, alokind_dealloc, ptr, 0);
|
||||
}
|
||||
|
||||
fn void *alloc_size(alo_t alo, size_t size) {
|
||||
return alo.proc(alo, alokind_alloc, NULL, size);
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
#pragma once
|
||||
#include "core_arena.h"
|
||||
#include "core_log.h"
|
||||
|
||||
typedef struct thread_ctx_t thread_ctx_t;
|
||||
struct thread_ctx_t {
|
||||
ma_arena_t scratch[3];
|
||||
ma_arena_t temp; // application specific arena
|
||||
|
||||
// I probably want to discourage using it implicitly like: tcx.perm, instead just pass it around
|
||||
// to functions that allocate pernament memory. temp and scratch very nicely create reusable code
|
||||
// perm is basically gb state so it would be nice to annotate which functions are reusable
|
||||
// and which functions have state
|
||||
ma_arena_t perm;
|
||||
|
||||
void *app_ctx;
|
||||
void *rn_ctx;
|
||||
void *ui_ctx;
|
||||
void *te_ctx;
|
||||
void *user_ctx;
|
||||
|
||||
logger_t log;
|
||||
int thread_index;
|
||||
};
|
||||
|
||||
#define alloc_type(alo, type) (type *)alloc_size((aloc), sizeof(type))
|
||||
#define alloc_array(alo, type, count) (type *)alloc_size((alo), sizeof(type) * (count))
|
||||
fn alo_t malo(ma_arena_t *arena);
|
||||
fn alo_t malot(ma_temp_t temp);
|
||||
fn void dealloc(alo_t alo, void *ptr);
|
||||
fn void *alloc_size(alo_t alo, size_t size);
|
||||
fn int tcx_alloc_id(void);
|
||||
|
||||
extern gb_thread thread_ctx_t *tcx;
|
||||
@@ -1,217 +0,0 @@
|
||||
#include "core_hash_table.h"
|
||||
#include "core_platform.h"
|
||||
|
||||
// xorshift64
|
||||
fn u64 get_random_u64(random_seed_t *seed) {
|
||||
u64 x = seed->a;
|
||||
x ^= x << 13;
|
||||
x ^= x >> 7;
|
||||
x ^= x << 17;
|
||||
return seed->a = x;
|
||||
}
|
||||
|
||||
fn u64 get_random_in_range_u64(random_seed_t *seed, u64 start, u64 one_past_end) {
|
||||
u64 r = get_random_u64(seed);
|
||||
u64 diff = one_past_end - start;
|
||||
u64 rr = r % diff;
|
||||
u64 rrr = rr + start;
|
||||
return rrr;
|
||||
}
|
||||
|
||||
fn i64 get_random_in_range_i64(random_seed_t *seed, i64 start, i64 one_past_end) {
|
||||
u64 r = get_random_u64(seed);
|
||||
i64 diff = one_past_end - start;
|
||||
u64 rr = r % diff;
|
||||
i64 rrr = (i64)rr + start;
|
||||
return rrr;
|
||||
}
|
||||
|
||||
fn f64 get_random_f64(random_seed_t *seed) {
|
||||
f64 random = (f64)get_random_in_range_u64(seed, 0, UINT32_MAX);
|
||||
f64 result = random / (f64)(UINT32_MAX - 1u);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn u64 hash_data(s8_t string) {
|
||||
u8 *data = (u8 *)string.str;
|
||||
u64 hash = (u64)14695981039346656037ULL;
|
||||
for (i64 i = 0; i < string.len; i++) {
|
||||
hash = hash ^ (u64)(data[i]);
|
||||
hash = hash * (u64)1099511628211ULL;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
fn u64 hash_mix(u64 x, u64 y) {
|
||||
x ^= y;
|
||||
x *= 0xff51afd7ed558ccd;
|
||||
x ^= x >> 32;
|
||||
return x;
|
||||
}
|
||||
|
||||
fn ht_t *ht_create(ma_arena_t *arena, i32 size) {
|
||||
ht_t *result = ma_push_type(arena, ht_t);
|
||||
result->buckets = ma_push_array(arena, ht_bucket_t, size);
|
||||
result->bucket_count = size;
|
||||
result->arena = arena;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn ht_node_t *ht_insert_kv(ht_t *ht, u64 hash, ht_key_value_t kv) {
|
||||
ht_node_t *result = ma_push_type(ht->arena, ht_node_t);
|
||||
result->kv = kv;
|
||||
i64 idx = hash % ht->bucket_count;
|
||||
SLLQ_APPEND(ht->buckets[idx].first, ht->buckets[idx].last, result);
|
||||
ht->item_count += 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn ht_node_t *ht_insert_u64(ht_t *ht, u64 key, u64 value) {
|
||||
u64 hash = hash_data(s8_struct(key));
|
||||
return ht_insert_kv(ht, hash, (ht_key_value_t){.key_u64 = key, .value_u64 = value});
|
||||
}
|
||||
|
||||
fn ht_node_t *ht_insert_ptr(ht_t *ht, void *key, void *value) {
|
||||
return ht_insert_u64(ht, (u64)key, (u64)value);
|
||||
}
|
||||
|
||||
fn ht_node_t *ht_insert_string(ht_t *ht, s8_t key, s8_t value) {
|
||||
u64 hash = hash_data(key);
|
||||
return ht_insert_kv(ht, hash, (ht_key_value_t){.key_string = key, .value_string = value});
|
||||
}
|
||||
|
||||
fn ht_node_t *ht_insert_string_ptr(ht_t *ht, s8_t key, void *value) {
|
||||
u64 hash = hash_data(key);
|
||||
return ht_insert_kv(ht, hash, (ht_key_value_t){.key_string = key, .value_ptr = value});
|
||||
}
|
||||
|
||||
fn ht_node_t *ht_search_u64_ex(ht_t *ht, u64 key) {
|
||||
u64 hash = hash_data(s8_struct(key));
|
||||
i64 idx = hash % ht->bucket_count;
|
||||
ht_bucket_t *bucket = ht->buckets + idx;
|
||||
for (ht_node_t *it = bucket->first; it; it = it->next) {
|
||||
if (it->kv.key_u64 == key) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fn ht_node_t *ht_search_ptr_ex(ht_t *ht, void *key) {
|
||||
u64 hash = hash_data(s8_struct(key));
|
||||
i64 idx = hash % ht->bucket_count;
|
||||
ht_bucket_t *bucket = ht->buckets + idx;
|
||||
for (ht_node_t *it = bucket->first; it; it = it->next) {
|
||||
if (it->kv.key_ptr == key) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fn ht_node_t *ht_search_string_ex(ht_t *ht, s8_t key) {
|
||||
u64 hash = hash_data(key);
|
||||
i64 idx = hash % ht->bucket_count;
|
||||
ht_bucket_t *bucket = ht->buckets + idx;
|
||||
for (ht_node_t *it = bucket->first; it; it = it->next) {
|
||||
if (s8_are_equal(it->kv.key_string, key)) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fn u64 *ht_search_u64(ht_t *ht, u64 key) {
|
||||
ht_node_t *node = ht_search_u64_ex(ht, key);
|
||||
if (node) return &node->kv.value_u64;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fn void **ht_search_ptr(ht_t *ht, void *key) {
|
||||
ht_node_t *node = ht_search_ptr_ex(ht, key);
|
||||
if (node) return &node->kv.value_ptr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fn s8_t *ht_search_string(ht_t *ht, s8_t key) {
|
||||
ht_node_t *node = ht_search_string_ex(ht, key);
|
||||
if (node) return &node->kv.value_string;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fn void **ht_search_string_ptr(ht_t *ht, s8_t key) {
|
||||
ht_node_t *node = ht_search_string_ex(ht, key);
|
||||
if (node) return &node->kv.value_ptr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
// string interning
|
||||
fn s8i_t *intern_string(ht_t *ht, s8_t string) {
|
||||
s8_t *item = ht_search_string(ht, string);
|
||||
if (!item) {
|
||||
string = s8_copy(ht->arena, string);
|
||||
ht_node_t *node = ht_insert_string(ht, string, string);
|
||||
item = &node->kv.value_string;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
fn s8i_t *internf(ht_t *ht, char *str, ...) {
|
||||
S8_FMT(ht->arena, str, string);
|
||||
return intern_string(ht, string);
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
// testing
|
||||
|
||||
fn_test void test_hash_table(void) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
{
|
||||
ht_t *ht = ht_create(scratch.arena, 16);
|
||||
|
||||
for (u64 i = 0; i < 128; i += 1) {
|
||||
ht_insert_u64(ht, i, i);
|
||||
ht_node_t *node = ht_search_u64_ex(ht, i);
|
||||
assert_expr(node->kv.value_u64 == i);
|
||||
assert(node->kv.key_u64 == i);
|
||||
}
|
||||
for (u64 i = 0; i < 128; i += 1) {
|
||||
ht_node_t *node = ht_search_u64_ex(ht, i);
|
||||
assert(node->kv.value_u64 == i);
|
||||
assert(node->kv.key_u64 == i);
|
||||
}
|
||||
|
||||
ht_node_t *node = ht_search_u64_ex(ht, 1111);
|
||||
assert(node == NULL);
|
||||
}
|
||||
{
|
||||
ht_t *ht = ht_create(scratch.arena, 16);
|
||||
|
||||
for (i32 i = 0; i < 128; i += 1) {
|
||||
s8_t s = s8_printf(scratch.arena, "%d", i);
|
||||
ht_insert_string(ht, s, s);
|
||||
ht_node_t *node = ht_search_string_ex(ht, s);
|
||||
assert(s8_are_equal(node->kv.value_string, s));
|
||||
assert(s8_are_equal(node->kv.key_string, s));
|
||||
}
|
||||
|
||||
ht_node_t *node = ht_search_string_ex(ht, s8("memes"));
|
||||
assert(node == NULL);
|
||||
}
|
||||
|
||||
ma_end_scratch(scratch);
|
||||
}
|
||||
|
||||
fn_test void test_intern_table(void) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
ht_t *ht = ht_create(scratch.arena, 4);
|
||||
assert(internf(ht, "asd") == internf(ht, "asd"));
|
||||
assert(internf(ht, "asdf") != internf(ht, "asd"));
|
||||
assert(internf(ht, "asdf") == internf(ht, "asdf"));
|
||||
assert(internf(ht, "123asdf") == internf(ht, "123asdf"));
|
||||
assert(internf(ht, "123asdf") != internf(ht, "133asdf"));
|
||||
assert(internf(ht, "") == internf(ht, ""));
|
||||
assert(internf(ht, "") != internf(ht, "a"));
|
||||
ma_end_scratch(scratch);
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
#pragma once
|
||||
#include "core_basic.h"
|
||||
#include "core_arena.h"
|
||||
#include "core_string.h"
|
||||
|
||||
typedef struct ht_key_value_t ht_key_value_t;
|
||||
struct ht_key_value_t {
|
||||
union {
|
||||
s8_t key_string;
|
||||
u64 key_u64;
|
||||
void *key_ptr;
|
||||
};
|
||||
union {
|
||||
s8_t value_string;
|
||||
void *value_ptr;
|
||||
u64 value_u64;
|
||||
};
|
||||
};
|
||||
|
||||
typedef struct ht_node_t ht_node_t;
|
||||
struct ht_node_t {
|
||||
ht_node_t *next;
|
||||
ht_key_value_t kv;
|
||||
};
|
||||
|
||||
typedef struct ht_bucket_t ht_bucket_t;
|
||||
struct ht_bucket_t {
|
||||
ht_node_t *first;
|
||||
ht_node_t *last;
|
||||
};
|
||||
|
||||
typedef struct ht_t ht_t;
|
||||
struct ht_t {
|
||||
ma_arena_t *arena;
|
||||
i32 item_count;
|
||||
i32 bucket_count;
|
||||
ht_bucket_t *buckets;
|
||||
};
|
||||
|
||||
fn ht_t *ht_create (ma_arena_t *arena, i32 size);
|
||||
fn ht_node_t *ht_insert_kv (ht_t *ht, u64 hash, ht_key_value_t kv);
|
||||
fn ht_node_t *ht_insert_u64 (ht_t *ht, u64 key, u64 value);
|
||||
fn ht_node_t *ht_insert_ptr (ht_t *ht, void *key, void *value);
|
||||
fn ht_node_t *ht_insert_string (ht_t *ht, s8_t key, s8_t value);
|
||||
fn ht_node_t *ht_insert_string_ptr (ht_t *ht, s8_t key, void *value);
|
||||
fn u64 *ht_search_u64 (ht_t *ht, u64 key);
|
||||
fn void **ht_search_ptr (ht_t *ht, void *key);
|
||||
fn s8_t *ht_search_string (ht_t *ht, s8_t key);
|
||||
fn void **ht_search_string_ptr (ht_t *ht, s8_t key);
|
||||
fn ht_node_t *ht_search_u64_ex (ht_t *ht, u64 key);
|
||||
fn ht_node_t *ht_search_ptr_ex (ht_t *ht, void *key);
|
||||
fn ht_node_t *ht_search_string_ex (ht_t *ht, s8_t key);
|
||||
|
||||
///////////////////////////////
|
||||
// string interning
|
||||
#define s8i_t s8_t
|
||||
fn s8i_t *intern_string(ht_t *ht, s8_t string);
|
||||
fn s8i_t *internf(ht_t *ht, char *str, ...);
|
||||
|
||||
|
||||
///////////////////////////////
|
||||
// Hashing
|
||||
typedef struct random_seed_t random_seed_t;
|
||||
struct random_seed_t {
|
||||
u64 a;
|
||||
};
|
||||
|
||||
fn u64 get_random_u64(random_seed_t *seed);
|
||||
fn u64 get_random_in_range_u64(random_seed_t *seed, u64 start, u64 one_past_end);
|
||||
fn i64 get_random_in_range_i64(random_seed_t *seed, i64 start, i64 one_past_end);
|
||||
fn f64 get_random_f64(random_seed_t *seed);
|
||||
fn u64 hash_data(s8_t string);
|
||||
fn u64 hash_mix(u64 x, u64 y);
|
||||
@@ -1,242 +0,0 @@
|
||||
#include "core_intrin.h"
|
||||
#include "core_basic.h"
|
||||
#include "core_platform.h"
|
||||
|
||||
fn void memory_copy(void *dst, void *src, usize n) {
|
||||
IF_PLATFORM_CLANG_ELSE(__builtin_memcpy(dst, src, n), memcpy(dst, src, n));
|
||||
}
|
||||
|
||||
fn void memory_move(void *dest, const void *src, usize n) {
|
||||
IF_PLATFORM_CLANG_ELSE(__builtin_memmove(dest, src, n), memmove(dest, src, n));
|
||||
}
|
||||
|
||||
fn void memory_set(void *dst, i32 c, usize size) {
|
||||
IF_PLATFORM_CLANG_ELSE(__builtin_memset(dst, c, size), memset(dst, c, size));
|
||||
}
|
||||
|
||||
fn int bad_memory__compare(void *left, void *right, usize size) {
|
||||
u8 *l = left;
|
||||
u8 *r = right;
|
||||
for (usize i = 0; i < size; i += 1) {
|
||||
if (l[i] != r[i]) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn b32 memory_equal(void *left, void *right, usize size) {
|
||||
int cmp = IF_PLATFORM_CLANG_ELSE(bad_memory__compare(left, right, size), memcmp(left, right, size));
|
||||
return cmp == 0;
|
||||
}
|
||||
|
||||
fn void memory_zero(void *dst, usize size) {
|
||||
memory_set(dst, 0, size);
|
||||
}
|
||||
|
||||
fn f32 f32_sqrt(f32 x) { return IF_PLATFORM_CLANG_ELSE(__builtin_sqrtf(x), sqrtf(x)); }
|
||||
fn f64 f64_sqrt(f64 x) { return IF_PLATFORM_CLANG_ELSE(__builtin_sqrt(x), sqrt(x)); }
|
||||
fn f32 f32_ceil(f32 x) { return IF_PLATFORM_CLANG_ELSE(__builtin_ceilf(x), ceilf(x)); }
|
||||
fn f64 f64_ceil(f64 x) { return IF_PLATFORM_CLANG_ELSE(__builtin_ceil(x), ceil(x)); }
|
||||
fn f32 f32_floor(f32 x) { return IF_PLATFORM_CLANG_ELSE(__builtin_floorf(x), floorf(x)); }
|
||||
fn f64 f64_floor(f64 x) { return IF_PLATFORM_CLANG_ELSE(__builtin_floor(x), floor(x)); }
|
||||
fn f32 f32_abs(f32 x) { return IF_PLATFORM_CLANG_ELSE(__builtin_fabsf(x), fabsf(x)); }
|
||||
fn f64 f64_abs(f64 x) { return IF_PLATFORM_CLANG_ELSE(__builtin_fabs(x), fabs(x)); }
|
||||
// fn f32 f32_round(f32 x) { return IF_PLATFORM_CLANG_ELSE(__builtin_roundf(x), roundf(x)); }
|
||||
fn f64 f64_round(f64 x) { return IF_PLATFORM_CLANG_ELSE(__builtin_round(x), round(x)); }
|
||||
fn f64 f64_mod(f64 a, f64 b) { return IF_PLATFORM_CLANG_ELSE(__builtin_fmod(a, b), fmod(a, b)); }
|
||||
fn f32 f32_mod(f32 a, f32 b) { return IF_PLATFORM_CLANG_ELSE(__builtin_fmodf(a, b), fmodf(a, b)); }
|
||||
|
||||
/* https://gitlab.com/nakst/essence/-/blob/master/shared/math.cpp
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*/
|
||||
|
||||
fn f64 f64_exp2(f64 x) {
|
||||
f64 a = f64_floor(x * 8);
|
||||
int64_t ai = (i64)a;
|
||||
|
||||
if (ai < -1024) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
f64 b = x - a / 8;
|
||||
|
||||
f64 y = U64_TO_F64(0x3FF0000000000000) +
|
||||
b * (U64_TO_F64(0x3FE62E42FEFA3A00) +
|
||||
b * (U64_TO_F64(0x3FCEBFBDFF829140) +
|
||||
b * (U64_TO_F64(0x3FAC6B08D73C4A40) +
|
||||
b * (U64_TO_F64(0x3F83B2AB53873280) +
|
||||
b * (U64_TO_F64(0x3F55D88F363C6C00) +
|
||||
b * (U64_TO_F64(0x3F242C003E4A2000) +
|
||||
b * U64_TO_F64(0x3EF0B291F6C00000)))))));
|
||||
|
||||
f64 m[8] = {
|
||||
U64_TO_F64(0x3FF0000000000000),
|
||||
U64_TO_F64(0x3FF172B83C7D517B),
|
||||
U64_TO_F64(0x3FF306FE0A31B715),
|
||||
U64_TO_F64(0x3FF4BFDAD5362A27),
|
||||
U64_TO_F64(0x3FF6A09E667F3BCD),
|
||||
U64_TO_F64(0x3FF8ACE5422AA0DB),
|
||||
U64_TO_F64(0x3FFAE89F995AD3AD),
|
||||
U64_TO_F64(0x3FFD5818DCFBA487),
|
||||
};
|
||||
|
||||
y *= m[ai & 7];
|
||||
|
||||
convert_f64_u64_t c;
|
||||
c.f = y;
|
||||
c.i += (ai >> 3) << 52;
|
||||
return c.f;
|
||||
}
|
||||
|
||||
fn f32 f32_exp2(f32 x) {
|
||||
f32 a = f32_floor(x);
|
||||
i32 ai = (i32)a;
|
||||
|
||||
if (ai < -128) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
f32 b = x - a;
|
||||
|
||||
f32 y = U32_TO_F32(0x3F7FFFFE) + b * (U32_TO_F32(0x3F31729A) + b * (U32_TO_F32(0x3E75E700) + b * (U32_TO_F32(0x3D64D520) + b * (U32_TO_F32(0x3C128280) + b * U32_TO_F32(0x3AF89400)))));
|
||||
|
||||
convert_f32_u32_t c;
|
||||
c.f = y;
|
||||
c.i += ai << 23;
|
||||
return c.f;
|
||||
}
|
||||
|
||||
fn f64 f64_log2(f64 x) {
|
||||
convert_f64_u64_t c;
|
||||
c.f = x;
|
||||
int64_t e = ((c.i >> 52) & 2047) - 0x3FF;
|
||||
c.i = (c.i & ~((uint64_t)0x7FF << 52)) + ((uint64_t)0x3FF << 52);
|
||||
x = c.f;
|
||||
|
||||
f64 a;
|
||||
|
||||
if (x < 1.125) {
|
||||
a = 0;
|
||||
} else if (x < 1.250) {
|
||||
x *= 1.125 / 1.250;
|
||||
a = U64_TO_F64(0xBFC374D65D9E608E);
|
||||
} else if (x < 1.375) {
|
||||
x *= 1.125 / 1.375;
|
||||
a = U64_TO_F64(0xBFD28746C334FECB);
|
||||
} else if (x < 1.500) {
|
||||
x *= 1.125 / 1.500;
|
||||
a = U64_TO_F64(0xBFDA8FF971810A5E);
|
||||
} else if (x < 1.625) {
|
||||
x *= 1.125 / 1.625;
|
||||
a = U64_TO_F64(0xBFE0F9F9FFC8932A);
|
||||
} else if (x < 1.750) {
|
||||
x *= 1.125 / 1.750;
|
||||
a = U64_TO_F64(0xBFE465D36ED11B11);
|
||||
} else if (x < 1.875) {
|
||||
x *= 1.125 / 1.875;
|
||||
a = U64_TO_F64(0xBFE79538DEA712F5);
|
||||
} else {
|
||||
x *= 1.125 / 2.000;
|
||||
a = U64_TO_F64(0xBFEA8FF971810A5E);
|
||||
}
|
||||
|
||||
f64 y = U64_TO_F64(0xC00FF8445026AD97) + x * (U64_TO_F64(0x40287A7A02D9353F) + x * (U64_TO_F64(0xC03711C58D55CEE2) + x * (U64_TO_F64(0x4040E8263C321A26) + x * (U64_TO_F64(0xC041EB22EA691BB3) + x * (U64_TO_F64(0x403B00FB376D1F10) + x * (U64_TO_F64(0xC02C416ABE857241) + x * (U64_TO_F64(0x40138BA7FAA3523A) + x * (U64_TO_F64(0xBFF019731AF80316) + x * U64_TO_F64(0x3FB7F1CD3852C200)))))))));
|
||||
|
||||
return y - a + e;
|
||||
}
|
||||
|
||||
fn f32 f32_log2(f32 x) {
|
||||
// TODO f32_log2(0.9999971379999999f) gives 0.00000143051147460938 (should be negative!!).
|
||||
|
||||
convert_f32_u32_t c;
|
||||
c.f = x;
|
||||
i32 e = ((c.i >> 23) & 255) - 0x7F;
|
||||
c.i = (c.i & ~(0xFF << 23)) + (0x7F << 23);
|
||||
x = c.f;
|
||||
|
||||
f32 y = U32_TO_F32(0xC05B5154) + x * (U32_TO_F32(0x410297C6) + x * (U32_TO_F32(0xC1205CEB) + x * (U32_TO_F32(0x4114DF63) + x * (U32_TO_F32(0xC0C0DBBB) + x * (U32_TO_F32(0x402942C6) + x * (U32_TO_F32(0xBF3FF98A) + x * (U32_TO_F32(0x3DFE1050) + x * U32_TO_F32(0xBC151480))))))));
|
||||
|
||||
return y + e;
|
||||
}
|
||||
|
||||
fn f64 f64_pow(f64 x, f64 y) {
|
||||
return f64_exp2(y * f64_log2(x));
|
||||
}
|
||||
|
||||
fn f32 f32_pow(f32 x, f32 y) {
|
||||
return f32_exp2(y * f32_log2(x));
|
||||
}
|
||||
|
||||
//
|
||||
/* https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=newlib/libm/common/s_round.c;hb=master
|
||||
* ====================================================
|
||||
* 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 f32 f32_round(f32 x) {
|
||||
u32 w = F32_TO_U32(x);
|
||||
/* Most significant word, least significant word. */
|
||||
int exponent_less_127;
|
||||
|
||||
/* Extract exponent field. */
|
||||
exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127;
|
||||
|
||||
if (exponent_less_127 < 23) {
|
||||
if (exponent_less_127 < 0) {
|
||||
w &= 0x80000000;
|
||||
if (exponent_less_127 == -1)
|
||||
/* Result is +1.0 or -1.0. */
|
||||
w |= ((u32)127 << 23);
|
||||
} else {
|
||||
unsigned int exponent_mask = 0x007fffff >> exponent_less_127;
|
||||
if ((w & exponent_mask) == 0)
|
||||
/* x has an integral value. */
|
||||
return x;
|
||||
|
||||
w += 0x00400000 >> exponent_less_127;
|
||||
w &= ~exponent_mask;
|
||||
}
|
||||
} else {
|
||||
if (exponent_less_127 == 128)
|
||||
/* x is NaN or infinite. */
|
||||
return x + x;
|
||||
else
|
||||
return x;
|
||||
}
|
||||
x = U32_TO_F32(w);
|
||||
return x;
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
// other
|
||||
fn u32 u32_safe_cast(u64 v) {
|
||||
assert_expr(v <= u32_max);
|
||||
return (u32)v;
|
||||
}
|
||||
|
||||
fn u16 u16_safe_cast(u64 v) {
|
||||
assert(v <= u16_max);
|
||||
return (u16)v;
|
||||
}
|
||||
|
||||
fn u8 u8_safe_cast(u64 v) {
|
||||
assert(v <= u8_max);
|
||||
return (u8)v;
|
||||
}
|
||||
|
||||
fn i32 i32_safe_cast(i64 v) {
|
||||
assert(v >= i32_min && v <= i32_max);
|
||||
return (i32)v;
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
#include "core_basic.h"
|
||||
|
||||
fn void memory_copy(void *dst, void *src, usize n);
|
||||
fn void memory_move(void *dest, const void *src, usize n);
|
||||
fn void memory_set(void *dst, i32 c, usize size);
|
||||
fn b32 memory_equal(void *left, void *right, usize size);
|
||||
fn void memory_zero(void *dst, usize size);
|
||||
|
||||
fn f32 f32_sqrt(f32 x);
|
||||
fn f64 f64_sqrt(f64 x);
|
||||
fn f32 f32_ceil(f32 x);
|
||||
fn f64 f64_ceil(f64 x);
|
||||
fn f32 f32_floor(f32 x);
|
||||
fn f64 f64_floor(f64 x);
|
||||
fn f32 f32_abs(f32 x);
|
||||
fn f64 f64_abs(f64 x);
|
||||
fn f32 f32_round(f32 x);
|
||||
fn f64 f64_round(f64 x);
|
||||
fn f64 f64_mod(f64 a, f64 b);
|
||||
fn f32 f32_mod(f32 a, f32 b);
|
||||
fn f64 f64_exp2(f64 x);
|
||||
fn f32 f32_exp2(f32 x);
|
||||
fn f64 f64_log2(f64 x);
|
||||
fn f32 f32_log2(f32 x);
|
||||
fn f64 f64_pow(f64 x, f64 y);
|
||||
fn f32 f32_pow(f32 x, f32 y);
|
||||
fn f32 f32_round(f32 x);
|
||||
fn u32 u32_safe_cast(u64 v);
|
||||
fn u16 u16_safe_cast(u64 v);
|
||||
fn u8 u8_safe_cast(u64 v);
|
||||
fn i32 i32_safe_cast(i64 v);
|
||||
@@ -1,469 +0,0 @@
|
||||
#include "core_lexer.h"
|
||||
#include "core_log.h"
|
||||
#include "core_platform.h"
|
||||
#include "core_ctx.h"
|
||||
|
||||
#define lex_error(TOKEN, STRING) do {\
|
||||
errorf(STRING);\
|
||||
TOKEN->kind = lex_kind_error;\
|
||||
TOKEN->string = s8(STRING);\
|
||||
} while (0)
|
||||
|
||||
fn lexer_t lex_make(s8_t stream, char *file_name) {
|
||||
lexer_t result = {.at = stream.str, .end = stream.str + stream.len, .file = file_name};
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 lex_at_end(lexer_t *lex) {
|
||||
b32 result = lex->at >= lex->end;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn void lex_advance(lexer_t *lex) {
|
||||
if (lex_at_end(lex)) return;
|
||||
if (lex->at[0] == '\n') { lex->column = 0; lex->line += 1; }
|
||||
lex->column += 1;
|
||||
lex->at += 1;
|
||||
}
|
||||
|
||||
fn b32 lex_match(lexer_t *lex, char c) {
|
||||
if (lex->at[0] == c) {
|
||||
lex_advance(lex);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void lex_eat_whitespace(lexer_t *lex) {
|
||||
for (;;) {
|
||||
if (char_is_whitespace(lex->at[0])) {
|
||||
if (lex->at[0] == '\n') lex->inside_macro = false;
|
||||
lex_advance(lex);
|
||||
} else if (lex->at[0] == '\\' && lex->at[1] == '\n') {
|
||||
lex_advance(lex); lex_advance(lex);
|
||||
} else if (lex->at[0] == '\\' && lex->at[1] == '\r' && lex->at[2] == '\n') {
|
||||
lex_advance(lex); lex_advance(lex); lex_advance(lex);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn void lex_eat_macro_whitespace(lexer_t *lex) {
|
||||
while (lex->at[0] == ' ' || lex->at[0] == '\t') lex_advance(lex);
|
||||
}
|
||||
|
||||
fn void lex_suffix(lexer_t *lex, lex_t *token) {
|
||||
if (lex_match(lex, 'f')) {
|
||||
token->kind = lex_kind_real;
|
||||
token->suffix = lex_suffix_f;
|
||||
} else if (lex_match(lex, 'd')) {
|
||||
token->kind = lex_kind_real;
|
||||
token->suffix = lex_suffix_d;
|
||||
} else if (token->kind == lex_kind_integer && ((lex->at[0] == 'u' && lex->at[1] == 'l' && lex->at[2] == 'l') || (lex->at[0] == 'U' && lex->at[1] == 'L' && lex->at[2] == 'L'))) {
|
||||
token->suffix = lex_suffix_ull;
|
||||
lex_advance(lex); lex_advance(lex); lex_advance(lex);
|
||||
} else if (token->kind == lex_kind_integer && ((lex->at[0] == 'u' && lex->at[1] == 'l') || (lex->at[0] == 'U' && lex->at[1] == 'L'))) {
|
||||
token->suffix = lex_suffix_ul;
|
||||
lex_advance(lex); lex_advance(lex);
|
||||
} else if (token->kind == lex_kind_integer && (lex->at[0] == 'l' || lex->at[0] == 'L')) {
|
||||
token->suffix = lex_suffix_l;
|
||||
lex_advance(lex);
|
||||
} else if (token->kind == lex_kind_integer && ((lex->at[0] == 'l' && lex->at[1] == 'l') || (lex->at[0] == 'L' && lex->at[1] == 'L'))) {
|
||||
token->suffix = lex_suffix_ll;
|
||||
lex_advance(lex); lex_advance(lex);
|
||||
}
|
||||
}
|
||||
|
||||
fn void lex_eat_number(lexer_t *lex, lex_t *token) {
|
||||
token->kind = lex_kind_integer;
|
||||
for (;;) {
|
||||
if (char_is_digit(lex->at[0])) {
|
||||
lex_advance(lex);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lex_match(lex, '.')) {
|
||||
if (token->kind == lex_kind_real) {
|
||||
lex_error(token, "multiple '.' periods in floating point number literal");
|
||||
return;
|
||||
}
|
||||
token->kind = lex_kind_real;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
lex_suffix(lex, token);
|
||||
}
|
||||
|
||||
fn void lex_eat_until(lexer_t *lex, char c) {
|
||||
while (lex->at[0] != c && !lex_at_end(lex)) lex_advance(lex);
|
||||
}
|
||||
|
||||
fn void lex_eat_string(lexer_t *lex, lex_t *token) {
|
||||
token->kind = lex_kind_string;
|
||||
for (;;) {
|
||||
if (lex->at[0] == '\\') {
|
||||
lex_advance(lex);
|
||||
} else if (lex_match(lex, token->str[0])) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (lex->at[0] == 0) {
|
||||
lex_error(token, "unclosed string");
|
||||
return;
|
||||
}
|
||||
|
||||
lex_advance(lex);
|
||||
}
|
||||
}
|
||||
|
||||
fn b32 lex_macro(lexer_t *lex, lex_t *token) {
|
||||
lex_eat_macro_whitespace(lex);
|
||||
token->str = lex->at;
|
||||
while (char_is_alphabetic(lex->at[0])) lex_advance(lex);
|
||||
token->len = (i32)(lex->at - token->str);
|
||||
|
||||
if (s8_are_equal(token->string, s8("define"))) {
|
||||
token->kind = lex_kind_preproc_define;
|
||||
} else if (s8_are_equal(token->string, s8("ifdef"))) {
|
||||
token->kind = lex_kind_preproc_ifdef;
|
||||
} else if (s8_are_equal(token->string, s8("ifndef"))) {
|
||||
token->kind = lex_kind_preproc_ifndef;
|
||||
} else if (s8_are_equal(token->string, s8("include"))) {
|
||||
token->kind = lex_kind_preproc_include;
|
||||
lex_eat_macro_whitespace(lex);
|
||||
char end = 0;
|
||||
if (lex_match(lex, '"')) {
|
||||
end = '"';
|
||||
} else if (lex_match(lex, '<')) {
|
||||
end = '>';
|
||||
token->system_include = true;
|
||||
} else {
|
||||
lex_error(token, "invalid include directive, should be followed by string or '<'");
|
||||
return false;
|
||||
}
|
||||
|
||||
token->str = lex->at;
|
||||
while (lex->at[0] != end) {
|
||||
if (lex_at_end(lex)) {
|
||||
lex_error(token, "invalid include directive, reached end of file");
|
||||
return false;
|
||||
}
|
||||
if (lex->at[0] == '\n') {
|
||||
lex_error(token, "invalid include directive, reached end of line");
|
||||
return false;
|
||||
}
|
||||
lex_advance(lex);
|
||||
}
|
||||
lex_advance(lex);
|
||||
} else if (s8_are_equal(token->string, s8("if"))) {
|
||||
token->kind = lex_kind_preproc_if;
|
||||
} else if (s8_are_equal(token->string, s8("endif"))) {
|
||||
token->kind = lex_kind_preproc_endif;
|
||||
} else if (s8_are_equal(token->string, s8("error"))) {
|
||||
token->kind = lex_kind_preproc_error;
|
||||
lex_eat_macro_whitespace(lex);
|
||||
token->str = lex->at;
|
||||
lex_eat_until(lex, '\n');
|
||||
} else if (s8_are_equal(token->string, s8("else"))) {
|
||||
token->kind = lex_kind_preproc_else;
|
||||
} else if (s8_are_equal(token->string, s8("elif"))) {
|
||||
token->kind = lex_kind_preproc_elif;
|
||||
} else if (s8_are_equal(token->string, s8("pragma"))) {
|
||||
token->kind = lex_kind_preproc_pragma;
|
||||
} else if (s8_are_equal(token->string, s8("undef"))) {
|
||||
token->kind = lex_kind_preproc_undef;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define LEX_CASE3(C1, K1, C2, K2, C3, K3) \
|
||||
case C1: { \
|
||||
token->kind = K1; \
|
||||
if (lex_match(lex, C2)) { \
|
||||
lex_advance(lex); \
|
||||
token->kind = K2; \
|
||||
} else if (lex_match(lex, C3)) { \
|
||||
lex_advance(lex); \
|
||||
token->kind = K3; \
|
||||
} \
|
||||
} break
|
||||
|
||||
fn void lex_token_ex(lexer_t *lex, lex_t *token) {
|
||||
lex_eat_whitespace(lex);
|
||||
*token = (lex_t){.str = lex->at, .file = lex->file, .line = lex->line, .column = lex->column};
|
||||
lex_advance(lex);
|
||||
|
||||
switch (token->str[0]) {
|
||||
case '\0': token->kind = lex_kind_eof; break;
|
||||
case '{': token->kind = lex_kind_open_brace; break;
|
||||
case '}': token->kind = lex_kind_close_brace; break;
|
||||
case '(': token->kind = lex_kind_open_paren; break;
|
||||
case ')': token->kind = lex_kind_close_paren; break;
|
||||
case '[': token->kind = lex_kind_open_bracket; break;
|
||||
case ']': token->kind = lex_kind_close_bracket; break;
|
||||
case '~': token->kind = lex_kind_bit_negation; break;
|
||||
case ';': token->kind = lex_kind_semicolon; break;
|
||||
case ':': token->kind = lex_kind_colon; break;
|
||||
case ',': token->kind = lex_kind_comma; break;
|
||||
case '@': token->kind = lex_kind_tag; break;
|
||||
case '?': token->kind = lex_kind_question; break;
|
||||
case '\\': token->kind = lex_kind_escape; break;
|
||||
case '"': lex_eat_string(lex, token); break;
|
||||
case '`': lex_eat_string(lex, token); break;
|
||||
case '\'': lex_eat_string(lex, token); break;
|
||||
|
||||
LEX_CASE3('^', lex_kind_bit_xor, '=', lex_kind_bit_xor_assign, /*ignored option*/'=', lex_kind_bit_xor_assign);
|
||||
LEX_CASE3('=', lex_kind_assign, '=', lex_kind_equals, /*ignored option*/'=', lex_kind_equals);
|
||||
LEX_CASE3('!', lex_kind_negation, '=', lex_kind_not_equals, /*ignored option*/'=', lex_kind_not_equals);
|
||||
LEX_CASE3('%', lex_kind_modulo, '=', lex_kind_modulo_assign, /*ignored option*/'=', lex_kind_modulo_assign);
|
||||
LEX_CASE3('*', lex_kind_multiply, '=', lex_kind_multiply_assign, /*ignored option*/'=', lex_kind_multiply_assign);
|
||||
LEX_CASE3('+', lex_kind_plus, '+', lex_kind_increment, '=', lex_kind_plus_assign);
|
||||
LEX_CASE3('-', lex_kind_minus, '-', lex_kind_decrement, '=', lex_kind_minus_assign);
|
||||
LEX_CASE3('&', lex_kind_bit_and, '&', lex_kind_and, '=', lex_kind_bit_and_assign);
|
||||
LEX_CASE3('|', lex_kind_bit_or, '|', lex_kind_or, '=', lex_kind_bit_or_assign);
|
||||
|
||||
case '.': {
|
||||
token->kind = lex_kind_dot;
|
||||
if (lex->at[0] == '.' && lex->at[1] == '.') {
|
||||
lex_advance(lex);
|
||||
lex_advance(lex);
|
||||
token->kind = lex_kind_three_dots;
|
||||
}
|
||||
} break;
|
||||
|
||||
case '/': {
|
||||
token->kind = lex_kind_divide;
|
||||
if (lex_match(lex, '/')) {
|
||||
token->kind = lex_kind_comment;
|
||||
lex_eat_until(lex, '\n');
|
||||
} else if (lex_match(lex, '*')) {
|
||||
token->kind = lex_kind_comment;
|
||||
for (;;) {
|
||||
if (lex->at[0] == '*' && lex->at[1] == '/') {
|
||||
break;
|
||||
}
|
||||
if (lex->at[0] == 0) {
|
||||
lex_error(token, "unclosed block comment");
|
||||
return;
|
||||
}
|
||||
lex_advance(lex);
|
||||
}
|
||||
lex_advance(lex);
|
||||
lex_advance(lex);
|
||||
} else if (lex_match(lex, '=')) {
|
||||
token->kind = lex_kind_divide_assign;
|
||||
}
|
||||
} break;
|
||||
|
||||
case '>': {
|
||||
token->kind = lex_kind_greater;
|
||||
if (lex_match(lex, '=')) {
|
||||
token->kind = lex_kind_greater_or_equal;
|
||||
} else if (lex_match(lex, '>')) {
|
||||
token->kind = lex_kind_bit_right_shift;
|
||||
if (lex_match(lex, '=')) {
|
||||
token->kind = lex_kind_bit_right_shift_assign;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case '<': {
|
||||
token->kind = lex_kind_lesser;
|
||||
if (lex_match(lex, '=')) {
|
||||
token->kind = lex_kind_lesser_or_equal;
|
||||
} else if (lex_match(lex, '<')) {
|
||||
token->kind = lex_kind_bit_left_shift;
|
||||
if (lex_match(lex, '=')) {
|
||||
token->kind = lex_kind_bit_left_shift_assign;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case '#': {
|
||||
if (lex_match(lex, '#')) {
|
||||
token->kind = lex_kind_preproc_concat;
|
||||
} else {
|
||||
b32 inside_macro = lex_macro(lex, token);
|
||||
if (inside_macro) {
|
||||
lex->inside_macro = true;
|
||||
} else {
|
||||
if (!lex->inside_macro) {
|
||||
lex_error(token, "invalid preprocessor directive");
|
||||
return;
|
||||
}
|
||||
token->kind = lex_kind_preproc_stringify;
|
||||
token->str = lex->at;
|
||||
// this is slighly wrong, first letter can't be number
|
||||
while (char_is_alphanumeric(lex->at[0]) || lex->at[0] == '_') lex_advance(lex);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '0': {
|
||||
lex_eat_number(lex, token);
|
||||
} break;
|
||||
|
||||
case 'A': case 'a': case 'B': case 'b': case 'C': case 'c':
|
||||
case 'D': case 'd': case 'E': case 'e': case 'F': case 'f':
|
||||
case 'G': case 'g': case 'H': case 'h': case 'I': case 'i':
|
||||
case 'J': case 'j': case 'K': case 'k': case 'L': case 'l':
|
||||
case 'M': case 'm': case 'N': case 'n': case 'O': case 'o':
|
||||
case 'P': case 'p': case 'Q': case 'q': case 'R': case 'r':
|
||||
case 'S': case 's': case 'T': case 't': case 'U': case 'u':
|
||||
case 'V': case 'v': case 'W': case 'w': case 'X': case 'x':
|
||||
case 'Y': case 'y': case 'Z': case 'z': case '_': {
|
||||
token->kind = lex_kind_ident;
|
||||
while (char_is_alphanumeric(lex->at[0]) || lex->at[0] == '_') lex_advance(lex);
|
||||
} break;
|
||||
|
||||
default: {
|
||||
lex_error(token, "found invalid character in the token stream");
|
||||
}
|
||||
}
|
||||
|
||||
token->len = (i32)(lex->at - token->str);
|
||||
|
||||
if (token->kind == lex_kind_integer) {
|
||||
s8_t string_to_lex = token->string;
|
||||
if (token->suffix != lex_suffix_none) {
|
||||
s8_t string_value = ti_enum_value_to_name(token->suffix, type(lex_suffix_t));
|
||||
s8_t prefix = s8("lex_suffix_");
|
||||
string_to_lex.len -= string_value.len - prefix.len;
|
||||
}
|
||||
token->integer = u64_from_s8(string_to_lex, 10);
|
||||
} else if (token->kind == lex_kind_real) {
|
||||
s8_t string_to_lex = token->string;
|
||||
if (token->suffix != lex_suffix_none) {
|
||||
s8_t string_value = ti_enum_value_to_name(token->suffix, type(lex_suffix_t));
|
||||
s8_t prefix = s8("lex_suffix_");
|
||||
string_to_lex.len -= string_value.len - prefix.len;
|
||||
}
|
||||
token->real = f64_from_s8(string_to_lex);
|
||||
} else if (token->kind == lex_kind_string) {
|
||||
token->str += 1;
|
||||
token->len -= 2;
|
||||
} else if (token->kind == lex_kind_preproc_include) {
|
||||
token->len -= 1;
|
||||
}
|
||||
if (lex->inside_macro) token->inside_macro = true;
|
||||
}
|
||||
|
||||
fn lex_t lex_token(lexer_t *lex) {
|
||||
lex_t result = {0};
|
||||
lex_token_ex(lex, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn lex_array_t lex_tokens(ma_arena_t *arena, char *file_name, s8_t stream) {
|
||||
usize align = arena->align;
|
||||
arena->align = 0;
|
||||
|
||||
lex_array_t token_array = {0};
|
||||
lexer_t l = lex_make(stream, file_name);
|
||||
for (;;) {
|
||||
lex_t *token = ma_push_type(arena, lex_t);
|
||||
if (token_array.data == NULL) {
|
||||
token_array.data = token;
|
||||
}
|
||||
token_array.len += 1;
|
||||
|
||||
do {
|
||||
lex_token_ex(&l, token);
|
||||
} while (token->kind == lex_kind_comment);
|
||||
if (token->kind == lex_kind_eof) break;
|
||||
}
|
||||
|
||||
arena->align = align;
|
||||
return token_array;
|
||||
}
|
||||
|
||||
gb_read_only s8_t lex_kind_simple_strings[] = {
|
||||
#define X(KIND, STR, SIMPLE) s8_const(SIMPLE),
|
||||
LEX_KIND_XLIST
|
||||
#undef X
|
||||
};
|
||||
|
||||
fn s8_t lex_kind_to_simple_s8(lex_kind_t kind) {
|
||||
assert_expr(kind >= 0 && kind < lex_kind_count);
|
||||
return lex_kind_simple_strings[kind];
|
||||
}
|
||||
|
||||
gb_read_only s8_t lex_kind_strings[] = {
|
||||
#define X(KIND, STR, SIMPLE) s8_const(STR),
|
||||
LEX_KIND_XLIST
|
||||
#undef X
|
||||
};
|
||||
|
||||
fn s8_t lex_kind_to_s8(lex_kind_t kind) {
|
||||
assert(kind >= 0 && kind < lex_kind_count);
|
||||
return lex_kind_strings[kind];
|
||||
}
|
||||
|
||||
fn lex_t *parser_next(parser_t *par) {
|
||||
lex_t *result = par->at;
|
||||
if (result->kind != lex_kind_eof) par->at += 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn lex_t *parser_match(parser_t *par, lex_kind_t kind) {
|
||||
if (par->at->kind == kind) {
|
||||
return parser_next(par);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
fn lex_t *parser_matchi(parser_t *par, s8_t str) {
|
||||
if (par->at->kind == lex_kind_ident && s8_are_equal(par->at->string, str)) {
|
||||
return parser_next(par);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
fn void parser_panicf(b32 print_only_file, lex_t *token, const char *str, ...) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
b8 p = tcx->log.print_file_and_line;
|
||||
tcx->log.print_file_and_line = false;
|
||||
|
||||
S8_FMT(scratch.arena, str, str8);
|
||||
if (print_only_file) {
|
||||
fatalf("%s: error: %.*s", token->file, s8_fmtspec(str8));
|
||||
} else {
|
||||
fatalf("%s(%d): error: %.*s", token->file, token->line, s8_fmtspec(str8));
|
||||
}
|
||||
|
||||
|
||||
tcx->log.print_file_and_line = p;
|
||||
ma_end_scratch(scratch);
|
||||
}
|
||||
|
||||
fn lex_t *parser_expect(parser_t *par, lex_kind_t kind) {
|
||||
lex_t *token = parser_match(par, kind);
|
||||
if (!token) parser_panicf(par->print_only_file, par->at, "expected token kind: %s, got instead: %s", lex_kind_to_s8(kind).str, lex_kind_to_s8(par->at->kind).str);
|
||||
return token;
|
||||
}
|
||||
|
||||
fn lex_t *parser_expecti(parser_t *par, s8_t ident) {
|
||||
lex_t *token = parser_matchi(par, ident);
|
||||
if (!token) parser_panicf(par->print_only_file, par->at, "expected identifier: '%.*s'", s8_fmtspec(ident));
|
||||
return token;
|
||||
}
|
||||
|
||||
fn void parser_eat_until(parser_t *par, lex_kind_t kind) {
|
||||
while (par->at->kind != kind && par->at->kind != lex_kind_eof) {
|
||||
parser_next(par);
|
||||
}
|
||||
}
|
||||
|
||||
fn void parser_eat_including(parser_t *par, lex_kind_t kind) {
|
||||
parser_eat_until(par, kind);
|
||||
parser_next(par);
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
#pragma once
|
||||
#include "core_basic.h"
|
||||
#include "core_string.h"
|
||||
#include "core_arena.h"
|
||||
#include "core_type_info.h"
|
||||
|
||||
typedef enum lex_kind_t {
|
||||
#define LEX_KIND_XLIST\
|
||||
X(lex_kind_eof , "end of file" , "---" )\
|
||||
X(lex_kind_error , "error" , "---" )\
|
||||
X(lex_kind_integer , "integer" , "---" )\
|
||||
X(lex_kind_real , "real" , "---" )\
|
||||
X(lex_kind_ident , "identifier" , "---" )\
|
||||
X(lex_kind_string , "string" , "---" )\
|
||||
X(lex_kind_comment , "comment" , "---" )\
|
||||
X(lex_kind_open_brace , "'{' open brace" , "{" )\
|
||||
X(lex_kind_close_brace , "'}' close brace" , "}" )\
|
||||
X(lex_kind_open_paren , "'(' open parenthesis" , "(" )\
|
||||
X(lex_kind_close_paren , "')' close parenthesis" , ")" )\
|
||||
X(lex_kind_open_bracket , "'[' open bracket" , "[" )\
|
||||
X(lex_kind_close_bracket , "']' close bracket" , "]" )\
|
||||
X(lex_kind_plus , "'+' plus" , "+" )\
|
||||
X(lex_kind_minus , "'-' minus" , "-" )\
|
||||
X(lex_kind_divide , "'/' division sign" , "/" )\
|
||||
X(lex_kind_multiply , "'*' multiplication sign" , "*" )\
|
||||
X(lex_kind_modulo , "'%' modulo" , "%" )\
|
||||
X(lex_kind_or , "'||' logical or" , "||" )\
|
||||
X(lex_kind_and , "'&&' logical and" , "&&" )\
|
||||
X(lex_kind_negation , "'!' logical negation" , "!" )\
|
||||
X(lex_kind_bit_negation , "'~' bit negation" , "~" )\
|
||||
X(lex_kind_bit_left_shift , "'<<' bit left shift" , "<<" )\
|
||||
X(lex_kind_bit_right_shift , "'>>' bit right shift" , ">>" )\
|
||||
X(lex_kind_bit_or , "'|' bit or" , "|" )\
|
||||
X(lex_kind_bit_and , "'&' bit and" , "&" )\
|
||||
X(lex_kind_bit_xor , "'^' bit xor" , "^" )\
|
||||
X(lex_kind_decrement , "'--' decrement" , "--" )\
|
||||
X(lex_kind_increment , "'++' increment" , "++" )\
|
||||
X(lex_kind_post_decrement , "'--' post decrement" , "--" )\
|
||||
X(lex_kind_post_increment , "'++' post increment" , "++" )\
|
||||
X(lex_kind_assign , "'=' assignment" , "=" )\
|
||||
X(lex_kind_divide_assign , "'/=' divide assignment" , "/=" )\
|
||||
X(lex_kind_multiply_assign , "'*=' multiply assignment" , "*=" )\
|
||||
X(lex_kind_plus_assign , "'+=' plus assignment" , "+=" )\
|
||||
X(lex_kind_minus_assign , "'-=' minus assignment" , "-=" )\
|
||||
X(lex_kind_modulo_assign , "'%=' modulo assignment" , "%=" )\
|
||||
X(lex_kind_bit_and_assign , "'&=' bit and assignment" , "&=" )\
|
||||
X(lex_kind_bit_or_assign , "'|=' bit or assignment" , "|=" )\
|
||||
X(lex_kind_bit_xor_assign , "'^=' bit xor assignment" , "^=" )\
|
||||
X(lex_kind_bit_left_shift_assign , "'<<=' bit left shift assignment" , "<<=" )\
|
||||
X(lex_kind_bit_right_shift_assign , "'>>=' bit right shift assignment" , ">>=" )\
|
||||
X(lex_kind_equals , "'==' equals sign" , "==" )\
|
||||
X(lex_kind_not_equals , "'!=' not equals sign" , "!=" )\
|
||||
X(lex_kind_lesser , "'<' lesser then" , "<" )\
|
||||
X(lex_kind_greater , "'>' greater then" , ">" )\
|
||||
X(lex_kind_lesser_or_equal , "'<=' lesser then or equal" , "<=" )\
|
||||
X(lex_kind_greater_or_equal , "'>=' greater then or equal" , ">=" )\
|
||||
X(lex_kind_comma , "',' comma" , "," )\
|
||||
X(lex_kind_dot , "'.' dot" , "." )\
|
||||
X(lex_kind_three_dots , "'...' three dots" , "..." )\
|
||||
X(lex_kind_semicolon , "';' semicolon" , ";" )\
|
||||
X(lex_kind_colon , "':' colon" , ":" )\
|
||||
X(lex_kind_arrow , "'->' arrow" , "->" )\
|
||||
X(lex_kind_question , "'?' question mark" , "?" )\
|
||||
X(lex_kind_tag , "'@' tag sign" , "@" )\
|
||||
X(lex_kind_escape , "'\\' escape" , "\\" )\
|
||||
X(lex_kind_preproc_null , "preproc_null" , "---" )\
|
||||
X(lex_kind_preproc_define , "preproc_define" , "---" )\
|
||||
X(lex_kind_preproc_ifdef , "preproc_ifdef" , "---" )\
|
||||
X(lex_kind_preproc_ifndef , "preproc_ifndef" , "---" )\
|
||||
X(lex_kind_preproc_include , "preproc_include" , "---" )\
|
||||
X(lex_kind_preproc_endif , "preproc_endif" , "---" )\
|
||||
X(lex_kind_preproc_if , "preproc_if" , "---" )\
|
||||
X(lex_kind_preproc_pragma , "preproc_pragma" , "---" )\
|
||||
X(lex_kind_preproc_error , "preproc_error" , "---" )\
|
||||
X(lex_kind_preproc_else , "preproc_else" , "---" )\
|
||||
X(lex_kind_preproc_elif , "preproc_elif" , "---" )\
|
||||
X(lex_kind_preproc_undef , "preproc_undef" , "---" )\
|
||||
X(lex_kind_preproc_concat , "preproc_concat" , "---" )\
|
||||
X(lex_kind_preproc_stringify , "preproc_stringify" , "---" )\
|
||||
|
||||
#define X(KIND, STR, SIMPLE) KIND,
|
||||
LEX_KIND_XLIST
|
||||
#undef X
|
||||
|
||||
lex_kind_count,
|
||||
} lex_kind_t;
|
||||
|
||||
typedef enum lex_suffix_t {
|
||||
#define LEX_SUFFIX_XLIST\
|
||||
X(lex_suffix_none) \
|
||||
X(lex_suffix_f) \
|
||||
X(lex_suffix_d) \
|
||||
X(lex_suffix_u) \
|
||||
X(lex_suffix_ul) \
|
||||
X(lex_suffix_ull) \
|
||||
X(lex_suffix_l) \
|
||||
X(lex_suffix_ll) \
|
||||
|
||||
#define X(KIND) KIND,
|
||||
LEX_SUFFIX_XLIST
|
||||
#undef X
|
||||
|
||||
lex_suffix_count,
|
||||
} lex_suffix_t;
|
||||
|
||||
typedef struct lex_t lex_t;
|
||||
struct lex_t {
|
||||
lex_kind_t kind;
|
||||
lex_suffix_t suffix;
|
||||
struct {
|
||||
b8 inside_macro: 1;
|
||||
b8 system_include: 1;
|
||||
};
|
||||
|
||||
union {
|
||||
struct {char *str; i64 len;};
|
||||
s8_t string;
|
||||
};
|
||||
|
||||
i32 line;
|
||||
i32 column;
|
||||
char *file;
|
||||
|
||||
union {
|
||||
u64 integer;
|
||||
f64 real;
|
||||
char *error;
|
||||
};
|
||||
};
|
||||
|
||||
typedef struct lexer_t lexer_t;
|
||||
struct lexer_t {
|
||||
char *at;
|
||||
char *end;
|
||||
char *file;
|
||||
i32 line;
|
||||
i32 column;
|
||||
|
||||
b8 inside_macro;
|
||||
};
|
||||
|
||||
typedef struct lex_array_t lex_array_t;
|
||||
struct lex_array_t {
|
||||
lex_t *data;
|
||||
i32 len;
|
||||
};
|
||||
|
||||
fn lex_array_t lex_tokens(ma_arena_t *arena, char *filename, s8_t stream);
|
||||
fn s8_t lex_kind_to_simple_s8(lex_kind_t kind);
|
||||
fn s8_t lex_kind_to_s8(lex_kind_t kind);
|
||||
|
||||
typedef struct parser_t parser_t;
|
||||
struct parser_t {
|
||||
ma_arena_t *arena;
|
||||
lex_t *at;
|
||||
b8 print_only_file;
|
||||
};
|
||||
|
||||
#define parser_make(ARENA, TOKEN) &(parser_t){.arena = ARENA, .at = TOKEN}
|
||||
fn lex_t *parser_next(parser_t *par);
|
||||
fn lex_t *parser_match(parser_t *par, lex_kind_t kind);
|
||||
fn lex_t *parser_matchi(parser_t *par, s8_t str);
|
||||
fn lex_t *parser_expect(parser_t *par, lex_kind_t kind);
|
||||
fn void parser_panicf(b32 print_only_file, lex_t *token, const char *str, ...);
|
||||
fn void parser_eat_until(parser_t *par, lex_kind_t kind);
|
||||
fn void parser_eat_including(parser_t *par, lex_kind_t kind);
|
||||
|
||||
static gb_read_only lex_t lex_null;
|
||||
|
||||
static gb_read_only type_member_t members__lex_kind_t[] = {
|
||||
#define X(KIND, STR, SIMPLE) {.name = s8_const("lex_kind_" #KIND), .value = KIND},
|
||||
LEX_KIND_XLIST
|
||||
#undef X
|
||||
};
|
||||
static DEFINE_ENUM(lex_kind_t);
|
||||
|
||||
static gb_read_only type_member_t members__lex_suffix_t[] = {
|
||||
#define X(KIND) {.name = s8_const(#KIND), .value = KIND},
|
||||
LEX_SUFFIX_XLIST
|
||||
#undef X
|
||||
};
|
||||
static DEFINE_ENUM(lex_suffix_t);
|
||||
@@ -1,42 +0,0 @@
|
||||
#include "core_log.h"
|
||||
#include "core_ctx.h"
|
||||
#include "core_platform.h"
|
||||
|
||||
fn void log_basef(log_level_t level, s8_t file_and_line, const char *str, ...) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
S8_FMT(scratch.arena, str, string);
|
||||
tcx->log.log_proc(level, file_and_line, string);
|
||||
ma_end_scratch(scratch);
|
||||
}
|
||||
|
||||
fn void default_log_proc(log_level_t level, s8_t file_and_line, s8_t string) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
|
||||
char *colon = ": ";
|
||||
if (tcx->log.print_file_and_line == false) {
|
||||
file_and_line.len = 0;
|
||||
colon = "";
|
||||
}
|
||||
|
||||
switch(level) {
|
||||
case log_level_debug:
|
||||
case log_level_info: {
|
||||
os_console_log(s8_printf(scratch.arena, "%.*s\n", s8_fmtspec(string)).str);
|
||||
} break;
|
||||
case log_level_warning: {
|
||||
os_console_log(s8_printf(scratch.arena, "%.*s%s%.*s\n", s8_fmtspec(file_and_line), colon, s8_fmtspec(string)).str);
|
||||
if (tcx->log.break_on_warning) debug_break();
|
||||
} break;
|
||||
case log_level_error: {
|
||||
os_console_log(s8_printf(scratch.arena, "%.*s%s%.*s\n", s8_fmtspec(file_and_line), colon, s8_fmtspec(string)).str);
|
||||
if (tcx->log.break_on_error) debug_break();
|
||||
} break;
|
||||
case log_level_fatal: {
|
||||
os_error_box(s8_printf(scratch.arena, "%.*s%s%.*s\n", s8_fmtspec(file_and_line), colon, s8_fmtspec(string)).str);
|
||||
if (tcx->log.break_on_fatal) debug_break();
|
||||
} break;
|
||||
default_is_invalid;
|
||||
}
|
||||
|
||||
ma_end_scratch(scratch);
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
#include "core_basic.h"
|
||||
#include "core_string.h"
|
||||
|
||||
typedef enum {
|
||||
log_level_debug,
|
||||
log_level_info,
|
||||
log_level_warning,
|
||||
log_level_error,
|
||||
log_level_fatal,
|
||||
} log_level_t;
|
||||
|
||||
typedef struct logger_t logger_t;
|
||||
struct logger_t {
|
||||
void (*log_proc)(log_level_t level, s8_t file_and_line, s8_t string);
|
||||
void *user_data;
|
||||
b8 break_on_fatal;
|
||||
b8 break_on_error;
|
||||
b8 break_on_warning;
|
||||
b8 print_file_and_line;
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
// main api
|
||||
#define debugf(...) log_basef(log_level_debug, S8_FILE_AND_LINE, __VA_ARGS__)
|
||||
#define errorf(...) log_basef(log_level_error, S8_FILE_AND_LINE, __VA_ARGS__)
|
||||
#define fatalf(...) (log_basef(log_level_fatal, S8_FILE_AND_LINE, __VA_ARGS__), 0)
|
||||
|
||||
fn void default_log_proc(log_level_t level, s8_t file_and_line, s8_t string);
|
||||
fn void log_basef(log_level_t level, s8_t file_and_line, const char *str, ...);
|
||||
@@ -1,555 +0,0 @@
|
||||
#include "core_math.h"
|
||||
#include "core_intrin.h"
|
||||
|
||||
/*
|
||||
* Converts an RGB color value to HSL. Conversion formula
|
||||
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
|
||||
* also https://gist.github.com/ciembor/1494530
|
||||
* alpha is ignored
|
||||
*/
|
||||
fn v3f32_t v3f32_rgb_to_hsl(v3f32_t color) {
|
||||
v3f32_t result;
|
||||
f32 max = MAX(MAX(color.r, color.g), color.b);
|
||||
f32 min = MIN(MIN(color.r, color.g), color.b);
|
||||
|
||||
result.h = result.s = result.l = (max + min) / 2.f;
|
||||
|
||||
if (max == min) {
|
||||
result.h = result.s = 0.f; // achromatic
|
||||
} else {
|
||||
f32 d = max - min;
|
||||
result.s = (result.l > 0.5f) ? d / (2.f - max - min) : d / (max + min);
|
||||
|
||||
if (max == color.r) {
|
||||
result.h = (color.g - color.b) / d + (color.g < color.b ? 6.f : 0.f);
|
||||
} else if (max == color.g) {
|
||||
result.h = (color.b - color.r) / d + 2.f;
|
||||
} else if (max == color.b) {
|
||||
result.h = (color.r - color.g) / d + 4.f;
|
||||
}
|
||||
|
||||
result.h /= 6.f;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts an HUE to r, g or b.
|
||||
* returns f32 in the set [0, 1].
|
||||
*/
|
||||
fn f32 f32_hue_to_rgb(f32 p, f32 q, f32 t) {
|
||||
if (t < 0.f)
|
||||
t += 1.f;
|
||||
if (t > 1.f)
|
||||
t -= 1.f;
|
||||
if (t < 1.f / 6.f)
|
||||
return p + (q - p) * 6.f * t;
|
||||
if (t < 1.f / 2.f)
|
||||
return q;
|
||||
if (t < 2.f / 3.f)
|
||||
return p + (q - p) * (2.f / 3.f - t) * 6.f;
|
||||
return p;
|
||||
}
|
||||
|
||||
fn v3f32_t v3f32_hsl_to_rgb(v3f32_t color) {
|
||||
v3f32_t result;
|
||||
|
||||
if (0 == color.s) {
|
||||
result.r = result.g = result.b = color.l; // achromatic
|
||||
} else {
|
||||
f32 q = color.l < 0.5f ? color.l * (1.f + color.s)
|
||||
: color.l + color.s - color.l * color.s;
|
||||
f32 p = 2.f * color.l - q;
|
||||
result.r = f32_hue_to_rgb(p, q, color.h + 1.f / 3.f);
|
||||
result.g = f32_hue_to_rgb(p, q, color.h);
|
||||
result.b = f32_hue_to_rgb(p, q, color.h - 1.f / 3.f);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn v4f32_t v4f32_rgba_to_hsla(v4f32_t rgba) {
|
||||
v3f32_t result = v3f32_rgb_to_hsl(rgba.xyz);
|
||||
return (v4f32_t){result.r, result.g, result.b, rgba.a};
|
||||
}
|
||||
|
||||
fn v4f32_t v4f32_hsla_to_rgba(v4f32_t color) {
|
||||
v3f32_t result = v3f32_hsl_to_rgb(color.xyz);
|
||||
return (v4f32_t){result.r, result.g, result.b, color.a};
|
||||
}
|
||||
|
||||
/*
|
||||
https://git.musl-libc.org/cgit/musl/tree/src/math
|
||||
https://gitlab.com/nakst/essence/-/blob/master/shared/math.cpp
|
||||
*/
|
||||
|
||||
|
||||
fn f64 f64_clamp01(f64 a) { return CLAMP(a, 0, 1); }
|
||||
fn f64 f64_lerp(f64 a, f64 b, f64 t) { return (1 - t) * a + t * b; }
|
||||
fn f64 f64_smooth_step(f64 t) { return t*t*(3-2*t); }
|
||||
fn f64 f64_ease_in_quint(f64 x) { return x * x * x * x * x; }
|
||||
fn f64 f64_ease_in_cubic(f64 x) { return x * x * x; }
|
||||
fn f64 f64_ping_pong(f64 x) { return 1.0 - f64_abs(1.0 - f64_mod(x,2)); }
|
||||
|
||||
fn f32 f32_clamp01(f32 a) { return CLAMP(a, 0, 1); }
|
||||
fn f32 f32_lerp(f32 a, f32 b, f32 t) { return (1 - t) * a + t * b; }
|
||||
fn f32 f32_smooth_step(f32 t) { return t*t*(3-2*t); }
|
||||
fn f32 f32_ease_in_quint(f32 x) { return x * x * x * x * x; }
|
||||
fn f32 f32_ease_in_cubic(f32 x) { return x * x * x; }
|
||||
fn f32 f32_ping_pong(f32 x) { return 1.0f - f32_abs(1.0f - f32_mod(x,2)); }
|
||||
|
||||
fn v4f32_t v4f32_lerp(v4f32_t a, v4f32_t b, f32 t) {
|
||||
return (v4f32_t){f32_lerp(a.x, b.x, t), f32_lerp(a.y, b.y, t), f32_lerp(a.z, b.z, t), f32_lerp(a.w, b.w, t)};
|
||||
}
|
||||
|
||||
fn v4f32_t v4f32_lerp_noa(v4f32_t a, v4f32_t b, f32 t) {
|
||||
return (v4f32_t){f32_lerp(a.x, b.x, t), f32_lerp(a.y, b.y, t), f32_lerp(a.z, b.z, t), a.w};
|
||||
}
|
||||
|
||||
fn f64 _Sine(f64 x) {
|
||||
// Calculates sin(x) for x in [0, pi/4].
|
||||
|
||||
f64 x2 = x * x;
|
||||
|
||||
return x * (U64_TO_F64(0x3FF0000000000000) + x2 * (U64_TO_F64(0xBFC5555555555540) + x2 * (U64_TO_F64(0x3F8111111110ED80) + x2 * (U64_TO_F64(0xBF2A01A019AE6000)
|
||||
+ x2 * (U64_TO_F64(0x3EC71DE349280000) + x2 * (U64_TO_F64(0xBE5AE5DC48000000) + x2 * U64_TO_F64(0x3DE5D68200000000)))))));
|
||||
}
|
||||
|
||||
fn f32 _SineFloat(f32 x) {
|
||||
// Calculates sin(x) for x in [0, pi/4].
|
||||
|
||||
f32 x2 = x * x;
|
||||
|
||||
return x * (U32_TO_F32(0x3F800000) + x2 * (U32_TO_F32(0xBE2AAAA0) + x2 * (U32_TO_F32(0x3C0882C0) + x2 * U32_TO_F32(0xB94C6000))));
|
||||
}
|
||||
|
||||
fn f64 _ArcSine(f64 x) {
|
||||
// Calculates arcsin(x) for x in [0, 0.5].
|
||||
|
||||
f64 x2 = x * x;
|
||||
|
||||
return x * (U64_TO_F64(0x3FEFFFFFFFFFFFE6) + x2 * (U64_TO_F64(0x3FC555555555FE00) + x2 * (U64_TO_F64(0x3FB333333292DF90) + x2 * (U64_TO_F64(0x3FA6DB6DFD3693A0)
|
||||
+ x2 * (U64_TO_F64(0x3F9F1C608DE51900) + x2 * (U64_TO_F64(0x3F96EA0659B9A080) + x2 * (U64_TO_F64(0x3F91B4ABF1029100)
|
||||
+ x2 * (U64_TO_F64(0x3F8DA8DAF31ECD00) + x2 * (U64_TO_F64(0x3F81C01FD5000C00) + x2 * (U64_TO_F64(0x3F94BDA038CF6B00)
|
||||
+ x2 * (U64_TO_F64(0xBF8E849CA75B1E00) + x2 * U64_TO_F64(0x3FA146C2D37F2C60))))))))))));
|
||||
}
|
||||
|
||||
fn f32 _ArcSineFloat(f32 x) {
|
||||
// Calculates arcsin(x) for x in [0, 0.5].
|
||||
|
||||
f32 x2 = x * x;
|
||||
|
||||
return x * (U32_TO_F32(0x3F800004) + x2 * (U32_TO_F32(0x3E2AA130) + x2 * (U32_TO_F32(0x3D9B2C28) + x2 * (U32_TO_F32(0x3D1C1800) + x2 * U32_TO_F32(0x3D5A97C0)))));
|
||||
}
|
||||
|
||||
fn f64 _ArcTangent(f64 x) {
|
||||
// Calculates arctan(x) for x in [0, 0.5].
|
||||
|
||||
f64 x2 = x * x;
|
||||
|
||||
return x * (U64_TO_F64(0x3FEFFFFFFFFFFFF8) + x2 * (U64_TO_F64(0xBFD5555555553B44) + x2 * (U64_TO_F64(0x3FC9999999803988) + x2 * (U64_TO_F64(0xBFC249248C882E80)
|
||||
+ x2 * (U64_TO_F64(0x3FBC71C5A4E4C220) + x2 * (U64_TO_F64(0xBFB745B3B75243F0) + x2 * (U64_TO_F64(0x3FB3AFAE9A2939E0)
|
||||
+ x2 * (U64_TO_F64(0xBFB1030C4A4A1B90) + x2 * (U64_TO_F64(0x3FAD6F65C35579A0) + x2 * (U64_TO_F64(0xBFA805BCFDAFEDC0)
|
||||
+ x2 * (U64_TO_F64(0x3F9FC6B5E115F2C0) + x2 * U64_TO_F64(0xBF87DCA5AB25BF80))))))))))));
|
||||
}
|
||||
|
||||
fn f32 _ArcTangentFloat(f32 x) {
|
||||
// Calculates arctan(x) for x in [0, 0.5].
|
||||
|
||||
f32 x2 = x * x;
|
||||
|
||||
return x * (U32_TO_F32(0x3F7FFFF8) + x2 * (U32_TO_F32(0xBEAAA53C) + x2 * (U32_TO_F32(0x3E4BC990) + x2 * (U32_TO_F32(0xBE084A60) + x2 * U32_TO_F32(0x3D8864B0)))));
|
||||
}
|
||||
|
||||
fn f64 _Cosine(f64 x) {
|
||||
// Calculates cos(x) for x in [0, pi/4].
|
||||
|
||||
f64 x2 = x * x;
|
||||
|
||||
return U64_TO_F64(0x3FF0000000000000) + x2 * (U64_TO_F64(0xBFDFFFFFFFFFFFA0) + x2 * (U64_TO_F64(0x3FA555555554F7C0) + x2 * (U64_TO_F64(0xBF56C16C16475C00)
|
||||
+ x2 * (U64_TO_F64(0x3EFA019F87490000) + x2 * (U64_TO_F64(0xBE927DF66B000000) + x2 * U64_TO_F64(0x3E21B949E0000000))))));
|
||||
}
|
||||
|
||||
fn f32 _CosineFloat(f32 x) {
|
||||
// Calculates cos(x) for x in [0, pi/4].
|
||||
|
||||
f32 x2 = x * x;
|
||||
|
||||
return U32_TO_F32(0x3F800000) + x2 * (U32_TO_F32(0xBEFFFFDA) + x2 * (U32_TO_F32(0x3D2A9F60) + x2 * U32_TO_F32(0xBAB22C00)));
|
||||
}
|
||||
|
||||
fn f64 _Tangent(f64 x) {
|
||||
// Calculates tan(x) for x in [0, pi/4].
|
||||
|
||||
f64 x2 = x * x;
|
||||
|
||||
return x * (U64_TO_F64(0x3FEFFFFFFFFFFFE8) + x2 * (U64_TO_F64(0x3FD5555555558000) + x2 * (U64_TO_F64(0x3FC1111110FACF90) + x2 * (U64_TO_F64(0x3FABA1BA266BFD20)
|
||||
+ x2 * (U64_TO_F64(0x3F9664F30E56E580) + x2 * (U64_TO_F64(0x3F822703B08BDC00) + x2 * (U64_TO_F64(0x3F6D698D2E4A4C00)
|
||||
+ x2 * (U64_TO_F64(0x3F57FF4F23EA4400) + x2 * (U64_TO_F64(0x3F424F3BEC845800) + x2 * (U64_TO_F64(0x3F34C78CA9F61000)
|
||||
+ x2 * (U64_TO_F64(0xBF042089F8510000) + x2 * (U64_TO_F64(0x3F29D7372D3A8000) + x2 * (U64_TO_F64(0xBF19D1C5EF6F0000)
|
||||
+ x2 * (U64_TO_F64(0x3F0980BDF11E8000)))))))))))))));
|
||||
}
|
||||
|
||||
fn f32 _TangentFloat(f32 x) {
|
||||
// Calculates tan(x) for x in [0, pi/4].
|
||||
|
||||
f32 x2 = x * x;
|
||||
|
||||
return x * (U32_TO_F32(0x3F800001) + x2 * (U32_TO_F32(0x3EAAA9AA) + x2 * (U32_TO_F32(0x3E08ABA8) + x2 * (U32_TO_F32(0x3D58EC90)
|
||||
+ x2 * (U32_TO_F32(0x3CD24840) + x2 * (U32_TO_F32(0x3AC3CA00) + x2 * U32_TO_F32(0x3C272F00)))))));
|
||||
}
|
||||
|
||||
fn f64 f64_sin(f64 x) {
|
||||
b32 negate = false;
|
||||
|
||||
// x in -infty, infty
|
||||
|
||||
if (x < 0) {
|
||||
x = -x;
|
||||
negate = true;
|
||||
}
|
||||
|
||||
// x in 0, infty
|
||||
|
||||
x -= 2 * F64_PI * f64_floor(x / (2 * F64_PI));
|
||||
|
||||
// x in 0, 2*pi
|
||||
|
||||
if (x < F64_PI / 2) {
|
||||
} else if (x < F64_PI) {
|
||||
x = F64_PI - x;
|
||||
} else if (x < 3 * F64_PI / 2) {
|
||||
x = x - F64_PI;
|
||||
negate = !negate;
|
||||
} else {
|
||||
x = F64_PI * 2 - x;
|
||||
negate = !negate;
|
||||
}
|
||||
|
||||
// x in 0, pi/2
|
||||
|
||||
f64 y = x < F64_PI / 4 ? _Sine(x) : _Cosine(F64_PI / 2 - x);
|
||||
return negate ? -y : y;
|
||||
}
|
||||
|
||||
|
||||
fn f32 f32_sin(f32 x) {
|
||||
b32 negate = false;
|
||||
|
||||
// x in -infty, infty
|
||||
|
||||
if (x < 0) {
|
||||
x = -x;
|
||||
negate = true;
|
||||
}
|
||||
|
||||
// x in 0, infty
|
||||
|
||||
x -= 2 * F32_PI * f32_floor(x / (2 * F32_PI));
|
||||
|
||||
// x in 0, 2*pi
|
||||
|
||||
if (x < F32_PI / 2) {
|
||||
} else if (x < F32_PI) {
|
||||
x = F32_PI - x;
|
||||
} else if (x < 3 * F32_PI / 2) {
|
||||
x = x - F32_PI;
|
||||
negate = !negate;
|
||||
} else {
|
||||
x = F32_PI * 2 - x;
|
||||
negate = !negate;
|
||||
}
|
||||
|
||||
// x in 0, pi/2
|
||||
|
||||
f32 y = x < F32_PI / 4 ? _SineFloat(x) : _CosineFloat(F32_PI / 2 - x);
|
||||
return negate ? -y : y;
|
||||
}
|
||||
|
||||
fn f64 f64_cos(f64 x) {
|
||||
b32 negate = false;
|
||||
|
||||
// x in -infty, infty
|
||||
|
||||
if (x < 0) {
|
||||
x = -x;
|
||||
}
|
||||
|
||||
// x in 0, infty
|
||||
|
||||
x -= 2 * F64_PI * f64_floor(x / (2 * F64_PI));
|
||||
|
||||
// x in 0, 2*pi
|
||||
|
||||
if (x < F64_PI / 2) {
|
||||
} else if (x < F64_PI) {
|
||||
x = F64_PI - x;
|
||||
negate = !negate;
|
||||
} else if (x < 3 * F64_PI / 2) {
|
||||
x = x - F64_PI;
|
||||
negate = !negate;
|
||||
} else {
|
||||
x = F64_PI * 2 - x;
|
||||
}
|
||||
|
||||
// x in 0, pi/2
|
||||
|
||||
f64 y = x < F64_PI / 4 ? _Cosine(x) : _Sine(F64_PI / 2 - x);
|
||||
return negate ? -y : y;
|
||||
}
|
||||
|
||||
fn f32 f32_cos(f32 x) {
|
||||
b32 negate = false;
|
||||
|
||||
// x in -infty, infty
|
||||
|
||||
if (x < 0) {
|
||||
x = -x;
|
||||
}
|
||||
|
||||
// x in 0, infty
|
||||
|
||||
x -= 2 * F32_PI * f32_floor(x / (2 * F32_PI));
|
||||
|
||||
// x in 0, 2*pi
|
||||
|
||||
if (x < F32_PI / 2) {
|
||||
} else if (x < F32_PI) {
|
||||
x = F32_PI - x;
|
||||
negate = !negate;
|
||||
} else if (x < 3 * F32_PI / 2) {
|
||||
x = x - F32_PI;
|
||||
negate = !negate;
|
||||
} else {
|
||||
x = F32_PI * 2 - x;
|
||||
}
|
||||
|
||||
// x in 0, pi/2
|
||||
|
||||
f32 y = x < F32_PI / 4 ? _CosineFloat(x) : _SineFloat(F32_PI / 2 - x);
|
||||
return negate ? -y : y;
|
||||
}
|
||||
|
||||
fn f64 f64_tan(f64 x) {
|
||||
b32 negate = false;
|
||||
|
||||
// x in -infty, infty
|
||||
|
||||
if (x < 0) {
|
||||
x = -x;
|
||||
negate = !negate;
|
||||
}
|
||||
|
||||
// x in 0, infty
|
||||
|
||||
x -= F64_PI * f64_floor(x / F64_PI);
|
||||
|
||||
// x in 0, pi
|
||||
|
||||
if (x > F64_PI / 2) {
|
||||
x = F64_PI - x;
|
||||
negate = !negate;
|
||||
}
|
||||
|
||||
// x in 0, pi/2
|
||||
|
||||
f64 y = x < F64_PI / 4 ? _Tangent(x) : (1.0 / _Tangent(F64_PI / 2 - x));
|
||||
return negate ? -y : y;
|
||||
}
|
||||
|
||||
fn f32 f32_tan(f32 x) {
|
||||
b32 negate = false;
|
||||
|
||||
// x in -infty, infty
|
||||
|
||||
if (x < 0) {
|
||||
x = -x;
|
||||
negate = !negate;
|
||||
}
|
||||
|
||||
// x in 0, infty
|
||||
|
||||
x -= F32_PI * f32_floor(x / F32_PI);
|
||||
|
||||
// x in 0, pi
|
||||
|
||||
if (x > F32_PI / 2) {
|
||||
x = F32_PI - x;
|
||||
negate = !negate;
|
||||
}
|
||||
|
||||
// x in 0, pi/2
|
||||
|
||||
f32 y = x < F32_PI / 4 ? _TangentFloat(x) : (1.0f / _TangentFloat(F32_PI / 2 - x));
|
||||
return negate ? -y : y;
|
||||
}
|
||||
|
||||
fn f64 f64_asin(f64 x) {
|
||||
b32 negate = false;
|
||||
|
||||
if (x < 0) {
|
||||
x = -x;
|
||||
negate = true;
|
||||
}
|
||||
|
||||
f64 y;
|
||||
|
||||
if (x < 0.5) {
|
||||
y = _ArcSine(x);
|
||||
} else {
|
||||
y = F64_PI / 2 - 2 * _ArcSine(f64_sqrt(0.5 - 0.5 * x));
|
||||
}
|
||||
|
||||
return negate ? -y : y;
|
||||
}
|
||||
|
||||
fn f32 f32_asin(f32 x) {
|
||||
b32 negate = false;
|
||||
|
||||
if (x < 0) {
|
||||
x = -x;
|
||||
negate = true;
|
||||
}
|
||||
|
||||
f32 y;
|
||||
|
||||
if (x < 0.5) {
|
||||
y = _ArcSineFloat(x);
|
||||
} else {
|
||||
y = F32_PI / 2 - 2 * _ArcSineFloat(f32_sqrt(0.5f - 0.5f * x));
|
||||
}
|
||||
|
||||
return negate ? -y : y;
|
||||
}
|
||||
|
||||
fn f64 f64_acos(f64 x) {
|
||||
return f64_asin(-x) + F64_PI / 2;
|
||||
}
|
||||
|
||||
fn f32 f32_acos(f32 x) {
|
||||
return f32_asin(-x) + F32_PI / 2;
|
||||
}
|
||||
|
||||
fn f64 f64_atan(f64 x) {
|
||||
b32 negate = false;
|
||||
|
||||
if (x < 0) {
|
||||
x = -x;
|
||||
negate = true;
|
||||
}
|
||||
|
||||
b32 reciprocalTaken = false;
|
||||
|
||||
if (x > 1) {
|
||||
x = 1 / x;
|
||||
reciprocalTaken = true;
|
||||
}
|
||||
|
||||
f64 y;
|
||||
|
||||
if (x < 0.5) {
|
||||
y = _ArcTangent(x);
|
||||
} else {
|
||||
y = 0.463647609000806116 + _ArcTangent((2 * x - 1) / (2 + x));
|
||||
}
|
||||
|
||||
if (reciprocalTaken) {
|
||||
y = F64_PI / 2 - y;
|
||||
}
|
||||
|
||||
return negate ? -y : y;
|
||||
}
|
||||
|
||||
fn f32 f32_atan(f32 x) {
|
||||
b32 negate = false;
|
||||
|
||||
if (x < 0) {
|
||||
x = -x;
|
||||
negate = true;
|
||||
}
|
||||
|
||||
b32 reciprocalTaken = false;
|
||||
|
||||
if (x > 1) {
|
||||
x = 1 / x;
|
||||
reciprocalTaken = true;
|
||||
}
|
||||
|
||||
f32 y;
|
||||
|
||||
if (x < 0.5f) {
|
||||
y = _ArcTangentFloat(x);
|
||||
} else {
|
||||
y = 0.463647609000806116f + _ArcTangentFloat((2 * x - 1) / (2 + x));
|
||||
}
|
||||
|
||||
if (reciprocalTaken) {
|
||||
y = F32_PI / 2 - y;
|
||||
}
|
||||
|
||||
return negate ? -y : y;
|
||||
}
|
||||
|
||||
fn f64 f64_atan2(f64 y, f64 x) {
|
||||
if (x == 0) return y > 0 ? F64_PI / 2 : -F64_PI / 2;
|
||||
else if (x > 0) return f64_atan(y / x);
|
||||
else if (y >= 0) return F64_PI + f64_atan(y / x);
|
||||
else return -F64_PI + f64_atan(y / x);
|
||||
}
|
||||
|
||||
fn f32 f32_atan2(f32 y, f32 x) {
|
||||
if (x == 0) return y > 0 ? F32_PI / 2 : -F32_PI / 2;
|
||||
else if (x > 0) return f32_atan(y / x);
|
||||
else if (y >= 0) return F32_PI + f32_atan(y / x);
|
||||
else return -F32_PI + f32_atan(y / x);
|
||||
}
|
||||
|
||||
fn b32 f32_are_equal(f32 a, f32 b) {
|
||||
f32 diff = f32_abs(a - b);
|
||||
b32 result = diff <= 0.00001f;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 f64_are_equal(f64 a, f64 b) {
|
||||
f64 diff = f64_abs(a - b);
|
||||
b32 result = diff <= 0.00001;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn f32 f32_ease_out_bounce(f32 x) {
|
||||
f32 n1 = 7.5625f;
|
||||
f32 d1 = 2.75f;
|
||||
|
||||
if (x < 1.f / d1) {
|
||||
return n1 * x * x;
|
||||
} else if (x < 2.f / d1) {
|
||||
return n1 * (x -= 1.5f / d1) * x + 0.75f;
|
||||
} else if (x < 2.5f / d1) {
|
||||
return n1 * (x -= 2.25f / d1) * x + 0.9375f;
|
||||
} else {
|
||||
return n1 * (x -= 2.625f / d1) * x + 0.984375f;
|
||||
}
|
||||
}
|
||||
|
||||
fn f32 f32_ease_out_elastic(f32 x) {
|
||||
f32 c4 = (2 * F32_PI) / 3;
|
||||
|
||||
return x == 0.f
|
||||
? 0
|
||||
: x == 1.f
|
||||
? 1
|
||||
: f32_pow(2.f, -10.f * x) * f32_sin((x * 10.f - 0.75f) * c4) + 1.f;
|
||||
}
|
||||
|
||||
fn f32 f32_ease_out_cubic(f32 x) {
|
||||
return 1.f - f32_pow(1.f - x, 3.f);
|
||||
}
|
||||
|
||||
fn f32 f32_ease_out_n(f32 x, f32 n) {
|
||||
return 1.f - f32_pow(1.f - x, n);
|
||||
}
|
||||
|
||||
fn r2f32_t r2f32_lerp(r2f32_t a, r2f32_t b, f32 t) {
|
||||
r2f32_t result = { .min = {f32_lerp(a.min.x, b.min.x, t), f32_lerp(a.min.y, b.min.y, t)}, .max = {f32_lerp(a.max.x, b.max.x, t), f32_lerp(a.max.y, b.max.y, t)} };
|
||||
return result;
|
||||
}
|
||||
@@ -1,959 +0,0 @@
|
||||
#include "core_math.h"
|
||||
#include "core_intrin.h"
|
||||
|
||||
fn_inline v2f32_t v2f32(f32 x, f32 y) { return (v2f32_t){ x, y }; }
|
||||
gb_read_only v2f32_t v2f32_null = {0};
|
||||
fn_inline b32 v2f32_is_null(v2f32_t a) { return memory_equal(&a, &v2f32_null, sizeof(a)); }
|
||||
fn_inline b32 v2f32_are_equal(v2f32_t a, v2f32_t b) { return memory_equal(&a, &b, sizeof(a)); }
|
||||
fn_inline v2f32_t v2f32_add(v2f32_t a, v2f32_t b) { return (v2f32_t){ a.x + b.x, a.y + b.y }; }
|
||||
fn_inline v2f32_t v2f32_adds(v2f32_t a, f32 b) { return (v2f32_t){ a.x + b, a.y + b }; }
|
||||
fn_inline v2f32_t v2f32_sub(v2f32_t a, v2f32_t b) { return (v2f32_t){ a.x - b.x, a.y - b.y }; }
|
||||
fn_inline v2f32_t v2f32_subs(v2f32_t a, f32 b) { return (v2f32_t){ a.x - b, a.y - b }; }
|
||||
fn_inline v2f32_t v2f32_mul(v2f32_t a, v2f32_t b) { return (v2f32_t){ a.x * b.x, a.y * b.y }; }
|
||||
fn_inline v2f32_t v2f32_muls(v2f32_t a, f32 b) { return (v2f32_t){ a.x * b, a.y * b }; }
|
||||
fn_inline v2f32_t v2f32_div(v2f32_t a, v2f32_t b) { return (v2f32_t){ a.x / b.x, a.y / b.y }; }
|
||||
fn_inline v2f32_t v2f32_divs(v2f32_t a, f32 b) { return (v2f32_t){ a.x / b, a.y / b }; }
|
||||
fn_inline v3f32_t v3f32(f32 x, f32 y, f32 z) { return (v3f32_t){ x, y, z }; }
|
||||
gb_read_only v3f32_t v3f32_null = {0};
|
||||
fn_inline b32 v3f32_is_null(v3f32_t a) { return memory_equal(&a, &v3f32_null, sizeof(a)); }
|
||||
fn_inline b32 v3f32_are_equal(v3f32_t a, v3f32_t b) { return memory_equal(&a, &b, sizeof(a)); }
|
||||
fn_inline v3f32_t v3f32_add(v3f32_t a, v3f32_t b) { return (v3f32_t){ a.x + b.x, a.y + b.y, a.z + b.z }; }
|
||||
fn_inline v3f32_t v3f32_adds(v3f32_t a, f32 b) { return (v3f32_t){ a.x + b, a.y + b, a.z + b }; }
|
||||
fn_inline v3f32_t v3f32_sub(v3f32_t a, v3f32_t b) { return (v3f32_t){ a.x - b.x, a.y - b.y, a.z - b.z }; }
|
||||
fn_inline v3f32_t v3f32_subs(v3f32_t a, f32 b) { return (v3f32_t){ a.x - b, a.y - b, a.z - b }; }
|
||||
fn_inline v3f32_t v3f32_mul(v3f32_t a, v3f32_t b) { return (v3f32_t){ a.x * b.x, a.y * b.y, a.z * b.z }; }
|
||||
fn_inline v3f32_t v3f32_muls(v3f32_t a, f32 b) { return (v3f32_t){ a.x * b, a.y * b, a.z * b }; }
|
||||
fn_inline v3f32_t v3f32_div(v3f32_t a, v3f32_t b) { return (v3f32_t){ a.x / b.x, a.y / b.y, a.z / b.z }; }
|
||||
fn_inline v3f32_t v3f32_divs(v3f32_t a, f32 b) { return (v3f32_t){ a.x / b, a.y / b, a.z / b }; }
|
||||
fn_inline v4f32_t v4f32(f32 x, f32 y, f32 z, f32 w) { return (v4f32_t){ x, y, z, w }; }
|
||||
gb_read_only v4f32_t v4f32_null = {0};
|
||||
fn_inline b32 v4f32_is_null(v4f32_t a) { return memory_equal(&a, &v4f32_null, sizeof(a)); }
|
||||
fn_inline b32 v4f32_are_equal(v4f32_t a, v4f32_t b) { return memory_equal(&a, &b, sizeof(a)); }
|
||||
fn_inline v4f32_t v4f32_add(v4f32_t a, v4f32_t b) { return (v4f32_t){ a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w }; }
|
||||
fn_inline v4f32_t v4f32_adds(v4f32_t a, f32 b) { return (v4f32_t){ a.x + b, a.y + b, a.z + b, a.w + b }; }
|
||||
fn_inline v4f32_t v4f32_sub(v4f32_t a, v4f32_t b) { return (v4f32_t){ a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w }; }
|
||||
fn_inline v4f32_t v4f32_subs(v4f32_t a, f32 b) { return (v4f32_t){ a.x - b, a.y - b, a.z - b, a.w - b }; }
|
||||
fn_inline v4f32_t v4f32_mul(v4f32_t a, v4f32_t b) { return (v4f32_t){ a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w }; }
|
||||
fn_inline v4f32_t v4f32_muls(v4f32_t a, f32 b) { return (v4f32_t){ a.x * b, a.y * b, a.z * b, a.w * b }; }
|
||||
fn_inline v4f32_t v4f32_div(v4f32_t a, v4f32_t b) { return (v4f32_t){ a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w }; }
|
||||
fn_inline v4f32_t v4f32_divs(v4f32_t a, f32 b) { return (v4f32_t){ a.x / b, a.y / b, a.z / b, a.w / b }; }
|
||||
fn_inline r2f32_t r2f32(f32 x0, f32 y0, f32 x1, f32 y1) { return (r2f32_t){ x0, y0, x1, y1 }; }
|
||||
gb_read_only r2f32_t r2f32_null = {0};
|
||||
fn_inline b32 r2f32_is_null(r2f32_t a) { return memory_equal(&a, &r2f32_null, sizeof(a)); }
|
||||
fn_inline b32 r2f32_are_equal(r2f32_t a, r2f32_t b) { return memory_equal(&a, &b, sizeof(a)); }
|
||||
fn_inline r2f32_t r2f32_add(r2f32_t a, r2f32_t b) { return (r2f32_t){ a.x0 + b.x0, a.y0 + b.y0, a.x1 + b.x1, a.y1 + b.y1 }; }
|
||||
fn_inline r2f32_t r2f32_adds(r2f32_t a, f32 b) { return (r2f32_t){ a.x0 + b, a.y0 + b, a.x1 + b, a.y1 + b }; }
|
||||
fn_inline r2f32_t r2f32_sub(r2f32_t a, r2f32_t b) { return (r2f32_t){ a.x0 - b.x0, a.y0 - b.y0, a.x1 - b.x1, a.y1 - b.y1 }; }
|
||||
fn_inline r2f32_t r2f32_subs(r2f32_t a, f32 b) { return (r2f32_t){ a.x0 - b, a.y0 - b, a.x1 - b, a.y1 - b }; }
|
||||
fn_inline r2f32_t r2f32_mul(r2f32_t a, r2f32_t b) { return (r2f32_t){ a.x0 * b.x0, a.y0 * b.y0, a.x1 * b.x1, a.y1 * b.y1 }; }
|
||||
fn_inline r2f32_t r2f32_muls(r2f32_t a, f32 b) { return (r2f32_t){ a.x0 * b, a.y0 * b, a.x1 * b, a.y1 * b }; }
|
||||
fn_inline r2f32_t r2f32_div(r2f32_t a, r2f32_t b) { return (r2f32_t){ a.x0 / b.x0, a.y0 / b.y0, a.x1 / b.x1, a.y1 / b.y1 }; }
|
||||
fn_inline r2f32_t r2f32_divs(r2f32_t a, f32 b) { return (r2f32_t){ a.x0 / b, a.y0 / b, a.x1 / b, a.y1 / b }; }
|
||||
fn_inline r3f32_t r3f32(f32 x0, f32 y0, f32 z0, f32 x1, f32 y1, f32 z1) { return (r3f32_t){ x0, y0, z0, x1, y1, z1 }; }
|
||||
gb_read_only r3f32_t r3f32_null = {0};
|
||||
fn_inline b32 r3f32_is_null(r3f32_t a) { return memory_equal(&a, &r3f32_null, sizeof(a)); }
|
||||
fn_inline b32 r3f32_are_equal(r3f32_t a, r3f32_t b) { return memory_equal(&a, &b, sizeof(a)); }
|
||||
fn_inline r3f32_t r3f32_add(r3f32_t a, r3f32_t b) { return (r3f32_t){ a.x0 + b.x0, a.y0 + b.y0, a.z0 + b.z0, a.x1 + b.x1, a.y1 + b.y1, a.z1 + b.z1 }; }
|
||||
fn_inline r3f32_t r3f32_adds(r3f32_t a, f32 b) { return (r3f32_t){ a.x0 + b, a.y0 + b, a.z0 + b, a.x1 + b, a.y1 + b, a.z1 + b }; }
|
||||
fn_inline r3f32_t r3f32_sub(r3f32_t a, r3f32_t b) { return (r3f32_t){ a.x0 - b.x0, a.y0 - b.y0, a.z0 - b.z0, a.x1 - b.x1, a.y1 - b.y1, a.z1 - b.z1 }; }
|
||||
fn_inline r3f32_t r3f32_subs(r3f32_t a, f32 b) { return (r3f32_t){ a.x0 - b, a.y0 - b, a.z0 - b, a.x1 - b, a.y1 - b, a.z1 - b }; }
|
||||
fn_inline r3f32_t r3f32_mul(r3f32_t a, r3f32_t b) { return (r3f32_t){ a.x0 * b.x0, a.y0 * b.y0, a.z0 * b.z0, a.x1 * b.x1, a.y1 * b.y1, a.z1 * b.z1 }; }
|
||||
fn_inline r3f32_t r3f32_muls(r3f32_t a, f32 b) { return (r3f32_t){ a.x0 * b, a.y0 * b, a.z0 * b, a.x1 * b, a.y1 * b, a.z1 * b }; }
|
||||
fn_inline r3f32_t r3f32_div(r3f32_t a, r3f32_t b) { return (r3f32_t){ a.x0 / b.x0, a.y0 / b.y0, a.z0 / b.z0, a.x1 / b.x1, a.y1 / b.y1, a.z1 / b.z1 }; }
|
||||
fn_inline r3f32_t r3f32_divs(r3f32_t a, f32 b) { return (r3f32_t){ a.x0 / b, a.y0 / b, a.z0 / b, a.x1 / b, a.y1 / b, a.z1 / b }; }
|
||||
fn_inline v2f64_t v2f64(f64 x, f64 y) { return (v2f64_t){ x, y }; }
|
||||
gb_read_only v2f64_t v2f64_null = {0};
|
||||
fn_inline b32 v2f64_is_null(v2f64_t a) { return memory_equal(&a, &v2f64_null, sizeof(a)); }
|
||||
fn_inline b32 v2f64_are_equal(v2f64_t a, v2f64_t b) { return memory_equal(&a, &b, sizeof(a)); }
|
||||
fn_inline v2f64_t v2f64_add(v2f64_t a, v2f64_t b) { return (v2f64_t){ a.x + b.x, a.y + b.y }; }
|
||||
fn_inline v2f64_t v2f64_adds(v2f64_t a, f64 b) { return (v2f64_t){ a.x + b, a.y + b }; }
|
||||
fn_inline v2f64_t v2f64_sub(v2f64_t a, v2f64_t b) { return (v2f64_t){ a.x - b.x, a.y - b.y }; }
|
||||
fn_inline v2f64_t v2f64_subs(v2f64_t a, f64 b) { return (v2f64_t){ a.x - b, a.y - b }; }
|
||||
fn_inline v2f64_t v2f64_mul(v2f64_t a, v2f64_t b) { return (v2f64_t){ a.x * b.x, a.y * b.y }; }
|
||||
fn_inline v2f64_t v2f64_muls(v2f64_t a, f64 b) { return (v2f64_t){ a.x * b, a.y * b }; }
|
||||
fn_inline v2f64_t v2f64_div(v2f64_t a, v2f64_t b) { return (v2f64_t){ a.x / b.x, a.y / b.y }; }
|
||||
fn_inline v2f64_t v2f64_divs(v2f64_t a, f64 b) { return (v2f64_t){ a.x / b, a.y / b }; }
|
||||
fn_inline v3f64_t v3f64(f64 x, f64 y, f64 z) { return (v3f64_t){ x, y, z }; }
|
||||
gb_read_only v3f64_t v3f64_null = {0};
|
||||
fn_inline b32 v3f64_is_null(v3f64_t a) { return memory_equal(&a, &v3f64_null, sizeof(a)); }
|
||||
fn_inline b32 v3f64_are_equal(v3f64_t a, v3f64_t b) { return memory_equal(&a, &b, sizeof(a)); }
|
||||
fn_inline v3f64_t v3f64_add(v3f64_t a, v3f64_t b) { return (v3f64_t){ a.x + b.x, a.y + b.y, a.z + b.z }; }
|
||||
fn_inline v3f64_t v3f64_adds(v3f64_t a, f64 b) { return (v3f64_t){ a.x + b, a.y + b, a.z + b }; }
|
||||
fn_inline v3f64_t v3f64_sub(v3f64_t a, v3f64_t b) { return (v3f64_t){ a.x - b.x, a.y - b.y, a.z - b.z }; }
|
||||
fn_inline v3f64_t v3f64_subs(v3f64_t a, f64 b) { return (v3f64_t){ a.x - b, a.y - b, a.z - b }; }
|
||||
fn_inline v3f64_t v3f64_mul(v3f64_t a, v3f64_t b) { return (v3f64_t){ a.x * b.x, a.y * b.y, a.z * b.z }; }
|
||||
fn_inline v3f64_t v3f64_muls(v3f64_t a, f64 b) { return (v3f64_t){ a.x * b, a.y * b, a.z * b }; }
|
||||
fn_inline v3f64_t v3f64_div(v3f64_t a, v3f64_t b) { return (v3f64_t){ a.x / b.x, a.y / b.y, a.z / b.z }; }
|
||||
fn_inline v3f64_t v3f64_divs(v3f64_t a, f64 b) { return (v3f64_t){ a.x / b, a.y / b, a.z / b }; }
|
||||
fn_inline v4f64_t v4f64(f64 x, f64 y, f64 z, f64 w) { return (v4f64_t){ x, y, z, w }; }
|
||||
gb_read_only v4f64_t v4f64_null = {0};
|
||||
fn_inline b32 v4f64_is_null(v4f64_t a) { return memory_equal(&a, &v4f64_null, sizeof(a)); }
|
||||
fn_inline b32 v4f64_are_equal(v4f64_t a, v4f64_t b) { return memory_equal(&a, &b, sizeof(a)); }
|
||||
fn_inline v4f64_t v4f64_add(v4f64_t a, v4f64_t b) { return (v4f64_t){ a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w }; }
|
||||
fn_inline v4f64_t v4f64_adds(v4f64_t a, f64 b) { return (v4f64_t){ a.x + b, a.y + b, a.z + b, a.w + b }; }
|
||||
fn_inline v4f64_t v4f64_sub(v4f64_t a, v4f64_t b) { return (v4f64_t){ a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w }; }
|
||||
fn_inline v4f64_t v4f64_subs(v4f64_t a, f64 b) { return (v4f64_t){ a.x - b, a.y - b, a.z - b, a.w - b }; }
|
||||
fn_inline v4f64_t v4f64_mul(v4f64_t a, v4f64_t b) { return (v4f64_t){ a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w }; }
|
||||
fn_inline v4f64_t v4f64_muls(v4f64_t a, f64 b) { return (v4f64_t){ a.x * b, a.y * b, a.z * b, a.w * b }; }
|
||||
fn_inline v4f64_t v4f64_div(v4f64_t a, v4f64_t b) { return (v4f64_t){ a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w }; }
|
||||
fn_inline v4f64_t v4f64_divs(v4f64_t a, f64 b) { return (v4f64_t){ a.x / b, a.y / b, a.z / b, a.w / b }; }
|
||||
fn_inline r2f64_t r2f64(f64 x0, f64 y0, f64 x1, f64 y1) { return (r2f64_t){ x0, y0, x1, y1 }; }
|
||||
gb_read_only r2f64_t r2f64_null = {0};
|
||||
fn_inline b32 r2f64_is_null(r2f64_t a) { return memory_equal(&a, &r2f64_null, sizeof(a)); }
|
||||
fn_inline b32 r2f64_are_equal(r2f64_t a, r2f64_t b) { return memory_equal(&a, &b, sizeof(a)); }
|
||||
fn_inline r2f64_t r2f64_add(r2f64_t a, r2f64_t b) { return (r2f64_t){ a.x0 + b.x0, a.y0 + b.y0, a.x1 + b.x1, a.y1 + b.y1 }; }
|
||||
fn_inline r2f64_t r2f64_adds(r2f64_t a, f64 b) { return (r2f64_t){ a.x0 + b, a.y0 + b, a.x1 + b, a.y1 + b }; }
|
||||
fn_inline r2f64_t r2f64_sub(r2f64_t a, r2f64_t b) { return (r2f64_t){ a.x0 - b.x0, a.y0 - b.y0, a.x1 - b.x1, a.y1 - b.y1 }; }
|
||||
fn_inline r2f64_t r2f64_subs(r2f64_t a, f64 b) { return (r2f64_t){ a.x0 - b, a.y0 - b, a.x1 - b, a.y1 - b }; }
|
||||
fn_inline r2f64_t r2f64_mul(r2f64_t a, r2f64_t b) { return (r2f64_t){ a.x0 * b.x0, a.y0 * b.y0, a.x1 * b.x1, a.y1 * b.y1 }; }
|
||||
fn_inline r2f64_t r2f64_muls(r2f64_t a, f64 b) { return (r2f64_t){ a.x0 * b, a.y0 * b, a.x1 * b, a.y1 * b }; }
|
||||
fn_inline r2f64_t r2f64_div(r2f64_t a, r2f64_t b) { return (r2f64_t){ a.x0 / b.x0, a.y0 / b.y0, a.x1 / b.x1, a.y1 / b.y1 }; }
|
||||
fn_inline r2f64_t r2f64_divs(r2f64_t a, f64 b) { return (r2f64_t){ a.x0 / b, a.y0 / b, a.x1 / b, a.y1 / b }; }
|
||||
fn_inline r3f64_t r3f64(f64 x0, f64 y0, f64 z0, f64 x1, f64 y1, f64 z1) { return (r3f64_t){ x0, y0, z0, x1, y1, z1 }; }
|
||||
gb_read_only r3f64_t r3f64_null = {0};
|
||||
fn_inline b32 r3f64_is_null(r3f64_t a) { return memory_equal(&a, &r3f64_null, sizeof(a)); }
|
||||
fn_inline b32 r3f64_are_equal(r3f64_t a, r3f64_t b) { return memory_equal(&a, &b, sizeof(a)); }
|
||||
fn_inline r3f64_t r3f64_add(r3f64_t a, r3f64_t b) { return (r3f64_t){ a.x0 + b.x0, a.y0 + b.y0, a.z0 + b.z0, a.x1 + b.x1, a.y1 + b.y1, a.z1 + b.z1 }; }
|
||||
fn_inline r3f64_t r3f64_adds(r3f64_t a, f64 b) { return (r3f64_t){ a.x0 + b, a.y0 + b, a.z0 + b, a.x1 + b, a.y1 + b, a.z1 + b }; }
|
||||
fn_inline r3f64_t r3f64_sub(r3f64_t a, r3f64_t b) { return (r3f64_t){ a.x0 - b.x0, a.y0 - b.y0, a.z0 - b.z0, a.x1 - b.x1, a.y1 - b.y1, a.z1 - b.z1 }; }
|
||||
fn_inline r3f64_t r3f64_subs(r3f64_t a, f64 b) { return (r3f64_t){ a.x0 - b, a.y0 - b, a.z0 - b, a.x1 - b, a.y1 - b, a.z1 - b }; }
|
||||
fn_inline r3f64_t r3f64_mul(r3f64_t a, r3f64_t b) { return (r3f64_t){ a.x0 * b.x0, a.y0 * b.y0, a.z0 * b.z0, a.x1 * b.x1, a.y1 * b.y1, a.z1 * b.z1 }; }
|
||||
fn_inline r3f64_t r3f64_muls(r3f64_t a, f64 b) { return (r3f64_t){ a.x0 * b, a.y0 * b, a.z0 * b, a.x1 * b, a.y1 * b, a.z1 * b }; }
|
||||
fn_inline r3f64_t r3f64_div(r3f64_t a, r3f64_t b) { return (r3f64_t){ a.x0 / b.x0, a.y0 / b.y0, a.z0 / b.z0, a.x1 / b.x1, a.y1 / b.y1, a.z1 / b.z1 }; }
|
||||
fn_inline r3f64_t r3f64_divs(r3f64_t a, f64 b) { return (r3f64_t){ a.x0 / b, a.y0 / b, a.z0 / b, a.x1 / b, a.y1 / b, a.z1 / b }; }
|
||||
fn_inline v2i32_t v2i32(i32 x, i32 y) { return (v2i32_t){ x, y }; }
|
||||
gb_read_only v2i32_t v2i32_null = {0};
|
||||
fn_inline b32 v2i32_is_null(v2i32_t a) { return memory_equal(&a, &v2i32_null, sizeof(a)); }
|
||||
fn_inline b32 v2i32_are_equal(v2i32_t a, v2i32_t b) { return memory_equal(&a, &b, sizeof(a)); }
|
||||
fn_inline v2i32_t v2i32_add(v2i32_t a, v2i32_t b) { return (v2i32_t){ a.x + b.x, a.y + b.y }; }
|
||||
fn_inline v2i32_t v2i32_adds(v2i32_t a, i32 b) { return (v2i32_t){ a.x + b, a.y + b }; }
|
||||
fn_inline v2i32_t v2i32_sub(v2i32_t a, v2i32_t b) { return (v2i32_t){ a.x - b.x, a.y - b.y }; }
|
||||
fn_inline v2i32_t v2i32_subs(v2i32_t a, i32 b) { return (v2i32_t){ a.x - b, a.y - b }; }
|
||||
fn_inline v2i32_t v2i32_mul(v2i32_t a, v2i32_t b) { return (v2i32_t){ a.x * b.x, a.y * b.y }; }
|
||||
fn_inline v2i32_t v2i32_muls(v2i32_t a, i32 b) { return (v2i32_t){ a.x * b, a.y * b }; }
|
||||
fn_inline v2i32_t v2i32_div(v2i32_t a, v2i32_t b) { return (v2i32_t){ a.x / b.x, a.y / b.y }; }
|
||||
fn_inline v2i32_t v2i32_divs(v2i32_t a, i32 b) { return (v2i32_t){ a.x / b, a.y / b }; }
|
||||
fn_inline v3i32_t v3i32(i32 x, i32 y, i32 z) { return (v3i32_t){ x, y, z }; }
|
||||
gb_read_only v3i32_t v3i32_null = {0};
|
||||
fn_inline b32 v3i32_is_null(v3i32_t a) { return memory_equal(&a, &v3i32_null, sizeof(a)); }
|
||||
fn_inline b32 v3i32_are_equal(v3i32_t a, v3i32_t b) { return memory_equal(&a, &b, sizeof(a)); }
|
||||
fn_inline v3i32_t v3i32_add(v3i32_t a, v3i32_t b) { return (v3i32_t){ a.x + b.x, a.y + b.y, a.z + b.z }; }
|
||||
fn_inline v3i32_t v3i32_adds(v3i32_t a, i32 b) { return (v3i32_t){ a.x + b, a.y + b, a.z + b }; }
|
||||
fn_inline v3i32_t v3i32_sub(v3i32_t a, v3i32_t b) { return (v3i32_t){ a.x - b.x, a.y - b.y, a.z - b.z }; }
|
||||
fn_inline v3i32_t v3i32_subs(v3i32_t a, i32 b) { return (v3i32_t){ a.x - b, a.y - b, a.z - b }; }
|
||||
fn_inline v3i32_t v3i32_mul(v3i32_t a, v3i32_t b) { return (v3i32_t){ a.x * b.x, a.y * b.y, a.z * b.z }; }
|
||||
fn_inline v3i32_t v3i32_muls(v3i32_t a, i32 b) { return (v3i32_t){ a.x * b, a.y * b, a.z * b }; }
|
||||
fn_inline v3i32_t v3i32_div(v3i32_t a, v3i32_t b) { return (v3i32_t){ a.x / b.x, a.y / b.y, a.z / b.z }; }
|
||||
fn_inline v3i32_t v3i32_divs(v3i32_t a, i32 b) { return (v3i32_t){ a.x / b, a.y / b, a.z / b }; }
|
||||
fn_inline v4i32_t v4i32(i32 x, i32 y, i32 z, i32 w) { return (v4i32_t){ x, y, z, w }; }
|
||||
gb_read_only v4i32_t v4i32_null = {0};
|
||||
fn_inline b32 v4i32_is_null(v4i32_t a) { return memory_equal(&a, &v4i32_null, sizeof(a)); }
|
||||
fn_inline b32 v4i32_are_equal(v4i32_t a, v4i32_t b) { return memory_equal(&a, &b, sizeof(a)); }
|
||||
fn_inline v4i32_t v4i32_add(v4i32_t a, v4i32_t b) { return (v4i32_t){ a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w }; }
|
||||
fn_inline v4i32_t v4i32_adds(v4i32_t a, i32 b) { return (v4i32_t){ a.x + b, a.y + b, a.z + b, a.w + b }; }
|
||||
fn_inline v4i32_t v4i32_sub(v4i32_t a, v4i32_t b) { return (v4i32_t){ a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w }; }
|
||||
fn_inline v4i32_t v4i32_subs(v4i32_t a, i32 b) { return (v4i32_t){ a.x - b, a.y - b, a.z - b, a.w - b }; }
|
||||
fn_inline v4i32_t v4i32_mul(v4i32_t a, v4i32_t b) { return (v4i32_t){ a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w }; }
|
||||
fn_inline v4i32_t v4i32_muls(v4i32_t a, i32 b) { return (v4i32_t){ a.x * b, a.y * b, a.z * b, a.w * b }; }
|
||||
fn_inline v4i32_t v4i32_div(v4i32_t a, v4i32_t b) { return (v4i32_t){ a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w }; }
|
||||
fn_inline v4i32_t v4i32_divs(v4i32_t a, i32 b) { return (v4i32_t){ a.x / b, a.y / b, a.z / b, a.w / b }; }
|
||||
fn_inline r2i32_t r2i32(i32 x0, i32 y0, i32 x1, i32 y1) { return (r2i32_t){ x0, y0, x1, y1 }; }
|
||||
gb_read_only r2i32_t r2i32_null = {0};
|
||||
fn_inline b32 r2i32_is_null(r2i32_t a) { return memory_equal(&a, &r2i32_null, sizeof(a)); }
|
||||
fn_inline b32 r2i32_are_equal(r2i32_t a, r2i32_t b) { return memory_equal(&a, &b, sizeof(a)); }
|
||||
fn_inline r2i32_t r2i32_add(r2i32_t a, r2i32_t b) { return (r2i32_t){ a.x0 + b.x0, a.y0 + b.y0, a.x1 + b.x1, a.y1 + b.y1 }; }
|
||||
fn_inline r2i32_t r2i32_adds(r2i32_t a, i32 b) { return (r2i32_t){ a.x0 + b, a.y0 + b, a.x1 + b, a.y1 + b }; }
|
||||
fn_inline r2i32_t r2i32_sub(r2i32_t a, r2i32_t b) { return (r2i32_t){ a.x0 - b.x0, a.y0 - b.y0, a.x1 - b.x1, a.y1 - b.y1 }; }
|
||||
fn_inline r2i32_t r2i32_subs(r2i32_t a, i32 b) { return (r2i32_t){ a.x0 - b, a.y0 - b, a.x1 - b, a.y1 - b }; }
|
||||
fn_inline r2i32_t r2i32_mul(r2i32_t a, r2i32_t b) { return (r2i32_t){ a.x0 * b.x0, a.y0 * b.y0, a.x1 * b.x1, a.y1 * b.y1 }; }
|
||||
fn_inline r2i32_t r2i32_muls(r2i32_t a, i32 b) { return (r2i32_t){ a.x0 * b, a.y0 * b, a.x1 * b, a.y1 * b }; }
|
||||
fn_inline r2i32_t r2i32_div(r2i32_t a, r2i32_t b) { return (r2i32_t){ a.x0 / b.x0, a.y0 / b.y0, a.x1 / b.x1, a.y1 / b.y1 }; }
|
||||
fn_inline r2i32_t r2i32_divs(r2i32_t a, i32 b) { return (r2i32_t){ a.x0 / b, a.y0 / b, a.x1 / b, a.y1 / b }; }
|
||||
fn_inline r3i32_t r3i32(i32 x0, i32 y0, i32 z0, i32 x1, i32 y1, i32 z1) { return (r3i32_t){ x0, y0, z0, x1, y1, z1 }; }
|
||||
gb_read_only r3i32_t r3i32_null = {0};
|
||||
fn_inline b32 r3i32_is_null(r3i32_t a) { return memory_equal(&a, &r3i32_null, sizeof(a)); }
|
||||
fn_inline b32 r3i32_are_equal(r3i32_t a, r3i32_t b) { return memory_equal(&a, &b, sizeof(a)); }
|
||||
fn_inline r3i32_t r3i32_add(r3i32_t a, r3i32_t b) { return (r3i32_t){ a.x0 + b.x0, a.y0 + b.y0, a.z0 + b.z0, a.x1 + b.x1, a.y1 + b.y1, a.z1 + b.z1 }; }
|
||||
fn_inline r3i32_t r3i32_adds(r3i32_t a, i32 b) { return (r3i32_t){ a.x0 + b, a.y0 + b, a.z0 + b, a.x1 + b, a.y1 + b, a.z1 + b }; }
|
||||
fn_inline r3i32_t r3i32_sub(r3i32_t a, r3i32_t b) { return (r3i32_t){ a.x0 - b.x0, a.y0 - b.y0, a.z0 - b.z0, a.x1 - b.x1, a.y1 - b.y1, a.z1 - b.z1 }; }
|
||||
fn_inline r3i32_t r3i32_subs(r3i32_t a, i32 b) { return (r3i32_t){ a.x0 - b, a.y0 - b, a.z0 - b, a.x1 - b, a.y1 - b, a.z1 - b }; }
|
||||
fn_inline r3i32_t r3i32_mul(r3i32_t a, r3i32_t b) { return (r3i32_t){ a.x0 * b.x0, a.y0 * b.y0, a.z0 * b.z0, a.x1 * b.x1, a.y1 * b.y1, a.z1 * b.z1 }; }
|
||||
fn_inline r3i32_t r3i32_muls(r3i32_t a, i32 b) { return (r3i32_t){ a.x0 * b, a.y0 * b, a.z0 * b, a.x1 * b, a.y1 * b, a.z1 * b }; }
|
||||
fn_inline r3i32_t r3i32_div(r3i32_t a, r3i32_t b) { return (r3i32_t){ a.x0 / b.x0, a.y0 / b.y0, a.z0 / b.z0, a.x1 / b.x1, a.y1 / b.y1, a.z1 / b.z1 }; }
|
||||
fn_inline r3i32_t r3i32_divs(r3i32_t a, i32 b) { return (r3i32_t){ a.x0 / b, a.y0 / b, a.z0 / b, a.x1 / b, a.y1 / b, a.z1 / b }; }
|
||||
fn_inline v2i64_t v2i64(i64 x, i64 y) { return (v2i64_t){ x, y }; }
|
||||
gb_read_only v2i64_t v2i64_null = {0};
|
||||
fn_inline b32 v2i64_is_null(v2i64_t a) { return memory_equal(&a, &v2i64_null, sizeof(a)); }
|
||||
fn_inline b32 v2i64_are_equal(v2i64_t a, v2i64_t b) { return memory_equal(&a, &b, sizeof(a)); }
|
||||
fn_inline v2i64_t v2i64_add(v2i64_t a, v2i64_t b) { return (v2i64_t){ a.x + b.x, a.y + b.y }; }
|
||||
fn_inline v2i64_t v2i64_adds(v2i64_t a, i64 b) { return (v2i64_t){ a.x + b, a.y + b }; }
|
||||
fn_inline v2i64_t v2i64_sub(v2i64_t a, v2i64_t b) { return (v2i64_t){ a.x - b.x, a.y - b.y }; }
|
||||
fn_inline v2i64_t v2i64_subs(v2i64_t a, i64 b) { return (v2i64_t){ a.x - b, a.y - b }; }
|
||||
fn_inline v2i64_t v2i64_mul(v2i64_t a, v2i64_t b) { return (v2i64_t){ a.x * b.x, a.y * b.y }; }
|
||||
fn_inline v2i64_t v2i64_muls(v2i64_t a, i64 b) { return (v2i64_t){ a.x * b, a.y * b }; }
|
||||
fn_inline v2i64_t v2i64_div(v2i64_t a, v2i64_t b) { return (v2i64_t){ a.x / b.x, a.y / b.y }; }
|
||||
fn_inline v2i64_t v2i64_divs(v2i64_t a, i64 b) { return (v2i64_t){ a.x / b, a.y / b }; }
|
||||
fn_inline v3i64_t v3i64(i64 x, i64 y, i64 z) { return (v3i64_t){ x, y, z }; }
|
||||
gb_read_only v3i64_t v3i64_null = {0};
|
||||
fn_inline b32 v3i64_is_null(v3i64_t a) { return memory_equal(&a, &v3i64_null, sizeof(a)); }
|
||||
fn_inline b32 v3i64_are_equal(v3i64_t a, v3i64_t b) { return memory_equal(&a, &b, sizeof(a)); }
|
||||
fn_inline v3i64_t v3i64_add(v3i64_t a, v3i64_t b) { return (v3i64_t){ a.x + b.x, a.y + b.y, a.z + b.z }; }
|
||||
fn_inline v3i64_t v3i64_adds(v3i64_t a, i64 b) { return (v3i64_t){ a.x + b, a.y + b, a.z + b }; }
|
||||
fn_inline v3i64_t v3i64_sub(v3i64_t a, v3i64_t b) { return (v3i64_t){ a.x - b.x, a.y - b.y, a.z - b.z }; }
|
||||
fn_inline v3i64_t v3i64_subs(v3i64_t a, i64 b) { return (v3i64_t){ a.x - b, a.y - b, a.z - b }; }
|
||||
fn_inline v3i64_t v3i64_mul(v3i64_t a, v3i64_t b) { return (v3i64_t){ a.x * b.x, a.y * b.y, a.z * b.z }; }
|
||||
fn_inline v3i64_t v3i64_muls(v3i64_t a, i64 b) { return (v3i64_t){ a.x * b, a.y * b, a.z * b }; }
|
||||
fn_inline v3i64_t v3i64_div(v3i64_t a, v3i64_t b) { return (v3i64_t){ a.x / b.x, a.y / b.y, a.z / b.z }; }
|
||||
fn_inline v3i64_t v3i64_divs(v3i64_t a, i64 b) { return (v3i64_t){ a.x / b, a.y / b, a.z / b }; }
|
||||
fn_inline v4i64_t v4i64(i64 x, i64 y, i64 z, i64 w) { return (v4i64_t){ x, y, z, w }; }
|
||||
gb_read_only v4i64_t v4i64_null = {0};
|
||||
fn_inline b32 v4i64_is_null(v4i64_t a) { return memory_equal(&a, &v4i64_null, sizeof(a)); }
|
||||
fn_inline b32 v4i64_are_equal(v4i64_t a, v4i64_t b) { return memory_equal(&a, &b, sizeof(a)); }
|
||||
fn_inline v4i64_t v4i64_add(v4i64_t a, v4i64_t b) { return (v4i64_t){ a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w }; }
|
||||
fn_inline v4i64_t v4i64_adds(v4i64_t a, i64 b) { return (v4i64_t){ a.x + b, a.y + b, a.z + b, a.w + b }; }
|
||||
fn_inline v4i64_t v4i64_sub(v4i64_t a, v4i64_t b) { return (v4i64_t){ a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w }; }
|
||||
fn_inline v4i64_t v4i64_subs(v4i64_t a, i64 b) { return (v4i64_t){ a.x - b, a.y - b, a.z - b, a.w - b }; }
|
||||
fn_inline v4i64_t v4i64_mul(v4i64_t a, v4i64_t b) { return (v4i64_t){ a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w }; }
|
||||
fn_inline v4i64_t v4i64_muls(v4i64_t a, i64 b) { return (v4i64_t){ a.x * b, a.y * b, a.z * b, a.w * b }; }
|
||||
fn_inline v4i64_t v4i64_div(v4i64_t a, v4i64_t b) { return (v4i64_t){ a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w }; }
|
||||
fn_inline v4i64_t v4i64_divs(v4i64_t a, i64 b) { return (v4i64_t){ a.x / b, a.y / b, a.z / b, a.w / b }; }
|
||||
fn_inline r2i64_t r2i64(i64 x0, i64 y0, i64 x1, i64 y1) { return (r2i64_t){ x0, y0, x1, y1 }; }
|
||||
gb_read_only r2i64_t r2i64_null = {0};
|
||||
fn_inline b32 r2i64_is_null(r2i64_t a) { return memory_equal(&a, &r2i64_null, sizeof(a)); }
|
||||
fn_inline b32 r2i64_are_equal(r2i64_t a, r2i64_t b) { return memory_equal(&a, &b, sizeof(a)); }
|
||||
fn_inline r2i64_t r2i64_add(r2i64_t a, r2i64_t b) { return (r2i64_t){ a.x0 + b.x0, a.y0 + b.y0, a.x1 + b.x1, a.y1 + b.y1 }; }
|
||||
fn_inline r2i64_t r2i64_adds(r2i64_t a, i64 b) { return (r2i64_t){ a.x0 + b, a.y0 + b, a.x1 + b, a.y1 + b }; }
|
||||
fn_inline r2i64_t r2i64_sub(r2i64_t a, r2i64_t b) { return (r2i64_t){ a.x0 - b.x0, a.y0 - b.y0, a.x1 - b.x1, a.y1 - b.y1 }; }
|
||||
fn_inline r2i64_t r2i64_subs(r2i64_t a, i64 b) { return (r2i64_t){ a.x0 - b, a.y0 - b, a.x1 - b, a.y1 - b }; }
|
||||
fn_inline r2i64_t r2i64_mul(r2i64_t a, r2i64_t b) { return (r2i64_t){ a.x0 * b.x0, a.y0 * b.y0, a.x1 * b.x1, a.y1 * b.y1 }; }
|
||||
fn_inline r2i64_t r2i64_muls(r2i64_t a, i64 b) { return (r2i64_t){ a.x0 * b, a.y0 * b, a.x1 * b, a.y1 * b }; }
|
||||
fn_inline r2i64_t r2i64_div(r2i64_t a, r2i64_t b) { return (r2i64_t){ a.x0 / b.x0, a.y0 / b.y0, a.x1 / b.x1, a.y1 / b.y1 }; }
|
||||
fn_inline r2i64_t r2i64_divs(r2i64_t a, i64 b) { return (r2i64_t){ a.x0 / b, a.y0 / b, a.x1 / b, a.y1 / b }; }
|
||||
fn_inline r3i64_t r3i64(i64 x0, i64 y0, i64 z0, i64 x1, i64 y1, i64 z1) { return (r3i64_t){ x0, y0, z0, x1, y1, z1 }; }
|
||||
gb_read_only r3i64_t r3i64_null = {0};
|
||||
fn_inline b32 r3i64_is_null(r3i64_t a) { return memory_equal(&a, &r3i64_null, sizeof(a)); }
|
||||
fn_inline b32 r3i64_are_equal(r3i64_t a, r3i64_t b) { return memory_equal(&a, &b, sizeof(a)); }
|
||||
fn_inline r3i64_t r3i64_add(r3i64_t a, r3i64_t b) { return (r3i64_t){ a.x0 + b.x0, a.y0 + b.y0, a.z0 + b.z0, a.x1 + b.x1, a.y1 + b.y1, a.z1 + b.z1 }; }
|
||||
fn_inline r3i64_t r3i64_adds(r3i64_t a, i64 b) { return (r3i64_t){ a.x0 + b, a.y0 + b, a.z0 + b, a.x1 + b, a.y1 + b, a.z1 + b }; }
|
||||
fn_inline r3i64_t r3i64_sub(r3i64_t a, r3i64_t b) { return (r3i64_t){ a.x0 - b.x0, a.y0 - b.y0, a.z0 - b.z0, a.x1 - b.x1, a.y1 - b.y1, a.z1 - b.z1 }; }
|
||||
fn_inline r3i64_t r3i64_subs(r3i64_t a, i64 b) { return (r3i64_t){ a.x0 - b, a.y0 - b, a.z0 - b, a.x1 - b, a.y1 - b, a.z1 - b }; }
|
||||
fn_inline r3i64_t r3i64_mul(r3i64_t a, r3i64_t b) { return (r3i64_t){ a.x0 * b.x0, a.y0 * b.y0, a.z0 * b.z0, a.x1 * b.x1, a.y1 * b.y1, a.z1 * b.z1 }; }
|
||||
fn_inline r3i64_t r3i64_muls(r3i64_t a, i64 b) { return (r3i64_t){ a.x0 * b, a.y0 * b, a.z0 * b, a.x1 * b, a.y1 * b, a.z1 * b }; }
|
||||
fn_inline r3i64_t r3i64_div(r3i64_t a, r3i64_t b) { return (r3i64_t){ a.x0 / b.x0, a.y0 / b.y0, a.z0 / b.z0, a.x1 / b.x1, a.y1 / b.y1, a.z1 / b.z1 }; }
|
||||
fn_inline r3i64_t r3i64_divs(r3i64_t a, i64 b) { return (r3i64_t){ a.x0 / b, a.y0 / b, a.z0 / b, a.x1 / b, a.y1 / b, a.z1 / b }; }
|
||||
fn_inline r2f32_t r2f32_min_dim(v2f32_t pos, v2f32_t size) { return (r2f32_t){ pos, v2f32_add(pos, size) }; }
|
||||
fn_inline r2f32_t r2f32_center_half_dim(v2f32_t center, v2f32_t halfdim) { return (r2f32_t){ v2f32_sub(center, halfdim), v2f32_add(center, halfdim) }; }
|
||||
fn_inline r3f32_t r3f32_min_dim(v3f32_t pos, v3f32_t size) { return (r3f32_t){ pos, v3f32_add(pos, size) }; }
|
||||
fn_inline r3f32_t r3f32_center_half_dim(v3f32_t center, v3f32_t halfdim) { return (r3f32_t){ v3f32_sub(center, halfdim), v3f32_add(center, halfdim) }; }
|
||||
fn_inline r2f64_t r2f64_min_dim(v2f64_t pos, v2f64_t size) { return (r2f64_t){ pos, v2f64_add(pos, size) }; }
|
||||
fn_inline r2f64_t r2f64_center_half_dim(v2f64_t center, v2f64_t halfdim) { return (r2f64_t){ v2f64_sub(center, halfdim), v2f64_add(center, halfdim) }; }
|
||||
fn_inline r3f64_t r3f64_min_dim(v3f64_t pos, v3f64_t size) { return (r3f64_t){ pos, v3f64_add(pos, size) }; }
|
||||
fn_inline r3f64_t r3f64_center_half_dim(v3f64_t center, v3f64_t halfdim) { return (r3f64_t){ v3f64_sub(center, halfdim), v3f64_add(center, halfdim) }; }
|
||||
fn_inline r2i32_t r2i32_min_dim(v2i32_t pos, v2i32_t size) { return (r2i32_t){ pos, v2i32_add(pos, size) }; }
|
||||
fn_inline r2i32_t r2i32_center_half_dim(v2i32_t center, v2i32_t halfdim) { return (r2i32_t){ v2i32_sub(center, halfdim), v2i32_add(center, halfdim) }; }
|
||||
fn_inline r3i32_t r3i32_min_dim(v3i32_t pos, v3i32_t size) { return (r3i32_t){ pos, v3i32_add(pos, size) }; }
|
||||
fn_inline r3i32_t r3i32_center_half_dim(v3i32_t center, v3i32_t halfdim) { return (r3i32_t){ v3i32_sub(center, halfdim), v3i32_add(center, halfdim) }; }
|
||||
fn_inline r2i64_t r2i64_min_dim(v2i64_t pos, v2i64_t size) { return (r2i64_t){ pos, v2i64_add(pos, size) }; }
|
||||
fn_inline r2i64_t r2i64_center_half_dim(v2i64_t center, v2i64_t halfdim) { return (r2i64_t){ v2i64_sub(center, halfdim), v2i64_add(center, halfdim) }; }
|
||||
fn_inline r3i64_t r3i64_min_dim(v3i64_t pos, v3i64_t size) { return (r3i64_t){ pos, v3i64_add(pos, size) }; }
|
||||
fn_inline r3i64_t r3i64_center_half_dim(v3i64_t center, v3i64_t halfdim) { return (r3i64_t){ v3i64_sub(center, halfdim), v3i64_add(center, halfdim) }; }
|
||||
fn r2f32_t r2f32_cut_left(r2f32_t *r, f32 value) {
|
||||
f32 minx = r->min.x;
|
||||
r->min.x = MIN(r->min.x + value, r->max.x);
|
||||
r2f32_t result = { .min = {.x = minx, .y = r->min.y}, .max = {.x = r->min.x, .y =r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f32_t r2f32_cut_right(r2f32_t *r, f32 value) {
|
||||
f32 maxx = r->max.x;
|
||||
r->max.x = MAX(r->min.x, r->max.x - value);
|
||||
r2f32_t result = { .min = {.x = r->max.x, .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f32_t r2f32_cut_top(r2f32_t *r, f32 value) { /* Y is down */
|
||||
f32 miny = r->min.y;
|
||||
r->min.y = MIN(r->max.y, r->min.y + value);
|
||||
r2f32_t result = { .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = r->min.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f32_t r2f32_cut_bottom(r2f32_t *r, f32 value) { /* Y is down */
|
||||
f32 maxy = r->max.y;
|
||||
r->max.y = MAX(r->min.y, r->max.y - value);
|
||||
r2f32_t result = { .min = {.x = r->min.x, .y = r->max.y}, .max = {.x = r->max.x, .y = maxy} };
|
||||
return result;
|
||||
}
|
||||
|
||||
fn r2f32_t r2f32_cut_left_no_squash(r2f32_t *r, f32 value) {
|
||||
f32 minx = r->min.x;
|
||||
r->min.x = r->min.x + value;
|
||||
r2f32_t result = { .min = {.x = minx, .y = r->min.y}, .max = {.x = r->min.x, .y =r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f32_t r2f32_cut_right_no_squash(r2f32_t *r, f32 value) {
|
||||
f32 maxx = r->max.x;
|
||||
r->max.x = r->max.x - value;
|
||||
r2f32_t result = { .min = {.x = r->max.x, .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f32_t r2f32_cut_top_no_squash(r2f32_t *r, f32 value) { /* Y is down */
|
||||
f32 miny = r->min.y;
|
||||
r->min.y = r->min.y + value;
|
||||
r2f32_t result = { .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = r->min.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f32_t r2f32_cut_bottom_no_squash(r2f32_t *r, f32 value) { /* Y is down */
|
||||
f32 maxy = r->max.y;
|
||||
r->max.y = r->max.y - value;
|
||||
r2f32_t result = { .min = {.x = r->min.x, .y = r->max.y}, .max = {.x = r->max.x, .y = maxy} };
|
||||
return result;
|
||||
}
|
||||
|
||||
fn r2f32_t r2f32_add_left(r2f32_t *r, f32 value) {
|
||||
r2f32_t result = { .min = {r->min.x - value, r->min.y}, .max = {r->min.x, r->max.y} };
|
||||
r->min.x -= value;
|
||||
return result;
|
||||
}
|
||||
fn r2f32_t r2f32_add_right(r2f32_t *r, f32 value) {
|
||||
r2f32_t result = { .min = {r->max.x, r->min.y}, .max = {r->max.x + value, r->max.y} };
|
||||
r->max.x += value;
|
||||
return result;
|
||||
}
|
||||
fn r2f32_t r2f32_add_top(r2f32_t *r, f32 value) {
|
||||
r2f32_t result = { .min = {r->min.x, r->min.y - value}, .max = {r->max.x, r->min.y} };
|
||||
r->min.y -= value;
|
||||
return result;
|
||||
}
|
||||
fn r2f32_t r2f32_add_bottom(r2f32_t *r, f32 value) {
|
||||
r2f32_t result = { .min = {r->min.x, r->max.y}, .max = {r->max.x, r->max.y + value} };
|
||||
r->max.y += value;
|
||||
return result;
|
||||
}
|
||||
|
||||
// get past left
|
||||
fn r2f32_t r2f32_getp_left(const r2f32_t *rect, f32 value) {
|
||||
r2f32_t result = r2f32(rect->min.x - value, rect->min.y, rect->min.x, rect->max.y);
|
||||
return result;
|
||||
}
|
||||
fn r2f32_t r2f32_getp_right(const r2f32_t *rect, f32 value) {
|
||||
r2f32_t result = r2f32(rect->max.x, rect->min.y, rect->max.x + value, rect->max.y);
|
||||
return result;
|
||||
}
|
||||
fn r2f32_t r2f32_getp_bottom(const r2f32_t *rect, f32 value) {
|
||||
r2f32_t result = r2f32(rect->min.x, rect->max.y, rect->max.x, rect->max.y + value);
|
||||
return result;
|
||||
}
|
||||
fn r2f32_t r2f32_getp_top(const r2f32_t *rect, f32 value) {
|
||||
r2f32_t result = r2f32(rect->min.x, rect->min.y - value, rect->max.x, rect->min.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn r2f32_t r2f32_get_left(const r2f32_t *r, f32 value) {
|
||||
f32 minx = r->min.x;
|
||||
r2f32_t result = (r2f32_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = MIN(r->min.x + value, r->max.x), .y =r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f32_t r2f32_get_right(const r2f32_t *r, f32 value) {
|
||||
f32 maxx = r->max.x;
|
||||
r2f32_t result = (r2f32_t){ .min = {.x = MAX(r->min.x, r->max.x - value), .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f32_t r2f32_get_top(const r2f32_t *r, f32 value) { /* Y is down */
|
||||
f32 miny = r->min.y;
|
||||
r2f32_t result = (r2f32_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = MIN(r->max.y, r->min.y + value)} };
|
||||
return result;
|
||||
}
|
||||
fn r2f32_t r2f32_get_bottom(const r2f32_t *r, f32 value) { /* Y is down */
|
||||
f32 maxy = r->max.y;
|
||||
r2f32_t result = (r2f32_t){ .min = {.x = r->min.x, .y = MAX(r->min.y, r->max.y - value)}, .max = {.x = r->max.x, .y = maxy} };
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
fn_inline r2f32_t r2f32_expand(r2f32_t rect, v2f32_t value) { return (r2f32_t){ v2f32_sub(rect.min, value), v2f32_add(rect.max, value) }; }
|
||||
fn_inline r2f32_t r2f32_shrink(r2f32_t rect, v2f32_t value) { return (r2f32_t){ v2f32_add(rect.min, value), v2f32_sub(rect.max, value) }; }
|
||||
fn_inline r2f32_t r2f32_shrinks(r2f32_t rect, f32 value) { return (r2f32_t){ v2f32_adds(rect.min, value), v2f32_subs(rect.max, value) }; }
|
||||
fn_inline v2f32_t r2f32_size(r2f32_t r) { return (v2f32_t){r.max.x - r.min.x, r.max.y - r.min.y}; }
|
||||
fn_inline v2f32_t r2f32_get_mid(r2f32_t r) { return v2f32_add(r.min, v2f32_divs(r2f32_size(r), 2)); }
|
||||
fn_inline b32 r2f32_contains(r2f32_t rec, v2f32_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); }
|
||||
fn_inline r2f32_t r2f32_intersect(r2f32_t a, r2f32_t b) { return (r2f32_t){ .min.x = MAX(a.min.x, b.min.x), .min.y = MAX(a.min.y, b.min.y), .max.x = MIN(a.max.x, b.max.x), .max.y = MIN(a.max.y, b.max.y) }; }
|
||||
fn_inline r2f32_t r2f32_union(r2f32_t a, r2f32_t b) { return (r2f32_t){ .min.x = MIN(a.min.x, b.min.x), .min.y = MIN(a.min.y, b.min.y), .max.x = MAX(a.max.x, b.max.x), .max.y = MAX(a.max.y, b.max.y) }; }
|
||||
|
||||
fn_inline f32 r1f32_size(r1f32_t a) { return a.max - a.min; }
|
||||
fn_inline r1f32_t r1f32(f32 min, f32 max) { return (r1f32_t){min,max}; }
|
||||
fn_inline r1f32_t r1f32_auto(f32 a, f32 b) { return (r1f32_t){MIN(a,b),MAX(a,b)}; }
|
||||
fn_inline r1f32_t r1f32s(f32 a) { return (r1f32_t){a,a}; }
|
||||
fn_inline r1f32_t r1f32_clamp(r1f32_t a, f32 min, f32 max) { return (r1f32_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; }
|
||||
fn_inline b32 r1f32_contains(r1f32_t rec, f32 point) { return (point >= rec.min) && (point < rec.max) && (point >= rec.min) && (point < rec.max); }
|
||||
fn_inline b32 r1f32_are_equal(r1f32_t a, r1f32_t b) { return a.min == b.min && a.max == b.max; }
|
||||
gb_read_only r1f32_t r1f32_null;
|
||||
|
||||
fn b32 r1f32_overlap(r1f32_t a, r1f32_t b) {
|
||||
b32 r1 = (a.max >= b.min && a.max <= b.max) || (a.min >= b.min && a.min <= b.max);
|
||||
b32 r2 = (b.max >= a.min && b.max <= a.max) || (b.min >= a.min && b.min <= a.max);
|
||||
return r1 || r2;
|
||||
}
|
||||
|
||||
fn v2f32_t v2f32_clamps(v2f32_t v, f32 min, f32 max) {
|
||||
v2f32_t result = { CLAMP(v.x, min, max), CLAMP(v.y, min, max) };
|
||||
return result;
|
||||
}
|
||||
fn v2f32_t v2f32_clamp(v2f32_t v, v2f32_t min, v2f32_t max) {
|
||||
v2f32_t result = { CLAMP(v.x, min.x, max.x), CLAMP(v.y, min.y, max.y) };
|
||||
return result;
|
||||
}
|
||||
|
||||
fn r2f32_t r2f32_fix(r2f32_t n) {
|
||||
r2f32_t result = {
|
||||
.min = {MIN(n.min.x, n.max.x), MIN(n.min.y, n.max.y)},
|
||||
.max = {MAX(n.min.x, n.max.x), MAX(n.min.y, n.max.y)},
|
||||
};
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_cut_left(r2f64_t *r, f64 value) {
|
||||
f64 minx = r->min.x;
|
||||
r->min.x = MIN(r->min.x + value, r->max.x);
|
||||
r2f64_t result = { .min = {.x = minx, .y = r->min.y}, .max = {.x = r->min.x, .y =r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_cut_right(r2f64_t *r, f64 value) {
|
||||
f64 maxx = r->max.x;
|
||||
r->max.x = MAX(r->min.x, r->max.x - value);
|
||||
r2f64_t result = { .min = {.x = r->max.x, .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_cut_top(r2f64_t *r, f64 value) { /* Y is down */
|
||||
f64 miny = r->min.y;
|
||||
r->min.y = MIN(r->max.y, r->min.y + value);
|
||||
r2f64_t result = { .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = r->min.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_cut_bottom(r2f64_t *r, f64 value) { /* Y is down */
|
||||
f64 maxy = r->max.y;
|
||||
r->max.y = MAX(r->min.y, r->max.y - value);
|
||||
r2f64_t result = { .min = {.x = r->min.x, .y = r->max.y}, .max = {.x = r->max.x, .y = maxy} };
|
||||
return result;
|
||||
}
|
||||
|
||||
fn r2f64_t r2f64_cut_left_no_squash(r2f64_t *r, f64 value) {
|
||||
f64 minx = r->min.x;
|
||||
r->min.x = r->min.x + value;
|
||||
r2f64_t result = { .min = {.x = minx, .y = r->min.y}, .max = {.x = r->min.x, .y =r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_cut_right_no_squash(r2f64_t *r, f64 value) {
|
||||
f64 maxx = r->max.x;
|
||||
r->max.x = r->max.x - value;
|
||||
r2f64_t result = { .min = {.x = r->max.x, .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_cut_top_no_squash(r2f64_t *r, f64 value) { /* Y is down */
|
||||
f64 miny = r->min.y;
|
||||
r->min.y = r->min.y + value;
|
||||
r2f64_t result = { .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = r->min.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_cut_bottom_no_squash(r2f64_t *r, f64 value) { /* Y is down */
|
||||
f64 maxy = r->max.y;
|
||||
r->max.y = r->max.y - value;
|
||||
r2f64_t result = { .min = {.x = r->min.x, .y = r->max.y}, .max = {.x = r->max.x, .y = maxy} };
|
||||
return result;
|
||||
}
|
||||
|
||||
fn r2f64_t r2f64_add_left(r2f64_t *r, f64 value) {
|
||||
r2f64_t result = { .min = {r->min.x - value, r->min.y}, .max = {r->min.x, r->max.y} };
|
||||
r->min.x -= value;
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_add_right(r2f64_t *r, f64 value) {
|
||||
r2f64_t result = { .min = {r->max.x, r->min.y}, .max = {r->max.x + value, r->max.y} };
|
||||
r->max.x += value;
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_add_top(r2f64_t *r, f64 value) {
|
||||
r2f64_t result = { .min = {r->min.x, r->min.y - value}, .max = {r->max.x, r->min.y} };
|
||||
r->min.y -= value;
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_add_bottom(r2f64_t *r, f64 value) {
|
||||
r2f64_t result = { .min = {r->min.x, r->max.y}, .max = {r->max.x, r->max.y + value} };
|
||||
r->max.y += value;
|
||||
return result;
|
||||
}
|
||||
|
||||
// get past left
|
||||
fn r2f64_t r2f64_getp_left(const r2f64_t *rect, f64 value) {
|
||||
r2f64_t result = r2f64(rect->min.x - value, rect->min.y, rect->min.x, rect->max.y);
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_getp_right(const r2f64_t *rect, f64 value) {
|
||||
r2f64_t result = r2f64(rect->max.x, rect->min.y, rect->max.x + value, rect->max.y);
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_getp_bottom(const r2f64_t *rect, f64 value) {
|
||||
r2f64_t result = r2f64(rect->min.x, rect->max.y, rect->max.x, rect->max.y + value);
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_getp_top(const r2f64_t *rect, f64 value) {
|
||||
r2f64_t result = r2f64(rect->min.x, rect->min.y - value, rect->max.x, rect->min.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn r2f64_t r2f64_get_left(const r2f64_t *r, f64 value) {
|
||||
f64 minx = r->min.x;
|
||||
r2f64_t result = (r2f64_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = MIN(r->min.x + value, r->max.x), .y =r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_get_right(const r2f64_t *r, f64 value) {
|
||||
f64 maxx = r->max.x;
|
||||
r2f64_t result = (r2f64_t){ .min = {.x = MAX(r->min.x, r->max.x - value), .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_get_top(const r2f64_t *r, f64 value) { /* Y is down */
|
||||
f64 miny = r->min.y;
|
||||
r2f64_t result = (r2f64_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = MIN(r->max.y, r->min.y + value)} };
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_get_bottom(const r2f64_t *r, f64 value) { /* Y is down */
|
||||
f64 maxy = r->max.y;
|
||||
r2f64_t result = (r2f64_t){ .min = {.x = r->min.x, .y = MAX(r->min.y, r->max.y - value)}, .max = {.x = r->max.x, .y = maxy} };
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
fn_inline r2f64_t r2f64_expand(r2f64_t rect, v2f64_t value) { return (r2f64_t){ v2f64_sub(rect.min, value), v2f64_add(rect.max, value) }; }
|
||||
fn_inline r2f64_t r2f64_shrink(r2f64_t rect, v2f64_t value) { return (r2f64_t){ v2f64_add(rect.min, value), v2f64_sub(rect.max, value) }; }
|
||||
fn_inline r2f64_t r2f64_shrinks(r2f64_t rect, f64 value) { return (r2f64_t){ v2f64_adds(rect.min, value), v2f64_subs(rect.max, value) }; }
|
||||
fn_inline v2f64_t r2f64_size(r2f64_t r) { return (v2f64_t){r.max.x - r.min.x, r.max.y - r.min.y}; }
|
||||
fn_inline v2f64_t r2f64_get_mid(r2f64_t r) { return v2f64_add(r.min, v2f64_divs(r2f64_size(r), 2)); }
|
||||
fn_inline b32 r2f64_contains(r2f64_t rec, v2f64_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); }
|
||||
fn_inline r2f64_t r2f64_intersect(r2f64_t a, r2f64_t b) { return (r2f64_t){ .min.x = MAX(a.min.x, b.min.x), .min.y = MAX(a.min.y, b.min.y), .max.x = MIN(a.max.x, b.max.x), .max.y = MIN(a.max.y, b.max.y) }; }
|
||||
fn_inline r2f64_t r2f64_union(r2f64_t a, r2f64_t b) { return (r2f64_t){ .min.x = MIN(a.min.x, b.min.x), .min.y = MIN(a.min.y, b.min.y), .max.x = MAX(a.max.x, b.max.x), .max.y = MAX(a.max.y, b.max.y) }; }
|
||||
|
||||
fn_inline f64 r1f64_size(r1f64_t a) { return a.max - a.min; }
|
||||
fn_inline r1f64_t r1f64(f64 min, f64 max) { return (r1f64_t){min,max}; }
|
||||
fn_inline r1f64_t r1f64_auto(f64 a, f64 b) { return (r1f64_t){MIN(a,b),MAX(a,b)}; }
|
||||
fn_inline r1f64_t r1f64s(f64 a) { return (r1f64_t){a,a}; }
|
||||
fn_inline r1f64_t r1f64_clamp(r1f64_t a, f64 min, f64 max) { return (r1f64_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; }
|
||||
fn_inline b32 r1f64_contains(r1f64_t rec, f64 point) { return (point >= rec.min) && (point < rec.max) && (point >= rec.min) && (point < rec.max); }
|
||||
fn_inline b32 r1f64_are_equal(r1f64_t a, r1f64_t b) { return a.min == b.min && a.max == b.max; }
|
||||
gb_read_only r1f64_t r1f64_null;
|
||||
|
||||
fn b32 r1f64_overlap(r1f64_t a, r1f64_t b) {
|
||||
b32 r1 = (a.max >= b.min && a.max <= b.max) || (a.min >= b.min && a.min <= b.max);
|
||||
b32 r2 = (b.max >= a.min && b.max <= a.max) || (b.min >= a.min && b.min <= a.max);
|
||||
return r1 || r2;
|
||||
}
|
||||
|
||||
fn v2f64_t v2f64_clamps(v2f64_t v, f64 min, f64 max) {
|
||||
v2f64_t result = { CLAMP(v.x, min, max), CLAMP(v.y, min, max) };
|
||||
return result;
|
||||
}
|
||||
fn v2f64_t v2f64_clamp(v2f64_t v, v2f64_t min, v2f64_t max) {
|
||||
v2f64_t result = { CLAMP(v.x, min.x, max.x), CLAMP(v.y, min.y, max.y) };
|
||||
return result;
|
||||
}
|
||||
|
||||
fn r2f64_t r2f64_fix(r2f64_t n) {
|
||||
r2f64_t result = {
|
||||
.min = {MIN(n.min.x, n.max.x), MIN(n.min.y, n.max.y)},
|
||||
.max = {MAX(n.min.x, n.max.x), MAX(n.min.y, n.max.y)},
|
||||
};
|
||||
return result;
|
||||
}
|
||||
fn r2i32_t r2i32_cut_left(r2i32_t *r, i32 value) {
|
||||
i32 minx = r->min.x;
|
||||
r->min.x = MIN(r->min.x + value, r->max.x);
|
||||
r2i32_t result = { .min = {.x = minx, .y = r->min.y}, .max = {.x = r->min.x, .y =r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2i32_t r2i32_cut_right(r2i32_t *r, i32 value) {
|
||||
i32 maxx = r->max.x;
|
||||
r->max.x = MAX(r->min.x, r->max.x - value);
|
||||
r2i32_t result = { .min = {.x = r->max.x, .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2i32_t r2i32_cut_top(r2i32_t *r, i32 value) { /* Y is down */
|
||||
i32 miny = r->min.y;
|
||||
r->min.y = MIN(r->max.y, r->min.y + value);
|
||||
r2i32_t result = { .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = r->min.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2i32_t r2i32_cut_bottom(r2i32_t *r, i32 value) { /* Y is down */
|
||||
i32 maxy = r->max.y;
|
||||
r->max.y = MAX(r->min.y, r->max.y - value);
|
||||
r2i32_t result = { .min = {.x = r->min.x, .y = r->max.y}, .max = {.x = r->max.x, .y = maxy} };
|
||||
return result;
|
||||
}
|
||||
|
||||
fn r2i32_t r2i32_cut_left_no_squash(r2i32_t *r, i32 value) {
|
||||
i32 minx = r->min.x;
|
||||
r->min.x = r->min.x + value;
|
||||
r2i32_t result = { .min = {.x = minx, .y = r->min.y}, .max = {.x = r->min.x, .y =r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2i32_t r2i32_cut_right_no_squash(r2i32_t *r, i32 value) {
|
||||
i32 maxx = r->max.x;
|
||||
r->max.x = r->max.x - value;
|
||||
r2i32_t result = { .min = {.x = r->max.x, .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2i32_t r2i32_cut_top_no_squash(r2i32_t *r, i32 value) { /* Y is down */
|
||||
i32 miny = r->min.y;
|
||||
r->min.y = r->min.y + value;
|
||||
r2i32_t result = { .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = r->min.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2i32_t r2i32_cut_bottom_no_squash(r2i32_t *r, i32 value) { /* Y is down */
|
||||
i32 maxy = r->max.y;
|
||||
r->max.y = r->max.y - value;
|
||||
r2i32_t result = { .min = {.x = r->min.x, .y = r->max.y}, .max = {.x = r->max.x, .y = maxy} };
|
||||
return result;
|
||||
}
|
||||
|
||||
fn r2i32_t r2i32_add_left(r2i32_t *r, i32 value) {
|
||||
r2i32_t result = { .min = {r->min.x - value, r->min.y}, .max = {r->min.x, r->max.y} };
|
||||
r->min.x -= value;
|
||||
return result;
|
||||
}
|
||||
fn r2i32_t r2i32_add_right(r2i32_t *r, i32 value) {
|
||||
r2i32_t result = { .min = {r->max.x, r->min.y}, .max = {r->max.x + value, r->max.y} };
|
||||
r->max.x += value;
|
||||
return result;
|
||||
}
|
||||
fn r2i32_t r2i32_add_top(r2i32_t *r, i32 value) {
|
||||
r2i32_t result = { .min = {r->min.x, r->min.y - value}, .max = {r->max.x, r->min.y} };
|
||||
r->min.y -= value;
|
||||
return result;
|
||||
}
|
||||
fn r2i32_t r2i32_add_bottom(r2i32_t *r, i32 value) {
|
||||
r2i32_t result = { .min = {r->min.x, r->max.y}, .max = {r->max.x, r->max.y + value} };
|
||||
r->max.y += value;
|
||||
return result;
|
||||
}
|
||||
|
||||
// get past left
|
||||
fn r2i32_t r2i32_getp_left(const r2i32_t *rect, i32 value) {
|
||||
r2i32_t result = r2i32(rect->min.x - value, rect->min.y, rect->min.x, rect->max.y);
|
||||
return result;
|
||||
}
|
||||
fn r2i32_t r2i32_getp_right(const r2i32_t *rect, i32 value) {
|
||||
r2i32_t result = r2i32(rect->max.x, rect->min.y, rect->max.x + value, rect->max.y);
|
||||
return result;
|
||||
}
|
||||
fn r2i32_t r2i32_getp_bottom(const r2i32_t *rect, i32 value) {
|
||||
r2i32_t result = r2i32(rect->min.x, rect->max.y, rect->max.x, rect->max.y + value);
|
||||
return result;
|
||||
}
|
||||
fn r2i32_t r2i32_getp_top(const r2i32_t *rect, i32 value) {
|
||||
r2i32_t result = r2i32(rect->min.x, rect->min.y - value, rect->max.x, rect->min.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn r2i32_t r2i32_get_left(const r2i32_t *r, i32 value) {
|
||||
i32 minx = r->min.x;
|
||||
r2i32_t result = (r2i32_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = MIN(r->min.x + value, r->max.x), .y =r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2i32_t r2i32_get_right(const r2i32_t *r, i32 value) {
|
||||
i32 maxx = r->max.x;
|
||||
r2i32_t result = (r2i32_t){ .min = {.x = MAX(r->min.x, r->max.x - value), .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2i32_t r2i32_get_top(const r2i32_t *r, i32 value) { /* Y is down */
|
||||
i32 miny = r->min.y;
|
||||
r2i32_t result = (r2i32_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = MIN(r->max.y, r->min.y + value)} };
|
||||
return result;
|
||||
}
|
||||
fn r2i32_t r2i32_get_bottom(const r2i32_t *r, i32 value) { /* Y is down */
|
||||
i32 maxy = r->max.y;
|
||||
r2i32_t result = (r2i32_t){ .min = {.x = r->min.x, .y = MAX(r->min.y, r->max.y - value)}, .max = {.x = r->max.x, .y = maxy} };
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
fn_inline r2i32_t r2i32_expand(r2i32_t rect, v2i32_t value) { return (r2i32_t){ v2i32_sub(rect.min, value), v2i32_add(rect.max, value) }; }
|
||||
fn_inline r2i32_t r2i32_shrink(r2i32_t rect, v2i32_t value) { return (r2i32_t){ v2i32_add(rect.min, value), v2i32_sub(rect.max, value) }; }
|
||||
fn_inline r2i32_t r2i32_shrinks(r2i32_t rect, i32 value) { return (r2i32_t){ v2i32_adds(rect.min, value), v2i32_subs(rect.max, value) }; }
|
||||
fn_inline v2i32_t r2i32_size(r2i32_t r) { return (v2i32_t){r.max.x - r.min.x, r.max.y - r.min.y}; }
|
||||
fn_inline v2i32_t r2i32_get_mid(r2i32_t r) { return v2i32_add(r.min, v2i32_divs(r2i32_size(r), 2)); }
|
||||
fn_inline b32 r2i32_contains(r2i32_t rec, v2i32_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); }
|
||||
fn_inline r2i32_t r2i32_intersect(r2i32_t a, r2i32_t b) { return (r2i32_t){ .min.x = MAX(a.min.x, b.min.x), .min.y = MAX(a.min.y, b.min.y), .max.x = MIN(a.max.x, b.max.x), .max.y = MIN(a.max.y, b.max.y) }; }
|
||||
fn_inline r2i32_t r2i32_union(r2i32_t a, r2i32_t b) { return (r2i32_t){ .min.x = MIN(a.min.x, b.min.x), .min.y = MIN(a.min.y, b.min.y), .max.x = MAX(a.max.x, b.max.x), .max.y = MAX(a.max.y, b.max.y) }; }
|
||||
|
||||
fn_inline i32 r1i32_size(r1i32_t a) { return a.max - a.min; }
|
||||
fn_inline r1i32_t r1i32(i32 min, i32 max) { return (r1i32_t){min,max}; }
|
||||
fn_inline r1i32_t r1i32_auto(i32 a, i32 b) { return (r1i32_t){MIN(a,b),MAX(a,b)}; }
|
||||
fn_inline r1i32_t r1i32s(i32 a) { return (r1i32_t){a,a}; }
|
||||
fn_inline r1i32_t r1i32_clamp(r1i32_t a, i32 min, i32 max) { return (r1i32_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; }
|
||||
fn_inline b32 r1i32_contains(r1i32_t rec, i32 point) { return (point >= rec.min) && (point < rec.max) && (point >= rec.min) && (point < rec.max); }
|
||||
fn_inline b32 r1i32_are_equal(r1i32_t a, r1i32_t b) { return a.min == b.min && a.max == b.max; }
|
||||
gb_read_only r1i32_t r1i32_null;
|
||||
|
||||
fn b32 r1i32_overlap(r1i32_t a, r1i32_t b) {
|
||||
b32 r1 = (a.max >= b.min && a.max <= b.max) || (a.min >= b.min && a.min <= b.max);
|
||||
b32 r2 = (b.max >= a.min && b.max <= a.max) || (b.min >= a.min && b.min <= a.max);
|
||||
return r1 || r2;
|
||||
}
|
||||
|
||||
fn v2i32_t v2i32_clamps(v2i32_t v, i32 min, i32 max) {
|
||||
v2i32_t result = { CLAMP(v.x, min, max), CLAMP(v.y, min, max) };
|
||||
return result;
|
||||
}
|
||||
fn v2i32_t v2i32_clamp(v2i32_t v, v2i32_t min, v2i32_t max) {
|
||||
v2i32_t result = { CLAMP(v.x, min.x, max.x), CLAMP(v.y, min.y, max.y) };
|
||||
return result;
|
||||
}
|
||||
|
||||
fn r2i32_t r2i32_fix(r2i32_t n) {
|
||||
r2i32_t result = {
|
||||
.min = {MIN(n.min.x, n.max.x), MIN(n.min.y, n.max.y)},
|
||||
.max = {MAX(n.min.x, n.max.x), MAX(n.min.y, n.max.y)},
|
||||
};
|
||||
return result;
|
||||
}
|
||||
fn r2i64_t r2i64_cut_left(r2i64_t *r, i64 value) {
|
||||
i64 minx = r->min.x;
|
||||
r->min.x = MIN(r->min.x + value, r->max.x);
|
||||
r2i64_t result = { .min = {.x = minx, .y = r->min.y}, .max = {.x = r->min.x, .y =r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2i64_t r2i64_cut_right(r2i64_t *r, i64 value) {
|
||||
i64 maxx = r->max.x;
|
||||
r->max.x = MAX(r->min.x, r->max.x - value);
|
||||
r2i64_t result = { .min = {.x = r->max.x, .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2i64_t r2i64_cut_top(r2i64_t *r, i64 value) { /* Y is down */
|
||||
i64 miny = r->min.y;
|
||||
r->min.y = MIN(r->max.y, r->min.y + value);
|
||||
r2i64_t result = { .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = r->min.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2i64_t r2i64_cut_bottom(r2i64_t *r, i64 value) { /* Y is down */
|
||||
i64 maxy = r->max.y;
|
||||
r->max.y = MAX(r->min.y, r->max.y - value);
|
||||
r2i64_t result = { .min = {.x = r->min.x, .y = r->max.y}, .max = {.x = r->max.x, .y = maxy} };
|
||||
return result;
|
||||
}
|
||||
|
||||
fn r2i64_t r2i64_cut_left_no_squash(r2i64_t *r, i64 value) {
|
||||
i64 minx = r->min.x;
|
||||
r->min.x = r->min.x + value;
|
||||
r2i64_t result = { .min = {.x = minx, .y = r->min.y}, .max = {.x = r->min.x, .y =r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2i64_t r2i64_cut_right_no_squash(r2i64_t *r, i64 value) {
|
||||
i64 maxx = r->max.x;
|
||||
r->max.x = r->max.x - value;
|
||||
r2i64_t result = { .min = {.x = r->max.x, .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2i64_t r2i64_cut_top_no_squash(r2i64_t *r, i64 value) { /* Y is down */
|
||||
i64 miny = r->min.y;
|
||||
r->min.y = r->min.y + value;
|
||||
r2i64_t result = { .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = r->min.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2i64_t r2i64_cut_bottom_no_squash(r2i64_t *r, i64 value) { /* Y is down */
|
||||
i64 maxy = r->max.y;
|
||||
r->max.y = r->max.y - value;
|
||||
r2i64_t result = { .min = {.x = r->min.x, .y = r->max.y}, .max = {.x = r->max.x, .y = maxy} };
|
||||
return result;
|
||||
}
|
||||
|
||||
fn r2i64_t r2i64_add_left(r2i64_t *r, i64 value) {
|
||||
r2i64_t result = { .min = {r->min.x - value, r->min.y}, .max = {r->min.x, r->max.y} };
|
||||
r->min.x -= value;
|
||||
return result;
|
||||
}
|
||||
fn r2i64_t r2i64_add_right(r2i64_t *r, i64 value) {
|
||||
r2i64_t result = { .min = {r->max.x, r->min.y}, .max = {r->max.x + value, r->max.y} };
|
||||
r->max.x += value;
|
||||
return result;
|
||||
}
|
||||
fn r2i64_t r2i64_add_top(r2i64_t *r, i64 value) {
|
||||
r2i64_t result = { .min = {r->min.x, r->min.y - value}, .max = {r->max.x, r->min.y} };
|
||||
r->min.y -= value;
|
||||
return result;
|
||||
}
|
||||
fn r2i64_t r2i64_add_bottom(r2i64_t *r, i64 value) {
|
||||
r2i64_t result = { .min = {r->min.x, r->max.y}, .max = {r->max.x, r->max.y + value} };
|
||||
r->max.y += value;
|
||||
return result;
|
||||
}
|
||||
|
||||
// get past left
|
||||
fn r2i64_t r2i64_getp_left(const r2i64_t *rect, i64 value) {
|
||||
r2i64_t result = r2i64(rect->min.x - value, rect->min.y, rect->min.x, rect->max.y);
|
||||
return result;
|
||||
}
|
||||
fn r2i64_t r2i64_getp_right(const r2i64_t *rect, i64 value) {
|
||||
r2i64_t result = r2i64(rect->max.x, rect->min.y, rect->max.x + value, rect->max.y);
|
||||
return result;
|
||||
}
|
||||
fn r2i64_t r2i64_getp_bottom(const r2i64_t *rect, i64 value) {
|
||||
r2i64_t result = r2i64(rect->min.x, rect->max.y, rect->max.x, rect->max.y + value);
|
||||
return result;
|
||||
}
|
||||
fn r2i64_t r2i64_getp_top(const r2i64_t *rect, i64 value) {
|
||||
r2i64_t result = r2i64(rect->min.x, rect->min.y - value, rect->max.x, rect->min.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn r2i64_t r2i64_get_left(const r2i64_t *r, i64 value) {
|
||||
i64 minx = r->min.x;
|
||||
r2i64_t result = (r2i64_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = MIN(r->min.x + value, r->max.x), .y =r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2i64_t r2i64_get_right(const r2i64_t *r, i64 value) {
|
||||
i64 maxx = r->max.x;
|
||||
r2i64_t result = (r2i64_t){ .min = {.x = MAX(r->min.x, r->max.x - value), .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2i64_t r2i64_get_top(const r2i64_t *r, i64 value) { /* Y is down */
|
||||
i64 miny = r->min.y;
|
||||
r2i64_t result = (r2i64_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = MIN(r->max.y, r->min.y + value)} };
|
||||
return result;
|
||||
}
|
||||
fn r2i64_t r2i64_get_bottom(const r2i64_t *r, i64 value) { /* Y is down */
|
||||
i64 maxy = r->max.y;
|
||||
r2i64_t result = (r2i64_t){ .min = {.x = r->min.x, .y = MAX(r->min.y, r->max.y - value)}, .max = {.x = r->max.x, .y = maxy} };
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
fn_inline r2i64_t r2i64_expand(r2i64_t rect, v2i64_t value) { return (r2i64_t){ v2i64_sub(rect.min, value), v2i64_add(rect.max, value) }; }
|
||||
fn_inline r2i64_t r2i64_shrink(r2i64_t rect, v2i64_t value) { return (r2i64_t){ v2i64_add(rect.min, value), v2i64_sub(rect.max, value) }; }
|
||||
fn_inline r2i64_t r2i64_shrinks(r2i64_t rect, i64 value) { return (r2i64_t){ v2i64_adds(rect.min, value), v2i64_subs(rect.max, value) }; }
|
||||
fn_inline v2i64_t r2i64_size(r2i64_t r) { return (v2i64_t){r.max.x - r.min.x, r.max.y - r.min.y}; }
|
||||
fn_inline v2i64_t r2i64_get_mid(r2i64_t r) { return v2i64_add(r.min, v2i64_divs(r2i64_size(r), 2)); }
|
||||
fn_inline b32 r2i64_contains(r2i64_t rec, v2i64_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); }
|
||||
fn_inline r2i64_t r2i64_intersect(r2i64_t a, r2i64_t b) { return (r2i64_t){ .min.x = MAX(a.min.x, b.min.x), .min.y = MAX(a.min.y, b.min.y), .max.x = MIN(a.max.x, b.max.x), .max.y = MIN(a.max.y, b.max.y) }; }
|
||||
fn_inline r2i64_t r2i64_union(r2i64_t a, r2i64_t b) { return (r2i64_t){ .min.x = MIN(a.min.x, b.min.x), .min.y = MIN(a.min.y, b.min.y), .max.x = MAX(a.max.x, b.max.x), .max.y = MAX(a.max.y, b.max.y) }; }
|
||||
|
||||
fn_inline i64 r1i64_size(r1i64_t a) { return a.max - a.min; }
|
||||
fn_inline r1i64_t r1i64(i64 min, i64 max) { return (r1i64_t){min,max}; }
|
||||
fn_inline r1i64_t r1i64_auto(i64 a, i64 b) { return (r1i64_t){MIN(a,b),MAX(a,b)}; }
|
||||
fn_inline r1i64_t r1i64s(i64 a) { return (r1i64_t){a,a}; }
|
||||
fn_inline r1i64_t r1i64_clamp(r1i64_t a, i64 min, i64 max) { return (r1i64_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; }
|
||||
fn_inline b32 r1i64_contains(r1i64_t rec, i64 point) { return (point >= rec.min) && (point < rec.max) && (point >= rec.min) && (point < rec.max); }
|
||||
fn_inline b32 r1i64_are_equal(r1i64_t a, r1i64_t b) { return a.min == b.min && a.max == b.max; }
|
||||
gb_read_only r1i64_t r1i64_null;
|
||||
|
||||
fn b32 r1i64_overlap(r1i64_t a, r1i64_t b) {
|
||||
b32 r1 = (a.max >= b.min && a.max <= b.max) || (a.min >= b.min && a.min <= b.max);
|
||||
b32 r2 = (b.max >= a.min && b.max <= a.max) || (b.min >= a.min && b.min <= a.max);
|
||||
return r1 || r2;
|
||||
}
|
||||
|
||||
fn v2i64_t v2i64_clamps(v2i64_t v, i64 min, i64 max) {
|
||||
v2i64_t result = { CLAMP(v.x, min, max), CLAMP(v.y, min, max) };
|
||||
return result;
|
||||
}
|
||||
fn v2i64_t v2i64_clamp(v2i64_t v, v2i64_t min, v2i64_t max) {
|
||||
v2i64_t result = { CLAMP(v.x, min.x, max.x), CLAMP(v.y, min.y, max.y) };
|
||||
return result;
|
||||
}
|
||||
|
||||
fn r2i64_t r2i64_fix(r2i64_t n) {
|
||||
r2i64_t result = {
|
||||
.min = {MIN(n.min.x, n.max.x), MIN(n.min.y, n.max.y)},
|
||||
.max = {MAX(n.min.x, n.max.x), MAX(n.min.y, n.max.y)},
|
||||
};
|
||||
return result;
|
||||
}
|
||||
fn_inline v2f64_t v2f32_to_v2f64(v2f32_t v) { return (v2f64_t){ (f64)v.x, (f64)v.y }; }
|
||||
fn_inline v3f64_t v3f32_to_v3f64(v3f32_t v) { return (v3f64_t){ (f64)v.x, (f64)v.y, (f64)v.z }; }
|
||||
fn_inline v4f64_t v4f32_to_v4f64(v4f32_t v) { return (v4f64_t){ (f64)v.x, (f64)v.y, (f64)v.z, (f64)v.w }; }
|
||||
fn_inline r2f64_t r2f32_to_r2f64(r2f32_t v) { return (r2f64_t){ (f64)v.x0, (f64)v.y0, (f64)v.x1, (f64)v.y1 }; }
|
||||
fn_inline r3f64_t r3f32_to_r3f64(r3f32_t v) { return (r3f64_t){ (f64)v.x0, (f64)v.y0, (f64)v.z0, (f64)v.x1, (f64)v.y1, (f64)v.z1 }; }
|
||||
fn_inline v2i32_t v2f32_to_v2i32(v2f32_t v) { return (v2i32_t){ (i32)v.x, (i32)v.y }; }
|
||||
fn_inline v3i32_t v3f32_to_v3i32(v3f32_t v) { return (v3i32_t){ (i32)v.x, (i32)v.y, (i32)v.z }; }
|
||||
fn_inline v4i32_t v4f32_to_v4i32(v4f32_t v) { return (v4i32_t){ (i32)v.x, (i32)v.y, (i32)v.z, (i32)v.w }; }
|
||||
fn_inline r2i32_t r2f32_to_r2i32(r2f32_t v) { return (r2i32_t){ (i32)v.x0, (i32)v.y0, (i32)v.x1, (i32)v.y1 }; }
|
||||
fn_inline r3i32_t r3f32_to_r3i32(r3f32_t v) { return (r3i32_t){ (i32)v.x0, (i32)v.y0, (i32)v.z0, (i32)v.x1, (i32)v.y1, (i32)v.z1 }; }
|
||||
fn_inline v2i64_t v2f32_to_v2i64(v2f32_t v) { return (v2i64_t){ (i64)v.x, (i64)v.y }; }
|
||||
fn_inline v3i64_t v3f32_to_v3i64(v3f32_t v) { return (v3i64_t){ (i64)v.x, (i64)v.y, (i64)v.z }; }
|
||||
fn_inline v4i64_t v4f32_to_v4i64(v4f32_t v) { return (v4i64_t){ (i64)v.x, (i64)v.y, (i64)v.z, (i64)v.w }; }
|
||||
fn_inline r2i64_t r2f32_to_r2i64(r2f32_t v) { return (r2i64_t){ (i64)v.x0, (i64)v.y0, (i64)v.x1, (i64)v.y1 }; }
|
||||
fn_inline r3i64_t r3f32_to_r3i64(r3f32_t v) { return (r3i64_t){ (i64)v.x0, (i64)v.y0, (i64)v.z0, (i64)v.x1, (i64)v.y1, (i64)v.z1 }; }
|
||||
fn_inline v2f32_t v2f64_to_v2f32(v2f64_t v) { return (v2f32_t){ (f32)v.x, (f32)v.y }; }
|
||||
fn_inline v3f32_t v3f64_to_v3f32(v3f64_t v) { return (v3f32_t){ (f32)v.x, (f32)v.y, (f32)v.z }; }
|
||||
fn_inline v4f32_t v4f64_to_v4f32(v4f64_t v) { return (v4f32_t){ (f32)v.x, (f32)v.y, (f32)v.z, (f32)v.w }; }
|
||||
fn_inline r2f32_t r2f64_to_r2f32(r2f64_t v) { return (r2f32_t){ (f32)v.x0, (f32)v.y0, (f32)v.x1, (f32)v.y1 }; }
|
||||
fn_inline r3f32_t r3f64_to_r3f32(r3f64_t v) { return (r3f32_t){ (f32)v.x0, (f32)v.y0, (f32)v.z0, (f32)v.x1, (f32)v.y1, (f32)v.z1 }; }
|
||||
fn_inline v2i32_t v2f64_to_v2i32(v2f64_t v) { return (v2i32_t){ (i32)v.x, (i32)v.y }; }
|
||||
fn_inline v3i32_t v3f64_to_v3i32(v3f64_t v) { return (v3i32_t){ (i32)v.x, (i32)v.y, (i32)v.z }; }
|
||||
fn_inline v4i32_t v4f64_to_v4i32(v4f64_t v) { return (v4i32_t){ (i32)v.x, (i32)v.y, (i32)v.z, (i32)v.w }; }
|
||||
fn_inline r2i32_t r2f64_to_r2i32(r2f64_t v) { return (r2i32_t){ (i32)v.x0, (i32)v.y0, (i32)v.x1, (i32)v.y1 }; }
|
||||
fn_inline r3i32_t r3f64_to_r3i32(r3f64_t v) { return (r3i32_t){ (i32)v.x0, (i32)v.y0, (i32)v.z0, (i32)v.x1, (i32)v.y1, (i32)v.z1 }; }
|
||||
fn_inline v2i64_t v2f64_to_v2i64(v2f64_t v) { return (v2i64_t){ (i64)v.x, (i64)v.y }; }
|
||||
fn_inline v3i64_t v3f64_to_v3i64(v3f64_t v) { return (v3i64_t){ (i64)v.x, (i64)v.y, (i64)v.z }; }
|
||||
fn_inline v4i64_t v4f64_to_v4i64(v4f64_t v) { return (v4i64_t){ (i64)v.x, (i64)v.y, (i64)v.z, (i64)v.w }; }
|
||||
fn_inline r2i64_t r2f64_to_r2i64(r2f64_t v) { return (r2i64_t){ (i64)v.x0, (i64)v.y0, (i64)v.x1, (i64)v.y1 }; }
|
||||
fn_inline r3i64_t r3f64_to_r3i64(r3f64_t v) { return (r3i64_t){ (i64)v.x0, (i64)v.y0, (i64)v.z0, (i64)v.x1, (i64)v.y1, (i64)v.z1 }; }
|
||||
fn_inline v2f32_t v2i32_to_v2f32(v2i32_t v) { return (v2f32_t){ (f32)v.x, (f32)v.y }; }
|
||||
fn_inline v3f32_t v3i32_to_v3f32(v3i32_t v) { return (v3f32_t){ (f32)v.x, (f32)v.y, (f32)v.z }; }
|
||||
fn_inline v4f32_t v4i32_to_v4f32(v4i32_t v) { return (v4f32_t){ (f32)v.x, (f32)v.y, (f32)v.z, (f32)v.w }; }
|
||||
fn_inline r2f32_t r2i32_to_r2f32(r2i32_t v) { return (r2f32_t){ (f32)v.x0, (f32)v.y0, (f32)v.x1, (f32)v.y1 }; }
|
||||
fn_inline r3f32_t r3i32_to_r3f32(r3i32_t v) { return (r3f32_t){ (f32)v.x0, (f32)v.y0, (f32)v.z0, (f32)v.x1, (f32)v.y1, (f32)v.z1 }; }
|
||||
fn_inline v2f64_t v2i32_to_v2f64(v2i32_t v) { return (v2f64_t){ (f64)v.x, (f64)v.y }; }
|
||||
fn_inline v3f64_t v3i32_to_v3f64(v3i32_t v) { return (v3f64_t){ (f64)v.x, (f64)v.y, (f64)v.z }; }
|
||||
fn_inline v4f64_t v4i32_to_v4f64(v4i32_t v) { return (v4f64_t){ (f64)v.x, (f64)v.y, (f64)v.z, (f64)v.w }; }
|
||||
fn_inline r2f64_t r2i32_to_r2f64(r2i32_t v) { return (r2f64_t){ (f64)v.x0, (f64)v.y0, (f64)v.x1, (f64)v.y1 }; }
|
||||
fn_inline r3f64_t r3i32_to_r3f64(r3i32_t v) { return (r3f64_t){ (f64)v.x0, (f64)v.y0, (f64)v.z0, (f64)v.x1, (f64)v.y1, (f64)v.z1 }; }
|
||||
fn_inline v2i64_t v2i32_to_v2i64(v2i32_t v) { return (v2i64_t){ (i64)v.x, (i64)v.y }; }
|
||||
fn_inline v3i64_t v3i32_to_v3i64(v3i32_t v) { return (v3i64_t){ (i64)v.x, (i64)v.y, (i64)v.z }; }
|
||||
fn_inline v4i64_t v4i32_to_v4i64(v4i32_t v) { return (v4i64_t){ (i64)v.x, (i64)v.y, (i64)v.z, (i64)v.w }; }
|
||||
fn_inline r2i64_t r2i32_to_r2i64(r2i32_t v) { return (r2i64_t){ (i64)v.x0, (i64)v.y0, (i64)v.x1, (i64)v.y1 }; }
|
||||
fn_inline r3i64_t r3i32_to_r3i64(r3i32_t v) { return (r3i64_t){ (i64)v.x0, (i64)v.y0, (i64)v.z0, (i64)v.x1, (i64)v.y1, (i64)v.z1 }; }
|
||||
fn_inline v2f32_t v2i64_to_v2f32(v2i64_t v) { return (v2f32_t){ (f32)v.x, (f32)v.y }; }
|
||||
fn_inline v3f32_t v3i64_to_v3f32(v3i64_t v) { return (v3f32_t){ (f32)v.x, (f32)v.y, (f32)v.z }; }
|
||||
fn_inline v4f32_t v4i64_to_v4f32(v4i64_t v) { return (v4f32_t){ (f32)v.x, (f32)v.y, (f32)v.z, (f32)v.w }; }
|
||||
fn_inline r2f32_t r2i64_to_r2f32(r2i64_t v) { return (r2f32_t){ (f32)v.x0, (f32)v.y0, (f32)v.x1, (f32)v.y1 }; }
|
||||
fn_inline r3f32_t r3i64_to_r3f32(r3i64_t v) { return (r3f32_t){ (f32)v.x0, (f32)v.y0, (f32)v.z0, (f32)v.x1, (f32)v.y1, (f32)v.z1 }; }
|
||||
fn_inline v2f64_t v2i64_to_v2f64(v2i64_t v) { return (v2f64_t){ (f64)v.x, (f64)v.y }; }
|
||||
fn_inline v3f64_t v3i64_to_v3f64(v3i64_t v) { return (v3f64_t){ (f64)v.x, (f64)v.y, (f64)v.z }; }
|
||||
fn_inline v4f64_t v4i64_to_v4f64(v4i64_t v) { return (v4f64_t){ (f64)v.x, (f64)v.y, (f64)v.z, (f64)v.w }; }
|
||||
fn_inline r2f64_t r2i64_to_r2f64(r2i64_t v) { return (r2f64_t){ (f64)v.x0, (f64)v.y0, (f64)v.x1, (f64)v.y1 }; }
|
||||
fn_inline r3f64_t r3i64_to_r3f64(r3i64_t v) { return (r3f64_t){ (f64)v.x0, (f64)v.y0, (f64)v.z0, (f64)v.x1, (f64)v.y1, (f64)v.z1 }; }
|
||||
fn_inline v2i32_t v2i64_to_v2i32(v2i64_t v) { return (v2i32_t){ (i32)v.x, (i32)v.y }; }
|
||||
fn_inline v3i32_t v3i64_to_v3i32(v3i64_t v) { return (v3i32_t){ (i32)v.x, (i32)v.y, (i32)v.z }; }
|
||||
fn_inline v4i32_t v4i64_to_v4i32(v4i64_t v) { return (v4i32_t){ (i32)v.x, (i32)v.y, (i32)v.z, (i32)v.w }; }
|
||||
fn_inline r2i32_t r2i64_to_r2i32(r2i64_t v) { return (r2i32_t){ (i32)v.x0, (i32)v.y0, (i32)v.x1, (i32)v.y1 }; }
|
||||
fn_inline r3i32_t r3i64_to_r3i32(r3i64_t v) { return (r3i32_t){ (i32)v.x0, (i32)v.y0, (i32)v.z0, (i32)v.x1, (i32)v.y1, (i32)v.z1 }; }
|
||||
fn_inline r2f32_t r2f32_add_v2f32(r2f32_t a, v2f32_t b) { return (r2f32_t){ v2f32_add(a.min, b), v2f32_add(a.max, b) }; }
|
||||
fn_inline r2f32_t r2f32_sub_v2f32(r2f32_t a, v2f32_t b) { return (r2f32_t){ v2f32_sub(a.min, b), v2f32_sub(a.max, b) }; }
|
||||
fn_inline r2f32_t r2f32_mul_v2f32(r2f32_t a, v2f32_t b) { return (r2f32_t){ v2f32_mul(a.min, b), v2f32_mul(a.max, b) }; }
|
||||
fn_inline r2f32_t r2f32_div_v2f32(r2f32_t a, v2f32_t b) { return (r2f32_t){ v2f32_div(a.min, b), v2f32_div(a.max, b) }; }
|
||||
fn_inline r2f64_t r2f64_add_v2f64(r2f64_t a, v2f64_t b) { return (r2f64_t){ v2f64_add(a.min, b), v2f64_add(a.max, b) }; }
|
||||
fn_inline r2f64_t r2f64_sub_v2f64(r2f64_t a, v2f64_t b) { return (r2f64_t){ v2f64_sub(a.min, b), v2f64_sub(a.max, b) }; }
|
||||
fn_inline r2f64_t r2f64_mul_v2f64(r2f64_t a, v2f64_t b) { return (r2f64_t){ v2f64_mul(a.min, b), v2f64_mul(a.max, b) }; }
|
||||
fn_inline r2f64_t r2f64_div_v2f64(r2f64_t a, v2f64_t b) { return (r2f64_t){ v2f64_div(a.min, b), v2f64_div(a.max, b) }; }
|
||||
fn_inline r2i32_t r2i32_add_v2i32(r2i32_t a, v2i32_t b) { return (r2i32_t){ v2i32_add(a.min, b), v2i32_add(a.max, b) }; }
|
||||
fn_inline r2i32_t r2i32_sub_v2i32(r2i32_t a, v2i32_t b) { return (r2i32_t){ v2i32_sub(a.min, b), v2i32_sub(a.max, b) }; }
|
||||
fn_inline r2i32_t r2i32_mul_v2i32(r2i32_t a, v2i32_t b) { return (r2i32_t){ v2i32_mul(a.min, b), v2i32_mul(a.max, b) }; }
|
||||
fn_inline r2i32_t r2i32_div_v2i32(r2i32_t a, v2i32_t b) { return (r2i32_t){ v2i32_div(a.min, b), v2i32_div(a.max, b) }; }
|
||||
fn_inline r2i64_t r2i64_add_v2i64(r2i64_t a, v2i64_t b) { return (r2i64_t){ v2i64_add(a.min, b), v2i64_add(a.max, b) }; }
|
||||
fn_inline r2i64_t r2i64_sub_v2i64(r2i64_t a, v2i64_t b) { return (r2i64_t){ v2i64_sub(a.min, b), v2i64_sub(a.max, b) }; }
|
||||
fn_inline r2i64_t r2i64_mul_v2i64(r2i64_t a, v2i64_t b) { return (r2i64_t){ v2i64_mul(a.min, b), v2i64_mul(a.max, b) }; }
|
||||
fn_inline r2i64_t r2i64_div_v2i64(r2i64_t a, v2i64_t b) { return (r2i64_t){ v2i64_div(a.min, b), v2i64_div(a.max, b) }; }
|
||||
fn f32 f32_safe_ratio(f32 a, f32 b, f32 if0) {
|
||||
f32 result = if0;
|
||||
if (b != 0.0) {
|
||||
result = a / b;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn_inline f32 f32_safe_ratio0(f32 a, f32 b) { return f32_safe_ratio(a, b, 0); }
|
||||
fn_inline f32 f32_safe_ratio1(f32 a, f32 b) { return f32_safe_ratio(a, b, 0); }
|
||||
fn f64 f64_safe_ratio(f64 a, f64 b, f64 if0) {
|
||||
f64 result = if0;
|
||||
if (b != 0.0) {
|
||||
result = a / b;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn_inline f64 f64_safe_ratio0(f64 a, f64 b) { return f64_safe_ratio(a, b, 0); }
|
||||
fn_inline f64 f64_safe_ratio1(f64 a, f64 b) { return f64_safe_ratio(a, b, 0); }
|
||||
@@ -1,215 +0,0 @@
|
||||
#pragma once
|
||||
#include "core_basic.h"
|
||||
|
||||
typedef union v2f32_t v2f32_t;
|
||||
union v2f32_t {
|
||||
struct { f32 x, y; };
|
||||
f32 e[2];
|
||||
};
|
||||
|
||||
typedef union v3f32_t v3f32_t;
|
||||
union v3f32_t {
|
||||
struct { f32 x, y, z; };
|
||||
struct { v2f32_t xy; };
|
||||
struct { f32 _x0; v2f32_t zw; };
|
||||
struct { f32 r, g, b; };
|
||||
struct { f32 h, s, l; };
|
||||
f32 e[3];
|
||||
};
|
||||
|
||||
typedef union v4f32_t v4f32_t;
|
||||
union v4f32_t {
|
||||
struct { f32 x, y, z, w; };
|
||||
struct { v2f32_t xy; v2f32_t zw; };
|
||||
struct { v3f32_t xyz; };
|
||||
struct { f32 _x0; f32 yzw; };
|
||||
struct { f32 r, g, b, a; };
|
||||
struct { f32 h, s, l, _a; };
|
||||
f32 e[4];
|
||||
};
|
||||
|
||||
typedef union r1f32_t r1f32_t;
|
||||
union r1f32_t {
|
||||
struct { f32 min, max; };
|
||||
struct { f32 x0, x1; };
|
||||
f32 e[2];
|
||||
};
|
||||
|
||||
typedef union r2f32_t r2f32_t;
|
||||
union r2f32_t {
|
||||
struct { v2f32_t min, max; };
|
||||
struct { f32 x0, y0, x1, y1; };
|
||||
v4f32_t e4;
|
||||
f32 e[4];
|
||||
};
|
||||
|
||||
typedef union r3f32_t r3f32_t;
|
||||
union r3f32_t {
|
||||
struct { v3f32_t min, max; };
|
||||
struct { f32 x0, y0, z0, x1, y1, z1; };
|
||||
f32 e[6];
|
||||
};
|
||||
|
||||
|
||||
typedef union v2f64_t v2f64_t;
|
||||
union v2f64_t {
|
||||
struct { f64 x, y; };
|
||||
f64 e[2];
|
||||
};
|
||||
|
||||
typedef union v3f64_t v3f64_t;
|
||||
union v3f64_t {
|
||||
struct { f64 x, y, z; };
|
||||
struct { v2f64_t xy; };
|
||||
struct { f64 _x0; v2f64_t zw; };
|
||||
struct { f64 r, g, b; };
|
||||
struct { f64 h, s, l; };
|
||||
f64 e[3];
|
||||
};
|
||||
|
||||
typedef union v4f64_t v4f64_t;
|
||||
union v4f64_t {
|
||||
struct { f64 x, y, z, w; };
|
||||
struct { v2f64_t xy; v2f64_t zw; };
|
||||
struct { v3f64_t xyz; };
|
||||
struct { f64 _x0; f64 yzw; };
|
||||
struct { f64 r, g, b, a; };
|
||||
struct { f64 h, s, l, _a; };
|
||||
f64 e[4];
|
||||
};
|
||||
|
||||
typedef union r1f64_t r1f64_t;
|
||||
union r1f64_t {
|
||||
struct { f64 min, max; };
|
||||
struct { f64 x0, x1; };
|
||||
f64 e[2];
|
||||
};
|
||||
|
||||
typedef union r2f64_t r2f64_t;
|
||||
union r2f64_t {
|
||||
struct { v2f64_t min, max; };
|
||||
struct { f64 x0, y0, x1, y1; };
|
||||
v4f64_t e4;
|
||||
f64 e[4];
|
||||
};
|
||||
|
||||
typedef union r3f64_t r3f64_t;
|
||||
union r3f64_t {
|
||||
struct { v3f64_t min, max; };
|
||||
struct { f64 x0, y0, z0, x1, y1, z1; };
|
||||
f64 e[6];
|
||||
};
|
||||
|
||||
|
||||
typedef union v2i32_t v2i32_t;
|
||||
union v2i32_t {
|
||||
struct { i32 x, y; };
|
||||
i32 e[2];
|
||||
};
|
||||
|
||||
typedef union v3i32_t v3i32_t;
|
||||
union v3i32_t {
|
||||
struct { i32 x, y, z; };
|
||||
struct { v2i32_t xy; };
|
||||
struct { i32 _x0; v2i32_t zw; };
|
||||
struct { i32 r, g, b; };
|
||||
struct { i32 h, s, l; };
|
||||
i32 e[3];
|
||||
};
|
||||
|
||||
typedef union v4i32_t v4i32_t;
|
||||
union v4i32_t {
|
||||
struct { i32 x, y, z, w; };
|
||||
struct { v2i32_t xy; v2i32_t zw; };
|
||||
struct { v3i32_t xyz; };
|
||||
struct { i32 _x0; i32 yzw; };
|
||||
struct { i32 r, g, b, a; };
|
||||
struct { i32 h, s, l, _a; };
|
||||
i32 e[4];
|
||||
};
|
||||
|
||||
typedef union r1i32_t r1i32_t;
|
||||
union r1i32_t {
|
||||
struct { i32 min, max; };
|
||||
struct { i32 x0, x1; };
|
||||
i32 e[2];
|
||||
};
|
||||
|
||||
typedef union r2i32_t r2i32_t;
|
||||
union r2i32_t {
|
||||
struct { v2i32_t min, max; };
|
||||
struct { i32 x0, y0, x1, y1; };
|
||||
v4i32_t e4;
|
||||
i32 e[4];
|
||||
};
|
||||
|
||||
typedef union r3i32_t r3i32_t;
|
||||
union r3i32_t {
|
||||
struct { v3i32_t min, max; };
|
||||
struct { i32 x0, y0, z0, x1, y1, z1; };
|
||||
i32 e[6];
|
||||
};
|
||||
|
||||
|
||||
typedef union v2i64_t v2i64_t;
|
||||
union v2i64_t {
|
||||
struct { i64 x, y; };
|
||||
i64 e[2];
|
||||
};
|
||||
|
||||
typedef union v3i64_t v3i64_t;
|
||||
union v3i64_t {
|
||||
struct { i64 x, y, z; };
|
||||
struct { v2i64_t xy; };
|
||||
struct { i64 _x0; v2i64_t zw; };
|
||||
struct { i64 r, g, b; };
|
||||
struct { i64 h, s, l; };
|
||||
i64 e[3];
|
||||
};
|
||||
|
||||
typedef union v4i64_t v4i64_t;
|
||||
union v4i64_t {
|
||||
struct { i64 x, y, z, w; };
|
||||
struct { v2i64_t xy; v2i64_t zw; };
|
||||
struct { v3i64_t xyz; };
|
||||
struct { i64 _x0; i64 yzw; };
|
||||
struct { i64 r, g, b, a; };
|
||||
struct { i64 h, s, l, _a; };
|
||||
i64 e[4];
|
||||
};
|
||||
|
||||
typedef union r1i64_t r1i64_t;
|
||||
union r1i64_t {
|
||||
struct { i64 min, max; };
|
||||
struct { i64 x0, x1; };
|
||||
i64 e[2];
|
||||
};
|
||||
|
||||
typedef union r2i64_t r2i64_t;
|
||||
union r2i64_t {
|
||||
struct { v2i64_t min, max; };
|
||||
struct { i64 x0, y0, x1, y1; };
|
||||
v4i64_t e4;
|
||||
i64 e[4];
|
||||
};
|
||||
|
||||
typedef union r3i64_t r3i64_t;
|
||||
union r3i64_t {
|
||||
struct { v3i64_t min, max; };
|
||||
struct { i64 x0, y0, z0, x1, y1, z1; };
|
||||
i64 e[6];
|
||||
};
|
||||
|
||||
#define F64_PI (3.1415926535897932384626433832795028841971693994)
|
||||
#define F64_DEG2RAD (F64_PI / 180.0); // @Usage: degree * DEG2RAD = radians;
|
||||
#define F64_RAD2DEG (180.0 / F64_PI);
|
||||
#define F32_PI ((f32)F64_PI)
|
||||
|
||||
#define v4f32_const_rgba255(r, g, b, a) { (f32)(r) / 255.0f, (f32)(g) / 255.0f, (f32)(b) / 255.0f, (f32)(a) / 255.0f }
|
||||
#define v4f32_rgba255(r, g, b, a) (v4f32_t){ (f32)(r) / 255.0f, (f32)(g) / 255.0f, (f32)(b) / 255.0f, (f32)(a) / 255.0f }
|
||||
|
||||
static gb_read_only v4f32_t white_color = v4f32_const_rgba255(255, 255, 255, 255);
|
||||
static gb_read_only v4f32_t black_color = v4f32_const_rgba255(0, 0, 0, 255);
|
||||
static gb_read_only v4f32_t magenta_color = v4f32_const_rgba255(255, 0, 255, 255);
|
||||
static gb_read_only v4f32_t red_color = v4f32_const_rgba255(255, 0, 0, 255);
|
||||
static gb_read_only v4f32_t blue_color = v4f32_const_rgba255(0, 0, 255, 255);
|
||||
@@ -1,406 +0,0 @@
|
||||
import sys
|
||||
|
||||
op_idx = 0
|
||||
opname_idx = 1
|
||||
symbols = [
|
||||
["+", "add"],
|
||||
["-", "sub"],
|
||||
["*", "mul"],
|
||||
["/", "div"],
|
||||
]
|
||||
|
||||
basic_types = [
|
||||
"f32",
|
||||
"f64",
|
||||
"i32",
|
||||
"i64",
|
||||
]
|
||||
|
||||
name_idx = 0
|
||||
member_idx = 1
|
||||
|
||||
vec_types = [
|
||||
["v2", ["x", "y"]],
|
||||
["v3", ["x", "y", "z"]],
|
||||
["v4", ["x", "y", "z", "w"]],
|
||||
]
|
||||
|
||||
rect_types = [
|
||||
["r2", ["x0", "y0", "x1", "y1"]],
|
||||
["r3", ["x0", "y0", "z0", "x1", "y1", "z1"]],
|
||||
]
|
||||
|
||||
struct_types = vec_types + rect_types
|
||||
|
||||
|
||||
# fd = open("core_math.gen.h", "w")
|
||||
# sys.stdout = fd
|
||||
|
||||
# print("// auto generated using:", __file__)
|
||||
|
||||
# for bt in basic_types:
|
||||
# s = """
|
||||
# typedef union v2f64_t v2f64_t;
|
||||
# union v2f64_t {
|
||||
# struct { f64 x, y; };
|
||||
# f64 e[2];
|
||||
# };
|
||||
|
||||
# typedef union v3f64_t v3f64_t;
|
||||
# union v3f64_t {
|
||||
# struct { f64 x, y, z; };
|
||||
# struct { v2f64_t xy; };
|
||||
# struct { f64 _x0; v2f64_t zw; };
|
||||
# struct { f64 r, g, b; };
|
||||
# struct { f64 h, s, l; };
|
||||
# f64 e[3];
|
||||
# };
|
||||
|
||||
# typedef union v4f64_t v4f64_t;
|
||||
# union v4f64_t {
|
||||
# struct { f64 x, y, z, w; };
|
||||
# struct { v2f64_t xy; v2f64_t zw; };
|
||||
# struct { v3f64_t xyz; };
|
||||
# struct { f64 _x0; f64 yzw; };
|
||||
# struct { f64 r, g, b, a; };
|
||||
# struct { f64 h, s, l, _a; };
|
||||
# f64 e[4];
|
||||
# };
|
||||
|
||||
# typedef union r1f64_t r1f64_t;
|
||||
# union r1f64_t {
|
||||
# struct { f64 min, max; };
|
||||
# struct { f64 x0, x1; };
|
||||
# f64 e[2];
|
||||
# };
|
||||
|
||||
# typedef union r2f64_t r2f64_t;
|
||||
# union r2f64_t {
|
||||
# struct { v2f64_t min, max; };
|
||||
# struct { f64 x0, y0, x1, y1; };
|
||||
# v4f64_t e4;
|
||||
# f64 e[4];
|
||||
# };
|
||||
|
||||
# typedef union r3f64_t r3f64_t;
|
||||
# union r3f64_t {
|
||||
# struct { v3f64_t min, max; };
|
||||
# struct { f64 x0, y0, z0, x1, y1, z1; };
|
||||
# f64 e[6];
|
||||
# };
|
||||
#
|
||||
#
|
||||
# type_t type__v2f64_t = { type_kind_struct, s8_const("v2f64_t"), sizeof(v2f64_t), .count = 2,
|
||||
# .members = (type_member_t[]){
|
||||
# {s8_const("x"), &type__f64, .offset = offsetof(v2f64_t, x)},
|
||||
# {s8_const("y"), &type__f64, .offset = offsetof(v2f64_t, y)},
|
||||
# }
|
||||
# };
|
||||
|
||||
# type_t type__v3f64_t = { type_kind_struct, s8_const("v3f64_t"), sizeof(v3f64_t), .count = 3,
|
||||
# .members = (type_member_t[]){
|
||||
# {s8_const("x"), &type__f64, .offset = offsetof(v3f64_t, x)},
|
||||
# {s8_const("y"), &type__f64, .offset = offsetof(v3f64_t, y)},
|
||||
# {s8_const("z"), &type__f64, .offset = offsetof(v3f64_t, z)},
|
||||
# }
|
||||
# };
|
||||
|
||||
# type_t type__v4f64_t = { type_kind_struct, s8_const("v4f64_t"), sizeof(v4f64_t), .count = 4,
|
||||
# .members = (type_member_t[]){
|
||||
# {s8_const("x"), &type__f64, .offset = offsetof(v4f64_t, x)},
|
||||
# {s8_const("y"), &type__f64, .offset = offsetof(v4f64_t, y)},
|
||||
# {s8_const("z"), &type__f64, .offset = offsetof(v4f64_t, z)},
|
||||
# {s8_const("w"), &type__f64, .offset = offsetof(v4f64_t, w)},
|
||||
# }
|
||||
# };
|
||||
|
||||
# type_t type__r3f64_t = { type_kind_struct, s8_const("r3f64_t"), sizeof(r3f64_t), .count = 6,
|
||||
# .members = (type_member_t[]){
|
||||
# {s8_const("x0"), &type__f64, .offset = offsetof(r3f64_t, x0)},
|
||||
# {s8_const("y0"), &type__f64, .offset = offsetof(r3f64_t, y0)},
|
||||
# {s8_const("x0"), &type__f64, .offset = offsetof(r3f64_t, z0)},
|
||||
# {s8_const("x1"), &type__f64, .offset = offsetof(r3f64_t, x1)},
|
||||
# {s8_const("y1"), &type__f64, .offset = offsetof(r3f64_t, y1)},
|
||||
# {s8_const("x1"), &type__f64, .offset = offsetof(r3f64_t, z1)},
|
||||
# }
|
||||
# };
|
||||
|
||||
# type_member_t members__r2f64_t[] = {
|
||||
# {s8_const("x0"), &type__f64, .offset = offsetof(r2f64_t, x0)},
|
||||
# {s8_const("y0"), &type__f64, .offset = offsetof(r2f64_t, y0)},
|
||||
# {s8_const("x1"), &type__f64, .offset = offsetof(r2f64_t, x1)},
|
||||
# {s8_const("y1"), &type__f64, .offset = offsetof(r2f64_t, y1)},
|
||||
# };
|
||||
# DEFINE_STRUCT(r2f64_t);
|
||||
|
||||
# type_member_t members__r1f64_t[] = {
|
||||
# {s8_const("x0"), &type__f64, .offset = offsetof(r1f64_t, x0)},
|
||||
# {s8_const("x1"), &type__f64, .offset = offsetof(r1f64_t, x1)},
|
||||
# };
|
||||
# DEFINE_STRUCT(r1f64_t);
|
||||
# """
|
||||
# s = s.replace("f64", bt)
|
||||
# print(s)
|
||||
|
||||
|
||||
fd = open("core_math.gen.c", "w", newline = "\n", encoding = "utf-8")
|
||||
sys.stdout = fd
|
||||
|
||||
for basic_type in basic_types:
|
||||
for st in struct_types:
|
||||
type = f"{st[name_idx]}{basic_type}"
|
||||
typedef = f"{type}_t"
|
||||
members = st[member_idx]
|
||||
|
||||
def members_as_func_params(members, basic_type):
|
||||
result = ""
|
||||
for i, m in enumerate(members):
|
||||
result += f"{basic_type} {m}"
|
||||
if i != len(members) - 1:
|
||||
result += ", "
|
||||
return result
|
||||
|
||||
print(f"""fn_inline {type}_t {type}({members_as_func_params(members, basic_type)}) {{ return ({type}_t){{ {members_as_func_params(members, "")} }}; }}""")
|
||||
print(f"""gb_read_only {type}_t {type}_null = {{0}};""")
|
||||
print(f"""fn_inline b32 {type}_is_null({type}_t a) {{ return memory_equal(&a, &{type}_null, sizeof(a)); }}""")
|
||||
print(f"""fn_inline b32 {type}_are_equal({type}_t a, {type}_t b) {{ return memory_equal(&a, &b, sizeof(a)); }}""")
|
||||
|
||||
for sym in symbols:
|
||||
op = sym[op_idx]
|
||||
opname = sym[opname_idx]
|
||||
|
||||
|
||||
def op_str(members, op, skip_right = False):
|
||||
result = ""
|
||||
for i in range(len(members)):
|
||||
mem = members[i]
|
||||
if skip_right:
|
||||
result += f"a.{mem} {op} b"
|
||||
else:
|
||||
result += f"a.{mem} {op} b.{mem}"
|
||||
|
||||
if i != len(members) - 1:
|
||||
result += ", "
|
||||
return result
|
||||
|
||||
|
||||
print(f"fn_inline {type}_t {type}_{opname}({type}_t a, {type}_t b) {{ return ({type}_t){{ {op_str(members, op)} }}; }}")
|
||||
print(f"fn_inline {type}_t {type}_{opname}s({type}_t a, {basic_type} b) {{ return ({type}_t){{ {op_str(members, op, skip_right = True)} }}; }}")
|
||||
|
||||
for basic_type in basic_types:
|
||||
for i in range(len(rect_types)):
|
||||
vec_type = vec_types[i]
|
||||
rect_type = rect_types[i]
|
||||
|
||||
vec_name = vec_type[name_idx] + basic_type
|
||||
vec_members = vec_type[member_idx]
|
||||
rect_name = rect_type[name_idx] + basic_type
|
||||
rect_members = rect_type[member_idx]
|
||||
|
||||
print(f"fn_inline {rect_name}_t {rect_name}_min_dim({vec_name}_t pos, {vec_name}_t size) {{ return ({rect_name}_t){{ pos, {vec_name}_add(pos, size) }}; }}")
|
||||
print(f"fn_inline {rect_name}_t {rect_name}_center_half_dim({vec_name}_t center, {vec_name}_t halfdim) {{ return ({rect_name}_t){{ {vec_name}_sub(center, halfdim), {vec_name}_add(center, halfdim) }}; }}")
|
||||
|
||||
for basic_type in basic_types:
|
||||
s = """
|
||||
fn r2f64_t r2f64_cut_left(r2f64_t *r, f64 value) {
|
||||
f64 minx = r->min.x;
|
||||
r->min.x = MIN(r->min.x + value, r->max.x);
|
||||
r2f64_t result = { .min = {.x = minx, .y = r->min.y}, .max = {.x = r->min.x, .y =r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_cut_right(r2f64_t *r, f64 value) {
|
||||
f64 maxx = r->max.x;
|
||||
r->max.x = MAX(r->min.x, r->max.x - value);
|
||||
r2f64_t result = { .min = {.x = r->max.x, .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_cut_top(r2f64_t *r, f64 value) { /* Y is down */
|
||||
f64 miny = r->min.y;
|
||||
r->min.y = MIN(r->max.y, r->min.y + value);
|
||||
r2f64_t result = { .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = r->min.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_cut_bottom(r2f64_t *r, f64 value) { /* Y is down */
|
||||
f64 maxy = r->max.y;
|
||||
r->max.y = MAX(r->min.y, r->max.y - value);
|
||||
r2f64_t result = { .min = {.x = r->min.x, .y = r->max.y}, .max = {.x = r->max.x, .y = maxy} };
|
||||
return result;
|
||||
}
|
||||
|
||||
fn r2f64_t r2f64_cut_left_no_squash(r2f64_t *r, f64 value) {
|
||||
f64 minx = r->min.x;
|
||||
r->min.x = r->min.x + value;
|
||||
r2f64_t result = { .min = {.x = minx, .y = r->min.y}, .max = {.x = r->min.x, .y =r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_cut_right_no_squash(r2f64_t *r, f64 value) {
|
||||
f64 maxx = r->max.x;
|
||||
r->max.x = r->max.x - value;
|
||||
r2f64_t result = { .min = {.x = r->max.x, .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_cut_top_no_squash(r2f64_t *r, f64 value) { /* Y is down */
|
||||
f64 miny = r->min.y;
|
||||
r->min.y = r->min.y + value;
|
||||
r2f64_t result = { .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = r->min.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_cut_bottom_no_squash(r2f64_t *r, f64 value) { /* Y is down */
|
||||
f64 maxy = r->max.y;
|
||||
r->max.y = r->max.y - value;
|
||||
r2f64_t result = { .min = {.x = r->min.x, .y = r->max.y}, .max = {.x = r->max.x, .y = maxy} };
|
||||
return result;
|
||||
}
|
||||
|
||||
fn r2f64_t r2f64_add_left(r2f64_t *r, f64 value) {
|
||||
r2f64_t result = { .min = {r->min.x - value, r->min.y}, .max = {r->min.x, r->max.y} };
|
||||
r->min.x -= value;
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_add_right(r2f64_t *r, f64 value) {
|
||||
r2f64_t result = { .min = {r->max.x, r->min.y}, .max = {r->max.x + value, r->max.y} };
|
||||
r->max.x += value;
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_add_top(r2f64_t *r, f64 value) {
|
||||
r2f64_t result = { .min = {r->min.x, r->min.y - value}, .max = {r->max.x, r->min.y} };
|
||||
r->min.y -= value;
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_add_bottom(r2f64_t *r, f64 value) {
|
||||
r2f64_t result = { .min = {r->min.x, r->max.y}, .max = {r->max.x, r->max.y + value} };
|
||||
r->max.y += value;
|
||||
return result;
|
||||
}
|
||||
|
||||
// get past left
|
||||
fn r2f64_t r2f64_getp_left(const r2f64_t *rect, f64 value) {
|
||||
r2f64_t result = r2f64(rect->min.x - value, rect->min.y, rect->min.x, rect->max.y);
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_getp_right(const r2f64_t *rect, f64 value) {
|
||||
r2f64_t result = r2f64(rect->max.x, rect->min.y, rect->max.x + value, rect->max.y);
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_getp_bottom(const r2f64_t *rect, f64 value) {
|
||||
r2f64_t result = r2f64(rect->min.x, rect->max.y, rect->max.x, rect->max.y + value);
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_getp_top(const r2f64_t *rect, f64 value) {
|
||||
r2f64_t result = r2f64(rect->min.x, rect->min.y - value, rect->max.x, rect->min.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn r2f64_t r2f64_get_left(const r2f64_t *r, f64 value) {
|
||||
f64 minx = r->min.x;
|
||||
r2f64_t result = (r2f64_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = MIN(r->min.x + value, r->max.x), .y =r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_get_right(const r2f64_t *r, f64 value) {
|
||||
f64 maxx = r->max.x;
|
||||
r2f64_t result = (r2f64_t){ .min = {.x = MAX(r->min.x, r->max.x - value), .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} };
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_get_top(const r2f64_t *r, f64 value) { /* Y is down */
|
||||
f64 miny = r->min.y;
|
||||
r2f64_t result = (r2f64_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = MIN(r->max.y, r->min.y + value)} };
|
||||
return result;
|
||||
}
|
||||
fn r2f64_t r2f64_get_bottom(const r2f64_t *r, f64 value) { /* Y is down */
|
||||
f64 maxy = r->max.y;
|
||||
r2f64_t result = (r2f64_t){ .min = {.x = r->min.x, .y = MAX(r->min.y, r->max.y - value)}, .max = {.x = r->max.x, .y = maxy} };
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
fn_inline r2f64_t r2f64_expand(r2f64_t rect, v2f64_t value) { return (r2f64_t){ v2f64_sub(rect.min, value), v2f64_add(rect.max, value) }; }
|
||||
fn_inline r2f64_t r2f64_shrink(r2f64_t rect, v2f64_t value) { return (r2f64_t){ v2f64_add(rect.min, value), v2f64_sub(rect.max, value) }; }
|
||||
fn_inline r2f64_t r2f64_shrinks(r2f64_t rect, f64 value) { return (r2f64_t){ v2f64_adds(rect.min, value), v2f64_subs(rect.max, value) }; }
|
||||
fn_inline v2f64_t r2f64_size(r2f64_t r) { return (v2f64_t){r.max.x - r.min.x, r.max.y - r.min.y}; }
|
||||
fn_inline v2f64_t r2f64_get_mid(r2f64_t r) { return v2f64_add(r.min, v2f64_divs(r2f64_size(r), 2)); }
|
||||
fn_inline b32 r2f64_contains(r2f64_t rec, v2f64_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); }
|
||||
fn_inline r2f64_t r2f64_intersect(r2f64_t a, r2f64_t b) { return (r2f64_t){ .min.x = MAX(a.min.x, b.min.x), .min.y = MAX(a.min.y, b.min.y), .max.x = MIN(a.max.x, b.max.x), .max.y = MIN(a.max.y, b.max.y) }; }
|
||||
fn_inline r2f64_t r2f64_union(r2f64_t a, r2f64_t b) { return (r2f64_t){ .min.x = MIN(a.min.x, b.min.x), .min.y = MIN(a.min.y, b.min.y), .max.x = MAX(a.max.x, b.max.x), .max.y = MAX(a.max.y, b.max.y) }; }
|
||||
|
||||
fn_inline f64 r1f64_size(r1f64_t a) { return a.max - a.min; }
|
||||
fn_inline r1f64_t r1f64(f64 min, f64 max) { return (r1f64_t){min,max}; }
|
||||
fn_inline r1f64_t r1f64_auto(f64 a, f64 b) { return (r1f64_t){MIN(a,b),MAX(a,b)}; }
|
||||
fn_inline r1f64_t r1f64s(f64 a) { return (r1f64_t){a,a}; }
|
||||
fn_inline r1f64_t r1f64_clamp(r1f64_t a, f64 min, f64 max) { return (r1f64_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; }
|
||||
fn_inline b32 r1f64_contains(r1f64_t rec, f64 point) { return (point >= rec.min) && (point < rec.max) && (point >= rec.min) && (point < rec.max); }
|
||||
fn_inline b32 r1f64_are_equal(r1f64_t a, r1f64_t b) { return a.min == b.min && a.max == b.max; }
|
||||
gb_read_only r1f64_t r1f64_null;
|
||||
|
||||
fn b32 r1f64_overlap(r1f64_t a, r1f64_t b) {
|
||||
b32 r1 = (a.max >= b.min && a.max <= b.max) || (a.min >= b.min && a.min <= b.max);
|
||||
b32 r2 = (b.max >= a.min && b.max <= a.max) || (b.min >= a.min && b.min <= a.max);
|
||||
return r1 || r2;
|
||||
}
|
||||
|
||||
fn v2f64_t v2f64_clamps(v2f64_t v, f64 min, f64 max) {
|
||||
v2f64_t result = { CLAMP(v.x, min, max), CLAMP(v.y, min, max) };
|
||||
return result;
|
||||
}
|
||||
fn v2f64_t v2f64_clamp(v2f64_t v, v2f64_t min, v2f64_t max) {
|
||||
v2f64_t result = { CLAMP(v.x, min.x, max.x), CLAMP(v.y, min.y, max.y) };
|
||||
return result;
|
||||
}
|
||||
|
||||
fn r2f64_t r2f64_fix(r2f64_t n) {
|
||||
r2f64_t result = {
|
||||
.min = {MIN(n.min.x, n.max.x), MIN(n.min.y, n.max.y)},
|
||||
.max = {MAX(n.min.x, n.max.x), MAX(n.min.y, n.max.y)},
|
||||
};
|
||||
return result;
|
||||
}
|
||||
""".strip()
|
||||
s = s.replace("f64", basic_type)
|
||||
print(s)
|
||||
|
||||
for at in basic_types:
|
||||
for bt in basic_types:
|
||||
if at == bt: continue
|
||||
for st in struct_types:
|
||||
atype = f"{st[name_idx]}{at}"
|
||||
btype = f"{st[name_idx]}{bt}"
|
||||
members = st[member_idx]
|
||||
|
||||
def cast_members(members, type):
|
||||
result = ""
|
||||
for i in range(len(members)):
|
||||
result += f"({type})v.{members[i]}"
|
||||
if i != len(members) - 1:
|
||||
result += ", "
|
||||
return result
|
||||
|
||||
print(f"fn_inline {btype}_t {atype}_to_{btype}({atype}_t v) {{ return ({btype}_t){{ {cast_members(members, bt)} }}; }}")
|
||||
|
||||
|
||||
#######
|
||||
# generate r2f64_sub_v2f64 for +-*/ and types
|
||||
for bt in basic_types:
|
||||
for sym in symbols:
|
||||
word_op = sym[1]
|
||||
s = """
|
||||
fn_inline r2f64_t r2f64_sub_v2f64(r2f64_t a, v2f64_t b) { return (r2f64_t){ v2f64_sub(a.min, b), v2f64_sub(a.max, b) }; }
|
||||
""".strip()
|
||||
s = s.replace("f64", bt)
|
||||
s = s.replace("sub", word_op)
|
||||
print(s)
|
||||
|
||||
|
||||
for bt in ["f32", "f64"]:
|
||||
s = """
|
||||
fn f64 f64_safe_ratio(f64 a, f64 b, f64 if0) {
|
||||
f64 result = if0;
|
||||
if (b != 0.0) {
|
||||
result = a / b;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn_inline f64 f64_safe_ratio0(f64 a, f64 b) { return f64_safe_ratio(a, b, 0); }
|
||||
fn_inline f64 f64_safe_ratio1(f64 a, f64 b) { return f64_safe_ratio(a, b, 0); }
|
||||
""".strip()
|
||||
s = s.replace("f64", bt)
|
||||
print(s)
|
||||
@@ -1,613 +0,0 @@
|
||||
#include "core_platform.h"
|
||||
#include "core_ctx.h"
|
||||
#include "stb_sprintf.h"
|
||||
|
||||
#if PLATFORM_WASM && PLATFORM_EMSCRIPTEN == 0
|
||||
#define gb_wasm_export __attribute__((visibility("default")))
|
||||
#define fn_export __attribute__((visibility("default")))
|
||||
#define fn_wasm_import
|
||||
|
||||
fn_wasm_import void wasm_alert(isize ptr, i32 len);
|
||||
fn_wasm_import f64 wasm_parse_float(isize str, i32 len);
|
||||
fn_wasm_import void wasm_write_to_console(isize str, i32 len);
|
||||
fn_wasm_import void wasm_trap(void);
|
||||
|
||||
extern char __heap_base;
|
||||
|
||||
fn void *os_vmem_reserve(usize size) {
|
||||
unused(size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_commit(void *p, usize size) {
|
||||
unused(p);
|
||||
unused(size);
|
||||
return false;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_release(void *p) {
|
||||
unused(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_decommit(void *p, usize size) {
|
||||
unused(p);
|
||||
unused(size);
|
||||
return true;
|
||||
}
|
||||
|
||||
fn void os_error_box(char *str) {
|
||||
wasm_alert((isize)str, str_len(str));
|
||||
}
|
||||
|
||||
fn void os_console_log(char *str) {
|
||||
s8_t string = s8_from_char(str);
|
||||
if (s8_ends_with(string, s8("\n"))) {
|
||||
string = s8_chop(string, 1);
|
||||
}
|
||||
wasm_write_to_console((isize)string.str, (i32)string.len);
|
||||
}
|
||||
|
||||
fn f64 os_parse_float(char *str) {
|
||||
return wasm_parse_float((isize)str, str_len((char *)str));
|
||||
}
|
||||
|
||||
fn void os_core_small_init(ma_arena_t *arena) {
|
||||
tcx = ma_push_type(arena, thread_ctx_t);
|
||||
tcx->log.break_on_error = true;
|
||||
tcx->log.break_on_fatal = true;
|
||||
tcx->log.break_on_warning = true;
|
||||
tcx->log.print_file_and_line = true;
|
||||
tcx->log.log_proc = default_log_proc;
|
||||
}
|
||||
|
||||
fn void os_core_init(void) {
|
||||
ma_arena_t perm = {0};
|
||||
isize page_size = kib(64);
|
||||
isize page_count = __builtin_wasm_memory_size(0);
|
||||
u8 *memory = (u8 *)&__heap_base;
|
||||
usize memory_size = page_count * (page_size) - (isize)memory;
|
||||
perm.data = memory;
|
||||
perm.commit = perm.reserve = memory_size;
|
||||
os_core_small_init(&perm);
|
||||
tcx->perm = perm;
|
||||
ma_push_arena_ex(&tcx->perm, &tcx->temp, mib(64));
|
||||
ma_push_arena_ex(&tcx->perm, &tcx->scratch[0], mib(2));
|
||||
ma_push_arena_ex(&tcx->perm, &tcx->scratch[1], kib(256));
|
||||
ma_push_arena_ex(&tcx->perm, &tcx->scratch[2], kib(64));
|
||||
debugf("memory size = %lld", memory_size);
|
||||
}
|
||||
#elif PLATFORM_WINDOWS
|
||||
#pragma comment(lib, "user32.lib")
|
||||
#pragma comment(lib, "DbgHelp.lib")
|
||||
#pragma comment(lib, "Shell32.lib")
|
||||
#pragma comment(lib, "Ole32.lib")
|
||||
#define NOMINMAX
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <DbgHelp.h>
|
||||
#include <TlHelp32.h>
|
||||
#include <Shlobj.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
|
||||
typedef struct b_stacktrace_tag* b_stacktrace_handle;
|
||||
|
||||
/*
|
||||
Returns a stack-trace handle from the point of view of the caller which
|
||||
can be expanded to a string via b_stacktrace_to_string.
|
||||
The handle is allocated with `malloc` and needs to be freed with `free`
|
||||
*/
|
||||
fn b_stacktrace_handle b_stacktrace_get();
|
||||
|
||||
/*
|
||||
Converts a stack-trace handle to a human-readable string.
|
||||
The string is allocated with `malloc` and needs to be freed with `free`
|
||||
*/
|
||||
fn char* b_stacktrace_to_string(b_stacktrace_handle stacktrace);
|
||||
|
||||
/*
|
||||
Returns a human-readable stack-trace string from the point of view of the
|
||||
caller.
|
||||
The string is allocated with `malloc` and needs to be freed with `free`
|
||||
*/
|
||||
fn char* b_stacktrace_get_string(void);
|
||||
|
||||
/* version */
|
||||
|
||||
typedef struct print_buf {
|
||||
char* buf;
|
||||
int pos;
|
||||
int size;
|
||||
} print_buf;
|
||||
|
||||
static print_buf buf_init(void) {
|
||||
print_buf ret = {
|
||||
(char*) malloc(1024),
|
||||
0,
|
||||
1024
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void buf_printf(print_buf* b, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
const int len = stbsp_vsnprintf(NULL, 0, fmt, args) + 1;
|
||||
va_end(args);
|
||||
|
||||
const int new_end = b->pos + len;
|
||||
|
||||
if (new_end > b->size) {
|
||||
while (new_end > b->size) b->size *= 2;
|
||||
b->buf = (char*)realloc(b->buf, b->size);
|
||||
}
|
||||
|
||||
va_start(args, fmt);
|
||||
b->pos += stbsp_vsnprintf(b->buf + b->pos, len, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
char* b_stacktrace_get_string(void) {
|
||||
b_stacktrace_handle h = b_stacktrace_get();
|
||||
char* ret = b_stacktrace_to_string(h);
|
||||
free(h);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define B_STACKTRACE_MAX_DEPTH 1024
|
||||
#define B_STACKTRACE_ERROR_FLAG ((DWORD64)1 << 63)
|
||||
|
||||
typedef struct b_stacktrace_entry {
|
||||
DWORD64 AddrPC_Offset;
|
||||
DWORD64 AddrReturn_Offset;
|
||||
} b_stacktrace_entry;
|
||||
|
||||
static int SymInitialize_called = 0;
|
||||
|
||||
b_stacktrace_handle b_stacktrace_get(void) {
|
||||
HANDLE process = GetCurrentProcess();
|
||||
HANDLE thread = GetCurrentThread();
|
||||
CONTEXT context;
|
||||
STACKFRAME64 frame; /* in/out stackframe */
|
||||
DWORD imageType;
|
||||
b_stacktrace_entry* ret = (b_stacktrace_entry*)malloc(B_STACKTRACE_MAX_DEPTH * sizeof(b_stacktrace_entry));
|
||||
int i = 0;
|
||||
|
||||
if (!SymInitialize_called) {
|
||||
SymInitialize(process, NULL, TRUE);
|
||||
SymInitialize_called = 1;
|
||||
}
|
||||
|
||||
RtlCaptureContext(&context);
|
||||
|
||||
memset(&frame, 0, sizeof(frame));
|
||||
#ifdef _M_IX86
|
||||
imageType = IMAGE_FILE_MACHINE_I386;
|
||||
frame.AddrPC.Offset = context.Eip;
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Offset = context.Ebp;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Offset = context.Esp;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_X64
|
||||
imageType = IMAGE_FILE_MACHINE_AMD64;
|
||||
frame.AddrPC.Offset = context.Rip;
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Offset = context.Rsp;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Offset = context.Rsp;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_IA64
|
||||
imageType = IMAGE_FILE_MACHINE_IA64;
|
||||
frame.AddrPC.Offset = context.StIIP;
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Offset = context.IntSp;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
frame.AddrBStore.Offset = context.RsBSP;
|
||||
frame.AddrBStore.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Offset = context.IntSp;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
#else
|
||||
#error "Platform not supported!"
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
b_stacktrace_entry* cur = ret + i++;
|
||||
if (i == B_STACKTRACE_MAX_DEPTH) {
|
||||
cur->AddrPC_Offset = 0;
|
||||
cur->AddrReturn_Offset = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!StackWalk64(imageType, process, thread, &frame, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) {
|
||||
cur->AddrPC_Offset = frame.AddrPC.Offset;
|
||||
cur->AddrReturn_Offset = B_STACKTRACE_ERROR_FLAG; /* mark error */
|
||||
cur->AddrReturn_Offset |= GetLastError();
|
||||
break;
|
||||
}
|
||||
|
||||
cur->AddrPC_Offset = frame.AddrPC.Offset;
|
||||
cur->AddrReturn_Offset = frame.AddrReturn.Offset;
|
||||
|
||||
if (frame.AddrReturn.Offset == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (b_stacktrace_handle)(ret);
|
||||
}
|
||||
|
||||
char* b_stacktrace_to_string(b_stacktrace_handle h) {
|
||||
const b_stacktrace_entry* entries = (b_stacktrace_entry*)h;
|
||||
int i = 0;
|
||||
HANDLE process = GetCurrentProcess();
|
||||
print_buf out = buf_init();
|
||||
IMAGEHLP_SYMBOL64* symbol = (IMAGEHLP_SYMBOL64*)malloc(sizeof(IMAGEHLP_SYMBOL64) + 1024);
|
||||
symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
|
||||
symbol->MaxNameLength = 1024;
|
||||
|
||||
while (1) {
|
||||
IMAGEHLP_LINE64 lineData;
|
||||
DWORD lineOffset = 0;
|
||||
DWORD64 symOffset = 0;
|
||||
const b_stacktrace_entry* cur = entries + i++;
|
||||
|
||||
if (cur->AddrReturn_Offset & B_STACKTRACE_ERROR_FLAG) {
|
||||
DWORD error = cur->AddrReturn_Offset & 0xFFFFFFFF;
|
||||
buf_printf(&out, "StackWalk64 error: %d @ %p\n", error, cur->AddrPC_Offset);
|
||||
break;
|
||||
}
|
||||
|
||||
if (cur->AddrPC_Offset == cur->AddrReturn_Offset) {
|
||||
buf_printf(&out, "Stack overflow @ %p\n", cur->AddrPC_Offset);
|
||||
break;
|
||||
}
|
||||
|
||||
SymGetLineFromAddr64(process, cur->AddrPC_Offset, &lineOffset, &lineData);
|
||||
buf_printf(&out, "%s(%d): ", lineData.FileName, lineData.LineNumber);
|
||||
|
||||
if (SymGetSymFromAddr64(process, cur->AddrPC_Offset, &symOffset, symbol)) {
|
||||
buf_printf(&out, "%s\n", symbol->Name);
|
||||
}
|
||||
else {
|
||||
buf_printf(&out, " Unkown symbol @ %p\n", cur->AddrPC_Offset);
|
||||
}
|
||||
|
||||
if (cur->AddrReturn_Offset == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(symbol);
|
||||
return out.buf;
|
||||
}
|
||||
|
||||
fn void *os_vmem_reserve(usize size) {
|
||||
void *result = VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_commit(void *p, usize size) {
|
||||
void *result = VirtualAlloc(p, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
return result ? true : false;
|
||||
}
|
||||
fn b32 os_vmem_release(void *p) {
|
||||
BOOL result = VirtualFree(p, 0, MEM_RELEASE);
|
||||
return result ? true : false;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_decommit(void *p, usize size) {
|
||||
BOOL result = VirtualFree(p, size, MEM_DECOMMIT);
|
||||
return result ? true : false;
|
||||
}
|
||||
|
||||
fn void os_error_box(char *str) {
|
||||
OutputDebugStringA(str);
|
||||
fprintf(stderr, "%s", str);
|
||||
fflush(stderr);
|
||||
MessageBoxA(NULL, str, "fatal error", MB_OK);
|
||||
}
|
||||
|
||||
fn void os_console_log(char *str) {
|
||||
OutputDebugStringA(str);
|
||||
fputs(str, stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
fn f64 os_parse_float(char *str) {
|
||||
return strtod(str, NULL);
|
||||
}
|
||||
|
||||
fn void os_win32_crash_handler(int signal) {
|
||||
printf("signal: %d\n", signal);
|
||||
puts(b_stacktrace_get_string());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fn void os_win32_register_crash_handler() {
|
||||
signal(SIGSEGV, os_win32_crash_handler);
|
||||
signal(SIGABRT, os_win32_crash_handler);
|
||||
}
|
||||
|
||||
fn void os_core_small_init(ma_arena_t *arena) {
|
||||
tcx = ma_push_type(arena, thread_ctx_t);
|
||||
tcx->log.break_on_error = true;
|
||||
tcx->log.break_on_fatal = true;
|
||||
tcx->log.break_on_warning = true;
|
||||
tcx->log.print_file_and_line = true;
|
||||
tcx->log.log_proc = default_log_proc;
|
||||
os_win32_register_crash_handler();
|
||||
}
|
||||
|
||||
fn void os_core_init(void) {
|
||||
ma_arena_t perm = {0};
|
||||
os_core_small_init(&perm);
|
||||
tcx->perm = perm;
|
||||
}
|
||||
#elif PLATFORM_POSIX
|
||||
#if defined(__linux__) && !defined(_GNU_SOURCE)
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
|
||||
#if !PLATFORM_EMSCRIPTEN
|
||||
#include <execinfo.h>
|
||||
#include <ucontext.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sys/mman.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
fn void *os_vmem_reserve(usize size) {
|
||||
void *result = mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||
return result == MAP_FAILED ? NULL : result;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_commit(void *ptr, usize size) {
|
||||
mprotect(ptr, size, PROT_READ|PROT_WRITE);
|
||||
return true;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_release(void *ptr) {
|
||||
int result = munmap(ptr, 0);
|
||||
return result == 0 ? true : false;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_decommit(void *ptr, usize size) {
|
||||
madvise(ptr, size, MADV_DONTNEED);
|
||||
mprotect(ptr, size, PROT_NONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
fn void os_error_box(char *str) {
|
||||
fprintf(stderr, "%s", str);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
fn void os_console_log(char *str) {
|
||||
fputs(str, stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
fn f64 os_parse_float(char *str) {
|
||||
return strtod(str, NULL);
|
||||
}
|
||||
|
||||
#if !PLATFORM_EMSCRIPTEN
|
||||
|
||||
typedef struct b_stacktrace_tag* b_stacktrace_handle;
|
||||
b_stacktrace_handle b_stacktrace_get();
|
||||
int b_stacktrace_depth(b_stacktrace_handle stacktrace);
|
||||
char* b_stacktrace_to_string(b_stacktrace_handle stacktrace);
|
||||
char* malloc_backtrace(void);
|
||||
|
||||
typedef struct print_buf {
|
||||
char* buf;
|
||||
int pos;
|
||||
int size;
|
||||
} print_buf;
|
||||
|
||||
static print_buf buf_init(void) {
|
||||
print_buf ret = {
|
||||
(char*) malloc(1024),
|
||||
0,
|
||||
1024
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void buf_printf(print_buf* b, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
const int len = vsnprintf(NULL, 0, fmt, args) + 1;
|
||||
va_end(args);
|
||||
|
||||
const int new_end = b->pos + len;
|
||||
|
||||
if (new_end > b->size) {
|
||||
while (new_end > b->size) b->size *= 2;
|
||||
b->buf = (char*)realloc(b->buf, b->size);
|
||||
}
|
||||
|
||||
va_start(args, fmt);
|
||||
b->pos += vsnprintf(b->buf + b->pos, len, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
char* malloc_backtrace(void) {
|
||||
b_stacktrace_handle h = b_stacktrace_get();
|
||||
char* ret = b_stacktrace_to_string(h);
|
||||
free(h);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define B_STACKTRACE_MAX_DEPTH 1024
|
||||
|
||||
typedef struct b_stacktrace {
|
||||
void* trace[B_STACKTRACE_MAX_DEPTH];
|
||||
int trace_size;
|
||||
} b_stacktrace;
|
||||
|
||||
b_stacktrace_handle b_stacktrace_get(void) {
|
||||
b_stacktrace* ret = (b_stacktrace*)malloc(sizeof(b_stacktrace));
|
||||
ret->trace_size = backtrace(ret->trace, B_STACKTRACE_MAX_DEPTH);
|
||||
return (b_stacktrace_handle)(ret);
|
||||
}
|
||||
|
||||
int b_stacktrace_depth(b_stacktrace_handle h) {
|
||||
const b_stacktrace* stacktrace = (b_stacktrace*)h;
|
||||
return stacktrace->trace_size;
|
||||
}
|
||||
|
||||
char* b_stacktrace_to_string(b_stacktrace_handle h) {
|
||||
const b_stacktrace* stacktrace = (b_stacktrace*)h;
|
||||
char** messages = backtrace_symbols(stacktrace->trace, stacktrace->trace_size);
|
||||
print_buf out = buf_init();
|
||||
int i;
|
||||
|
||||
int skip_count = 5;
|
||||
for (i = 0; i < stacktrace->trace_size; ++i) {
|
||||
void* tracei = stacktrace->trace[i];
|
||||
char* msg = messages[i];
|
||||
|
||||
if (i < skip_count) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* calculate load offset */
|
||||
Dl_info info;
|
||||
dladdr(tracei, &info);
|
||||
if (info.dli_fbase == (void*)0x400000) {
|
||||
/* address from executable, so don't offset */
|
||||
info.dli_fbase = NULL;
|
||||
}
|
||||
|
||||
while (*msg && *msg != '(') ++msg;
|
||||
*msg = 0;
|
||||
|
||||
{
|
||||
char cmd[1024];
|
||||
char line[2048];
|
||||
|
||||
FILE* fp;
|
||||
snprintf(cmd, 1024, "addr2line -e %s -f -C -p %p 2>/dev/null", messages[i], (void*)((char*)tracei - (char*)info.dli_fbase));
|
||||
|
||||
fp = popen(cmd, "r");
|
||||
if (!fp) {
|
||||
buf_printf(&out, "Failed to generate trace further...\n");
|
||||
break;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
buf_printf(&out, " ");
|
||||
if (strstr(line, "?? ")) {
|
||||
/* just output address if nothing can be found */
|
||||
buf_printf(&out, "%p\n", tracei);
|
||||
}
|
||||
else {
|
||||
int len = strlen(line);
|
||||
char *at = strstr(line, " at ");
|
||||
if (at) {
|
||||
char *function = line;
|
||||
int function_len = at - line;
|
||||
char *file = at + 4;
|
||||
int file_len = strlen(file) - 1;
|
||||
buf_printf(&out, " %.*s %.*s (%s)\n", file_len, file, function_len, function, messages[i]);
|
||||
} else {
|
||||
buf_printf(&out, " %.*s (%s)\n", len - 1, line, messages[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
free(messages);
|
||||
return out.buf;
|
||||
}
|
||||
|
||||
void print_backtrace(void) {
|
||||
char *backtrace = malloc_backtrace();
|
||||
puts(backtrace);
|
||||
free(backtrace);
|
||||
}
|
||||
|
||||
const char *signal_to_string(int signal) {
|
||||
switch (signal) {
|
||||
case SIGINT: return "SIGINT";
|
||||
case SIGILL: return "SIGILL";
|
||||
case SIGABRT: return "SIGABRT";
|
||||
case SIGFPE: return "SIGFPE";
|
||||
case SIGSEGV: return "SIGSEGV";
|
||||
case SIGTERM: return "SIGTERM";
|
||||
case SIGHUP: return "SIGALRM";
|
||||
case SIGQUIT: return "SIGQUIT";
|
||||
case SIGTRAP: return "SIGTRAP";
|
||||
case SIGKILL: return "SIGKILL";
|
||||
case SIGPIPE: return "SIGPIPE";
|
||||
case SIGALRM: return "SIGHUP";
|
||||
case SIGPOLL: return "SIGPOLL";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
void backtrace_handle_signal(int signal, siginfo_t* info, void* context) {
|
||||
(void)context; // unused
|
||||
printf("Stack trace (%s/%d):", signal_to_string(signal), signal);
|
||||
if (signal == SIGSEGV) {
|
||||
printf(" at addr: %p", info->si_addr);
|
||||
}
|
||||
puts("");
|
||||
print_backtrace();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void setup_backtrace() {
|
||||
struct sigaction sa;
|
||||
sa.sa_sigaction = backtrace_handle_signal;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||
|
||||
sigaction(SIGSEGV, &sa, NULL);
|
||||
sigaction(SIGABRT, &sa, NULL);
|
||||
sigaction(SIGFPE, &sa, NULL);
|
||||
sigaction(SIGBUS, &sa, NULL);
|
||||
sigaction(SIGILL, &sa, NULL);
|
||||
sigaction(SIGTRAP, &sa, NULL);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
fn void os_core_small_init(ma_arena_t *arena) {
|
||||
tcx = ma_push_type(arena, thread_ctx_t);
|
||||
tcx->log.break_on_error = true;
|
||||
tcx->log.break_on_fatal = true;
|
||||
tcx->log.break_on_warning = true;
|
||||
tcx->log.print_file_and_line = true;
|
||||
tcx->log.log_proc = default_log_proc;
|
||||
#if !PLATFORM_EMSCRIPTEN
|
||||
setup_backtrace();
|
||||
#endif
|
||||
}
|
||||
|
||||
fn void os_core_init(void) {
|
||||
ma_arena_t perm = {0};
|
||||
os_core_small_init(&perm);
|
||||
tcx->perm = perm;
|
||||
}
|
||||
#endif
|
||||
@@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
#include "core_basic.h"
|
||||
#include "core_arena.h"
|
||||
|
||||
fn void os_error_box(char *str);
|
||||
fn void os_console_log(char *str);
|
||||
|
||||
fn void *os_vmem_reserve(usize size);
|
||||
fn b32 os_vmem_commit(void *p, usize size);
|
||||
fn b32 os_vmem_release(void *p);
|
||||
fn b32 os_vmem_decommit(void *p, usize size);
|
||||
|
||||
fn void os_core_init(void);
|
||||
fn void os_core_small_init(ma_arena_t *arena);
|
||||
fn f64 os_parse_float(char *str);
|
||||
|
||||
|
||||
#if PLATFORM_DEBUG_ASSERT
|
||||
#define assert_expr(x) (!(x) && (os_error_box(FILE_AND_LINE ": assertion failed: " #x "\n"), debug_break()))
|
||||
#define assert(x) do { assert_expr(x); } while(0)
|
||||
#define assert_ex(x, msg) (!(x) && (os_error_box(FILE_AND_LINE ": assertion failed: " #x " :: " #msg "\n"), debug_break()))
|
||||
#else
|
||||
#define assert_expr(x) (void)(x)
|
||||
#define assert(x) do { (void)(x); } while(0)
|
||||
#define assert_ex(x, msg) do { (void)(x); } while(0)
|
||||
#endif
|
||||
#define not_implemented assert(!"not implemented!")
|
||||
#define invalid_codepath assert(!"invalid code path!")
|
||||
#define default_is_invalid default: assert(!"invalid default case")
|
||||
#define else_is_invalid else { assert(!"else was not expected to be executed!"); }
|
||||
@@ -1,557 +0,0 @@
|
||||
#include "core_string.h"
|
||||
#include "core_intrin.h"
|
||||
#include "core_platform.h"
|
||||
#include "stb_sprintf.h"
|
||||
|
||||
fn i32 str_len(char *str) {
|
||||
i32 i = 0;
|
||||
while (str[i]) i += 1;
|
||||
return i;
|
||||
}
|
||||
|
||||
fn b32 str_eq(char *a, char *b) {
|
||||
return s8_are_equal(s8_from_char(a), s8_from_char(b));
|
||||
}
|
||||
|
||||
fn char char_to_lower_case(char a) {
|
||||
if (a >= 'A' && a <= 'Z') a += 32;
|
||||
return a;
|
||||
}
|
||||
|
||||
fn char char_to_upper_case(char a) {
|
||||
if (a >= 'a' && a <= 'z') a -= 32;
|
||||
return a;
|
||||
}
|
||||
|
||||
fn b32 char_is_whitespace(char w) {
|
||||
b32 result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r';
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 char_is_alphabetic(char a) {
|
||||
b32 result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 char_is_ident(char a) {
|
||||
b32 result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || a == '_';
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 char_is_digit(char a) {
|
||||
b32 result = a >= '0' && a <= '9';
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 char_is_alphanumeric(char a) {
|
||||
b32 result = char_is_digit(a) || char_is_alphabetic(a);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 char_is_symbol(char w) {
|
||||
b32 result = (w >= '!' && w <= '/') || (w >= ':' && w <= '@') || (w >= '[' && w <= '`') || (w >= '{' && w <= '~');
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 char_is_non_word(char w) {
|
||||
b32 result = w != '_' && (char_is_symbol(w) || char_is_whitespace(w));
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 char_is_word(char w) {
|
||||
b32 result = !char_is_non_word(w);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t s8_from_range(char *begin, char *end) {
|
||||
assert(end >= begin);
|
||||
intptr_t size = (intptr_t)end - (intptr_t)begin;
|
||||
s8_t result = {begin, size};
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t s8_from_char(char *string) {
|
||||
s8_t result;
|
||||
result.str = (char *)string;
|
||||
result.len = str_len(string);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t s8_copy(ma_arena_t *ma, s8_t string) {
|
||||
char *copy = (char *)ma_push_size(ma, sizeof(char) * (string.len + 1));
|
||||
memory_copy(copy, string.str, string.len);
|
||||
copy[string.len] = 0;
|
||||
s8_t result = s8_make(copy, string.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t s8_copy_char(ma_arena_t *ma, char *s) {
|
||||
int64_t len = str_len(s);
|
||||
char *copy = (char *)ma_push_size(ma, sizeof(char) * (len + 1));
|
||||
memory_copy(copy, s, len);
|
||||
copy[len] = 0;
|
||||
s8_t result = s8_make(copy, len);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 s8_are_equal_ex(s8_t a, s8_t b, b32 ignore_case) {
|
||||
if (a.len != b.len) return false;
|
||||
for (int64_t i = 0; i < a.len; i++) {
|
||||
char A = a.str[i];
|
||||
char B = b.str[i];
|
||||
if (ignore_case) {
|
||||
A = char_to_lower_case(A);
|
||||
B = char_to_lower_case(B);
|
||||
}
|
||||
if (A != B)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
fn b32 s8_are_equal(s8_t a, s8_t b) {
|
||||
return s8_are_equal_ex(a, b, false);
|
||||
}
|
||||
|
||||
fn s8_t s8_get_postfix(s8_t string, int64_t len) {
|
||||
len = CLAMP_TOP(len, string.len);
|
||||
int64_t remain_len = string.len - len;
|
||||
s8_t result = s8_make(string.str + remain_len, len);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t s8_get_prefix(s8_t string, int64_t len) {
|
||||
len = CLAMP_TOP(len, string.len);
|
||||
s8_t result = s8_make(string.str, len);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t s8_chop(s8_t string, int64_t len) {
|
||||
len = CLAMP_TOP(len, string.len);
|
||||
s8_t result = s8_make(string.str, string.len - len);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t s8_skip(s8_t string, int64_t len) {
|
||||
len = CLAMP_TOP(len, string.len);
|
||||
int64_t remain = string.len - len;
|
||||
s8_t result = s8_make(string.str + len, remain);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 s8_ends_with_ex(s8_t a, s8_t end, b32 ignore_case) {
|
||||
s8_t a_end = s8_get_postfix(a, end.len);
|
||||
b32 result = s8_are_equal_ex(end, a_end, ignore_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 s8_starts_with_ex(s8_t a, s8_t start, b32 ignore_case) {
|
||||
s8_t a_start = s8_get_prefix(a, start.len);
|
||||
b32 result = s8_are_equal_ex(start, a_start, ignore_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 s8_ends_with(s8_t a, s8_t end) {
|
||||
return s8_ends_with_ex(a, end, false);
|
||||
}
|
||||
|
||||
fn b32 s8_starts_with(s8_t a, s8_t start) {
|
||||
return s8_starts_with_ex(a, start, false);
|
||||
}
|
||||
|
||||
fn void s8_normalize_path_unsafe(s8_t s) {
|
||||
for (int64_t i = 0; i < s.len; i++) {
|
||||
if (s.str[i] == '\\')
|
||||
s.str[i] = '/';
|
||||
}
|
||||
}
|
||||
|
||||
fn b32 s8_is_pointer_inside(s8_t string, char *p) {
|
||||
uintptr_t pointer = (uintptr_t)p;
|
||||
uintptr_t start = (uintptr_t)string.str;
|
||||
uintptr_t stop = start + (uintptr_t)string.len;
|
||||
b32 result = pointer >= start && pointer < stop;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t s8_cut_start(s8_t *in, i64 size) {
|
||||
s8_t result = s8_get_prefix(*in, size);
|
||||
*in = s8_skip(*in, result.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t s8_cut_end(s8_t *in, i64 size) {
|
||||
s8_t result = s8_get_postfix(*in, size);
|
||||
*in = s8_chop(*in, result.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if 0
|
||||
s8_t s8_skip_to_p(s8_t string, char *p) {
|
||||
if (s8_is_pointer_inside(string, p)) {
|
||||
s8_t result = s8_make(p, p - string.str);
|
||||
return result;
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
s8_t s8_skip_past(s8_t string, s8_t a) {
|
||||
if (s8_is_pointer_inside(string, a.str)) {
|
||||
s8_t on_p = s8_make(a.str, a.str - string.str);
|
||||
s8_t result = s8_skip(on_p, a.len);
|
||||
return result;
|
||||
}
|
||||
return string;
|
||||
}
|
||||
#endif
|
||||
|
||||
fn s8_t s8_slice(s8_t string, int64_t first_index, int64_t one_past_last_index) {
|
||||
if (one_past_last_index < 0) one_past_last_index = string.len + one_past_last_index + 1;
|
||||
if (first_index < 0) first_index = string.len + first_index;
|
||||
assert(first_index < one_past_last_index);
|
||||
assert(string.len > 0);
|
||||
s8_t result = string;
|
||||
if (string.len > 0) {
|
||||
if (one_past_last_index > first_index) {
|
||||
first_index = CLAMP_TOP(first_index, string.len - 1);
|
||||
one_past_last_index = CLAMP_TOP(one_past_last_index, string.len);
|
||||
result.str += first_index;
|
||||
result.len = one_past_last_index - first_index;
|
||||
}
|
||||
else {
|
||||
result.len = 0;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t s8_trim(s8_t string) {
|
||||
if (string.len == 0)
|
||||
return string;
|
||||
|
||||
int64_t whitespace_begin = 0;
|
||||
for (; whitespace_begin < string.len; whitespace_begin++) {
|
||||
if (!char_is_whitespace(string.str[whitespace_begin])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t whitespace_end = string.len;
|
||||
for (; whitespace_end != whitespace_begin; whitespace_end--) {
|
||||
if (!char_is_whitespace(string.str[whitespace_end - 1])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (whitespace_begin == whitespace_end) {
|
||||
string.len = 0;
|
||||
}
|
||||
else {
|
||||
string = s8_slice(string, whitespace_begin, whitespace_end);
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
fn s8_t s8_trim_end(s8_t string) {
|
||||
int64_t whitespace_end = string.len;
|
||||
for (; whitespace_end != 0; whitespace_end--) {
|
||||
if (!char_is_whitespace(string.str[whitespace_end - 1])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
s8_t result = s8_get_prefix(string, whitespace_end);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 s8_seek(s8_t string, s8_t find, s8_seek_t flags, int64_t *index_out) {
|
||||
b32 ignore_case = flags & s8_seek_ignore_case ? true : false;
|
||||
b32 result = false;
|
||||
if (flags & s8_seek_match_find_last) {
|
||||
for (int64_t i = string.len; i != 0; i--) {
|
||||
int64_t index = i - 1;
|
||||
s8_t substring = s8_slice(string, index, index + find.len);
|
||||
if (s8_are_equal_ex(substring, find, ignore_case)) {
|
||||
if (index_out)
|
||||
*index_out = index;
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int64_t i = 0; i < string.len; i++) {
|
||||
s8_t substring = s8_slice(string, i, i + find.len);
|
||||
if (s8_are_equal_ex(substring, find, ignore_case)) {
|
||||
if (index_out)
|
||||
*index_out = i;
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn int64_t s8_find(s8_t string, s8_t find, s8_seek_t flag) {
|
||||
int64_t result = -1;
|
||||
s8_seek(string, find, flag, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t s8_chop_last_char(s8_t s, s8_t ch) {
|
||||
s8_t result = s;
|
||||
s8_seek(s, ch, s8_seek_match_find_last, &result.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t s8_chop_last_slash(s8_t s) {
|
||||
return s8_chop_last_char(s, s8("/"));
|
||||
}
|
||||
|
||||
fn s8_t s8_chop_last_period(s8_t s) {
|
||||
return s8_chop_last_char(s, s8("."));
|
||||
}
|
||||
|
||||
fn s8_t s8_skip_to_last_char(s8_t s, s8_t ch) {
|
||||
int64_t pos;
|
||||
s8_t result = s;
|
||||
if (s8_seek(s, ch, s8_seek_match_find_last, &pos)) {
|
||||
result = s8_skip(result, pos + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t s8_skip_to_last_slash(s8_t s) {
|
||||
return s8_skip_to_last_char(s, s8("/"));
|
||||
}
|
||||
|
||||
fn s8_t s8_skip_to_last_period(s8_t s) {
|
||||
return s8_skip_to_last_char(s, s8("."));
|
||||
}
|
||||
|
||||
fn s8_t s8_get_name_no_ext(s8_t s) {
|
||||
return s8_skip_to_last_slash(s8_chop_last_period(s));
|
||||
}
|
||||
|
||||
fn s8_t s8_normalize_path(ma_arena_t *ma, s8_t s) {
|
||||
s8_t copy = s8_copy(ma, s);
|
||||
s8_normalize_path_unsafe(copy);
|
||||
return copy;
|
||||
}
|
||||
|
||||
fn s8_t s8_to_lower_case(ma_arena_t *ma, s8_t s) {
|
||||
s8_t copy = s8_copy(ma, s);
|
||||
for (int64_t i = 0; i < copy.len; i++) {
|
||||
copy.str[i] = char_to_lower_case(copy.str[i]);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
fn s8_t s8_to_upper_case(ma_arena_t *ma, s8_t s) {
|
||||
s8_t copy = s8_copy(ma, s);
|
||||
for (int64_t i = 0; i < copy.len; i++) {
|
||||
copy.str[i] = char_to_upper_case(copy.str[i]);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
fn s8_t s8_vfmt(ma_arena_t *ma, const char *str, va_list args1) {
|
||||
va_list args2;
|
||||
va_copy(args2, args1);
|
||||
int64_t len = s8_vsnprintf(0, 0, str, args2);
|
||||
va_end(args2);
|
||||
|
||||
char *result = (char *)ma_push_size(ma, sizeof(char) * (len + 1));
|
||||
s8_vsnprintf(result, (i32)(len + 1), str, args1);
|
||||
s8_t res = s8_make(result, len);
|
||||
return res;
|
||||
}
|
||||
|
||||
fn s8_t s8_printf(ma_arena_t *ma, const char *str, ...) {
|
||||
S8_FMT(ma, str, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn sb8_node_t *sb8_create_node(ma_arena_t *ma, s8_t str) {
|
||||
sb8_node_t *node = ma_push_type(ma, sb8_node_t);
|
||||
node->string = str;
|
||||
return node;
|
||||
}
|
||||
|
||||
fn sb8_node_t *sb8_append(sb8_t *list, s8_t string) {
|
||||
assert(list->arena != NULL);
|
||||
sb8_node_t *node = sb8_create_node(list->arena, string);
|
||||
SLLQ_APPEND(list->first, list->last, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
fn s8_t sb8_printf(sb8_t *sb, const char *str, ...) {
|
||||
S8_FMT(sb->arena, str, result);
|
||||
sb8_append(sb, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn void sb8_indent(sb8_t *sb) {
|
||||
sb8_printf(sb, "\n%.*s", sb->indent*4, " ");
|
||||
}
|
||||
|
||||
fn s8_t sb8_stmtf(sb8_t *sb, const char *str, ...) {
|
||||
S8_FMT(sb->arena, str, result);
|
||||
sb8_indent(sb);
|
||||
sb8_append(sb, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn int64_t sb8_char_size(sb8_t *sb) {
|
||||
int64_t result = 0;
|
||||
for (sb8_node_t *it = sb->first; it; it = it->next) {
|
||||
result += it->len;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t sb8_merge(ma_arena_t *arena, sb8_t *sb) {
|
||||
int64_t size = sb8_char_size(sb) + 1;
|
||||
char *str = ma_push_size(arena, size);
|
||||
s8_t result = {str, 0};
|
||||
for (sb8_node_t *it = sb->first; it; it = it->next) {
|
||||
memory_copy(result.str + result.len, it->str, it->len);
|
||||
result.len += it->len;
|
||||
}
|
||||
result.str[result.len] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn sb8_t s8_split(ma_arena_t *ma, s8_t string, s8_t find, s8_split_t flags) {
|
||||
sb8_t result = (sb8_t){ma};
|
||||
int64_t index = 0;
|
||||
|
||||
s8_seek_t find_flag = flags & s8_split_ignore_case ? s8_seek_ignore_case : s8_seek_none;
|
||||
while (s8_seek(string, find, find_flag, &index)) {
|
||||
s8_t before_match = s8_make(string.str, index);
|
||||
if (flags & s8_split_cleanup) {
|
||||
before_match = s8_trim(before_match);
|
||||
if (before_match.len) {
|
||||
sb8_append(&result, before_match);
|
||||
}
|
||||
} else {
|
||||
sb8_append(&result, before_match);
|
||||
}
|
||||
if (flags & s8_split_inclusive) {
|
||||
s8_t match = s8_make(string.str + index, find.len);
|
||||
sb8_append(&result, match);
|
||||
}
|
||||
string = s8_skip(string, index + find.len);
|
||||
}
|
||||
if (string.len) sb8_append(&result, string);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn u64 u64_from_hexchar(char c) {
|
||||
switch (c) {
|
||||
case '0': return 0; break;
|
||||
case '1': return 1; break;
|
||||
case '2': return 2; break;
|
||||
case '3': return 3; break;
|
||||
case '4': return 4; break;
|
||||
case '5': return 5; break;
|
||||
case '6': return 6; break;
|
||||
case '7': return 7; break;
|
||||
case '8': return 8; break;
|
||||
case '9': return 9; break;
|
||||
case 'a':
|
||||
case 'A': return 10; break;
|
||||
case 'b':
|
||||
case 'B': return 11; break;
|
||||
case 'c':
|
||||
case 'C': return 12; break;
|
||||
case 'd':
|
||||
case 'D': return 13; break;
|
||||
case 'e':
|
||||
case 'E': return 14; break;
|
||||
case 'f':
|
||||
case 'F': return 15; break;
|
||||
default: return 255;
|
||||
}
|
||||
}
|
||||
|
||||
fn u64 u64_from_s8(s8_t s, u64 base) {
|
||||
assert(base >= 2 && base <= 16);
|
||||
u64 acc = 0;
|
||||
for (i64 i = 0; i < s.len; i++) {
|
||||
u64 num = u64_from_hexchar(s.str[i]);
|
||||
assert(num < base);
|
||||
acc *= base;
|
||||
acc += num;
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
fn u64 u64_from_s8_loose(s8_t s, u64 base) {
|
||||
assert(base >= 2 && base <= 16);
|
||||
u64 acc = 0;
|
||||
for (i64 i = 0; i < s.len; i++) {
|
||||
u64 num = u64_from_hexchar(s.str[i]);
|
||||
if (num >= base) {
|
||||
break;
|
||||
}
|
||||
acc *= base;
|
||||
acc += num;
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
fn f64 f64_from_s8(s8_t string) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s8_t num_string = s8_copy(scratch.arena, string);
|
||||
f64 result = os_parse_float(num_string.str);
|
||||
ma_end_scratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
gb_read_only i64 s8_fuzzy_closer_word_begin = 5;
|
||||
gb_read_only i64 s8_fuzzy_consecutive_multiplier = 3;
|
||||
fn i64 s8_fuzzy_rate_string(s8_t string, s8_t with) {
|
||||
if (with.len == 0) return 0;
|
||||
|
||||
i64 points = 0;
|
||||
i64 consecutive = 0;
|
||||
i64 with_i = 0;
|
||||
for (i64 i = 0; i < string.len; i++) {
|
||||
if (string.str[i] == with.str[with_i]) {
|
||||
i64 closer_begin = CLAMP_BOT((i64)0, s8_fuzzy_closer_word_begin - i);
|
||||
points += closer_begin;
|
||||
consecutive++;
|
||||
with_i += 1;
|
||||
} else {
|
||||
points += consecutive * s8_fuzzy_consecutive_multiplier;
|
||||
consecutive = 0;
|
||||
with_i = 0;
|
||||
}
|
||||
|
||||
if (with_i >= with.len) with_i = 0;
|
||||
}
|
||||
points += consecutive * s8_fuzzy_consecutive_multiplier;
|
||||
return points;
|
||||
}
|
||||
|
||||
fn fuzzy_pair_t *s8_fuzzy_rate_array(ma_arena_t *arena, s8_t needle, s8_t *array, i32 len) {
|
||||
fuzzy_pair_t *pairs = ma_push_array(arena, fuzzy_pair_t, len);
|
||||
for (i32 i = 0; i < len; i += 1) {
|
||||
pairs[i].rating = s8_fuzzy_rate_string(array[i], needle);
|
||||
pairs[i].index = i;
|
||||
}
|
||||
|
||||
for (i32 i = 0; i < len - 1; i++) {
|
||||
for (i32 j = 0; j < len - 1; j++) {
|
||||
if (pairs[j].rating < pairs[j + 1].rating) {
|
||||
SWAP(fuzzy_pair_t, pairs[j], pairs[j + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pairs;
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
#pragma once
|
||||
#include "core_basic.h"
|
||||
#include "core_arena.h"
|
||||
|
||||
typedef struct s8_t s8_t;
|
||||
struct s8_t {
|
||||
char *str;
|
||||
int64_t len;
|
||||
};
|
||||
|
||||
typedef struct sb8_node_t sb8_node_t;
|
||||
struct sb8_node_t {
|
||||
sb8_node_t *next;
|
||||
union {
|
||||
struct { char *str; int64_t len; };
|
||||
s8_t string;
|
||||
};
|
||||
};
|
||||
|
||||
typedef struct sb8_t sb8_t;
|
||||
struct sb8_t {
|
||||
ma_arena_t *arena;
|
||||
sb8_node_t *first;
|
||||
sb8_node_t *last;
|
||||
|
||||
// WARNING: remember to update typeinfo after editing this
|
||||
i32 indent;
|
||||
};
|
||||
|
||||
typedef i32 s8_seek_t;
|
||||
enum {
|
||||
s8_seek_none = 0,
|
||||
s8_seek_ignore_case = 1,
|
||||
s8_seek_match_find_last = 2,
|
||||
};
|
||||
|
||||
typedef i32 s8_split_t;
|
||||
enum {
|
||||
s8_split_none = 0,
|
||||
s8_split_ignore_case = 1,
|
||||
s8_split_inclusive = 2,
|
||||
s8_split_cleanup = 4,
|
||||
};
|
||||
|
||||
enum { s8_ignore_case = 1 };
|
||||
|
||||
//
|
||||
// char and char string operations
|
||||
fn i32 str_len(char *str);
|
||||
fn b32 str_eq(char *a, char *b);
|
||||
fn char char_to_lower_case(char a);
|
||||
fn char char_to_upper_case(char a);
|
||||
fn b32 char_is_whitespace(char w);
|
||||
fn b32 char_is_alphabetic(char a);
|
||||
fn b32 char_is_ident(char a);
|
||||
fn b32 char_is_digit(char a);
|
||||
fn b32 char_is_alphanumeric(char a);
|
||||
|
||||
fn u64 u64_from_hexchar(char c);
|
||||
fn u64 u64_from_s8(s8_t s, u64 base);
|
||||
fn f64 f64_from_s8(s8_t string);
|
||||
|
||||
//
|
||||
// string8 constructors
|
||||
#define s8_make(str,len) (s8_t){str, len}
|
||||
#define s8_null (s8_t){0}
|
||||
#define s8_invalid s8("<INVALID>")
|
||||
#define s8(string) (s8_t){(char *)string, sizeof(string) - 1}
|
||||
#define s8_const(string) { string, sizeof(string) - 1 }
|
||||
#define s8_struct(DATA) (s8_t){.str = (char *)&(DATA), .len = sizeof(DATA)}
|
||||
#define s8_array(DATA) (s8_t){.str = (char *)(DATA), .len = lengthof(DATA)}
|
||||
#define s8_array_lit(DATA) {.str = (char *)(DATA), .len = lengthof(DATA)}
|
||||
fn s8_t s8_from_range(char *begin, char *end);
|
||||
fn s8_t s8_from_char(char *string);
|
||||
fn s8_t s8_copy(ma_arena_t *ma, s8_t string);
|
||||
|
||||
//
|
||||
// conditional
|
||||
fn b32 s8_are_equal_ex(s8_t a, s8_t b, b32 ignore_case);
|
||||
fn b32 s8_are_equal(s8_t a, s8_t b);
|
||||
fn b32 s8_ends_with_ex(s8_t a, s8_t end, b32 ignore_case);
|
||||
fn b32 s8_starts_with_ex(s8_t a, s8_t start, b32 ignore_case);
|
||||
fn b32 s8_ends_with(s8_t a, s8_t end);
|
||||
fn b32 s8_starts_with(s8_t a, s8_t start);
|
||||
fn b32 s8_is_pointer_inside(s8_t string, char *p);// @todo: maybe more general?
|
||||
|
||||
//
|
||||
// search
|
||||
fn b32 s8_seek(s8_t string, s8_t find, s8_seek_t flags, int64_t *index_out);
|
||||
fn int64_t s8_find(s8_t string, s8_t find, s8_seek_t flag);
|
||||
fn sb8_t s8_split(ma_arena_t *ma, s8_t string, s8_t find, s8_split_t flags);
|
||||
|
||||
//
|
||||
// string view transforms
|
||||
fn s8_t s8_slice(s8_t string, int64_t first_index, int64_t one_past_last_index);
|
||||
fn s8_t s8_get_postfix(s8_t string, int64_t len);
|
||||
fn s8_t s8_get_prefix(s8_t string, int64_t len);
|
||||
fn s8_t s8_chop(s8_t string, int64_t len);
|
||||
fn s8_t s8_skip(s8_t string, int64_t len);
|
||||
fn s8_t s8_chop_last_slash(s8_t s);
|
||||
fn s8_t s8_chop_last_period(s8_t s);
|
||||
fn s8_t s8_skip_to_last_slash(s8_t s);
|
||||
fn s8_t s8_skip_to_last_period(s8_t s);
|
||||
fn s8_t s8_get_name_no_ext(s8_t s);
|
||||
fn s8_t s8_trim(s8_t string);
|
||||
fn s8_t s8_trim_end(s8_t string);
|
||||
fn s8_t s8_cut_start(s8_t *in, i64 size);
|
||||
fn s8_t s8_cut_end(s8_t *in, i64 size);
|
||||
//
|
||||
// formatting
|
||||
fn void s8_normalize_path_unsafe(s8_t s);
|
||||
fn s8_t s8_normalize_path(ma_arena_t *ma, s8_t s);
|
||||
fn s8_t s8_to_lower_case(ma_arena_t *ma, s8_t s);
|
||||
fn s8_t s8_to_upper_case(ma_arena_t *ma, s8_t s);
|
||||
fn s8_t s8_vfmt(ma_arena_t *ma, const char *str, va_list args1);
|
||||
fn s8_t s8_printf(ma_arena_t *ma, const char *str, ...);
|
||||
|
||||
//
|
||||
// string builder
|
||||
#define sb8(ARENA) &(sb8_t){.arena = arena}
|
||||
#define sb8_serial_begin(ARENA) &(sb8_t){.arena = ARENA}
|
||||
#define sb8_serial_end(ARENA, SB) sb8_merge(ARENA, SB)
|
||||
|
||||
fn s8_t sb8_printf(sb8_t *sb, const char *str, ...);
|
||||
fn sb8_node_t *sb8_append(sb8_t *list, s8_t string);
|
||||
fn sb8_node_t *sb8_create_node(ma_arena_t *ma, s8_t str);
|
||||
|
||||
fn s8_t sb8_merge(ma_arena_t *arena, sb8_t *sb);
|
||||
fn void sb8_indent(sb8_t *sb);
|
||||
fn s8_t sb8_stmtf(sb8_t *sb, const char *str, ...);
|
||||
fn int64_t sb8_char_size(sb8_t *sb);
|
||||
|
||||
#if 0
|
||||
sb8_t *sbin_write_begin(ma_arena_t *arena);
|
||||
s8_t sbin_write_end(sb8_t *sb);
|
||||
|
||||
void *sbin_write_data(sb8_t *sb, void *p, i64 size);
|
||||
void *sbin_write_size(sb8_t *sb, i64 size);
|
||||
void *sbin_write_u64(sb8_t *sb, u64 value);
|
||||
void *sbin_write_u32(sb8_t *sb, u32 value);
|
||||
void *sbin_write_u16(sb8_t *sb, u16 value);
|
||||
void *sbin_write_u8(sb8_t *sb, u8 value);
|
||||
s8_t *sbin_write_s8(sb8_t *sb, s8_t string);
|
||||
char *sbin_write_str(sb8_t *sb, char *string);
|
||||
|
||||
stream_t sbin_read_begin(void *p, i64 size);
|
||||
void sbin_read_end(stream_t *stream);
|
||||
void *sbin_read_data(stream_t *stream, i64 size);
|
||||
#endif
|
||||
|
||||
//
|
||||
// other
|
||||
#define s8_fmtspec(string) (int)(string).len, (string).str
|
||||
#define S8_CODE(...) s8(#__VA_ARGS__)
|
||||
#define S8_FILE s8(__FILE__)
|
||||
#define S8_FILE_AND_LINE s8(FILE_AND_LINE)
|
||||
|
||||
#define S8_FMT(ma, str, result) \
|
||||
va_list args1; \
|
||||
va_start(args1, str); \
|
||||
s8_t result = s8_vfmt(ma, str, args1); \
|
||||
va_end(args1)
|
||||
|
||||
#if PLATFORM_TCC
|
||||
#define s8_vsnprintf vsnprintf
|
||||
#else
|
||||
#define s8_vsnprintf stbsp_vsnprintf
|
||||
#endif
|
||||
|
||||
#define STR_FMT(buff, str) \
|
||||
va_list args1; \
|
||||
va_start(args1, str); \
|
||||
i32 len = s8_vsnprintf(buff, sizeof(buff), str, args1); \
|
||||
va_end(args1)
|
||||
|
||||
typedef struct fuzzy_pair_t fuzzy_pair_t;
|
||||
struct fuzzy_pair_t {
|
||||
i64 index;
|
||||
i64 rating;
|
||||
};
|
||||
@@ -1,622 +0,0 @@
|
||||
#include "core_string16.h"
|
||||
#include "core_platform.h"
|
||||
#include "core_intrin.h"
|
||||
#include "core_unicode.h"
|
||||
#include "core_ctx.h"
|
||||
|
||||
fn i64 str16_len(u16 *string) {
|
||||
i64 len = 0;
|
||||
while (*string++ != 0)
|
||||
len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
fn u16 char16_to_lower_case(u16 a) {
|
||||
if (a >= 'A' && a <= 'Z') a += 32;
|
||||
return a;
|
||||
}
|
||||
|
||||
fn u16 char16_to_upper_case(u16 a) {
|
||||
if (a >= 'a' && a <= 'z') a -= 32;
|
||||
return a;
|
||||
}
|
||||
|
||||
fn b32 char16_is_whitespace(u16 w) {
|
||||
b32 result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r';
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 char16_is_alphabetic(u16 a) {
|
||||
b32 result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 char16_is_ident(u16 a) {
|
||||
b32 result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || a == '_';
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 char16_is_digit(u16 a) {
|
||||
b32 result = a >= '0' && a <= '9';
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 char16_is_symbol(u16 w) {
|
||||
b32 result = (w >= '!' && w <= '/') || (w >= ':' && w <= '@') || (w >= '[' && w <= '`') || (w >= '{' && w <= '~');
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 char16_is_non_word(u16 w) {
|
||||
b32 result = w != '_' && (char16_is_symbol(w) || char16_is_whitespace(w));
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 char16_is_word(u16 w) {
|
||||
b32 result = !char16_is_non_word(w);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 char16_is_alphanumeric(u16 a) {
|
||||
b32 result = char16_is_digit(a) || char16_is_alphabetic(a);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s16_t s16_from_range(u16 *begin, u16 *end) {
|
||||
assert_expr(end >= begin);
|
||||
intptr_t size = (intptr_t)end - (intptr_t)begin;
|
||||
s16_t result = {begin, size / 2};
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s16_t s16_from_str16(u16 *string) {
|
||||
s16_t result;
|
||||
result.str = string;
|
||||
result.len = str16_len(string);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s16_t s16_copy(ma_arena_t *ma, s16_t string) {
|
||||
i64 byte_size = sizeof(u16) * string.len;
|
||||
u16 *copy = (u16 *)ma_push_size(ma, byte_size + sizeof(u16));
|
||||
memory_copy(copy, string.str, byte_size);
|
||||
copy[string.len] = 0;
|
||||
s16_t result = s16_make(copy, string.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s16_t s16_copy_str16(ma_arena_t *ma, u16 *s) {
|
||||
return s16_copy(ma, s16_make(s, str16_len(s)));
|
||||
}
|
||||
|
||||
fn b32 s16_are_equal_ex(s16_t a, s16_t b, b32 ignore_case) {
|
||||
if (a.len != b.len) return false;
|
||||
for (i64 i = 0; i < a.len; i++) {
|
||||
u16 A = a.str[i];
|
||||
u16 B = b.str[i];
|
||||
if (ignore_case) {
|
||||
A = char16_to_lower_case(A);
|
||||
B = char16_to_lower_case(B);
|
||||
}
|
||||
if (A != B)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
fn b32 s16_are_equal(s16_t a, s16_t b) {
|
||||
return s16_are_equal_ex(a, b, false);
|
||||
}
|
||||
|
||||
fn s16_t s16_from_s8(ma_arena_t *ma, s8_t string) {
|
||||
u16 *buffer = ma_push_array(ma, u16, string.len + 1);
|
||||
i64 len = wstr_from_str(buffer, string.len + 1, string.str, string.len);
|
||||
assert(len <= string.len); // @todo: verify
|
||||
return (s16_t){buffer, len};
|
||||
}
|
||||
|
||||
fn s8_t s8_from_s16(ma_arena_t *ma, s16_t string) {
|
||||
i64 buffer_size = (string.len + 1) * 2;
|
||||
char *buffer = ma_push_array(ma, char, buffer_size);
|
||||
i64 len = str_from_wstr(buffer, buffer_size, string.str, string.len);
|
||||
assert(len < buffer_size);
|
||||
return (s8_t){buffer, len};
|
||||
}
|
||||
|
||||
fn s16_t s16_get_postfix(s16_t string, i64 len) {
|
||||
len = CLAMP_TOP(len, string.len);
|
||||
i64 remain_len = string.len - len;
|
||||
s16_t result = s16_make(string.str + remain_len, len);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s16_t s16_get_prefix(s16_t string, i64 len) {
|
||||
len = CLAMP_TOP(len, string.len);
|
||||
s16_t result = s16_make(string.str, len);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s16_t s16_chop(s16_t string, i64 len) {
|
||||
len = CLAMP_TOP(len, string.len);
|
||||
s16_t result = s16_make(string.str, string.len - len);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s16_t s16_skip(s16_t string, i64 len) {
|
||||
len = CLAMP_TOP(len, string.len);
|
||||
i64 remain = string.len - len;
|
||||
s16_t result = s16_make(string.str + len, remain);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 s16_ends_with_ex(s16_t a, s16_t end, b32 ignore_case) {
|
||||
s16_t a_end = s16_get_postfix(a, end.len);
|
||||
b32 result = s16_are_equal_ex(end, a_end, ignore_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 s16_starts_with_ex(s16_t a, s16_t start, b32 ignore_case) {
|
||||
s16_t a_start = s16_get_prefix(a, start.len);
|
||||
b32 result = s16_are_equal_ex(start, a_start, ignore_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 s16_ends_with(s16_t a, s16_t end) {
|
||||
return s16_ends_with_ex(a, end, false);
|
||||
}
|
||||
|
||||
fn b32 s16_starts_with(s16_t a, s16_t start) {
|
||||
return s16_ends_with_ex(a, start, false);
|
||||
}
|
||||
|
||||
fn void s16_normalize_path_unsafe(s16_t s) {
|
||||
for (i64 i = 0; i < s.len; i++) {
|
||||
if (s.str[i] == '\\')
|
||||
s.str[i] = '/';
|
||||
}
|
||||
}
|
||||
|
||||
fn s16_t s16_normalize_path(ma_arena_t *ma, s16_t s) {
|
||||
s16_t copy = s16_copy(ma, s);
|
||||
s16_normalize_path_unsafe(copy);
|
||||
return copy;
|
||||
}
|
||||
|
||||
fn b32 s16_is_pointer_inside(s16_t string, u16 *p) {
|
||||
uintptr_t pointer = (uintptr_t)p;
|
||||
uintptr_t start = (uintptr_t)string.str;
|
||||
uintptr_t stop = start + (uintptr_t)string.len * sizeof(u16);
|
||||
b32 result = pointer >= start && pointer < stop;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s16_t s16_cut_start(s16_t *in, i64 size) {
|
||||
s16_t result = s16_get_prefix(*in, size);
|
||||
*in = s16_skip(*in, result.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s16_t s16_cut_end(s16_t *in, i64 size) {
|
||||
s16_t result = s16_get_postfix(*in, size);
|
||||
*in = s16_chop(*in, result.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s16_t s16_slice(s16_t string, i64 first_index, i64 one_past_last_index) {
|
||||
if (one_past_last_index < 0) one_past_last_index = string.len + one_past_last_index + 1;
|
||||
if (first_index < 0) first_index = string.len + first_index;
|
||||
assert(first_index < one_past_last_index);
|
||||
assert(string.len > 0);
|
||||
s16_t result = string;
|
||||
if (string.len > 0) {
|
||||
if (one_past_last_index > first_index) {
|
||||
first_index = CLAMP_TOP(first_index, string.len - 1);
|
||||
one_past_last_index = CLAMP_TOP(one_past_last_index, string.len);
|
||||
result.str += first_index;
|
||||
result.len = one_past_last_index - first_index;
|
||||
}
|
||||
else {
|
||||
result.len = 0;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s16_t s16_trim(s16_t string) {
|
||||
if (string.len == 0)
|
||||
return string;
|
||||
|
||||
i64 whitespace_begin = 0;
|
||||
for (; whitespace_begin < string.len; whitespace_begin++) {
|
||||
if (!char16_is_whitespace(string.str[whitespace_begin])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
i64 whitespace_end = string.len;
|
||||
for (; whitespace_end != whitespace_begin; whitespace_end--) {
|
||||
if (!char16_is_whitespace(string.str[whitespace_end - 1])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (whitespace_begin == whitespace_end) {
|
||||
string.len = 0;
|
||||
}
|
||||
else {
|
||||
string = s16_slice(string, whitespace_begin, whitespace_end);
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
fn s16_t s16_trim_end(s16_t string) {
|
||||
i64 whitespace_end = string.len;
|
||||
for (; whitespace_end != 0; whitespace_end--) {
|
||||
if (!char16_is_whitespace(string.str[whitespace_end - 1])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
s16_t result = s16_get_prefix(string, whitespace_end);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 s16_seek(s16_t string, s16_t find, s16_seek_t flags, int64_t *index_out) {
|
||||
b32 ignore_case = flags & s16_seek_ignore_case ? true : false;
|
||||
b32 result = false;
|
||||
if (flags & s16_seek_match_find_last) {
|
||||
for (int64_t i = string.len; i != 0; i--) {
|
||||
int64_t index = i - 1;
|
||||
s16_t substring = s16_slice(string, index, index + find.len);
|
||||
if (s16_are_equal_ex(substring, find, ignore_case)) {
|
||||
if (index_out)
|
||||
*index_out = index;
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int64_t i = 0; i < string.len; i++) {
|
||||
s16_t substring = s16_slice(string, i, i + find.len);
|
||||
if (s16_are_equal_ex(substring, find, ignore_case)) {
|
||||
if (index_out)
|
||||
*index_out = i;
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn int64_t s16_find(s16_t string, s16_t find, s16_seek_t flag) {
|
||||
int64_t result = -1;
|
||||
s16_seek(string, find, flag, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s16_t s16_chop_last_char(s16_t s, s16_t ch) {
|
||||
s16_t result = s;
|
||||
s16_seek(s, ch, s16_seek_match_find_last, &result.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
// fn s16_t s16_chop_last_slash(s16_t s) {
|
||||
// return s16_chop_last_char(s, s16("/"));
|
||||
// }
|
||||
|
||||
// fn s16_t s16_chop_last_period(s16_t s) {
|
||||
// return s16_chop_last_char(s, s16("."));
|
||||
// }
|
||||
|
||||
fn s16_t s16_skip_to_last_char(s16_t s, s16_t ch) {
|
||||
int64_t pos;
|
||||
s16_t result = s;
|
||||
if (s16_seek(s, ch, s16_seek_match_find_last, &pos)) {
|
||||
result = s16_skip(result, pos + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// fn s16_t s16_skip_to_last_slash(s16_t s) {
|
||||
// return s16_skip_to_last_char(s, s16("/"));
|
||||
// }
|
||||
|
||||
// fn s16_t s16_skip_to_last_period(s16_t s) {
|
||||
// return s16_skip_to_last_char(s, s16("."));
|
||||
// }
|
||||
|
||||
// fn s16_t s16_get_name_no_ext(s16_t s) {
|
||||
// return s16_skip_to_last_slash(s16_chop_last_period(s));
|
||||
// }
|
||||
|
||||
fn s16_t s16_to_lower_case(ma_arena_t *ma, s16_t s) {
|
||||
s16_t copy = s16_copy(ma, s);
|
||||
for (int64_t i = 0; i < copy.len; i++) {
|
||||
copy.str[i] = char16_to_lower_case(copy.str[i]);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
fn s16_t s16_to_upper_case(ma_arena_t *ma, s16_t s) {
|
||||
s16_t copy = s16_copy(ma, s);
|
||||
for (int64_t i = 0; i < copy.len; i++) {
|
||||
copy.str[i] = char16_to_upper_case(copy.str[i]);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
fn s16_t s16_printf(ma_arena_t *ma, const char *str, ...) {
|
||||
ma_temp_t scratch = ma_begin_scratch1(ma);
|
||||
S8_FMT(scratch.arena, str, result);
|
||||
s16_t result16 = s16_from_s8(ma, result);
|
||||
ma_end_scratch(scratch);
|
||||
return result16;
|
||||
}
|
||||
|
||||
fn u64 u64_from_s16(s16_t s, u64 base) {
|
||||
assert(base >= 2 && base <= 16);
|
||||
u64 acc = 0;
|
||||
for (i64 i = 0; i < s.len; i++) {
|
||||
u64 num = u64_from_hexchar((char)s.str[i]);
|
||||
assert(num < base);
|
||||
acc *= base;
|
||||
acc += num;
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
fn f64 f64_from_s16(s16_t string) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s8_t num_string = s8_from_s16(scratch.arena, string);
|
||||
f64 result = os_parse_float(num_string.str);
|
||||
ma_end_scratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
gb_read_only i64 s16_fuzzy_closer_word_begin = 5;
|
||||
gb_read_only i64 s16_fuzzy_consecutive_multiplier = 3;
|
||||
fn i64 s16_fuzzy_rate(s16_t string, s16_t with) {
|
||||
if (with.len == 0) return 0;
|
||||
|
||||
i64 points = 0;
|
||||
i64 consecutive = 0;
|
||||
i64 with_i = 0;
|
||||
for (i64 i = 0; i < string.len; i++) {
|
||||
if (string.str[i] == with.str[with_i]) {
|
||||
i64 closer_begin = CLAMP_BOT((i64)0, s16_fuzzy_closer_word_begin - i);
|
||||
points += closer_begin;
|
||||
consecutive++;
|
||||
with_i += 1;
|
||||
} else {
|
||||
points += consecutive * s16_fuzzy_consecutive_multiplier;
|
||||
consecutive = 0;
|
||||
with_i = 0;
|
||||
}
|
||||
|
||||
if (with_i >= with.len) with_i = 0;
|
||||
}
|
||||
points += consecutive * s16_fuzzy_consecutive_multiplier;
|
||||
return points;
|
||||
}
|
||||
|
||||
fn fuzzy_pair_t *s16_fuzzy_rate_array(ma_arena_t *arena, s16_t needle, s16_t *array, i32 len) {
|
||||
fuzzy_pair_t *pairs = ma_push_array(arena, fuzzy_pair_t, len);
|
||||
for (i32 i = 0; i < len; i += 1) {
|
||||
pairs[i].rating = s16_fuzzy_rate(array[i], needle);
|
||||
pairs[i].index = i;
|
||||
}
|
||||
|
||||
for (i32 i = 0; i < len - 1; i++) {
|
||||
for (i32 j = 0; j < len - 1; j++) {
|
||||
if (pairs[j].rating < pairs[j + 1].rating) {
|
||||
SWAP(fuzzy_pair_t, pairs[j], pairs[j + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pairs;
|
||||
}
|
||||
|
||||
fn sb16_node_t *sb16_create_node(ma_arena_t *ma, s16_t str) {
|
||||
sb16_node_t *node = ma_push_type(ma, sb16_node_t);
|
||||
node->string = str;
|
||||
return node;
|
||||
}
|
||||
|
||||
fn sb16_node_t *sb16_append(sb16_t *list, s16_t string) {
|
||||
assert(list->arena != NULL);
|
||||
sb16_node_t *node = sb16_create_node(list->arena, string);
|
||||
SLLQ_APPEND(list->first, list->last, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
fn s16_t sb16_printf(sb16_t *sb, const char *str, ...) {
|
||||
ma_temp_t scratch = ma_begin_scratch1(sb->arena);
|
||||
S8_FMT(scratch.arena, str, result);
|
||||
s16_t result16 = s16_from_s8(sb->arena, result);
|
||||
sb16_append(sb, result16);
|
||||
ma_end_scratch(scratch);
|
||||
return result16;
|
||||
}
|
||||
|
||||
fn void sb16_indent(sb16_t *sb) {
|
||||
sb16_printf(sb, "\n%.*s", sb->indent*4, " ");
|
||||
}
|
||||
|
||||
fn s16_t sb16_stmtf(sb16_t *sb, const char *str, ...) {
|
||||
ma_temp_t scratch = ma_begin_scratch1(sb->arena);
|
||||
S8_FMT(scratch.arena, str, result);
|
||||
s16_t result16 = s16_from_s8(sb->arena, result);
|
||||
sb16_indent(sb);
|
||||
sb16_append(sb, result16);
|
||||
ma_end_scratch(scratch);
|
||||
return result16;
|
||||
}
|
||||
|
||||
fn int64_t sb16_char_size(sb16_t *sb) {
|
||||
int64_t result = 0;
|
||||
for (sb16_node_t *it = sb->first; it; it = it->next) {
|
||||
result += it->len;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s16_t sb16_merge(ma_arena_t *arena, sb16_t *sb) {
|
||||
int64_t size = sb16_char_size(sb) + 1;
|
||||
i64 byte_size = size * sizeof(u16);
|
||||
u16 *str = ma_push_size(arena, byte_size);
|
||||
s16_t result = {str, 0};
|
||||
for (sb16_node_t *it = sb->first; it; it = it->next) {
|
||||
memory_copy(result.str + result.len, it->str, it->len * sizeof(u16));
|
||||
result.len += it->len;
|
||||
}
|
||||
result.str[result.len] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn sb16_t s16_split(ma_arena_t *ma, s16_t string, s16_t find, s16_split_t flags) {
|
||||
sb16_t result = (sb16_t){ma};
|
||||
int64_t index = 0;
|
||||
|
||||
s16_seek_t find_flag = flags & s16_split_ignore_case ? s16_seek_ignore_case : s16_seek_none;
|
||||
while (s16_seek(string, find, find_flag, &index)) {
|
||||
s16_t before_match = s16_make(string.str, index);
|
||||
sb16_append(&result, before_match);
|
||||
if (flags & s16_split_inclusive) {
|
||||
s16_t match = s16_make(string.str + index, find.len);
|
||||
sb16_append(&result, match);
|
||||
}
|
||||
string = s16_skip(string, index + find.len);
|
||||
}
|
||||
if (string.len) sb16_append(&result, string);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s16_t s16_copy_ex(alo_t ma, s16_t string) {
|
||||
i64 byte_size = sizeof(u16) * string.len;
|
||||
u16 *copy = (u16 *)alloc_size(ma, byte_size + sizeof(u16));
|
||||
memory_copy(copy, string.str, byte_size);
|
||||
copy[string.len] = 0;
|
||||
s16_t result = s16_make(copy, string.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if 0
|
||||
fn_test void test_string16(void) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
|
||||
// string16 lit basic tests
|
||||
{
|
||||
s16_t a = s16("memes");
|
||||
assert(a.len == 5);
|
||||
assert(a.str[0] == char16('m'));
|
||||
assert(a.str[4] == char16('s'));
|
||||
s16_t b = s16("not memes");
|
||||
s16_t c = s16("MEMES");
|
||||
assert(s16_are_equal(a, b) == false);
|
||||
assert(s16_are_equal(a, a) == true);
|
||||
assert(s16_are_equal_ex(a, c, true) == true);
|
||||
}
|
||||
|
||||
{
|
||||
assert(char16_to_upper_case('a') == char16('A'));
|
||||
assert(char16_to_upper_case('a') == 'A');
|
||||
assert(char16_to_upper_case(char16('a')) == 'A');
|
||||
assert(char16_is_digit('3'));
|
||||
}
|
||||
|
||||
{
|
||||
s16_t a = s16_from_str16(str16("memes"));
|
||||
assert(a.len == 5);
|
||||
assert(s16_are_equal(a, s16("memes")) == true);
|
||||
|
||||
s16_t b = s16_copy(scratch.arena, a);
|
||||
assert(a.str != b.str);
|
||||
assert(b.len == a.len);
|
||||
assert(s16_are_equal(a, b));
|
||||
|
||||
s16_t c = s16_from_range(a.str, a.str + a.len);
|
||||
assert(s16_are_equal(a, c));
|
||||
}
|
||||
|
||||
{
|
||||
s16_t a = s16("thing itself");
|
||||
assert(s16_starts_with_ex(a, s16("thing"), false));
|
||||
assert(!s16_starts_with_ex(a, s16("thing itself and"), false));
|
||||
assert(!s16_starts_with_ex(a, s16("thing itself "), false));
|
||||
assert(s16_starts_with_ex(a, s16("THING"), true));
|
||||
assert(!s16_starts_with_ex(a, s16("a"), false));
|
||||
|
||||
assert(s16_ends_with_ex(a, s16("itself"), false));
|
||||
assert(s16_ends_with_ex(a, s16("thing itself"), false));
|
||||
assert(!s16_ends_with_ex(a, s16("thing itselfa"), false));
|
||||
|
||||
s16_t b = s16("memes");
|
||||
assert(s16_is_pointer_inside(b, b.str));
|
||||
assert(s16_is_pointer_inside(b, b.str + 4));
|
||||
assert(!s16_is_pointer_inside(b, b.str + 5));
|
||||
}
|
||||
|
||||
{
|
||||
s16_t a = s16("C:\\Program Files\\Memes");
|
||||
u16 memes[256] = {0};
|
||||
memory_copy(memes, a.str, a.len * 2 + 2);
|
||||
s16_t b = s16_make(memes, a.len);
|
||||
|
||||
s16_normalize_path_unsafe(b);
|
||||
assert(b.str[2] == '/');
|
||||
}
|
||||
|
||||
{
|
||||
s16_t a = s16(" thing ");
|
||||
s16_t b = s16_trim(a);
|
||||
assert(s16_are_equal(b, s16("thing")));
|
||||
}
|
||||
|
||||
{
|
||||
s16_t a = s16_printf(scratch.arena, "%d", 432);
|
||||
assert(s16_are_equal(a, s16("432")));
|
||||
assert(f64_from_s16(s16("432.0")) == 432.0);
|
||||
assert(u64_from_s16(s16("432"), 10) == 432);
|
||||
}
|
||||
|
||||
{
|
||||
sb16_t *list = sb16_serial_begin(scratch.arena);
|
||||
sb16_printf(list, "%d ", 987);
|
||||
sb16_printf(list, "ter ");
|
||||
sb16_printf(list, "|");
|
||||
s16_t string = sb16_serial_end(scratch.arena, list);
|
||||
assert(s16_are_equal(string, s16("987 ter |")));
|
||||
}
|
||||
|
||||
{
|
||||
s16_t a = s16("A|B|dek|mek|vek|");
|
||||
sb16_t b = s16_split(scratch.arena, a, s16("|"), s16_split_none);
|
||||
assert(s16_are_equal(b.first->string, s16("A")));
|
||||
assert(s16_are_equal(b.first->next->string, s16("B")));
|
||||
assert(s16_are_equal(b.first->next->next->string, s16("dek")));
|
||||
assert(s16_are_equal(b.first->next->next->next->string, s16("mek")));
|
||||
assert(s16_are_equal(b.first->next->next->next->next->string, s16("vek")));
|
||||
assert(b.first->next->next->next->next->next == NULL);
|
||||
}
|
||||
|
||||
{
|
||||
s16_t a = s16_to_upper_case(scratch.arena, s16("thing Meme"));
|
||||
assert(s16_are_equal(a, s16("THING MEME")));
|
||||
|
||||
a = s16_to_lower_case(scratch.arena, s16("THING Meme"));
|
||||
assert(s16_are_equal(a, s16("thing meme")));
|
||||
}
|
||||
|
||||
{
|
||||
s16_t a = s16("C:/thing/itself");
|
||||
assert(s16_are_equal(s16_chop_last_slash(a), s16("C:/thing")));
|
||||
}
|
||||
|
||||
ma_end_scratch(scratch);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,128 +0,0 @@
|
||||
#pragma once
|
||||
#include "core_basic.h"
|
||||
#include "core_arena.h"
|
||||
#include "core_string.h"
|
||||
|
||||
typedef struct s16_t s16_t;
|
||||
struct s16_t {
|
||||
u16 *str;
|
||||
i64 len;
|
||||
};
|
||||
|
||||
typedef struct sb16_node_t sb16_node_t;
|
||||
struct sb16_node_t {
|
||||
sb16_node_t *next;
|
||||
union {
|
||||
struct { u16 *str; int64_t len; };
|
||||
s16_t string;
|
||||
};
|
||||
};
|
||||
|
||||
typedef struct sb16_t sb16_t;
|
||||
struct sb16_t {
|
||||
ma_arena_t *arena;
|
||||
sb16_node_t *first;
|
||||
sb16_node_t *last;
|
||||
|
||||
// WARNING: remember to update typeinfo after editing this
|
||||
i32 indent;
|
||||
};
|
||||
|
||||
typedef i32 s16_seek_t;
|
||||
enum {
|
||||
s16_seek_none = 0,
|
||||
s16_seek_ignore_case = 1,
|
||||
s16_seek_match_find_last = 2,
|
||||
};
|
||||
|
||||
typedef i32 s16_split_t;
|
||||
enum {
|
||||
s16_split_none = 0,
|
||||
s16_split_ignore_case = 1,
|
||||
s16_split_inclusive = 2,
|
||||
};
|
||||
|
||||
enum { s16_ignore_case = 1 };
|
||||
|
||||
|
||||
typedef struct s32_t s32_t;
|
||||
struct s32_t {
|
||||
u32 *str;
|
||||
i64 len;
|
||||
};
|
||||
|
||||
#define s16_make(str,len) (s16_t){str, len}
|
||||
|
||||
#define s16(str) (s16_t){(u16 *)u##str, lengthof(str) - 1}
|
||||
#define s16_const(string) { (u16 *)u##string, lengthof(string) - 1 }
|
||||
#define s16_invalid s16_lit("<INVALID>")
|
||||
#define s16_null (s16_t){0}
|
||||
|
||||
#define str16(str) ((u16 *)u##str)
|
||||
#define char16(str) ((u16)u##str)
|
||||
|
||||
#define sb16(ARENA) &(sb16_t){.arena = arena}
|
||||
#define sb16_serial_begin(ARENA) &(sb16_t){.arena = ARENA}
|
||||
#define sb16_serial_end(ARENA, SB) sb16_merge(ARENA, SB)
|
||||
|
||||
fn i64 str16_len(u16 *string);
|
||||
fn u16 char16_to_lower_case(u16 a);
|
||||
fn u16 char16_to_upper_case(u16 a);
|
||||
fn b32 char16_is_whitespace(u16 w);
|
||||
fn b32 char16_is_alphabetic(u16 a);
|
||||
fn b32 char16_is_ident(u16 a);
|
||||
fn b32 char16_is_digit(u16 a);
|
||||
fn b32 char16_is_symbol(u16 w);
|
||||
fn b32 char16_is_non_word(u16 w);
|
||||
fn b32 char16_is_word(u16 w);
|
||||
fn b32 char16_is_alphanumeric(u16 a);
|
||||
fn s16_t s16_from_range(u16 *begin, u16 *end);
|
||||
fn s16_t s16_from_str16(u16 *string);
|
||||
fn s16_t s16_copy(ma_arena_t *ma, s16_t string);
|
||||
fn s16_t s16_copy_str16(ma_arena_t *ma, u16 *s);
|
||||
fn b32 s16_are_equal_ex(s16_t a, s16_t b, b32 ignore_case);
|
||||
fn b32 s16_are_equal(s16_t a, s16_t b);
|
||||
fn s16_t s16_from_s8(ma_arena_t *ma, s8_t string);
|
||||
fn s8_t s8_from_s16(ma_arena_t *ma, s16_t string);
|
||||
fn s16_t s16_get_postfix(s16_t string, i64 len);
|
||||
fn s16_t s16_get_prefix(s16_t string, i64 len);
|
||||
fn s16_t s16_chop(s16_t string, i64 len);
|
||||
fn s16_t s16_skip(s16_t string, i64 len);
|
||||
fn b32 s16_ends_with_ex(s16_t a, s16_t end, b32 ignore_case);
|
||||
fn b32 s16_starts_with_ex(s16_t a, s16_t start, b32 ignore_case);
|
||||
fn b32 s16_ends_with(s16_t a, s16_t end);
|
||||
fn b32 s16_starts_with(s16_t a, s16_t start);
|
||||
fn void s16_normalize_path_unsafe(s16_t s);
|
||||
fn s16_t s16_normalize_path(ma_arena_t *ma, s16_t s);
|
||||
fn b32 s16_is_pointer_inside(s16_t string, u16 *p);
|
||||
fn s16_t s16_cut_start(s16_t *in, i64 size);
|
||||
fn s16_t s16_cut_end(s16_t *in, i64 size);
|
||||
fn s16_t s16_slice(s16_t string, i64 first_index, i64 one_past_last_index);
|
||||
fn s16_t s16_trim(s16_t string);
|
||||
fn s16_t s16_trim_end(s16_t string);
|
||||
fn b32 s16_seek(s16_t string, s16_t find, s16_seek_t flags, int64_t *index_out);
|
||||
fn int64_t s16_find(s16_t string, s16_t find, s16_seek_t flag);
|
||||
fn s16_t s16_chop_last_char(s16_t s, s16_t ch);
|
||||
fn s16_t s16_chop_last_slash(s16_t s);
|
||||
fn s16_t s16_chop_last_period(s16_t s);
|
||||
fn s16_t s16_skip_to_last_char(s16_t s, s16_t ch);
|
||||
fn s16_t s16_skip_to_last_slash(s16_t s);
|
||||
fn s16_t s16_skip_to_last_period(s16_t s);
|
||||
fn s16_t s16_get_name_no_ext(s16_t s);
|
||||
fn s16_t s16_to_lower_case(ma_arena_t *ma, s16_t s);
|
||||
fn s16_t s16_to_upper_case(ma_arena_t *ma, s16_t s);
|
||||
fn s16_t s16_printf(ma_arena_t *ma, const char *str, ...);
|
||||
fn u64 u64_from_s16(s16_t s, u64 base);
|
||||
fn f64 f64_from_s16(s16_t string);
|
||||
fn i64 s16_fuzzy_rate(s16_t string, s16_t with);
|
||||
fn fuzzy_pair_t *s16_fuzzy_rate_array(ma_arena_t *arena, s16_t needle, s16_t *array, i32 len);
|
||||
fn sb16_node_t *sb16_create_node(ma_arena_t *ma, s16_t str);
|
||||
fn sb16_node_t *sb16_append(sb16_t *list, s16_t string);
|
||||
fn s16_t sb16_printf(sb16_t *sb, const char *str, ...);
|
||||
fn void sb16_indent(sb16_t *sb);
|
||||
fn s16_t sb16_stmtf(sb16_t *sb, const char *str, ...);
|
||||
fn int64_t sb16_char_size(sb16_t *sb);
|
||||
fn s16_t sb16_merge(ma_arena_t *arena, sb16_t *sb);
|
||||
fn sb16_t s16_split(ma_arena_t *ma, s16_t string, s16_t find, s16_split_t flags);
|
||||
fn s16_t s16_copy_ex(alo_t ma, s16_t string);
|
||||
fn_test void test_string16(void);
|
||||
@@ -1,466 +0,0 @@
|
||||
#include "core_type_info.h"
|
||||
#include "core_platform.h"
|
||||
#include "core_log.h"
|
||||
#include "core_lexer.h"
|
||||
|
||||
fn s8_t ti_enum_value_to_name(i64 value, type_t *type) {
|
||||
assert(type->kind == type_kind_enum);
|
||||
for (i32 i = 0; i < type->count; i += 1) {
|
||||
type_member_t *it = type->members + i;
|
||||
if (value == it->value) {
|
||||
return it->name;
|
||||
}
|
||||
}
|
||||
return s8("invalid");
|
||||
}
|
||||
|
||||
fn i64 ti_enum_name_to_value(s8_t name, type_t *type) {
|
||||
assert(type->kind == type_kind_enum);
|
||||
for (i32 i = 0; i < type->count; i += 1) {
|
||||
type_member_t *it = type->members + i;
|
||||
if (s8_are_equal(it->name, name)) {
|
||||
return it->value;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
fn i64 ti_enum_read_value(void *p, type_t *type) {
|
||||
assert(type->kind == type_kind_enum);
|
||||
i64 value = 0;
|
||||
if (type->size == 1) {
|
||||
value = *(i8 *)p;
|
||||
} else if (type->size == 2) {
|
||||
value = *(i16 *)p;
|
||||
} else if (type->size == 4) {
|
||||
value = *(i32 *)p;
|
||||
} else if (type->size == 8) {
|
||||
value = *(i64 *)p;
|
||||
} else {
|
||||
fatalf("invalid size of enum: %d", type->size);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
fn void ti_enum_write_value(void *p, i64 value, type_t *type) {
|
||||
assert(type->kind == type_kind_enum);
|
||||
if (type->size == 1) {
|
||||
*(i8 *)p = (i8)value;
|
||||
} else if (type->size == 2) {
|
||||
*(i16 *)p = (i16)value;
|
||||
} else if (type->size == 4) {
|
||||
*(i32 *)p = (i32)value;
|
||||
} else if (type->size == 8) {
|
||||
*(i64 *)p = (i64)value;
|
||||
} else {
|
||||
fatalf("invalid size of enum: %d", type->size);
|
||||
}
|
||||
}
|
||||
|
||||
fn i64 ti_enum_next_value(i64 value, type_t *type) {
|
||||
assert(type->kind == type_kind_enum);
|
||||
i32 next_i = 0;
|
||||
for (i32 i = 0; i < type->count; i += 1) {
|
||||
type_member_t *it = type->members + i;
|
||||
if (value == it->value) {
|
||||
if (i + 1 < type->count) {
|
||||
next_i = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type_member_t *tm = type->members + next_i;
|
||||
return tm->value;
|
||||
}
|
||||
|
||||
fn type_member_t *ti_get_member(s8_t name, type_t *type) {
|
||||
for (i32 i = 0; i < type->count; i += 1) {
|
||||
type_member_t *it = type->members + i;
|
||||
if (s8_are_equal(it->name, name)) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fn void *ti_extract_member(void *p, type_member_t *tm) {
|
||||
u8 *p8 = (u8 *)p;
|
||||
u8 *result = p8 + tm->offset;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn void *ti_extract_array_idx(void *p, type_t *type, i32 idx) {
|
||||
assert(idx < type->count);
|
||||
|
||||
u8 *p8 = (u8 *)p;
|
||||
u8 *result = p8 + type->base->size * idx;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn void ti__serial_data_ex(sb8_t *sb, void *p, type_t *type) {
|
||||
assert(type->kind != type_kind_invalid);
|
||||
|
||||
switch(type->kind) {
|
||||
case type_kind_i8: {
|
||||
i8 n = *(i8 *)p;
|
||||
sb8_printf(sb, "%d", n);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_i16: {
|
||||
i16 n = *(i16 *)p;
|
||||
sb8_printf(sb, "%d", n);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_i32: {
|
||||
i32 n = *(i32 *)p;
|
||||
sb8_printf(sb, "%d", n);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_i64: {
|
||||
i64 n = *(i64 *)p;
|
||||
sb8_printf(sb, "%lld", (long long)n);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_u8: {
|
||||
u8 n = *(u8 *)p;
|
||||
sb8_printf(sb, "%u", n);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_u16: {
|
||||
u16 n = *(u16 *)p;
|
||||
sb8_printf(sb, "%u", n);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_u32: {
|
||||
u32 n = *(u32 *)p;
|
||||
sb8_printf(sb, "%u", n);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_u64: {
|
||||
u64 n = *(u64 *)p;
|
||||
sb8_printf(sb, "%llu", (unsigned long long)n);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_b8: {
|
||||
b8 n = *(b8 *)p;
|
||||
sb8_printf(sb, "%d", n);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_b16: {
|
||||
b16 n = *(b16 *)p;
|
||||
sb8_printf(sb, "%d", n);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_b32: {
|
||||
b32 n = *(b32 *)p;
|
||||
sb8_printf(sb, "%d", n);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_b64: {
|
||||
b64 n = *(b64 *)p;
|
||||
sb8_printf(sb, "%lld", (long long)n);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_f32: {
|
||||
f32 n = *(f32 *)p;
|
||||
sb8_printf(sb, "%f", n);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_f64: {
|
||||
f64 n = *(f64 *)p;
|
||||
sb8_printf(sb, "%f", n);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_isize: {
|
||||
isize n = *(isize *)p;
|
||||
sb8_printf(sb, "%lld", (long long)n);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_usize: {
|
||||
usize n = *(usize *)p;
|
||||
sb8_printf(sb, "%llu", (unsigned long long)n);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_int: {
|
||||
int n = *(int *)p;
|
||||
sb8_printf(sb, "%d", n);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_char: {
|
||||
char n = *(char *)p;
|
||||
sb8_printf(sb, "%c", n);
|
||||
return;
|
||||
} break;
|
||||
}
|
||||
|
||||
if (type == &type__s8_t) {
|
||||
s8_t n = *(s8_t *)p;
|
||||
sb8_printf(sb, "\"%.*s\"", s8_fmtspec(n));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (type->kind == type_kind_pointer) {
|
||||
sb8_printf(sb, "0x%x", p);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type->kind == type_kind_enum) {
|
||||
i64 value = ti_enum_read_value(p, type);
|
||||
s8_t s = ti_enum_value_to_name(value, type);
|
||||
sb8_append(sb, s);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type->kind == type_kind_array) {
|
||||
sb8_printf(sb, "{");
|
||||
sb->indent += 1;
|
||||
for (i32 i = 0; i < type->count; i += 1) {
|
||||
void *mem_p = ti_extract_array_idx(p, type, i);
|
||||
sb8_indent(sb);
|
||||
ti__serial_data_ex(sb, mem_p, type->base);
|
||||
sb8_printf(sb, ",");
|
||||
}
|
||||
sb->indent -= 1;
|
||||
sb8_stmtf(sb, "}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (type->kind == type_kind_struct) {
|
||||
sb8_printf(sb, "{");
|
||||
sb->indent += 1;
|
||||
for (i32 i = 0; i < type->count; i += 1) {
|
||||
type_member_t *mem = type->members + i;
|
||||
void *mem_p = ti_extract_member(p, mem);
|
||||
sb8_indent(sb);
|
||||
sb8_printf(sb, "%.*s: ", s8_fmtspec(mem->name));
|
||||
ti__serial_data_ex(sb, mem_p, mem->type);
|
||||
sb8_printf(sb, ",");
|
||||
}
|
||||
sb->indent -= 1;
|
||||
sb8_stmtf(sb, "}");
|
||||
return;
|
||||
}
|
||||
|
||||
fatalf("can't serialize: unhandled type");
|
||||
}
|
||||
|
||||
fn s8_t ti__serial_data(ma_arena_t *arena, void *p, type_t *type) {
|
||||
ma_temp_t scratch = ma_begin_scratch1(arena);
|
||||
sb8_t *sb = sb8_serial_begin(scratch.arena);
|
||||
ti__serial_data_ex(sb, p, type);
|
||||
s8_t string = sb8_serial_end(arena, sb);
|
||||
ma_end_scratch(scratch);
|
||||
return string;
|
||||
}
|
||||
|
||||
fn i64 ti__parser_match_i64(parser_t *par) {
|
||||
i64 minus = 1;
|
||||
if (parser_match(par, lex_kind_minus)) {
|
||||
minus = -1;
|
||||
}
|
||||
lex_t *token = parser_expect(par, lex_kind_integer);
|
||||
i64 result = (i64)token->integer * minus;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn void ti__deserial_data_ex(ma_arena_t *arena, parser_t *par, void *p, type_t *type) {
|
||||
switch(type->kind) {
|
||||
case type_kind_i8: {
|
||||
i8 *n = (i8 *)p;
|
||||
n[0] = (i8)ti__parser_match_i64(par);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_i16: {
|
||||
i16 *n = (i16 *)p;
|
||||
n[0] = (i16)ti__parser_match_i64(par);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_i32: {
|
||||
i32 *n = (i32 *)p;
|
||||
n[0] = (i32)ti__parser_match_i64(par);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_i64: {
|
||||
i64 *n = (i64 *)p;
|
||||
n[0] = (i64)ti__parser_match_i64(par);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_b8: {
|
||||
b8 *n = (b8 *)p;
|
||||
n[0] = (b8)ti__parser_match_i64(par);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_b16: {
|
||||
b16 *n = (b16 *)p;
|
||||
n[0] = (b16)ti__parser_match_i64(par);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_b32: {
|
||||
b32 *n = (b32 *)p;
|
||||
n[0] = (b32)ti__parser_match_i64(par);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_b64: {
|
||||
b64 *n = (b64 *)p;
|
||||
n[0] = (b64)ti__parser_match_i64(par);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_u8: {
|
||||
lex_t *token = parser_expect(par, lex_kind_integer);
|
||||
u8 *n = (u8 *)p;
|
||||
n[0] = (u8)token->integer;
|
||||
return;
|
||||
} break;
|
||||
case type_kind_u16: {
|
||||
lex_t *token = parser_expect(par, lex_kind_integer);
|
||||
u16 *n = (u16 *)p;
|
||||
n[0] = (u16)token->integer;
|
||||
return;
|
||||
} break;
|
||||
case type_kind_u32: {
|
||||
lex_t *token = parser_expect(par, lex_kind_integer);
|
||||
u32 *n = (u32 *)p;
|
||||
n[0] = (u32)token->integer;
|
||||
return;
|
||||
} break;
|
||||
case type_kind_u64: {
|
||||
lex_t *token = parser_expect(par, lex_kind_integer);
|
||||
u64 *n = (u64 *)p;
|
||||
n[0] = (u64)token->integer;
|
||||
return;
|
||||
} break;
|
||||
case type_kind_f32: {
|
||||
lex_t *token = parser_expect(par, lex_kind_real);
|
||||
f32 *n = (f32 *)p;
|
||||
n[0] = (f32)token->real;
|
||||
return;
|
||||
} break;
|
||||
case type_kind_f64: {
|
||||
lex_t *token = parser_expect(par, lex_kind_real);
|
||||
f64 *n = (f64 *)p;
|
||||
n[0] = token->real;
|
||||
return;
|
||||
} break;
|
||||
case type_kind_isize: {
|
||||
isize *n = (isize *)p;
|
||||
n[0] = (isize)ti__parser_match_i64(par);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_usize: {
|
||||
lex_t *token = parser_expect(par, lex_kind_integer);
|
||||
usize *n = (usize *)p;
|
||||
n[0] = (usize)token->integer;
|
||||
return;
|
||||
} break;
|
||||
case type_kind_int: {
|
||||
int *n = (int *)p;
|
||||
n[0] = (int)ti__parser_match_i64(par);
|
||||
return;
|
||||
} break;
|
||||
case type_kind_char: {
|
||||
char *n = (char *)p;
|
||||
n[0] = (char)ti__parser_match_i64(par);
|
||||
return;
|
||||
} break;
|
||||
}
|
||||
|
||||
if (type == &type__s8_t) {
|
||||
lex_t *token = parser_expect(par, lex_kind_string);
|
||||
s8_t *n = (s8_t *)p;
|
||||
n[0] = s8_copy(arena, token->string);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type->kind == type_kind_enum) {
|
||||
lex_t *token = parser_expect(par, lex_kind_ident);
|
||||
i64 value = ti_enum_name_to_value(token->string, type);
|
||||
if (value == -1) {
|
||||
fatalf("invalid enum value: %.*s", s8_fmtspec(token->string));
|
||||
}
|
||||
|
||||
ti_enum_write_value(p, value, type);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type->kind == type_kind_struct) {
|
||||
#if 0
|
||||
parser_expect(par, lex_kind_open_brace);
|
||||
while (!parser_match(par, lex_kind_close_brace)) {
|
||||
lex_t *ident = parser_expect(par, lex_kind_ident);
|
||||
parser_expect(par, lex_kind_colon);
|
||||
|
||||
type_member_t *mem = ti_get_member(type, ident->string);
|
||||
if (mem) {
|
||||
u8 *mem_p = p8 + mem->offset;
|
||||
ti__deserial_data_ex(arena, par, mem_p, mem->type);
|
||||
} else {
|
||||
debugf("deserial - skipping field: %.*s", s8_fmtspec(ident->string));
|
||||
parser_eat_until(par, lex_kind_comma);
|
||||
}
|
||||
|
||||
parser_expect(par, lex_kind_comma);
|
||||
}
|
||||
|
||||
#else // strict
|
||||
parser_expect(par, lex_kind_open_brace);
|
||||
for (i32 i = 0; i < type->count; i += 1) {
|
||||
type_member_t *mem = type->members + i;
|
||||
void *memp = ti_extract_member(p, mem);
|
||||
|
||||
lex_t *ident = parser_expect(par, lex_kind_ident);
|
||||
parser_expect(par, lex_kind_colon);
|
||||
|
||||
expect (s8_are_equal(ident->string, mem->name)) fatalf("expected identifier: %.*s, got instead %.*s", s8_fmtspec(mem->name), s8_fmtspec(ident->string));
|
||||
|
||||
ti__deserial_data_ex(arena, par, memp, mem->type);
|
||||
parser_expect(par, lex_kind_comma);
|
||||
}
|
||||
parser_expect(par, lex_kind_close_brace);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (type->kind == type_kind_array) {
|
||||
parser_expect(par, lex_kind_open_brace);
|
||||
for (i32 i = 0; i < type->count; i += 1) {
|
||||
void *memp = ti_extract_array_idx(p, type, i);
|
||||
ti__deserial_data_ex(arena, par, memp, type->base);
|
||||
parser_expect(par, lex_kind_comma);
|
||||
}
|
||||
parser_expect(par, lex_kind_close_brace);
|
||||
}
|
||||
}
|
||||
|
||||
fn void *ti__deserial_data(ma_arena_t *arena, s8_t data, type_t *type) {
|
||||
void *p = ma_push_size(arena, type->size);
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
lex_array_t tokens = lex_tokens(scratch.arena, "data serializing", data);
|
||||
parser_t *par = parser_make(scratch.arena, tokens.data);
|
||||
ti__deserial_data_ex(arena, par, p, type);
|
||||
ma_end_scratch(scratch);
|
||||
return p;
|
||||
}
|
||||
|
||||
fn b32 ti_is_basic(type_t *type) {
|
||||
b32 result = type->kind >= type_kind_first_basic && type->kind <= type_kind_last_basic;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 ti_has_type_name(type_t *type) {
|
||||
b32 result = ti_is_basic(type) || type->kind == type_kind_struct || type->kind == type_kind_enum || type->kind == type_kind_union;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t ti_serial_type(ma_arena_t *arena, type_t *type) {
|
||||
if (ti_has_type_name(type)) {
|
||||
return type->name;
|
||||
} else if (type->kind == type_kind_pointer) {
|
||||
s8_t name = ti_serial_type(arena, type->base);
|
||||
return s8_printf(arena, "%.*s*", s8_fmtspec(name));
|
||||
} else if (type->kind == type_kind_array) {
|
||||
s8_t name = ti_serial_type(arena, type->base);
|
||||
return s8_printf(arena, "%.*s[%d]", s8_fmtspec(name), type->count);
|
||||
} else_is_invalid;
|
||||
return s8_invalid;
|
||||
}
|
||||
@@ -1,364 +0,0 @@
|
||||
#pragma once
|
||||
#include "core_basic.h"
|
||||
#include "core_arena.h"
|
||||
#include "core_string.h"
|
||||
#include "core_string16.h"
|
||||
#include "core_math.h"
|
||||
|
||||
typedef int type_kind_t;
|
||||
enum {
|
||||
type_kind_invalid,
|
||||
|
||||
type_kind_i8,
|
||||
type_kind_i16,
|
||||
type_kind_i32,
|
||||
type_kind_i64,
|
||||
type_kind_b8,
|
||||
type_kind_b16,
|
||||
type_kind_b32,
|
||||
type_kind_b64,
|
||||
type_kind_u8,
|
||||
type_kind_u16,
|
||||
type_kind_u32,
|
||||
type_kind_u64,
|
||||
type_kind_f32,
|
||||
type_kind_f64,
|
||||
type_kind_isize,
|
||||
type_kind_usize,
|
||||
type_kind_int,
|
||||
type_kind_char,
|
||||
type_kind_void,
|
||||
|
||||
type_kind_pointer,
|
||||
type_kind_array,
|
||||
type_kind_struct,
|
||||
type_kind_union,
|
||||
type_kind_enum,
|
||||
|
||||
type_kind_first_basic = type_kind_i8,
|
||||
type_kind_last_basic = type_kind_void,
|
||||
};
|
||||
|
||||
typedef struct type_t type_t;
|
||||
struct type_t {
|
||||
type_kind_t kind;
|
||||
s8_t name;
|
||||
i32 size;
|
||||
i32 count;
|
||||
struct type_member_t *members;
|
||||
type_t *base;
|
||||
};
|
||||
|
||||
typedef struct type_member_t type_member_t;
|
||||
struct type_member_t {
|
||||
s8_t name;
|
||||
type_t *type;
|
||||
i64 value;
|
||||
u64 offset;
|
||||
b8 dont_serialize;
|
||||
};
|
||||
|
||||
typedef struct mt_tweak_t mt_tweak_t;
|
||||
struct mt_tweak_t {
|
||||
type_t *type;
|
||||
s8_t name;
|
||||
void *ptr;
|
||||
f32 min, max;
|
||||
};
|
||||
#define mt_embed_file(name, path)
|
||||
#define mt_tweak_f32(name, default, min, max)
|
||||
#define mt_tweak_color(name, ...)
|
||||
#define mt_tweak_b32(name, default)
|
||||
|
||||
//
|
||||
// serialization to string
|
||||
#define ti_deserial_data(ARENA, DATA, TYPE) (TYPE *)ti__deserial_data(ARENA, DATA, type(TYPE))
|
||||
#define ti_serial_data(ARENA, DATA, TYPE) ti__serial_data(ARENA, DATA, type(TYPE))
|
||||
|
||||
fn void *ti__deserial_data(ma_arena_t *arena, s8_t data, type_t *type);
|
||||
fn s8_t ti__serial_data(ma_arena_t *arena, void *p, type_t *type);
|
||||
|
||||
//
|
||||
// utilities
|
||||
fn s8_t ti_enum_value_to_name(i64 value, type_t *type);
|
||||
fn i64 ti_enum_name_to_value(s8_t name, type_t *type);
|
||||
fn type_member_t *ti_get_member(s8_t name, type_t *type);
|
||||
|
||||
//
|
||||
// core type info data
|
||||
#define type(X) (&type__##X)
|
||||
#define DEFINE_ENUM(x) type_t type__##x = {type_kind_enum, s8_const(#x), sizeof(x), .members = members__##x, .count = lengthof(members__##x)}
|
||||
#define DEFINE_STRUCT(x) type_t type__##x = {type_kind_struct, s8_const(#x), sizeof(x), .members = members__##x, .count = lengthof(members__##x)}
|
||||
#define POINTER(x) (type_t){type_kind_pointer, s8_const(#x "*"), sizeof(void *), .base = &type__##x}
|
||||
|
||||
static gb_read_only type_t type__i8 = {type_kind_i8, s8_const("i8"), sizeof(i8)};
|
||||
static gb_read_only type_t type__i16 = {type_kind_i16, s8_const("i16"), sizeof(i16)};
|
||||
static gb_read_only type_t type__i32 = {type_kind_i32, s8_const("i32"), sizeof(i32)};
|
||||
static gb_read_only type_t type__i64 = {type_kind_i64, s8_const("i64"), sizeof(i64)};
|
||||
static gb_read_only type_t type__u8 = {type_kind_u8, s8_const("u8"), sizeof(u8)};
|
||||
static gb_read_only type_t type__u16 = {type_kind_u16, s8_const("u16"), sizeof(u16)};
|
||||
static gb_read_only type_t type__u32 = {type_kind_u32, s8_const("u32"), sizeof(u32)};
|
||||
static gb_read_only type_t type__u64 = {type_kind_u64, s8_const("u64"), sizeof(u64)};
|
||||
static gb_read_only type_t type__b8 = {type_kind_b8, s8_const("b8"), sizeof(b8)};
|
||||
static gb_read_only type_t type__b16 = {type_kind_b16, s8_const("b16"), sizeof(b16)};
|
||||
static gb_read_only type_t type__b32 = {type_kind_b32, s8_const("b32"), sizeof(b32)};
|
||||
static gb_read_only type_t type__b64 = {type_kind_b64, s8_const("b64"), sizeof(b64)};
|
||||
static gb_read_only type_t type__f32 = {type_kind_f32, s8_const("f32"), sizeof(f32)};
|
||||
static gb_read_only type_t type__f64 = {type_kind_f64, s8_const("f64"), sizeof(f64)};
|
||||
static gb_read_only type_t type__isize = {type_kind_isize, s8_const("isize"), sizeof(isize)};
|
||||
static gb_read_only type_t type__usize = {type_kind_usize, s8_const("usize"), sizeof(usize)};
|
||||
static gb_read_only type_t type__int = {type_kind_int, s8_const("int"), sizeof(int)};
|
||||
static gb_read_only type_t type__char = {type_kind_char, s8_const("char"), sizeof(char)};
|
||||
static gb_read_only type_t type__void = {type_kind_void, s8_const("void")};
|
||||
|
||||
static gb_read_only type_t type__s8_t = { type_kind_struct, s8_const("s8_t"), sizeof(s8_t), .count = 2,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("str"), &POINTER(char), .offset = offsetof(s8_t, str)},
|
||||
{s8_const("len"), &type__i64, .offset = offsetof(s8_t, len)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_t type__s16_t = { type_kind_struct, s8_const("s16_t"), sizeof(s16_t), .count = 2,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("str"), &POINTER(u16), .offset = offsetof(s16_t, str)},
|
||||
{s8_const("len"), &type__i64, .offset = offsetof(s16_t, len)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_t type__s32_t = { type_kind_struct, s8_const("s32_t"), sizeof(s32_t), .count = 2,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("str"), &POINTER(u32), .offset = offsetof(s32_t, str)},
|
||||
{s8_const("len"), &type__i64, .offset = offsetof(s32_t, len)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_t type__ma_arena_t = { type_kind_struct, s8_const("ma_arena_t"), sizeof(ma_arena_t), .count = 6,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("data"), &POINTER(u8), .offset = offsetof(ma_arena_t, data)},
|
||||
{s8_const("len"), &type__usize, .offset = offsetof(ma_arena_t, len)},
|
||||
{s8_const("base_len"), &type__usize, .offset = offsetof(ma_arena_t, base_len)},
|
||||
{s8_const("reserve"), &type__usize, .offset = offsetof(ma_arena_t, reserve)},
|
||||
{s8_const("commit"), &type__usize, .offset = offsetof(ma_arena_t, commit)},
|
||||
{s8_const("align"), &type__usize, .offset = offsetof(ma_arena_t, align)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_t type__sb8_node_t = {type_kind_struct, s8_const("sb8_node_t"), sizeof(sb8_t), .count = 2,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("next"), &POINTER(sb8_node_t), .offset = offsetof(sb8_node_t, next)},
|
||||
{s8_const("string"), &type__s8_t, .offset = offsetof(sb8_node_t, string)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_t type__sb8_t = { type_kind_struct, s8_const("sb8_t"), sizeof(sb8_t), .count = 4,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("arena"), &POINTER(ma_arena_t), .offset = offsetof(sb8_t, arena)},
|
||||
{s8_const("first"), &POINTER(sb8_node_t), .offset = offsetof(sb8_t, first)},
|
||||
{s8_const("last"), &POINTER(sb8_node_t), .offset = offsetof(sb8_t, last)},
|
||||
{s8_const("indent"), &type__i32, .offset = offsetof(sb8_t, indent)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_t type__ma_temp_t = { type_kind_struct, s8_const("ma_temp_t"), sizeof(ma_temp_t), .count = 2,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("arena"), &POINTER(ma_arena_t), .offset = offsetof(ma_temp_t, arena)},
|
||||
{s8_const("len"), &type__usize, .offset = offsetof(ma_temp_t, len)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_t type__v2f32_t = { type_kind_struct, s8_const("v2f32_t"), sizeof(v2f32_t), .count = 2,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("x"), &type__f32, .offset = offsetof(v2f32_t, x)},
|
||||
{s8_const("y"), &type__f32, .offset = offsetof(v2f32_t, y)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_t type__v3f32_t = { type_kind_struct, s8_const("v3f32_t"), sizeof(v3f32_t), .count = 3,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("x"), &type__f32, .offset = offsetof(v3f32_t, x)},
|
||||
{s8_const("y"), &type__f32, .offset = offsetof(v3f32_t, y)},
|
||||
{s8_const("z"), &type__f32, .offset = offsetof(v3f32_t, z)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_t type__v4f32_t = { type_kind_struct, s8_const("v4f32_t"), sizeof(v4f32_t), .count = 4,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("x"), &type__f32, .offset = offsetof(v4f32_t, x)},
|
||||
{s8_const("y"), &type__f32, .offset = offsetof(v4f32_t, y)},
|
||||
{s8_const("z"), &type__f32, .offset = offsetof(v4f32_t, z)},
|
||||
{s8_const("w"), &type__f32, .offset = offsetof(v4f32_t, w)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_t type__r3f32_t = { type_kind_struct, s8_const("r3f32_t"), sizeof(r3f32_t), .count = 6,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("x0"), &type__f32, .offset = offsetof(r3f32_t, x0)},
|
||||
{s8_const("y0"), &type__f32, .offset = offsetof(r3f32_t, y0)},
|
||||
{s8_const("x0"), &type__f32, .offset = offsetof(r3f32_t, z0)},
|
||||
{s8_const("x1"), &type__f32, .offset = offsetof(r3f32_t, x1)},
|
||||
{s8_const("y1"), &type__f32, .offset = offsetof(r3f32_t, y1)},
|
||||
{s8_const("x1"), &type__f32, .offset = offsetof(r3f32_t, z1)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_member_t members__r2f32_t[] = {
|
||||
{s8_const("x0"), &type__f32, .offset = offsetof(r2f32_t, x0)},
|
||||
{s8_const("y0"), &type__f32, .offset = offsetof(r2f32_t, y0)},
|
||||
{s8_const("x1"), &type__f32, .offset = offsetof(r2f32_t, x1)},
|
||||
{s8_const("y1"), &type__f32, .offset = offsetof(r2f32_t, y1)},
|
||||
};
|
||||
static gb_read_only DEFINE_STRUCT(r2f32_t);
|
||||
|
||||
static gb_read_only type_member_t members__r1f32_t[] = {
|
||||
{s8_const("x0"), &type__f32, .offset = offsetof(r1f32_t, x0)},
|
||||
{s8_const("x1"), &type__f32, .offset = offsetof(r1f32_t, x1)},
|
||||
};
|
||||
static gb_read_only DEFINE_STRUCT(r1f32_t);
|
||||
|
||||
static gb_read_only type_t type__v2f64_t = { type_kind_struct, s8_const("v2f64_t"), sizeof(v2f64_t), .count = 2,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("x"), &type__f64, .offset = offsetof(v2f64_t, x)},
|
||||
{s8_const("y"), &type__f64, .offset = offsetof(v2f64_t, y)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_t type__v3f64_t = { type_kind_struct, s8_const("v3f64_t"), sizeof(v3f64_t), .count = 3,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("x"), &type__f64, .offset = offsetof(v3f64_t, x)},
|
||||
{s8_const("y"), &type__f64, .offset = offsetof(v3f64_t, y)},
|
||||
{s8_const("z"), &type__f64, .offset = offsetof(v3f64_t, z)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_t type__v4f64_t = { type_kind_struct, s8_const("v4f64_t"), sizeof(v4f64_t), .count = 4,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("x"), &type__f64, .offset = offsetof(v4f64_t, x)},
|
||||
{s8_const("y"), &type__f64, .offset = offsetof(v4f64_t, y)},
|
||||
{s8_const("z"), &type__f64, .offset = offsetof(v4f64_t, z)},
|
||||
{s8_const("w"), &type__f64, .offset = offsetof(v4f64_t, w)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_t type__r3f64_t = { type_kind_struct, s8_const("r3f64_t"), sizeof(r3f64_t), .count = 6,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("x0"), &type__f64, .offset = offsetof(r3f64_t, x0)},
|
||||
{s8_const("y0"), &type__f64, .offset = offsetof(r3f64_t, y0)},
|
||||
{s8_const("x0"), &type__f64, .offset = offsetof(r3f64_t, z0)},
|
||||
{s8_const("x1"), &type__f64, .offset = offsetof(r3f64_t, x1)},
|
||||
{s8_const("y1"), &type__f64, .offset = offsetof(r3f64_t, y1)},
|
||||
{s8_const("x1"), &type__f64, .offset = offsetof(r3f64_t, z1)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_member_t members__r2f64_t[] = {
|
||||
{s8_const("x0"), &type__f64, .offset = offsetof(r2f64_t, x0)},
|
||||
{s8_const("y0"), &type__f64, .offset = offsetof(r2f64_t, y0)},
|
||||
{s8_const("x1"), &type__f64, .offset = offsetof(r2f64_t, x1)},
|
||||
{s8_const("y1"), &type__f64, .offset = offsetof(r2f64_t, y1)},
|
||||
};
|
||||
static gb_read_only DEFINE_STRUCT(r2f64_t);
|
||||
|
||||
static gb_read_only type_member_t members__r1f64_t[] = {
|
||||
{s8_const("x0"), &type__f64, .offset = offsetof(r1f64_t, x0)},
|
||||
{s8_const("x1"), &type__f64, .offset = offsetof(r1f64_t, x1)},
|
||||
};
|
||||
static gb_read_only DEFINE_STRUCT(r1f64_t);
|
||||
|
||||
static gb_read_only type_t type__v2i32_t = { type_kind_struct, s8_const("v2i32_t"), sizeof(v2i32_t), .count = 2,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("x"), &type__i32, .offset = offsetof(v2i32_t, x)},
|
||||
{s8_const("y"), &type__i32, .offset = offsetof(v2i32_t, y)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_t type__v3i32_t = { type_kind_struct, s8_const("v3i32_t"), sizeof(v3i32_t), .count = 3,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("x"), &type__i32, .offset = offsetof(v3i32_t, x)},
|
||||
{s8_const("y"), &type__i32, .offset = offsetof(v3i32_t, y)},
|
||||
{s8_const("z"), &type__i32, .offset = offsetof(v3i32_t, z)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_t type__v4i32_t = { type_kind_struct, s8_const("v4i32_t"), sizeof(v4i32_t), .count = 4,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("x"), &type__i32, .offset = offsetof(v4i32_t, x)},
|
||||
{s8_const("y"), &type__i32, .offset = offsetof(v4i32_t, y)},
|
||||
{s8_const("z"), &type__i32, .offset = offsetof(v4i32_t, z)},
|
||||
{s8_const("w"), &type__i32, .offset = offsetof(v4i32_t, w)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_t type__r3i32_t = { type_kind_struct, s8_const("r3i32_t"), sizeof(r3i32_t), .count = 6,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("x0"), &type__i32, .offset = offsetof(r3i32_t, x0)},
|
||||
{s8_const("y0"), &type__i32, .offset = offsetof(r3i32_t, y0)},
|
||||
{s8_const("x0"), &type__i32, .offset = offsetof(r3i32_t, z0)},
|
||||
{s8_const("x1"), &type__i32, .offset = offsetof(r3i32_t, x1)},
|
||||
{s8_const("y1"), &type__i32, .offset = offsetof(r3i32_t, y1)},
|
||||
{s8_const("x1"), &type__i32, .offset = offsetof(r3i32_t, z1)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_member_t members__r2i32_t[] = {
|
||||
{s8_const("x0"), &type__i32, .offset = offsetof(r2i32_t, x0)},
|
||||
{s8_const("y0"), &type__i32, .offset = offsetof(r2i32_t, y0)},
|
||||
{s8_const("x1"), &type__i32, .offset = offsetof(r2i32_t, x1)},
|
||||
{s8_const("y1"), &type__i32, .offset = offsetof(r2i32_t, y1)},
|
||||
};
|
||||
static gb_read_only DEFINE_STRUCT(r2i32_t);
|
||||
|
||||
static gb_read_only type_member_t members__r1i32_t[] = {
|
||||
{s8_const("x0"), &type__i32, .offset = offsetof(r1i32_t, x0)},
|
||||
{s8_const("x1"), &type__i32, .offset = offsetof(r1i32_t, x1)},
|
||||
};
|
||||
static gb_read_only DEFINE_STRUCT(r1i32_t);
|
||||
|
||||
|
||||
static gb_read_only type_t type__v2i64_t = { type_kind_struct, s8_const("v2i64_t"), sizeof(v2i64_t), .count = 2,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("x"), &type__i64, .offset = offsetof(v2i64_t, x)},
|
||||
{s8_const("y"), &type__i64, .offset = offsetof(v2i64_t, y)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_t type__v3i64_t = { type_kind_struct, s8_const("v3i64_t"), sizeof(v3i64_t), .count = 3,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("x"), &type__i64, .offset = offsetof(v3i64_t, x)},
|
||||
{s8_const("y"), &type__i64, .offset = offsetof(v3i64_t, y)},
|
||||
{s8_const("z"), &type__i64, .offset = offsetof(v3i64_t, z)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_t type__v4i64_t = { type_kind_struct, s8_const("v4i64_t"), sizeof(v4i64_t), .count = 4,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("x"), &type__i64, .offset = offsetof(v4i64_t, x)},
|
||||
{s8_const("y"), &type__i64, .offset = offsetof(v4i64_t, y)},
|
||||
{s8_const("z"), &type__i64, .offset = offsetof(v4i64_t, z)},
|
||||
{s8_const("w"), &type__i64, .offset = offsetof(v4i64_t, w)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_t type__r3i64_t = { type_kind_struct, s8_const("r3i64_t"), sizeof(r3i64_t), .count = 6,
|
||||
.members = (type_member_t[]){
|
||||
{s8_const("x0"), &type__i64, .offset = offsetof(r3i64_t, x0)},
|
||||
{s8_const("y0"), &type__i64, .offset = offsetof(r3i64_t, y0)},
|
||||
{s8_const("x0"), &type__i64, .offset = offsetof(r3i64_t, z0)},
|
||||
{s8_const("x1"), &type__i64, .offset = offsetof(r3i64_t, x1)},
|
||||
{s8_const("y1"), &type__i64, .offset = offsetof(r3i64_t, y1)},
|
||||
{s8_const("x1"), &type__i64, .offset = offsetof(r3i64_t, z1)},
|
||||
}
|
||||
};
|
||||
|
||||
static gb_read_only type_member_t members__r2i64_t[] = {
|
||||
{s8_const("x0"), &type__i64, .offset = offsetof(r2i64_t, x0)},
|
||||
{s8_const("y0"), &type__i64, .offset = offsetof(r2i64_t, y0)},
|
||||
{s8_const("x1"), &type__i64, .offset = offsetof(r2i64_t, x1)},
|
||||
{s8_const("y1"), &type__i64, .offset = offsetof(r2i64_t, y1)},
|
||||
};
|
||||
static gb_read_only DEFINE_STRUCT(r2i64_t);
|
||||
|
||||
static gb_read_only type_member_t members__r1i64_t[] = {
|
||||
{s8_const("x0"), &type__i64, .offset = offsetof(r1i64_t, x0)},
|
||||
{s8_const("x1"), &type__i64, .offset = offsetof(r1i64_t, x1)},
|
||||
};
|
||||
static gb_read_only DEFINE_STRUCT(r1i64_t);
|
||||
@@ -1,199 +0,0 @@
|
||||
#include "core_unicode.h"
|
||||
|
||||
fn utf32_result_t utf16_to_utf32(uint16_t *c, int max_advance) {
|
||||
utf32_result_t result = {0};
|
||||
if (max_advance >= 1) {
|
||||
result.advance = 1;
|
||||
result.out_str = c[0];
|
||||
if (c[0] >= 0xD800 && c[0] <= 0xDBFF && c[1] >= 0xDC00 && c[1] <= 0xDFFF) {
|
||||
if (max_advance >= 2) {
|
||||
result.out_str = 0x10000;
|
||||
result.out_str += (uint32_t)(c[0] & 0x03FF) << 10u | (c[1] & 0x03FF);
|
||||
result.advance = 2;
|
||||
} else {
|
||||
result.error = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
result.error = 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn utf8_result_t utf32_to_utf8(uint32_t codepoint) {
|
||||
utf8_result_t result = {0};
|
||||
|
||||
if (codepoint <= 0x7F) {
|
||||
result.len = 1;
|
||||
result.out_str[0] = (char)codepoint;
|
||||
}
|
||||
else if (codepoint <= 0x7FF) {
|
||||
result.len = 2;
|
||||
result.out_str[0] = 0xc0 | (0x1f & (codepoint >> 6));
|
||||
result.out_str[1] = 0x80 | (0x3f & codepoint);
|
||||
}
|
||||
else if (codepoint <= 0xFFFF) { // 16 bit word
|
||||
result.len = 3;
|
||||
result.out_str[0] = 0xe0 | (0xf & (codepoint >> 12)); // 4 bits
|
||||
result.out_str[1] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits
|
||||
result.out_str[2] = 0x80 | (0x3f & codepoint); // 6 bits
|
||||
}
|
||||
else if (codepoint <= 0x10FFFF) { // 21 bit word
|
||||
result.len = 4;
|
||||
result.out_str[0] = 0xf0 | (0x7 & (codepoint >> 18)); // 3 bits
|
||||
result.out_str[1] = 0x80 | (0x3f & (codepoint >> 12)); // 6 bits
|
||||
result.out_str[2] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits
|
||||
result.out_str[3] = 0x80 | (0x3f & codepoint); // 6 bits
|
||||
}
|
||||
else {
|
||||
result.error = 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn utf32_result_t utf8_to_utf32(char *c, int max_advance) {
|
||||
utf32_result_t result = {0};
|
||||
|
||||
if ((c[0] & 0x80) == 0) { // Check if leftmost zero of first byte is unset
|
||||
if (max_advance >= 1) {
|
||||
result.out_str = c[0];
|
||||
result.advance = 1;
|
||||
}
|
||||
else result.error = 1;
|
||||
}
|
||||
|
||||
else if ((c[0] & 0xe0) == 0xc0) {
|
||||
if ((c[1] & 0xc0) == 0x80) { // Continuation byte required
|
||||
if (max_advance >= 2) {
|
||||
result.out_str = (uint32_t)(c[0] & 0x1f) << 6u | (c[1] & 0x3f);
|
||||
result.advance = 2;
|
||||
}
|
||||
else result.error = 2;
|
||||
}
|
||||
else result.error = 2;
|
||||
}
|
||||
|
||||
else if ((c[0] & 0xf0) == 0xe0) {
|
||||
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80) { // Two continuation bytes required
|
||||
if (max_advance >= 3) {
|
||||
result.out_str = (uint32_t)(c[0] & 0xf) << 12u | (uint32_t)(c[1] & 0x3f) << 6u | (c[2] & 0x3f);
|
||||
result.advance = 3;
|
||||
}
|
||||
else result.error = 3;
|
||||
}
|
||||
else result.error = 3;
|
||||
}
|
||||
|
||||
else if ((c[0] & 0xf8) == 0xf0) {
|
||||
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80 && (c[3] & 0xc0) == 0x80) { // Three continuation bytes required
|
||||
if (max_advance >= 4) {
|
||||
result.out_str = (uint32_t)(c[0] & 0xf) << 18u | (uint32_t)(c[1] & 0x3f) << 12u | (uint32_t)(c[2] & 0x3f) << 6u | (uint32_t)(c[3] & 0x3f);
|
||||
result.advance = 4;
|
||||
}
|
||||
else result.error = 4;
|
||||
}
|
||||
else result.error = 4;
|
||||
}
|
||||
else result.error = 4;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn utf16_result_t utf32_to_utf16(uint32_t codepoint) {
|
||||
utf16_result_t result = {0};
|
||||
if (codepoint < 0x10000) {
|
||||
result.out_str[0] = (uint16_t)codepoint;
|
||||
result.out_str[1] = 0;
|
||||
result.len = 1;
|
||||
}
|
||||
else if (codepoint <= 0x10FFFF) {
|
||||
uint32_t code = (codepoint - 0x10000);
|
||||
result.out_str[0] = (uint16_t)(0xD800 | (code >> 10));
|
||||
result.out_str[1] = (uint16_t)(0xDC00 | (code & 0x3FF));
|
||||
result.len = 2;
|
||||
}
|
||||
else {
|
||||
result.error = 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#define UTF__HANDLE_DECODE_ERROR(question_mark) \
|
||||
{ \
|
||||
if (outlen < buffer_size - 1) buffer[outlen++] = (question_mark); \
|
||||
break; \
|
||||
}
|
||||
|
||||
fn int64_t str_from_wstr(char *buffer, int64_t buffer_size, uint16_t *in, int64_t inlen) {
|
||||
int64_t outlen = 0;
|
||||
for (int64_t i = 0; i < inlen && in[i];) {
|
||||
utf32_result_t decode = utf16_to_utf32((uint16_t *)(in + i), (int)(inlen - i));
|
||||
if (!decode.error) {
|
||||
i += decode.advance;
|
||||
utf8_result_t encode = utf32_to_utf8(decode.out_str);
|
||||
if (!encode.error) {
|
||||
for (int64_t j = 0; j < encode.len; j++) {
|
||||
if (outlen < buffer_size - 1) {
|
||||
buffer[outlen++] = encode.out_str[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
else UTF__HANDLE_DECODE_ERROR('?');
|
||||
}
|
||||
else UTF__HANDLE_DECODE_ERROR('?');
|
||||
}
|
||||
|
||||
buffer[outlen] = 0;
|
||||
return outlen;
|
||||
}
|
||||
|
||||
fn int64_t wstr_from_str(uint16_t *buffer, int64_t buffer_size, char *in, int64_t inlen) {
|
||||
int64_t outlen = 0;
|
||||
for (int64_t i = 0; i < inlen;) {
|
||||
utf32_result_t decode = utf8_to_utf32(in + i, (int)(inlen - i));
|
||||
if (!decode.error) {
|
||||
i += decode.advance;
|
||||
utf16_result_t encode = utf32_to_utf16(decode.out_str);
|
||||
if (!encode.error) {
|
||||
for (int64_t j = 0; j < encode.len; j++) {
|
||||
if (outlen < buffer_size - 1) {
|
||||
buffer[outlen++] = encode.out_str[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
else UTF__HANDLE_DECODE_ERROR(0x003f);
|
||||
}
|
||||
else UTF__HANDLE_DECODE_ERROR(0x003f);
|
||||
}
|
||||
|
||||
buffer[outlen] = 0;
|
||||
return outlen;
|
||||
}
|
||||
|
||||
fn void utf8_advance(utf8_iter_t *iter) {
|
||||
iter->i += iter->utf8_codepoint_byte_size;
|
||||
utf32_result_t r = utf8_to_utf32(iter->str + iter->i, iter->len - iter->i);
|
||||
if (r.error) {
|
||||
iter->item = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
iter->utf8_codepoint_byte_size = r.advance;
|
||||
iter->item = r.out_str;
|
||||
}
|
||||
|
||||
// @todo: s8_t, change name
|
||||
fn utf8_iter_t utf8_iterate_ex(char *str, int len) {
|
||||
utf8_iter_t result = {str, len};
|
||||
if (len) utf8_advance(&result);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn utf8_iter_t utf8_iterate(char *str) {
|
||||
int length = 0;
|
||||
while (str[length]) length += 1;
|
||||
return utf8_iterate_ex(str, length);
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
#pragma once
|
||||
#include "core_basic.h"
|
||||
|
||||
typedef struct utf32_result_t utf32_result_t;
|
||||
struct utf32_result_t {
|
||||
uint32_t out_str;
|
||||
int advance;
|
||||
int error;
|
||||
};
|
||||
|
||||
typedef struct utf16_result_t utf16_result_t;
|
||||
struct utf16_result_t {
|
||||
uint16_t out_str[2];
|
||||
int len;
|
||||
int error;
|
||||
};
|
||||
|
||||
typedef struct utf8_result_t utf8_result_t;
|
||||
struct utf8_result_t {
|
||||
uint8_t out_str[4];
|
||||
int len;
|
||||
int error;
|
||||
};
|
||||
|
||||
typedef struct utf8_iter_t utf8_iter_t;
|
||||
struct utf8_iter_t {
|
||||
char *str;
|
||||
int len;
|
||||
int utf8_codepoint_byte_size;
|
||||
int i;
|
||||
uint32_t item;
|
||||
};
|
||||
|
||||
//
|
||||
// codepoint conversions
|
||||
fn utf32_result_t utf16_to_utf32(uint16_t *c, int max_advance);
|
||||
fn utf8_result_t utf32_to_utf8(uint32_t codepoint);
|
||||
fn utf32_result_t utf8_to_utf32(char *c, int max_advance);
|
||||
fn utf16_result_t utf32_to_utf16(uint32_t codepoint);
|
||||
|
||||
//
|
||||
// widestring / string conversions
|
||||
fn int64_t str_from_wstr(char *buffer, int64_t buffer_size, uint16_t *in, int64_t inlen);
|
||||
fn int64_t wstr_from_str(uint16_t *buffer, int64_t buffer_size, char *in, int64_t inlen);
|
||||
|
||||
//
|
||||
// codepoint iterator
|
||||
fn utf8_iter_t utf8_iterate(char *str);
|
||||
fn utf8_iter_t utf8_iterate_ex(char *str, int len);
|
||||
fn void utf8_advance(utf8_iter_t *iter);
|
||||
/* example:
|
||||
for (utf8_iter_t iter = utf8_iterate(string); iter.item; utf8_advance(&iter)) {
|
||||
u32 codepoint = iter.item;
|
||||
}
|
||||
|
||||
*/
|
||||
@@ -1,3 +0,0 @@
|
||||
#define STB_SPRINTF_IMPLEMENTATION
|
||||
#include "stb_sprintf.h"
|
||||
#undef STB_SPRINTF_IMPLEMENTATION
|
||||
File diff suppressed because it is too large
Load Diff
Vendored
+1819
File diff suppressed because it is too large
Load Diff
Vendored
+3655
File diff suppressed because it is too large
Load Diff
Vendored
+311
@@ -0,0 +1,311 @@
|
||||
#ifndef __khrplatform_h_
|
||||
#define __khrplatform_h_
|
||||
|
||||
/*
|
||||
** Copyright (c) 2008-2018 The Khronos Group Inc.
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a
|
||||
** copy of this software and/or associated documentation files (the
|
||||
** "Materials"), to deal in the Materials without restriction, including
|
||||
** without limitation the rights to use, copy, modify, merge, publish,
|
||||
** distribute, sublicense, and/or sell copies of the Materials, and to
|
||||
** permit persons to whom the Materials are 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 Materials.
|
||||
**
|
||||
** THE MATERIALS ARE 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
|
||||
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
*/
|
||||
|
||||
/* Khronos platform-specific types and definitions.
|
||||
*
|
||||
* The master copy of khrplatform.h is maintained in the Khronos EGL
|
||||
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
|
||||
* The last semantic modification to khrplatform.h was at commit ID:
|
||||
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
|
||||
*
|
||||
* Adopters may modify this file to suit their platform. Adopters are
|
||||
* encouraged to submit platform specific modifications to the Khronos
|
||||
* group so that they can be included in future versions of this file.
|
||||
* Please submit changes by filing pull requests or issues on
|
||||
* the EGL Registry repository linked above.
|
||||
*
|
||||
*
|
||||
* See the Implementer's Guidelines for information about where this file
|
||||
* should be located on your system and for more details of its use:
|
||||
* http://www.khronos.org/registry/implementers_guide.pdf
|
||||
*
|
||||
* This file should be included as
|
||||
* #include <KHR/khrplatform.h>
|
||||
* by Khronos client API header files that use its types and defines.
|
||||
*
|
||||
* The types in khrplatform.h should only be used to define API-specific types.
|
||||
*
|
||||
* Types defined in khrplatform.h:
|
||||
* khronos_int8_t signed 8 bit
|
||||
* khronos_uint8_t unsigned 8 bit
|
||||
* khronos_int16_t signed 16 bit
|
||||
* khronos_uint16_t unsigned 16 bit
|
||||
* khronos_int32_t signed 32 bit
|
||||
* khronos_uint32_t unsigned 32 bit
|
||||
* khronos_int64_t signed 64 bit
|
||||
* khronos_uint64_t unsigned 64 bit
|
||||
* khronos_intptr_t signed same number of bits as a pointer
|
||||
* khronos_uintptr_t unsigned same number of bits as a pointer
|
||||
* khronos_ssize_t signed size
|
||||
* khronos_usize_t unsigned size
|
||||
* khronos_float_t signed 32 bit floating point
|
||||
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
|
||||
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
|
||||
* nanoseconds
|
||||
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
|
||||
* khronos_boolean_enum_t enumerated boolean type. This should
|
||||
* only be used as a base type when a client API's boolean type is
|
||||
* an enum. Client APIs which use an integer or other type for
|
||||
* booleans cannot use this as the base type for their boolean.
|
||||
*
|
||||
* Tokens defined in khrplatform.h:
|
||||
*
|
||||
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
|
||||
*
|
||||
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
|
||||
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
|
||||
*
|
||||
* Calling convention macros defined in this file:
|
||||
* KHRONOS_APICALL
|
||||
* KHRONOS_APIENTRY
|
||||
* KHRONOS_APIATTRIBUTES
|
||||
*
|
||||
* These may be used in function prototypes as:
|
||||
*
|
||||
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
|
||||
* int arg1,
|
||||
* int arg2) KHRONOS_APIATTRIBUTES;
|
||||
*/
|
||||
|
||||
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
|
||||
# define KHRONOS_STATIC 1
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APICALL
|
||||
*-------------------------------------------------------------------------
|
||||
* This precedes the return type of the function in the function prototype.
|
||||
*/
|
||||
#if defined(KHRONOS_STATIC)
|
||||
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
|
||||
* header compatible with static linking. */
|
||||
# define KHRONOS_APICALL
|
||||
#elif defined(_WIN32)
|
||||
# define KHRONOS_APICALL __declspec(dllimport)
|
||||
#elif defined (__SYMBIAN32__)
|
||||
# define KHRONOS_APICALL IMPORT_C
|
||||
#elif defined(__ANDROID__)
|
||||
# define KHRONOS_APICALL __attribute__((visibility("default")))
|
||||
#else
|
||||
# define KHRONOS_APICALL
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIENTRY
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the return type of the function and precedes the function
|
||||
* name in the function prototype.
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
|
||||
/* Win32 but not WinCE */
|
||||
# define KHRONOS_APIENTRY __stdcall
|
||||
#else
|
||||
# define KHRONOS_APIENTRY
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIATTRIBUTES
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the closing parenthesis of the function prototype arguments.
|
||||
*/
|
||||
#if defined (__ARMCC_2__)
|
||||
#define KHRONOS_APIATTRIBUTES __softfp
|
||||
#else
|
||||
#define KHRONOS_APIATTRIBUTES
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* basic type definitions
|
||||
*-----------------------------------------------------------------------*/
|
||||
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
|
||||
|
||||
|
||||
/*
|
||||
* Using <stdint.h>
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
/*
|
||||
* To support platform where unsigned long cannot be used interchangeably with
|
||||
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
|
||||
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
|
||||
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
|
||||
* unsigned long long or similar (this results in different C++ name mangling).
|
||||
* To avoid changes for existing platforms, we restrict usage of intptr_t to
|
||||
* platforms where the size of a pointer is larger than the size of long.
|
||||
*/
|
||||
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
|
||||
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
|
||||
#define KHRONOS_USE_INTPTR_T
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif defined(__VMS ) || defined(__sgi)
|
||||
|
||||
/*
|
||||
* Using <inttypes.h>
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
||||
|
||||
/*
|
||||
* Win32
|
||||
*/
|
||||
typedef __int32 khronos_int32_t;
|
||||
typedef unsigned __int32 khronos_uint32_t;
|
||||
typedef __int64 khronos_int64_t;
|
||||
typedef unsigned __int64 khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(__sun__) || defined(__digital__)
|
||||
|
||||
/*
|
||||
* Sun or Digital
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#if defined(__arch64__) || defined(_LP64)
|
||||
typedef long int khronos_int64_t;
|
||||
typedef unsigned long int khronos_uint64_t;
|
||||
#else
|
||||
typedef long long int khronos_int64_t;
|
||||
typedef unsigned long long int khronos_uint64_t;
|
||||
#endif /* __arch64__ */
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif 0
|
||||
|
||||
/*
|
||||
* Hypothetical platform with no float or int64 support
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#define KHRONOS_SUPPORT_INT64 0
|
||||
#define KHRONOS_SUPPORT_FLOAT 0
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Generic fallback
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Types that are (so far) the same on all platforms
|
||||
*/
|
||||
typedef signed char khronos_int8_t;
|
||||
typedef unsigned char khronos_uint8_t;
|
||||
typedef signed short int khronos_int16_t;
|
||||
typedef unsigned short int khronos_uint16_t;
|
||||
|
||||
/*
|
||||
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
||||
* to be the only LLP64 architecture in current use.
|
||||
*/
|
||||
#ifdef KHRONOS_USE_INTPTR_T
|
||||
typedef intptr_t khronos_intptr_t;
|
||||
typedef uintptr_t khronos_uintptr_t;
|
||||
#elif defined(_WIN64)
|
||||
typedef signed long long int khronos_intptr_t;
|
||||
typedef unsigned long long int khronos_uintptr_t;
|
||||
#else
|
||||
typedef signed long int khronos_intptr_t;
|
||||
typedef unsigned long int khronos_uintptr_t;
|
||||
#endif
|
||||
|
||||
#if defined(_WIN64)
|
||||
typedef signed long long int khronos_ssize_t;
|
||||
typedef unsigned long long int khronos_usize_t;
|
||||
#else
|
||||
typedef signed long int khronos_ssize_t;
|
||||
typedef unsigned long int khronos_usize_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_FLOAT
|
||||
/*
|
||||
* Float type
|
||||
*/
|
||||
typedef float khronos_float_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_INT64
|
||||
/* Time types
|
||||
*
|
||||
* These types can be used to represent a time interval in nanoseconds or
|
||||
* an absolute Unadjusted System Time. Unadjusted System Time is the number
|
||||
* of nanoseconds since some arbitrary system event (e.g. since the last
|
||||
* time the system booted). The Unadjusted System Time is an unsigned
|
||||
* 64 bit value that wraps back to 0 every 584 years. Time intervals
|
||||
* may be either signed or unsigned.
|
||||
*/
|
||||
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
|
||||
typedef khronos_int64_t khronos_stime_nanoseconds_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Dummy value used to pad enum types to 32 bits.
|
||||
*/
|
||||
#ifndef KHRONOS_MAX_ENUM
|
||||
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enumerated boolean type
|
||||
*
|
||||
* Values other than zero should be considered to be true. Therefore
|
||||
* comparisons should not be made against KHRONOS_TRUE.
|
||||
*/
|
||||
typedef enum {
|
||||
KHRONOS_FALSE = 0,
|
||||
KHRONOS_TRUE = 1,
|
||||
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
|
||||
} khronos_boolean_enum_t;
|
||||
|
||||
#endif /* __khrplatform_h_ */
|
||||
Vendored
+2023
File diff suppressed because it is too large
Load Diff
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#include "stb_truetype.h"
|
||||
Vendored
+5077
File diff suppressed because it is too large
Load Diff
-654
@@ -1,654 +0,0 @@
|
||||
#include "os.h"
|
||||
#include "core/core_string16.h"
|
||||
#include "core/core_platform.h"
|
||||
#include "core/core_log.h"
|
||||
|
||||
///////////////////////////////////
|
||||
#if PLATFORM_WINDOWS
|
||||
#define NOMINMAX
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <DbgHelp.h>
|
||||
#include <TlHelp32.h>
|
||||
#include <Shlobj.h>
|
||||
///////////////////////////////
|
||||
fn os_date_t os_local_time(void) {
|
||||
SYSTEMTIME lt;
|
||||
GetLocalTime(<);
|
||||
os_date_t result = {0};
|
||||
result.ms = lt.wMilliseconds;
|
||||
result.sec = lt.wSecond;
|
||||
result.min = lt.wMinute;
|
||||
result.hour = lt.wHour;
|
||||
result.day = lt.wDay;
|
||||
result.month = lt.wMonth;
|
||||
result.year = lt.wYear;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn os_date_t os_universal_time(void) {
|
||||
SYSTEMTIME lt;
|
||||
GetSystemTime(<);
|
||||
os_date_t result = {0};
|
||||
result.ms = lt.wMilliseconds;
|
||||
result.sec = lt.wSecond;
|
||||
result.min = lt.wMinute;
|
||||
result.hour = lt.wHour;
|
||||
result.day = lt.wDay;
|
||||
result.month = lt.wMonth;
|
||||
result.year = lt.wYear;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int64_t win32_counts_per_second;
|
||||
fn f64 os_seconds(void) {
|
||||
if (win32_counts_per_second == 0) {
|
||||
LARGE_INTEGER freq;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
win32_counts_per_second = freq.QuadPart;
|
||||
}
|
||||
|
||||
LARGE_INTEGER time;
|
||||
QueryPerformanceCounter(&time);
|
||||
f64 result = (f64)time.QuadPart / (f64)win32_counts_per_second;
|
||||
return result;
|
||||
}
|
||||
|
||||
// @todo: revise these conversions
|
||||
fn f64 os_get_microseconds(void) {
|
||||
f64 secs = os_seconds();
|
||||
f64 result = secs * 1000000.0;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn f64 os_milliseconds(void) {
|
||||
f64 secs = os_seconds();
|
||||
f64 result = secs * 1000.0;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn void os_sleep(u32 milliseconds) {
|
||||
Sleep(milliseconds);
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
// files
|
||||
fn b32 os_copy(s8_t from, s8_t to, b32 overwrite) {
|
||||
BOOL fail_if_exists = true;
|
||||
if (overwrite) fail_if_exists = false;
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s16_t from16 = s16_from_s8(scratch.arena, from);
|
||||
s16_t to16 = s16_from_s8(scratch.arena, to);
|
||||
BOOL success = CopyFileW(from16.str, to16.str, fail_if_exists);
|
||||
ma_end_scratch(scratch);
|
||||
return success ? true : false;
|
||||
}
|
||||
|
||||
fn b32 os_delete(s8_t path) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s16_t path16 = s16_from_s8(scratch.arena, path);
|
||||
BOOL success = DeleteFileW(path16.str);
|
||||
ma_end_scratch(scratch);
|
||||
return success ? true : false;
|
||||
}
|
||||
|
||||
fn os_mkdir_t os_mkdir(s8_t path) {
|
||||
os_mkdir_t result = os_mkdir_success;
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s16_t path16 = s16_from_s8(scratch.arena, path);
|
||||
BOOL success = CreateDirectoryW(path16.str, NULL);
|
||||
if (success == 0) {
|
||||
DWORD error = GetLastError();
|
||||
if (error == ERROR_ALREADY_EXISTS) result = os_mkdir_file_exists;
|
||||
else if (error == ERROR_PATH_NOT_FOUND) result = os_mkdir_path_not_found;
|
||||
else result = os_mkdir_other_error;
|
||||
}
|
||||
ma_end_scratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn os_write_t os_write(s8_t path, s8_t content) {
|
||||
os_write_t result = os_write_other_error;
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s16_t path16 = s16_from_s8(scratch.arena, path);
|
||||
HANDLE handle = CreateFileW(path16.str, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
assert_expr(content.len == (DWORD)content.len);
|
||||
DWORD bytes_written = 0;
|
||||
BOOL error = WriteFile(handle, content.str, (DWORD)content.len, &bytes_written, NULL);
|
||||
if (error == TRUE) {
|
||||
if (bytes_written == content.len) {
|
||||
result = os_write_success;
|
||||
}
|
||||
}
|
||||
CloseHandle(handle);
|
||||
} else result = os_write_path_not_found;
|
||||
ma_end_scratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
// @todo: improve to return error codes
|
||||
fn s8_t os_read(ma_arena_t *arena, s8_t path) {
|
||||
s8_t result = {0};
|
||||
ma_temp_t scratch = ma_begin_scratch1(arena);
|
||||
s16_t path16 = s16_from_s8(scratch.arena, path);
|
||||
HANDLE handle = CreateFileW(path16.str, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
LARGE_INTEGER file_size;
|
||||
if (GetFileSizeEx(handle, &file_size)) {
|
||||
if (file_size.QuadPart != 0) {
|
||||
result.len = file_size.QuadPart;
|
||||
result.str = ma_push_array(arena, char, result.len + 1);
|
||||
assert(result.len == (i64)(DWORD)result.len);
|
||||
DWORD read;
|
||||
if (ReadFile(handle, result.str, (DWORD)result.len, &read, NULL)) { // @todo: can only read 32 byte size files?
|
||||
if (read == result.len) {
|
||||
result.str[result.len] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
CloseHandle(handle);
|
||||
}
|
||||
ma_end_scratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct w32_file_iter_t w32_file_iter_t;
|
||||
struct w32_file_iter_t {
|
||||
HANDLE handle;
|
||||
WIN32_FIND_DATAW data;
|
||||
};
|
||||
|
||||
fn void os_advance(os_iter_t *it) {
|
||||
while (FindNextFileW(it->w32->handle, &it->w32->data) != 0) {
|
||||
WIN32_FIND_DATAW *data = &it->w32->data;
|
||||
|
||||
// Skip '.' and '..'
|
||||
if (data->cFileName[0] == '.' && data->cFileName[1] == '.' && data->cFileName[2] == 0) continue;
|
||||
if (data->cFileName[0] == '.' && data->cFileName[1] == 0) continue;
|
||||
|
||||
it->is_directory = data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
|
||||
it->name = s8_from_s16(it->arena, s16_make(data->cFileName, str16_len(data->cFileName)));
|
||||
|
||||
char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/";
|
||||
it->rel = s8_printf(it->arena, "%.*s%s%.*s", s8_fmtspec(it->path), separator, s8_fmtspec(it->name));
|
||||
it->abs = os_abs(it->arena, it->rel);
|
||||
it->is_valid = true;
|
||||
s8_normalize_path_unsafe(it->rel);
|
||||
s8_normalize_path_unsafe(it->abs);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
it->is_valid = false;
|
||||
DWORD error = GetLastError();
|
||||
assert(error == ERROR_NO_MORE_FILES);
|
||||
FindClose(it->w32->handle);
|
||||
}
|
||||
|
||||
fn os_iter_t *os_iter(ma_arena_t *arena, s8_t path) {
|
||||
os_iter_t *it = ma_push_type(arena, os_iter_t);
|
||||
it->w32 = ma_push_type(arena, w32_file_iter_t);
|
||||
it->arena = arena;
|
||||
it->path = path;
|
||||
it->is_valid = true;
|
||||
|
||||
ma_temp_t scratch = ma_begin_scratch1(arena);
|
||||
s8_t mod_path = s8_printf(scratch.arena, "%.*s\\*", s8_fmtspec(path));
|
||||
s16_t mod_path16 = s16_from_s8(scratch.arena, mod_path);
|
||||
|
||||
it->w32->handle = FindFirstFileW(mod_path16.str, &it->w32->data);
|
||||
if (it->w32->handle == INVALID_HANDLE_VALUE) {
|
||||
it->is_valid = false;
|
||||
}
|
||||
|
||||
if (it->is_valid) {
|
||||
assert(it->w32->data.cFileName[0] == '.' && it->w32->data.cFileName[1] == 0);
|
||||
os_advance(it);
|
||||
}
|
||||
|
||||
ma_end_scratch(scratch);
|
||||
return it;
|
||||
}
|
||||
|
||||
fn i64 os_mod_time(s8_t file) {
|
||||
WIN32_FIND_DATAW data;
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s16_t wpath = s16_from_s8(scratch.arena, file);
|
||||
HANDLE handle = FindFirstFileW(wpath.str, &data);
|
||||
i64 result = -1;
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
FindClose(handle);
|
||||
FILETIME time = data.ftLastWriteTime;
|
||||
result = (i64)time.dwHighDateTime << 32 | time.dwLowDateTime;
|
||||
}
|
||||
ma_end_scratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t os_abs(ma_arena_t *arena, s8_t rel) {
|
||||
const int buffer_size = 2048;
|
||||
ma_temp_t scratch = ma_begin_scratch1(arena);
|
||||
s16_t rel16 = s16_from_s8(scratch.arena, rel);
|
||||
wchar_t *buffer = ma_push_array(scratch.arena, wchar_t, buffer_size);
|
||||
DWORD written = GetFullPathNameW(rel16.str, buffer_size, buffer, 0);
|
||||
assert(written != 0);
|
||||
assert((i64)written < (i64)buffer_size);
|
||||
s8_t result = s8_from_s16(arena, s16_make(buffer, written));
|
||||
ma_end_scratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t os_exe(ma_arena_t *arena) {
|
||||
const int buffer_size = 2048;
|
||||
ma_temp_t scratch = ma_begin_scratch1(arena);
|
||||
wchar_t *buffer = ma_push_array(scratch.arena, wchar_t, buffer_size);
|
||||
DWORD wsize = GetModuleFileNameW(0, buffer, buffer_size);
|
||||
assert(wsize != 0);
|
||||
s8_t result = s8_from_s16(arena, s16_make(buffer, wsize));
|
||||
s8_normalize_path_unsafe(result);
|
||||
ma_end_scratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t os_exe_dir(ma_arena_t *arena) {
|
||||
ma_temp_t scratch = ma_begin_scratch1(arena);
|
||||
s8_t exe = os_exe(scratch.arena);
|
||||
s8_t path = s8_chop_last_slash(exe);
|
||||
s8_t result = s8_printf(arena, "%.*s", s8_fmtspec(path));
|
||||
ma_end_scratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 os_is_dir(s8_t path) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s16_t path16 = s16_from_s8(scratch.arena, path);
|
||||
DWORD dwAttrib = GetFileAttributesW(path16.str);
|
||||
ma_end_scratch(scratch);
|
||||
return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
|
||||
}
|
||||
|
||||
fn b32 os_is_file(s8_t path) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s16_t path16 = s16_from_s8(scratch.arena, path);
|
||||
DWORD dwAttrib = GetFileAttributesW(path16.str);
|
||||
ma_end_scratch(scratch);
|
||||
b32 is_file = (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0;
|
||||
return dwAttrib != INVALID_FILE_ATTRIBUTES && is_file;
|
||||
}
|
||||
|
||||
fn b32 os_is_abs(s8_t path) {
|
||||
b32 result = path.len > 3 && char_is_alphabetic(path.str[0]) && path.str[1] == ':' && path.str[2] == '/';
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 os_exists(s8_t path) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s16_t path16 = s16_from_s8(scratch.arena, path);
|
||||
DWORD attribs = GetFileAttributesW(path16.str);
|
||||
ma_end_scratch(scratch);
|
||||
return attribs == INVALID_FILE_ATTRIBUTES ? false : true;
|
||||
}
|
||||
|
||||
fn s8_t os_cwd(ma_arena_t *arena) {
|
||||
ma_temp_t scratch = ma_begin_scratch1(arena);
|
||||
const u32 buffer_size = 1024;
|
||||
wchar_t *buffer = ma_push_array(scratch.arena, wchar_t, buffer_size);
|
||||
DWORD wsize = GetCurrentDirectoryW(buffer_size, buffer);
|
||||
assert(wsize != 0);
|
||||
assert(wsize <= buffer_size);
|
||||
s8_t path = s8_from_s16(scratch.arena, s16_make(buffer, wsize));
|
||||
s8_normalize_path_unsafe(path);
|
||||
s8_t result = s8_printf(arena, "%.*s", s8_fmtspec(path));
|
||||
ma_end_scratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn void os_set_cwd(s8_t path) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s16_t path16 = s16_from_s8(scratch.arena, path);
|
||||
SetCurrentDirectoryW(path16.str);
|
||||
ma_end_scratch(scratch);
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
// threading
|
||||
|
||||
typedef struct w32_thread_t w32_thread_t;
|
||||
struct w32_thread_t {
|
||||
HANDLE handle;
|
||||
DWORD id;
|
||||
};
|
||||
|
||||
fn os_thread_t os_thread_create(ma_arena_t *arena, os_thread_fn_t *func, void *user_data) {
|
||||
assert(func);
|
||||
|
||||
LPSECURITY_ATTRIBUTES security_attribs = NULL;
|
||||
SIZE_T stack_size = 0;
|
||||
DWORD creation_flags = 0;
|
||||
DWORD thread_id = 0;
|
||||
HANDLE thread_handle = CreateThread(security_attribs, stack_size, (LPTHREAD_START_ROUTINE)func, user_data, creation_flags, &thread_id);
|
||||
if (thread_handle == NULL) {
|
||||
debugf("%s.CreatedThread failed: %d", __FUNCTION__, GetLastError());
|
||||
return (os_thread_t){.exited = true};
|
||||
}
|
||||
|
||||
os_thread_t result = {0};
|
||||
result.w32 = ma_push_type(arena, w32_thread_t);
|
||||
result.w32->handle = thread_handle;
|
||||
result.w32->id = thread_id;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn i32 os_thread_join(os_thread_t *thread) {
|
||||
DWORD return_value_indicates_event = WaitForSingleObject(thread->w32->handle, INFINITE);
|
||||
if (return_value_indicates_event != WAIT_OBJECT_0) {
|
||||
debugf("%s.WaitForSingleObject failed: %d", __FUNCTION__, GetLastError());
|
||||
}
|
||||
|
||||
DWORD exit_code = 0;
|
||||
BOOL if_fails_then_zero = GetExitCodeThread(thread->w32->handle, &exit_code);
|
||||
if (if_fails_then_zero == 0) {
|
||||
debugf("%s.GetExitCodeThread failed: %d", __FUNCTION__, GetLastError());
|
||||
} else {
|
||||
thread->exit_code = exit_code;
|
||||
}
|
||||
|
||||
CloseHandle(thread->w32->handle);
|
||||
thread->exited = true;
|
||||
return thread->exit_code;
|
||||
}
|
||||
|
||||
// @todo:
|
||||
// This will probably create 16 arenas or more XD
|
||||
// fn i32 os__thread_log(void *data) {
|
||||
// os_core_init();
|
||||
// debugf("testing");
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
// fn void os_test_threads(void) {
|
||||
// os_thread_t threads[16];
|
||||
// ma_temp_t scratch = ma_begin_scratch();
|
||||
// for (int i = 0; i < lengthof(threads); i += 1) {
|
||||
// threads[i] = os_thread_create(scratch.arena, os__thread_log, NULL);
|
||||
// }
|
||||
// for (int i = 0; i < lengthof(threads); i += 1) {
|
||||
// os_thread_join(&threads[i]);
|
||||
// }
|
||||
// ma_end_scratch(scratch);
|
||||
// }
|
||||
|
||||
fn s8_t os_appdata(ma_arena_t *arena, s8_t name) {
|
||||
assert(name.len != 0);
|
||||
assert(name.str);
|
||||
assert(arena);
|
||||
|
||||
wchar_t *out_path = NULL;
|
||||
HRESULT hr = SHGetKnownFolderPath(&FOLDERID_RoamingAppData, KF_FLAG_CREATE, NULL, &out_path);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
debugf("%s.SHGetKnownFolderPath failed with hr: %d", __FUNCTION__, hr);
|
||||
return s8_null;
|
||||
}
|
||||
|
||||
s16_t appdata_path = s16_from_str16((u16 *)out_path);
|
||||
|
||||
ma_temp_t scratch = ma_begin_scratch1(arena);
|
||||
s8_t tmp = s8_from_s16(scratch.arena, appdata_path);
|
||||
s8_normalize_path_unsafe(tmp);
|
||||
s8_t path = s8_printf(arena, "%.*s/%.*s", s8_fmtspec(tmp), s8_fmtspec(name));
|
||||
os_mkdir_t mkdir_status = os_mkdir(path);
|
||||
if (mkdir_status != os_mkdir_success && mkdir_status != os_mkdir_file_exists) {
|
||||
debugf("%s.os_mkdir failed with status: %d, for: %.*s", __FUNCTION__, mkdir_status, s8_fmtspec(path));
|
||||
path = s8_null;
|
||||
}
|
||||
|
||||
CoTaskMemFree(out_path);
|
||||
ma_end_scratch(scratch);
|
||||
return path;
|
||||
}
|
||||
|
||||
///////////////////////////////////
|
||||
#elif PLATFORM_POSIX
|
||||
///////////////////////////////////
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
fn os_date_t os_local_time(void) {
|
||||
os_date_t result = {0};
|
||||
time_t t = time(NULL);
|
||||
struct tm *lt = localtime(&t);
|
||||
result.sec = lt->tm_sec;
|
||||
result.min = lt->tm_min;
|
||||
result.hour = lt->tm_hour;
|
||||
result.day = lt->tm_mday;
|
||||
result.month = lt->tm_mon;
|
||||
result.year = lt->tm_year;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn os_date_t os_universal_time(void) {
|
||||
os_date_t result = {0};
|
||||
time_t t = time(NULL);
|
||||
struct tm u = {0};
|
||||
gmtime_r(&t, &u);
|
||||
result.sec = u.tm_sec;
|
||||
result.min = u.tm_min;
|
||||
result.hour = u.tm_hour;
|
||||
result.day = u.tm_mday;
|
||||
result.month = u.tm_mon;
|
||||
result.year = u.tm_year;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn f64 os_get_microseconds(void) {
|
||||
struct timespec t;
|
||||
clock_gettime(CLOCK_MONOTONIC, &t);
|
||||
u64 result = t.tv_sec*million(1) + (t.tv_nsec/thousand(1));
|
||||
return (f64)result;
|
||||
}
|
||||
|
||||
fn f64 os_get_milliseconds(void) {
|
||||
u64 micros = os_get_microseconds();
|
||||
f64 result = (f64)micros / 1000.0;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t os_exe(ma_arena_t *arena) {
|
||||
const i32 buffer_size = 2048;
|
||||
ma_temp_t temp = ma_begin_scratch1(arena);
|
||||
char *buffer = ma_push_array(arena, char, buffer_size);
|
||||
ssize_t size = readlink("/proc/self/exe", buffer, buffer_size);
|
||||
assert(size < buffer_size); // @todo:?
|
||||
assert(size != -1);
|
||||
s8_t string = s8_copy(arena, s8_make(buffer, size));
|
||||
ma_end_scratch(temp);
|
||||
return string;
|
||||
}
|
||||
|
||||
fn s8_t os_exe_dir(ma_arena_t *arena) {
|
||||
ma_temp_t temp = ma_begin_scratch1(arena);
|
||||
s8_t exe = os_exe(temp.arena);
|
||||
s8_t dir = s8_chop_last_slash(exe);
|
||||
s8_t result = s8_copy(arena, dir);
|
||||
ma_end_scratch(temp);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t os_cwd(ma_arena_t *arena) {
|
||||
const i32 buffer_size = 2048;
|
||||
ma_temp_t temp = ma_begin_scratch1(arena);
|
||||
char *buffer = ma_push_array(arena, char, buffer_size);
|
||||
char *ok = getcwd(buffer, buffer_size);
|
||||
assert(ok);
|
||||
s8_t result = s8_copy(arena, s8_from_char(buffer));
|
||||
ma_end_scratch(temp);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn void os_set_cwd(s8_t path) {
|
||||
ma_temp_t temp = ma_begin_scratch();
|
||||
chdir(s8_copy(temp.arena, path).str);
|
||||
ma_end_scratch(temp);
|
||||
}
|
||||
|
||||
fn b32 os_exists(s8_t path) {
|
||||
ma_temp_t temp = ma_begin_scratch();
|
||||
s8_t copy = s8_copy(temp.arena, path);
|
||||
int ok = access(copy.str, F_OK);
|
||||
ma_end_scratch(temp);
|
||||
return ok == 0 ? true : false;
|
||||
}
|
||||
|
||||
fn b32 os_is_dir(s8_t path) {
|
||||
b32 result = false;
|
||||
ma_temp_t temp = ma_begin_scratch();
|
||||
s8_t copy = s8_copy(temp.arena, path);
|
||||
DIR *dir = opendir(copy.str);
|
||||
if (dir) {
|
||||
result = true;
|
||||
closedir(dir);
|
||||
}
|
||||
ma_end_scratch(temp);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 os_is_file(s8_t path) {
|
||||
ma_temp_t temp = ma_begin_scratch();
|
||||
s8_t copy = s8_copy(temp.arena, path);
|
||||
struct stat path_stat;
|
||||
stat(copy.str, &path_stat);
|
||||
ma_end_scratch(temp);
|
||||
b32 result = S_ISREG(path_stat.st_mode);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 os_is_abs(s8_t path) {
|
||||
b32 result = path.len && path.str[0] == '/';
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t os_abs(ma_arena_t *arena, s8_t path) {
|
||||
ma_temp_t temp = ma_begin_scratch1(arena);
|
||||
s8_t null_term_path = s8_copy(temp.arena, path);
|
||||
char buffer[PATH_MAX];
|
||||
char *resolved_path = realpath(null_term_path.str, buffer);
|
||||
s8_t result = s8_copy(arena, s8_from_char(resolved_path));
|
||||
ma_end_scratch(temp);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn s8_t os_read(ma_arena_t *arena, s8_t path) {
|
||||
s8_t result = {0};
|
||||
ma_temp_t temp = ma_begin_scratch1(arena);
|
||||
s8_t copy_path = s8_copy(temp.arena, path);
|
||||
FILE *fd = fopen(copy_path.str, "rb");
|
||||
if (fd) {
|
||||
fseek(fd, 0, SEEK_END);
|
||||
long size = ftell(fd);
|
||||
fseek(fd, 0, SEEK_SET);
|
||||
char *string = malloc(size + 1);
|
||||
fread(string, size, 1, fd);
|
||||
fclose(fd);
|
||||
|
||||
result = s8_make(string, size);
|
||||
result.str[result.len] = 0;
|
||||
}
|
||||
ma_end_scratch(temp);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn os_write_t os_write(s8_t path, s8_t content) {
|
||||
os_write_t result = os_write_path_not_found;
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s8_t copy_path = s8_copy(scratch.arena, path);
|
||||
FILE *fd = fopen(copy_path.str, "w");
|
||||
if (fd) {
|
||||
size_t size_written = fwrite(content.str, 1, content.len, fd);
|
||||
assert((int64_t)size_written == content.len);
|
||||
fclose(fd);
|
||||
result = os_write_success;
|
||||
}
|
||||
ma_end_scratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 os_copy(s8_t src_path, s8_t dst_path, b32 overwrite) {
|
||||
if (overwrite == false && os_exists(dst_path)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ma_temp_t temp = ma_begin_scratch();
|
||||
s8_t src_data = os_read(temp.arena, src_path);
|
||||
os_write_t write_result = os_write(dst_path, src_data);
|
||||
b32 result = write_result == os_write_success;
|
||||
ma_end_scratch(temp);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 os_delete(s8_t path) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
s8_t copy_path = s8_copy(scratch.arena, path);
|
||||
int rc = unlink(copy_path.str);
|
||||
ma_end_scratch(scratch);
|
||||
return (b32)(rc == 0);
|
||||
}
|
||||
|
||||
fn void os_advance(os_iter_t *it) {
|
||||
struct dirent *file = NULL;
|
||||
while ((file = readdir((DIR *)it->unix_dir)) != NULL) {
|
||||
if (file->d_name[0] == '.' && file->d_name[1] == '.' && file->d_name[2] == 0) {
|
||||
continue;
|
||||
}
|
||||
if (file->d_name[0] == '.' && file->d_name[1] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
it->is_directory = file->d_type == DT_DIR;
|
||||
it->name = s8_copy(it->arena, s8_from_char(file->d_name));
|
||||
|
||||
const char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/";
|
||||
it->rel = s8_printf(it->arena, "%.*s%s%s", s8_fmtspec(it->path), separator, file->d_name);
|
||||
it->abs = os_abs(it->arena, it->rel);
|
||||
it->is_valid = true;
|
||||
return;
|
||||
}
|
||||
|
||||
it->is_valid = false;
|
||||
closedir((DIR *)it->unix_dir);
|
||||
}
|
||||
|
||||
fn os_iter_t *os_iter(ma_arena_t *arena, s8_t path) {
|
||||
os_iter_t *it = ma_push_type(arena, os_iter_t);
|
||||
it->arena = arena;
|
||||
it->path = s8_copy(arena, path);
|
||||
it->unix_dir = opendir(s8_copy(arena, it->path).str);
|
||||
if (it->unix_dir) {
|
||||
it->is_valid = true;
|
||||
os_advance(it);
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
///////////////////////////////////
|
||||
#endif // PLATFORM_POSIX
|
||||
///////////////////////////////////
|
||||
|
||||
fn s8_t os_format_date(ma_arena_t *arena, os_date_t date) {
|
||||
s8_t result = s8_printf(arena, "%04u-%02u-%02u %02u:%02u:%02u.%03u", date.year, date.month, date.day, date.hour, date.min, date.sec, date.ms);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
#if PLATFORM_WINDOWS || PLATFORM_POSIX
|
||||
fn int os_systemf(const char *string, ...) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
S8_FMT(scratch.arena, string, result);
|
||||
int error_code = system(result.str);
|
||||
ma_end_scratch(scratch);
|
||||
return error_code;
|
||||
}
|
||||
#endif
|
||||
-90
@@ -1,90 +0,0 @@
|
||||
#pragma once
|
||||
#include "core/core_basic.h"
|
||||
#include "core/core_string.h"
|
||||
|
||||
typedef struct os_date_t os_date_t;
|
||||
struct os_date_t {
|
||||
u16 ms;
|
||||
u16 sec;
|
||||
u16 min;
|
||||
u16 hour;
|
||||
u16 day;
|
||||
u16 month;
|
||||
u16 year;
|
||||
};
|
||||
|
||||
typedef struct os_iter_t os_iter_t;
|
||||
struct os_iter_t {
|
||||
s8_t abs;
|
||||
s8_t rel;
|
||||
s8_t name;
|
||||
b8 is_directory;
|
||||
b8 is_valid;
|
||||
|
||||
s8_t path;
|
||||
ma_arena_t *arena;
|
||||
|
||||
union {
|
||||
struct w32_file_iter_t *w32;
|
||||
void *platform;
|
||||
void *unix_dir;
|
||||
};
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
os_mkdir_success,
|
||||
os_mkdir_file_exists,
|
||||
os_mkdir_path_not_found,
|
||||
os_mkdir_other_error,
|
||||
} os_mkdir_t;
|
||||
|
||||
typedef enum {
|
||||
os_write_success,
|
||||
os_write_path_not_found,
|
||||
os_write_other_error,
|
||||
} os_write_t;
|
||||
|
||||
fn os_date_t os_local_time(void);
|
||||
fn os_date_t os_universal_time(void);
|
||||
fn s8_t os_format_date(ma_arena_t *arena, os_date_t date);
|
||||
fn f64 os_seconds(void);
|
||||
fn f64 os_milliseconds(void);
|
||||
fn f64 os_get_microseconds(void);
|
||||
|
||||
fn b32 os_copy(s8_t from, s8_t to, b32 overwrite);
|
||||
fn b32 os_delete(s8_t path);
|
||||
fn os_mkdir_t os_mkdir(s8_t path);
|
||||
fn os_write_t os_write(s8_t path, s8_t content);
|
||||
fn s8_t os_read(ma_arena_t *arena, s8_t path);
|
||||
|
||||
fn void os_advance(os_iter_t *it);
|
||||
fn os_iter_t *os_iter(ma_arena_t *arena, s8_t path);
|
||||
|
||||
fn i64 os_mod_time(s8_t file);
|
||||
fn s8_t os_abs(ma_arena_t *arena, s8_t rel);
|
||||
fn s8_t os_exe(ma_arena_t *arena);
|
||||
fn s8_t os_exe_dir(ma_arena_t *arena);
|
||||
fn b32 os_is_dir(s8_t path);
|
||||
fn b32 os_is_file(s8_t path);
|
||||
fn b32 os_is_abs(s8_t path);
|
||||
fn b32 os_exists(s8_t path);
|
||||
|
||||
fn s8_t os_cwd(ma_arena_t *arena);
|
||||
fn void os_set_cwd(s8_t new_cwd);
|
||||
|
||||
fn s8_t os_appdata(ma_arena_t *arena, s8_t name);
|
||||
|
||||
|
||||
typedef i32 os_thread_fn_t(void *user_data);
|
||||
typedef struct os_thread_t os_thread_t;
|
||||
struct os_thread_t {
|
||||
b32 exited;
|
||||
i32 exit_code;
|
||||
union {
|
||||
struct w32_thread_t *w32;
|
||||
void *ptr;
|
||||
};
|
||||
};
|
||||
|
||||
fn os_thread_t os_thread_create(ma_arena_t *arena, os_thread_fn_t *func, void *user_data);
|
||||
fn i32 os_thread_wait_for_exit(os_thread_t *thread);
|
||||
@@ -0,0 +1,165 @@
|
||||
struct Glyph {
|
||||
Vec2 size;
|
||||
Vec2 offset;
|
||||
float xadvance;
|
||||
float left_side_bearing;
|
||||
Rect2 atlas_bounding_box;
|
||||
};
|
||||
|
||||
struct Font {
|
||||
Array<Glyph> glyphs;
|
||||
uint32_t first_char, last_char;
|
||||
uint32_t texture_id;
|
||||
|
||||
float size;
|
||||
float descent;
|
||||
float ascent;
|
||||
float line_gap;
|
||||
|
||||
Int line_spacing;
|
||||
Int char_spacing;
|
||||
|
||||
Rect2 white_texture_bounding_box;
|
||||
};
|
||||
|
||||
struct Atlas {
|
||||
uint8_t *bitmap;
|
||||
Vec2I size;
|
||||
Vec2 inverse_size;
|
||||
int xcursor;
|
||||
int ycursor;
|
||||
|
||||
int32_t biggest_height;
|
||||
Rect2 white_texture_bounding_box;
|
||||
uint32_t texture_id;
|
||||
};
|
||||
|
||||
Atlas CreateAtlas(Allocator allocator, Vec2I size) {
|
||||
Atlas result = {};
|
||||
result.size = size;
|
||||
result.inverse_size.x = 1.f / (float)result.size.x;
|
||||
result.inverse_size.y = 1.f / (float)result.size.y;
|
||||
result.bitmap = AllocArray(allocator, uint8_t, size.x * size.y);
|
||||
|
||||
// Add a whitebox first for rectangle rendering
|
||||
for (int y = 0; y < 16; y++) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
uint8_t *dst = result.bitmap + x + y * result.size.x;
|
||||
*dst = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
result.white_texture_bounding_box = {
|
||||
{ 2.f * result.inverse_size.x, 2.f / (float)result.size.y},
|
||||
{14.f * result.inverse_size.x, 14.f / (float)result.size.y}
|
||||
};
|
||||
result.xcursor += 16;
|
||||
result.biggest_height += 16;
|
||||
return result;
|
||||
}
|
||||
|
||||
Rect2 PackBitmap(Atlas *atlas, uint8_t *bitmap, int width, int height) {
|
||||
// Packing into a texture atlas
|
||||
// @Inefficient The algorithm is a simplest thing I had in mind, first we advance
|
||||
// through the atlas in X packing consecutive glyphs. After we get to the end of the row
|
||||
// we advance to the next row by the Y size of the biggest packed glyph. If we get to the
|
||||
// end of atlas and fail to pack everything the app panics.
|
||||
|
||||
int spacing = 4;
|
||||
if (atlas->xcursor + width > atlas->size.x) {
|
||||
if (atlas->ycursor + height < atlas->size.y) {
|
||||
atlas->xcursor = 0;
|
||||
atlas->ycursor += atlas->biggest_height + spacing;
|
||||
} else {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Font loading error!", "Error while packing a font into atlas. Atlas size for this font scale is a bit too small", NULL);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t *src = bitmap;
|
||||
for (int y = atlas->ycursor; y < atlas->ycursor + height; y += 1) {
|
||||
for (int x = atlas->xcursor; x < atlas->xcursor + width; x += 1) {
|
||||
uint8_t *dst = atlas->bitmap + x + y * atlas->size.x;
|
||||
*dst = *src++;
|
||||
}
|
||||
}
|
||||
|
||||
Vec2 size = {(float)width * atlas->inverse_size.x, (float)height * atlas->inverse_size.y};
|
||||
Vec2 pos = {(float)atlas->xcursor * atlas->inverse_size.x, (float)atlas->ycursor * atlas->inverse_size.y};
|
||||
Rect2 result = {pos.x, pos.y, pos.x + size.x, pos.y + size.y};
|
||||
|
||||
atlas->xcursor += width + spacing;
|
||||
if (height > atlas->biggest_height) {
|
||||
atlas->biggest_height = height;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Glyph *GetGlyph(Font *font, uint32_t codepoint) {
|
||||
bool is_in_range = codepoint >= font->first_char && codepoint <= font->last_char;
|
||||
if (is_in_range) {
|
||||
uint32_t index = codepoint - font->first_char;
|
||||
return &font->glyphs[index];
|
||||
} else {
|
||||
uint32_t index = '?' - font->first_char;
|
||||
return &font->glyphs[index];
|
||||
}
|
||||
}
|
||||
|
||||
Font CreateFont(Atlas *atlas, int32_t size, String path) {
|
||||
Allocator allocator = GetSystemAllocator();
|
||||
Scratch scratch;
|
||||
|
||||
Font result = {};
|
||||
result.glyphs.allocator = allocator;
|
||||
String file = ReadFile(scratch, path);
|
||||
if (file.len == 0) {
|
||||
file = BakedInFont;
|
||||
Assert(file.len != 0);
|
||||
}
|
||||
|
||||
stbtt_fontinfo stb_font;
|
||||
int success = stbtt_InitFont(&stb_font, (const unsigned char *)file.data, 0);
|
||||
if (!success) {
|
||||
return result;
|
||||
}
|
||||
|
||||
float scale = stbtt_ScaleForPixelHeight(&stb_font, (float)size);
|
||||
// float em_scale = stbtt_ScaleForMappingEmToPixels(&stb_font, (float)size);
|
||||
|
||||
int ascent, descent, line_gap;
|
||||
stbtt_GetFontVMetrics(&stb_font, &ascent, &descent, &line_gap);
|
||||
result.ascent = (float)ascent * scale;
|
||||
result.descent = (float)descent * scale;
|
||||
result.line_gap = (float)line_gap * scale;
|
||||
result.size = (float)size;
|
||||
result.first_char = ' ';
|
||||
result.last_char = '~';
|
||||
result.white_texture_bounding_box = atlas->white_texture_bounding_box;
|
||||
|
||||
int glyph_count = result.last_char - result.first_char + 1; // + 1 because it's '<=' not '<'
|
||||
Reserve(&result.glyphs, glyph_count);
|
||||
|
||||
for (uint32_t ascii_symbol = result.first_char; ascii_symbol <= result.last_char; ascii_symbol++) {
|
||||
int width, height, xoff, yoff;
|
||||
uint8_t *bitmap = (uint8_t *)stbtt_GetCodepointBitmap(&stb_font, 0, scale, ascii_symbol, &width, &height, &xoff, &yoff);
|
||||
defer { stbtt_FreeBitmap(bitmap, 0); };
|
||||
|
||||
int xadvance, left_side_bearing;
|
||||
stbtt_GetCodepointHMetrics(&stb_font, ascii_symbol, &xadvance, &left_side_bearing);
|
||||
|
||||
Glyph glyph = {};
|
||||
glyph.atlas_bounding_box = PackBitmap(atlas, bitmap, width, height);
|
||||
glyph.size = {(float)width, (float)height};
|
||||
glyph.offset = {(float)xoff, (float)yoff};
|
||||
glyph.xadvance = (float)xadvance * scale;
|
||||
glyph.left_side_bearing = (float)left_side_bearing * scale;
|
||||
Add(&result.glyphs, glyph);
|
||||
}
|
||||
|
||||
Glyph *g = GetGlyph(&result, '_');
|
||||
result.char_spacing = (Int)g->xadvance ? (Int)g->xadvance : (Int)g->size.x;
|
||||
result.line_spacing = (Int)(result.ascent - result.descent + result.line_gap);
|
||||
|
||||
return result;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,404 @@
|
||||
struct Vertex2D {
|
||||
Vec2 pos;
|
||||
Vec2 tex;
|
||||
Color color;
|
||||
};
|
||||
|
||||
struct VertexNode2D {
|
||||
VertexNode2D *next;
|
||||
int count;
|
||||
Vertex2D vertices[1024 * 16];
|
||||
Rect2 scissor;
|
||||
};
|
||||
|
||||
struct VertexList2D {
|
||||
VertexNode2D *first;
|
||||
VertexNode2D *last;
|
||||
};
|
||||
|
||||
struct Shader {
|
||||
GLuint program; // linked program (vertex+fragment)
|
||||
GLint uni_invHalf; // uniform location for inv half-screen size
|
||||
GLint uni_texture; // sampler location
|
||||
};
|
||||
|
||||
VertexList2D Vertices;
|
||||
int64_t TotalVertexCount;
|
||||
|
||||
unsigned VBO, VAO;
|
||||
Shader Shader2D;
|
||||
BlockArena RenderArena;
|
||||
Rect2 CurrentScissor;
|
||||
|
||||
Font PrimaryFont;
|
||||
Font SecondaryFont;
|
||||
|
||||
// ---------- shaders (ES3 / WebGL2) ----------
|
||||
static const char *glsl_vshader_es3 = R"==(#version 300 es
|
||||
precision highp float;
|
||||
|
||||
uniform vec2 U_InvHalfScreenSize; // same meaning as before
|
||||
|
||||
layout(location = 0) in vec2 aPos;
|
||||
layout(location = 1) in vec2 aTex;
|
||||
layout(location = 2) in vec4 aColor; // normalized unsigned byte will map to 0..1 if we use normalized=true
|
||||
|
||||
out vec2 vUV;
|
||||
out vec4 vColor;
|
||||
|
||||
void main() {
|
||||
vec2 pos = aPos * U_InvHalfScreenSize;
|
||||
pos.y = 2.0 - pos.y; // invert Y the same way you had
|
||||
pos -= vec2(1.0, 1.0);
|
||||
gl_Position = vec4(pos, 0.0, 1.0);
|
||||
vUV = aTex;
|
||||
vColor = aColor; // already 0..1
|
||||
}
|
||||
)==";
|
||||
|
||||
static const char *glsl_fshader_es3 = R"==(#version 300 es
|
||||
precision mediump float;
|
||||
|
||||
in vec2 vUV;
|
||||
in vec4 vColor;
|
||||
|
||||
uniform sampler2D S_Texture;
|
||||
out vec4 OutColor;
|
||||
|
||||
void main() {
|
||||
float a = texture(S_Texture, vUV).r;
|
||||
vec4 c = vColor;
|
||||
c.a *= a;
|
||||
OutColor = c;
|
||||
}
|
||||
)==";
|
||||
|
||||
void ReportWarningf(const char *fmt, ...);
|
||||
void GLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *user) {
|
||||
ReportWarningf("OpenGL message: %s", message);
|
||||
if (severity == GL_DEBUG_SEVERITY_HIGH || severity == GL_DEBUG_SEVERITY_MEDIUM) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "OpenGL error", message, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- helper: compile/link ----------
|
||||
static GLuint CompileShaderSrc(GLenum kind, const char *src) {
|
||||
GLuint s = glCreateShader(kind);
|
||||
glShaderSource(s, 1, &src, NULL);
|
||||
glCompileShader(s);
|
||||
GLint ok = 0;
|
||||
glGetShaderiv(s, GL_COMPILE_STATUS, &ok);
|
||||
if (!ok) {
|
||||
char buf[1024];
|
||||
glGetShaderInfoLog(s, sizeof(buf), NULL, buf);
|
||||
SDL_Log("%s", buf);
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
|
||||
(kind == GL_VERTEX_SHADER ? "Vertex shader compile error" : "Fragment shader compile error"),
|
||||
buf, NULL);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Shader CreateShaderES3(const char *vsrc, const char *fsrc) {
|
||||
Shader out = {};
|
||||
GLuint vs = CompileShaderSrc(GL_VERTEX_SHADER, vsrc);
|
||||
GLuint fs = CompileShaderSrc(GL_FRAGMENT_SHADER, fsrc);
|
||||
|
||||
GLuint prog = glCreateProgram();
|
||||
glAttachShader(prog, vs);
|
||||
glAttachShader(prog, fs);
|
||||
|
||||
// Bind attribute locations to match layout locations (optional because we use layout(location=...))
|
||||
// glBindAttribLocation(prog, 0, "aPos");
|
||||
// glBindAttribLocation(prog, 1, "aTex");
|
||||
// glBindAttribLocation(prog, 2, "aColor");
|
||||
|
||||
glLinkProgram(prog);
|
||||
GLint linked = 0;
|
||||
glGetProgramiv(prog, GL_LINK_STATUS, &linked);
|
||||
if (!linked) {
|
||||
char buf[1024];
|
||||
glGetProgramInfoLog(prog, sizeof(buf), NULL, buf);
|
||||
SDL_Log("%s", buf);
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Program link error", buf, NULL);
|
||||
}
|
||||
|
||||
// We can detach/delete shaders after linking
|
||||
glDetachShader(prog, vs);
|
||||
glDetachShader(prog, fs);
|
||||
glDeleteShader(vs);
|
||||
glDeleteShader(fs);
|
||||
|
||||
out.program = prog;
|
||||
out.uni_invHalf = glGetUniformLocation(prog, "U_InvHalfScreenSize");
|
||||
out.uni_texture = glGetUniformLocation(prog, "S_Texture");
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// ---------- InitRender for ES3 ----------
|
||||
void InitRender() {
|
||||
#if !OS_WASM
|
||||
glDebugMessageCallback(&GLDebugCallback, NULL);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
#endif
|
||||
|
||||
// Create VBO (non-DSA path)
|
||||
glGenBuffers(1, &VBO);
|
||||
// reserve buffer big enough for one VertexNode2D->vertices array (same as original intent)
|
||||
GLsizeiptr vbo_size = (GLsizeiptr) (Lengthof(((VertexNode2D*)0)->vertices) * sizeof(Vertex2D));
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, vbo_size, NULL, GL_DYNAMIC_DRAW); // NULL initial, dynamic usage
|
||||
|
||||
// Create and setup VAO
|
||||
glGenVertexArrays(1, &VAO);
|
||||
glBindVertexArray(VAO);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VBO);
|
||||
// attribute 0 : pos (vec2, floats)
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (const void*)offsetof(Vertex2D, pos));
|
||||
// attribute 1 : tex (vec2)
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (const void*)offsetof(Vertex2D, tex));
|
||||
// attribute 2 : color (vec4) -- stored as 4 unsigned bytes; we want normalized floats 0..1
|
||||
glEnableVertexAttribArray(2);
|
||||
// Using normalized GL_TRUE to map 0..255 -> 0..1 directly in shader
|
||||
glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex2D), (const void*)offsetof(Vertex2D, color));
|
||||
|
||||
// Unbind VAO and VBO
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
// Create shader program (ES3)
|
||||
Shader2D = CreateShaderES3(glsl_vshader_es3, glsl_fshader_es3);
|
||||
|
||||
// other initializations like fonts will be done elsewhere (ReloadFont)
|
||||
}
|
||||
|
||||
void BeginFrameRender(float wx, float wy) {
|
||||
Release(&RenderArena);
|
||||
TotalVertexCount = 0;
|
||||
Vertices.first = NULL;
|
||||
Vertices.last = NULL;
|
||||
CurrentScissor = Rect0Size(wx, wy);
|
||||
}
|
||||
|
||||
// ---------- EndFrameRender for ES3 ----------
|
||||
void EndFrameRender(float wx, float wy, Color color) {
|
||||
ProfileFunction();
|
||||
glEnable(GL_BLEND);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
glViewport(0, 0, (GLsizei)wx, (GLsizei)wy);
|
||||
glScissor(0, 0, (GLsizei)wx, (GLsizei)wy);
|
||||
glClearColor(color.r, color.g, color.b, color.a);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
|
||||
// Use program and set uniforms
|
||||
glUseProgram(Shader2D.program);
|
||||
float xinverse = 1.f / (wx / 2.f);
|
||||
float yinverse = 1.f / (wy / 2.f);
|
||||
// set uniform (U_InvHalfScreenSize)
|
||||
if (Shader2D.uni_invHalf >= 0) {
|
||||
glUniform2f(Shader2D.uni_invHalf, xinverse, yinverse);
|
||||
}
|
||||
// set sampler to texture unit 0
|
||||
if (Shader2D.uni_texture >= 0) {
|
||||
glUniform1i(Shader2D.uni_texture, 0);
|
||||
}
|
||||
|
||||
glBindVertexArray(VAO);
|
||||
for (VertexNode2D *it = Vertices.first; it; it = it->next) {
|
||||
Rect2 rect = it->scissor;
|
||||
GLint x = (GLint)rect.min.x;
|
||||
GLint y = (GLint)rect.min.y;
|
||||
GLsizei w = (GLsizei)(rect.max.x - x);
|
||||
GLsizei h = (GLsizei)(rect.max.y - y);
|
||||
// convert scissor Y to OpenGL bottom-left origin like before
|
||||
glScissor(x, (GLint)wy - (GLint)rect.max.y, w, h);
|
||||
|
||||
// upload vertex data into VBO at offset 0
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VBO);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, it->count * sizeof(Vertex2D), it->vertices);
|
||||
|
||||
// bind texture unit 0 and the font texture
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, PrimaryFont.texture_id);
|
||||
|
||||
// draw
|
||||
glDrawArrays(GL_TRIANGLES, 0, it->count);
|
||||
|
||||
// unbind VBO (optional)
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
glBindVertexArray(0);
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
|
||||
VertexNode2D *AllocVertexNode2D(Allocator allocator, VertexList2D *list) {
|
||||
VertexNode2D *node = AllocType(allocator, VertexNode2D);
|
||||
SLL_QUEUE_ADD(list->first, list->last, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
void SetScissor(Rect2 rect) {
|
||||
CurrentScissor = rect;
|
||||
|
||||
VertexNode2D *node = Vertices.last;
|
||||
if (!node) {
|
||||
node = AllocVertexNode2D(RenderArena, &Vertices);
|
||||
node->scissor = rect;
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->scissor != rect && node->count == 0) {
|
||||
node->scissor = rect;
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->scissor != rect) {
|
||||
node = AllocVertexNode2D(RenderArena, &Vertices);
|
||||
node->scissor = rect;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void SetScissor(Rect2I rect) { SetScissor(ToRect2(rect)); }
|
||||
|
||||
Vertex2D *AllocVertex2D(Allocator allocator, VertexList2D *list, int count) {
|
||||
VertexNode2D *node = list->last;
|
||||
if (node == 0 || node->count + count > Lengthof(node->vertices)) {
|
||||
node = AllocVertexNode2D(allocator, list);
|
||||
node->scissor = CurrentScissor;
|
||||
}
|
||||
|
||||
TotalVertexCount += count;
|
||||
Vertex2D *result = node->vertices + node->count;
|
||||
node->count += count;
|
||||
return result;
|
||||
}
|
||||
|
||||
void PushVertex2D(Allocator allocator, VertexList2D *list, Vertex2D *vertices, int count) {
|
||||
Vertex2D *result = AllocVertex2D(allocator, list, count);
|
||||
for (int i = 0; i < count; i += 1) result[i] = vertices[i];
|
||||
}
|
||||
|
||||
void PushQuad2D(Allocator arena, VertexList2D *list, Rect2 rect, Rect2 tex, Color color, float rotation = 0.f, Vec2 rotation_point = {}) {
|
||||
Vertex2D *v = AllocVertex2D(arena, list, 6);
|
||||
v[0] = { {rect.min.x, rect.max.y}, { tex.min.x, tex.max.y}, color };
|
||||
v[1] = { {rect.max.x, rect.max.y}, { tex.max.x, tex.max.y}, color };
|
||||
v[2] = { {rect.min.x, rect.min.y}, { tex.min.x, tex.min.y}, color };
|
||||
v[3] = { {rect.min.x, rect.min.y}, { tex.min.x, tex.min.y}, color };
|
||||
v[4] = { {rect.max.x, rect.max.y}, { tex.max.x, tex.max.y}, color };
|
||||
v[5] = { {rect.max.x, rect.min.y}, { tex.max.x, tex.min.y}, color };
|
||||
if (rotation != 0.f) {
|
||||
float s = sinf(rotation);
|
||||
float c = cosf(rotation);
|
||||
for (int i = 0; i < 6; i += 1) {
|
||||
v[i].pos -= rotation_point;
|
||||
v[i].pos = {v[i].pos.x * c + v[i].pos.y * (-s), v[i].pos.x * s + v[i].pos.y * c};
|
||||
v[i].pos += rotation_point;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawRect(Rect2 rect, Color color) {
|
||||
PushQuad2D(RenderArena, &Vertices, rect, PrimaryFont.white_texture_bounding_box, color);
|
||||
}
|
||||
void DrawRect(Rect2I rect, Color color) {
|
||||
PushQuad2D(RenderArena, &Vertices, ToRect2(rect), PrimaryFont.white_texture_bounding_box, color);
|
||||
}
|
||||
|
||||
void DrawRectOutline(Rect2 rect, Color color, float thickness = 2) {
|
||||
Rect2 a = CutLeft(&rect, thickness);
|
||||
Rect2 b = CutRight(&rect, thickness);
|
||||
Rect2 c = CutTop(&rect, thickness);
|
||||
Rect2 d = CutBottom(&rect, thickness);
|
||||
PushQuad2D(RenderArena, &Vertices, a, PrimaryFont.white_texture_bounding_box, color);
|
||||
PushQuad2D(RenderArena, &Vertices, b, PrimaryFont.white_texture_bounding_box, color);
|
||||
PushQuad2D(RenderArena, &Vertices, c, PrimaryFont.white_texture_bounding_box, color);
|
||||
PushQuad2D(RenderArena, &Vertices, d, PrimaryFont.white_texture_bounding_box, color);
|
||||
}
|
||||
|
||||
void DrawRectOutline(Rect2I rect, Color color, Int thickness = 2) {
|
||||
DrawRectOutline(ToRect2(rect), color, (float)thickness);
|
||||
}
|
||||
|
||||
Vec2 DrawString(Font *font, String16 string, Vec2 pos, Color color, bool draw = true) {
|
||||
pos.y += font->line_spacing + font->descent; // TODO: descent?
|
||||
Vec2 original_pos = pos;
|
||||
For(string) {
|
||||
Glyph *g = GetGlyph(font, it);
|
||||
Rect2 rect = Rect2FromSize(pos + g->offset, g->size);
|
||||
if (draw && it != '\n' && it != ' ' && it != '\t') {
|
||||
PushQuad2D(RenderArena, &Vertices, rect, g->atlas_bounding_box, color);
|
||||
}
|
||||
pos.x += g->xadvance;
|
||||
}
|
||||
Vec2 result = {pos.x - original_pos.x, font->size};
|
||||
return result;
|
||||
}
|
||||
|
||||
void DrawCircle(Vec2 pos, float radius, Color color) {
|
||||
const int segment_count = 16;
|
||||
const int vertex_count = segment_count * 3;
|
||||
Vec2 points[segment_count + 1];
|
||||
for (int i = 0; i < Lengthof(points); i += 1) {
|
||||
float radians = 2.0f * PI32 * float(i) / float(segment_count);
|
||||
float x = radius * cosf(radians);
|
||||
float y = radius * sinf(radians);
|
||||
points[i] = {x, y};
|
||||
}
|
||||
points[segment_count] = points[0]; // wrap around
|
||||
|
||||
Vertex2D *vertices = AllocVertex2D(RenderArena, &Vertices, vertex_count);
|
||||
int point_i = 0;
|
||||
int segment_i = 0;
|
||||
for (; segment_i < vertex_count; segment_i += 3, point_i += 1) {
|
||||
Rect2 tex = PrimaryFont.white_texture_bounding_box;
|
||||
Vertex2D *it = vertices + segment_i;
|
||||
it[0].color = color;
|
||||
it[1].color = color;
|
||||
it[2].color = color;
|
||||
it[0].tex = {tex.min.x, tex.max.y};
|
||||
it[1].tex = {tex.max.x, tex.max.y};
|
||||
it[2].tex = {tex.min.x, tex.min.y};
|
||||
|
||||
it[0].pos = {pos + points[point_i]};
|
||||
it[1].pos = {pos + points[point_i + 1]};
|
||||
it[2].pos = {pos};
|
||||
}
|
||||
}
|
||||
|
||||
GLuint UploadAtlas(Atlas *atlas) {
|
||||
GLuint tex = 0;
|
||||
glGenTextures(1, &tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, (GLsizei)atlas->size.x, (GLsizei)atlas->size.y, 0, GL_RED, GL_UNSIGNED_BYTE, atlas->bitmap);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
atlas->texture_id = tex;
|
||||
return tex;
|
||||
}
|
||||
|
||||
void ReloadFont(String path, U32 size) {
|
||||
if (PrimaryFont.texture_id) {
|
||||
glDeleteTextures(1, &PrimaryFont.texture_id);
|
||||
Dealloc(&PrimaryFont.glyphs);
|
||||
PrimaryFont = {};
|
||||
}
|
||||
|
||||
Scratch scratch;
|
||||
Atlas atlas = CreateAtlas(scratch, {2048, 2048});
|
||||
PrimaryFont = CreateFont(&atlas, (uint32_t)ClampBottom(2u, (U32)size), path);
|
||||
SecondaryFont = CreateFont(&atlas, 12, path);
|
||||
SecondaryFont.texture_id = PrimaryFont.texture_id = UploadAtlas(&atlas);
|
||||
}
|
||||
Binary file not shown.
@@ -1,213 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Canvas</title>
|
||||
<style>
|
||||
html,body{margin:0;height:100%;overflow:hidden;padding:0;}
|
||||
canvas{display:block;width:100%;height:100%;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="glcanvas"></canvas>
|
||||
<script>
|
||||
|
||||
CANVAS = null;
|
||||
GL = null;
|
||||
class wglRender {
|
||||
constructor() {
|
||||
const VERTEX_SRC = `#version 300 es
|
||||
precision mediump float;
|
||||
in vec2 a_position;
|
||||
out vec2 v_texCoord;
|
||||
void main() {
|
||||
v_texCoord = (a_position + 1.0) * 0.5; // [0,1]
|
||||
v_texCoord.y = (1.0 - v_texCoord.y); // Flip the Y to get the standard memory behavior
|
||||
gl_Position = vec4(a_position, 0.0, 1.0);
|
||||
}`;
|
||||
|
||||
const FRAGMENT_SRC = `#version 300 es
|
||||
precision mediump float;
|
||||
in vec2 v_texCoord;
|
||||
uniform sampler2D u_texture;
|
||||
out vec4 outColor;
|
||||
void main() {
|
||||
vec4 c = texture(u_texture, v_texCoord);
|
||||
outColor = c;
|
||||
}`;
|
||||
|
||||
CANVAS = document.getElementById('glcanvas');
|
||||
GL = CANVAS.getContext('webgl2');
|
||||
if (!GL) { alert('WebGL 2 not supported'); return; }
|
||||
|
||||
const vs = this.createShader(GL.VERTEX_SHADER, VERTEX_SRC);
|
||||
const fs = this.createShader(GL.FRAGMENT_SHADER, FRAGMENT_SRC);
|
||||
this.program = this.createProgram(vs, fs);
|
||||
|
||||
this.posLoc = GL.getAttribLocation(this.program, 'a_position');
|
||||
this.texLoc = GL.getUniformLocation(this.program, 'u_texture');
|
||||
|
||||
// Fullscreen quad
|
||||
const quad = new Float32Array([
|
||||
-1, -1, // bottom left
|
||||
1, -1, // bottom right
|
||||
-1, 1, // top left
|
||||
-1, 1, // top left
|
||||
1, -1, // bottom right
|
||||
1, 1 // top right
|
||||
]);
|
||||
|
||||
this.vao = GL.createVertexArray();
|
||||
GL.bindVertexArray(this.vao);
|
||||
this.vbo = GL.createBuffer();
|
||||
GL.bindBuffer(GL.ARRAY_BUFFER, this.vbo);
|
||||
GL.bufferData(GL.ARRAY_BUFFER, quad, GL.STATIC_DRAW);
|
||||
GL.enableVertexAttribArray(this.posLoc);
|
||||
GL.vertexAttribPointer(this.posLoc, 2, GL.FLOAT, false, 0, 0);
|
||||
}
|
||||
|
||||
render() {
|
||||
GL.useProgram(this.program);
|
||||
GL.bindVertexArray(this.vao);
|
||||
GL.activeTexture(GL.TEXTURE0);
|
||||
GL.bindTexture(GL.TEXTURE_2D, this.texture);
|
||||
GL.uniform1i(this.texLoc, 0);
|
||||
GL.drawArrays(GL.TRIANGLES, 0, 6);
|
||||
}
|
||||
|
||||
updateTextureSize(wasm) {
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
const w = Math.floor(CANVAS.clientWidth * dpr);
|
||||
const h = Math.floor(CANVAS.clientHeight * dpr);
|
||||
if (w !== this.w || h !== this.h) {
|
||||
CANVAS.width = w;
|
||||
CANVAS.height = h;
|
||||
GL.viewport(0, 0, w, h);
|
||||
|
||||
this.w = w;
|
||||
this.h = h;
|
||||
if (this.texture) {
|
||||
GL.deleteTexture(this.texture);
|
||||
}
|
||||
const rawPtr = wasm.exports.update(w, h, window.devicePixelRatio || 1.0);
|
||||
const pixels = wasm.getU8View(rawPtr, w*h*4);
|
||||
{
|
||||
this.texture = GL.createTexture();
|
||||
GL.bindTexture(GL.TEXTURE_2D, this.texture);
|
||||
// Allocate an empty texture of the canvas size
|
||||
GL.texImage2D(
|
||||
GL.TEXTURE_2D, 0, GL.RGBA8,
|
||||
w, h, 0,
|
||||
GL.RGBA, GL.UNSIGNED_BYTE, pixels
|
||||
);
|
||||
// Set parameters – no filtering needed for empty data
|
||||
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.NEAREST);
|
||||
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.NEAREST);
|
||||
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE);
|
||||
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
// GL.activeTexture(GL.TEXTURE0);
|
||||
// GL.bindTexture(GL.TEXTURE_2D, this.texture);
|
||||
// GL.texSubImage2D(GL.TEXTURE_2D, 0, 0, 0, w, h, GL.RGBA, GL.UNSIGNED_BYTE,pixels);
|
||||
}
|
||||
}
|
||||
|
||||
createShader(type, src) {
|
||||
const sh = GL.createShader(type);
|
||||
GL.shaderSource(sh, src);
|
||||
GL.compileShader(sh);
|
||||
if (!GL.getShaderParameter(sh, GL.COMPILE_STATUS)) {
|
||||
console.error('Shader compile error:', GL.getShaderInfoLog(sh));
|
||||
GL.deleteShader(sh);
|
||||
return null;
|
||||
}
|
||||
return sh;
|
||||
}
|
||||
|
||||
createProgram(vs, fs) {
|
||||
const prg = GL.createProgram();
|
||||
GL.attachShader(prg, vs);
|
||||
GL.attachShader(prg, fs);
|
||||
GL.linkProgram(prg);
|
||||
if (!GL.getProgramParameter(prg, GL.LINK_STATUS)) {
|
||||
console.error('Program link error:', GL.getProgramInfoLog(prg));
|
||||
GL.deleteProgram(prg);
|
||||
return null;
|
||||
}
|
||||
return prg;
|
||||
}
|
||||
}
|
||||
|
||||
class WASMMemory {
|
||||
constructor(wasm_memory) {
|
||||
this.mem = wasm_memory;
|
||||
this.u8 = new Uint8Array(this.mem.buffer);
|
||||
this.data_view = new DataView(this.mem.buffer);
|
||||
this.utf8decoder = new TextDecoder("utf-8");
|
||||
this.utf8encoder = new TextEncoder("utf-8");
|
||||
this.exports = null;
|
||||
}
|
||||
|
||||
decodeString(str, len) {
|
||||
const arr = this.u8.subarray(str, str+len);
|
||||
const text = this.utf8decoder.decode(arr);
|
||||
return text;
|
||||
}
|
||||
|
||||
getU8View(p, len) {
|
||||
const arr = this.u8.subarray(p, p+len);
|
||||
return arr;
|
||||
}
|
||||
|
||||
getView(p, len) {
|
||||
const res = new DataView(this.mem.buffer, p, p+len);
|
||||
return res;
|
||||
}
|
||||
|
||||
encodeString(ptr, ptr_len, string) {
|
||||
const bytes = this.utf8encoder.encode(string);
|
||||
let i = 0;
|
||||
for (; i < bytes.length && i < (ptr_len-1); i += 1) {
|
||||
this.u8[ptr + i] = bytes[i];
|
||||
}
|
||||
this.u8[ptr + i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
async function WASMInit(name) {
|
||||
const request = await fetch(name);
|
||||
const mem = new WASMMemory(new WebAssembly['Memory']({ initial: 2000, maximum: 65536 }));
|
||||
const binary = await request.arrayBuffer();
|
||||
const import_table = {
|
||||
memory: mem.mem,
|
||||
wasm_parse_float: (str, len) => { return parseFloat(mem.decodeString(str, len)); },
|
||||
wasm_alert: (str, len) => { alert(mem.decodeString(str,len)); },
|
||||
wasm_trap: () => { debugger; },
|
||||
wasm_write_to_console: (str, len) => { console.log(mem.decodeString(str, len)); },
|
||||
};
|
||||
const program = await WebAssembly['instantiate'](binary, { "env": import_table });
|
||||
const instance = program['instance'];
|
||||
const export_table = instance['exports'];
|
||||
mem.exports = export_table;
|
||||
return mem;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const render = new wglRender();
|
||||
const wasm = await WASMInit("main.wasm");
|
||||
wasm.exports.init();
|
||||
|
||||
function frameLoop() {
|
||||
render.updateTextureSize(wasm);
|
||||
render.render();
|
||||
requestAnimationFrame(frameLoop);
|
||||
}
|
||||
|
||||
requestAnimationFrame(frameLoop);
|
||||
|
||||
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,212 +0,0 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core.c"
|
||||
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#define STBTT_ifloor(x) ((i32)f64_floor(x))
|
||||
#define STBTT_iceil(x) ((i32)f64_ceil(x))
|
||||
#define STBTT_sqrt(x) (f64_sqrt(x))
|
||||
#define STBTT_fmod(x,y) (f64_mod(x,y))
|
||||
#define STBTT_pow(x,y) (f64_pow(x,y))
|
||||
#define STBTT_cos(x) (f64_cos(x))
|
||||
#define STBTT_acos(x) (f64_acos(x))
|
||||
#define STBTT_fabs(x) (f64_abs(x))
|
||||
#define STBTT_malloc(x,u) ((void)u,ma_push_size(&tcx->temp,x))
|
||||
#define STBTT_free(x,u) ((void)x,(void)u)
|
||||
#define STBTT_assert(x) assert(x)
|
||||
#define STBTT_strlen(x) str_len(x)
|
||||
#define STBTT_memcpy memory_copy
|
||||
#define STBTT_memset memory_set
|
||||
|
||||
#include "vendor/stb/stb_truetype.h"
|
||||
#include "fira_code.c"
|
||||
|
||||
typedef struct bitmap_t bitmap_t;
|
||||
struct bitmap_t {
|
||||
u32 *data;
|
||||
i32 x, y;
|
||||
};
|
||||
|
||||
typedef struct glyph_t glyph_t;
|
||||
struct glyph_t {
|
||||
bitmap_t bitmap;
|
||||
i32 xoff, yoff;
|
||||
i32 left_side_bearing, xadvance;
|
||||
r2f32_t tex;
|
||||
};
|
||||
|
||||
typedef struct font_t font_t;
|
||||
struct font_t {
|
||||
glyph_t glyphs[256];
|
||||
bitmap_t atlas;
|
||||
i32 ascent, descent, line_gap, size;
|
||||
f32 scale;
|
||||
i32 height;
|
||||
};
|
||||
|
||||
fn font_t create_font(i32 size) {
|
||||
font_t font = {0};
|
||||
font.size = size;
|
||||
stbtt_fontinfo stb_font;
|
||||
i32 rc = stbtt_InitFont(&stb_font, main_font_data, 0);
|
||||
assert_expr(rc != 0);
|
||||
|
||||
font.scale = stbtt_ScaleForPixelHeight(&stb_font, (f32)font.size);
|
||||
stbtt_GetFontVMetrics(&stb_font, &font.ascent, &font.descent, &font.line_gap);
|
||||
font.height = f32_round((font.ascent - font.descent) + font.line_gap) * font.scale;
|
||||
for (i32 c = 0; c < 256; c += 1) {
|
||||
glyph_t *glyph = font.glyphs + c;
|
||||
u8 *temp_data = (u8 *)stbtt_GetCodepointBitmap(&stb_font, 0, font.scale, c, &glyph->bitmap.x, &glyph->bitmap.y, &glyph->xoff, &glyph->yoff);
|
||||
glyph->bitmap.data = ma_push_array(&tcx->temp, u32, glyph->bitmap.x * glyph->bitmap.y);
|
||||
for (i32 y = 0; y < glyph->bitmap.y; y += 1) {
|
||||
for (i32 x = 0; x < glyph->bitmap.x; x += 1) {
|
||||
glyph->bitmap.data[x + y * glyph->bitmap.x] = temp_data[x + y * glyph->bitmap.x] << 24;
|
||||
}
|
||||
}
|
||||
stbtt_GetCodepointHMetrics(&stb_font, c, &glyph->xadvance, &glyph->left_side_bearing);
|
||||
}
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
fn font_t create_font_atlas(i32 size) {
|
||||
stbtt_fontinfo stb_font;
|
||||
i32 rc = stbtt_InitFont(&stb_font, main_font_data, 0);
|
||||
assert_expr(rc != 0);
|
||||
|
||||
font_t font = { 0 };
|
||||
font.size = size;
|
||||
font.atlas.x = 512;
|
||||
font.atlas.y = 512;
|
||||
font.atlas.data = ma_push_array(&tcx->temp, u32, font.atlas.x * font.atlas.y);
|
||||
font.scale = stbtt_ScaleForPixelHeight(&stb_font, (f32)font.size);
|
||||
stbtt_GetFontVMetrics(&stb_font, &font.ascent, &font.descent, &font.line_gap);
|
||||
|
||||
i32 xiter = 2;
|
||||
i32 yiter = 2;
|
||||
i32 max_yiter_for_row = 0;
|
||||
for (i32 c = ' '; c < '~'; c += 1) {
|
||||
glyph_t *glyph = font.glyphs + (c - ' ');
|
||||
u8 *temp_data = (u8 *)stbtt_GetCodepointBitmap(&stb_font, 0, font.scale, c, &glyph->bitmap.x, &glyph->bitmap.y, &glyph->xoff, &glyph->yoff);
|
||||
if (xiter + glyph->bitmap.x >= font.atlas.x) {
|
||||
xiter = 0;
|
||||
yiter += max_yiter_for_row + 2;
|
||||
}
|
||||
|
||||
glyph->tex.min.x = (f32)xiter / (f32)font.atlas.x;
|
||||
glyph->tex.min.y = (f32)yiter / (f32)font.atlas.y;
|
||||
glyph->tex.max.x = (f32)(xiter + glyph->bitmap.x)/ (f32)font.atlas.x;
|
||||
glyph->tex.max.y = (f32)(yiter + glyph->bitmap.y)/ (f32)font.atlas.y;
|
||||
|
||||
max_yiter_for_row = MAX(max_yiter_for_row, glyph->bitmap.y);
|
||||
for (i32 y = 0; y < glyph->bitmap.y; y += 1) {
|
||||
for (i32 x = 0; x < glyph->bitmap.x; x += 1) {
|
||||
font.atlas.data[(xiter + x) + (yiter + y)*font.atlas.x] = temp_data[x + y * glyph->bitmap.x] << 24;
|
||||
}
|
||||
}
|
||||
xiter += glyph->bitmap.x + 2;
|
||||
}
|
||||
return font;
|
||||
}
|
||||
|
||||
fn_export void init(void) {
|
||||
os_core_init();
|
||||
}
|
||||
|
||||
fn void draw_bitmap(bitmap_t *canvas, bitmap_t *bitmap, i32 x, i32 y) {
|
||||
i32 x0 = x;
|
||||
i32 y0 = y;
|
||||
i32 x1 = x + bitmap->x;
|
||||
i32 y1 = y + bitmap->y;
|
||||
|
||||
i32 cx0 = CLAMP(x0, 0, canvas->x);
|
||||
i32 cy0 = CLAMP(y0, 0, canvas->y);
|
||||
i32 cx1 = CLAMP(x1, 0, canvas->x);
|
||||
i32 cy1 = CLAMP(y1, 0, canvas->y);
|
||||
|
||||
|
||||
if (cx0 >= cx1 || cy0 >= cy1) {
|
||||
return;
|
||||
}
|
||||
|
||||
i32 sx_off = cx0 - x0;
|
||||
i32 sy_off = cy0 - y0;
|
||||
|
||||
i32 sy = sy_off;
|
||||
for (i32 y = cy0; y < cy1; y += 1) {
|
||||
i32 sx = sx_off;
|
||||
for (i32 x = cx0; x < cx1; x += 1) {
|
||||
canvas->data[x + y * canvas->x] = bitmap->data[sx + sy * bitmap->x];
|
||||
//canvas->data[x + y * canvas->x] = blend_pixel_u32(canvas->data[x + y * canvas->x], bitmap->data[sx + sy * bitmap->x]);
|
||||
sx += 1;
|
||||
}
|
||||
sy += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn void draw_rect(bitmap_t *canvas, i32 x, i32 y, i32 w, i32 h, u32 color) {
|
||||
i32 x0 = x;
|
||||
i32 y0 = y;
|
||||
i32 x1 = x + w;
|
||||
i32 y1 = y + h;
|
||||
|
||||
i32 cx0 = CLAMP(x0, 0, canvas->x);
|
||||
i32 cy0 = CLAMP(y0, 0, canvas->y);
|
||||
i32 cx1 = CLAMP(x1, 0, canvas->x);
|
||||
i32 cy1 = CLAMP(y1, 0, canvas->y);
|
||||
|
||||
|
||||
if (cx0 >= cx1 || cy0 >= cy1) {
|
||||
return;
|
||||
}
|
||||
|
||||
i32 sx_off = cx0 - x0;
|
||||
i32 sy_off = cy0 - y0;
|
||||
|
||||
for (i32 y = cy0; y < cy1; y += 1) {
|
||||
for (i32 x = cx0; x < cx1; x += 1) {
|
||||
canvas->data[x + y * canvas->x] = color;
|
||||
//canvas->data[x + y * canvas->x] = blend_pixel_u32(canvas->data[x + y * canvas->x], color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn v2i32_t draw_string(bitmap_t *canvas, font_t *font, i32 ix, i32 iy, b32 draw, char *string) {
|
||||
i32 xiter = 0;
|
||||
v2i32_t R = {0};
|
||||
for (char *it = string; *it; it += 1) {
|
||||
glyph_t *g = font->glyphs + (*it);
|
||||
|
||||
i32 curr_xiter = xiter;
|
||||
xiter += g->xadvance * font->scale;
|
||||
R.x = xiter;
|
||||
R.y = MAX(R.y, g->bitmap.y);
|
||||
|
||||
if (!draw) {
|
||||
continue;
|
||||
}
|
||||
|
||||
draw_bitmap(canvas, &g->bitmap, curr_xiter + g->xoff + ix, g->yoff + font->ascent*font->scale + iy);
|
||||
}
|
||||
return R;
|
||||
}
|
||||
|
||||
fn_export u32 *update(i32 width, i32 height, f32 dpr) {
|
||||
ma_set0(&tcx->temp);
|
||||
u32 *pixels = ma_push_array(&tcx->temp, u32, width * height);
|
||||
bitmap_t canvas = {pixels, width, height};
|
||||
font_t font = create_font_atlas(60);
|
||||
draw_bitmap(&canvas, &font.atlas, 0, 0);
|
||||
for (i32 i = 0; i < lengthof(font.glyphs); i += 1) {
|
||||
glyph_t *g = &font.glyphs[i];
|
||||
i32 x = g->tex.min.x * font.atlas.x;
|
||||
i32 y = g->tex.min.y * font.atlas.y;
|
||||
i32 w = (g->tex.max.x - g->tex.min.x) * font.atlas.x;
|
||||
i32 h = (g->tex.max.y - g->tex.min.y) * font.atlas.y;
|
||||
//draw_rect(&canvas, x, y, w, h, 0xFF0000FF);
|
||||
}
|
||||
//bitmap_t canvas = {pixels, width, height};
|
||||
//font_t font = create_font(60);
|
||||
//v2i32_t size = draw_string(&canvas, &font, 200, 500, false, "Hello world (stb)");
|
||||
//draw_string(&canvas, &font, (width-size.x)/2, height - size.y*2, true, "Hello world (stb)");
|
||||
return pixels;
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
String TestDir;
|
||||
BlockArena TestArena;
|
||||
|
||||
void AddCtrlPress(SDL_Keycode key) {
|
||||
Event event = {};
|
||||
event.key = key;
|
||||
event.ctrl = 1;
|
||||
event.xwindow = 1280;
|
||||
event.ywindow = 720;
|
||||
Add(&EventPlayback, event);
|
||||
}
|
||||
|
||||
void AddText(String string) {
|
||||
Event event = {};
|
||||
event.kind = EVENT_TEXT_INPUT;
|
||||
event.xwindow = 1280;
|
||||
event.ywindow = 720;
|
||||
event.text = Copy(SysAllocator, string).data;
|
||||
Add(&EventPlayback, event);
|
||||
}
|
||||
|
||||
void Wait(mco_coro *co) {
|
||||
Add(&EventPlayback, {EVENT_KIND_INVALID});
|
||||
for (Event *event = CoYield(co); event->kind != EVENT_KIND_INVALID; event = CoYield(co)) {
|
||||
}
|
||||
}
|
||||
|
||||
void PlayTestOpen(mco_coro *co) {
|
||||
// Open file, move a little, then open again and verify the caret didn't move
|
||||
// String basic_env_cpp = Format(SysAllocator, "%S/test_env", TestDir);
|
||||
|
||||
// AddCtrlPress(SDLK_P);
|
||||
// Add(&EventPlayback, {EVENT_KEY_PRESS, SDLK_UP, 1280, 720});
|
||||
// AddCtrlPress(SDLK_Q);
|
||||
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_PERIOD; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.ctrl = 1; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_PERIOD; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.ctrl = 1; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_F; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.ctrl = 1; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_S; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.text = "s"; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_R; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.text = "r"; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_C; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.text = "c"; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_ESCAPE; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_ESCAPE; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_LCTRL; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.ctrl = 1; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_Q; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.ctrl = 1; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_LCTRL; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.ctrl = 1; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_F; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.ctrl = 1; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_T; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.text = "t"; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_E; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.text = "e"; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_S; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.text = "s"; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_T; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.text = "t"; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_ESCAPE; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_ESCAPE; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_LCTRL; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.ctrl = 1; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_Q; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.ctrl = 1; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_LCTRL; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.ctrl = 1; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_F; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.ctrl = 1; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_T; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.text = "t"; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_E; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.text = "e"; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_S; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.text = "s"; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_ESCAPE; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_ESCAPE; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_LCTRL; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.ctrl = 1; Add(&EventPlayback, ev);}
|
||||
// {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_Q; ev.xwindow = 1910; ev.ywindow = 2040; ev.xmouse = 581; ev.ymouse = 952; ev.ctrl = 1; Add(&EventPlayback, ev);}
|
||||
|
||||
// Wait(co);
|
||||
|
||||
// {
|
||||
// BSet main = GetActiveMainSet();
|
||||
// Assert(main.buffer->name == basic_env_cpp);
|
||||
// Assert(main.view->carets[0].range.min == 0);
|
||||
// Assert(main.view->carets[0].range.max == 0);
|
||||
// Int line = PosToLine(main.buffer, main.view->carets[0].range.min);
|
||||
// Assert(line == 0);
|
||||
// }
|
||||
|
||||
// AddCtrlPress(SDLK_DOWN);
|
||||
// AddCtrlPress(SDLK_DOWN);
|
||||
// AddCtrlPress(SDLK_DOWN);
|
||||
// Wait(co);
|
||||
|
||||
// Range range = {};
|
||||
// {
|
||||
// BSet main = GetActiveMainSet();
|
||||
// Assert(main.view->carets[0].range.min > 0);
|
||||
// Assert(main.view->carets[0].range.max > 0);
|
||||
// range = main.view->carets[0].range;
|
||||
// }
|
||||
|
||||
// AddCtrlPress(SDLK_P);
|
||||
// Add(&EventPlayback, {EVENT_KEY_PRESS, SDLK_UP, 1280, 720});
|
||||
// AddCtrlPress(SDLK_Q);
|
||||
// Wait(co);
|
||||
|
||||
// Int buffer_len = 0;
|
||||
// {
|
||||
// BSet main = GetActiveMainSet();
|
||||
// Assert(main.buffer->name == basic_env_cpp);
|
||||
// Assert(main.view->carets[0].range.min == range.min);
|
||||
// Assert(main.view->carets[0].range.max == range.max);
|
||||
// buffer_len = main.buffer->len;
|
||||
// }
|
||||
|
||||
// AddText(Format(SysAllocator, "%S:20", basic_env_cpp));
|
||||
// AddCtrlPress(SDLK_Q);
|
||||
// Wait(co);
|
||||
|
||||
// {
|
||||
// BSet main = GetActiveMainSet();
|
||||
// Int pos = main.view->carets[0].range.min;
|
||||
// Int line = PosToLine(main.buffer, pos);
|
||||
// Assert(line == 19);
|
||||
// Assert(main.buffer->name == basic_env_cpp);
|
||||
// Assert(main.buffer->len != buffer_len);
|
||||
// Assert(main.buffer->dirty);
|
||||
// }
|
||||
|
||||
// AddCtrlPress(SDLK_Z);
|
||||
// AddCtrlPress(SDLK_PAGEUP);
|
||||
// Wait(co);
|
||||
|
||||
// {
|
||||
// BSet main = GetActiveMainSet();
|
||||
// Assert(main.buffer->len == buffer_len);
|
||||
// Assert(main.view->carets[0].range.min == 0);
|
||||
// Assert(main.view->carets[0].range.max == 0);
|
||||
// }
|
||||
ReportConsolef("%s DONE", __FUNCTION__);
|
||||
|
||||
}
|
||||
|
||||
void Test(mco_coro *co) {
|
||||
Wait(co); // First phase starts immediately but stuff is not initialized so Open acts weird
|
||||
Open(TestDir);
|
||||
PlayTestOpen(co);
|
||||
Release(&TestArena);
|
||||
|
||||
|
||||
// Add(&EventPlayback, {EVENT_QUIT});
|
||||
}
|
||||
|
||||
void InitTests() {
|
||||
WaitForEvents = false;
|
||||
TestDir = Format(TestArena, "%S/test_env", GetExeDir(TestArena));
|
||||
|
||||
CoRemove("Test");
|
||||
CoData *data = CoAdd(Test);
|
||||
data->dont_wait_until_resolved = true;
|
||||
CoResume(data);
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
#include "core/core.h"
|
||||
#include "os/os.h"
|
||||
|
||||
#include "core/core.c"
|
||||
#include "os/os.c"
|
||||
|
||||
fn void os_test(void) {
|
||||
ma_temp_t scratch = ma_begin_scratch();
|
||||
|
||||
os_date_t local_time = os_local_time();
|
||||
os_date_t universal_time = os_universal_time();
|
||||
unused(universal_time); unused(local_time);
|
||||
|
||||
s8_t exe_dir = os_exe_dir(&tcx->temp);
|
||||
assert(exe_dir.str[exe_dir.len - 1] != '/');
|
||||
s8_t exe = os_exe(&tcx->temp);
|
||||
s8_t cwd = os_cwd(&tcx->temp);
|
||||
assert(cwd.str[cwd.len - 1] != '/');
|
||||
assert(os_is_dir(exe_dir));
|
||||
assert(os_is_file(exe));
|
||||
assert(os_is_dir(cwd));
|
||||
assert(os_exists(exe_dir));
|
||||
assert(os_exists(exe));
|
||||
assert(os_exists(cwd));
|
||||
assert(os_is_abs(exe_dir));
|
||||
assert(os_is_abs(exe));
|
||||
assert(os_is_abs(cwd));
|
||||
assert(!os_is_abs(s8("../path/")));
|
||||
|
||||
s8_t file = os_read(&tcx->temp, s8("../.gitignore"));
|
||||
assert(file.str != 0);
|
||||
assert(file.len != 0);
|
||||
|
||||
// TEST os_copy
|
||||
{
|
||||
b32 a = os_copy(s8("../.gitignore"), s8(".gitignore_copy_test"), true);
|
||||
assert(a);
|
||||
|
||||
s8_t gitignore_copy = os_read(scratch.arena, s8(".gitignore_copy_test"));
|
||||
s8_t gitignore = os_read(scratch.arena, s8("../.gitignore"));
|
||||
assert(s8_are_equal(gitignore, gitignore_copy));
|
||||
os_delete(s8(".gitignore_copy_test"));
|
||||
}
|
||||
|
||||
// TEST os_iter
|
||||
{
|
||||
b32 found_gitignore = false;
|
||||
b32 found_src = false;
|
||||
for (os_iter_t *iter = os_iter(scratch.arena, s8("../")); iter->is_valid; os_advance(iter)) {
|
||||
assert(os_is_abs(iter->abs));
|
||||
assert(!os_is_abs(iter->rel));
|
||||
if (s8_ends_with(iter->rel, s8(".gitignore"))) {
|
||||
found_gitignore = true;
|
||||
assert(os_is_file(iter->rel));
|
||||
}
|
||||
if (s8_ends_with(iter->rel, s8("src"))) {
|
||||
found_src = true;
|
||||
assert(os_is_dir(iter->rel));
|
||||
}
|
||||
|
||||
if (os_is_dir(iter->abs)) {
|
||||
assert(iter->abs.str[iter->abs.len - 1] != '/');
|
||||
}
|
||||
}
|
||||
assert(found_src);
|
||||
assert(found_gitignore);
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
s8_t path = os_appdata(scratch.arena, s8("testing"));
|
||||
assert(path.str[path.len - 1] != '/');
|
||||
assert(s8_starts_with(path, s8("C:/")));
|
||||
assert(s8_ends_with(path, s8("/testing")));
|
||||
#endif
|
||||
ma_end_scratch(scratch);
|
||||
}
|
||||
|
||||
fn void test_s8(void) {
|
||||
ma_arena_t *arena = ma_create(ma_default_reserve_size);
|
||||
|
||||
{
|
||||
ma_temp_t temp = ma_begin_temp(arena);
|
||||
sb8_t *sb = &(sb8_t){arena};
|
||||
|
||||
s8_t memes = s8("memes");
|
||||
sb8_printf(sb, "%.*s", s8_fmtspec(memes));
|
||||
assert(sb->first == sb->last);
|
||||
assert(sb->first->len == 5);
|
||||
assert(s8_are_equal(sb->first->string, memes));
|
||||
|
||||
sb8_printf(sb, "%s", "things are going fine");
|
||||
s8_t string = sb8_merge(temp.arena, sb);
|
||||
assert(s8_are_equal(string, s8("memesthings are going fine")));
|
||||
|
||||
ma_end_temp(temp);
|
||||
}
|
||||
|
||||
{
|
||||
s8_t str = s8("thing|another|");
|
||||
sb8_t sb = s8_split(arena, str, s8("|"), s8_split_none);
|
||||
|
||||
assert(s8_are_equal(sb.first->string, s8("thing")));
|
||||
assert(s8_are_equal(sb.first->next->string, s8("another")));
|
||||
assert(sb.first->next->next == NULL);
|
||||
}
|
||||
|
||||
{
|
||||
s8_t str = s8("thing|another|");
|
||||
sb8_t sb = s8_split(arena, str, s8("|"), s8_split_inclusive);
|
||||
|
||||
assert(s8_are_equal(sb.first->string, s8("thing")));
|
||||
assert(s8_are_equal(sb.first->next->string, s8("|")));
|
||||
assert(s8_are_equal(sb.first->next->next->string, s8("another")));
|
||||
assert(s8_are_equal(sb.first->next->next->next->string, s8("|")));
|
||||
assert(sb.first->next->next->next->next == NULL);
|
||||
}
|
||||
|
||||
{
|
||||
s8_t str = s8("aabaaBaa");
|
||||
sb8_t sb = s8_split(arena, str, s8("b"), s8_split_inclusive | s8_split_ignore_case);
|
||||
|
||||
assert(s8_are_equal(sb.first->string, s8("aa")));
|
||||
assert(s8_are_equal(sb.first->next->string, s8("b")));
|
||||
assert(s8_are_equal(sb.first->next->next->string, s8("aa")));
|
||||
assert(s8_are_equal(sb.first->next->next->next->string, s8("B")));
|
||||
assert(s8_are_equal(sb.first->next->next->next->next->string, s8("aa")));
|
||||
assert(sb.first->next->next->next->next->next == NULL);
|
||||
}
|
||||
|
||||
{
|
||||
s8_t str = s8("aabaaBaa");
|
||||
sb8_t sb = s8_split(arena, str, s8("b"), s8_split_inclusive);
|
||||
|
||||
assert(s8_are_equal(sb.first->string, s8("aa")));
|
||||
assert(s8_are_equal(sb.first->next->string, s8("b")));
|
||||
assert(s8_are_equal(sb.first->next->next->string, s8("aaBaa")));
|
||||
}
|
||||
|
||||
{
|
||||
s8_t s = s8("0123456789");
|
||||
assert(s8_are_equal(s8_slice(s, 0, 4), s8("0123")));
|
||||
assert(s8_are_equal(s8_slice(s, -2, -1), s8("89")));
|
||||
assert(s8_are_equal(s8_slice(s, -2, 10), s8("89")));
|
||||
assert(s8_are_equal(s8_slice(s, 8, 10), s8("89")));
|
||||
}
|
||||
|
||||
{
|
||||
s8_t s = s8(" a \n");
|
||||
s = s8_trim(s);
|
||||
assert(s8_are_equal(s, s8("a")));
|
||||
}
|
||||
|
||||
{
|
||||
s8_t s = s8("C:/memes/the_thing.c");
|
||||
s8_t ss = s8_get_name_no_ext(s);
|
||||
assert(s8_are_equal(ss, s8("the_thing")));
|
||||
}
|
||||
|
||||
{
|
||||
s8_t s = s8_printf(arena, "%d%sv%s", 32, "|", ">");
|
||||
assert(s8_are_equal(s, s8("32|v>")));
|
||||
}
|
||||
|
||||
{
|
||||
s8_t s0 = s8("0123456789");
|
||||
s8_t s1 = s8_cut_start(&s0, 2);
|
||||
assert(s8_are_equal(s0, s8("23456789")));
|
||||
assert(s8_are_equal(s1, s8("01")));
|
||||
}
|
||||
|
||||
ma_destroy(arena);
|
||||
}
|
||||
|
||||
int main() {
|
||||
os_core_init();
|
||||
|
||||
test_s8();
|
||||
os_test();
|
||||
debugf("Testing OK");
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,252 @@
|
||||
View *GetViewForFixingWhenBufferCommand(Buffer *buffer, bool *is_active = NULL) {
|
||||
View *view = NULL;
|
||||
if (is_active) {
|
||||
*is_active = false;
|
||||
}
|
||||
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
if (active.buffer->id == buffer->id) {
|
||||
if (is_active) {
|
||||
*is_active = true;
|
||||
}
|
||||
return active.view;
|
||||
}
|
||||
|
||||
For(Views) {
|
||||
if (it->active_buffer != buffer->id) {
|
||||
continue;
|
||||
}
|
||||
view = it;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!view) {
|
||||
view = CreateView(buffer->id);
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
void ReplaceWithoutMovingCarets(Buffer *buffer, Range range, String16 string) {
|
||||
View *view = GetViewForFixingWhenBufferCommand(buffer);
|
||||
Array<Caret> carets = Copy(GetSystemAllocator(), view->carets);
|
||||
|
||||
Scratch scratch;
|
||||
SelectRange(view, range);
|
||||
ReplaceEx(scratch, view, string);
|
||||
|
||||
Dealloc(&view->carets);
|
||||
view->carets = carets;
|
||||
}
|
||||
|
||||
// @todo: revamp interface since it scrolls ALL VIEWS??? or maybe not??
|
||||
void Append(View *view, String16 string, bool scroll_to_end_if_cursor_on_last_line) {
|
||||
Scratch scratch;
|
||||
Buffer *buffer = GetBuffer(view->active_buffer);
|
||||
|
||||
struct ViewInfo {
|
||||
View *view;
|
||||
Array<Caret> carets;
|
||||
bool scroll_to_end;
|
||||
};
|
||||
|
||||
Array<ViewInfo> view_info = {scratch};
|
||||
ForItem(it_view, Views) {
|
||||
if (it_view->active_buffer != buffer->id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ViewInfo vi = {it_view};
|
||||
if (scroll_to_end_if_cursor_on_last_line) {
|
||||
Int line = PosToLine(buffer, GetFront(it_view->carets[0]));
|
||||
if (line == buffer->line_starts.len - 1) {
|
||||
vi.scroll_to_end = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vi.scroll_to_end) {
|
||||
vi.carets = Copy(GetSystemAllocator(), it_view->carets);
|
||||
}
|
||||
|
||||
Add(&view_info, vi);
|
||||
}
|
||||
|
||||
SelectRange(view, GetBufferEndAsRange(buffer));
|
||||
Replace(view, string);
|
||||
|
||||
For (view_info) {
|
||||
if (it.scroll_to_end) {
|
||||
it.view->carets[0] = MakeCaret(GetBufferEndAsRange(buffer).min);
|
||||
} else {
|
||||
Dealloc(&it.view->carets);
|
||||
it.view->carets = it.carets;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Append(View *view, String string, bool scroll_to_end_if_cursor_on_last_line) {
|
||||
Scratch scratch;
|
||||
String16 string16 = ToString16(scratch, string);
|
||||
Append(view, string16, scroll_to_end_if_cursor_on_last_line);
|
||||
}
|
||||
|
||||
void Appendf(View *view, const char *fmt, ...) {
|
||||
Scratch scratch;
|
||||
STRING_FORMAT(scratch, fmt, string);
|
||||
Append(view, string, true);
|
||||
}
|
||||
|
||||
void ReportErrorf(const char *fmt, ...) {
|
||||
Scratch scratch;
|
||||
STRING_FORMAT(scratch, fmt, string);
|
||||
if (BreakOnError) {
|
||||
BREAK();
|
||||
}
|
||||
|
||||
Appendf(LogView, "%S\n", string);
|
||||
ShowUIMessagef("%S", string);
|
||||
}
|
||||
|
||||
void ReportConsolef(const char *fmt, ...) {
|
||||
Scratch scratch;
|
||||
STRING_FORMAT(scratch, fmt, string);
|
||||
Appendf(LogView, "%S\n", string);
|
||||
}
|
||||
|
||||
void ReportWarningf(const char *fmt, ...) {
|
||||
ErrorCount += 1;
|
||||
Scratch scratch;
|
||||
STRING_FORMAT(scratch, fmt, string);
|
||||
if (BreakOnError) {
|
||||
BREAK();
|
||||
}
|
||||
Appendf(LogView, "%S\n", string);
|
||||
}
|
||||
|
||||
void CMD_CenterView() {
|
||||
CenterView(PrimaryWindowID);
|
||||
} RegisterCommand(CMD_CenterView, "", "");
|
||||
|
||||
void TrimWhitespace(Buffer *buffer, bool trim_lines_with_caret) {
|
||||
Scratch scratch;
|
||||
|
||||
bool is_active_view = false;
|
||||
View *view = GetViewForFixingWhenBufferCommand(buffer, &is_active_view);
|
||||
if (!is_active_view && !trim_lines_with_caret) {
|
||||
trim_lines_with_caret = true;
|
||||
}
|
||||
|
||||
Array<Edit> edits = BeginEdit(scratch, buffer, view->carets);
|
||||
MergeCarets(buffer, &view->carets);
|
||||
|
||||
Array<Range> lines_to_skip_triming = {};
|
||||
if (!trim_lines_with_caret) {
|
||||
lines_to_skip_triming = GetSelectedLinesSorted(scratch, view);
|
||||
}
|
||||
|
||||
for (Int i = 0; i < buffer->line_starts.len; i += 1) {
|
||||
Int range_index = FindRangeByPos(&lines_to_skip_triming, i);
|
||||
if (range_index != -1) continue;
|
||||
|
||||
Range range = GetLineRangeWithoutNL(buffer, i);
|
||||
Int whitespace_end = range.max;
|
||||
for (; whitespace_end > range.min; whitespace_end -= 1) {
|
||||
U16 w = buffer->data[whitespace_end - 1];
|
||||
bool is_whitespace = w == ' ' || w == '\t' || w == '\v' || w == '\r';
|
||||
if (!is_whitespace) break;
|
||||
}
|
||||
|
||||
Range whitespace_range = {whitespace_end, range.max};
|
||||
AddEdit(&edits, whitespace_range, u"");
|
||||
}
|
||||
|
||||
EndEdit(buffer, &edits, &view->carets, !KILL_SELECTION);
|
||||
view->update_scroll = false;
|
||||
}
|
||||
|
||||
void ApplyFormattingTool(Buffer *buffer, String tool) {
|
||||
Scratch scratch;
|
||||
String string = AllocCharString(scratch, buffer);
|
||||
ExecResult exec_result = ExecAndWait(scratch, tool, GetDirectory(buffer), string);
|
||||
String16 string16 = {exec_result.buffer->str, exec_result.buffer->len};
|
||||
if (exec_result.exit_code == 0) {
|
||||
ReplaceWithoutMovingCarets(buffer, GetRange(buffer), string16);
|
||||
} else {
|
||||
Append(LogView, string16, true);
|
||||
}
|
||||
}
|
||||
|
||||
void SaveAll() {
|
||||
For(Buffers) {
|
||||
// NOTE: file_mod_time is only set when buffer got read or written to disk already so should be saved
|
||||
if (it->file_mod_time && it->dirty) {
|
||||
SaveBuffer(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @todo: plugin_languages ? and then branch out language_cpp ...
|
||||
void CMD_FormatSelection() {
|
||||
Scratch scratch;
|
||||
BSet primary = GetBSet(PrimaryWindowID);
|
||||
|
||||
String tool = "";
|
||||
if (EndsWith(primary.buffer->name, ".c") || EndsWith(primary.buffer->name, ".cpp") || EndsWith(primary.buffer->name, ".h") || EndsWith(primary.buffer->name, ".hpp")) {
|
||||
tool = "clang-format";
|
||||
} else if (EndsWith(primary.buffer->name, ".go")) {
|
||||
tool = "gofmt";
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
Array<Edit> edits = BeginEdit(scratch, primary.buffer, primary.view->carets);
|
||||
MergeCarets(primary.buffer, &primary.view->carets);
|
||||
For (primary.view->carets) {
|
||||
String input_string = ToString(scratch, GetString(primary.buffer, it.range));
|
||||
ExecResult exec_result = ExecAndWait(scratch, tool, GetDirectory(primary.buffer), input_string);
|
||||
String16 string16 = {exec_result.buffer->str, exec_result.buffer->len};
|
||||
AddEdit(&edits, it.range, string16);
|
||||
}
|
||||
EndEdit(primary.buffer, &edits, &primary.view->carets, KILL_SELECTION);
|
||||
} RegisterCommand(CMD_FormatSelection, "", "");
|
||||
|
||||
void CMD_KillProcess() {
|
||||
BSet main = GetBSet(PrimaryWindowID);
|
||||
KillProcess(main.view);
|
||||
} RegisterCommand(CMD_KillProcess, "", "Kill process in the last active primary window");
|
||||
|
||||
void CMD_MakeFontLarger() {
|
||||
FontSize += 1;
|
||||
ReloadFont(PathToFont, (U32)FontSize);
|
||||
} RegisterCommand(CMD_MakeFontLarger, "ctrl-equals", "Increase the font size");
|
||||
|
||||
void CMD_MakeFontSmaller() {
|
||||
if (FontSize > 4) {
|
||||
FontSize -= 1;
|
||||
ReloadFont(PathToFont, (U32)FontSize);
|
||||
}
|
||||
} RegisterCommand(CMD_MakeFontSmaller, "ctrl-minus", "Decrease the font size");
|
||||
|
||||
void CMD_ToggleFullscreen() {
|
||||
if (IsInFullscreen) {
|
||||
SDL_SetWindowSize(SDLWindow, FullScreenSizeX, FullScreenSizeY);
|
||||
SDL_SetWindowPosition(SDLWindow, FullScreenPositionX, FullScreenPositionY);
|
||||
} else {
|
||||
SDL_GetWindowSize(SDLWindow, &FullScreenSizeX, &FullScreenSizeY);
|
||||
SDL_GetWindowPosition(SDLWindow, &FullScreenPositionX, &FullScreenPositionY);
|
||||
SDL_DisplayID display = SDL_GetDisplayForWindow(SDLWindow);
|
||||
const SDL_DisplayMode *dm = SDL_GetCurrentDisplayMode(display);
|
||||
SDL_SetWindowSize(SDLWindow, dm->w, dm->h);
|
||||
SDL_SetWindowPosition(SDLWindow, 0, 0);
|
||||
}
|
||||
IsInFullscreen = !IsInFullscreen;
|
||||
} RegisterCommand(CMD_ToggleFullscreen, "f11", "Switches between the fullscreen and non-fulscreen mode");
|
||||
|
||||
void CMD_OpenLogs() {
|
||||
ErrorCount = 0;
|
||||
Open(LogBuffer->name);
|
||||
} RegisterCommand(CMD_OpenLogs, "", "Opens the text editor logs and clear error counter");
|
||||
|
||||
void CMD_Errors() {
|
||||
CMD_OpenLogs();
|
||||
} RegisterCommand(CMD_Errors, "", "Opens the text editor logs and clear error counter");
|
||||
@@ -0,0 +1,105 @@
|
||||
#if OS_WASM
|
||||
EM_ASYNC_JS(void, _SetClipboardText, (const char* c_str), {
|
||||
const type = "text/plain";
|
||||
const clipboardItemData = {
|
||||
[type]: UTF8ToString(c_str),
|
||||
};
|
||||
const clipboardItem = new ClipboardItem(clipboardItemData);
|
||||
await navigator.clipboard.write([clipboardItem]);
|
||||
})
|
||||
|
||||
EM_ASYNC_JS(const char *, GetClipboardText, (), {
|
||||
const text = await navigator.clipboard.readText();
|
||||
return stringToNewUTF8(text);
|
||||
})
|
||||
#define FreeClipboardText free
|
||||
#else
|
||||
#define FreeClipboardText SDL_free
|
||||
#define GetClipboardText SDL_GetClipboardText
|
||||
#define _SetClipboardText SDL_SetClipboardText
|
||||
#endif
|
||||
|
||||
void FreeClipboardGlobals() {
|
||||
Release(&ClipboardArena);
|
||||
SavedClipboardString = {};
|
||||
SavedClipboardCarets = {};
|
||||
SavedClipboardCarets.allocator = ClipboardArena;
|
||||
}
|
||||
|
||||
void SaveStringInClipboard(String16 string) {
|
||||
Scratch scratch;
|
||||
FreeClipboardGlobals();
|
||||
SavedClipboardString = Copy16(ClipboardArena, string);
|
||||
_SetClipboardText(ToString(scratch, SavedClipboardString).data);
|
||||
}
|
||||
|
||||
void ClipboardCopy(View *view) {
|
||||
Scratch scratch;
|
||||
Buffer *buffer = GetBuffer(view->active_buffer);
|
||||
|
||||
// First, if there is no selection - select the entire line
|
||||
For(view->carets) {
|
||||
if (GetSize(it.range) == 0) {
|
||||
Int line = PosToLine(buffer, it.range.min);
|
||||
Range line_range = GetLineRange(buffer, line);
|
||||
it.range = line_range;
|
||||
}
|
||||
}
|
||||
|
||||
FreeClipboardGlobals();
|
||||
Reserve(&SavedClipboardCarets, view->carets.len);
|
||||
For(view->carets) {
|
||||
String16 string = GetString(buffer, it.range);
|
||||
String16 copy = Copy16(ClipboardArena, string);
|
||||
Add(&SavedClipboardCarets, copy);
|
||||
}
|
||||
|
||||
// @todo: maybe only add new line if there is no new line at the end, experiment with it
|
||||
SavedClipboardString = Merge(ClipboardArena, SavedClipboardCarets, u"\n");
|
||||
_SetClipboardText(ToString(scratch, SavedClipboardString).data);
|
||||
}
|
||||
|
||||
void ClipboardPaste(View *view) {
|
||||
Scratch scratch;
|
||||
Buffer *buffer = GetBuffer(view->active_buffer);
|
||||
const char *text = GetClipboardText();
|
||||
defer { FreeClipboardText((void *)text); };
|
||||
|
||||
String16 string = ToUnixString16(scratch, text);
|
||||
|
||||
// Regular paste
|
||||
if (string != SavedClipboardString || SavedClipboardCarets.len != view->carets.len) {
|
||||
Array<Edit> edits = BeginEdit(scratch, buffer, view->carets);
|
||||
MergeCarets(buffer, &view->carets);
|
||||
For(view->carets) AddEdit(&edits, it.range, string);
|
||||
EndEdit(buffer, &edits, &view->carets, KILL_SELECTION);
|
||||
return;
|
||||
}
|
||||
|
||||
// Multicursor paste
|
||||
Array<Edit> edits = BeginEdit(scratch, buffer, view->carets);
|
||||
MergeCarets(buffer, &view->carets);
|
||||
for (int64_t i = 0; i < view->carets.len; i += 1) {
|
||||
String16 string = SavedClipboardCarets[i];
|
||||
Caret &it = view->carets[i];
|
||||
AddEdit(&edits, it.range, string);
|
||||
}
|
||||
EndEdit(buffer, &edits, &view->carets, KILL_SELECTION);
|
||||
}
|
||||
|
||||
void CMD_Paste() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
ClipboardPaste(active.view);
|
||||
} RegisterCommand(CMD_Paste, "ctrl-v", "Paste the content of system clipboard at caret");
|
||||
|
||||
void CMD_Copy() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
ClipboardCopy(active.view);
|
||||
} RegisterCommand(CMD_Copy, "ctrl-c", "Copy currently selected content to system clipboard");
|
||||
|
||||
void CMD_Cut() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
SaveCaretHistoryBeforeBeginEdit(active.buffer, active.view->carets);
|
||||
ClipboardCopy(active.view);
|
||||
Replace(active.view, u"");
|
||||
} RegisterCommand(CMD_Cut, "ctrl-x", "Copy and delete currently selected content to system clipboard");
|
||||
@@ -0,0 +1,285 @@
|
||||
struct Lexer {
|
||||
Allocator allocator;
|
||||
char *at;
|
||||
char *start;
|
||||
char *end;
|
||||
char *name;
|
||||
int line, column;
|
||||
};
|
||||
|
||||
enum TriggerKind {
|
||||
TriggerKind_Error,
|
||||
TriggerKind_Key,
|
||||
TriggerKind_Mouse,
|
||||
TriggerKind_Binary,
|
||||
TriggerKind_CatchAll,
|
||||
};
|
||||
|
||||
struct Trigger {
|
||||
TriggerKind kind;
|
||||
Trigger *left;
|
||||
Trigger *right;
|
||||
SDL_Keycode key;
|
||||
struct {
|
||||
EventKind event_kind : 8;
|
||||
U32 ctrl : 1;
|
||||
U32 alt : 1;
|
||||
U32 shift : 1;
|
||||
};
|
||||
};
|
||||
|
||||
struct CachedTrigger {
|
||||
Trigger *trigger;
|
||||
String key;
|
||||
};
|
||||
Array<CachedTrigger> CachedTriggers;
|
||||
|
||||
void Advance(Lexer *lex) {
|
||||
if (lex->at < lex->end) {
|
||||
if (lex->at[0] == '\n') {
|
||||
lex->line += 1;
|
||||
lex->column = 0;
|
||||
} else {
|
||||
lex->column += 1;
|
||||
}
|
||||
lex->at += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Advance(Lexer *lex, int n) {
|
||||
for (int i = 0; i < n; i += 1) Advance(lex);
|
||||
}
|
||||
|
||||
char At(Lexer *lex) {
|
||||
if (lex->at < lex->end) {
|
||||
return lex->at[0];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
String AsString(Lexer *lex) {
|
||||
String result = {lex->at, lex->end - lex->at};
|
||||
return result;
|
||||
}
|
||||
|
||||
void EatWhitespace(Lexer *lex) {
|
||||
while (At(lex) != '\n' && IsWhitespace(At(lex))) {
|
||||
Advance(lex);
|
||||
}
|
||||
}
|
||||
|
||||
Trigger *TriggerBinary(Lexer *lex, Trigger *left, Trigger *right, int key) {
|
||||
Trigger *result = AllocType(lex->allocator, Trigger);
|
||||
result->kind = TriggerKind_Binary;
|
||||
result->key = key;
|
||||
result->left = left;
|
||||
result->right = right;
|
||||
return result;
|
||||
}
|
||||
|
||||
Trigger *ParseKeyAtom(Lexer *lex) {
|
||||
Trigger *result = AllocType(lex->allocator, Trigger);
|
||||
result->kind = TriggerKind_Key;
|
||||
EatWhitespace(lex);
|
||||
for (;;) {
|
||||
String lex_string = AsString(lex);
|
||||
if (StartsWith(lex_string, "ctrl")) {
|
||||
result->ctrl = true;
|
||||
Advance(lex, 4);
|
||||
} else if (StartsWith(lex_string, "shift")) {
|
||||
result->shift = true;
|
||||
Advance(lex, 5);
|
||||
} else if (StartsWith(lex_string, "alt")) {
|
||||
result->alt = true;
|
||||
Advance(lex, 3);
|
||||
} else if (IsAlphanumeric(At(lex))) {
|
||||
char *start = lex->at;
|
||||
while (IsAlphanumeric(At(lex))) {
|
||||
Advance(lex);
|
||||
}
|
||||
String a = {start, lex->at - start};
|
||||
Advance(lex);
|
||||
|
||||
bool found = false;
|
||||
for (int i = 0; !found && i < Lengthof(MouseConversionTable); i += 1) {
|
||||
if (a == MouseConversionTable[i].string) {
|
||||
result->event_kind = MouseConversionTable[i].value;
|
||||
result->kind = TriggerKind_Mouse;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; !found && i < Lengthof(SDLKeycodeConversionTable); i += 1) {
|
||||
if (a == SDLKeycodeConversionTable[i].string) {
|
||||
result->key = SDLKeycodeConversionTable[i].value;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
result->kind = TriggerKind_Error;
|
||||
ReportErrorf("%s:%d:%d: Failed to parse key trigger, unexpected identifier: '%d'", lex->name, lex->line, lex->column, result->key);
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
result->kind = TriggerKind_Error;
|
||||
ReportErrorf("%s:%d:%d: Failed to parse key trigger, unexpected character: '%c'", lex->name, lex->line, lex->column, At(lex));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (At(lex) == '-') {
|
||||
Advance(lex);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Trigger *ParseKeyChord(Lexer *lex) {
|
||||
Trigger *left = ParseKeyAtom(lex);
|
||||
EatWhitespace(lex);
|
||||
while (IsAlphanumeric(At(lex))) {
|
||||
left = TriggerBinary(lex, left, ParseKeyChord(lex), ' ');
|
||||
EatWhitespace(lex);
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
Trigger *ParseKeyOr(Lexer *lex) {
|
||||
Trigger *left = ParseKeyChord(lex);
|
||||
EatWhitespace(lex);
|
||||
while (At(lex) == '|') {
|
||||
Advance(lex);
|
||||
left = TriggerBinary(lex, left, ParseKeyOr(lex), '|');
|
||||
EatWhitespace(lex);
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
Trigger *ParseKeyCatchAll(Lexer *lex) {
|
||||
String string = AsString(lex);
|
||||
if (string == "catch_all") {
|
||||
Trigger *result = AllocType(lex->allocator, Trigger);
|
||||
result->kind = TriggerKind_CatchAll;
|
||||
return result;
|
||||
} else {
|
||||
return ParseKeyOr(lex);
|
||||
}
|
||||
}
|
||||
|
||||
Trigger *ParseKey(Allocator allocator, String key, char *debug_name) {
|
||||
Lexer lex = {allocator, key.data, key.data, key.data + key.len, debug_name};
|
||||
Trigger *result = ParseKeyCatchAll(&lex);
|
||||
return result;
|
||||
}
|
||||
|
||||
Trigger *ParseKeyCached(String key) {
|
||||
key = Trim(key);
|
||||
if (key.len == 0) {
|
||||
return NULL;
|
||||
}
|
||||
For (CachedTriggers) {
|
||||
if (it.key == key) {
|
||||
return it.trigger;
|
||||
}
|
||||
}
|
||||
|
||||
Allocator allocator = GetSystemAllocator();
|
||||
Trigger *result = ParseKey(allocator, key, key.data);
|
||||
if (!result) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Add(&CachedTriggers, {result, key});
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MatchEvent(Trigger *trigger, Event *event) {
|
||||
if (trigger->kind == TriggerKind_Key) {
|
||||
if (trigger->key == event->key && trigger->ctrl == event->ctrl && trigger->alt == event->alt && trigger->shift == event->shift) {
|
||||
return true;
|
||||
}
|
||||
} else if (trigger->kind == TriggerKind_Mouse) {
|
||||
if (trigger->event_kind == event->kind) {
|
||||
return true;
|
||||
}
|
||||
} else if (trigger->kind == TriggerKind_Binary) {
|
||||
if (trigger->key == ' ') {
|
||||
return false;
|
||||
} else if (trigger->key == '|') {
|
||||
bool ok = MatchEvent(trigger->left, event);
|
||||
if (ok) return ok;
|
||||
ok = MatchEvent(trigger->right, event);
|
||||
if (ok) return ok;
|
||||
} ElseInvalidCodepath();
|
||||
} else if (trigger->kind == TriggerKind_CatchAll) {
|
||||
if (event->kind == EVENT_MOUSE_LEFT || event->kind == EVENT_MOUSE_RIGHT || event->kind == EVENT_MOUSE_MIDDLE || event->kind == EVENT_MOUSE_X1 || event->kind == EVENT_MOUSE_X2 || event->kind == EVENT_KEY_PRESS || event->kind == EVENT_TEXT_INPUT) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TestParser() {
|
||||
Scratch scratch;
|
||||
{
|
||||
char *cmd = "ctrl-b";
|
||||
Lexer base_lex = {scratch, cmd, cmd, cmd + strlen(cmd), "keybinding"};
|
||||
Trigger *trigger = ParseKeyCatchAll(&base_lex);
|
||||
Assert(trigger->kind == TriggerKind_Key);
|
||||
Assert(trigger->key == SDLK_B);
|
||||
Assert(trigger->ctrl);
|
||||
Assert(trigger->shift == 0);
|
||||
}
|
||||
|
||||
{
|
||||
char *cmd = "ctrl-b shift-ctrl-a";
|
||||
Lexer base_lex = {scratch, cmd, cmd, cmd + strlen(cmd), "keybinding"};
|
||||
Trigger *trigger = ParseKeyCatchAll(&base_lex);
|
||||
Assert(trigger->kind == TriggerKind_Binary);
|
||||
Assert(trigger->key == ' ');
|
||||
Assert(trigger->left->kind == TriggerKind_Key);
|
||||
Assert(trigger->left->key == SDLK_B);
|
||||
Assert(trigger->left->ctrl);
|
||||
Assert(trigger->left->shift == 0);
|
||||
Assert(trigger->right->kind == TriggerKind_Key);
|
||||
Assert(trigger->right->key == SDLK_A);
|
||||
Assert(trigger->right->ctrl);
|
||||
Assert(trigger->right->shift);
|
||||
}
|
||||
|
||||
{
|
||||
char *cmd = "ctrl-b shift-ctrl-a | ctrl-c | ctrl-d";
|
||||
Lexer base_lex = {scratch, cmd, cmd, cmd + strlen(cmd), "keybinding"};
|
||||
Trigger *trigger = ParseKeyCatchAll(&base_lex);
|
||||
Assert(trigger->kind == TriggerKind_Binary);
|
||||
Assert(trigger->key == '|');
|
||||
|
||||
Assert(trigger->left->kind == TriggerKind_Binary);
|
||||
Assert(trigger->left->key == ' ');
|
||||
|
||||
Assert(trigger->right->kind == TriggerKind_Binary);
|
||||
Assert(trigger->right->key == '|');
|
||||
|
||||
|
||||
Event event = {};
|
||||
event.kind = EVENT_KEY_PRESS;
|
||||
event.key = SDLK_D;
|
||||
event.ctrl = 1;
|
||||
bool ok = MatchEvent(trigger, &event);
|
||||
Assert(ok);
|
||||
|
||||
event.key = SDLK_F1;
|
||||
ok = MatchEvent(trigger, &event);
|
||||
Assert(ok == 0);
|
||||
|
||||
event.ctrl = 1;
|
||||
event.shift = 1;
|
||||
event.key = SDLK_A;
|
||||
ok = MatchEvent(trigger, &event);
|
||||
Assert(!ok);
|
||||
}
|
||||
} RegisterFunction(&TestFunctions, TestParser);
|
||||
@@ -0,0 +1,91 @@
|
||||
typedef void CoroutineProc(mco_coro *co);
|
||||
CoData *CoCurr = NULL;
|
||||
|
||||
void CoDestroy(CoData *n) {
|
||||
mco_destroy(n->co);
|
||||
Release(&n->arena);
|
||||
}
|
||||
|
||||
void CoRemove(String name) {
|
||||
IterRemove(ActiveCoroutines) {
|
||||
IterRemovePrepare(ActiveCoroutines);
|
||||
if (it.name == name) {
|
||||
CoDestroy(&it);
|
||||
remove_item = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define CoAdd(x) CoAddEx(x, #x)
|
||||
CoData *CoAddEx(CoroutineProc *proc, String name) {
|
||||
mco_desc desc = mco_desc_init(proc, 0);
|
||||
mco_coro *coro = NULL;
|
||||
mco_result ok = mco_create(&coro, &desc);
|
||||
if (ok != MCO_SUCCESS) {
|
||||
ReportWarningf("failed to create coroutine %d", ok);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Add(&ActiveCoroutines, {coro, name});
|
||||
return GetLast(ActiveCoroutines);
|
||||
}
|
||||
|
||||
void CoResume(CoData *dat) {
|
||||
CoData *prev_curr = CoCurr;
|
||||
CoCurr = dat;
|
||||
mco_result ok = mco_resume(dat->co);
|
||||
Assert(ok == MCO_SUCCESS);
|
||||
CoCurr = prev_curr;
|
||||
}
|
||||
|
||||
void CoUpdate(Event *event) {
|
||||
ProfileFunction();
|
||||
double start = GetTimeSeconds();
|
||||
for (;ActiveCoroutines.len;) {
|
||||
IterRemove(ActiveCoroutines) {
|
||||
IterRemovePrepare(ActiveCoroutines);
|
||||
ProfileScopeEx(it.name);
|
||||
|
||||
mco_state status = mco_status(it.co);
|
||||
if (status == MCO_DEAD) {
|
||||
CoDestroy(&it);
|
||||
remove_item = true;
|
||||
} else {
|
||||
mco_push(it.co, &event, sizeof(Event *));
|
||||
CoCurr = ⁢
|
||||
mco_result ok = mco_resume(it.co);
|
||||
if (ok != MCO_SUCCESS) {
|
||||
ReportWarningf("failed to resume coroutine %d", ok);
|
||||
CoDestroy(&it);
|
||||
remove_item = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG_BUILD
|
||||
//:CoroutineLeakCheck
|
||||
// Make sure we don't leak scratch arenas between coroutine boundaries.
|
||||
// it leads to memory corruption! If you hit Assert here make sure you
|
||||
// don't leak the scratch arena.
|
||||
for (int i = 0; i < Lengthof(ScratchArenas); i += 1) {
|
||||
Assert(ScratchArenas[i].refs == 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
double took = GetTimeSeconds() - start;
|
||||
if (took > (0.016666 / 3.0)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Event *CoYield(mco_coro *co) {
|
||||
mco_result ok = mco_yield(co);
|
||||
Assert(ok == MCO_SUCCESS);
|
||||
|
||||
Event *event = NULL;
|
||||
ok = mco_pop(co, &event, sizeof(Event *));
|
||||
Assert(ok == MCO_SUCCESS);
|
||||
|
||||
return event;
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
Vec2I GetCellSize(Window *window) {
|
||||
Vec2I result = {(Int)window->font->char_spacing, (Int)window->font->line_spacing};
|
||||
return result;
|
||||
}
|
||||
|
||||
Rect2I GetVisibleCells(Window *window) {
|
||||
ProfileFunction();
|
||||
View *view = GetView(window->active_view);
|
||||
Vec2I size = GetSize(window->document_rect);
|
||||
|
||||
Int _cx = size.x / window->font->char_spacing;
|
||||
Int _cy = size.y / window->font->line_spacing;
|
||||
Int cx = _cx + 1;
|
||||
Int cy = _cy + 2;
|
||||
|
||||
Vec2I pos = {SafeDivide(view->scroll.x, window->font->char_spacing), SafeDivide(view->scroll.y, window->font->line_spacing)};
|
||||
Int x = pos.x;
|
||||
Int y = pos.y;
|
||||
Rect2I result = {x, y, x + cx, y + cy};
|
||||
return result;
|
||||
}
|
||||
|
||||
Scroller ComputeScrollerRect(Window *window) {
|
||||
View *view = GetView(window->active_view);
|
||||
Buffer *buffer = GetBuffer(view->active_buffer);
|
||||
Vec2I size = GetSize(window->scrollbar_rect);
|
||||
Rect2I vis = GetVisibleCells(window);
|
||||
Int line_count = buffer->line_starts.len + GetSize(vis).y - 1;
|
||||
|
||||
double begin = (double)vis.min.y / (double)line_count;
|
||||
double end = (double)vis.max.y / (double)line_count;
|
||||
|
||||
Rect2 rect = {
|
||||
{(float)window->scrollbar_rect.min.x, (float)window->scrollbar_rect.min.y + (float)((double)size.y * begin)},
|
||||
{(float)window->scrollbar_rect.max.x, (float)window->scrollbar_rect.min.y + (float)((double)size.y * end)},
|
||||
};
|
||||
Scroller result = {rect, begin, end, line_count};
|
||||
return result;
|
||||
}
|
||||
|
||||
void DrawVisibleText(Window *window, Color tint) {
|
||||
ProfileFunction();
|
||||
View *view = GetView(window->active_view);
|
||||
Buffer *buffer = GetBuffer(view->active_buffer);
|
||||
|
||||
Rect2I visible = GetVisibleCells(window);
|
||||
for (Int line_index = visible.min.y; line_index < visible.max.y && line_index >= 0 && line_index < buffer->line_starts.len; line_index += 1) {
|
||||
String16 line_string = GetLineString(buffer, line_index);
|
||||
Vec2I pos = Vec2I{visible.min.x, line_index} * Vec2I{(Int)window->font->char_spacing, (Int)window->font->line_spacing} - view->scroll + window->document_rect.min;
|
||||
|
||||
float text_offset_x = 0;
|
||||
for (Int col_index = visible.min.x; col_index < visible.max.x && col_index >= 0 && col_index < line_string.len; col_index += 1) {
|
||||
int codepoint = line_string[col_index];
|
||||
Glyph *g = GetGlyph(window->font, codepoint);
|
||||
Vec2 p = ToVec2(pos);
|
||||
p.y += window->font->line_spacing + window->font->descent;
|
||||
p.x += text_offset_x;
|
||||
Rect2 rect = Rect2FromSize(p + g->offset, g->size);
|
||||
|
||||
if (codepoint != '\n' && codepoint != '\r' && codepoint != ' ' && codepoint != '\t') {
|
||||
PushQuad2D(RenderArena, &Vertices, rect, g->atlas_bounding_box, tint);
|
||||
}
|
||||
|
||||
text_offset_x += window->font->char_spacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vec2I XYToWorldPos(Window *window, XY xy) {
|
||||
Vec2I result = {xy.col * (Int)window->font->char_spacing, xy.line * (Int)window->font->line_spacing};
|
||||
return result;
|
||||
}
|
||||
|
||||
Rect2I XYToRect(Window *window, XY xy) {
|
||||
Rect2I result = Rect2IFromSize(XYToWorldPos(window, xy), GetCellSize(window));
|
||||
return result;
|
||||
}
|
||||
|
||||
void DrawCaret(Window *window, XY xy, float size, Color color) {
|
||||
View *view = GetView(window->active_view);
|
||||
Rect2I _rect = XYToRect(window, xy);
|
||||
Rect2I rect = CutLeft(&_rect, (Int)(size * (float)window->font->char_spacing));
|
||||
Rect2I scrolled_rect = rect - view->scroll + window->document_rect.min;
|
||||
DrawRect(ToRect2(scrolled_rect), color);
|
||||
}
|
||||
|
||||
void DrawUnderline(Window *window, View *view, Buffer *buffer, Range range, Color color, Int size = 1) {
|
||||
XY xy_min = PosToXY(buffer, range.min);
|
||||
XY xy_max = PosToXY(buffer, range.max);
|
||||
|
||||
// @todo: maybe consider underlining multiple lines but then we would need to consider
|
||||
// optimizing and so on
|
||||
if (xy_min.line != xy_max.line) return;
|
||||
|
||||
Vec2I min = {xy_min.col * window->font->char_spacing, (xy_min.line + 1) * window->font->line_spacing - size};
|
||||
Vec2I max = {xy_max.col * window->font->char_spacing, (xy_max.line + 1) * window->font->line_spacing};
|
||||
Rect2I rect = {min, max};
|
||||
Rect2I scrolled_rect = rect - view->scroll + window->document_rect.min;
|
||||
DrawRectOutline(scrolled_rect, color);
|
||||
}
|
||||
|
||||
void DrawWindow(Window *window, Event &event) {
|
||||
ProfileFunction();
|
||||
View *view = GetView(window->active_view);
|
||||
Buffer *buffer = GetBuffer(view->active_buffer);
|
||||
Rect2 screen_rect = Rect0Size(event.xwindow, event.ywindow);
|
||||
SetScissor(screen_rect);
|
||||
|
||||
bool is_active = window->id == ActiveWindowID;
|
||||
bool active_layed_out_doc = window->id == PrimaryWindowID;
|
||||
|
||||
Color color_whitespace_during_selection = SelectedWhitespaceColor;
|
||||
Color color_background = BackgroundColor;
|
||||
Color color_line_highlight = LineHighlightColor;
|
||||
Color color_selection = SelectionColor;
|
||||
Color color_main_caret = PrimaryCaretColor;
|
||||
Color color_sub_caret = SecondaryCaretColor;
|
||||
Color color_text_line_numbers = LineNumberTextColor;
|
||||
Color color_text = TextColor;
|
||||
|
||||
if (window->secondary_window_style) {
|
||||
color_background = SecondaryWindowStyle_BackgroundColor;
|
||||
color_selection = SecondaryWindowStyle_SelectionColor;
|
||||
color_text = SecondaryWindowStyle_TextColor;
|
||||
color_line_highlight = SecondaryWindowStyle_LineHighlightColor;
|
||||
}
|
||||
|
||||
|
||||
DrawRect(window->total_rect, color_background);
|
||||
|
||||
|
||||
Rect2I combined_document_line_number = window->document_rect;
|
||||
if (DrawLineNumbers && window->draw_line_numbers) {
|
||||
combined_document_line_number.min.x = window->line_numbers_rect.min.x;
|
||||
}
|
||||
SetScissor(combined_document_line_number);
|
||||
BeginProfileScope(draw_caret_selection);
|
||||
Rect2I visible = GetVisibleCells(window);
|
||||
For(view->carets) {
|
||||
XY min = PosToXY(buffer, it.range.min);
|
||||
XY max = PosToXY(buffer, it.range.max);
|
||||
if (visible.min.y > max.line) continue;
|
||||
if (visible.max.y < min.line) continue;
|
||||
|
||||
if (GetSize(it.range)) {
|
||||
//
|
||||
// Draw selection
|
||||
for (Int line = visible.min.y; line <= visible.max.y && line >= 0 && line < buffer->line_starts.len; line += 1) {
|
||||
String16 line_string = GetLineString(buffer, line);
|
||||
|
||||
for (Int col = visible.min.x; col < visible.max.x && col >= 0 && col < line_string.len; col += 1) {
|
||||
bool a = line > min.line && line < max.line;
|
||||
bool b = min.line != max.line && (line == min.line && col >= min.col);
|
||||
bool c = min.line != max.line && (line == max.line && col < max.col);
|
||||
bool d = min.line == max.line && line == max.line && col >= min.col && col < max.col;
|
||||
if (!(a || b || c || d)) continue;
|
||||
|
||||
Vec2I pos = {col * window->font->char_spacing, line * window->font->line_spacing};
|
||||
pos -= view->scroll;
|
||||
pos += window->document_rect.min;
|
||||
Rect2 rect = Rect2FromSize({(float)pos.x, (float)pos.y}, {(float)window->font->char_spacing, (float)window->font->line_spacing});
|
||||
DrawRect(rect, color_selection);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the little artifacts signaling whitespace
|
||||
// @copypaste
|
||||
Glyph *xglyph = GetGlyph(window->font, 'x');
|
||||
for (Int line = visible.min.y; line <= visible.max.y && line >= 0 && line < buffer->line_starts.len; line += 1) {
|
||||
String16 line_string = GetLineString(buffer, line);
|
||||
|
||||
for (Int col = visible.min.x; col < visible.max.x && col >= 0 && col < line_string.len; col += 1) {
|
||||
bool a = line > min.line && line < max.line;
|
||||
bool b = min.line != max.line && (line == min.line && col >= min.col);
|
||||
bool c = min.line != max.line && (line == max.line && col < max.col);
|
||||
bool d = min.line == max.line && line == max.line && col >= min.col && col < max.col;
|
||||
if (!(a || b || c || d)) continue;
|
||||
if (line_string[col] == ' ' || line_string[col] == '\t' || line_string[col] == '\n' || line_string[col] == '\r') {
|
||||
Vec2I pos = {col * window->font->char_spacing, line * window->font->line_spacing};
|
||||
Vec2I scrolled_pos = pos - view->scroll + window->document_rect.min;
|
||||
Vec2 circle_pos = {scrolled_pos.x + (float)window->font->char_spacing / 2.f, (float)scrolled_pos.y + xglyph->size.y};
|
||||
float circle_size = window->font->size / 16.f;
|
||||
DrawCircle(circle_pos, circle_size, color_whitespace_during_selection);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (window->draw_line_highlight) {
|
||||
//
|
||||
// Draw highlight
|
||||
Int front = GetFront(it);
|
||||
XY fxy = PosToXY(buffer, front);
|
||||
Vec2I pos = XYToWorldPos(window, XYLine(fxy.line));
|
||||
Vec2I scrolled_pos = pos - view->scroll + window->document_rect.min;
|
||||
Rect2 rect = {
|
||||
{(float)window->total_rect.min.x, (float)scrolled_pos.y},
|
||||
{(float)window->total_rect.max.x, (float)scrolled_pos.y + (float)window->font->line_spacing}
|
||||
};
|
||||
DrawRect(rect, color_line_highlight);
|
||||
}
|
||||
}
|
||||
|
||||
// Underline word under mouse cursor
|
||||
Caret caret = view->carets[0];
|
||||
Vec2I mouse = MouseVec2I();
|
||||
bool mouse_in_document = AreOverlapping(mouse, window->document_rect);
|
||||
if (mouse_in_document) {
|
||||
View *view = GetView(window->active_view);
|
||||
Buffer *buffer = GetBuffer(view->active_buffer);
|
||||
Int p = ScreenSpaceToBufferPosErrorOutOfBounds(window, view, buffer, mouse);
|
||||
if (p != -1) {
|
||||
Range range = EncloseLoadWord(buffer, p);
|
||||
if (InBounds(caret.range, p)) range = caret.range;
|
||||
DrawUnderline(window, view, buffer, range, MouseUnderlineColor, 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (event.ctrl) {
|
||||
if (is_active) {
|
||||
if (GetSize(caret.range) == 0) {
|
||||
Range range = EncloseLoadWord(buffer, caret.range.min);
|
||||
DrawUnderline(window, view, buffer, range, CaretUnderlineColor);
|
||||
} else {
|
||||
DrawUnderline(window, view, buffer, caret.range, CaretUnderlineColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EndProfileScope();
|
||||
DrawVisibleText(window, color_text);
|
||||
|
||||
// Draw caret "|" markings
|
||||
if (is_active) {
|
||||
ProfileScope(DrawCarets);
|
||||
For(view->carets) {
|
||||
Int front = GetFront(it);
|
||||
XY fxy = PosToXY(buffer, front);
|
||||
if (fxy.col >= visible.min.x && fxy.col < visible.max.x && fxy.line >= visible.min.y && fxy.line <= visible.max.y) {
|
||||
bool main_caret = &it == &view->carets.data[0];
|
||||
DrawCaret(window, fxy, 0.3f, main_caret ? color_main_caret : color_sub_caret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw line numbers
|
||||
if (DrawLineNumbers && window->draw_line_numbers) {
|
||||
ProfileScope(DrawLineNumbers);
|
||||
Scratch scratch;
|
||||
SetScissor(window->line_numbers_rect);
|
||||
|
||||
Rect2I vlines = GetVisibleCells(window);
|
||||
for (Int line = vlines.min.y; line <= visible.max.y && line >= 0 && line < buffer->line_starts.len; line += 1) {
|
||||
Vec2I pos = {0, line * window->font->line_spacing};
|
||||
pos.y -= view->scroll.y;
|
||||
pos += window->line_numbers_rect.min;
|
||||
String s = Format(scratch, "%lld", (long long)line + 1ll);
|
||||
String16 string = ToString16(scratch, s);
|
||||
float x = window->font->char_spacing * (float)string.len;
|
||||
Vec2 p = ToVec2(pos);
|
||||
float rectx = (float)GetSize(window->line_numbers_rect).x;
|
||||
p.x += (rectx - x) / 2.f;
|
||||
if (x > rectx) p.x = (float)window->line_numbers_rect.min.x;
|
||||
DrawString(window->font, string, p, color_text_line_numbers);
|
||||
}
|
||||
}
|
||||
|
||||
#if PLUGIN_SEARCH_WINDOW
|
||||
if (SearchWindowID == window->id) {
|
||||
SetScissor(window->line_numbers_rect);
|
||||
DrawString(window->font, u"Find: ", ToVec2(window->line_numbers_rect.min), color_text_line_numbers);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Draw scrollbar
|
||||
if (DrawScrollbar && window->draw_scrollbar) {
|
||||
ProfileScope(DrawScrollbar);
|
||||
SetScissor(window->scrollbar_rect);
|
||||
DrawRect(window->scrollbar_rect, ScrollbarBackgroundColor);
|
||||
Scroller scroller = ComputeScrollerRect(window);
|
||||
Rect2 rect = Shrink(scroller.rect, 2);
|
||||
Color color = ScrollerColor;
|
||||
if (ScrollbarSelected.id == window->id.id) color = ScrollerSelectedColor;
|
||||
DrawRect(rect, color);
|
||||
}
|
||||
|
||||
// color the floating object to make it stand out
|
||||
if (window->z >= 1) {
|
||||
ProfileScope(ColorFloating);
|
||||
SetScissor(window->total_rect);
|
||||
DrawRect(window->total_rect, {255, 255, 255, 25});
|
||||
}
|
||||
|
||||
if (window->secondary_window_style) {
|
||||
SetScissor(window->total_rect);
|
||||
Rect2I rect = window->total_rect;
|
||||
DrawRect(CutTop(&rect, 1), SecondaryWindowStyle_OutlineColor);
|
||||
}
|
||||
|
||||
// darken the inactive windows
|
||||
if (!is_active && !active_layed_out_doc) {
|
||||
ProfileScope(DarkenInactiveRect);
|
||||
SetScissor(screen_rect);
|
||||
DrawRect(window->total_rect, InactiveWindowColor);
|
||||
}
|
||||
|
||||
// Draw resizer rect
|
||||
if (GetSize(window->resizer_rect).x > 0){
|
||||
Rect2I rect = window->resizer_rect;
|
||||
SetScissor(window->resizer_rect);
|
||||
DrawRect(rect, ResizerBackgroundColor);
|
||||
DrawRect(CutRight(&rect, 1), ResizerOutlineColor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,515 @@
|
||||
const char *SDLKeycodeToName(SDL_Keycode keycode) {
|
||||
switch(keycode) {
|
||||
case SDLK_UNKNOWN: return "SDLK_UNKNOWN"; break;
|
||||
case SDLK_RETURN: return "SDLK_RETURN"; break;
|
||||
case SDLK_ESCAPE: return "SDLK_ESCAPE"; break;
|
||||
case SDLK_BACKSPACE: return "SDLK_BACKSPACE"; break;
|
||||
case SDLK_TAB: return "SDLK_TAB"; break;
|
||||
case SDLK_SPACE: return "SDLK_SPACE"; break;
|
||||
case SDLK_EXCLAIM: return "SDLK_EXCLAIM"; break;
|
||||
case SDLK_DBLAPOSTROPHE: return "SDLK_DBLAPOSTROPHE"; break;
|
||||
case SDLK_HASH: return "SDLK_HASH"; break;
|
||||
case SDLK_DOLLAR: return "SDLK_DOLLAR"; break;
|
||||
case SDLK_PERCENT: return "SDLK_PERCENT"; break;
|
||||
case SDLK_AMPERSAND: return "SDLK_AMPERSAND"; break;
|
||||
case SDLK_APOSTROPHE: return "SDLK_APOSTROPHE"; break;
|
||||
case SDLK_LEFTPAREN: return "SDLK_LEFTPAREN"; break;
|
||||
case SDLK_RIGHTPAREN: return "SDLK_RIGHTPAREN"; break;
|
||||
case SDLK_ASTERISK: return "SDLK_ASTERISK"; break;
|
||||
case SDLK_PLUS: return "SDLK_PLUS"; break;
|
||||
case SDLK_COMMA: return "SDLK_COMMA"; break;
|
||||
case SDLK_MINUS: return "SDLK_MINUS"; break;
|
||||
case SDLK_PERIOD: return "SDLK_PERIOD"; break;
|
||||
case SDLK_SLASH: return "SDLK_SLASH"; break;
|
||||
case SDLK_0: return "SDLK_0"; break;
|
||||
case SDLK_1: return "SDLK_1"; break;
|
||||
case SDLK_2: return "SDLK_2"; break;
|
||||
case SDLK_3: return "SDLK_3"; break;
|
||||
case SDLK_4: return "SDLK_4"; break;
|
||||
case SDLK_5: return "SDLK_5"; break;
|
||||
case SDLK_6: return "SDLK_6"; break;
|
||||
case SDLK_7: return "SDLK_7"; break;
|
||||
case SDLK_8: return "SDLK_8"; break;
|
||||
case SDLK_9: return "SDLK_9"; break;
|
||||
case SDLK_COLON: return "SDLK_COLON"; break;
|
||||
case SDLK_SEMICOLON: return "SDLK_SEMICOLON"; break;
|
||||
case SDLK_LESS: return "SDLK_LESS"; break;
|
||||
case SDLK_EQUALS: return "SDLK_EQUALS"; break;
|
||||
case SDLK_GREATER: return "SDLK_GREATER"; break;
|
||||
case SDLK_QUESTION: return "SDLK_QUESTION"; break;
|
||||
case SDLK_AT: return "SDLK_AT"; break;
|
||||
case SDLK_LEFTBRACKET: return "SDLK_LEFTBRACKET"; break;
|
||||
case SDLK_BACKSLASH: return "SDLK_BACKSLASH"; break;
|
||||
case SDLK_RIGHTBRACKET: return "SDLK_RIGHTBRACKET"; break;
|
||||
case SDLK_CARET: return "SDLK_CARET"; break;
|
||||
case SDLK_UNDERSCORE: return "SDLK_UNDERSCORE"; break;
|
||||
case SDLK_GRAVE: return "SDLK_GRAVE"; break;
|
||||
case SDLK_A: return "SDLK_A"; break;
|
||||
case SDLK_B: return "SDLK_B"; break;
|
||||
case SDLK_C: return "SDLK_C"; break;
|
||||
case SDLK_D: return "SDLK_D"; break;
|
||||
case SDLK_E: return "SDLK_E"; break;
|
||||
case SDLK_F: return "SDLK_F"; break;
|
||||
case SDLK_G: return "SDLK_G"; break;
|
||||
case SDLK_H: return "SDLK_H"; break;
|
||||
case SDLK_I: return "SDLK_I"; break;
|
||||
case SDLK_J: return "SDLK_J"; break;
|
||||
case SDLK_K: return "SDLK_K"; break;
|
||||
case SDLK_L: return "SDLK_L"; break;
|
||||
case SDLK_M: return "SDLK_M"; break;
|
||||
case SDLK_N: return "SDLK_N"; break;
|
||||
case SDLK_O: return "SDLK_O"; break;
|
||||
case SDLK_P: return "SDLK_P"; break;
|
||||
case SDLK_Q: return "SDLK_Q"; break;
|
||||
case SDLK_R: return "SDLK_R"; break;
|
||||
case SDLK_S: return "SDLK_S"; break;
|
||||
case SDLK_T: return "SDLK_T"; break;
|
||||
case SDLK_U: return "SDLK_U"; break;
|
||||
case SDLK_V: return "SDLK_V"; break;
|
||||
case SDLK_W: return "SDLK_W"; break;
|
||||
case SDLK_X: return "SDLK_X"; break;
|
||||
case SDLK_Y: return "SDLK_Y"; break;
|
||||
case SDLK_Z: return "SDLK_Z"; break;
|
||||
case SDLK_LEFTBRACE: return "SDLK_LEFTBRACE"; break;
|
||||
case SDLK_PIPE: return "SDLK_PIPE"; break;
|
||||
case SDLK_RIGHTBRACE: return "SDLK_RIGHTBRACE"; break;
|
||||
case SDLK_TILDE: return "SDLK_TILDE"; break;
|
||||
case SDLK_DELETE: return "SDLK_DELETE"; break;
|
||||
case SDLK_PLUSMINUS: return "SDLK_PLUSMINUS"; break;
|
||||
case SDLK_CAPSLOCK: return "SDLK_CAPSLOCK"; break;
|
||||
case SDLK_F1: return "SDLK_F1"; break;
|
||||
case SDLK_F2: return "SDLK_F2"; break;
|
||||
case SDLK_F3: return "SDLK_F3"; break;
|
||||
case SDLK_F4: return "SDLK_F4"; break;
|
||||
case SDLK_F5: return "SDLK_F5"; break;
|
||||
case SDLK_F6: return "SDLK_F6"; break;
|
||||
case SDLK_F7: return "SDLK_F7"; break;
|
||||
case SDLK_F8: return "SDLK_F8"; break;
|
||||
case SDLK_F9: return "SDLK_F9"; break;
|
||||
case SDLK_F10: return "SDLK_F10"; break;
|
||||
case SDLK_F11: return "SDLK_F11"; break;
|
||||
case SDLK_F12: return "SDLK_F12"; break;
|
||||
case SDLK_PRINTSCREEN: return "SDLK_PRINTSCREEN"; break;
|
||||
case SDLK_SCROLLLOCK: return "SDLK_SCROLLLOCK"; break;
|
||||
case SDLK_PAUSE: return "SDLK_PAUSE"; break;
|
||||
case SDLK_INSERT: return "SDLK_INSERT"; break;
|
||||
case SDLK_HOME: return "SDLK_HOME"; break;
|
||||
case SDLK_PAGEUP: return "SDLK_PAGEUP"; break;
|
||||
case SDLK_END: return "SDLK_END"; break;
|
||||
case SDLK_PAGEDOWN: return "SDLK_PAGEDOWN"; break;
|
||||
case SDLK_RIGHT: return "SDLK_RIGHT"; break;
|
||||
case SDLK_LEFT: return "SDLK_LEFT"; break;
|
||||
case SDLK_DOWN: return "SDLK_DOWN"; break;
|
||||
case SDLK_UP: return "SDLK_UP"; break;
|
||||
case SDLK_NUMLOCKCLEAR: return "SDLK_NUMLOCKCLEAR"; break;
|
||||
case SDLK_KP_DIVIDE: return "SDLK_KP_DIVIDE"; break;
|
||||
case SDLK_KP_MULTIPLY: return "SDLK_KP_MULTIPLY"; break;
|
||||
case SDLK_KP_MINUS: return "SDLK_KP_MINUS"; break;
|
||||
case SDLK_KP_PLUS: return "SDLK_KP_PLUS"; break;
|
||||
case SDLK_KP_ENTER: return "SDLK_KP_ENTER"; break;
|
||||
case SDLK_KP_1: return "SDLK_KP_1"; break;
|
||||
case SDLK_KP_2: return "SDLK_KP_2"; break;
|
||||
case SDLK_KP_3: return "SDLK_KP_3"; break;
|
||||
case SDLK_KP_4: return "SDLK_KP_4"; break;
|
||||
case SDLK_KP_5: return "SDLK_KP_5"; break;
|
||||
case SDLK_KP_6: return "SDLK_KP_6"; break;
|
||||
case SDLK_KP_7: return "SDLK_KP_7"; break;
|
||||
case SDLK_KP_8: return "SDLK_KP_8"; break;
|
||||
case SDLK_KP_9: return "SDLK_KP_9"; break;
|
||||
case SDLK_KP_0: return "SDLK_KP_0"; break;
|
||||
case SDLK_KP_PERIOD: return "SDLK_KP_PERIOD"; break;
|
||||
case SDLK_APPLICATION: return "SDLK_APPLICATION"; break;
|
||||
case SDLK_POWER: return "SDLK_POWER"; break;
|
||||
case SDLK_KP_EQUALS: return "SDLK_KP_EQUALS"; break;
|
||||
case SDLK_F13: return "SDLK_F13"; break;
|
||||
case SDLK_F14: return "SDLK_F14"; break;
|
||||
case SDLK_F15: return "SDLK_F15"; break;
|
||||
case SDLK_F16: return "SDLK_F16"; break;
|
||||
case SDLK_F17: return "SDLK_F17"; break;
|
||||
case SDLK_F18: return "SDLK_F18"; break;
|
||||
case SDLK_F19: return "SDLK_F19"; break;
|
||||
case SDLK_F20: return "SDLK_F20"; break;
|
||||
case SDLK_F21: return "SDLK_F21"; break;
|
||||
case SDLK_F22: return "SDLK_F22"; break;
|
||||
case SDLK_F23: return "SDLK_F23"; break;
|
||||
case SDLK_F24: return "SDLK_F24"; break;
|
||||
case SDLK_EXECUTE: return "SDLK_EXECUTE"; break;
|
||||
case SDLK_HELP: return "SDLK_HELP"; break;
|
||||
case SDLK_MENU: return "SDLK_MENU"; break;
|
||||
case SDLK_SELECT: return "SDLK_SELECT"; break;
|
||||
case SDLK_STOP: return "SDLK_STOP"; break;
|
||||
case SDLK_AGAIN: return "SDLK_AGAIN"; break;
|
||||
case SDLK_UNDO: return "SDLK_UNDO"; break;
|
||||
case SDLK_CUT: return "SDLK_CUT"; break;
|
||||
case SDLK_COPY: return "SDLK_COPY"; break;
|
||||
case SDLK_PASTE: return "SDLK_PASTE"; break;
|
||||
case SDLK_FIND: return "SDLK_FIND"; break;
|
||||
case SDLK_MUTE: return "SDLK_MUTE"; break;
|
||||
case SDLK_VOLUMEUP: return "SDLK_VOLUMEUP"; break;
|
||||
case SDLK_VOLUMEDOWN: return "SDLK_VOLUMEDOWN"; break;
|
||||
case SDLK_KP_COMMA: return "SDLK_KP_COMMA"; break;
|
||||
case SDLK_KP_EQUALSAS400: return "SDLK_KP_EQUALSAS400"; break;
|
||||
case SDLK_ALTERASE: return "SDLK_ALTERASE"; break;
|
||||
case SDLK_SYSREQ: return "SDLK_SYSREQ"; break;
|
||||
case SDLK_CANCEL: return "SDLK_CANCEL"; break;
|
||||
case SDLK_CLEAR: return "SDLK_CLEAR"; break;
|
||||
case SDLK_PRIOR: return "SDLK_PRIOR"; break;
|
||||
case SDLK_RETURN2: return "SDLK_RETURN2"; break;
|
||||
case SDLK_SEPARATOR: return "SDLK_SEPARATOR"; break;
|
||||
case SDLK_OUT: return "SDLK_OUT"; break;
|
||||
case SDLK_OPER: return "SDLK_OPER"; break;
|
||||
case SDLK_CLEARAGAIN: return "SDLK_CLEARAGAIN"; break;
|
||||
case SDLK_CRSEL: return "SDLK_CRSEL"; break;
|
||||
case SDLK_EXSEL: return "SDLK_EXSEL"; break;
|
||||
case SDLK_KP_00: return "SDLK_KP_00"; break;
|
||||
case SDLK_KP_000: return "SDLK_KP_000"; break;
|
||||
case SDLK_THOUSANDSSEPARATOR: return "SDLK_THOUSANDSSEPARATOR"; break;
|
||||
case SDLK_DECIMALSEPARATOR: return "SDLK_DECIMALSEPARATOR"; break;
|
||||
case SDLK_CURRENCYUNIT: return "SDLK_CURRENCYUNIT"; break;
|
||||
case SDLK_CURRENCYSUBUNIT: return "SDLK_CURRENCYSUBUNIT"; break;
|
||||
case SDLK_KP_LEFTPAREN: return "SDLK_KP_LEFTPAREN"; break;
|
||||
case SDLK_KP_RIGHTPAREN: return "SDLK_KP_RIGHTPAREN"; break;
|
||||
case SDLK_KP_LEFTBRACE: return "SDLK_KP_LEFTBRACE"; break;
|
||||
case SDLK_KP_RIGHTBRACE: return "SDLK_KP_RIGHTBRACE"; break;
|
||||
case SDLK_KP_TAB: return "SDLK_KP_TAB"; break;
|
||||
case SDLK_KP_BACKSPACE: return "SDLK_KP_BACKSPACE"; break;
|
||||
case SDLK_KP_A: return "SDLK_KP_A"; break;
|
||||
case SDLK_KP_B: return "SDLK_KP_B"; break;
|
||||
case SDLK_KP_C: return "SDLK_KP_C"; break;
|
||||
case SDLK_KP_D: return "SDLK_KP_D"; break;
|
||||
case SDLK_KP_E: return "SDLK_KP_E"; break;
|
||||
case SDLK_KP_F: return "SDLK_KP_F"; break;
|
||||
case SDLK_KP_XOR: return "SDLK_KP_XOR"; break;
|
||||
case SDLK_KP_POWER: return "SDLK_KP_POWER"; break;
|
||||
case SDLK_KP_PERCENT: return "SDLK_KP_PERCENT"; break;
|
||||
case SDLK_KP_LESS: return "SDLK_KP_LESS"; break;
|
||||
case SDLK_KP_GREATER: return "SDLK_KP_GREATER"; break;
|
||||
case SDLK_KP_AMPERSAND: return "SDLK_KP_AMPERSAND"; break;
|
||||
case SDLK_KP_DBLAMPERSAND: return "SDLK_KP_DBLAMPERSAND"; break;
|
||||
case SDLK_KP_VERTICALBAR: return "SDLK_KP_VERTICALBAR"; break;
|
||||
case SDLK_KP_DBLVERTICALBAR: return "SDLK_KP_DBLVERTICALBAR"; break;
|
||||
case SDLK_KP_COLON: return "SDLK_KP_COLON"; break;
|
||||
case SDLK_KP_HASH: return "SDLK_KP_HASH"; break;
|
||||
case SDLK_KP_SPACE: return "SDLK_KP_SPACE"; break;
|
||||
case SDLK_KP_AT: return "SDLK_KP_AT"; break;
|
||||
case SDLK_KP_EXCLAM: return "SDLK_KP_EXCLAM"; break;
|
||||
case SDLK_KP_MEMSTORE: return "SDLK_KP_MEMSTORE"; break;
|
||||
case SDLK_KP_MEMRECALL: return "SDLK_KP_MEMRECALL"; break;
|
||||
case SDLK_KP_MEMCLEAR: return "SDLK_KP_MEMCLEAR"; break;
|
||||
case SDLK_KP_MEMADD: return "SDLK_KP_MEMADD"; break;
|
||||
case SDLK_KP_MEMSUBTRACT: return "SDLK_KP_MEMSUBTRACT"; break;
|
||||
case SDLK_KP_MEMMULTIPLY: return "SDLK_KP_MEMMULTIPLY"; break;
|
||||
case SDLK_KP_MEMDIVIDE: return "SDLK_KP_MEMDIVIDE"; break;
|
||||
case SDLK_KP_PLUSMINUS: return "SDLK_KP_PLUSMINUS"; break;
|
||||
case SDLK_KP_CLEAR: return "SDLK_KP_CLEAR"; break;
|
||||
case SDLK_KP_CLEARENTRY: return "SDLK_KP_CLEARENTRY"; break;
|
||||
case SDLK_KP_BINARY: return "SDLK_KP_BINARY"; break;
|
||||
case SDLK_KP_OCTAL: return "SDLK_KP_OCTAL"; break;
|
||||
case SDLK_KP_DECIMAL: return "SDLK_KP_DECIMAL"; break;
|
||||
case SDLK_KP_HEXADECIMAL: return "SDLK_KP_HEXADECIMAL"; break;
|
||||
case SDLK_LCTRL: return "SDLK_LCTRL"; break;
|
||||
case SDLK_LSHIFT: return "SDLK_LSHIFT"; break;
|
||||
case SDLK_LALT: return "SDLK_LALT"; break;
|
||||
case SDLK_LGUI: return "SDLK_LGUI"; break;
|
||||
case SDLK_RCTRL: return "SDLK_RCTRL"; break;
|
||||
case SDLK_RSHIFT: return "SDLK_RSHIFT"; break;
|
||||
case SDLK_RALT: return "SDLK_RALT"; break;
|
||||
case SDLK_RGUI: return "SDLK_RGUI"; break;
|
||||
case SDLK_MODE: return "SDLK_MODE"; break;
|
||||
case SDLK_SLEEP: return "SDLK_SLEEP"; break;
|
||||
case SDLK_WAKE: return "SDLK_WAKE"; break;
|
||||
case SDLK_CHANNEL_INCREMENT: return "SDLK_CHANNEL_INCREMENT"; break;
|
||||
case SDLK_CHANNEL_DECREMENT: return "SDLK_CHANNEL_DECREMENT"; break;
|
||||
case SDLK_MEDIA_PLAY: return "SDLK_MEDIA_PLAY"; break;
|
||||
case SDLK_MEDIA_PAUSE: return "SDLK_MEDIA_PAUSE"; break;
|
||||
case SDLK_MEDIA_RECORD: return "SDLK_MEDIA_RECORD"; break;
|
||||
case SDLK_MEDIA_FAST_FORWARD: return "SDLK_MEDIA_FAST_FORWARD"; break;
|
||||
case SDLK_MEDIA_REWIND: return "SDLK_MEDIA_REWIND"; break;
|
||||
case SDLK_MEDIA_NEXT_TRACK: return "SDLK_MEDIA_NEXT_TRACK"; break;
|
||||
case SDLK_MEDIA_PREVIOUS_TRACK: return "SDLK_MEDIA_PREVIOUS_TRACK"; break;
|
||||
case SDLK_MEDIA_STOP: return "SDLK_MEDIA_STOP"; break;
|
||||
case SDLK_MEDIA_EJECT: return "SDLK_MEDIA_EJECT"; break;
|
||||
case SDLK_MEDIA_PLAY_PAUSE: return "SDLK_MEDIA_PLAY_PAUSE"; break;
|
||||
case SDLK_MEDIA_SELECT: return "SDLK_MEDIA_SELECT"; break;
|
||||
case SDLK_AC_NEW: return "SDLK_AC_NEW"; break;
|
||||
case SDLK_AC_OPEN: return "SDLK_AC_OPEN"; break;
|
||||
case SDLK_AC_CLOSE: return "SDLK_AC_CLOSE"; break;
|
||||
case SDLK_AC_EXIT: return "SDLK_AC_EXIT"; break;
|
||||
case SDLK_AC_SAVE: return "SDLK_AC_SAVE"; break;
|
||||
case SDLK_AC_PRINT: return "SDLK_AC_PRINT"; break;
|
||||
case SDLK_AC_PROPERTIES: return "SDLK_AC_PROPERTIES"; break;
|
||||
case SDLK_AC_SEARCH: return "SDLK_AC_SEARCH"; break;
|
||||
case SDLK_AC_HOME: return "SDLK_AC_HOME"; break;
|
||||
case SDLK_AC_BACK: return "SDLK_AC_BACK"; break;
|
||||
case SDLK_AC_FORWARD: return "SDLK_AC_FORWARD"; break;
|
||||
case SDLK_AC_STOP: return "SDLK_AC_STOP"; break;
|
||||
case SDLK_AC_REFRESH: return "SDLK_AC_REFRESH"; break;
|
||||
case SDLK_AC_BOOKMARKS: return "SDLK_AC_BOOKMARKS"; break;
|
||||
case SDLK_SOFTLEFT: return "SDLK_SOFTLEFT"; break;
|
||||
case SDLK_SOFTRIGHT: return "SDLK_SOFTRIGHT"; break;
|
||||
case SDLK_CALL: return "SDLK_CALL"; break;
|
||||
case SDLK_ENDCALL: return "SDLK_ENDCALL"; break;
|
||||
case SDLK_LEFT_TAB: return "SDLK_LEFT_TAB"; break;
|
||||
case SDLK_LEVEL5_SHIFT: return "SDLK_LEVEL5_SHIFT"; break;
|
||||
case SDLK_MULTI_KEY_COMPOSE: return "SDLK_MULTI_KEY_COMPOSE"; break;
|
||||
case SDLK_LMETA: return "SDLK_LMETA"; break;
|
||||
case SDLK_RMETA: return "SDLK_RMETA"; break;
|
||||
case SDLK_LHYPER: return "SDLK_LHYPER"; break;
|
||||
case SDLK_RHYPER: return "SDLK_RHYPER"; break;
|
||||
default: return "SDLK_INVALID";
|
||||
}
|
||||
}
|
||||
|
||||
struct { String string; EventKind value; } MouseConversionTable[] = {
|
||||
{"mousex1", EVENT_MOUSE_X1},
|
||||
{"mousex2", EVENT_MOUSE_X2},
|
||||
{"mouseleft", EVENT_MOUSE_LEFT},
|
||||
{"mouseright", EVENT_MOUSE_RIGHT},
|
||||
{"mousemiddle", EVENT_MOUSE_MIDDLE},
|
||||
};
|
||||
|
||||
struct { String string; SDL_Keycode value; } SDLKeycodeConversionTable[] = {
|
||||
{"enter", SDLK_RETURN},
|
||||
{"escape", SDLK_ESCAPE},
|
||||
{"backspace", SDLK_BACKSPACE},
|
||||
{"tab", SDLK_TAB},
|
||||
{"space", SDLK_SPACE},
|
||||
{"minus", SDLK_MINUS},
|
||||
{"period", SDLK_PERIOD},
|
||||
{"slash", SDLK_SLASH},
|
||||
{"0", SDLK_0},
|
||||
{"1", SDLK_1},
|
||||
{"2", SDLK_2},
|
||||
{"3", SDLK_3},
|
||||
{"4", SDLK_4},
|
||||
{"5", SDLK_5},
|
||||
{"6", SDLK_6},
|
||||
{"7", SDLK_7},
|
||||
{"8", SDLK_8},
|
||||
{"9", SDLK_9},
|
||||
{"semicolon", SDLK_SEMICOLON},
|
||||
{"less", SDLK_LESS},
|
||||
{"equals", SDLK_EQUALS},
|
||||
{"greater", SDLK_GREATER},
|
||||
{"leftbracket", SDLK_LEFTBRACKET},
|
||||
{"backslash", SDLK_BACKSLASH},
|
||||
{"rightbracket", SDLK_RIGHTBRACKET},
|
||||
{"grave", SDLK_GRAVE},
|
||||
{"a", SDLK_A},
|
||||
{"b", SDLK_B},
|
||||
{"c", SDLK_C},
|
||||
{"d", SDLK_D},
|
||||
{"e", SDLK_E},
|
||||
{"f", SDLK_F},
|
||||
{"g", SDLK_G},
|
||||
{"h", SDLK_H},
|
||||
{"i", SDLK_I},
|
||||
{"j", SDLK_J},
|
||||
{"k", SDLK_K},
|
||||
{"l", SDLK_L},
|
||||
{"m", SDLK_M},
|
||||
{"n", SDLK_N},
|
||||
{"o", SDLK_O},
|
||||
{"p", SDLK_P},
|
||||
{"q", SDLK_Q},
|
||||
{"r", SDLK_R},
|
||||
{"s", SDLK_S},
|
||||
{"t", SDLK_T},
|
||||
{"u", SDLK_U},
|
||||
{"v", SDLK_V},
|
||||
{"w", SDLK_W},
|
||||
{"x", SDLK_X},
|
||||
{"y", SDLK_Y},
|
||||
{"z", SDLK_Z},
|
||||
{"delete", SDLK_DELETE},
|
||||
{"capslock", SDLK_CAPSLOCK},
|
||||
{"f1", SDLK_F1},
|
||||
{"f2", SDLK_F2},
|
||||
{"f3", SDLK_F3},
|
||||
{"f4", SDLK_F4},
|
||||
{"f5", SDLK_F5},
|
||||
{"f6", SDLK_F6},
|
||||
{"f7", SDLK_F7},
|
||||
{"f8", SDLK_F8},
|
||||
{"f9", SDLK_F9},
|
||||
{"f10", SDLK_F10},
|
||||
{"f11", SDLK_F11},
|
||||
{"f12", SDLK_F12},
|
||||
{"insert", SDLK_INSERT},
|
||||
{"home", SDLK_HOME},
|
||||
{"pageup", SDLK_PAGEUP},
|
||||
{"end", SDLK_END},
|
||||
{"pagedown", SDLK_PAGEDOWN},
|
||||
{"right", SDLK_RIGHT},
|
||||
{"left", SDLK_LEFT},
|
||||
{"down", SDLK_DOWN},
|
||||
{"up", SDLK_UP},
|
||||
};
|
||||
|
||||
void FillEventWithBasicData(Event *event) {
|
||||
SDL_Keymod mod = SDL_GetModState();
|
||||
event->shift = (mod & SDL_KMOD_SHIFT) != 0;
|
||||
event->ctrl = (mod & SDL_KMOD_CTRL) != 0;
|
||||
event->alt = (mod & SDL_KMOD_ALT) != 0;
|
||||
event->super = (mod & SDL_KMOD_GUI) != 0;
|
||||
|
||||
float xmouse, ymouse;
|
||||
SDL_GetMouseState(&xmouse, &ymouse);
|
||||
event->xmouse = (int16_t)roundf(DPIScale * xmouse);
|
||||
event->ymouse = (int16_t)roundf(DPIScale * ymouse);
|
||||
|
||||
int xwindow, ywindow;
|
||||
SDL_GetWindowSizeInPixels(SDLWindow, &xwindow, &ywindow);
|
||||
event->xwindow = xwindow;
|
||||
event->ywindow = ywindow;
|
||||
event->text = "";
|
||||
}
|
||||
|
||||
Event TranslateSDLEvent(SDL_Event *input_event) {
|
||||
ProfileFunction();
|
||||
Event event = {};
|
||||
FillEventWithBasicData(&event);
|
||||
|
||||
switch (input_event->type) {
|
||||
case SDL_EVENT_QUIT: {
|
||||
event.kind = EVENT_QUIT;
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_KEY_DOWN: {
|
||||
event.kind = EVENT_KEY_PRESS;
|
||||
SDL_KeyboardEvent &key = input_event->key;
|
||||
event.key = key.key;
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_TEXT_INPUT: {
|
||||
event.kind = EVENT_TEXT_INPUT;
|
||||
SDL_TextInputEvent &b = input_event->text;
|
||||
String string = b.text;
|
||||
event.text = Intern(&GlobalInternTable, string).data;
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN: {
|
||||
SDL_MouseButtonEvent &b = input_event->button;
|
||||
event.xmouse = (int16_t)roundf(DPIScale * b.x);
|
||||
event.ymouse = (int16_t)roundf(DPIScale * b.y);
|
||||
event.clicks = b.clicks;
|
||||
if (b.button == SDL_BUTTON_LEFT) {
|
||||
event.kind = EVENT_MOUSE_LEFT;
|
||||
} else if (b.button == SDL_BUTTON_RIGHT) {
|
||||
event.kind = EVENT_MOUSE_RIGHT;
|
||||
} else if (b.button == SDL_BUTTON_MIDDLE) {
|
||||
event.kind = EVENT_MOUSE_MIDDLE;
|
||||
} else if (b.button == SDL_BUTTON_X1) {
|
||||
event.kind = EVENT_MOUSE_X1;
|
||||
} else if (b.button == SDL_BUTTON_X2) {
|
||||
event.kind = EVENT_MOUSE_X2;
|
||||
} else {
|
||||
event.kind = EVENT_NONE;
|
||||
event.clicks = 0;
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP: {
|
||||
SDL_MouseButtonEvent &b = input_event->button;
|
||||
event.xmouse = (int16_t)roundf(DPIScale * b.x);
|
||||
event.ymouse = (int16_t)roundf(DPIScale * b.y);
|
||||
if (b.button == SDL_BUTTON_LEFT) {
|
||||
event.kind = EVENT_MOUSE_LEFT_UP;
|
||||
} else if (b.button == SDL_BUTTON_RIGHT) {
|
||||
event.kind = EVENT_MOUSE_RIGHT_UP;
|
||||
} else if (b.button == SDL_BUTTON_MIDDLE) {
|
||||
event.kind = EVENT_MOUSE_MIDDLE_UP;
|
||||
} else if (b.button == SDL_BUTTON_X1) {
|
||||
event.kind = EVENT_MOUSE_X1_UP;
|
||||
} else if (b.button == SDL_BUTTON_X2) {
|
||||
event.kind = EVENT_MOUSE_X2_UP;
|
||||
} else {
|
||||
event.kind = EVENT_NONE;
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_MOUSE_WHEEL: {
|
||||
event.kind = EVENT_MOUSE_WHEEL;
|
||||
SDL_MouseWheelEvent &b = input_event->wheel;
|
||||
event.xmouse = (int16_t)roundf(DPIScale * b.mouse_x);
|
||||
event.ymouse = (int16_t)roundf(DPIScale * b.mouse_y);
|
||||
event.xwheel = b.x;
|
||||
event.ywheel = b.y;
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_MOUSE_MOTION: {
|
||||
event.kind = EVENT_UPDATE;
|
||||
} break;
|
||||
|
||||
case SDL_EVENT_DROP_FILE: {
|
||||
event.kind = EVENT_DROP_FILE;
|
||||
SDL_DropEvent &b = input_event->drop;
|
||||
String string = b.data;
|
||||
event.text = Intern(&GlobalInternTable, string).data;
|
||||
} break;
|
||||
default: {
|
||||
};
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
inline void AddEvent(Array<Event> *events, Event ev) {
|
||||
bool any_special_modifiers = (ev.ctrl || ev.alt || ev.super);
|
||||
if (ev.kind == EVENT_TEXT_INPUT && any_special_modifiers) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev.kind == EVENT_KEY_PRESS && !any_special_modifiers && ev.key >= SDLK_A && ev.key <= SDLK_Z) {
|
||||
if (ev.key != SDLK_RETURN) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ev.kind == EVENT_NONE) {
|
||||
return;
|
||||
}
|
||||
Add(events, ev);
|
||||
}
|
||||
|
||||
void GetEventsForFrame(Array<Event> *events) {
|
||||
ProfileFunction();
|
||||
For (EventPlayback) {
|
||||
Add(events, it);
|
||||
}
|
||||
|
||||
SDL_Event event;
|
||||
if (WaitForEventsState) {
|
||||
SDL_WaitEvent(&event);
|
||||
Event ev = TranslateSDLEvent(&event);
|
||||
AddEvent(events, ev);
|
||||
}
|
||||
|
||||
while (SDL_PollEvent(&event)) {
|
||||
Event ev = TranslateSDLEvent(&event);
|
||||
AddEvent(events, ev);
|
||||
}
|
||||
|
||||
if (events->len == 0) {
|
||||
Event event = {};
|
||||
FillEventWithBasicData(&event);
|
||||
event.kind = EVENT_UPDATE;
|
||||
Add(events, event);
|
||||
}
|
||||
|
||||
Assert(events->len);
|
||||
}
|
||||
|
||||
|
||||
#define Press(KEY) (event.key == KEY)
|
||||
#define CtrlPress(KEY) (event.key == KEY && event.ctrl)
|
||||
#define ShiftPress(KEY) (event.key == KEY && event.shift)
|
||||
#define AltPress(KEY) (event.key == KEY && event.alt)
|
||||
#define CtrlShiftPress(KEY) (event.key == KEY && event.ctrl && event.shift)
|
||||
#define CtrlAltPress(KEY) (event.key == KEY && event.ctrl && event.alt)
|
||||
#define AltShiftPress(KEY) (event.key == KEY && event.shift && event.alt)
|
||||
#define MouseVec2() Vec2 { (float)event.xmouse, (float)event.ymouse }
|
||||
#define MouseVec2I() Vec2I { (Int) event.xmouse, (Int)event.ymouse }
|
||||
#define Mouse(x) (event.kind == EVENT_MOUSE_##x)
|
||||
#define MousePress() (Mouse(LEFT) || Mouse(RIGHT) || Mouse(MIDDLE))
|
||||
#define MouseUp() (Mouse(LEFT_UP) || Mouse(RIGHT_UP) || Mouse(MIDDLE_UP))
|
||||
@@ -0,0 +1,160 @@
|
||||
float NewFuzzyRate(String16 s, String16 p) {
|
||||
float score = 0;
|
||||
// try to do this: https://github.com/junegunn/fzf/blob/master/src/algo/algo.go
|
||||
return score;
|
||||
}
|
||||
|
||||
float FuzzyRate(String16 s, String16 p) {
|
||||
float score = 0;
|
||||
for (Int outer_pi = 0; outer_pi < p.len; outer_pi += 1) {
|
||||
String16 pit = Skip(p, outer_pi);
|
||||
if (IsWhitespace(At(pit, 0))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float matching = 0;
|
||||
for (Int outer_si = 0; outer_si < s.len; outer_si += 1) {
|
||||
String16 sit = Skip(s, outer_si);
|
||||
if (IsWhitespace(At(sit, 0))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Int si = 0;
|
||||
Int pi = 0;
|
||||
for (;si < sit.len && pi < pit.len;) {
|
||||
while (si < sit.len && IsWhitespace(sit[si])) si += 1;
|
||||
while (pi < pit.len && IsWhitespace(pit[pi])) pi += 1;
|
||||
if (pi >= pit.len) break;
|
||||
if (si >= sit.len) break;
|
||||
|
||||
if (ToLowerCase(sit[si]) == ToLowerCase(pit[pi])) {
|
||||
matching += 1.0f;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
si += 1;
|
||||
pi += 1;
|
||||
}
|
||||
}
|
||||
score += matching;
|
||||
}
|
||||
score = score / (float)s.len;
|
||||
return score;
|
||||
}
|
||||
|
||||
inline bool MergeSortCompare(FuzzyPair *a, FuzzyPair *b) {
|
||||
bool result = a->rating > b->rating;
|
||||
return result;
|
||||
}
|
||||
|
||||
Array<FuzzyPair> FuzzySearchLines(Allocator allocator, Buffer *buffer, Int line_min, Int line_max, String16 needle) {
|
||||
ProfileFunction();
|
||||
if (line_min < 0 || line_min >= buffer->line_starts.len) return {};
|
||||
if (line_max < 0 || line_min > buffer->line_starts.len) return {};
|
||||
Array<FuzzyPair> ratings = {allocator};
|
||||
Reserve(&ratings, line_max - line_min + 4);
|
||||
for (Int i = line_min; i < line_max; i += 1) {
|
||||
String16 s = GetLineStringWithoutNL(buffer, i);
|
||||
|
||||
Int idx = 0;
|
||||
if (Seek(s, u"||>", &idx, SeekFlag_None)) {
|
||||
s = GetPrefix(s, idx);
|
||||
} else if (Seek(s, u"<||", &idx, SeekFlag_None)) {
|
||||
s = GetPrefix(s, idx);
|
||||
}
|
||||
s = Trim(s);
|
||||
|
||||
float rating = FuzzyRate(s, needle);
|
||||
Add(&ratings, {(int32_t)i, rating});
|
||||
}
|
||||
Array<FuzzyPair> temp = Copy(allocator, ratings);
|
||||
MergeSort(ratings.len, ratings.data, temp.data);
|
||||
return ratings;
|
||||
}
|
||||
|
||||
void UpdateFuzzySearchView() {
|
||||
ProfileFunction();
|
||||
Scratch scratch;
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
String16 line_string = GetLineStringWithoutNL(active.buffer, 0);
|
||||
uint64_t hash = HashBytes(line_string.data, line_string.len * sizeof(char16_t));
|
||||
if (active.view->prev_search_line_hash != hash) {
|
||||
active.view->prev_search_line_hash = hash;
|
||||
Array<FuzzyPair> ratings = FuzzySearchLines(scratch, active.buffer, 1, active.buffer->line_starts.len, line_string);
|
||||
|
||||
Buffer *scratch_buff = CreateScratchBuffer(scratch, active.buffer->cap);
|
||||
RawAppend(scratch_buff, line_string);
|
||||
For (IterateInReverse(&ratings)) {
|
||||
String16 s = GetLineStringWithoutNL(active.buffer, it.index);
|
||||
if (s.len == 0) continue;
|
||||
RawAppend(scratch_buff, u"\n");
|
||||
RawAppend(scratch_buff, s);
|
||||
}
|
||||
|
||||
Caret caret = active.view->carets[0];
|
||||
SaveCaretHistoryBeforeBeginEdit(active.buffer, active.view->carets);
|
||||
SelectEntireBuffer(active.view);
|
||||
Replace(active.view, GetString(scratch_buff));
|
||||
active.view->carets[0] = caret;
|
||||
}
|
||||
}
|
||||
|
||||
String16 FetchFuzzyViewLoadLine(View *view) {
|
||||
Buffer *buffer = GetBuffer(view->active_buffer);
|
||||
Range range = view->carets[0].range;
|
||||
String16 string = GetString(buffer, range);
|
||||
Int line = PosToLine(buffer, range.min);
|
||||
if (GetSize(range) == 0 || line == 0) {
|
||||
if (line == 0) {
|
||||
line = ClampTop(1ll, buffer->line_starts.len - 1ll);
|
||||
}
|
||||
string = GetLineStringWithoutNL(buffer, line);
|
||||
|
||||
Int idx = 0;
|
||||
String16 delim = u"||>";
|
||||
if (Seek(string, delim, &idx, SeekFlag_None)) {
|
||||
string = Skip(string, idx + delim.len);
|
||||
}
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
void CMD_OpenLoadWord() {
|
||||
Scratch scratch;
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
For (active.view->carets) {
|
||||
Range range = it.range;
|
||||
if (GetSize(range) == 0) {
|
||||
range = EncloseLoadWord(active.buffer, GetFront(it));
|
||||
}
|
||||
String16 load_word = GetString(active.buffer, range);
|
||||
Open(load_word);
|
||||
}
|
||||
} RegisterCommand(CMD_OpenLoadWord, "ctrl-q | f12", "Open a link under the caret (file link, url, command) or open the selection");
|
||||
|
||||
void CMD_OpenForFuzzyView() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
BSet main = GetBSet(PrimaryWindowID);
|
||||
NextActiveWindowID = main.window->id;
|
||||
if (active.view->carets.len == 1 && GetSize(active.view->carets[0].range) == 0) {
|
||||
String16 string = FetchFuzzyViewLoadLine(active.view);
|
||||
Open(string);
|
||||
} else {
|
||||
CMD_OpenLoadWord();
|
||||
}
|
||||
}
|
||||
|
||||
void CMD_OpenAndSetGotoNext() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
BSet main = GetBSet(PrimaryWindowID);
|
||||
NextActiveWindowID = main.window->id;
|
||||
String16 string = FetchFuzzyViewLoadLine(active.view);
|
||||
main.window->active_goto_list = active.view->id;
|
||||
main.window->goto_list_pos = active.view->carets[0].range.min;
|
||||
Open(string);
|
||||
}
|
||||
|
||||
void SetFuzzy(View *view) {
|
||||
AddCommand(&view->commands, "Open", OpenKeySet, CMD_OpenForFuzzyView);
|
||||
view->update_hook = UpdateFuzzySearchView;
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
SDL_Window *SDLWindow;
|
||||
bool IsInFullscreen;
|
||||
int FullScreenSizeX, FullScreenSizeY;
|
||||
int FullScreenPositionX, FullScreenPositionY;
|
||||
bool Testing = false;
|
||||
bool AppIsRunning = true;
|
||||
bool WaitForEventsState = true;
|
||||
bool RunGCThisFrame;
|
||||
bool SearchCaseSensitive = false;
|
||||
bool SearchWordBoundary = false;
|
||||
bool BreakOnError = false;
|
||||
Int ErrorCount;
|
||||
|
||||
Allocator SysAllocator = {SystemAllocatorProc};
|
||||
String ConfigDir;
|
||||
float DPIScale = 1.0f;
|
||||
|
||||
// @WARNING: be careful about using this, should only be used for debugging
|
||||
// the problem with this is that we want events to be reproducible.
|
||||
// We eat as many events as we can in a frame, we abstract the frame and so on.
|
||||
// Dont use it
|
||||
Int FrameID;
|
||||
|
||||
WindowID WindowIDs;
|
||||
ViewID ViewIDs;
|
||||
Int BufferIDs;
|
||||
|
||||
Array<Window *> Windows;
|
||||
Array<View *> Views;
|
||||
Array<Buffer *> Buffers;
|
||||
|
||||
View *LogView;
|
||||
Buffer *LogBuffer;
|
||||
|
||||
BufferID NullBufferID;
|
||||
ViewID NullViewID;
|
||||
WindowID NullWindowID;
|
||||
|
||||
#if PLUGIN_SEARCH_WINDOW
|
||||
WindowID SearchWindowID;
|
||||
ViewID SearchViewID;
|
||||
BufferID SearchBufferID;
|
||||
#endif
|
||||
|
||||
Trigger *EscapeKey;
|
||||
Trigger *EnterKey;
|
||||
Trigger *OpenKeySet;
|
||||
Trigger *EnterOrEscapeKeySet;
|
||||
Trigger *AltEnterKeySet;
|
||||
Trigger *ShiftEnterKeySet;
|
||||
|
||||
WindowID NextActiveWindowID;
|
||||
WindowID ActiveWindowID;
|
||||
WindowID PrimaryWindowID;
|
||||
WindowID ScrollbarSelected = {-1};
|
||||
WindowID DocumentSelected = {-1};
|
||||
WindowID ResizerSelected = {-1};
|
||||
WindowID ResizerHover = {-1};
|
||||
Caret DocumentAnchor;
|
||||
Vec2I MouseMiddleAnchor;
|
||||
|
||||
RandomSeed UniqueBufferNameSeed = {};
|
||||
Array<Event> EventPlayback;
|
||||
BlockArena Perm;
|
||||
|
||||
// clipboard
|
||||
BlockArena ClipboardArena;
|
||||
String16 SavedClipboardString;
|
||||
Array<String16> SavedClipboardCarets = {SysAllocator};
|
||||
|
||||
struct InternTable {
|
||||
HashTable<String> strings; // general allocator
|
||||
BlockArena arena;
|
||||
};
|
||||
|
||||
String Intern(InternTable *table, String string) {
|
||||
String *value = table->strings.get(string);
|
||||
if (!value) {
|
||||
String copy = Copy(table->arena, string);
|
||||
table->strings.put(copy, copy);
|
||||
return copy;
|
||||
}
|
||||
return *value;
|
||||
}
|
||||
|
||||
// We use the intern table to optimize for space, I don't want to worry about freeing
|
||||
// buffer names and event text data so let it all accumulate and interning will at least
|
||||
// optimize worst offenders (like event text)
|
||||
InternTable GlobalInternTable;
|
||||
|
||||
Array<Command> GlobalCommands;
|
||||
Array<FunctionData> TestFunctions;
|
||||
Array<Variable> Variables;
|
||||
|
||||
Array<Process> ActiveProcesses = {};
|
||||
Array<String> ProcessEnviroment = {};
|
||||
|
||||
struct CoData { mco_coro *co; String name; BlockArena arena; bool dont_wait_until_resolved; void *user_ctx; };
|
||||
Array<CoData> ActiveCoroutines;
|
||||
|
||||
Color GruvboxDark0Hard = {0x1d, 0x20, 0x21, 0xff};
|
||||
Color GruvboxDark0 = {0x28, 0x28, 0x28, 0xff};
|
||||
Color GruvboxDark0Soft = {0x32, 0x30, 0x2f, 0xff};
|
||||
Color GruvboxDark1 = {0x3c, 0x38, 0x36, 0xff};
|
||||
Color GruvboxDark2 = {0x50, 0x49, 0x45, 0xff};
|
||||
Color GruvboxDark3 = {0x66, 0x5c, 0x54, 0xff};
|
||||
Color GruvboxDark4 = {0x7c, 0x6f, 0x64, 0xff};
|
||||
Color GruvboxGray245 = {0x92, 0x83, 0x74, 0xff};
|
||||
Color GruvboxGray244 = {0x92, 0x83, 0x74, 0xff};
|
||||
Color GruvboxLight0Hard = {0xf9, 0xf5, 0xd7, 0xff};
|
||||
Color GruvboxLight0 = {0xfb, 0xf1, 0xc7, 0xff};
|
||||
Color GruvboxLight0Soft = {0xf2, 0xe5, 0xbc, 0xff};
|
||||
Color GruvboxLight1 = {0xeb, 0xdb, 0xb2, 0xff};
|
||||
Color GruvboxLight2 = {0xd5, 0xc4, 0xa1, 0xff};
|
||||
Color GruvboxLight3 = {0xbd, 0xae, 0x93, 0xff};
|
||||
Color GruvboxLight4 = {0xa8, 0x99, 0x84, 0xff};
|
||||
Color GruvboxBrightRed = {0xfb, 0x49, 0x34, 0xff};
|
||||
Color GruvboxBrightGreen = {0xb8, 0xbb, 0x26, 0xff};
|
||||
Color GruvboxBrightYellow = {0xfa, 0xbd, 0x2f, 0xff};
|
||||
Color GruvboxBrightBlue = {0x83, 0xa5, 0x98, 0xff};
|
||||
Color GruvboxBrightPurple = {0xd3, 0x86, 0x9b, 0xff};
|
||||
Color GruvboxBrightAqua = {0x8e, 0xc0, 0x7c, 0xff};
|
||||
Color GruvboxBrightOrange = {0xfe, 0x80, 0x19, 0xff};
|
||||
Color GruvboxNeutralRed = {0xcc, 0x24, 0x1d, 0xff};
|
||||
Color GruvboxNeutralGreen = {0x98, 0x97, 0x1a, 0xff};
|
||||
Color GruvboxNeutralYellow = {0xd7, 0x99, 0x21, 0xff};
|
||||
Color GruvboxNeutralBlue = {0x45, 0x85, 0x88, 0xff};
|
||||
Color GruvboxNeutralPurple = {0xb1, 0x62, 0x86, 0xff};
|
||||
Color GruvboxNeutralAqua = {0x68, 0x9d, 0x6a, 0xff};
|
||||
Color GruvboxNeutralOrange = {0xd6, 0x5d, 0x0e, 0xff};
|
||||
Color GruvboxFadedRed = {0x9d, 0x00, 0x06, 0xff};
|
||||
Color GruvboxFadedGreen = {0x79, 0x74, 0x0e, 0xff};
|
||||
Color GruvboxFadedYellow = {0xb5, 0x76, 0x14, 0xff};
|
||||
Color GruvboxFadedBlue = {0x07, 0x66, 0x78, 0xff};
|
||||
Color GruvboxFadedPurple = {0x8f, 0x3f, 0x71, 0xff};
|
||||
Color GruvboxFadedAqua = {0x42, 0x7b, 0x58, 0xff};
|
||||
Color GruvboxFadedOrange = {0xaf, 0x3a, 0x03, 0xff};
|
||||
RegisterVariable(Color, TextColor, GruvboxDark0Hard);
|
||||
RegisterVariable(Color, BackgroundColor, GruvboxLight0Hard);
|
||||
RegisterVariable(Color, InactiveWindowColor, {0x00, 0x00, 0x00, 0x1F});
|
||||
RegisterVariable(Color, LineNumberTextColor, GruvboxDark4);
|
||||
RegisterVariable(Color, LineHighlightColor, GruvboxLight0Soft);
|
||||
RegisterVariable(Color, PrimaryCaretColor, GruvboxDark0Hard);
|
||||
RegisterVariable(Color, SecondaryCaretColor, GruvboxGray245);
|
||||
RegisterVariable(Color, SelectionColor, GruvboxLight1);
|
||||
RegisterVariable(Color, SelectedWhitespaceColor, GruvboxLight4);
|
||||
RegisterVariable(Color, MouseUnderlineColor, GruvboxDark0Hard);
|
||||
RegisterVariable(Color, CaretUnderlineColor, GruvboxGray245);
|
||||
RegisterVariable(Color, ScrollbarBackgroundColor, GruvboxLight2);
|
||||
RegisterVariable(Color, ScrollerColor, GruvboxLight1);
|
||||
RegisterVariable(Color, ScrollerSelectedColor, GruvboxLight0Hard);
|
||||
RegisterVariable(Color, SecondaryWindowStyle_TextColor, GruvboxDark0Hard);
|
||||
RegisterVariable(Color, SecondaryWindowStyle_BackgroundColor, GruvboxLight0Hard);
|
||||
RegisterVariable(Color, SecondaryWindowStyle_SelectionColor, GruvboxLight3);
|
||||
RegisterVariable(Color, SecondaryWindowStyle_LineHighlightColor, GruvboxLight0Soft);
|
||||
RegisterVariable(Color, SecondaryWindowStyle_OutlineColor, GruvboxLight3);
|
||||
RegisterVariable(Color, ResizerBackgroundColor, GruvboxLight0Hard);
|
||||
RegisterVariable(Color, ResizerOutlineColor, GruvboxLight3);
|
||||
RegisterVariable(Int, WaitForEvents, 1);
|
||||
RegisterVariable(Int, DrawLineNumbers, 1);
|
||||
RegisterVariable(Int, DrawScrollbar, 1);
|
||||
RegisterVariable(Int, IndentSize, 4);
|
||||
RegisterVariable(String, IndentKindWhichIsTabsOrSpaces, "spaces");
|
||||
RegisterVariable(Int, FontSize, 15);
|
||||
RegisterVariable(String, PathToFont, "");
|
||||
RegisterVariable(Float, UndoMergeTime, 0.3);
|
||||
RegisterVariable(Float, JumpHistoryMergeTime, 0.3);
|
||||
RegisterVariable(String, InternetBrowser, "firefox");
|
||||
RegisterVariable(String, OpenCodePatterns, ".c .h .cpp .hpp .cc .cxx .rs .go .zig .py .lua .js .ts .jsx .tsx .java .kt .swift .cs .rb .php .html .css .scss .bat .sh .bash .zsh .sql .asm .s .cmake .make .json .yaml .toml .ini .txt .md .rst .Makefile .Dockerfile .gitignore .bashrc .zshrc");
|
||||
RegisterVariable(String, OpenCodeExcludePatterns, "");
|
||||
RegisterVariable(Int, TrimTrailingWhitespace, 1);
|
||||
|
||||
// PROJECT_MANAGEMENT
|
||||
// Set at the beginning of the program to current directory
|
||||
RegisterVariable(String, ProjectDirectory, "");
|
||||
|
||||
// PLUGIN_BUILD_WINDOW
|
||||
RegisterVariable(String, Build1OnWindows, "build.bat");
|
||||
RegisterVariable(String, Build1OnUnix, "sh build.sh");
|
||||
RegisterVariable(String, Build2OnWindows, "build.bat release");
|
||||
RegisterVariable(String, Build2OnUnix, "sh build.sh release");
|
||||
|
||||
// PLUGIN_LOAD_VCVARS
|
||||
RegisterVariable(String, VCVarsPath, "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvars64.bat");
|
||||
|
||||
// PLUGIN_REMEDYBG
|
||||
RegisterVariable(String, BinaryUnderDebug, "");
|
||||
RegisterVariable(String, RemedyBGPath, "remedybg.exe");
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
Hi! Welcome to my world, the world where text is all around and plentiful. I'm tee,
|
||||
a simple text editor that hopefully will help you in your text journey!
|
||||
|
||||
Execute some of these using your mouse right button, hover over a
|
||||
word like ":New" and click!
|
||||
|
||||
:New (ctrl-n) - create new text file
|
||||
:OpenUpFolder (ctrl-o) - navigate directories
|
||||
:ShowCommands (ctrl-shift-p) - open up the command lister
|
||||
:ShowBufferList (ctrl-p) - open up a lister with open files
|
||||
|
||||
Alternatively you can open with your keyboard - ctrl-q / f12, try that also!
|
||||
|
||||
-----------------------------------------------------------------------------------------
|
||||
|
||||
Navigate to next parts of the guide by executing them like these commands you just played
|
||||
with:
|
||||
|
||||
:GuideBigPicture
|
||||
:GuideNavigation
|
||||
:GuideProjects
|
||||
:GuideBindings
|
||||
:GuideConfig
|
||||
@@ -0,0 +1,249 @@
|
||||
void CMD_Redo() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
RedoEdit(active.buffer, &active.view->carets);
|
||||
} RegisterCommand(CMD_Redo, "ctrl-shift-z", "Redo after undoing changes to the buffer");
|
||||
|
||||
void CMD_Undo() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
UndoEdit(active.buffer, &active.view->carets);
|
||||
} RegisterCommand(CMD_Undo, "ctrl-z", "Undo last change you made to the buffer");
|
||||
|
||||
void CMD_EncloseLine() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
EncloseLine(active.view);
|
||||
} RegisterCommand(CMD_EncloseLine, "ctrl-l", "Select the entire line on which your caret is placed");
|
||||
|
||||
void CMD_SelectAll() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
SelectEntireBuffer(active.view);
|
||||
active.view->update_scroll = false;
|
||||
} RegisterCommand(CMD_SelectAll, "ctrl-a", "Select the entire buffer");
|
||||
|
||||
void CMD_KillSelectedLines() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
KillSelectedLines(active.view);
|
||||
} RegisterCommand(CMD_KillSelectedLines, "ctrl-shift-k", "Delete the selected lines, don't put to clipboard");
|
||||
|
||||
void CMD_IndentSelectedLines() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
IndentSelectedLines(active.view);
|
||||
} RegisterCommand(CMD_IndentSelectedLines, "ctrl-rightbracket | tab", "");
|
||||
|
||||
void CMD_DedentSelectedLines() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
IndentSelectedLines(active.view, true);
|
||||
} RegisterCommand(CMD_DedentSelectedLines, "ctrl-leftbracket | shift-tab", "");
|
||||
|
||||
void CMD_DuplicateLineDown() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
DuplicateLine(active.view, DIR_DOWN);
|
||||
} RegisterCommand(CMD_DuplicateLineDown, "ctrl-alt-down", "");
|
||||
|
||||
void CMD_CreateCursorDown() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
CreateCursorVertical(active.view, DIR_DOWN);
|
||||
} RegisterCommand(CMD_CreateCursorDown, "alt-shift-down", "");
|
||||
|
||||
void CMD_SelectDownToEmptyLine() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCarets(active.view, DIR_DOWN, CTRL_PRESSED, SHIFT_PRESS);
|
||||
} RegisterCommand(CMD_SelectDownToEmptyLine, "ctrl-shift-down", "");
|
||||
|
||||
void CMD_MoveLineDown() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCaretsLine(active.view, DIR_DOWN);
|
||||
} RegisterCommand(CMD_MoveLineDown, "alt-down", "");
|
||||
|
||||
void CMD_MoveDownToEmptyLine() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCarets(active.view, DIR_DOWN, CTRL_PRESSED);
|
||||
} RegisterCommand(CMD_MoveDownToEmptyLine, "ctrl-down", "");
|
||||
|
||||
void CMD_SelectDown() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCarets(active.view, DIR_DOWN, false, SHIFT_PRESS);
|
||||
} RegisterCommand(CMD_SelectDown, "shift-down", "");
|
||||
|
||||
void CMD_MoveDown() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCarets(active.view, DIR_DOWN);
|
||||
} RegisterCommand(CMD_MoveDown, "down", "");
|
||||
|
||||
void CMD_DuplicateLineUp() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
DuplicateLine(active.view, DIR_UP);
|
||||
} RegisterCommand(CMD_DuplicateLineUp, "ctrl-alt-up", "");
|
||||
|
||||
void CMD_CreateCursorUp() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
CreateCursorVertical(active.view, DIR_UP);
|
||||
} RegisterCommand(CMD_CreateCursorUp, "alt-shift-up", "");
|
||||
|
||||
void CMD_SelectUpToEmptyLine() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCarets(active.view, DIR_UP, CTRL_PRESSED, SHIFT_PRESS);
|
||||
} RegisterCommand(CMD_SelectUpToEmptyLine, "ctrl-shift-up", "");
|
||||
|
||||
void CMD_MoveLineUp() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCaretsLine(active.view, DIR_UP);
|
||||
} RegisterCommand(CMD_MoveLineUp, "alt-up", "");
|
||||
|
||||
void CMD_MoveUpToEmptyLine() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCarets(active.view, DIR_UP, CTRL_PRESSED);
|
||||
} RegisterCommand(CMD_MoveUpToEmptyLine, "ctrl-up", "");
|
||||
|
||||
void CMD_SelectUp() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCarets(active.view, DIR_UP, false, SHIFT_PRESS);
|
||||
} RegisterCommand(CMD_SelectUp, "shift-up", "");
|
||||
|
||||
void CMD_MoveUp() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCarets(active.view, DIR_UP);
|
||||
} RegisterCommand(CMD_MoveUp, "up", "");
|
||||
|
||||
void CMD_BoundarySelectLeft() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCarets(active.view, DIR_LEFT, CTRL_PRESSED, SHIFT_PRESS);
|
||||
} RegisterCommand(CMD_BoundarySelectLeft, "ctrl-shift-left", "");
|
||||
|
||||
void CMD_BoundaryMoveLeft() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCarets(active.view, DIR_LEFT, CTRL_PRESSED);
|
||||
} RegisterCommand(CMD_BoundaryMoveLeft, "ctrl-left", "");
|
||||
|
||||
void CMD_SelectLeft() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCarets(active.view, DIR_LEFT, false, SHIFT_PRESS);
|
||||
} RegisterCommand(CMD_SelectLeft, "shift-left", "");
|
||||
|
||||
void CMD_MoveLeft() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCarets(active.view, DIR_LEFT);
|
||||
} RegisterCommand(CMD_MoveLeft, "left", "");
|
||||
|
||||
void CMD_BoundarySelectRight() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCarets(active.view, DIR_RIGHT, CTRL_PRESSED, SHIFT_PRESS);
|
||||
} RegisterCommand(CMD_BoundarySelectRight, "ctrl-shift-right", "");
|
||||
|
||||
void CMD_BoundaryMoveRight() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCarets(active.view, DIR_RIGHT, CTRL_PRESSED);
|
||||
} RegisterCommand(CMD_BoundaryMoveRight, "ctrl-right", "");
|
||||
|
||||
void CMD_SelectRight() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCarets(active.view, DIR_RIGHT, false, SHIFT_PRESS);
|
||||
} RegisterCommand(CMD_SelectRight, "shift-right", "");
|
||||
|
||||
void CMD_MoveRight() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCarets(active.view, DIR_RIGHT);
|
||||
} RegisterCommand(CMD_MoveRight, "right", "");
|
||||
|
||||
void CMD_MoveUpAPage() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCursorByPageSize(active.window, DIR_UP);
|
||||
} RegisterCommand(CMD_MoveUpAPage, "pageup", "");
|
||||
|
||||
void CMD_SelectUpPage() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCursorByPageSize(active.window, DIR_UP, SHIFT_PRESS);
|
||||
} RegisterCommand(CMD_SelectUpPage, "shift-pageup", "");
|
||||
|
||||
void CMD_MoveToStart() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
SelectRange(active.view, MakeRange(0));
|
||||
} RegisterCommand(CMD_MoveToStart, "ctrl-pageup", "");
|
||||
|
||||
void CMD_SelectDownPage() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCursorByPageSize(active.window, DIR_DOWN, SHIFT_PRESS);
|
||||
} RegisterCommand(CMD_SelectDownPage, "shift-pagedown", "");
|
||||
|
||||
void CMD_MoveToEnd() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
SelectRange(active.view, MakeRange(active.buffer->len));
|
||||
} RegisterCommand(CMD_MoveToEnd, "ctrl-pagedown", "");
|
||||
|
||||
void CMD_MoveDownPage() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCursorByPageSize(active.window, DIR_DOWN);
|
||||
} RegisterCommand(CMD_MoveDownPage, "pagedown", "");
|
||||
|
||||
void CMD_SelectToLineStart() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCursorToSide(active.view, DIR_LEFT, SHIFT_PRESS);
|
||||
} RegisterCommand(CMD_SelectToLineStart, "shift-home", "");
|
||||
|
||||
void CMD_MoveToLineStart() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCursorToSide(active.view, DIR_LEFT);
|
||||
} RegisterCommand(CMD_MoveToLineStart, "home", "");
|
||||
|
||||
void CMD_MoveToLineEnd() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCursorToSide(active.view, DIR_RIGHT);
|
||||
} RegisterCommand(CMD_MoveToLineEnd, "end", "");
|
||||
|
||||
void CMD_SelectToLineEnd() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
MoveCursorToSide(active.view, DIR_RIGHT, SHIFT_PRESS);
|
||||
} RegisterCommand(CMD_SelectToLineEnd, "shift-end", "");
|
||||
|
||||
void CMD_DeleteCharacter() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
Delete(active.view, DIR_LEFT);
|
||||
} RegisterCommand(CMD_DeleteCharacter, "shift-backspace | backspace", "");
|
||||
|
||||
void CMD_DeleteBoundary() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
Delete(active.view, DIR_LEFT, SHIFT_PRESS);
|
||||
} RegisterCommand(CMD_DeleteBoundary, "ctrl-backspace", "");
|
||||
|
||||
void CMD_DeleteForward() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
Delete(active.view, DIR_RIGHT);
|
||||
} RegisterCommand(CMD_DeleteForward, "shift-delete | delete", "");
|
||||
|
||||
void CMD_DeleteForwardBoundary() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
Delete(active.view, DIR_RIGHT, SHIFT_PRESS);
|
||||
} RegisterCommand(CMD_DeleteForwardBoundary, "ctrl-delete", "");
|
||||
|
||||
void CMD_InsertNewLineUp() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
SaveCaretHistoryBeforeBeginEdit(active.buffer, active.view->carets);
|
||||
MoveCursorToSide(active.view, DIR_LEFT);
|
||||
IndentedNewLine(active.view);
|
||||
MoveCarets(active.view, DIR_UP);
|
||||
} RegisterCommand(CMD_InsertNewLineUp, "ctrl-shift-enter", "");
|
||||
|
||||
void CMD_InsertNewLineDown() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
SaveCaretHistoryBeforeBeginEdit(active.buffer, active.view->carets);
|
||||
MoveCursorToSide(active.view, DIR_RIGHT);
|
||||
IndentedNewLine(active.view);
|
||||
} RegisterCommand(CMD_InsertNewLineDown, "ctrl-enter", "");
|
||||
|
||||
void CMD_NewLine() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
IndentedNewLine(active.view);
|
||||
} RegisterCommand(CMD_NewLine, "enter | shift-enter", "");
|
||||
|
||||
void CMD_CreateCaretOnNextFind() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
String16 string = GetString(active.buffer, active.view->carets[0].range);
|
||||
Caret caret = FindNext(active.buffer, string, active.view->carets[0]);
|
||||
Insert(&active.view->carets, caret, 0);
|
||||
MergeCarets(active.buffer, &active.view->carets);
|
||||
} RegisterCommand(CMD_CreateCaretOnNextFind, "ctrl-d", "");
|
||||
|
||||
void CMD_ClearCarets() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
active.view->carets.len = 1;
|
||||
active.view->carets[0] = MakeCaret(GetFront(active.view->carets[0]));
|
||||
} RegisterCommand(CMD_ClearCarets, "escape", "Clear all carets and reset to 1 caret, also do some windowing stuff that closes things on escape");
|
||||
@@ -0,0 +1,75 @@
|
||||
WindowID BuildWindowID;
|
||||
ViewID BuildViewID;
|
||||
BufferID BuildBufferID;
|
||||
|
||||
void InitBuildWindow() {
|
||||
Window *window = CreateWind();
|
||||
BuildWindowID = window->id;
|
||||
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(ProjectDirectory, "build"));
|
||||
buffer->special = true;
|
||||
buffer->no_history = true;
|
||||
BuildBufferID = buffer->id;
|
||||
View *view = CreateView(buffer->id);
|
||||
view->special = true;
|
||||
BuildViewID = view->id;
|
||||
window->active_view = view->id;
|
||||
window->secondary_window_style = true;
|
||||
window->draw_line_highlight = true;
|
||||
window->primary = false;
|
||||
window->visible = false;
|
||||
window->lose_visibility_on_escape = true;
|
||||
window->jump_history = false;
|
||||
}
|
||||
|
||||
void LayoutBuildWindow(Rect2I *rect, int16_t wx, int16_t wy) {
|
||||
Window *window = GetWindow(BuildWindowID);
|
||||
Rect2I copy_rect = *rect;
|
||||
if (!window->visible) {
|
||||
rect = ©_rect;
|
||||
}
|
||||
Int barsize = window->font->line_spacing * 10;
|
||||
window->document_rect = window->total_rect = CutBottom(rect, barsize);
|
||||
}
|
||||
|
||||
BSet ExecBuild(String windows_cmd, String unix_cmd, String working_dir = ProjectDirectory) {
|
||||
SaveAll();
|
||||
BSet build = GetBSet(BuildWindowID);
|
||||
BSet main = GetBSet(PrimaryWindowID);
|
||||
SelectRange(build.view, Range{});
|
||||
ResetBuffer(build.buffer);
|
||||
if (OS_WINDOWS) {
|
||||
Exec(build.view->id, true, windows_cmd, working_dir);
|
||||
} else {
|
||||
Exec(build.view->id, true, unix_cmd, working_dir);
|
||||
}
|
||||
main.window->active_goto_list = build.view->id;
|
||||
main.window->goto_list_pos = 0;
|
||||
build.window->visible = true;
|
||||
return build;
|
||||
}
|
||||
|
||||
void CMD_Build1() {
|
||||
ExecBuild(Build1OnWindows, Build1OnUnix);
|
||||
} RegisterCommand(CMD_Build1, "f1", "Run Build1OnWindows or OnUnix in working directory, output is printed in a popup console and a special build buffer");
|
||||
|
||||
void CMD_Build2() {
|
||||
ExecBuild(Build2OnWindows, Build2OnUnix);
|
||||
} RegisterCommand(CMD_Build2, "f2", "Run Build2OnWindows or OnUnix in working directory, output is printed in a popup console and a special build buffer");
|
||||
|
||||
void CMD_RunFile() {
|
||||
Scratch scratch;
|
||||
BSet primary = GetBSet(PrimaryWindowID);
|
||||
String windows_command = Format(scratch, "cl %S -Fe:cfile.exe /Zi /FC /nologo /WX /W3 /wd4200 /wd4334 /diagnostics:column && cfile.exe && del cfile.*", primary.buffer->name);
|
||||
String unix_command = Format(scratch, "bash <<EOF\nclang %S -o cfile.exe -g -Wall -Wno-missing-braces -Wno-writable-strings -Wno-writable-strings -fsanitize=address -fdiagnostics-absolute-paths -lm \n./cfile.exe \nrm cfile.exe\nEOF", primary.buffer->name);
|
||||
ExecBuild(windows_command, unix_command, GetDirectory(primary.buffer));
|
||||
} RegisterCommand(CMD_RunFile, "", "Run and build current file, currently only C/C++ supported");
|
||||
|
||||
void CMD_ShowBuildWindow() {
|
||||
BSet main = GetBSet(BuildWindowID);
|
||||
if (ActiveWindowID != BuildWindowID) {
|
||||
main.window->visible = true;
|
||||
NextActiveWindowID = BuildWindowID;
|
||||
} else {
|
||||
main.window->visible = false;
|
||||
}
|
||||
} RegisterCommand(CMD_ShowBuildWindow, "ctrl-grave", "Toggles visibility of the build window");
|
||||
@@ -0,0 +1,85 @@
|
||||
WindowID CommandWindowID;
|
||||
|
||||
void CMD_ShowCommands() {
|
||||
BSet command_bar = GetBSet(CommandWindowID);
|
||||
command_bar.window->visible = true;
|
||||
NextActiveWindowID = command_bar.window->id;
|
||||
ResetBuffer(command_bar.buffer);
|
||||
For (GlobalCommands) {
|
||||
RawAppendf(command_bar.buffer, "\n:%-30S <|| ", it.name);
|
||||
if (it.docs.len) {
|
||||
RawAppendf(command_bar.buffer, "%S", it.docs);
|
||||
}
|
||||
}
|
||||
command_bar.view->update_scroll = true;
|
||||
SelectRange(command_bar.view, GetBufferBeginAsRange(command_bar.buffer));
|
||||
} RegisterCommand(CMD_ShowCommands, "ctrl-shift-p", "List available commands and their documentation inside the command window");
|
||||
|
||||
void CMD_ShowDebugBufferList() {
|
||||
BSet command_bar = GetBSet(CommandWindowID);
|
||||
command_bar.window->visible = true;
|
||||
NextActiveWindowID = command_bar.window->id;
|
||||
ResetBuffer(command_bar.buffer);
|
||||
For (Buffers) {
|
||||
bool is_special = it->special || it->temp || it->dont_try_to_save_in_bulk_ops;
|
||||
#if PLUGIN_DIRECTORY_NAVIGATION
|
||||
is_special |= it->is_dir;
|
||||
#endif
|
||||
if (!is_special) {
|
||||
continue;
|
||||
}
|
||||
RawAppendf(command_bar.buffer, "\n%S", it->name);
|
||||
}
|
||||
command_bar.view->update_scroll = true;
|
||||
SelectRange(command_bar.view, GetBufferBeginAsRange(command_bar.buffer));
|
||||
} RegisterCommand(CMD_ShowDebugBufferList, "ctrl-shift-alt-p", "Show full list of buffers, including the special ones that normally just clutter list");
|
||||
|
||||
void CMD_ShowBufferList() {
|
||||
BSet command_bar = GetBSet(CommandWindowID);
|
||||
command_bar.window->visible = true;
|
||||
NextActiveWindowID = command_bar.window->id;
|
||||
ResetBuffer(command_bar.buffer);
|
||||
For (Buffers) {
|
||||
bool is_special = it->special || it->temp || it->dont_try_to_save_in_bulk_ops;
|
||||
#if PLUGIN_DIRECTORY_NAVIGATION
|
||||
is_special |= it->is_dir;
|
||||
#endif
|
||||
if (is_special) {
|
||||
continue;
|
||||
}
|
||||
RawAppendf(command_bar.buffer, "\n%S", it->name);
|
||||
}
|
||||
command_bar.view->update_scroll = true;
|
||||
SelectRange(command_bar.view, GetBufferBeginAsRange(command_bar.buffer));
|
||||
} RegisterCommand(CMD_ShowBufferList, "ctrl-p", "List open buffers inside the command window that you can fuzzy search over");
|
||||
|
||||
void LayoutCommandWindow(Rect2I *rect, int16_t wx, int16_t wy) {
|
||||
Window *window = GetWindow(CommandWindowID);
|
||||
Rect2I copy_rect = *rect;
|
||||
if (!window->visible) {
|
||||
rect = ©_rect;
|
||||
}
|
||||
Int barsize = Clamp((Int)window->font->line_spacing*10, (Int)0, (Int)wx - 100);
|
||||
window->document_rect = window->total_rect = CutBottom(rect, barsize);
|
||||
}
|
||||
|
||||
void InitCommandWindow() {
|
||||
Window *window = CreateWind();
|
||||
CommandWindowID = window->id;
|
||||
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(ProjectDirectory, "command_bar"));
|
||||
buffer->special = true;
|
||||
buffer->no_history = true;
|
||||
View *view = CreateView(buffer->id);
|
||||
view->special = true;
|
||||
SetFuzzy(view);
|
||||
window->active_view = view->id;
|
||||
window->draw_line_numbers = false;
|
||||
window->draw_scrollbar = false;
|
||||
window->secondary_window_style = true;
|
||||
window->draw_line_highlight = true;
|
||||
window->primary = false;
|
||||
window->visible = false;
|
||||
window->sync_visibility_with_focus = true;
|
||||
window->lose_focus_on_escape = true;
|
||||
window->jump_history = false;
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
#if PLUGIN_CONFIG
|
||||
BufferID GlobalConfigBufferID;
|
||||
|
||||
void CMD_OpenConfig() {
|
||||
Buffer *buffer = GetBuffer(GlobalConfigBufferID);
|
||||
Open(buffer->name);
|
||||
} RegisterCommand(CMD_OpenConfig, "", "Open the global config file");
|
||||
|
||||
void CMD_OpenConfigOptions() {
|
||||
BSet main = GetBSet(PrimaryWindowID);
|
||||
JumpTempBuffer(&main);
|
||||
NextActiveWindowID = main.window->id;
|
||||
For (Variables) {
|
||||
RawAppendf(main.buffer, "\n:Set %-50S ", it.name);
|
||||
switch(it.type) {
|
||||
case VariableType_Color: RawAppendf(main.buffer, "%x", it.color->value); break;
|
||||
case VariableType_String: RawAppendf(main.buffer, "'%S'", *it.string); break;
|
||||
case VariableType_Int: RawAppendf(main.buffer, "%lld", (long long)*it.i); break;
|
||||
case VariableType_Float: RawAppendf(main.buffer, "%f", *it.f); break;
|
||||
default: InvalidCodepath();
|
||||
}
|
||||
}
|
||||
For (GlobalCommands) {
|
||||
RawAppendf(main.buffer, "\n:Set %-50S '%S'", it.name, it.binding);
|
||||
}
|
||||
} RegisterCommand(CMD_OpenConfigOptions, "", "List available variables and associated documentation inside the command window");
|
||||
|
||||
void EvalCommandsLineByLine(BSet set) {
|
||||
WindowID save_last = PrimaryWindowID;
|
||||
WindowID save_active = ActiveWindowID;
|
||||
WindowID save_next = NextActiveWindowID;
|
||||
Caret save_caret = set.view->carets[0];
|
||||
ActiveWindowID = set.window->id;
|
||||
PrimaryWindowID = set.window->id;
|
||||
NextActiveWindowID = set.window->id;
|
||||
for (Int i = 0; i < set.buffer->line_starts.len; i += 1) {
|
||||
Range range = GetLineRangeWithoutNL(set.buffer, i);
|
||||
String16 string = GetString(set.buffer, range);
|
||||
string = Trim(string);
|
||||
if (string.len == 0) {
|
||||
continue;
|
||||
}
|
||||
if (StartsWith(string, u"//")) {
|
||||
continue;
|
||||
}
|
||||
Open(string);
|
||||
}
|
||||
set.view->carets[0] = save_caret;
|
||||
PrimaryWindowID = save_last;
|
||||
ActiveWindowID = save_active;
|
||||
NextActiveWindowID = save_next;
|
||||
}
|
||||
|
||||
void CMD_EvalCommandsLineByLine() {
|
||||
BSet set = GetBSet(PrimaryWindowID);
|
||||
EvalCommandsLineByLine(set);
|
||||
} RegisterCommand(CMD_EvalCommandsLineByLine, "", "Goes line by line over a buffer and evaluates every line as a command, ignores empty or lines starting with '//'");
|
||||
|
||||
BufferID LoadConfig(String config_path) {
|
||||
ReportConsolef("Loading config %S...", config_path);
|
||||
Window *window = GetWindow(NullWindowID);
|
||||
ViewID active_view_before = window->active_view;
|
||||
View *view = WindowOpenBufferView(window, config_path);
|
||||
Buffer *buffer = GetBuffer(view->active_buffer);
|
||||
EvalCommandsLineByLine({window, view, buffer});
|
||||
if (window->active_view == view->id) {
|
||||
window->active_view = active_view_before;
|
||||
}
|
||||
return buffer->id;
|
||||
}
|
||||
|
||||
|
||||
#define ExpectP(x, ...) \
|
||||
if (!(x)) { \
|
||||
ReportErrorf("Failed to parse '" __FUNCTION__ "' command, " __VA_ARGS__); \
|
||||
return; \
|
||||
}
|
||||
|
||||
void Set(String string) {
|
||||
String name = SkipIdent(&string);
|
||||
ExpectP(name.len != 0, "expected a variable name, instead got '%S'", string);
|
||||
|
||||
Variable *var = NULL;
|
||||
For (Variables) {
|
||||
if (name == it.name) {
|
||||
var = ⁢
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (var) {
|
||||
SkipWhitespace(&string);
|
||||
if (var->type == VariableType_String) {
|
||||
char c = At(string, 0);
|
||||
ExpectP(c == u'"' || c == u'\'', "Expected string to follow the command name, instead got %S", string);
|
||||
string = Skip(string, 1);
|
||||
String quote = SkipUntil(&string, {&c, 1});
|
||||
ExpectP(At(string, 0) == c, ":Set %S <error here>, unclosed quote", name);
|
||||
ReportConsolef(":Set %S %c%S%c", name, c, quote, c);
|
||||
*var->string = Intern(&GlobalInternTable, quote);
|
||||
} else if (var->type == VariableType_Int) {
|
||||
ExpectP(IsDigit(At(string, 0)), "Expected an integer to follow the command name, instead got: %S", string);
|
||||
Int number = SkipInt(&string);
|
||||
ReportConsolef(":Set %S %lld", name, number);
|
||||
*var->i = number;
|
||||
} else if (var->type == VariableType_Float) {
|
||||
ExpectP(IsDigit(At(string, 0)), "Expected float to follow the command name, instead got: %S", string);
|
||||
Float number = SkipFloat(&string);
|
||||
ReportConsolef(":Set %S %f", name, number);
|
||||
*var->f = number;
|
||||
} else if (var->type == VariableType_Color) {
|
||||
ExpectP(IsHexDigit(At(string, 0)), "Expected hex integer to follow the command name, instead got: %S", string);
|
||||
String begin = {string.data, 0};
|
||||
while (IsHexDigit(At(string, 0))) {
|
||||
string = Skip(string, 1);
|
||||
begin.len += 1;
|
||||
}
|
||||
ReportConsolef(":Set %S %S", name, begin);
|
||||
var->color->value = (uint32_t)strtoll(begin.data, NULL, 16);
|
||||
} ElseInvalidCodepath();
|
||||
|
||||
|
||||
if (name == "FontSize" || name == "PathToFont") {
|
||||
ReloadFont(PathToFont, (U32)FontSize);
|
||||
}
|
||||
|
||||
#if PLUGIN_PROJECT_MANAGEMENT
|
||||
if (name == "ProjectDirectory") {
|
||||
SetProjectDirectory(*var->string);
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
Command *cmd = NULL;
|
||||
For (GlobalCommands) {
|
||||
if (it.name == name) {
|
||||
cmd = ⁢
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd) {
|
||||
SkipWhitespace(&string);
|
||||
char c = At(string, 0);
|
||||
ExpectP(c == u'"' || c == u'\'', "Expected string to follow the command name, instead got %S", string);
|
||||
string = Skip(string, 1);
|
||||
String quote = SkipUntil(&string, {&c, 1});
|
||||
ExpectP(At(string, 0) == c, "Failed to parse command, unclose quote");
|
||||
quote = Intern(&GlobalInternTable, quote);
|
||||
ReportConsolef(":Set %S %c%S%c", name, c, quote, c);
|
||||
Trigger *trigger = ParseKeyCached(quote);
|
||||
if (trigger) {
|
||||
cmd->binding = quote;
|
||||
cmd->trigger = trigger;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ReportErrorf("Failed to :Set, no such variable found: %S", name);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
#if PLUGIN_CONFIG
|
||||
void Set(String string);
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user