Compare commits
715 Commits
main
...
f0b6844feb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0b6844feb | ||
|
|
b712c70c75 | ||
|
|
797ca65c66 | ||
|
|
762b7a4276 | ||
|
|
a511441f7e | ||
|
|
cd61d710df | ||
|
|
f88ad7efcd | ||
|
|
38320018dc | ||
|
|
d883d212b1 | ||
|
|
dedb2a2399 | ||
|
|
8604c55de8 | ||
|
|
e949e8550c | ||
|
|
eb5c6cf736 | ||
|
|
94ce2c97be | ||
|
|
3f164c6b82 | ||
|
|
f953f3d3ff | ||
|
|
9fc75fe445 | ||
|
|
7a5bb6a56b | ||
|
|
d54c6b7f6b | ||
|
|
db5d6a7f80 | ||
|
|
4a92f44cf6 | ||
|
|
376b9f7272 | ||
|
|
7e9e4c47ae | ||
|
|
603fc200e6 | ||
|
|
c11e84b248 | ||
|
|
be13120554 | ||
|
|
9943edad5a | ||
|
|
f4049be975 | ||
|
|
6cf7e01fe9 | ||
|
|
b51d2f5295 | ||
|
|
49d941ad65 | ||
|
|
a66c3bdae5 | ||
|
|
547eabb403 | ||
|
|
79851394b3 | ||
|
|
fcdc922343 | ||
|
|
0b11d76576 | ||
|
|
f7097398ca | ||
|
|
85b1252b9e | ||
|
|
1cd12b5f16 | ||
|
|
c84b66d078 | ||
|
|
27adb9486b | ||
|
|
8d5369fafe | ||
|
|
feafc57b63 | ||
|
|
f9b347f9d9 | ||
|
|
6e1825d6b4 | ||
|
|
ec2baeb276 | ||
|
|
6cb8bda6e1 | ||
|
|
05e57801e7 | ||
|
|
b924c85669 | ||
|
|
09c1aa44d8 | ||
|
|
1d470fb5ba | ||
|
|
b4e4f7a6cc | ||
|
|
55d30a7cc3 | ||
|
|
b62186441b | ||
|
|
8caba1e774 | ||
|
|
7bdb428938 | ||
|
|
3ea707c0e7 | ||
|
|
18d582c6ce | ||
|
|
8b2bb95b81 | ||
|
|
2ef496a24a | ||
|
|
c0a89e8951 | ||
|
|
fc953df281 | ||
|
|
fe2dca16f0 | ||
|
|
f662aa6da2 | ||
|
|
fee3677fb9 | ||
|
|
4818c6e3dd | ||
|
|
971eb737c1 | ||
|
|
9a3286ad57 | ||
|
|
c9e09b840e | ||
|
|
8136248a67 | ||
|
|
0d67e46041 | ||
|
|
bc1087f5a7 | ||
|
|
3a9c6fc51a | ||
|
|
7774f56ab6 | ||
|
|
14a608effd | ||
|
|
5fae3cec2a | ||
|
|
b0c22b61ec | ||
|
|
82b049204d | ||
|
|
aa59142bfa | ||
|
|
c55bb77ff5 | ||
|
|
9b4e2e1e21 | ||
|
|
202c38a65a | ||
|
|
720bc418a6 | ||
|
|
d77f455065 | ||
|
|
7e08f0fb66 | ||
|
|
9e4a87021c | ||
|
|
5887f6bcaa | ||
|
|
5d67559e33 | ||
|
|
e631eb7a7a | ||
|
|
64ff4c232a | ||
|
|
0ec4f970f7 | ||
|
|
afe95be42f | ||
|
|
14ac1c9904 | ||
|
|
7c11ea3694 | ||
|
|
c1fd341698 | ||
|
|
0deab1be51 | ||
|
|
5a623adaa6 | ||
|
|
4363118d9d | ||
|
|
d613da4b6c | ||
|
|
f46f5b2050 | ||
|
|
d9c4114b5f | ||
|
|
ddd36af0f1 | ||
|
|
35fd30ee29 | ||
|
|
4a0652684c | ||
|
|
33c81f00b7 | ||
|
|
55714b90cd | ||
|
|
9b47603a1d | ||
|
|
2e2a913412 | ||
|
|
42b85dc33b | ||
|
|
a15ffcb071 | ||
|
|
692b77fb8e | ||
|
|
044378cfa3 | ||
|
|
f1d4d5f995 | ||
|
|
19d0fb6fcd | ||
|
|
7933265095 | ||
|
|
d810644018 | ||
|
|
a7bfc1c2ec | ||
|
|
104b2740bc | ||
|
|
65501837b7 | ||
|
|
3ed0a54847 | ||
|
|
cbd2519b9a | ||
|
|
e8a73f9696 | ||
|
|
1a0d114861 | ||
|
|
5c3497681e | ||
|
|
b05cf9ef09 | ||
|
|
a74eb357a1 | ||
|
|
8eb71084f0 | ||
|
|
ef1077fd7b | ||
|
|
f1ba5c7e0f | ||
|
|
97ea4986af | ||
|
|
25c021c15b | ||
|
|
2bf12a52d1 | ||
|
|
6ada36d3cb | ||
|
|
42b90ae76c | ||
|
|
ccc61cb10c | ||
|
|
be5b81740e | ||
|
|
e7458ca10a | ||
|
|
b10168eb1c | ||
|
|
30463c9082 | ||
|
|
dc7391dc89 | ||
|
|
eb98d70a0b | ||
|
|
dcd8374b89 | ||
|
|
8e4216215e | ||
|
|
5bd7099b96 | ||
|
|
9a63d3b2da | ||
|
|
bf02330db9 | ||
|
|
5806a6484f | ||
|
|
0fa5401800 | ||
|
|
b30f4cbfb5 | ||
|
|
ba37183c9c | ||
|
|
2f298a1979 | ||
|
|
8c282a5d83 | ||
|
|
d34c0a5abe | ||
|
|
8f3348cf2b | ||
|
|
38c0bc7bae | ||
|
|
313b00b11f | ||
|
|
165a379c73 | ||
|
|
a7f37236bf | ||
|
|
51532336b0 | ||
|
|
03d4b47f63 | ||
|
|
8b57edde6b | ||
|
|
778778fede | ||
|
|
f7449c4ab9 | ||
|
|
fd2bcc9156 | ||
|
|
a5b1555725 | ||
|
|
e74fdbc55b | ||
|
|
008c777a9f | ||
|
|
d8a9d4a24e | ||
|
|
bc0e1fa898 | ||
|
|
17f1737c9a | ||
|
|
868444f043 | ||
|
|
fdae253695 | ||
|
|
d4adcff958 | ||
|
|
2c59c9a3cc | ||
|
|
3a59a64355 | ||
|
|
9363c1cdaf | ||
|
|
198e6d7cf6 | ||
|
|
07ee898f4f | ||
|
|
6feb8a99d2 | ||
|
|
e57c1fc9fc | ||
|
|
a11b5ae41f | ||
|
|
c67a7cec5b | ||
|
|
91f04ce250 | ||
|
|
926df2b276 | ||
|
|
9fe878bbec | ||
|
|
217dbca7b7 | ||
|
|
13852e865c | ||
|
|
679d47131d | ||
|
|
8b1bccb79b | ||
|
|
e86e755c51 | ||
|
|
8ec6d4c9fc | ||
|
|
08cdf88586 | ||
|
|
6f7d97cf94 | ||
|
|
5e434f5131 | ||
|
|
a152d0aac5 | ||
|
|
879706e6e9 | ||
|
|
00f1f30a08 | ||
|
|
a5813f9ba5 | ||
|
|
5652af3384 | ||
|
|
22cd9af8cc | ||
|
|
a9cf9bceef | ||
|
|
6c0f864a6e | ||
|
|
e4509d9482 | ||
|
|
0f23e1f0f4 | ||
|
|
642a6aa4ad | ||
|
|
432c296b7b | ||
|
|
b576d373c4 | ||
|
|
522aa8e762 | ||
|
|
146802fa4c | ||
|
|
cc8af25d73 | ||
|
|
f5f4bf58ad | ||
|
|
3784da0d18 | ||
|
|
9eb72f4392 | ||
|
|
f7bf6d5e62 | ||
|
|
adb14ba373 | ||
|
|
7391d91317 | ||
|
|
2149cec29f | ||
|
|
ad756c36fc | ||
|
|
b56316e9da | ||
|
|
a989c44211 | ||
|
|
217e5f81cc | ||
|
|
5f2549b198 | ||
|
|
dcd4d0daeb | ||
|
|
faf4220b38 | ||
|
|
193ddaa2f6 | ||
|
|
46eb27883a | ||
|
|
2db7cdb71e | ||
|
|
5411c5aa4a | ||
|
|
f8a1a10897 | ||
|
|
adbe13938e | ||
|
|
4d5b14753d | ||
|
|
ba9fa00947 | ||
|
|
98cedf155c | ||
|
|
88e3998664 | ||
|
|
c0c0bbc1bf | ||
|
|
650e1b4fc5 | ||
|
|
6c1ada8d0a | ||
|
|
7d00c2670f | ||
|
|
bca7e9a1e8 | ||
|
|
3748f0304f | ||
|
|
2576bdbd14 | ||
|
|
e341a36287 | ||
|
|
bba09a3cd0 | ||
|
|
985df3532b | ||
|
|
72041a52e8 | ||
|
|
891144dac1 | ||
|
|
41e7b53903 | ||
|
|
6b0920e8c0 | ||
|
|
4285729d5c | ||
|
|
a9c10d0751 | ||
|
|
74c79c7eff | ||
|
|
9174a89971 | ||
|
|
5c94a583bc | ||
|
|
6e1fc2766f | ||
|
|
d3bb00cb55 | ||
|
|
5a5656b2d3 | ||
|
|
1a1e584cba | ||
|
|
65fa05f998 | ||
|
|
2276fc95b8 | ||
|
|
1e173c178d | ||
|
|
773747cf9c | ||
|
|
4972284dde | ||
|
|
45789fda08 | ||
|
|
3b5bc63d1b | ||
|
|
f1089e2b8a | ||
|
|
6d93c1eb92 | ||
|
|
363c325c79 | ||
|
|
583504ebe0 | ||
|
|
b354b77f8b | ||
|
|
74af46cb4a | ||
|
|
19dab08275 | ||
|
|
3840fbf957 | ||
|
|
78c091f7f8 | ||
|
|
274ecbba78 | ||
|
|
683c2a68cd | ||
|
|
ad98181069 | ||
|
|
a549336530 | ||
|
|
4eb95c963d | ||
|
|
22caacd2a9 | ||
|
|
af30d537da | ||
|
|
f1bd26fb92 | ||
|
|
5c6bbcb62f | ||
|
|
21bd87bb07 | ||
|
|
79450df04c | ||
|
|
7f8b7b811e | ||
|
|
3c068aa0ae | ||
|
|
86df258365 | ||
|
|
d99e704728 | ||
|
|
0d620f3e0f | ||
|
|
4dce0f9074 | ||
|
|
54f89cba33 | ||
|
|
de88f60d1a | ||
|
|
f7060970e6 | ||
|
|
e7a98ac6cc | ||
|
|
10544db52e | ||
|
|
5e123031aa | ||
|
|
388f530edd | ||
|
|
d354cccd37 | ||
|
|
714305ef56 | ||
|
|
f83ae1e9c6 | ||
|
|
c38e8de6b5 | ||
|
|
97638f7ade | ||
|
|
326a30d1af | ||
|
|
32e1473c94 | ||
|
|
bf617036c7 | ||
|
|
ce55422a24 | ||
|
|
388cc7c3bb | ||
|
|
37f9404d93 | ||
|
|
38dff41e25 | ||
|
|
d360340b9e | ||
|
|
0f63cfa43f | ||
|
|
537780ee1e | ||
|
|
4ca99fcb4e | ||
|
|
eb7ee13f43 | ||
|
|
b2de706693 | ||
|
|
6a8180470d | ||
|
|
12d56be5cc | ||
|
|
bb4d81a4fa | ||
|
|
b254ade69b | ||
|
|
ef4ebaa969 | ||
|
|
99f8133b91 | ||
|
|
51eb44bf40 | ||
|
|
a0be415e09 | ||
|
|
071da18fa3 | ||
|
|
c62e820bcf | ||
|
|
46c34db6cb | ||
|
|
25a2a4879c | ||
|
|
8be28012ee | ||
|
|
5aed186827 | ||
|
|
91f812e17f | ||
|
|
f0b22c48b2 | ||
|
|
52c4eebd77 | ||
|
|
24f0d26fce | ||
|
|
825ec221b7 | ||
|
|
e31080bce3 | ||
|
|
7a5d5cabad | ||
|
|
f7de310889 | ||
|
|
e209ca7c82 | ||
|
|
ee8de77a90 | ||
|
|
db49cbd6e2 | ||
|
|
e001eecb7b | ||
|
|
7f34d00c95 | ||
|
|
2c18adbddd | ||
|
|
97c7fc42d1 | ||
|
|
7da0627f8e | ||
|
|
27cef23823 | ||
|
|
b7fc2dc3d0 | ||
|
|
8af390e0f6 | ||
|
|
96d6453ea8 | ||
|
|
2b9900e56e | ||
|
|
86f58f60cb | ||
|
|
064ce568c2 | ||
|
|
6aff459e1c | ||
|
|
0b1b4d8f7e | ||
|
|
3fc2c3529a | ||
|
|
b0e9ab0519 | ||
|
|
668517a723 | ||
|
|
649f08ec78 | ||
|
|
2f2c298c68 | ||
|
|
90e48970e6 | ||
|
|
480842a203 | ||
|
|
5425394880 | ||
|
|
a365813fa9 | ||
|
|
9d64dbd5c2 | ||
|
|
30bb61a775 | ||
|
|
1f36ed0cf9 | ||
|
|
d54c76f88a | ||
|
|
cbb9f47ee5 | ||
|
|
b68d5a5833 | ||
|
|
94d2090777 | ||
|
|
e97585daf9 | ||
|
|
924fc2118c | ||
|
|
51f4c0c750 | ||
|
|
37b93da650 | ||
|
|
35e739dcdd | ||
|
|
8352392b38 | ||
|
|
413f05bfca | ||
|
|
dc1aff58ed | ||
|
|
9f75d9cfe5 | ||
|
|
a42af7e973 | ||
|
|
2ce244d303 | ||
|
|
a775a920d0 | ||
|
|
4f84faf392 | ||
|
|
a4cb5d8360 | ||
|
|
da7f09cf82 | ||
|
|
0166af472b | ||
|
|
884d986bd6 | ||
|
|
59b807189f | ||
|
|
fb1c7015b1 | ||
|
|
d4123f62b2 | ||
|
|
a3f410d1a1 | ||
|
|
1d19a4bffe | ||
|
|
271dd91292 | ||
|
|
9bd4d68f9c | ||
|
|
3c3c7826ef | ||
|
|
2207357b93 | ||
|
|
3a69768eb0 | ||
|
|
afb29ff3ec | ||
|
|
e6f0f891a6 | ||
|
|
36e5aa4683 | ||
|
|
7738050105 | ||
|
|
4bf11ec349 | ||
|
|
d821012eed | ||
|
|
35c6edd989 | ||
|
|
633cb4f282 | ||
|
|
4d4d0e26a9 | ||
|
|
feea2d4024 | ||
|
|
0ffd2a5c1d | ||
|
|
232fdcb82c | ||
|
|
0ccc23d544 | ||
|
|
789ca3db1a | ||
|
|
cb359a05dc | ||
|
|
14982c137a | ||
|
|
0acab11620 | ||
|
|
02f0239016 | ||
|
|
ab61b49aca | ||
|
|
4da1d6fd27 | ||
|
|
909e847369 | ||
|
|
eafa09fecf | ||
|
|
8175348284 | ||
|
|
b2832cb47a | ||
|
|
9f499991c8 | ||
|
|
9a416e8ae8 | ||
|
|
911922c6a3 | ||
|
|
1f2fd59ad5 | ||
|
|
708d401d2c | ||
|
|
ed0dcacab3 | ||
|
|
e86050f343 | ||
|
|
57f7da6ce1 | ||
|
|
93e6455171 | ||
|
|
8f38780197 | ||
|
|
341f7e41e5 | ||
|
|
265fe9c62e | ||
|
|
3b9d60d7cb | ||
|
|
278b873e89 | ||
|
|
e640344d7a | ||
|
|
7151bb86a8 | ||
|
|
2a34391b71 | ||
|
|
3d95cf02f3 | ||
|
|
dd3f34cb2c | ||
|
|
0c316ebfb2 | ||
|
|
282bf24f65 | ||
|
|
f964f6be8d | ||
|
|
0202ccec5f | ||
|
|
636c308993 | ||
|
|
6fdbe6f9c2 | ||
|
|
c19f4c019a | ||
|
|
83eb3dc0cb | ||
|
|
481c8406f3 | ||
|
|
0129619d9a | ||
|
|
e0479b291d | ||
|
|
b847d7dfd5 | ||
|
|
245dff8027 | ||
|
|
fed690a7f2 | ||
|
|
54d981120d | ||
|
|
f79250c4d4 | ||
|
|
78b62776d2 | ||
|
|
bda4614783 | ||
|
|
0ab3332ad3 | ||
|
|
9e0abbc2f0 | ||
|
|
496adb61a4 | ||
|
|
4a4a3bf184 | ||
|
|
f33e78882e | ||
|
|
0ff067bdb7 | ||
|
|
31ac3260ed | ||
|
|
d82c6c2337 | ||
|
|
632b699475 | ||
|
|
85039020d3 | ||
|
|
1a0fdc5a44 | ||
|
|
fb1bab7c30 | ||
|
|
7eb43990ad | ||
|
|
53f4b5a9da | ||
|
|
1d4a6c3a42 | ||
|
|
40083e4aa1 | ||
|
|
bd929bff07 | ||
|
|
cd4a0530fa | ||
|
|
273fdd2235 | ||
|
|
b20f2e8d31 | ||
|
|
e756cde2b1 | ||
|
|
de18d3e64d | ||
|
|
441999ba9f | ||
|
|
dd046b1ace | ||
|
|
926aa238ab | ||
|
|
01fa521a03 | ||
|
|
f31da19266 | ||
|
|
48edc38817 | ||
|
|
ac12132ac0 | ||
|
|
13fabcc1f1 | ||
|
|
67005a80be | ||
|
|
f43bfcb398 | ||
|
|
d5ce4c9d2c | ||
|
|
1cf7ef3de6 | ||
|
|
5248a3fe48 | ||
|
|
812e61ca70 | ||
|
|
2d0a5a9e15 | ||
|
|
f32d594879 | ||
|
|
c2ad76fe4f | ||
|
|
10d9b72da1 | ||
|
|
2fe9af7165 | ||
|
|
0deda83d05 | ||
|
|
ff5bcd4416 | ||
|
|
b65cd1d09b | ||
|
|
bc35a561d3 | ||
|
|
06bc807e34 | ||
|
|
6262e41de1 | ||
|
|
0cb53efa01 | ||
|
|
4e859bedbc | ||
|
|
f139fc2229 | ||
|
|
e48acbb03b | ||
|
|
d1155c968e | ||
|
|
88a2c60065 | ||
|
|
5bfcf6783e | ||
|
|
94f8a657f1 | ||
|
|
7fac2a7526 | ||
|
|
46dcf98fc1 | ||
|
|
58ce907327 | ||
|
|
6ecc8cac0e | ||
|
|
bd95f17426 | ||
|
|
0718bea5a1 | ||
|
|
175f07cd2f | ||
|
|
7b19d6e479 | ||
|
|
77c83e5552 | ||
|
|
b15deb420f | ||
|
|
b38989d594 | ||
|
|
79e6de325f | ||
|
|
163d2e4ba8 | ||
|
|
4f8f3ddc29 | ||
|
|
82a1a29260 | ||
|
|
8a5608df91 | ||
|
|
3f1c0ec91b | ||
|
|
1406a75a92 | ||
|
|
8001493df3 | ||
|
|
8c1f5bfe1e | ||
|
|
ec8b9640e2 | ||
|
|
4ae1332a43 | ||
|
|
10c884bba4 | ||
|
|
c15f031c3f | ||
|
|
1b4c744974 | ||
|
|
d9068eebb5 | ||
|
|
3ad0d2328d | ||
|
|
3f2beb4547 | ||
|
|
be14a6c239 | ||
|
|
3aa0eeb4a3 | ||
|
|
b3eeb6412f | ||
|
|
d38470c8e2 | ||
|
|
a159c980ee | ||
|
|
a993d997ad | ||
|
|
4475e3e184 | ||
|
|
cf0320e47d | ||
|
|
cd03a95128 | ||
|
|
51e299c7e3 | ||
|
|
6f65453fd4 | ||
|
|
67e0c21e0f | ||
|
|
702016a6e3 | ||
|
|
d74ce4950c | ||
|
|
59a682c720 | ||
|
|
7bd4593748 | ||
|
|
c5b006bf19 | ||
|
|
115c44630d | ||
|
|
1dc81abca4 | ||
|
|
5aaf2128a8 | ||
|
|
6aeac17072 | ||
|
|
6d425182a2 | ||
|
|
04ac23b67c | ||
|
|
5494e2c125 | ||
|
|
aba82564f5 | ||
|
|
93abee9c7c | ||
|
|
4034bef42e | ||
|
|
821d065eba | ||
|
|
2614437ba0 | ||
|
|
1aac3a0425 | ||
|
|
a4568f9263 | ||
|
|
a180e72b6f | ||
|
|
2de64b592d | ||
|
|
9c0f3dd996 | ||
|
|
079df39ca8 | ||
|
|
60a99d1d23 | ||
|
|
fe87c08a02 | ||
|
|
8637959289 | ||
|
|
6be53668b9 | ||
|
|
d1b7249803 | ||
|
|
ff7c50c627 | ||
|
|
12779cdef8 | ||
|
|
f5e676b2b7 | ||
|
|
8e5e5f819f | ||
|
|
370a958379 | ||
|
|
0ee7da92a3 | ||
|
|
a0bd3dc54f | ||
|
|
809eb2fe3e | ||
|
|
7010d8614f | ||
|
|
69f13f1896 | ||
|
|
bdaf7cddcb | ||
|
|
8d6db168d6 | ||
|
|
2fabe1949c | ||
|
|
c660df14ec | ||
|
|
e704968f96 | ||
|
|
32359df939 | ||
|
|
641ed23380 | ||
|
|
9f977488fa | ||
|
|
ac0b22f9b9 | ||
|
|
7752b02fb7 | ||
|
|
7610670287 | ||
|
|
31a1b23fb7 | ||
|
|
91c8f9a596 | ||
|
|
f70cd3ea77 | ||
|
|
5db5ff069a | ||
|
|
b8d852ddb7 | ||
|
|
46eedbd1a4 | ||
|
|
e760bafeeb | ||
|
|
12351d5cb6 | ||
|
|
e84f613c4d | ||
|
|
5db4e5b4d5 | ||
|
|
b00dd8d68d | ||
|
|
abbbf7ec15 | ||
|
|
22c72d8c70 | ||
|
|
d0b1457f30 | ||
|
|
a423cd8bb3 | ||
|
|
db076058b9 | ||
|
|
fe10ea85db | ||
|
|
a1100624bf | ||
|
|
28e1497f88 | ||
|
|
8d6111641e | ||
|
|
3ee20d1a84 | ||
|
|
002c2d0aca | ||
|
|
de9f109f2a | ||
|
|
461a5774f8 | ||
|
|
914f718767 | ||
|
|
ebfd092075 | ||
|
|
e322826347 | ||
|
|
3998c5f955 | ||
|
|
762d22ed28 | ||
|
|
f2362b2b78 | ||
|
|
471ac80420 | ||
|
|
4a887fc706 | ||
|
|
e49d3c7bfe | ||
|
|
c5b83074ac | ||
|
|
79090c2648 | ||
|
|
7a6b1c8e47 | ||
|
|
8988ce2766 | ||
|
|
dcde2ae6b4 | ||
|
|
c62849a783 | ||
|
|
f453e8e170 | ||
|
|
990887891e | ||
|
|
5da801d12b | ||
|
|
3a4557d417 | ||
|
|
26d9a3e253 | ||
|
|
eef3631a5a | ||
|
|
88ee35165f | ||
|
|
c8f05b4a7a | ||
|
|
c32584cca0 | ||
|
|
2995a36942 | ||
|
|
c1dbafc101 | ||
|
|
3e8ab8271d | ||
|
|
5b3a00c64f | ||
|
|
0ce9fd8597 | ||
|
|
c9badb5a1c | ||
|
|
a513bc5749 | ||
|
|
5d5487315f | ||
|
|
3508df67b1 | ||
|
|
06ce1f0667 | ||
|
|
f9c3ae7090 | ||
|
|
1fb8c211f0 | ||
|
|
9c7670847e | ||
|
|
a24c2d9be2 | ||
|
|
f4db246658 | ||
|
|
7f90079ea7 | ||
|
|
f4b4987d43 | ||
|
|
7f88ba70d4 | ||
|
|
ac094a48d6 | ||
|
|
779912d8af | ||
|
|
f205b8e883 | ||
|
|
f9a0412e78 | ||
|
|
0ef318633c | ||
|
|
2f8c9746e3 | ||
|
|
6d6bef1b04 | ||
|
|
3dab392296 | ||
|
|
f8a2c60c8d | ||
|
|
770f7716a0 | ||
|
|
a011c0384f | ||
|
|
0d356c5bbc | ||
|
|
d67de70126 | ||
|
|
6f334756c5 | ||
|
|
310713d203 | ||
|
|
7d2ab53baa | ||
|
|
2152b8b95f | ||
|
|
8ac1ae1574 | ||
|
|
4fd21bc303 | ||
|
|
15037bfc7a | ||
|
|
5831c72aad | ||
|
|
a063d041c9 | ||
|
|
3572e9794a | ||
|
|
cef6999dc7 | ||
|
|
6ed9651176 | ||
|
|
3efbe22a1b | ||
|
|
96579b88cf | ||
|
|
2ec18855f2 | ||
|
|
b222581d18 | ||
|
|
a8e3ee6f19 | ||
|
|
a083e588ba | ||
|
|
9b500842a0 | ||
|
|
b21348379f | ||
|
|
633055293e | ||
|
|
ae9d618803 | ||
|
|
9c744dfc44 | ||
|
|
faf1b661bb | ||
|
|
22e45278a2 | ||
|
|
43f4657566 | ||
|
|
70f2908056 | ||
|
|
ef381d0600 | ||
|
|
cfa87526a7 | ||
|
|
39b560fde3 | ||
|
|
0c582b4490 | ||
|
|
61caf566fc | ||
|
|
76d5364a55 | ||
|
|
5224df321e |
13
.clangd
13
.clangd
@@ -1,13 +0,0 @@
|
||||
Diagnostics:
|
||||
Suppress: target_unsupported_type
|
||||
|
||||
CompileFlags:
|
||||
Remove: [
|
||||
-fstrict-volatile-bitfields,
|
||||
-fno-tree-loop-distribute-patterns
|
||||
]
|
||||
Add: [
|
||||
-D__banan_os__,
|
||||
-D__arch__=x86_64,
|
||||
-D__x86_64__
|
||||
]
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -2,5 +2,7 @@
|
||||
.idea/
|
||||
build/
|
||||
base/
|
||||
script/fakeroot-context
|
||||
tools/update-image-perms
|
||||
*.tar.*
|
||||
toolchain/*/
|
||||
|
||||
!base-sysroot.tar.gz
|
||||
|
||||
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -0,0 +1,4 @@
|
||||
[submodule "kernel/lai"]
|
||||
path = kernel/lai
|
||||
url = https://github.com/managarm/lai.git
|
||||
ignore = untracked
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
# .pre-commit-config.yaml
|
||||
exclude: '.patch$'
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.1.0 # this is optional, use `pre-commit autoupdate` to get the latest rev!
|
||||
hooks:
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
38
.vscode/c_cpp_properties.json
vendored
38
.vscode/c_cpp_properties.json
vendored
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "banan-os",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/BAN/include",
|
||||
"${workspaceFolder}/kernel/include",
|
||||
"${workspaceFolder}/userspace/libraries/*/include"
|
||||
],
|
||||
"defines": [
|
||||
"__arch=x86_64",
|
||||
"__enable_sse=1"
|
||||
],
|
||||
"compilerPath": "${workspaceFolder}/toolchain/local/bin/x86_64-banan_os-gcc",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "gnu++20",
|
||||
"intelliSenseMode": "linux-gcc-x64"
|
||||
},
|
||||
{
|
||||
"name": "banan-os-kernel",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/BAN/include",
|
||||
"${workspaceFolder}/kernel/include",
|
||||
"${workspaceFolder}/userspace/libraries/*/include"
|
||||
],
|
||||
"defines": [
|
||||
"__arch=x86_64",
|
||||
"__is_kernel",
|
||||
"__enable_sse=1"
|
||||
],
|
||||
"compilerPath": "${workspaceFolder}/toolchain/local/bin/x86_64-banan_os-gcc",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "gnu++20",
|
||||
"intelliSenseMode": "linux-gcc-x64"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
10
.vscode/settings.json
vendored
10
.vscode/settings.json
vendored
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"cmake.configureOnOpen": false,
|
||||
"editor.tabSize": 4,
|
||||
"editor.insertSpaces": false,
|
||||
"editor.detectIndentation": false,
|
||||
"clangd.arguments": [
|
||||
"--compile-commands-dir=${workspaceFolder}/build",
|
||||
"-header-insertion=never"
|
||||
]
|
||||
}
|
||||
3
BAN/.gitignore
vendored
Normal file
3
BAN/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*.a
|
||||
*.d
|
||||
*.o
|
||||
@@ -1,22 +0,0 @@
|
||||
#include <BAN/Assert.h>
|
||||
|
||||
#if __is_kernel
|
||||
|
||||
#include <kernel/Panic.h>
|
||||
|
||||
[[noreturn]] void __ban_assertion_failed(const char* location, const char* msg)
|
||||
{
|
||||
Kernel::panic_impl(location, msg);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <BAN/Debug.h>
|
||||
|
||||
[[noreturn]] void __ban_assertion_failed(const char* location, const char* msg)
|
||||
{
|
||||
derrorln("{}: {}", location, msg);
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
#endif
|
||||
262
BAN/BAN/String.cpp
Normal file
262
BAN/BAN/String.cpp
Normal file
@@ -0,0 +1,262 @@
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/Math.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/New.h>
|
||||
#include <BAN/String.h>
|
||||
#include <BAN/StringView.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
String::String()
|
||||
{
|
||||
MUST(copy_impl(""sv));
|
||||
}
|
||||
|
||||
String::String(const String& other)
|
||||
{
|
||||
MUST(copy_impl(other.sv()));
|
||||
}
|
||||
|
||||
String::String(String&& other)
|
||||
{
|
||||
move_impl(move(other));
|
||||
}
|
||||
|
||||
String::String(StringView other)
|
||||
{
|
||||
MUST(copy_impl(other));
|
||||
}
|
||||
|
||||
String::~String()
|
||||
{
|
||||
BAN::deallocator(m_data);
|
||||
}
|
||||
|
||||
String& String::operator=(const String& other)
|
||||
{
|
||||
MUST(copy_impl(other.sv()));
|
||||
return *this;
|
||||
}
|
||||
|
||||
String& String::operator=(String&& other)
|
||||
{
|
||||
BAN::deallocator(m_data);
|
||||
move_impl(move(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
String& String::operator=(StringView other)
|
||||
{
|
||||
MUST(copy_impl(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
ErrorOr<void> String::push_back(char ch)
|
||||
{
|
||||
TRY(ensure_capacity(m_size + 2));
|
||||
m_data[m_size] = ch;
|
||||
m_size++;
|
||||
m_data[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> String::insert(char ch, size_type index)
|
||||
{
|
||||
ASSERT(index <= m_size);
|
||||
TRY(ensure_capacity(m_size + 1 + 1));
|
||||
memmove(m_data + index + 1, m_data + index, m_size - index);
|
||||
m_data[index] = ch;
|
||||
m_size += 1;
|
||||
m_data[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> String::insert(StringView other, size_type index)
|
||||
{
|
||||
ASSERT(index <= m_size);
|
||||
TRY(ensure_capacity(m_size + other.size() + 1));
|
||||
memmove(m_data + index + other.size(), m_data + index, m_size - index);
|
||||
memcpy(m_data + index, other.data(), other.size());
|
||||
m_size += other.size();
|
||||
m_data[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> String::append(StringView other)
|
||||
{
|
||||
TRY(ensure_capacity(m_size + other.size() + 1));
|
||||
memcpy(m_data + m_size, other.data(), other.size());
|
||||
m_size += other.size();
|
||||
m_data[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> String::append(const String& string)
|
||||
{
|
||||
TRY(append(string.sv()));
|
||||
return {};
|
||||
}
|
||||
|
||||
void String::pop_back()
|
||||
{
|
||||
ASSERT(m_size > 0);
|
||||
m_size--;
|
||||
m_data[m_size] = '\0';
|
||||
}
|
||||
|
||||
void String::remove(size_type index)
|
||||
{
|
||||
erase(index, 1);
|
||||
}
|
||||
|
||||
void String::erase(size_type index, size_type count)
|
||||
{
|
||||
ASSERT(index + count <= m_size);
|
||||
memmove(m_data + index, m_data + index + count, m_size - index - count);
|
||||
m_size -= count;
|
||||
m_data[m_size] = '\0';
|
||||
}
|
||||
|
||||
void String::clear()
|
||||
{
|
||||
m_size = 0;
|
||||
m_data[0] = '\0';
|
||||
}
|
||||
|
||||
char String::operator[](size_type index) const
|
||||
{
|
||||
ASSERT(index < m_size);
|
||||
return m_data[index];
|
||||
}
|
||||
|
||||
char& String::operator[](size_type index)
|
||||
{
|
||||
ASSERT(index < m_size);
|
||||
return m_data[index];
|
||||
}
|
||||
|
||||
bool String::operator==(const String& other) const
|
||||
{
|
||||
if (m_size != other.m_size)
|
||||
return false;
|
||||
return memcmp(m_data, other.m_data, m_size) == 0;
|
||||
}
|
||||
|
||||
bool String::operator==(StringView other) const
|
||||
{
|
||||
if (m_size != other.size())
|
||||
return false;
|
||||
return memcmp(m_data, other.data(), m_size) == 0;
|
||||
}
|
||||
|
||||
bool String::operator==(const char* other) const
|
||||
{
|
||||
for (size_type i = 0; i <= m_size; i++)
|
||||
if (m_data[i] != other[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
ErrorOr<void> String::resize(size_type size, char ch)
|
||||
{
|
||||
if (size < m_size)
|
||||
{
|
||||
m_data[size] = '\0';
|
||||
m_size = size;
|
||||
}
|
||||
else if (size > m_size)
|
||||
{
|
||||
TRY(ensure_capacity(size + 1));
|
||||
for (size_type i = m_size; i < size; i++)
|
||||
m_data[i] = ch;
|
||||
m_data[size] = '\0';
|
||||
m_size = size;
|
||||
}
|
||||
m_size = size;
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> String::reserve(size_type size)
|
||||
{
|
||||
TRY(ensure_capacity(size));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> String::shrink_to_fit()
|
||||
{
|
||||
size_type temp = m_capacity;
|
||||
m_capacity = 0;
|
||||
auto error_or = ensure_capacity(m_size);
|
||||
if (error_or.is_error())
|
||||
{
|
||||
m_capacity = temp;
|
||||
return error_or;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
StringView String::sv() const
|
||||
{
|
||||
return StringView(*this);
|
||||
}
|
||||
|
||||
bool String::empty() const
|
||||
{
|
||||
return m_size == 0;
|
||||
}
|
||||
|
||||
String::size_type String::size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
String::size_type String::capacity() const
|
||||
{
|
||||
return m_capacity;
|
||||
}
|
||||
|
||||
const char* String::data() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
ErrorOr<void> String::ensure_capacity(size_type size)
|
||||
{
|
||||
if (m_capacity >= size)
|
||||
return {};
|
||||
size_type new_cap = BAN::Math::max<size_type>(size, m_capacity * 2);
|
||||
void* new_data = BAN::allocator(new_cap);
|
||||
if (new_data == nullptr)
|
||||
return Error::from_errno(ENOMEM);
|
||||
if (m_data)
|
||||
memcpy(new_data, m_data, m_size + 1);
|
||||
BAN::deallocator(m_data);
|
||||
m_data = (char*)new_data;
|
||||
m_capacity = new_cap;
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> String::copy_impl(StringView other)
|
||||
{
|
||||
TRY(ensure_capacity(other.size() + 1));
|
||||
memcpy(m_data, other.data(), other.size());
|
||||
m_size = other.size();
|
||||
m_data[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
void String::move_impl(String&& other)
|
||||
{
|
||||
m_data = other.m_data;
|
||||
m_size = other.m_size;
|
||||
m_capacity = other.m_capacity;
|
||||
|
||||
other.m_data = nullptr;
|
||||
other.m_size = 0;
|
||||
other.m_capacity = 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,180 @@
|
||||
#include <BAN/String.h>
|
||||
#include <BAN/StringView.h>
|
||||
#include <BAN/Vector.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
StringView::StringView()
|
||||
{ }
|
||||
|
||||
StringView::StringView(const String& other)
|
||||
: StringView(other.data(), other.size())
|
||||
{ }
|
||||
|
||||
}
|
||||
StringView::StringView(const char* string, size_type len)
|
||||
{
|
||||
if (len == size_type(-1))
|
||||
len = strlen(string);
|
||||
m_data = string;
|
||||
m_size = len;
|
||||
}
|
||||
|
||||
char StringView::operator[](size_type index) const
|
||||
{
|
||||
ASSERT(index < m_size);
|
||||
return m_data[index];
|
||||
}
|
||||
|
||||
bool StringView::operator==(const String& other) const
|
||||
{
|
||||
if (m_size != other.size())
|
||||
return false;
|
||||
return memcmp(m_data, other.data(), m_size) == 0;
|
||||
}
|
||||
|
||||
bool StringView::operator==(StringView other) const
|
||||
{
|
||||
if (m_size != other.m_size)
|
||||
return false;
|
||||
return memcmp(m_data, other.m_data, m_size) == 0;
|
||||
}
|
||||
|
||||
bool StringView::operator==(const char* other) const
|
||||
{
|
||||
if (memcmp(m_data, other, m_size))
|
||||
return false;
|
||||
return other[m_size] == '\0';
|
||||
}
|
||||
|
||||
StringView StringView::substring(size_type index, size_type len) const
|
||||
{
|
||||
ASSERT(index <= m_size);
|
||||
if (len == size_type(-1))
|
||||
len = m_size - index;
|
||||
ASSERT(len <= m_size - index); // weird order to avoid overflow
|
||||
StringView result;
|
||||
result.m_data = m_data + index;
|
||||
result.m_size = len;
|
||||
return result;
|
||||
}
|
||||
|
||||
ErrorOr<Vector<StringView>> StringView::split(char delim, bool allow_empties)
|
||||
{
|
||||
size_type count = 0;
|
||||
{
|
||||
size_type start = 0;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
{
|
||||
if (m_data[i] == delim)
|
||||
{
|
||||
if (allow_empties || start != i)
|
||||
count++;
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
if (start != m_size)
|
||||
count++;
|
||||
}
|
||||
|
||||
Vector<StringView> result;
|
||||
TRY(result.reserve(count));
|
||||
|
||||
size_type start = 0;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
{
|
||||
if (m_data[i] == delim)
|
||||
{
|
||||
if (allow_empties || start != i)
|
||||
TRY(result.push_back(this->substring(start, i - start)));
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
if (start != m_size)
|
||||
TRY(result.push_back(this->substring(start)));
|
||||
return result;
|
||||
}
|
||||
|
||||
ErrorOr<Vector<StringView>> StringView::split(bool(*comp)(char), bool allow_empties)
|
||||
{
|
||||
size_type count = 0;
|
||||
{
|
||||
size_type start = 0;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
{
|
||||
if (comp(m_data[i]))
|
||||
{
|
||||
if (allow_empties || start != i)
|
||||
count++;
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
if (start != m_size)
|
||||
count++;
|
||||
}
|
||||
|
||||
Vector<StringView> result;
|
||||
TRY(result.reserve(count));
|
||||
|
||||
size_type start = 0;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
{
|
||||
if (comp(m_data[i]))
|
||||
{
|
||||
if (allow_empties || start != i)
|
||||
TRY(result.push_back(this->substring(start, i - start)));
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
if (start != m_size)
|
||||
TRY(result.push_back(this->substring(start)));
|
||||
return result;
|
||||
}
|
||||
|
||||
char StringView::back() const
|
||||
{
|
||||
ASSERT(m_size > 0);
|
||||
return m_data[m_size - 1];
|
||||
}
|
||||
|
||||
char StringView::front() const
|
||||
{
|
||||
ASSERT(m_size > 0);
|
||||
return m_data[0];
|
||||
}
|
||||
|
||||
bool StringView::contains(char ch) const
|
||||
{
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (m_data[i] == ch)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
StringView::size_type StringView::count(char ch) const
|
||||
{
|
||||
size_type result = 0;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (m_data[i] == ch)
|
||||
result++;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool StringView::empty() const
|
||||
{
|
||||
return m_size == 0;
|
||||
}
|
||||
|
||||
StringView::size_type StringView::size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
const char* StringView::data() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -38,7 +38,7 @@ namespace BAN
|
||||
}
|
||||
|
||||
BAN::Time from_unix_time(uint64_t unix_time)
|
||||
{
|
||||
{
|
||||
BAN::Time time {};
|
||||
|
||||
time.second = unix_time % 60; unix_time /= 60;
|
||||
@@ -68,4 +68,4 @@ namespace BAN
|
||||
return time;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,26 @@
|
||||
cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
project(BAN CXX)
|
||||
|
||||
set(BAN_SOURCES
|
||||
BAN/Assert.cpp
|
||||
BAN/New.cpp
|
||||
BAN/String.cpp
|
||||
BAN/StringView.cpp
|
||||
BAN/Time.cpp
|
||||
)
|
||||
|
||||
add_custom_target(ban-headers
|
||||
COMMAND sudo rsync -a ${CMAKE_CURRENT_SOURCE_DIR}/include/ ${BANAN_INCLUDE}/
|
||||
DEPENDS sysroot
|
||||
USES_TERMINAL
|
||||
)
|
||||
|
||||
add_library(ban ${BAN_SOURCES})
|
||||
target_link_options(ban PRIVATE -nolibc)
|
||||
banan_link_library(ban libc)
|
||||
add_dependencies(ban headers libc-install)
|
||||
|
||||
set_target_properties(ban PROPERTIES OUTPUT_NAME libban)
|
||||
|
||||
# set SONAME as cmake doesn't set it for some reason??
|
||||
set_target_properties(ban PROPERTIES LINK_FLAGS "-Wl,-soname,libban.so")
|
||||
|
||||
banan_install_headers(ban)
|
||||
install(TARGETS ban OPTIONAL)
|
||||
add_custom_target(ban-install
|
||||
COMMAND sudo cp ${CMAKE_CURRENT_BINARY_DIR}/libban.a ${BANAN_LIB}/
|
||||
DEPENDS ban
|
||||
BYPRODUCTS ${BANAN_LIB}/libban.a
|
||||
USES_TERMINAL
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/Iterators.h>
|
||||
#include <BAN/Span.h>
|
||||
|
||||
@@ -18,78 +19,85 @@ namespace BAN
|
||||
using const_iterator = ConstIteratorSimple<T, Array>;
|
||||
|
||||
public:
|
||||
constexpr Array() = default;
|
||||
constexpr Array(const T&);
|
||||
Array();
|
||||
Array(const T&);
|
||||
|
||||
iterator begin() { return iterator(m_data); }
|
||||
iterator end() { return iterator(m_data + size()); }
|
||||
const_iterator begin() const { return const_iterator(m_data); }
|
||||
const_iterator end() const { return const_iterator(m_data + size()); }
|
||||
|
||||
constexpr const T& operator[](size_type) const;
|
||||
constexpr T& operator[](size_type);
|
||||
const T& operator[](size_type) const;
|
||||
T& operator[](size_type);
|
||||
|
||||
constexpr const T& back() const;
|
||||
constexpr T& back();
|
||||
constexpr const T& front() const;
|
||||
constexpr T& front();
|
||||
const T& back() const;
|
||||
T& back();
|
||||
const T& front() const;
|
||||
T& front();
|
||||
|
||||
Span<T> span() { return Span(m_data, size()); }
|
||||
Span<const T> span() const { return Span(m_data, size()); }
|
||||
const Span<T> span() const { return Span(m_data, size()); }
|
||||
|
||||
constexpr size_type size() const;
|
||||
|
||||
constexpr const T* data() const { return m_data; }
|
||||
constexpr T* data() { return m_data; }
|
||||
|
||||
const T* data() const { return m_data; }
|
||||
T* data() { return m_data; }
|
||||
|
||||
private:
|
||||
T m_data[S] {};
|
||||
T m_data[S];
|
||||
};
|
||||
|
||||
template<typename T, size_t S>
|
||||
constexpr Array<T, S>::Array(const T& value)
|
||||
Array<T, S>::Array()
|
||||
{
|
||||
for (size_type i = 0; i < S; i++)
|
||||
m_data[i] = T();
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
Array<T, S>::Array(const T& value)
|
||||
{
|
||||
for (size_type i = 0; i < S; i++)
|
||||
m_data[i] = value;
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
constexpr const T& Array<T, S>::operator[](size_type index) const
|
||||
const T& Array<T, S>::operator[](size_type index) const
|
||||
{
|
||||
ASSERT(index < S);
|
||||
return m_data[index];
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
constexpr T& Array<T, S>::operator[](size_type index)
|
||||
T& Array<T, S>::operator[](size_type index)
|
||||
{
|
||||
ASSERT(index < S);
|
||||
return m_data[index];
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
constexpr const T& Array<T, S>::back() const
|
||||
const T& Array<T, S>::back() const
|
||||
{
|
||||
ASSERT(S != 0);
|
||||
return m_data[S - 1];
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
constexpr T& Array<T, S>::back()
|
||||
T& Array<T, S>::back()
|
||||
{
|
||||
ASSERT(S != 0);
|
||||
return m_data[S - 1];
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
constexpr const T& Array<T, S>::front() const
|
||||
const T& Array<T, S>::front() const
|
||||
{
|
||||
ASSERT(S != 0);
|
||||
return m_data[0];
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
constexpr T& Array<T, S>::front()
|
||||
T& Array<T, S>::front()
|
||||
{
|
||||
ASSERT(S != 0);
|
||||
return m_data[0];
|
||||
@@ -101,4 +109,4 @@ namespace BAN
|
||||
return S;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#define __ban_assert_stringify_helper(s) #s
|
||||
#define __ban_assert_stringify(s) __ban_assert_stringify_helper(s)
|
||||
|
||||
#define ASSERT(cond) \
|
||||
(__builtin_expect(!(cond), 0) \
|
||||
? __ban_assertion_failed(__FILE__ ":" __ban_assert_stringify(__LINE__), "ASSERT(" #cond ") failed") \
|
||||
: (void)0)
|
||||
|
||||
#define ASSERT_NOT_REACHED() \
|
||||
__ban_assertion_failed(__FILE__ ":" __ban_assert_stringify(__LINE__), "ASSERT_NOT_REACHED() reached")
|
||||
|
||||
[[noreturn]] void __ban_assertion_failed(const char* location, const char* msg);
|
||||
#if defined(__is_kernel)
|
||||
#include <kernel/Panic.h>
|
||||
#define ASSERT(cond) do { if (!(cond)) Kernel::panic("ASSERT("#cond") failed"); } while (false)
|
||||
#define ASSERT_NOT_REACHED() Kernel::panic("ASSERT_NOT_REACHED() failed")
|
||||
#else
|
||||
#include <assert.h>
|
||||
#define ASSERT(cond) assert((cond) && "ASSERT("#cond") failed")
|
||||
#define ASSERT_NOT_REACHED() do { assert(false && "ASSERT_NOT_REACHED() failed"); __builtin_unreachable(); } while (false)
|
||||
#endif
|
||||
@@ -1,99 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Traits.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
enum MemoryOrder
|
||||
{
|
||||
memory_order_relaxed = __ATOMIC_RELAXED,
|
||||
memory_order_consume = __ATOMIC_CONSUME,
|
||||
memory_order_acquire = __ATOMIC_ACQUIRE,
|
||||
memory_order_release = __ATOMIC_RELEASE,
|
||||
memory_order_acq_rel = __ATOMIC_ACQ_REL,
|
||||
memory_order_seq_cst = __ATOMIC_SEQ_CST,
|
||||
};
|
||||
|
||||
template<typename T> concept atomic_c = is_integral_v<T> || is_pointer_v<T>;
|
||||
template<typename T> concept atomic_lockfree_c = (is_integral_v<T> || is_pointer_v<T>) && __atomic_always_lock_free(sizeof(T), 0);
|
||||
|
||||
template<atomic_lockfree_c T, atomic_c U>
|
||||
inline void atomic_store(T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { __atomic_store_n(&obj, value, mem_order); }
|
||||
template<atomic_lockfree_c T>
|
||||
inline T atomic_load(T& obj, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_load_n(&obj, mem_order); }
|
||||
|
||||
template<atomic_lockfree_c T, atomic_c U>
|
||||
inline T atomic_exchange(T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_exchange_n(&obj, value, mem_order); }
|
||||
template<atomic_lockfree_c T, atomic_lockfree_c U, atomic_c V>
|
||||
inline bool atomic_compare_exchange(T& obj, U& expected, V value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_compare_exchange_n(&obj, &expected, value, false, mem_order, mem_order); }
|
||||
|
||||
#define DECL_ATOMIC_INLINE template<atomic_lockfree_c T, atomic_c U> inline
|
||||
DECL_ATOMIC_INLINE T atomic_add_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_add_fetch (&obj, value, mem_order); }
|
||||
DECL_ATOMIC_INLINE T atomic_sub_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_sub_fetch (&obj, value, mem_order); }
|
||||
DECL_ATOMIC_INLINE T atomic_and_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_and_fetch (&obj, value, mem_order); }
|
||||
DECL_ATOMIC_INLINE T atomic_xor_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_xor_fetch (&obj, value, mem_order); }
|
||||
DECL_ATOMIC_INLINE T atomic_or_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_or_fetch (&obj, value, mem_order); }
|
||||
DECL_ATOMIC_INLINE T atomic_nand_fetch(T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_nand_fetch(&obj, value, mem_order); }
|
||||
|
||||
DECL_ATOMIC_INLINE T atomic_fetch_add (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_add (&obj, value, mem_order); }
|
||||
DECL_ATOMIC_INLINE T atomic_fetch_sub (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_sub (&obj, value, mem_order); }
|
||||
DECL_ATOMIC_INLINE T atomic_fetch_and (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_and (&obj, value, mem_order); }
|
||||
DECL_ATOMIC_INLINE T atomic_fetch_xor (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_xor (&obj, value, mem_order); }
|
||||
DECL_ATOMIC_INLINE T atomic_fetch_or (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_or (&obj, value, mem_order); }
|
||||
DECL_ATOMIC_INLINE T atomic_fetch_nand(T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_nand(&obj, value, mem_order); }
|
||||
#undef DECL_ATOMIC_INLINE
|
||||
|
||||
template<atomic_lockfree_c T, MemoryOrder MEM_ORDER = MemoryOrder::memory_order_seq_cst>
|
||||
class Atomic
|
||||
{
|
||||
Atomic(const Atomic&) = delete;
|
||||
Atomic(Atomic&&) = delete;
|
||||
Atomic& operator=(const Atomic&) volatile = delete;
|
||||
Atomic& operator=(Atomic&&) volatile = delete;
|
||||
|
||||
public:
|
||||
constexpr Atomic() : m_value(0) {}
|
||||
constexpr Atomic(T val) : m_value(val) {}
|
||||
|
||||
inline T load(MemoryOrder mem_order = MEM_ORDER) const volatile { return atomic_load(m_value, mem_order); }
|
||||
inline void store(T val, MemoryOrder mem_order = MEM_ORDER) volatile { atomic_store(m_value, val, mem_order); }
|
||||
|
||||
inline T operator=(T val) volatile { store(val); return val; }
|
||||
|
||||
inline operator T() const volatile { return load(); }
|
||||
|
||||
inline T operator+=(T val) volatile { return atomic_add_fetch(m_value, val, MEM_ORDER); }
|
||||
inline T operator-=(T val) volatile { return atomic_sub_fetch(m_value, val, MEM_ORDER); }
|
||||
inline T operator&=(T val) volatile { return atomic_and_fetch(m_value, val, MEM_ORDER); }
|
||||
inline T operator^=(T val) volatile { return atomic_xor_fetch(m_value, val, MEM_ORDER); }
|
||||
inline T operator|=(T val) volatile { return atomic_or_fetch(m_value, val, MEM_ORDER); }
|
||||
|
||||
inline T operator--() volatile { return atomic_sub_fetch(m_value, 1, MEM_ORDER); }
|
||||
inline T operator++() volatile { return atomic_add_fetch(m_value, 1, MEM_ORDER); }
|
||||
|
||||
inline T operator--(int) volatile { return atomic_fetch_sub(m_value, 1, MEM_ORDER); }
|
||||
inline T operator++(int) volatile { return atomic_fetch_add(m_value, 1, MEM_ORDER); }
|
||||
|
||||
inline bool compare_exchange(T& expected, T desired, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_compare_exchange(m_value, expected, desired, mem_order); }
|
||||
inline T exchange(T desired, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_exchange(m_value, desired, mem_order); };
|
||||
|
||||
inline T add_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_add_fetch (m_value, val, mem_order); }
|
||||
inline T sub_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_sub_fetch (m_value, val, mem_order); }
|
||||
inline T and_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_and_fetch (m_value, val, mem_order); }
|
||||
inline T xor_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_xor_fetch (m_value, val, mem_order); }
|
||||
inline T or_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_or_fetch (m_value, val, mem_order); }
|
||||
inline T nand_fetch(T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_nand_fetch(m_value, val, mem_order); }
|
||||
|
||||
inline T fetch_add (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_add (m_value, val, mem_order); }
|
||||
inline T fetch_sub (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_sub (m_value, val, mem_order); }
|
||||
inline T fetch_and (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_and (m_value, val, mem_order); }
|
||||
inline T fetch_xor (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_xor (m_value, val, mem_order); }
|
||||
inline T fetch_or (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_or (m_value, val, mem_order); }
|
||||
inline T fetch_nand(T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_nand(m_value, val, mem_order); }
|
||||
|
||||
private:
|
||||
T m_value;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename To, typename From>
|
||||
constexpr To bit_cast(const From& from)
|
||||
{
|
||||
return __builtin_bit_cast(To, from);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Span.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<bool CONST>
|
||||
class ByteSpanGeneral
|
||||
{
|
||||
public:
|
||||
using value_type = maybe_const_t<CONST, uint8_t>;
|
||||
using size_type = size_t;
|
||||
|
||||
public:
|
||||
ByteSpanGeneral() = default;
|
||||
ByteSpanGeneral(value_type* data, size_type size)
|
||||
: m_data(data)
|
||||
, m_size(size)
|
||||
{ }
|
||||
|
||||
template<bool SRC_CONST>
|
||||
ByteSpanGeneral(const ByteSpanGeneral<SRC_CONST>& other) requires(CONST || !SRC_CONST)
|
||||
: m_data(other.data())
|
||||
, m_size(other.size())
|
||||
{ }
|
||||
template<bool SRC_CONST>
|
||||
ByteSpanGeneral(ByteSpanGeneral<SRC_CONST>&& other) requires(CONST || !SRC_CONST)
|
||||
: m_data(other.data())
|
||||
, m_size(other.size())
|
||||
{
|
||||
other.clear();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ByteSpanGeneral(const Span<T>& other) requires(is_same_v<T, uint8_t> || (is_same_v<T, const uint8_t> && CONST))
|
||||
: m_data(other.data())
|
||||
, m_size(other.size())
|
||||
{ }
|
||||
template<typename T>
|
||||
ByteSpanGeneral(Span<T>&& other) requires(is_same_v<T, uint8_t> || (is_same_v<T, const uint8_t> && CONST))
|
||||
: m_data(other.data())
|
||||
, m_size(other.size())
|
||||
{
|
||||
other.clear();
|
||||
}
|
||||
|
||||
template<bool SRC_CONST>
|
||||
ByteSpanGeneral& operator=(const ByteSpanGeneral<SRC_CONST>& other) requires(CONST || !SRC_CONST)
|
||||
{
|
||||
m_data = other.data();
|
||||
m_size = other.size();
|
||||
return *this;
|
||||
}
|
||||
template<bool SRC_CONST>
|
||||
ByteSpanGeneral& operator=(ByteSpanGeneral<SRC_CONST>&& other) requires(CONST || !SRC_CONST)
|
||||
{
|
||||
m_data = other.data();
|
||||
m_size = other.size();
|
||||
other.clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
static ByteSpanGeneral from(S& value) requires(CONST || !is_const_v<S>)
|
||||
{
|
||||
return ByteSpanGeneral(reinterpret_cast<value_type*>(&value), sizeof(S));
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
S& as() const requires(!CONST || is_const_v<S>)
|
||||
{
|
||||
ASSERT(m_data);
|
||||
ASSERT(m_size >= sizeof(S));
|
||||
return *reinterpret_cast<S*>(m_data);
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
Span<S> as_span() const requires(!CONST || is_const_v<S>)
|
||||
{
|
||||
ASSERT(m_data);
|
||||
return Span<S>(reinterpret_cast<S*>(m_data), m_size / sizeof(S));
|
||||
}
|
||||
|
||||
[[nodiscard]] ByteSpanGeneral slice(size_type offset, size_type length = size_type(-1)) const
|
||||
{
|
||||
ASSERT(m_data);
|
||||
ASSERT(m_size >= offset);
|
||||
if (length == size_type(-1))
|
||||
length = m_size - offset;
|
||||
ASSERT(m_size >= offset + length);
|
||||
return ByteSpanGeneral(m_data + offset, length);
|
||||
}
|
||||
|
||||
value_type& operator[](size_type offset) const
|
||||
{
|
||||
ASSERT(offset < m_size);
|
||||
return m_data[offset];
|
||||
}
|
||||
|
||||
value_type* data() const { return m_data; }
|
||||
|
||||
bool empty() const { return m_size == 0; }
|
||||
size_type size() const { return m_size; }
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_data = nullptr;
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
value_type* m_data { nullptr };
|
||||
size_type m_size { 0 };
|
||||
|
||||
friend class ByteSpanGeneral<!CONST>;
|
||||
};
|
||||
|
||||
using ByteSpan = ByteSpanGeneral<false>;
|
||||
using ConstByteSpan = ByteSpanGeneral<true>;
|
||||
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Assert.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/PlacementNew.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
@@ -24,25 +22,17 @@ namespace BAN
|
||||
void push(const T&);
|
||||
void push(T&&);
|
||||
template<typename... Args>
|
||||
void emplace(Args&&... args) requires is_constructible_v<T, Args...>;
|
||||
void emplace(Args&&... args);
|
||||
|
||||
void pop();
|
||||
|
||||
const T& front() const;
|
||||
T& front();
|
||||
|
||||
const T& back() const;
|
||||
T& back();
|
||||
|
||||
const T& operator[](size_t index) const;
|
||||
T& operator[](size_t index);
|
||||
|
||||
void clear();
|
||||
|
||||
size_type size() const { return m_size; }
|
||||
bool empty() const { return size() == 0; }
|
||||
bool full() const { return size() == capacity(); }
|
||||
|
||||
|
||||
static constexpr size_type capacity() { return S; }
|
||||
|
||||
private:
|
||||
@@ -58,7 +48,8 @@ namespace BAN
|
||||
template<typename T, size_t S>
|
||||
CircularQueue<T, S>::~CircularQueue()
|
||||
{
|
||||
clear();
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
element_at((m_first + i) % capacity())->~T();
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
@@ -75,7 +66,7 @@ namespace BAN
|
||||
|
||||
template<typename T, size_t S>
|
||||
template<typename... Args>
|
||||
void CircularQueue<T, S>::emplace(Args&&... args) requires is_constructible_v<T, Args...>
|
||||
void CircularQueue<T, S>::emplace(Args&&... args)
|
||||
{
|
||||
ASSERT(!full());
|
||||
new (element_at(((m_first + m_size) % capacity()))) T(BAN::forward<Args>(args)...);
|
||||
@@ -105,42 +96,6 @@ namespace BAN
|
||||
return *element_at(m_first);
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
const T& CircularQueue<T, S>::back() const
|
||||
{
|
||||
ASSERT(!empty());
|
||||
return *element_at((m_first + m_size - 1) % capacity());
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
T& CircularQueue<T, S>::back()
|
||||
{
|
||||
ASSERT(!empty());
|
||||
return *element_at((m_first + m_size - 1) % capacity());
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
const T& CircularQueue<T, S>::operator[](size_t index) const
|
||||
{
|
||||
ASSERT(index < m_size);
|
||||
return *element_at((m_first + index) % capacity());
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
T& CircularQueue<T, S>::operator[](size_t index)
|
||||
{
|
||||
ASSERT(index < m_size);
|
||||
return *element_at((m_first + index) % capacity());
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
void CircularQueue<T, S>::clear()
|
||||
{
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
element_at((m_first + i) % capacity())->~T();
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
const T* CircularQueue<T, S>::element_at(size_type index) const
|
||||
{
|
||||
@@ -155,4 +110,4 @@ namespace BAN
|
||||
return (T*)(m_storage + index * sizeof(T));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#if __is_kernel
|
||||
|
||||
#include <kernel/Debug.h>
|
||||
|
||||
#else
|
||||
|
||||
#include <BAN/Formatter.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define __debug_putchar [](int c) { putc_unlocked(c, stddbg); }
|
||||
|
||||
#define dprintln(...) \
|
||||
do { \
|
||||
flockfile(stddbg); \
|
||||
BAN::Formatter::print(__debug_putchar, __VA_ARGS__); \
|
||||
BAN::Formatter::print(__debug_putchar,"\n"); \
|
||||
fflush(stddbg); \
|
||||
funlockfile(stddbg); \
|
||||
} while (false)
|
||||
|
||||
#define dwarnln(...) \
|
||||
do { \
|
||||
flockfile(stddbg); \
|
||||
BAN::Formatter::print(__debug_putchar, "\e[33m"); \
|
||||
BAN::Formatter::print(__debug_putchar, __VA_ARGS__); \
|
||||
BAN::Formatter::print(__debug_putchar, "\e[m\n"); \
|
||||
fflush(stddbg); \
|
||||
funlockfile(stddbg); \
|
||||
} while(false)
|
||||
|
||||
#define derrorln(...) \
|
||||
do { \
|
||||
flockfile(stddbg); \
|
||||
BAN::Formatter::print(__debug_putchar, "\e[31m"); \
|
||||
BAN::Formatter::print(__debug_putchar, __VA_ARGS__); \
|
||||
BAN::Formatter::print(__debug_putchar, "\e[m\n"); \
|
||||
fflush(stddbg); \
|
||||
funlockfile(stddbg); \
|
||||
} while(false)
|
||||
|
||||
#define dprintln_if(cond, ...) \
|
||||
do { \
|
||||
if constexpr(cond) \
|
||||
dprintln(__VA_ARGS__); \
|
||||
} while(false)
|
||||
|
||||
#define dwarnln_if(cond, ...) \
|
||||
do { \
|
||||
if constexpr(cond) \
|
||||
dwarnln(__VA_ARGS__); \
|
||||
} while(false)
|
||||
|
||||
#define derrorln_if(cond, ...) \
|
||||
do { \
|
||||
if constexpr(cond) \
|
||||
derrorln(__VA_ARGS__); \
|
||||
} while(false)
|
||||
|
||||
#endif
|
||||
@@ -45,12 +45,6 @@ namespace BAN
|
||||
#endif
|
||||
}
|
||||
|
||||
template<integral T>
|
||||
constexpr T little_endian_to_host(T value)
|
||||
{
|
||||
return host_to_little_endian(value);
|
||||
}
|
||||
|
||||
template<integral T>
|
||||
constexpr T host_to_big_endian(T value)
|
||||
{
|
||||
@@ -61,28 +55,13 @@ namespace BAN
|
||||
#endif
|
||||
}
|
||||
|
||||
template<integral T>
|
||||
constexpr T big_endian_to_host(T value)
|
||||
{
|
||||
return host_to_big_endian(value);
|
||||
}
|
||||
|
||||
template<integral T>
|
||||
struct LittleEndian
|
||||
{
|
||||
constexpr LittleEndian()
|
||||
: raw(0)
|
||||
{ }
|
||||
|
||||
constexpr LittleEndian(T value)
|
||||
: raw(host_to_little_endian(value))
|
||||
{ }
|
||||
|
||||
constexpr operator T() const
|
||||
{
|
||||
return host_to_little_endian(raw);
|
||||
}
|
||||
|
||||
private:
|
||||
T raw;
|
||||
};
|
||||
@@ -90,36 +69,12 @@ namespace BAN
|
||||
template<integral T>
|
||||
struct BigEndian
|
||||
{
|
||||
constexpr BigEndian()
|
||||
: raw(0)
|
||||
{ }
|
||||
|
||||
constexpr BigEndian(T value)
|
||||
: raw(host_to_big_endian(value))
|
||||
{ }
|
||||
|
||||
constexpr operator T() const
|
||||
{
|
||||
return host_to_big_endian(raw);
|
||||
}
|
||||
|
||||
private:
|
||||
T raw;
|
||||
};
|
||||
|
||||
template<integral T>
|
||||
using NetworkEndian = BigEndian<T>;
|
||||
|
||||
template<integral T>
|
||||
constexpr T host_to_network_endian(T value)
|
||||
{
|
||||
return host_to_big_endian(value);
|
||||
}
|
||||
|
||||
template<integral T>
|
||||
constexpr T network_endian_to_host(T value)
|
||||
{
|
||||
return big_endian_to_host(value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Formatter.h>
|
||||
#include <BAN/NoCopyMove.h>
|
||||
#include <BAN/StringView.h>
|
||||
#include <BAN/Variant.h>
|
||||
|
||||
#include <errno.h>
|
||||
@@ -10,16 +10,16 @@
|
||||
#ifdef __is_kernel
|
||||
#include <kernel/Panic.h>
|
||||
#include <kernel/Errors.h>
|
||||
#define MUST(...) ({ auto&& e = (__VA_ARGS__); if (e.is_error()) Kernel::panic("{}", e.error()); e.release_value(); })
|
||||
#define MUST_REF(...) *({ auto&& e = (__VA_ARGS__); if (e.is_error()) Kernel::panic("{}", e.error()); &e.release_value(); })
|
||||
#define MUST(expr) ({ auto&& e = expr; if (e.is_error()) Kernel::panic("{}", e.error()); e.release_value(); })
|
||||
#define MUST_REF(expr) *({ auto&& e = expr; if (e.is_error()) Kernel::panic("{}", e.error()); &e.release_value(); })
|
||||
#else
|
||||
#include <BAN/Debug.h>
|
||||
#define MUST(...) ({ auto&& e = (__VA_ARGS__); if (e.is_error()) { derrorln("MUST(" #__VA_ARGS__ "): {}", e.error()); __builtin_trap(); } e.release_value(); })
|
||||
#define MUST_REF(...) *({ auto&& e = (__VA_ARGS__); if (e.is_error()) { derrorln("MUST(" #__VA_ARGS__ "): {}", e.error()); __builtin_trap(); } &e.release_value(); })
|
||||
#include <assert.h>
|
||||
#define MUST(expr) ({ auto&& e = expr; assert(!e.is_error()); e.release_value(); })
|
||||
#define MUST_REF(expr) *({ auto&& e = expr; assert(!e.is_error()); &e.release_value(); })
|
||||
#endif
|
||||
|
||||
#define TRY(...) ({ auto&& e = (__VA_ARGS__); if (e.is_error()) return e.release_error(); e.release_value(); })
|
||||
#define TRY_REF(...) *({ auto&& e = (__VA_ARGS__); if (e.is_error()) return e.release_error(); &e.release_value(); })
|
||||
#define TRY(expr) ({ auto&& e = expr; if (e.is_error()) return e.release_error(); e.release_value(); })
|
||||
#define TRY_REF(expr) *({ auto&& e = expr; if (e.is_error()) return e.release_error(); &e.release_value(); })
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
@@ -37,14 +37,7 @@ namespace BAN
|
||||
{
|
||||
return Error((uint64_t)error | kernel_error_mask);
|
||||
}
|
||||
#else
|
||||
template<size_t N>
|
||||
consteval static Error from_literal(const char (&message)[N])
|
||||
{
|
||||
return Error(message);
|
||||
}
|
||||
#endif
|
||||
|
||||
static Error from_errno(int error)
|
||||
{
|
||||
return Error(error);
|
||||
@@ -62,43 +55,27 @@ namespace BAN
|
||||
}
|
||||
#endif
|
||||
|
||||
constexpr uint64_t get_error_code() const { return m_error_code; }
|
||||
const char* get_message() const
|
||||
uint64_t get_error_code() const { return m_error_code; }
|
||||
BAN::StringView get_message() const
|
||||
{
|
||||
#ifdef __is_kernel
|
||||
if (m_error_code & kernel_error_mask)
|
||||
return Kernel::error_string(kernel_error());
|
||||
#else
|
||||
if (m_message)
|
||||
return m_message;
|
||||
#endif
|
||||
if (auto* desc = strerrordesc_np(m_error_code))
|
||||
return desc;
|
||||
return "Unknown error";
|
||||
return strerror(m_error_code);
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr Error(uint64_t error)
|
||||
Error(uint64_t error)
|
||||
: m_error_code(error)
|
||||
{}
|
||||
|
||||
#ifndef __is_kernel
|
||||
constexpr Error(const char* message)
|
||||
: m_message(message)
|
||||
{}
|
||||
#endif
|
||||
|
||||
uint64_t m_error_code { 0 };
|
||||
|
||||
#ifndef __is_kernel
|
||||
const char* m_message { nullptr };
|
||||
#endif
|
||||
uint64_t m_error_code;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class [[nodiscard]] ErrorOr
|
||||
{
|
||||
BAN_NON_COPYABLE(ErrorOr);
|
||||
public:
|
||||
ErrorOr(const T& value)
|
||||
: m_data(value)
|
||||
@@ -112,14 +89,6 @@ namespace BAN
|
||||
ErrorOr(Error&& error)
|
||||
: m_data(move(error))
|
||||
{}
|
||||
ErrorOr(ErrorOr&& other)
|
||||
: m_data(move(other.m_data))
|
||||
{}
|
||||
ErrorOr& operator=(ErrorOr&& other)
|
||||
{
|
||||
m_data = move(other.m_data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool is_error() const { return m_data.template has<Error>(); }
|
||||
const Error& error() const { return m_data.template get<Error>(); }
|
||||
|
||||
@@ -10,19 +10,22 @@ namespace BAN::Formatter
|
||||
|
||||
struct ValueFormat;
|
||||
|
||||
template<typename F>
|
||||
static void print(F putc, const char* format);
|
||||
|
||||
template<typename F, typename Arg, typename... Args>
|
||||
static void print(F putc, const char* format, Arg&& arg, Args&&... args);
|
||||
|
||||
template<typename F, typename... Args>
|
||||
concept PrintableArguments = requires(F putc, Args&&... args, const ValueFormat& format)
|
||||
{
|
||||
(print_argument(putc, BAN::forward<Args>(args), format), ...);
|
||||
};
|
||||
static void println(F putc, const char* format, Args&&... args);
|
||||
|
||||
template<typename F, typename T>
|
||||
inline void print_argument(F putc, T value, const ValueFormat& format);
|
||||
static void print_argument(F putc, T value, const ValueFormat& format);
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template<typename F, typename T>
|
||||
inline size_t parse_format_and_print_argument(F putc, const char* format, T&& arg);
|
||||
static size_t parse_format_and_print_argument(F putc, const char* format, T&& arg);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -36,12 +39,11 @@ namespace BAN::Formatter
|
||||
int base = 10;
|
||||
int percision = 3;
|
||||
int fill = 0;
|
||||
char fill_char = '0';
|
||||
bool upper = false;
|
||||
};
|
||||
|
||||
template<typename F>
|
||||
inline void print(F putc, const char* format)
|
||||
void print(F putc, const char* format)
|
||||
{
|
||||
while (*format)
|
||||
{
|
||||
@@ -50,8 +52,8 @@ namespace BAN::Formatter
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F, typename Arg, typename... Args> requires PrintableArguments<F, Arg, Args...>
|
||||
inline void print(F putc, const char* format, Arg&& arg, Args&&... args)
|
||||
template<typename F, typename Arg, typename... Args>
|
||||
void print(F putc, const char* format, Arg&& arg, Args&&... args)
|
||||
{
|
||||
while (*format && *format != '{')
|
||||
{
|
||||
@@ -69,7 +71,7 @@ namespace BAN::Formatter
|
||||
}
|
||||
|
||||
template<typename F, typename... Args>
|
||||
inline void println(F putc, const char* format, Args&&... args)
|
||||
void println(F putc, const char* format, Args&&... args)
|
||||
{
|
||||
print(putc, format, args...);
|
||||
putc('\n');
|
||||
@@ -79,7 +81,7 @@ namespace BAN::Formatter
|
||||
{
|
||||
|
||||
template<typename F, typename Arg>
|
||||
inline size_t parse_format_and_print_argument(F putc, const char* format, Arg&& argument)
|
||||
size_t parse_format_and_print_argument(F putc, const char* format, Arg&& argument)
|
||||
{
|
||||
ValueFormat value_format;
|
||||
|
||||
@@ -92,12 +94,6 @@ namespace BAN::Formatter
|
||||
if (!format[i] || format[i] == '}')
|
||||
break;
|
||||
|
||||
if (format[i] == ' ')
|
||||
{
|
||||
value_format.fill_char = ' ';
|
||||
i++;
|
||||
}
|
||||
|
||||
if ('0' <= format[i] && format[i] <= '9')
|
||||
{
|
||||
int fill = 0;
|
||||
@@ -147,7 +143,7 @@ namespace BAN::Formatter
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
inline char value_to_base_char(uint8_t value, int base, bool upper)
|
||||
static char value_to_base_char(uint8_t value, int base, bool upper)
|
||||
{
|
||||
if (base <= 10)
|
||||
return value + '0';
|
||||
@@ -161,13 +157,12 @@ namespace BAN::Formatter
|
||||
}
|
||||
|
||||
template<typename F, typename T>
|
||||
inline void print_integer(F putc, T value, const ValueFormat& format)
|
||||
void print_integer(F putc, T value, const ValueFormat& format)
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
for (int i = 0; i < format.fill - 1; i++)
|
||||
putc(format.fill_char);
|
||||
putc('0');
|
||||
for (int i = 0; i < format.fill || i < 1; i++)
|
||||
putc('0');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -193,31 +188,27 @@ namespace BAN::Formatter
|
||||
}
|
||||
|
||||
while (ptr >= buffer + sizeof(buffer) - format.fill)
|
||||
*(--ptr) = format.fill_char;
|
||||
*(--ptr) = '0';
|
||||
|
||||
if (sign)
|
||||
*(--ptr) = '-';
|
||||
|
||||
|
||||
print(putc, ptr);
|
||||
}
|
||||
|
||||
template<typename F, typename T>
|
||||
inline void print_floating(F putc, T value, const ValueFormat& format)
|
||||
void print_floating(F putc, T value, const ValueFormat& format)
|
||||
{
|
||||
if (value < 0)
|
||||
{
|
||||
putc('-');
|
||||
return print_floating(putc, -value, format);
|
||||
}
|
||||
|
||||
int64_t int_part = (int64_t)value;
|
||||
T frac_part = value - (T)int_part;
|
||||
if (frac_part < 0)
|
||||
frac_part = -frac_part;
|
||||
|
||||
print_integer(putc, int_part, format);
|
||||
|
||||
|
||||
if (format.percision > 0)
|
||||
putc('.');
|
||||
|
||||
|
||||
for (int i = 0; i < format.percision; i++)
|
||||
{
|
||||
frac_part *= format.base;
|
||||
@@ -229,7 +220,7 @@ namespace BAN::Formatter
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline void print_pointer(F putc, void* ptr, const ValueFormat& format)
|
||||
void print_pointer(F putc, void* ptr, const ValueFormat& format)
|
||||
{
|
||||
uintptr_t value = (uintptr_t)ptr;
|
||||
print(putc, "0x");
|
||||
@@ -245,13 +236,13 @@ namespace BAN::Formatter
|
||||
|
||||
*/
|
||||
|
||||
template<typename F, integral T> inline void print_argument(F putc, T value, const ValueFormat& format) { detail::print_integer(putc, value, format); }
|
||||
template<typename F, floating_point T> inline void print_argument(F putc, T value, const ValueFormat& format) { detail::print_floating(putc, value, format); }
|
||||
template<typename F, pointer T> inline void print_argument(F putc, T value, const ValueFormat& format) { detail::print_pointer(putc, (void*)value, format); }
|
||||
template<typename F, integral T> void print_argument(F putc, T value, const ValueFormat& format) { detail::print_integer(putc, value, format); }
|
||||
template<typename F, floating_point T> void print_argument(F putc, T value, const ValueFormat& format) { detail::print_floating(putc, value, format); }
|
||||
template<typename F, pointer T> void print_argument(F putc, T value, const ValueFormat& format) { detail::print_pointer(putc, (void*)value, format); }
|
||||
|
||||
template<typename F> inline void print_argument(F putc, char value, const ValueFormat&) { putc(value); }
|
||||
template<typename F> inline void print_argument(F putc, bool value, const ValueFormat&) { print(putc, value ? "true" : "false"); }
|
||||
template<typename F> inline void print_argument(F putc, const char* value, const ValueFormat&) { print(putc, value); }
|
||||
template<typename F> inline void print_argument(F putc, char* value, const ValueFormat&) { print(putc, value); }
|
||||
template<typename F> void print_argument(F putc, char value, const ValueFormat&) { putc(value); }
|
||||
template<typename F> void print_argument(F putc, bool value, const ValueFormat&) { print(putc, value ? "true" : "false"); }
|
||||
template<typename F> void print_argument(F putc, const char* value, const ValueFormat&) { print(putc, value); }
|
||||
template<typename F> void print_argument(F putc, char* value, const ValueFormat&) { print(putc, value); }
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Traits.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace BAN
|
||||
@@ -15,6 +14,5 @@ namespace BAN
|
||||
class StringView;
|
||||
template<typename> class Vector;
|
||||
template<typename> class LinkedList;
|
||||
template<typename... Ts> requires (!is_const_v<Ts> && ...) class Variant;
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/PlacementNew.h>
|
||||
#include <BAN/New.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
@@ -20,19 +20,19 @@ namespace BAN
|
||||
new (m_storage) CallablePointer(function);
|
||||
}
|
||||
template<typename Own>
|
||||
Function(Ret(Own::*function)(Args...), Own& owner)
|
||||
Function(Ret(Own::*function)(Args...), Own* owner)
|
||||
{
|
||||
static_assert(sizeof(CallableMember<Own>) <= m_size);
|
||||
new (m_storage) CallableMember<Own>(function, owner);
|
||||
}
|
||||
template<typename Own>
|
||||
Function(Ret(Own::*function)(Args...) const, const Own& owner)
|
||||
Function(Ret(Own::*function)(Args...) const, const Own* owner)
|
||||
{
|
||||
static_assert(sizeof(CallableMemberConst<Own>) <= m_size);
|
||||
new (m_storage) CallableMemberConst<Own>(function, owner);
|
||||
}
|
||||
template<typename Lambda>
|
||||
Function(Lambda lambda) requires requires(Lambda lamda, Args&&... args) { { lambda(forward<Args>(args)...) } -> BAN::same_as<Ret>; }
|
||||
Function(Lambda lambda)
|
||||
{
|
||||
static_assert(sizeof(CallableLambda<Lambda>) <= m_size);
|
||||
new (m_storage) CallableLambda<Lambda>(lambda);
|
||||
@@ -56,7 +56,7 @@ namespace BAN
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void clear()
|
||||
{
|
||||
if (*this)
|
||||
@@ -91,36 +91,36 @@ namespace BAN
|
||||
template<typename Own>
|
||||
struct CallableMember : public CallableBase
|
||||
{
|
||||
CallableMember(Ret(Own::*function)(Args...), Own& owner)
|
||||
CallableMember(Ret(Own::*function)(Args...), Own* owner)
|
||||
: m_owner(owner)
|
||||
, m_function(function)
|
||||
{ }
|
||||
|
||||
virtual Ret call(Args... args) const override
|
||||
{
|
||||
return (m_owner.*m_function)(forward<Args>(args)...);
|
||||
return (m_owner->*m_function)(forward<Args>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
Own& m_owner;
|
||||
Own* m_owner = nullptr;
|
||||
Ret(Own::*m_function)(Args...) = nullptr;
|
||||
};
|
||||
|
||||
template<typename Own>
|
||||
struct CallableMemberConst : public CallableBase
|
||||
{
|
||||
CallableMemberConst(Ret(Own::*function)(Args...) const, const Own& owner)
|
||||
CallableMemberConst(Ret(Own::*function)(Args...) const, const Own* owner)
|
||||
: m_owner(owner)
|
||||
, m_function(function)
|
||||
{ }
|
||||
|
||||
virtual Ret call(Args... args) const override
|
||||
{
|
||||
return (m_owner.*m_function)(forward<Args>(args)...);
|
||||
return (m_owner->*m_function)(forward<Args>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
const Own& m_owner;
|
||||
const Own* m_owner = nullptr;
|
||||
Ret(Own::*m_function)(Args...) const = nullptr;
|
||||
};
|
||||
|
||||
@@ -141,8 +141,8 @@ namespace BAN
|
||||
};
|
||||
|
||||
private:
|
||||
static constexpr size_t m_size = sizeof(void*) * 8;
|
||||
static constexpr size_t m_size = sizeof(void*) * 5;
|
||||
alignas(CallableBase) uint8_t m_storage[m_size] { 0 };
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Optional.h>
|
||||
#include <BAN/String.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
struct GUID
|
||||
{
|
||||
uint32_t component1 { 0 };
|
||||
uint16_t component2 { 0 };
|
||||
uint16_t component3 { 0 };
|
||||
uint8_t component45[8] { };
|
||||
|
||||
bool operator==(const GUID& other) const
|
||||
{
|
||||
return memcmp(this, &other, sizeof(GUID)) == 0;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::String> to_string() const
|
||||
{
|
||||
char buffer[37];
|
||||
char* ptr = buffer;
|
||||
|
||||
const auto append_hex_nibble =
|
||||
[&ptr](uint8_t nibble)
|
||||
{
|
||||
if (nibble < 10)
|
||||
*ptr++ = '0' + nibble;
|
||||
else
|
||||
*ptr++ = 'A' + nibble - 10;
|
||||
};
|
||||
|
||||
const auto append_hex_byte =
|
||||
[&append_hex_nibble](uint8_t byte)
|
||||
{
|
||||
append_hex_nibble(byte >> 4);
|
||||
append_hex_nibble(byte & 0xF);
|
||||
};
|
||||
|
||||
append_hex_byte((component1 >> 24) & 0xFF);
|
||||
append_hex_byte((component1 >> 16) & 0xFF);
|
||||
append_hex_byte((component1 >> 8) & 0xFF);
|
||||
append_hex_byte((component1 >> 0) & 0xFF);
|
||||
*ptr++ = '-';
|
||||
append_hex_byte((component2 >> 8) & 0xFF);
|
||||
append_hex_byte((component2 >> 0) & 0xFF);
|
||||
*ptr++ = '-';
|
||||
append_hex_byte((component3 >> 8) & 0xFF);
|
||||
append_hex_byte((component3 >> 0) & 0xFF);
|
||||
*ptr++ = '-';
|
||||
append_hex_byte(component45[0]);
|
||||
append_hex_byte(component45[1]);
|
||||
*ptr++ = '-';
|
||||
append_hex_byte(component45[2]);
|
||||
append_hex_byte(component45[3]);
|
||||
append_hex_byte(component45[4]);
|
||||
append_hex_byte(component45[5]);
|
||||
append_hex_byte(component45[6]);
|
||||
append_hex_byte(component45[7]);
|
||||
*ptr = '\0';
|
||||
|
||||
BAN::String guid;
|
||||
TRY(guid.append(buffer));
|
||||
return BAN::move(guid);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(GUID) == 16);
|
||||
|
||||
}
|
||||
@@ -47,4 +47,4 @@ namespace BAN
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,9 @@
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename Container>
|
||||
class HashMapIterator;
|
||||
|
||||
template<typename Key, typename T, typename HASH = BAN::hash<Key>>
|
||||
class HashMap
|
||||
{
|
||||
@@ -14,17 +17,11 @@ namespace BAN
|
||||
struct Entry
|
||||
{
|
||||
template<typename... Args>
|
||||
Entry(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
|
||||
Entry(const Key& key, Args&&... args)
|
||||
: key(key)
|
||||
, value(forward<Args>(args)...)
|
||||
{}
|
||||
|
||||
template<typename... Args>
|
||||
Entry(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
|
||||
: key(BAN::move(key))
|
||||
, value(forward<Args>(args)...)
|
||||
{}
|
||||
|
||||
Key key;
|
||||
T value;
|
||||
};
|
||||
@@ -45,27 +42,10 @@ namespace BAN
|
||||
HashMap<Key, T, HASH>& operator=(const HashMap<Key, T, HASH>&);
|
||||
HashMap<Key, T, HASH>& operator=(HashMap<Key, T, HASH>&&);
|
||||
|
||||
ErrorOr<iterator> insert(const Key& key, const T& value) { return emplace(key, value); }
|
||||
ErrorOr<iterator> insert(const Key& key, T&& value) { return emplace(key, move(value)); }
|
||||
ErrorOr<iterator> insert(Key&& key, const T& value) { return emplace(move(key), value); }
|
||||
ErrorOr<iterator> insert(Key&& key, T&& value) { return emplace(move(key), move(value)); }
|
||||
|
||||
ErrorOr<iterator> insert_or_assign(const Key& key, const T& value) { return emplace_or_assign(key, value); }
|
||||
ErrorOr<iterator> insert_or_assign(const Key& key, T&& value) { return emplace_or_assign(key, move(value)); }
|
||||
ErrorOr<iterator> insert_or_assign(Key&& key, const T& value) { return emplace_or_assign(move(key), value); }
|
||||
ErrorOr<iterator> insert_or_assign(Key&& key, T&& value) { return emplace_or_assign(move(key), move(value)); }
|
||||
|
||||
ErrorOr<void> insert(const Key&, const T&);
|
||||
ErrorOr<void> insert(const Key&, T&&);
|
||||
template<typename... Args>
|
||||
ErrorOr<iterator> emplace(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
|
||||
{ return emplace(Key(key), forward<Args>(args)...); }
|
||||
template<typename... Args>
|
||||
ErrorOr<iterator> emplace(Key&&, Args&&...) requires is_constructible_v<T, Args...>;
|
||||
|
||||
template<typename... Args>
|
||||
ErrorOr<iterator> emplace_or_assign(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
|
||||
{ return emplace_or_assign(Key(key), forward<Args>(args)...); }
|
||||
template<typename... Args>
|
||||
ErrorOr<iterator> emplace_or_assign(Key&&, Args&&...) requires is_constructible_v<T, Args...>;
|
||||
ErrorOr<void> emplace(const Key&, Args&&...);
|
||||
|
||||
iterator begin() { return iterator(m_buckets.end(), m_buckets.begin()); }
|
||||
iterator end() { return iterator(m_buckets.end(), m_buckets.end()); }
|
||||
@@ -75,14 +55,11 @@ namespace BAN
|
||||
ErrorOr<void> reserve(size_type);
|
||||
|
||||
void remove(const Key&);
|
||||
void remove(iterator it);
|
||||
void clear();
|
||||
|
||||
T& operator[](const Key&);
|
||||
const T& operator[](const Key&) const;
|
||||
|
||||
iterator find(const Key& key);
|
||||
const_iterator find(const Key& key) const;
|
||||
bool contains(const Key&) const;
|
||||
|
||||
bool empty() const;
|
||||
@@ -92,9 +69,7 @@ namespace BAN
|
||||
ErrorOr<void> rebucket(size_type);
|
||||
LinkedList<Entry>& get_bucket(const Key&);
|
||||
const LinkedList<Entry>& get_bucket(const Key&) const;
|
||||
Vector<LinkedList<Entry>>::iterator get_bucket_iterator(const Key&);
|
||||
Vector<LinkedList<Entry>>::const_iterator get_bucket_iterator(const Key&) const;
|
||||
|
||||
|
||||
private:
|
||||
Vector<LinkedList<Entry>> m_buckets;
|
||||
size_type m_size = 0;
|
||||
@@ -140,36 +115,29 @@ namespace BAN
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
template<typename... Args>
|
||||
ErrorOr<typename HashMap<Key, T, HASH>::iterator> HashMap<Key, T, HASH>::emplace(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
|
||||
ErrorOr<void> HashMap<Key, T, HASH>::insert(const Key& key, const T& value)
|
||||
{
|
||||
ASSERT(!contains(key));
|
||||
TRY(rebucket(m_size + 1));
|
||||
return insert(key, move(T(value)));
|
||||
}
|
||||
|
||||
auto bucket_it = get_bucket_iterator(key);
|
||||
TRY(bucket_it->emplace_back(move(key), forward<Args>(args)...));
|
||||
m_size++;
|
||||
|
||||
return iterator(m_buckets.end(), bucket_it, prev(bucket_it->end(), 1));
|
||||
template<typename Key, typename T, typename HASH>
|
||||
ErrorOr<void> HashMap<Key, T, HASH>::insert(const Key& key, T&& value)
|
||||
{
|
||||
return emplace(key, move(value));
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
template<typename... Args>
|
||||
ErrorOr<typename HashMap<Key, T, HASH>::iterator> HashMap<Key, T, HASH>::emplace_or_assign(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
|
||||
ErrorOr<void> HashMap<Key, T, HASH>::emplace(const Key& key, Args&&... args)
|
||||
{
|
||||
if (empty())
|
||||
return emplace(move(key), forward<Args>(args)...);
|
||||
|
||||
auto bucket_it = get_bucket_iterator(key);
|
||||
for (auto entry_it = bucket_it->begin(); entry_it != bucket_it->end(); entry_it++)
|
||||
{
|
||||
if (entry_it->key != key)
|
||||
continue;
|
||||
entry_it->value = T(forward<Args>(args)...);
|
||||
return iterator(m_buckets.end(), bucket_it, entry_it);
|
||||
}
|
||||
|
||||
return emplace(move(key), forward<Args>(args)...);
|
||||
ASSERT(!contains(key));
|
||||
TRY(rebucket(m_size + 1));
|
||||
auto& bucket = get_bucket(key);
|
||||
auto result = bucket.emplace_back(key, forward<Args>(args)...);
|
||||
if (result.is_error())
|
||||
return Error::from_errno(ENOMEM);
|
||||
m_size++;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
@@ -182,16 +150,17 @@ namespace BAN
|
||||
template<typename Key, typename T, typename HASH>
|
||||
void HashMap<Key, T, HASH>::remove(const Key& key)
|
||||
{
|
||||
auto it = find(key);
|
||||
if (it != end())
|
||||
remove(it);
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
void HashMap<Key, T, HASH>::remove(iterator it)
|
||||
{
|
||||
it.outer_current()->remove(it.inner_current());
|
||||
m_size--;
|
||||
if (empty()) return;
|
||||
auto& bucket = get_bucket(key);
|
||||
for (auto it = bucket.begin(); it != bucket.end(); it++)
|
||||
{
|
||||
if (it->key == key)
|
||||
{
|
||||
bucket.remove(it);
|
||||
m_size--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
@@ -209,7 +178,7 @@ namespace BAN
|
||||
for (Entry& entry : bucket)
|
||||
if (entry.key == key)
|
||||
return entry.value;
|
||||
ASSERT_NOT_REACHED();
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
@@ -220,37 +189,18 @@ namespace BAN
|
||||
for (const Entry& entry : bucket)
|
||||
if (entry.key == key)
|
||||
return entry.value;
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
typename HashMap<Key, T, HASH>::iterator HashMap<Key, T, HASH>::find(const Key& key)
|
||||
{
|
||||
if (empty())
|
||||
return end();
|
||||
auto bucket_it = get_bucket_iterator(key);
|
||||
for (auto it = bucket_it->begin(); it != bucket_it->end(); it++)
|
||||
if (it->key == key)
|
||||
return iterator(m_buckets.end(), bucket_it, it);
|
||||
return end();
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
typename HashMap<Key, T, HASH>::const_iterator HashMap<Key, T, HASH>::find(const Key& key) const
|
||||
{
|
||||
if (empty())
|
||||
return end();
|
||||
auto bucket_it = get_bucket_iterator(key);
|
||||
for (auto it = bucket_it->begin(); it != bucket_it->end(); it++)
|
||||
if (it->key == key)
|
||||
return const_iterator(m_buckets.end(), bucket_it, it);
|
||||
return end();
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
bool HashMap<Key, T, HASH>::contains(const Key& key) const
|
||||
{
|
||||
return find(key) != end();
|
||||
if (empty()) return false;
|
||||
const auto& bucket = get_bucket(key);
|
||||
for (const Entry& entry : bucket)
|
||||
if (entry.key == key)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
@@ -273,14 +223,18 @@ namespace BAN
|
||||
|
||||
size_type new_bucket_count = BAN::Math::max<size_type>(bucket_count, m_buckets.size() * 2);
|
||||
Vector<LinkedList<Entry>> new_buckets;
|
||||
TRY(new_buckets.resize(new_bucket_count));
|
||||
|
||||
if (new_buckets.resize(new_bucket_count).is_error())
|
||||
return Error::from_errno(ENOMEM);
|
||||
|
||||
// NOTE: We have to copy the old entries to the new entries and not move
|
||||
// since we might run out of memory half way through.
|
||||
for (auto& bucket : m_buckets)
|
||||
{
|
||||
for (auto it = bucket.begin(); it != bucket.end();)
|
||||
for (Entry& entry : bucket)
|
||||
{
|
||||
size_type new_bucket_index = HASH()(it->key) % new_buckets.size();
|
||||
it = bucket.move_element_to_other_linked_list(new_buckets[new_bucket_index], new_buckets[new_bucket_index].end(), it);
|
||||
size_type bucket_index = HASH()(entry.key) % new_buckets.size();
|
||||
if (new_buckets[bucket_index].push_back(entry).is_error())
|
||||
return Error::from_errno(ENOMEM);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,29 +245,17 @@ namespace BAN
|
||||
template<typename Key, typename T, typename HASH>
|
||||
LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key)
|
||||
{
|
||||
return *get_bucket_iterator(key);
|
||||
ASSERT(!m_buckets.empty());
|
||||
auto index = HASH()(key) % m_buckets.size();
|
||||
return m_buckets[index];
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
const LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key) const
|
||||
{
|
||||
return *get_bucket_iterator(key);
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
Vector<LinkedList<typename HashMap<Key, T, HASH>::Entry>>::iterator HashMap<Key, T, HASH>::get_bucket_iterator(const Key& key)
|
||||
{
|
||||
ASSERT(!m_buckets.empty());
|
||||
auto index = HASH()(key) % m_buckets.size();
|
||||
return next(m_buckets.begin(), index);
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
Vector<LinkedList<typename HashMap<Key, T, HASH>::Entry>>::const_iterator HashMap<Key, T, HASH>::get_bucket_iterator(const Key& key) const
|
||||
{
|
||||
ASSERT(!m_buckets.empty());
|
||||
auto index = HASH()(key) % m_buckets.size();
|
||||
return next(m_buckets.begin(), index);
|
||||
return m_buckets[index];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/Hash.h>
|
||||
#include <BAN/Iterators.h>
|
||||
#include <BAN/LinkedList.h>
|
||||
#include <BAN/Math.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/Vector.h>
|
||||
@@ -11,22 +9,24 @@
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename T, typename HASH>
|
||||
class HashSetIterator;
|
||||
|
||||
template<typename T, typename HASH = hash<T>>
|
||||
class HashSet
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using iterator = IteratorDouble<T, Vector, LinkedList, HashSet>;
|
||||
using const_iterator = ConstIteratorDouble<T, Vector, LinkedList, HashSet>;
|
||||
|
||||
using size_type = hash_t;
|
||||
using const_iterator = HashSetIterator<T, HASH>;
|
||||
|
||||
public:
|
||||
HashSet() = default;
|
||||
HashSet(const HashSet&);
|
||||
HashSet(HashSet&&);
|
||||
HashSet(const HashSet<T, HASH>&);
|
||||
HashSet(HashSet<T, HASH>&&);
|
||||
|
||||
HashSet& operator=(const HashSet&);
|
||||
HashSet& operator=(HashSet&&);
|
||||
HashSet<T, HASH>& operator=(const HashSet<T, HASH>&);
|
||||
HashSet<T, HASH>& operator=(HashSet<T, HASH>&&);
|
||||
|
||||
ErrorOr<void> insert(const T&);
|
||||
ErrorOr<void> insert(T&&);
|
||||
@@ -35,10 +35,8 @@ namespace BAN
|
||||
|
||||
ErrorOr<void> reserve(size_type);
|
||||
|
||||
iterator begin() { return iterator(m_buckets.end(), m_buckets.begin()); }
|
||||
iterator end() { return iterator(m_buckets.end(), m_buckets.end()); }
|
||||
const_iterator begin() const { return const_iterator(m_buckets.end(), m_buckets.begin()); }
|
||||
const_iterator end() const { return const_iterator(m_buckets.end(), m_buckets.end()); }
|
||||
const_iterator begin() const { return const_iterator(this, m_buckets.begin()); }
|
||||
const_iterator end() const { return const_iterator(this, m_buckets.end()); }
|
||||
|
||||
bool contains(const T&) const;
|
||||
|
||||
@@ -47,23 +45,55 @@ namespace BAN
|
||||
|
||||
private:
|
||||
ErrorOr<void> rebucket(size_type);
|
||||
LinkedList<T>& get_bucket(const T&);
|
||||
const LinkedList<T>& get_bucket(const T&) const;
|
||||
Vector<T>& get_bucket(const T&);
|
||||
const Vector<T>& get_bucket(const T&) const;
|
||||
|
||||
private:
|
||||
Vector<LinkedList<T>> m_buckets;
|
||||
Vector<Vector<T>> m_buckets;
|
||||
size_type m_size = 0;
|
||||
|
||||
friend class HashSetIterator<T, HASH>;
|
||||
};
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSet<T, HASH>::HashSet(const HashSet& other)
|
||||
class HashSetIterator
|
||||
{
|
||||
public:
|
||||
HashSetIterator() = default;
|
||||
HashSetIterator(const HashSetIterator<T, HASH>&);
|
||||
|
||||
HashSetIterator<T, HASH>& operator++();
|
||||
HashSetIterator<T, HASH> operator++(int);
|
||||
|
||||
const T& operator*() const;
|
||||
const T* operator->() const;
|
||||
|
||||
bool operator==(const HashSetIterator<T, HASH>&) const;
|
||||
bool operator!=(const HashSetIterator<T, HASH>&) const;
|
||||
|
||||
operator bool() const { return m_owner && m_current_bucket; }
|
||||
|
||||
private:
|
||||
HashSetIterator(const HashSet<T, HASH>* owner, Vector<Vector<T>>::const_iterator bucket);
|
||||
void find_next();
|
||||
|
||||
private:
|
||||
const HashSet<T, HASH>* m_owner = nullptr;
|
||||
Vector<Vector<T>>::const_iterator m_current_bucket;
|
||||
Vector<T>::const_iterator m_current_key;
|
||||
|
||||
friend class HashSet<T, HASH>;
|
||||
};
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSet<T, HASH>::HashSet(const HashSet<T, HASH>& other)
|
||||
: m_buckets(other.m_buckets)
|
||||
, m_size(other.m_size)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSet<T, HASH>::HashSet(HashSet&& other)
|
||||
HashSet<T, HASH>::HashSet(HashSet<T, HASH>&& other)
|
||||
: m_buckets(move(other.m_buckets))
|
||||
, m_size(other.m_size)
|
||||
{
|
||||
@@ -71,7 +101,7 @@ namespace BAN
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSet<T, HASH>& HashSet<T, HASH>::operator=(const HashSet& other)
|
||||
HashSet<T, HASH>& HashSet<T, HASH>::operator=(const HashSet<T, HASH>& other)
|
||||
{
|
||||
clear();
|
||||
m_buckets = other.m_buckets;
|
||||
@@ -80,7 +110,7 @@ namespace BAN
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSet<T, HASH>& HashSet<T, HASH>::operator=(HashSet&& other)
|
||||
HashSet<T, HASH>& HashSet<T, HASH>::operator=(HashSet<T, HASH>&& other)
|
||||
{
|
||||
clear();
|
||||
m_buckets = move(other.m_buckets);
|
||||
@@ -111,15 +141,15 @@ namespace BAN
|
||||
void HashSet<T, HASH>::remove(const T& key)
|
||||
{
|
||||
if (empty()) return;
|
||||
auto& bucket = get_bucket(key);
|
||||
for (auto it = bucket.begin(); it != bucket.end(); it++)
|
||||
Vector<T>& bucket = get_bucket(key);
|
||||
for (size_type i = 0; i < bucket.size(); i++)
|
||||
{
|
||||
if (*it == key)
|
||||
if (bucket[i] == key)
|
||||
{
|
||||
bucket.remove(it);
|
||||
bucket.remove(i);
|
||||
m_size--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,17 +192,20 @@ namespace BAN
|
||||
if (m_buckets.size() >= bucket_count)
|
||||
return {};
|
||||
|
||||
size_type new_bucket_count = Math::max<size_type>(bucket_count, m_buckets.size() * 2);
|
||||
Vector<LinkedList<T>> new_buckets;
|
||||
size_type new_bucket_count = BAN::Math::max<size_type>(bucket_count, m_buckets.size() * 2);
|
||||
Vector<Vector<T>> new_buckets;
|
||||
if (new_buckets.resize(new_bucket_count).is_error())
|
||||
return Error::from_errno(ENOMEM);
|
||||
|
||||
for (auto& bucket : m_buckets)
|
||||
|
||||
// NOTE: We have to copy the old keys to the new keys and not move
|
||||
// since we might run out of memory half way through.
|
||||
for (Vector<T>& bucket : m_buckets)
|
||||
{
|
||||
for (auto it = bucket.begin(); it != bucket.end();)
|
||||
for (T& key : bucket)
|
||||
{
|
||||
size_type new_bucket_index = HASH()(*it) % new_buckets.size();
|
||||
it = bucket.move_element_to_other_linked_list(new_buckets[new_bucket_index], new_buckets[new_bucket_index].end(), it);
|
||||
size_type bucket_index = HASH()(key) % new_buckets.size();
|
||||
if (new_buckets[bucket_index].push_back(key).is_error())
|
||||
return Error::from_errno(ENOMEM);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +214,7 @@ namespace BAN
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
LinkedList<T>& HashSet<T, HASH>::get_bucket(const T& key)
|
||||
Vector<T>& HashSet<T, HASH>::get_bucket(const T& key)
|
||||
{
|
||||
ASSERT(!m_buckets.empty());
|
||||
size_type index = HASH()(key) % m_buckets.size();
|
||||
@@ -189,11 +222,83 @@ namespace BAN
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
const LinkedList<T>& HashSet<T, HASH>::get_bucket(const T& key) const
|
||||
const Vector<T>& HashSet<T, HASH>::get_bucket(const T& key) const
|
||||
{
|
||||
ASSERT(!m_buckets.empty());
|
||||
size_type index = HASH()(key) % m_buckets.size();
|
||||
return m_buckets[index];
|
||||
}
|
||||
|
||||
}
|
||||
template<typename T, typename HASH>
|
||||
HashSetIterator<T, HASH>& HashSetIterator<T, HASH>::operator++()
|
||||
{
|
||||
ASSERT(*this);
|
||||
if (m_current_key == m_current_bucket->end())
|
||||
m_current_bucket++;
|
||||
else
|
||||
m_current_key++;
|
||||
find_next();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSetIterator<T, HASH> HashSetIterator<T, HASH>::operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
const T& HashSetIterator<T, HASH>::operator*() const
|
||||
{
|
||||
ASSERT(m_owner && m_current_bucket && m_current_key);
|
||||
return *m_current_key;
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
const T* HashSetIterator<T, HASH>::operator->() const
|
||||
{
|
||||
return &**this;
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
bool HashSetIterator<T, HASH>::operator==(const HashSetIterator<T, HASH>& other) const
|
||||
{
|
||||
if (!m_owner || m_owner != other.m_owner)
|
||||
return false;
|
||||
return m_current_bucket == other.m_current_bucket
|
||||
&& m_current_key == other.m_current_key;
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
bool HashSetIterator<T, HASH>::operator!=(const HashSetIterator<T, HASH>& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSetIterator<T, HASH>::HashSetIterator(const HashSet<T, HASH>* owner, Vector<Vector<T>>::const_iterator bucket)
|
||||
: m_owner(owner)
|
||||
, m_current_bucket(bucket)
|
||||
{
|
||||
if (m_current_bucket != m_owner->m_buckets.end())
|
||||
m_current_key = m_current_bucket->begin();
|
||||
find_next();
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
void HashSetIterator<T, HASH>::find_next()
|
||||
{
|
||||
ASSERT(m_owner && m_current_bucket);
|
||||
while (m_current_bucket != m_owner->m_buckets.end())
|
||||
{
|
||||
if (m_current_key && m_current_key != m_current_bucket->end())
|
||||
return;
|
||||
m_current_bucket++;
|
||||
m_current_key = m_current_bucket->begin();
|
||||
}
|
||||
m_current_key = typename Vector<T>::const_iterator();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Iterators.h>
|
||||
#include <BAN/Swap.h>
|
||||
#include <BAN/Traits.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename It, typename Comp>
|
||||
void heapify_up(It begin, size_t index, Comp comp)
|
||||
{
|
||||
size_t parent = (index - 1) / 2;
|
||||
while (parent < index)
|
||||
{
|
||||
if (comp(*(begin + index), *(begin + parent)))
|
||||
break;
|
||||
swap(*(begin + parent), *(begin + index));
|
||||
index = parent;
|
||||
parent = (index - 1) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename It, typename Comp>
|
||||
void heapify_down(It begin, size_t index, size_t len, Comp comp)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
const size_t lchild = 2 * index + 1;
|
||||
const size_t rchild = 2 * index + 2;
|
||||
|
||||
size_t child = 0;
|
||||
if (lchild < len && !comp(*(begin + lchild), *(begin + index)))
|
||||
{
|
||||
if (rchild < len && !comp(*(begin + rchild), *(begin + lchild)))
|
||||
child = rchild;
|
||||
else
|
||||
child = lchild;
|
||||
}
|
||||
else if (rchild < len && !comp(*(begin + rchild), *(begin + index)))
|
||||
child = rchild;
|
||||
else
|
||||
break;
|
||||
|
||||
swap(*(begin + child), *(begin + index));
|
||||
index = child;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename It, typename Comp = less<it_value_type_t<It>>>
|
||||
void make_heap(It begin, It end, Comp comp = {})
|
||||
{
|
||||
const size_t len = distance(begin, end);
|
||||
if (len <= 1)
|
||||
return;
|
||||
|
||||
size_t index = (len - 2) / 2;
|
||||
while (index < len)
|
||||
detail::heapify_down(begin, index--, len, comp);
|
||||
}
|
||||
|
||||
template<typename It, typename Comp = less<it_value_type_t<It>>>
|
||||
void push_heap(It begin, It end, Comp comp = {})
|
||||
{
|
||||
const size_t len = distance(begin, end);
|
||||
detail::heapify_up(begin, len - 1, comp);
|
||||
}
|
||||
|
||||
template<typename It, typename Comp = less<it_value_type_t<It>>>
|
||||
void pop_heap(It begin, It end, Comp comp = {})
|
||||
{
|
||||
const size_t len = distance(begin, end);
|
||||
swap(*begin, *(begin + len - 1));
|
||||
detail::heapify_down(begin, 0, len - 1, comp);
|
||||
}
|
||||
|
||||
template<typename It, typename Comp = less<it_value_type_t<It>>>
|
||||
void sort_heap(It begin, It end, Comp comp = {})
|
||||
{
|
||||
while (begin != end)
|
||||
pop_heap(begin, end--, comp);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Endianness.h>
|
||||
#include <BAN/Formatter.h>
|
||||
#include <BAN/Hash.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
struct IPv4Address
|
||||
{
|
||||
constexpr IPv4Address()
|
||||
: IPv4Address(0)
|
||||
{ }
|
||||
|
||||
constexpr IPv4Address(uint32_t u32_address)
|
||||
{
|
||||
raw = u32_address;
|
||||
}
|
||||
|
||||
constexpr IPv4Address(uint8_t oct1, uint8_t oct2, uint8_t oct3, uint8_t oct4)
|
||||
{
|
||||
octets[0] = oct1;
|
||||
octets[1] = oct2;
|
||||
octets[2] = oct3;
|
||||
octets[3] = oct4;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const IPv4Address& other) const
|
||||
{
|
||||
return raw == other.raw;
|
||||
}
|
||||
|
||||
constexpr IPv4Address mask(const IPv4Address& other) const
|
||||
{
|
||||
return IPv4Address(raw & other.raw);
|
||||
}
|
||||
|
||||
union
|
||||
{
|
||||
uint8_t octets[4];
|
||||
uint32_t raw;
|
||||
} __attribute__((packed));
|
||||
};
|
||||
static_assert(sizeof(IPv4Address) == 4);
|
||||
|
||||
template<>
|
||||
struct hash<IPv4Address>
|
||||
{
|
||||
constexpr hash_t operator()(IPv4Address ipv4) const
|
||||
{
|
||||
return hash<uint32_t>()(ipv4.raw);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace BAN::Formatter
|
||||
{
|
||||
|
||||
template<typename F>
|
||||
void print_argument(F putc, const IPv4Address& ipv4, const ValueFormat&)
|
||||
{
|
||||
ValueFormat format {
|
||||
.base = 10,
|
||||
.percision = 0,
|
||||
.fill = 0,
|
||||
.upper = false,
|
||||
};
|
||||
|
||||
print_argument(putc, ipv4.octets[0], format);
|
||||
for (size_t i = 1; i < 4; i++)
|
||||
{
|
||||
putc('.');
|
||||
print_argument(putc, ipv4.octets[i], format);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,4 +9,4 @@ namespace BAN
|
||||
Break
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,180 +1,92 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Assert.h>
|
||||
#include <BAN/Traits.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename It>
|
||||
constexpr It next(It it, size_t count)
|
||||
{
|
||||
for (size_t i = 0; i < count; i++)
|
||||
++it;
|
||||
return it;
|
||||
}
|
||||
|
||||
template<typename It>
|
||||
requires requires(It it, size_t n) { requires is_same_v<decltype(it + n), It>; }
|
||||
constexpr It next(It it, size_t count)
|
||||
{
|
||||
return it + count;
|
||||
}
|
||||
|
||||
template<typename It>
|
||||
constexpr It prev(It it, size_t count)
|
||||
{
|
||||
for (size_t i = 0; i < count; i++)
|
||||
--it;
|
||||
return it;
|
||||
}
|
||||
|
||||
template<typename It>
|
||||
requires requires(It it, size_t n) { requires is_same_v<decltype(it - n), It>; }
|
||||
constexpr It prev(It it, size_t count)
|
||||
{
|
||||
return it - count;
|
||||
}
|
||||
|
||||
template<typename It>
|
||||
constexpr size_t distance(It it1, It it2)
|
||||
{
|
||||
size_t dist = 0;
|
||||
while (it1 != it2)
|
||||
{
|
||||
++it1;
|
||||
++dist;
|
||||
}
|
||||
return dist;
|
||||
}
|
||||
|
||||
template<typename It>
|
||||
requires requires(It it1, It it2) { requires is_integral_v<decltype(it2 - it1)>; }
|
||||
constexpr size_t distance(It it1, It it2)
|
||||
{
|
||||
return it2 - it1;
|
||||
}
|
||||
|
||||
template<typename T, typename Container, bool CONST>
|
||||
class IteratorSimpleGeneral
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
public:
|
||||
constexpr IteratorSimpleGeneral() = default;
|
||||
IteratorSimpleGeneral() = default;
|
||||
template<bool CONST2, typename = enable_if_t<CONST2 == CONST || CONST>>
|
||||
constexpr IteratorSimpleGeneral(const IteratorSimpleGeneral<T, Container, CONST2>& other)
|
||||
IteratorSimpleGeneral(const IteratorSimpleGeneral<T, Container, CONST2>& other)
|
||||
: m_pointer(other.m_pointer)
|
||||
, m_valid(other.m_valid)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr const T& operator*() const
|
||||
const T& operator*() const
|
||||
{
|
||||
ASSERT(m_pointer);
|
||||
return *m_pointer;
|
||||
}
|
||||
template<bool CONST2 = CONST>
|
||||
constexpr enable_if_t<!CONST2, T&> operator*()
|
||||
enable_if_t<!CONST2, T&> operator*()
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_pointer);
|
||||
return *m_pointer;
|
||||
}
|
||||
|
||||
constexpr const T* operator->() const
|
||||
const T* operator->() const
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_pointer);
|
||||
return m_pointer;
|
||||
}
|
||||
template<bool CONST2 = CONST>
|
||||
constexpr enable_if_t<!CONST2, T*> operator->()
|
||||
enable_if_t<!CONST2, T*> operator->()
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_pointer);
|
||||
return m_pointer;
|
||||
}
|
||||
|
||||
constexpr IteratorSimpleGeneral& operator++()
|
||||
IteratorSimpleGeneral& operator++()
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_pointer);
|
||||
++m_pointer;
|
||||
return *this;
|
||||
}
|
||||
constexpr IteratorSimpleGeneral operator++(int)
|
||||
IteratorSimpleGeneral operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
constexpr IteratorSimpleGeneral& operator--()
|
||||
IteratorSimpleGeneral& operator--()
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_pointer);
|
||||
--m_pointer;
|
||||
return *this;
|
||||
return --m_pointer;
|
||||
}
|
||||
constexpr IteratorSimpleGeneral operator--(int)
|
||||
IteratorSimpleGeneral operator--(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
constexpr size_t operator-(const IteratorSimpleGeneral& other) const
|
||||
bool operator==(const IteratorSimpleGeneral& other) const
|
||||
{
|
||||
ASSERT(*this && other);
|
||||
return m_pointer - other.m_pointer;
|
||||
}
|
||||
|
||||
constexpr IteratorSimpleGeneral operator+(size_t offset) const
|
||||
{
|
||||
return IteratorSimpleGeneral(m_pointer + offset);
|
||||
}
|
||||
|
||||
constexpr IteratorSimpleGeneral operator-(size_t offset) const
|
||||
{
|
||||
return IteratorSimpleGeneral(m_pointer - offset);
|
||||
}
|
||||
|
||||
constexpr bool operator<(const IteratorSimpleGeneral& other) const
|
||||
{
|
||||
ASSERT(*this);
|
||||
return m_pointer < other.m_pointer;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const IteratorSimpleGeneral& other) const
|
||||
{
|
||||
ASSERT(*this);
|
||||
return m_pointer == other.m_pointer;
|
||||
}
|
||||
constexpr bool operator!=(const IteratorSimpleGeneral& other) const
|
||||
bool operator!=(const IteratorSimpleGeneral& other) const
|
||||
{
|
||||
ASSERT(*this);
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
constexpr explicit operator bool() const
|
||||
operator bool() const
|
||||
{
|
||||
return m_valid;
|
||||
return m_pointer;
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr IteratorSimpleGeneral(maybe_const_t<CONST, T>* pointer)
|
||||
IteratorSimpleGeneral(maybe_const_t<CONST, T>* pointer)
|
||||
: m_pointer(pointer)
|
||||
, m_valid(true)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
maybe_const_t<CONST, T>* m_pointer = nullptr;
|
||||
bool m_valid = false;
|
||||
|
||||
friend IteratorSimpleGeneral<T, Container, !CONST>;
|
||||
friend Container;
|
||||
@@ -190,19 +102,17 @@ namespace BAN
|
||||
using InnerIterator = either_or_t<CONST, typename Inner::const_iterator, typename Inner::iterator>;
|
||||
using OuterIterator = either_or_t<CONST, typename Outer::const_iterator, typename Outer::iterator>;
|
||||
|
||||
using value_type = T;
|
||||
|
||||
public:
|
||||
constexpr IteratorDoubleGeneral() = default;
|
||||
IteratorDoubleGeneral() = default;
|
||||
template<bool CONST2, typename = enable_if_t<CONST2 == CONST || CONST>>
|
||||
constexpr IteratorDoubleGeneral(const IteratorDoubleGeneral<T, OuterContainer, InnerContainer, Container, CONST2>& other)
|
||||
IteratorDoubleGeneral(const IteratorDoubleGeneral<T, OuterContainer, InnerContainer, Container, CONST2>& other)
|
||||
: m_outer_end(other.m_outer_end)
|
||||
, m_outer_current(other.m_outer_current)
|
||||
, m_inner_current(other.m_inner_current)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr const T& operator*() const
|
||||
const T& operator*() const
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_outer_current != m_outer_end);
|
||||
@@ -210,7 +120,7 @@ namespace BAN
|
||||
return m_inner_current.operator*();
|
||||
}
|
||||
template<bool CONST2 = CONST>
|
||||
constexpr enable_if_t<!CONST2, T&> operator*()
|
||||
enable_if_t<!CONST2, T&> operator*()
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_outer_current != m_outer_end);
|
||||
@@ -218,7 +128,7 @@ namespace BAN
|
||||
return m_inner_current.operator*();
|
||||
}
|
||||
|
||||
constexpr const T* operator->() const
|
||||
const T* operator->() const
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_outer_current != m_outer_end);
|
||||
@@ -226,7 +136,7 @@ namespace BAN
|
||||
return m_inner_current.operator->();
|
||||
}
|
||||
template<bool CONST2 = CONST>
|
||||
constexpr enable_if_t<!CONST2, T*> operator->()
|
||||
enable_if_t<!CONST2, T*> operator->()
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_outer_current != m_outer_end);
|
||||
@@ -234,7 +144,7 @@ namespace BAN
|
||||
return m_inner_current.operator->();
|
||||
}
|
||||
|
||||
constexpr IteratorDoubleGeneral& operator++()
|
||||
IteratorDoubleGeneral& operator++()
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_outer_current != m_outer_end);
|
||||
@@ -243,37 +153,37 @@ namespace BAN
|
||||
find_valid_or_end();
|
||||
return *this;
|
||||
}
|
||||
constexpr IteratorDoubleGeneral operator++(int)
|
||||
IteratorDoubleGeneral operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const IteratorDoubleGeneral& other) const
|
||||
bool operator==(const IteratorDoubleGeneral& other) const
|
||||
{
|
||||
ASSERT(*this && other);
|
||||
if (!*this || !other)
|
||||
return false;
|
||||
if (m_outer_end != other.m_outer_end)
|
||||
return false;
|
||||
if (m_outer_current != other.m_outer_current)
|
||||
return false;
|
||||
if (m_outer_current == m_outer_end)
|
||||
return true;
|
||||
ASSERT(m_inner_current && other.m_inner_current);
|
||||
return m_inner_current == other.m_inner_current;
|
||||
}
|
||||
constexpr bool operator!=(const IteratorDoubleGeneral& other) const
|
||||
bool operator!=(const IteratorDoubleGeneral& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
constexpr explicit operator bool() const
|
||||
operator bool() const
|
||||
{
|
||||
return !!m_outer_current;
|
||||
return m_outer_end && m_outer_current;
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr IteratorDoubleGeneral(const OuterIterator& outer_end, const OuterIterator& outer_current)
|
||||
IteratorDoubleGeneral(const OuterIterator& outer_end, const OuterIterator& outer_current)
|
||||
: m_outer_end(outer_end)
|
||||
, m_outer_current(outer_current)
|
||||
{
|
||||
@@ -284,15 +194,7 @@ namespace BAN
|
||||
}
|
||||
}
|
||||
|
||||
constexpr IteratorDoubleGeneral(const OuterIterator& outer_end, const OuterIterator& outer_current, const InnerIterator& inner_current)
|
||||
: m_outer_end(outer_end)
|
||||
, m_outer_current(outer_current)
|
||||
, m_inner_current(inner_current)
|
||||
{
|
||||
find_valid_or_end();
|
||||
}
|
||||
|
||||
constexpr void find_valid_or_end()
|
||||
void find_valid_or_end()
|
||||
{
|
||||
while (m_inner_current == m_outer_current->end())
|
||||
{
|
||||
@@ -303,9 +205,6 @@ namespace BAN
|
||||
}
|
||||
}
|
||||
|
||||
constexpr OuterIterator outer_current() { return m_outer_current; }
|
||||
constexpr InnerIterator inner_current() { return m_inner_current; }
|
||||
|
||||
private:
|
||||
OuterIterator m_outer_end;
|
||||
OuterIterator m_outer_current;
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Traits.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
class numeric_limits
|
||||
{
|
||||
public:
|
||||
numeric_limits() = delete;
|
||||
|
||||
static inline constexpr T max()
|
||||
{
|
||||
if constexpr(is_same_v<T, char>)
|
||||
return __SCHAR_MAX__;
|
||||
if constexpr(is_same_v<T, signed char>)
|
||||
return __SCHAR_MAX__;
|
||||
if constexpr(is_same_v<T, unsigned char>)
|
||||
return (T)__SCHAR_MAX__ * 2 + 1;
|
||||
|
||||
if constexpr(is_same_v<T, short>)
|
||||
return __SHRT_MAX__;
|
||||
if constexpr(is_same_v<T, int>)
|
||||
return __INT_MAX__;
|
||||
if constexpr(is_same_v<T, long>)
|
||||
return __LONG_MAX__;
|
||||
if constexpr(is_same_v<T, long long>)
|
||||
return __LONG_LONG_MAX__;
|
||||
|
||||
if constexpr(is_same_v<T, unsigned short>)
|
||||
return (T)__SHRT_MAX__ * 2 + 1;
|
||||
if constexpr(is_same_v<T, unsigned int>)
|
||||
return (T)__INT_MAX__ * 2 + 1;
|
||||
if constexpr(is_same_v<T, unsigned long>)
|
||||
return (T)__LONG_MAX__ * 2 + 1;
|
||||
if constexpr(is_same_v<T, unsigned long long>)
|
||||
return (T)__LONG_LONG_MAX__ * 2 + 1;
|
||||
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __FLT_MAX__;
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __DBL_MAX__;
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __LDBL_MAX__;
|
||||
}
|
||||
|
||||
static inline constexpr T min()
|
||||
{
|
||||
if constexpr(is_signed_v<T> && is_integral_v<T>)
|
||||
return -max() - 1;
|
||||
|
||||
if constexpr(is_unsigned_v<T> && is_integral_v<T>)
|
||||
return 0;
|
||||
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __FLT_MIN__;
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __DBL_MIN__;
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __LDBL_MIN__;
|
||||
}
|
||||
|
||||
static inline constexpr bool has_infinity()
|
||||
{
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __FLT_HAS_INFINITY__;
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __DBL_HAS_INFINITY__;
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __LDBL_HAS_INFINITY__;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline constexpr T infinity() requires(has_infinity())
|
||||
{
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __builtin_inff();
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __builtin_inf();
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __builtin_infl();
|
||||
}
|
||||
|
||||
static inline constexpr bool has_quiet_NaN()
|
||||
{
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __FLT_HAS_QUIET_NAN__;
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __DBL_HAS_QUIET_NAN__;
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __LDBL_HAS_QUIET_NAN__;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline constexpr T quiet_NaN() requires(has_quiet_NaN())
|
||||
{
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __builtin_nanf("");
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __builtin_nan("");
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __builtin_nanl("");
|
||||
}
|
||||
|
||||
static inline constexpr int max_exponent2()
|
||||
{
|
||||
static_assert(__FLT_RADIX__ == 2);
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __FLT_MAX_EXP__;
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __DBL_MAX_EXP__;
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __LDBL_MAX_EXP__;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline constexpr int max_exponent10()
|
||||
{
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __FLT_MAX_10_EXP__;
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __DBL_MAX_10_EXP__;
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __LDBL_MAX_10_EXP__;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline constexpr int min_exponent2()
|
||||
{
|
||||
static_assert(__FLT_RADIX__ == 2);
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __FLT_MIN_EXP__;
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __DBL_MIN_EXP__;
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __LDBL_MIN_EXP__;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline constexpr int min_exponent10()
|
||||
{
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __FLT_MIN_10_EXP__;
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __DBL_MIN_10_EXP__;
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __LDBL_MIN_10_EXP__;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/New.h>
|
||||
#include <BAN/PlacementNew.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
@@ -22,11 +21,11 @@ namespace BAN
|
||||
|
||||
public:
|
||||
LinkedList() = default;
|
||||
LinkedList(const LinkedList<T>& other) requires is_copy_constructible_v<T> { *this = other; }
|
||||
LinkedList(const LinkedList<T>& other) { *this = other; }
|
||||
LinkedList(LinkedList<T>&& other) { *this = move(other); }
|
||||
~LinkedList() { clear(); }
|
||||
|
||||
LinkedList<T>& operator=(const LinkedList<T>&) requires is_copy_constructible_v<T>;
|
||||
LinkedList<T>& operator=(const LinkedList<T>&);
|
||||
LinkedList<T>& operator=(LinkedList<T>&&);
|
||||
|
||||
ErrorOr<void> push_back(const T&);
|
||||
@@ -34,16 +33,14 @@ namespace BAN
|
||||
ErrorOr<void> insert(iterator, const T&);
|
||||
ErrorOr<void> insert(iterator, T&&);
|
||||
template<typename... Args>
|
||||
ErrorOr<void> emplace_back(Args&&...) requires is_constructible_v<T, Args...>;
|
||||
ErrorOr<void> emplace_back(Args&&...);
|
||||
template<typename... Args>
|
||||
ErrorOr<void> emplace(iterator, Args&&...) requires is_constructible_v<T, Args...>;
|
||||
ErrorOr<void> emplace(iterator, Args&&...);
|
||||
|
||||
void pop_back();
|
||||
iterator remove(iterator);
|
||||
void clear();
|
||||
|
||||
iterator move_element_to_other_linked_list(LinkedList& dest_list, iterator dest_iter, iterator src_iter);
|
||||
|
||||
iterator begin() { return iterator(m_data, empty()); }
|
||||
const_iterator begin() const { return const_iterator(m_data, empty()); }
|
||||
iterator end() { return iterator(m_last, true); }
|
||||
@@ -67,11 +64,7 @@ namespace BAN
|
||||
Node* prev;
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
ErrorOr<Node*> allocate_node(Args&&...) const;
|
||||
|
||||
Node* remove_node(iterator);
|
||||
void insert_node(iterator, Node*);
|
||||
ErrorOr<Node*> allocate_node() const;
|
||||
|
||||
Node* m_data = nullptr;
|
||||
Node* m_last = nullptr;
|
||||
@@ -122,7 +115,7 @@ namespace BAN
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
LinkedList<T>& LinkedList<T>::operator=(const LinkedList<T>& other) requires is_copy_constructible_v<T>
|
||||
LinkedList<T>& LinkedList<T>::operator=(const LinkedList<T>& other)
|
||||
{
|
||||
clear();
|
||||
for (const T& elem : other)
|
||||
@@ -143,31 +136,6 @@ namespace BAN
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
LinkedList<T>::Node* LinkedList<T>::remove_node(iterator iter)
|
||||
{
|
||||
ASSERT(!empty() && iter);
|
||||
Node* node = iter.m_current;
|
||||
Node* prev = node->prev;
|
||||
Node* next = node->next;
|
||||
(prev ? prev->next : m_data) = next;
|
||||
(next ? next->prev : m_last) = prev;
|
||||
m_size--;
|
||||
return node;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void LinkedList<T>::insert_node(iterator iter, Node* node)
|
||||
{
|
||||
Node* next = iter.m_past_end ? nullptr : iter.m_current;
|
||||
Node* prev = next ? next->prev : m_last;
|
||||
node->next = next;
|
||||
node->prev = prev;
|
||||
(prev ? prev->next : m_data) = node;
|
||||
(next ? next->prev : m_last) = node;
|
||||
m_size++;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ErrorOr<void> LinkedList<T>::push_back(const T& value)
|
||||
{
|
||||
@@ -189,31 +157,44 @@ namespace BAN
|
||||
template<typename T>
|
||||
ErrorOr<void> LinkedList<T>::insert(iterator iter, T&& value)
|
||||
{
|
||||
Node* new_node = TRY(allocate_node(move(value)));
|
||||
insert_node(iter, new_node);
|
||||
Node* next = iter.m_past_end ? nullptr : iter.m_current;
|
||||
Node* prev = next ? next->prev : m_last;
|
||||
Node* new_node = TRY(allocate_node());
|
||||
new (&new_node->value) T(move(value));
|
||||
new_node->next = next;
|
||||
new_node->prev = prev;
|
||||
(prev ? prev->next : m_data) = new_node;
|
||||
(next ? next->prev : m_last) = new_node;
|
||||
m_size++;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename... Args>
|
||||
ErrorOr<void> LinkedList<T>::emplace_back(Args&&... args) requires is_constructible_v<T, Args...>
|
||||
ErrorOr<void> LinkedList<T>::emplace_back(Args&&... args)
|
||||
{
|
||||
return emplace(end(), forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename... Args>
|
||||
ErrorOr<void> LinkedList<T>::emplace(iterator iter, Args&&... args) requires is_constructible_v<T, Args...>
|
||||
ErrorOr<void> LinkedList<T>::emplace(iterator iter, Args&&... args)
|
||||
{
|
||||
Node* new_node = TRY(allocate_node(forward<Args>(args)...));
|
||||
insert_node(iter, new_node);
|
||||
Node* next = iter.m_past_end ? nullptr : iter.m_current;
|
||||
Node* prev = next ? next->prev : m_last;
|
||||
Node* new_node = TRY(allocate_node());
|
||||
new (&new_node->value) T(forward<Args>(args)...);
|
||||
new_node->next = next;
|
||||
new_node->prev = prev;
|
||||
(prev ? prev->next : m_data) = new_node;
|
||||
(next ? next->prev : m_last) = new_node;
|
||||
m_size++;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void LinkedList<T>::pop_back()
|
||||
{
|
||||
ASSERT(!empty());
|
||||
remove(iterator(m_last, false));
|
||||
}
|
||||
|
||||
@@ -221,10 +202,14 @@ namespace BAN
|
||||
LinkedList<T>::iterator LinkedList<T>::remove(iterator iter)
|
||||
{
|
||||
ASSERT(!empty() && iter);
|
||||
Node* node = remove_node(iter);
|
||||
Node* node = iter.m_current;
|
||||
Node* prev = node->prev;
|
||||
Node* next = node->next;
|
||||
node->value.~T();
|
||||
BAN::deallocator(node);
|
||||
(prev ? prev->next : m_data) = next;
|
||||
(next ? next->prev : m_last) = prev;
|
||||
m_size--;
|
||||
return next ? iterator(next, false) : iterator(m_last, true);
|
||||
}
|
||||
|
||||
@@ -244,16 +229,6 @@ namespace BAN
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
LinkedList<T>::iterator LinkedList<T>::move_element_to_other_linked_list(LinkedList& dest_list, iterator dest_iter, iterator src_iter)
|
||||
{
|
||||
ASSERT(!empty() && src_iter);
|
||||
Node* node = remove_node(src_iter);
|
||||
iterator ret = node->next ? iterator(node->next, false) : iterator(m_last, true);
|
||||
dest_list.insert_node(dest_iter, node);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T& LinkedList<T>::back() const
|
||||
{
|
||||
@@ -308,13 +283,11 @@ namespace BAN
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename... Args>
|
||||
ErrorOr<typename LinkedList<T>::Node*> LinkedList<T>::allocate_node(Args&&... args) const
|
||||
ErrorOr<typename LinkedList<T>::Node*> LinkedList<T>::allocate_node() const
|
||||
{
|
||||
Node* node = (Node*)BAN::allocator(sizeof(Node));
|
||||
if (node == nullptr)
|
||||
return Error::from_errno(ENOMEM);
|
||||
new (&node->value) T(forward<Args>(args)...);
|
||||
return Error::from_errno(ENOMEM);
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -423,4 +396,4 @@ namespace BAN
|
||||
return m_current;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Formatter.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
struct MACAddress
|
||||
{
|
||||
uint8_t address[6];
|
||||
|
||||
constexpr bool operator==(const MACAddress& other) const
|
||||
{
|
||||
return
|
||||
address[0] == other.address[0] &&
|
||||
address[1] == other.address[1] &&
|
||||
address[2] == other.address[2] &&
|
||||
address[3] == other.address[3] &&
|
||||
address[4] == other.address[4] &&
|
||||
address[5] == other.address[5];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace BAN::Formatter
|
||||
{
|
||||
|
||||
template<typename F>
|
||||
void print_argument(F putc, const MACAddress& mac, const ValueFormat&)
|
||||
{
|
||||
ValueFormat format {
|
||||
.base = 16,
|
||||
.percision = 0,
|
||||
.fill = 2,
|
||||
.upper = true,
|
||||
};
|
||||
|
||||
print_argument(putc, mac.address[0], format);
|
||||
for (size_t i = 1; i < 6; i++)
|
||||
{
|
||||
putc(':');
|
||||
print_argument(putc, mac.address[i], format);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Limits.h>
|
||||
#include <BAN/Numbers.h>
|
||||
#include <BAN/Traits.h>
|
||||
|
||||
#include <float.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace BAN::Math
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
inline constexpr T abs(T x)
|
||||
{
|
||||
return x < 0 ? -x : x;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline constexpr T min(T a, T b)
|
||||
{
|
||||
@@ -36,11 +29,12 @@ namespace BAN::Math
|
||||
template<integral T>
|
||||
inline constexpr T gcd(T a, T b)
|
||||
{
|
||||
T t;
|
||||
while (b)
|
||||
{
|
||||
T temp = b;
|
||||
t = b;
|
||||
b = a % b;
|
||||
a = temp;
|
||||
a = t;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
@@ -58,408 +52,54 @@ namespace BAN::Math
|
||||
}
|
||||
|
||||
template<integral T>
|
||||
inline constexpr bool is_power_of_two(T x)
|
||||
inline constexpr bool is_power_of_two(T value)
|
||||
{
|
||||
if (x == 0)
|
||||
if (value == 0)
|
||||
return false;
|
||||
return (x & (x - 1)) == 0;
|
||||
}
|
||||
|
||||
template<integral T>
|
||||
__attribute__((always_inline))
|
||||
inline constexpr bool will_multiplication_overflow(T a, T b)
|
||||
{
|
||||
T dummy;
|
||||
return __builtin_mul_overflow(a, b, &dummy);
|
||||
}
|
||||
|
||||
template<integral T>
|
||||
__attribute__((always_inline))
|
||||
inline constexpr bool will_addition_overflow(T a, T b)
|
||||
{
|
||||
T dummy;
|
||||
return __builtin_add_overflow(a, b, &dummy);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
requires is_same_v<T, unsigned int> || is_same_v<T, unsigned long> || is_same_v<T, unsigned long long>
|
||||
inline constexpr T ilog2(T x)
|
||||
{
|
||||
if constexpr(is_same_v<T, unsigned int>)
|
||||
return sizeof(T) * 8 - __builtin_clz(x) - 1;
|
||||
if constexpr(is_same_v<T, unsigned long>)
|
||||
return sizeof(T) * 8 - __builtin_clzl(x) - 1;
|
||||
return sizeof(T) * 8 - __builtin_clzll(x) - 1;
|
||||
}
|
||||
|
||||
// This is ugly but my clangd does not like including
|
||||
// intrinsic headers at all
|
||||
#if !defined(__SSE__) || !defined(__SSE2__)
|
||||
#pragma GCC push_options
|
||||
#ifndef __SSE__
|
||||
#pragma GCC target("sse")
|
||||
#endif
|
||||
#ifndef __SSE2__
|
||||
#pragma GCC target("sse2")
|
||||
#endif
|
||||
#define BAN_MATH_POP_OPTIONS
|
||||
#endif
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T floor(T x)
|
||||
{
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __builtin_floorf(x);
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __builtin_floor(x);
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __builtin_floorl(x);
|
||||
return (value & (value - 1)) == 0;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T ceil(T x)
|
||||
inline constexpr T log2(T value)
|
||||
{
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __builtin_ceilf(x);
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __builtin_ceil(x);
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __builtin_ceill(x);
|
||||
T result;
|
||||
asm volatile("fyl2x" : "=t"(result) : "0"(value), "u"((T)1.0) : "st(1)");
|
||||
return result;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T round(T x)
|
||||
inline constexpr T log10(T value)
|
||||
{
|
||||
if (x == (T)0.0)
|
||||
return x;
|
||||
if (x > (T)0.0)
|
||||
return floor<T>(x + (T)0.5);
|
||||
return ceil<T>(x - (T)0.5);
|
||||
constexpr T INV_LOG_2_10 = 0.3010299956639811952137388947244930267681898814621085413104274611;
|
||||
T result;
|
||||
asm volatile("fyl2x" : "=t"(result) : "0"(value), "u"(INV_LOG_2_10) : "st(1)");
|
||||
return result;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T trunc(T x)
|
||||
inline constexpr T log(T value, T base)
|
||||
{
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __builtin_truncf(x);
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __builtin_trunc(x);
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __builtin_truncl(x);
|
||||
return log2(value) / log2(base);
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T rint(T x)
|
||||
inline constexpr T pow(T base, T exp)
|
||||
{
|
||||
asm("frndint" : "+t"(x));
|
||||
return x;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T fmod(T a, T b)
|
||||
{
|
||||
asm(
|
||||
"1:"
|
||||
"fprem;"
|
||||
"fnstsw %%ax;"
|
||||
"testb $4, %%ah;"
|
||||
"jne 1b;"
|
||||
: "+t"(a)
|
||||
: "u"(b)
|
||||
: "ax", "cc"
|
||||
);
|
||||
return a;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T remainder(T a, T b)
|
||||
{
|
||||
asm(
|
||||
"1:"
|
||||
"fprem1;"
|
||||
"fnstsw %%ax;"
|
||||
"testb $4, %%ah;"
|
||||
"jne 1b;"
|
||||
: "+t"(a)
|
||||
: "u"(b)
|
||||
: "ax", "cc"
|
||||
);
|
||||
return a;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
static T modf(T x, T* iptr)
|
||||
{
|
||||
const T frac = BAN::Math::fmod<T>(x, (T)1.0);
|
||||
*iptr = x - frac;
|
||||
return frac;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T frexp(T num, int* exp)
|
||||
{
|
||||
if (num == (T)0.0)
|
||||
{
|
||||
*exp = 0;
|
||||
return (T)0.0;
|
||||
}
|
||||
|
||||
T e;
|
||||
asm("fxtract" : "+t"(num), "=u"(e));
|
||||
*exp = (int)e + 1;
|
||||
return num / (T)2.0;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T copysign(T x, T y)
|
||||
{
|
||||
if ((x < (T)0.0) != (y < (T)0.0))
|
||||
x = -x;
|
||||
return x;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T fyl2x(T x, T y)
|
||||
{
|
||||
asm("fyl2x" : "+t"(x) : "u"(y) : "st(1)");
|
||||
return x;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T log(T x)
|
||||
{
|
||||
return detail::fyl2x<T>(x, numbers::ln2_v<T>);
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T log2(T x)
|
||||
{
|
||||
return detail::fyl2x<T>(x, 1.0);
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T log10(T x)
|
||||
{
|
||||
return detail::fyl2x<T>(x, numbers::lg2_v<T>);
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T logb(T x)
|
||||
{
|
||||
static_assert(FLT_RADIX == 2);
|
||||
return log2<T>(x);
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T exp2(T x)
|
||||
{
|
||||
if (abs(x) <= (T)1.0)
|
||||
{
|
||||
asm("f2xm1" : "+t"(x));
|
||||
return x + (T)1.0;
|
||||
}
|
||||
|
||||
asm(
|
||||
T result;
|
||||
asm volatile(
|
||||
"fyl2x;"
|
||||
"fld1;"
|
||||
"fld %%st(1);"
|
||||
"fprem;"
|
||||
"f2xm1;"
|
||||
"faddp;"
|
||||
"fscale;"
|
||||
"fstp %%st(1);"
|
||||
: "+t"(x)
|
||||
"fxch %%st(1);"
|
||||
"fstp %%st;"
|
||||
: "=t"(result)
|
||||
: "0"(base), "u"(exp)
|
||||
);
|
||||
|
||||
return x;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T exp(T x)
|
||||
{
|
||||
return exp2<T>(x * numbers::log2e_v<T>);
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T pow(T x, T y)
|
||||
{
|
||||
if (x == (T)0.0)
|
||||
return (T)0.0;
|
||||
return exp2<T>(y * log2<T>(x));
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T scalbn(T x, int n)
|
||||
{
|
||||
asm("fscale" : "+t"(x) : "u"(static_cast<T>(n)));
|
||||
return x;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T ldexp(T x, int y)
|
||||
{
|
||||
const bool exp_sign = y < 0;
|
||||
if (exp_sign)
|
||||
y = -y;
|
||||
|
||||
T exp = (T)1.0;
|
||||
T mult = (T)2.0;
|
||||
while (y)
|
||||
{
|
||||
if (y & 1)
|
||||
exp *= mult;
|
||||
mult *= mult;
|
||||
y >>= 1;
|
||||
}
|
||||
|
||||
if (exp_sign)
|
||||
exp = (T)1.0 / exp;
|
||||
|
||||
return x * exp;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T sqrt(T x)
|
||||
{
|
||||
if constexpr(BAN::is_same_v<T, float>)
|
||||
{
|
||||
using v4sf = float __attribute__((vector_size(16)));
|
||||
return __builtin_ia32_sqrtss((v4sf) { x, 0.0f, 0.0f, 0.0f })[0];
|
||||
}
|
||||
else if constexpr(BAN::is_same_v<T, double>)
|
||||
{
|
||||
using v2df = double __attribute__((vector_size(16)));
|
||||
return __builtin_ia32_sqrtsd((v2df) { x, 0.0 })[0];
|
||||
}
|
||||
else if constexpr(BAN::is_same_v<T, long double>)
|
||||
{
|
||||
asm("fsqrt" : "+t"(x));
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T cbrt(T value)
|
||||
{
|
||||
return pow<T>(value, (T)1.0 / (T)3.0);
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T sin(T x)
|
||||
{
|
||||
asm("fsin" : "+t"(x));
|
||||
return x;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T cos(T x)
|
||||
{
|
||||
asm("fcos" : "+t"(x));
|
||||
return x;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr void sincos(T x, T& sin, T& cos)
|
||||
{
|
||||
asm("fsincos" : "=t"(cos), "=u"(sin) : "0"(x));
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T tan(T x)
|
||||
{
|
||||
T one, ret;
|
||||
asm("fptan" : "=t"(one), "=u"(ret) : "0"(x));
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T atan2(T y, T x)
|
||||
{
|
||||
asm("fpatan" : "+t"(x) : "u"(y) : "st(1)");
|
||||
return x;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T atan(T x)
|
||||
{
|
||||
return atan2<T>(x, (T)1.0);
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T asin(T x)
|
||||
{
|
||||
if (x == (T)0.0)
|
||||
return (T)0.0;
|
||||
if (x == (T)1.0)
|
||||
return +numbers::pi_v<T> / (T)2.0;
|
||||
if (x == (T)-1.0)
|
||||
return -numbers::pi_v<T> / (T)2.0;
|
||||
return (T)2.0 * atan<T>(x / ((T)1.0 + sqrt<T>((T)1.0 - x * x)));
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T acos(T x)
|
||||
{
|
||||
if (x == (T)0.0)
|
||||
return numbers::pi_v<T> / (T)2.0;
|
||||
if (x == (T)1.0)
|
||||
return (T)0.0;
|
||||
if (x == (T)-1.0)
|
||||
return numbers::pi_v<T>;
|
||||
return (T)2.0 * atan<T>(sqrt<T>((T)1.0 - x * x) / ((T)1.0 + x));
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T sinh(T x)
|
||||
{
|
||||
return (exp<T>(x) - exp<T>(-x)) / (T)2.0;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T cosh(T x)
|
||||
{
|
||||
return (exp<T>(x) + exp<T>(-x)) / (T)2.0;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T tanh(T x)
|
||||
{
|
||||
const T exp_px = exp<T>(+x);
|
||||
const T exp_nx = exp<T>(-x);
|
||||
return (exp_px - exp_nx) / (exp_px + exp_nx);
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T asinh(T x)
|
||||
{
|
||||
return log<T>(x + sqrt<T>(x * x + (T)1.0));
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T acosh(T x)
|
||||
{
|
||||
return log<T>(x + sqrt<T>(x * x - (T)1.0));
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T atanh(T x)
|
||||
{
|
||||
return (T)0.5 * log<T>(((T)1.0 + x) / ((T)1.0 - x));
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T hypot(T x, T y)
|
||||
{
|
||||
return sqrt<T>(x * x + y * y);
|
||||
}
|
||||
|
||||
#ifdef BAN_MATH_POP_OPTIONS
|
||||
#undef BAN_MATH_POP_OPTIONS
|
||||
#pragma GCC pop_options
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
@@ -27,3 +27,6 @@ namespace BAN
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inline void* operator new(size_t, void* addr) { return addr; }
|
||||
inline void* operator new[](size_t, void* addr) { return addr; }
|
||||
@@ -9,12 +9,10 @@
|
||||
namespace BAN
|
||||
{
|
||||
#if defined(__is_kernel)
|
||||
static constexpr void*(*allocator)(size_t) = kmalloc;
|
||||
static constexpr void*(*reallocator)(void*, size_t) = nullptr;
|
||||
static constexpr void(*deallocator)(void*) = kfree;
|
||||
static constexpr void*(&allocator)(size_t) = kmalloc;
|
||||
static constexpr void(&deallocator)(void*) = kfree;
|
||||
#else
|
||||
static constexpr void*(*allocator)(size_t) = malloc;
|
||||
static constexpr void*(*reallocator)(void*, size_t) = realloc;
|
||||
static constexpr void(*deallocator)(void*) = free;
|
||||
static constexpr void*(&allocator)(size_t) = malloc;
|
||||
static constexpr void(&deallocator)(void*) = free;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Traits.h>
|
||||
|
||||
namespace BAN::numbers
|
||||
{
|
||||
|
||||
template<floating_point T> inline constexpr T e_v = 2.71828182845904523536;
|
||||
template<floating_point T> inline constexpr T log2e_v = 1.44269504088896340736;
|
||||
template<floating_point T> inline constexpr T lge_v = 0.43429448190325182765;
|
||||
template<floating_point T> inline constexpr T lg2_v = 0.30102999566398119521;
|
||||
template<floating_point T> inline constexpr T ln2_v = 0.69314718055994530942;
|
||||
template<floating_point T> inline constexpr T ln10_v = 2.30258509299404568402;
|
||||
template<floating_point T> inline constexpr T pi_v = 3.14159265358979323846;
|
||||
template<floating_point T> inline constexpr T sqrt2_v = 1.41421356237309504880;
|
||||
template<floating_point T> inline constexpr T sqrt3_v = 1.73205080756887729353;
|
||||
|
||||
inline constexpr double e = e_v<double>;
|
||||
inline constexpr double log2e = log2e_v<double>;
|
||||
inline constexpr double lge = lge_v<double>;
|
||||
inline constexpr double lg2 = lge_v<double>;
|
||||
inline constexpr double ln2 = ln2_v<double>;
|
||||
inline constexpr double ln10 = ln10_v<double>;
|
||||
inline constexpr double pi = pi_v<double>;
|
||||
inline constexpr double sqrt2 = sqrt2_v<double>;
|
||||
inline constexpr double sqrt3 = sqrt3_v<double>;
|
||||
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <BAN/Assert.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/PlacementNew.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -13,74 +12,64 @@ namespace BAN
|
||||
class Optional
|
||||
{
|
||||
public:
|
||||
constexpr Optional();
|
||||
constexpr Optional(Optional&&);
|
||||
constexpr Optional(const Optional&);
|
||||
constexpr Optional(const T&);
|
||||
constexpr Optional(T&&);
|
||||
Optional();
|
||||
Optional(const T&);
|
||||
Optional(T&&);
|
||||
template<typename... Args>
|
||||
Optional(Args&&...);
|
||||
|
||||
~Optional();
|
||||
|
||||
constexpr Optional& operator=(Optional&&);
|
||||
constexpr Optional& operator=(const Optional&);
|
||||
Optional& operator=(const Optional&);
|
||||
Optional& operator=(Optional&&);
|
||||
|
||||
template<typename... Args>
|
||||
constexpr Optional& emplace(Args&&...) requires is_constructible_v<T, Args...>;
|
||||
Optional& emplace(Args&&...);
|
||||
|
||||
constexpr T* operator->();
|
||||
constexpr const T* operator->() const;
|
||||
T* operator->();
|
||||
const T* operator->() const;
|
||||
|
||||
constexpr T& operator*();
|
||||
constexpr const T& operator*() const;
|
||||
T& operator*();
|
||||
const T& operator*() const;
|
||||
|
||||
constexpr bool has_value() const;
|
||||
bool has_value() const;
|
||||
|
||||
constexpr T release_value();
|
||||
constexpr T& value();
|
||||
constexpr const T& value() const;
|
||||
constexpr T& value_or(T&);
|
||||
constexpr const T& value_or(const T&) const;
|
||||
T&& release_value();
|
||||
const T& value() const;
|
||||
T& value();
|
||||
|
||||
constexpr void clear();
|
||||
void clear();
|
||||
|
||||
private:
|
||||
alignas(T) uint8_t m_storage[sizeof(T)] {};
|
||||
alignas(T) uint8_t m_storage[sizeof(T)];
|
||||
bool m_has_value { false };
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
constexpr Optional<T>::Optional()
|
||||
Optional<T>::Optional()
|
||||
: m_has_value(false)
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
constexpr Optional<T>::Optional(Optional<T>&& other)
|
||||
: m_has_value(other.has_value())
|
||||
{
|
||||
if (other.has_value())
|
||||
new (m_storage) T(move(other.release_value()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Optional<T>::Optional(const Optional<T>& other)
|
||||
: m_has_value(other.has_value())
|
||||
{
|
||||
if (other.has_value())
|
||||
new (m_storage) T(other.value());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Optional<T>::Optional(const T& value)
|
||||
Optional<T>::Optional(const T& value)
|
||||
: m_has_value(true)
|
||||
{
|
||||
new (m_storage) T(value);
|
||||
new (m_storage) T(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Optional<T>::Optional(T&& value)
|
||||
Optional<T>::Optional(T&& value)
|
||||
: m_has_value(true)
|
||||
{
|
||||
new (m_storage) T(move(value));
|
||||
new (m_storage) T(BAN::move(value));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename... Args>
|
||||
Optional<T>::Optional(Args&&... args)
|
||||
: m_has_value(true)
|
||||
{
|
||||
new (m_storage) T(BAN::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -90,115 +79,101 @@ namespace BAN
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Optional<T>& Optional<T>::operator=(Optional&& other)
|
||||
Optional<T>& Optional<T>::operator=(const Optional& other)
|
||||
{
|
||||
clear();
|
||||
m_has_value = other.has_value();
|
||||
if (other.has_value())
|
||||
new (m_storage) T(move(other.release_value()));
|
||||
{
|
||||
m_has_value = true;
|
||||
new (m_storage) T(other.value());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Optional<T>& Optional<T>::operator=(const Optional& other)
|
||||
Optional<T>& Optional<T>::operator=(Optional&& other)
|
||||
{
|
||||
clear();
|
||||
m_has_value = other.has_value();
|
||||
if (other.has_value())
|
||||
new (m_storage) T(other.value());
|
||||
{
|
||||
m_has_value = true;
|
||||
new (m_storage) T(BAN::move(other.release_value()));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename... Args>
|
||||
constexpr Optional<T>& Optional<T>::emplace(Args&&... args) requires is_constructible_v<T, Args...>
|
||||
Optional<T>& Optional<T>::emplace(Args&&... args)
|
||||
{
|
||||
clear();
|
||||
m_has_value = true;
|
||||
new (m_storage) T(forward<Args>(args)...);
|
||||
new (m_storage) T(BAN::forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T* Optional<T>::operator->()
|
||||
T* Optional<T>::operator->()
|
||||
{
|
||||
ASSERT(has_value());
|
||||
return &value();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr const T* Optional<T>::operator->() const
|
||||
const T* Optional<T>::operator->() const
|
||||
{
|
||||
ASSERT(has_value());
|
||||
return &value();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T& Optional<T>::operator*()
|
||||
T& Optional<T>::operator*()
|
||||
{
|
||||
ASSERT(has_value());
|
||||
return value();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr const T& Optional<T>::operator*() const
|
||||
const T& Optional<T>::operator*() const
|
||||
{
|
||||
ASSERT(has_value());
|
||||
return value();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr bool Optional<T>::has_value() const
|
||||
bool Optional<T>::has_value() const
|
||||
{
|
||||
return m_has_value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T Optional<T>::release_value()
|
||||
T&& Optional<T>::release_value()
|
||||
{
|
||||
ASSERT(has_value());
|
||||
T released_value = move(value());
|
||||
value().~T();
|
||||
m_has_value = false;
|
||||
return move(released_value);
|
||||
return BAN::move((T&)m_storage);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T& Optional<T>::value()
|
||||
const T& Optional<T>::value() const
|
||||
{
|
||||
ASSERT(has_value());
|
||||
return *reinterpret_cast<T*>(&m_storage);
|
||||
return (const T&)m_storage;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr const T& Optional<T>::value() const
|
||||
T& Optional<T>::value()
|
||||
{
|
||||
ASSERT(has_value());
|
||||
return *reinterpret_cast<const T*>(&m_storage);
|
||||
return (T&)m_storage;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T& Optional<T>::value_or(T& empty)
|
||||
{
|
||||
if (!has_value())
|
||||
return empty;
|
||||
return value();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr const T& Optional<T>::value_or(const T& empty) const
|
||||
{
|
||||
if (!has_value())
|
||||
return empty;
|
||||
return value();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr void Optional<T>::clear()
|
||||
void Optional<T>::clear()
|
||||
{
|
||||
if (m_has_value)
|
||||
value().~T();
|
||||
m_has_value = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#if __has_include(<new>)
|
||||
#include <new>
|
||||
#else
|
||||
#include <stddef.h>
|
||||
|
||||
inline void* operator new(size_t, void* addr) { return addr; }
|
||||
inline void* operator new[](size_t, void* addr) { return addr; }
|
||||
#endif
|
||||
@@ -1,64 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "BAN/Errors.h"
|
||||
#include <BAN/Vector.h>
|
||||
#include <BAN/Heap.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename T, typename Comp = less<T>>
|
||||
class PriorityQueue
|
||||
{
|
||||
public:
|
||||
PriorityQueue() = default;
|
||||
PriorityQueue(Comp comp)
|
||||
: m_comp(comp)
|
||||
{ }
|
||||
|
||||
ErrorOr<void> push(const T& value)
|
||||
{
|
||||
TRY(m_data.push_back(value));
|
||||
push_heap(m_data.begin(), m_data.end());
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> push(T&& value)
|
||||
{
|
||||
TRY(m_data.push_back(move(value)));
|
||||
push_heap(m_data.begin(), m_data.end());
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
ErrorOr<void> emplace(Args&&... args) requires is_constructible_v<T, Args...>
|
||||
{
|
||||
TRY(m_data.emplace_back(forward<Args>(args)...));
|
||||
push_heap(m_data.begin(), m_data.end());
|
||||
return {};
|
||||
}
|
||||
|
||||
void pop()
|
||||
{
|
||||
pop_heap(m_data.begin(), m_data.end());
|
||||
m_data.pop_back();
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> reserve(Vector<T>::size_type size)
|
||||
{
|
||||
return m_data.reserve(size);
|
||||
}
|
||||
|
||||
T& top() { return m_data.front(); }
|
||||
const T& top() const { return m_data.front(); }
|
||||
|
||||
bool empty() const { return m_data.empty(); }
|
||||
Vector<T>::size_type size() const { return m_data.size(); }
|
||||
Vector<T>::size_type capacity() const { return m_data.capacity(); }
|
||||
|
||||
private:
|
||||
Comp m_comp;
|
||||
Vector<T> m_data;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <BAN/Math.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/New.h>
|
||||
#include <BAN/PlacementNew.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
@@ -31,7 +30,7 @@ namespace BAN
|
||||
ErrorOr<void> push(T&&);
|
||||
ErrorOr<void> push(const T&);
|
||||
template<typename... Args>
|
||||
ErrorOr<void> emplace(Args&&...) requires is_constructible_v<T, Args...>;
|
||||
ErrorOr<void> emplace(Args&&...);
|
||||
|
||||
ErrorOr<void> reserve(size_type);
|
||||
ErrorOr<void> shrink_to_fit();
|
||||
@@ -45,7 +44,6 @@ namespace BAN
|
||||
void clear();
|
||||
|
||||
bool empty() const;
|
||||
size_type capacity() const;
|
||||
size_type size() const;
|
||||
|
||||
const T& front() const;
|
||||
@@ -131,7 +129,7 @@ namespace BAN
|
||||
|
||||
template<typename T>
|
||||
template<typename... Args>
|
||||
ErrorOr<void> Queue<T>::emplace(Args&&... args) requires is_constructible_v<T, Args...>
|
||||
ErrorOr<void> Queue<T>::emplace(Args&&... args)
|
||||
{
|
||||
TRY(ensure_capacity(m_size + 1));
|
||||
new (m_data + m_size) T(forward<Args>(args)...);
|
||||
@@ -187,12 +185,6 @@ namespace BAN
|
||||
return m_size == 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename Queue<T>::size_type Queue<T>::capacity() const
|
||||
{
|
||||
return m_capacity;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename Queue<T>::size_type Queue<T>::size() const
|
||||
{
|
||||
@@ -233,4 +225,4 @@ namespace BAN
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Atomic.h>
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/NoCopyMove.h>
|
||||
@@ -23,36 +22,24 @@ namespace BAN
|
||||
|
||||
void ref() const
|
||||
{
|
||||
uint32_t old = m_ref_count.fetch_add(1, MemoryOrder::memory_order_relaxed);
|
||||
ASSERT(old > 0);
|
||||
}
|
||||
|
||||
bool try_ref() const
|
||||
{
|
||||
uint32_t expected = m_ref_count.load(MemoryOrder::memory_order_relaxed);
|
||||
for (;;)
|
||||
{
|
||||
if (expected == 0)
|
||||
return false;
|
||||
if (m_ref_count.compare_exchange(expected, expected + 1, MemoryOrder::memory_order_acquire))
|
||||
return true;
|
||||
}
|
||||
ASSERT(m_ref_count > 0);
|
||||
m_ref_count++;
|
||||
}
|
||||
|
||||
void unref() const
|
||||
{
|
||||
uint32_t old = m_ref_count.fetch_sub(1);
|
||||
ASSERT(old > 0);
|
||||
if (old == 1)
|
||||
delete static_cast<const T*>(this);
|
||||
ASSERT(m_ref_count > 0);
|
||||
m_ref_count--;
|
||||
if (m_ref_count == 0)
|
||||
delete (const T*)this;
|
||||
}
|
||||
|
||||
protected:
|
||||
RefCounted() = default;
|
||||
virtual ~RefCounted() { ASSERT(m_ref_count == 0); }
|
||||
~RefCounted() { ASSERT(m_ref_count == 0); }
|
||||
|
||||
private:
|
||||
mutable Atomic<uint32_t> m_ref_count = 1;
|
||||
mutable uint32_t m_ref_count = 1;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@@ -76,9 +63,8 @@ namespace BAN
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// NOTE: don't use is_constructible_v<T, Args...> as RefPtr<T> is allowed with friends
|
||||
template<typename... Args>
|
||||
static ErrorOr<RefPtr> create(Args&&... args) requires requires(Args&&... args) { T(forward<Args>(args)...); }
|
||||
static ErrorOr<RefPtr> create(Args&&... args)
|
||||
{
|
||||
T* pointer = new T(forward<Args>(args)...);
|
||||
if (pointer == nullptr)
|
||||
@@ -138,11 +124,8 @@ namespace BAN
|
||||
T* operator->() { return ptr(); }
|
||||
const T* operator->() const { return ptr(); }
|
||||
|
||||
bool operator==(RefPtr other) const { return m_pointer == other.m_pointer; }
|
||||
bool operator!=(RefPtr other) const { return m_pointer != other.m_pointer; }
|
||||
|
||||
bool empty() const { return m_pointer == nullptr; }
|
||||
explicit operator bool() const { return m_pointer; }
|
||||
operator bool() const { return m_pointer; }
|
||||
|
||||
void clear()
|
||||
{
|
||||
|
||||
@@ -25,4 +25,4 @@ namespace BAN
|
||||
bool m_enabled { true };
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Heap.h>
|
||||
#include <BAN/Math.h>
|
||||
#include <BAN/Swap.h>
|
||||
#include <BAN/Traits.h>
|
||||
#include <BAN/Vector.h>
|
||||
|
||||
namespace BAN::sort
|
||||
{
|
||||
|
||||
template<typename It, typename Comp = less<it_value_type_t<It>>>
|
||||
void exchange_sort(It begin, It end, Comp comp = {})
|
||||
{
|
||||
for (It lhs = begin; lhs != end; ++lhs)
|
||||
for (It rhs = next(lhs, 1); rhs != end; ++rhs)
|
||||
if (!comp(*lhs, *rhs))
|
||||
swap(*lhs, *rhs);
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename It, typename Comp>
|
||||
It partition(It begin, It end, Comp comp)
|
||||
{
|
||||
It pivot = prev(end, 1);
|
||||
|
||||
It it1 = begin;
|
||||
for (It it2 = begin; it2 != pivot; ++it2)
|
||||
{
|
||||
if (comp(*it2, *pivot))
|
||||
{
|
||||
swap(*it1, *it2);
|
||||
++it1;
|
||||
}
|
||||
}
|
||||
|
||||
swap(*it1, *pivot);
|
||||
|
||||
return it1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename It, typename Comp = less<it_value_type_t<It>>>
|
||||
void quick_sort(It begin, It end, Comp comp = {})
|
||||
{
|
||||
if (distance(begin, end) <= 1)
|
||||
return;
|
||||
It mid = detail::partition(begin, end, comp);
|
||||
quick_sort(begin, mid, comp);
|
||||
quick_sort(++mid, end, comp);
|
||||
}
|
||||
|
||||
template<typename It, typename Comp = less<it_value_type_t<It>>>
|
||||
void insertion_sort(It begin, It end, Comp comp = {})
|
||||
{
|
||||
if (distance(begin, end) <= 1)
|
||||
return;
|
||||
for (It it1 = next(begin, 1); it1 != end; ++it1)
|
||||
{
|
||||
auto x = move(*it1);
|
||||
It it2 = it1;
|
||||
for (; it2 != begin && comp(x, *prev(it2, 1)); --it2)
|
||||
*it2 = move(*prev(it2, 1));
|
||||
*it2 = move(x);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename It, typename Comp = less<it_value_type_t<It>>>
|
||||
void heap_sort(It begin, It end, Comp comp = {})
|
||||
{
|
||||
make_heap(begin, end, comp);
|
||||
sort_heap(begin, end, comp);
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename It, typename Comp>
|
||||
void intro_sort_impl(It begin, It end, size_t max_depth, Comp comp)
|
||||
{
|
||||
if (distance(begin, end) <= 16)
|
||||
return insertion_sort(begin, end, comp);
|
||||
if (max_depth == 0)
|
||||
return heap_sort(begin, end, comp);
|
||||
It mid = detail::partition(begin, end, comp);
|
||||
intro_sort_impl(begin, mid, max_depth - 1, comp);
|
||||
intro_sort_impl(++mid, end, max_depth - 1, comp);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename It, typename Comp = less<it_value_type_t<It>>>
|
||||
void intro_sort(It begin, It end, Comp comp = {})
|
||||
{
|
||||
const size_t len = distance(begin, end);
|
||||
if (len <= 1)
|
||||
return;
|
||||
detail::intro_sort_impl(begin, end, 2 * Math::ilog2(len), comp);
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<unsigned_integral T>
|
||||
consteval T lsb_index(T value)
|
||||
{
|
||||
for (T result = 0;; result++)
|
||||
if (value & (1 << result))
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename It, size_t radix = 256>
|
||||
requires is_unsigned_v<it_value_type_t<It>> && (radix > 0 && (radix & (radix - 1)) == 0)
|
||||
BAN::ErrorOr<void> radix_sort(It begin, It end)
|
||||
{
|
||||
using value_type = it_value_type_t<It>;
|
||||
|
||||
const size_t len = distance(begin, end);
|
||||
if (len <= 1)
|
||||
return {};
|
||||
|
||||
Vector<value_type> temp;
|
||||
TRY(temp.resize(len));
|
||||
|
||||
Vector<size_t> counts;
|
||||
TRY(counts.resize(radix));
|
||||
|
||||
constexpr size_t mask = radix - 1;
|
||||
constexpr size_t shift = detail::lsb_index(radix);
|
||||
|
||||
for (size_t s = 0; s < sizeof(value_type) * 8; s += shift)
|
||||
{
|
||||
for (auto& cnt : counts)
|
||||
cnt = 0;
|
||||
for (It it = begin; it != end; ++it)
|
||||
counts[(*it >> s) & mask]++;
|
||||
|
||||
for (size_t i = 0; i < radix - 1; i++)
|
||||
counts[i + 1] += counts[i];
|
||||
|
||||
for (It it = end; it != begin;)
|
||||
{
|
||||
--it;
|
||||
temp[--counts[(*it >> s) & mask]] = *it;
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < temp.size(); j++)
|
||||
*next(begin, j) = temp[j];
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename It, typename Comp = less<it_value_type_t<It>>>
|
||||
void sort(It begin, It end, Comp comp = {})
|
||||
{
|
||||
return intro_sort(begin, end, comp);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,89 +14,119 @@ namespace BAN
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using iterator = IteratorSimple<value_type, Span>;
|
||||
using const_iterator = ConstIteratorSimple<value_type, Span>;
|
||||
|
||||
private:
|
||||
template<typename S>
|
||||
static inline constexpr bool can_init_from_v = is_same_v<value_type, const S> || is_same_v<value_type, S>;
|
||||
using iterator = IteratorSimple<T, Span>;
|
||||
using const_iterator = ConstIteratorSimple<T, Span>;
|
||||
|
||||
public:
|
||||
Span() = default;
|
||||
Span(value_type* data, size_type size)
|
||||
: m_data(data)
|
||||
, m_size(size)
|
||||
{ }
|
||||
|
||||
Span(T*, size_type);
|
||||
Span(Span<T>&);
|
||||
template<typename S>
|
||||
Span(const Span<S>& other) requires can_init_from_v<S>
|
||||
: m_data(other.m_data)
|
||||
, m_size(other.m_size)
|
||||
{ }
|
||||
template<typename S>
|
||||
Span(Span<S>&& other) requires can_init_from_v<S>
|
||||
: m_data(other.m_data)
|
||||
, m_size(other.m_size)
|
||||
{
|
||||
other.clear();
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
Span& operator=(const Span<S>& other) requires can_init_from_v<S>
|
||||
{
|
||||
m_data = other.m_data;
|
||||
m_size = other.m_size;
|
||||
return *this;
|
||||
}
|
||||
template<typename S>
|
||||
Span& operator=(Span<S>&& other) requires can_init_from_v<S>
|
||||
{
|
||||
m_data = other.m_data;
|
||||
m_size = other.m_size;
|
||||
return *this;
|
||||
}
|
||||
requires(is_same_v<T, const S>)
|
||||
Span(const Span<S>&);
|
||||
|
||||
iterator begin() { return iterator(m_data); }
|
||||
iterator end() { return iterator(m_data + m_size); }
|
||||
const_iterator begin() const { return const_iterator(m_data); }
|
||||
const_iterator end() const { return const_iterator(m_data + m_size); }
|
||||
|
||||
value_type& operator[](size_type index) const
|
||||
{
|
||||
ASSERT(index < m_size);
|
||||
return m_data[index];
|
||||
}
|
||||
T& operator[](size_type);
|
||||
const T& operator[](size_type) const;
|
||||
|
||||
value_type* data() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
T* data();
|
||||
const T* data() const;
|
||||
|
||||
bool empty() const { return m_size == 0; }
|
||||
size_type size() const { return m_size; }
|
||||
bool empty() const;
|
||||
size_type size() const;
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_data = nullptr;
|
||||
m_size = 0;
|
||||
}
|
||||
void clear();
|
||||
|
||||
Span slice(size_type start, size_type length = ~size_type(0)) const
|
||||
{
|
||||
ASSERT(start <= m_size);
|
||||
if (length == ~size_type(0))
|
||||
length = m_size - start;
|
||||
ASSERT(m_size - start >= length);
|
||||
return Span(m_data + start, length);
|
||||
}
|
||||
|
||||
Span<const value_type> as_const() const { return *this; }
|
||||
Span slice(size_type, size_type = ~size_type(0));
|
||||
|
||||
private:
|
||||
value_type* m_data = nullptr;
|
||||
T* m_data = nullptr;
|
||||
size_type m_size = 0;
|
||||
|
||||
friend class Span<const value_type>;
|
||||
};
|
||||
|
||||
}
|
||||
template<typename T>
|
||||
Span<T>::Span(T* data, size_type size)
|
||||
: m_data(data)
|
||||
, m_size(size)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Span<T>::Span(Span& other)
|
||||
: m_data(other.data())
|
||||
, m_size(other.size())
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename S>
|
||||
requires(is_same_v<T, const S>)
|
||||
Span<T>::Span(const Span<S>& other)
|
||||
: m_data(other.data())
|
||||
, m_size(other.size())
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T& Span<T>::operator[](size_type index)
|
||||
{
|
||||
ASSERT(m_data);
|
||||
ASSERT(index < m_size);
|
||||
return m_data[index];
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T& Span<T>::operator[](size_type index) const
|
||||
{
|
||||
ASSERT(m_data);
|
||||
ASSERT(index < m_size);
|
||||
return m_data[index];
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* Span<T>::data()
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T* Span<T>::data() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool Span<T>::empty() const
|
||||
{
|
||||
return m_size == 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename Span<T>::size_type Span<T>::size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Span<T>::clear()
|
||||
{
|
||||
m_data = nullptr;
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Span<T> Span<T>::slice(size_type start, size_type length)
|
||||
{
|
||||
ASSERT(m_data);
|
||||
ASSERT(start <= m_size);
|
||||
if (length == ~size_type(0))
|
||||
length = m_size - start;
|
||||
ASSERT(start + length <= m_size);
|
||||
return Span(m_data + start, m_size - start - length);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/ForwardList.h>
|
||||
#include <BAN/Formatter.h>
|
||||
#include <BAN/Hash.h>
|
||||
#include <BAN/Iterators.h>
|
||||
#include <BAN/New.h>
|
||||
#include <BAN/StringView.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
@@ -14,320 +13,85 @@ namespace BAN
|
||||
{
|
||||
public:
|
||||
using size_type = size_t;
|
||||
using value_type = char;
|
||||
using iterator = IteratorSimple<char, String>;
|
||||
using const_iterator = ConstIteratorSimple<char, String>;
|
||||
static constexpr size_type sso_capacity = 15;
|
||||
|
||||
public:
|
||||
String() {}
|
||||
String(const String& other) { *this = other; }
|
||||
String(String&& other) { *this = move(other); }
|
||||
String(StringView other) { *this = other; }
|
||||
~String() { clear(); }
|
||||
String();
|
||||
String(const String&);
|
||||
String(String&&);
|
||||
String(StringView);
|
||||
~String();
|
||||
|
||||
template<typename... Args>
|
||||
static BAN::ErrorOr<String> formatted(const char* format, Args&&... args)
|
||||
{
|
||||
size_type length = 0;
|
||||
BAN::Formatter::print([&](char) { length++; }, format, BAN::forward<Args>(args)...);
|
||||
static String formatted(const char* format, const Args&... args);
|
||||
|
||||
String result;
|
||||
TRY(result.reserve(length));
|
||||
BAN::Formatter::print([&](char c){ MUST(result.push_back(c)); }, format, BAN::forward<Args>(args)...);
|
||||
String& operator=(const String&);
|
||||
String& operator=(String&&);
|
||||
String& operator=(StringView);
|
||||
|
||||
return result;
|
||||
}
|
||||
ErrorOr<void> push_back(char);
|
||||
ErrorOr<void> insert(char, size_type);
|
||||
ErrorOr<void> insert(StringView, size_type);
|
||||
ErrorOr<void> append(StringView);
|
||||
ErrorOr<void> append(const String&);
|
||||
|
||||
String& operator=(const String& other)
|
||||
{
|
||||
clear();
|
||||
MUST(ensure_capacity(other.size()));
|
||||
memcpy(data(), other.data(), other.size() + 1);
|
||||
m_size = other.size();
|
||||
return *this;
|
||||
}
|
||||
void pop_back();
|
||||
void remove(size_type);
|
||||
void erase(size_type, size_type);
|
||||
|
||||
String& operator=(String&& other)
|
||||
{
|
||||
clear();
|
||||
void clear();
|
||||
|
||||
if (other.has_sso())
|
||||
memcpy(data(), other.data(), other.size() + 1);
|
||||
else
|
||||
{
|
||||
m_storage.general_storage = other.m_storage.general_storage;
|
||||
m_has_sso = false;
|
||||
}
|
||||
m_size = other.m_size;
|
||||
const_iterator begin() const { return const_iterator(m_data); }
|
||||
iterator begin() { return iterator(m_data); }
|
||||
const_iterator end() const { return const_iterator(m_data + m_size); }
|
||||
iterator end() { return iterator(m_data + m_size); }
|
||||
|
||||
other.m_size = 0;
|
||||
other.m_storage.sso_storage = SSOStorage();
|
||||
other.m_has_sso = true;
|
||||
char front() const { ASSERT(!empty()); return m_data[0]; }
|
||||
char& front() { ASSERT(!empty()); return m_data[0]; }
|
||||
|
||||
return *this;
|
||||
}
|
||||
char back() const { ASSERT(!empty()); return m_data[m_size - 1]; }
|
||||
char& back() { ASSERT(!empty()); return m_data[m_size - 1]; }
|
||||
|
||||
String& operator=(StringView other)
|
||||
{
|
||||
clear();
|
||||
MUST(ensure_capacity(other.size()));
|
||||
memcpy(data(), other.data(), other.size());
|
||||
m_size = other.size();
|
||||
data()[m_size] = '\0';
|
||||
return *this;
|
||||
}
|
||||
char operator[](size_type) const;
|
||||
char& operator[](size_type);
|
||||
|
||||
ErrorOr<void> push_back(char c)
|
||||
{
|
||||
TRY(ensure_capacity(m_size + 1));
|
||||
data()[m_size] = c;
|
||||
m_size++;
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
bool operator==(const String&) const;
|
||||
bool operator==(StringView) const;
|
||||
bool operator==(const char*) const;
|
||||
|
||||
ErrorOr<void> insert(char c, size_type index)
|
||||
{
|
||||
ASSERT(index <= m_size);
|
||||
TRY(ensure_capacity(m_size + 1));
|
||||
memmove(data() + index + 1, data() + index, m_size - index);
|
||||
data()[index] = c;
|
||||
m_size++;
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
ErrorOr<void> resize(size_type, char = '\0');
|
||||
ErrorOr<void> reserve(size_type);
|
||||
ErrorOr<void> shrink_to_fit();
|
||||
|
||||
ErrorOr<void> insert(StringView str, size_type index)
|
||||
{
|
||||
ASSERT(index <= m_size);
|
||||
TRY(ensure_capacity(m_size + str.size()));
|
||||
memmove(data() + index + str.size(), data() + index, m_size - index);
|
||||
memcpy(data() + index, str.data(), str.size());
|
||||
m_size += str.size();
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
StringView sv() const;
|
||||
|
||||
ErrorOr<void> append(StringView str)
|
||||
{
|
||||
TRY(ensure_capacity(m_size + str.size()));
|
||||
memcpy(data() + m_size, str.data(), str.size());
|
||||
m_size += str.size();
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
bool empty() const;
|
||||
size_type size() const;
|
||||
size_type capacity() const;
|
||||
|
||||
void pop_back()
|
||||
{
|
||||
ASSERT(m_size > 0);
|
||||
m_size--;
|
||||
data()[m_size] = '\0';
|
||||
}
|
||||
|
||||
void remove(size_type index)
|
||||
{
|
||||
ASSERT(index < m_size);
|
||||
memmove(data() + index, data() + index + 1, m_size - index);
|
||||
m_size--;
|
||||
data()[m_size] = '\0';
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
if (!has_sso())
|
||||
{
|
||||
deallocator(m_storage.general_storage.data);
|
||||
m_storage.sso_storage = SSOStorage();
|
||||
m_has_sso = true;
|
||||
}
|
||||
m_size = 0;
|
||||
data()[m_size] = '\0';
|
||||
}
|
||||
|
||||
const_iterator begin() const { return const_iterator(data()); }
|
||||
iterator begin() { return iterator(data()); }
|
||||
const_iterator end() const { return const_iterator(data() + size()); }
|
||||
iterator end() { return iterator(data() + size()); }
|
||||
|
||||
char front() const { ASSERT(m_size > 0); return data()[0]; }
|
||||
char& front() { ASSERT(m_size > 0); return data()[0]; }
|
||||
|
||||
char back() const { ASSERT(m_size > 0); return data()[m_size - 1]; }
|
||||
char& back() { ASSERT(m_size > 0); return data()[m_size - 1]; }
|
||||
|
||||
char operator[](size_type index) const { ASSERT(index < m_size); return data()[index]; }
|
||||
char& operator[](size_type index) { ASSERT(index < m_size); return data()[index]; }
|
||||
|
||||
bool operator==(const String& str) const
|
||||
{
|
||||
if (size() != str.size())
|
||||
return false;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (data()[i] != str.data()[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator==(StringView str) const
|
||||
{
|
||||
if (size() != str.size())
|
||||
return false;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (data()[i] != str.data()[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator==(const char* cstr) const
|
||||
{
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (data()[i] != cstr[i])
|
||||
return false;
|
||||
if (cstr[size()] != '\0')
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
ErrorOr<void> resize(size_type new_size, char init_c = '\0')
|
||||
{
|
||||
if (m_size == new_size)
|
||||
return {};
|
||||
|
||||
// expanding
|
||||
if (m_size < new_size)
|
||||
{
|
||||
TRY(ensure_capacity(new_size));
|
||||
memset(data() + m_size, init_c, new_size - m_size);
|
||||
m_size = new_size;
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
m_size = new_size;
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> reserve(size_type new_size)
|
||||
{
|
||||
TRY(ensure_capacity(new_size));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> shrink_to_fit()
|
||||
{
|
||||
if (has_sso())
|
||||
return {};
|
||||
|
||||
if (fits_in_sso())
|
||||
{
|
||||
char* data = m_storage.general_storage.data;
|
||||
m_storage.sso_storage = SSOStorage();
|
||||
m_has_sso = true;
|
||||
memcpy(this->data(), data, m_size + 1);
|
||||
deallocator(data);
|
||||
return {};
|
||||
}
|
||||
|
||||
GeneralStorage& storage = m_storage.general_storage;
|
||||
if (storage.capacity == m_size)
|
||||
return {};
|
||||
|
||||
char* new_data = (char*)allocator(m_size + 1);
|
||||
if (new_data == nullptr)
|
||||
return Error::from_errno(ENOMEM);
|
||||
|
||||
memcpy(new_data, storage.data, m_size);
|
||||
deallocator(storage.data);
|
||||
|
||||
storage.capacity = m_size;
|
||||
storage.data = new_data;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
StringView sv() const { return StringView(data(), size()); }
|
||||
|
||||
bool empty() const { return m_size == 0; }
|
||||
size_type size() const { return m_size; }
|
||||
|
||||
size_type capacity() const
|
||||
{
|
||||
if (has_sso())
|
||||
return sso_capacity;
|
||||
return m_storage.general_storage.capacity;
|
||||
}
|
||||
|
||||
char* data()
|
||||
{
|
||||
if (has_sso())
|
||||
return m_storage.sso_storage.data;
|
||||
return m_storage.general_storage.data;
|
||||
}
|
||||
|
||||
const char* data() const
|
||||
{
|
||||
if (has_sso())
|
||||
return m_storage.sso_storage.data;
|
||||
return m_storage.general_storage.data;
|
||||
}
|
||||
const char* data() const;
|
||||
|
||||
private:
|
||||
ErrorOr<void> ensure_capacity(size_type new_size)
|
||||
{
|
||||
if (m_size >= new_size)
|
||||
return {};
|
||||
if (has_sso() && fits_in_sso(new_size))
|
||||
return {};
|
||||
ErrorOr<void> ensure_capacity(size_type);
|
||||
|
||||
char* new_data = (char*)allocator(new_size + 1);
|
||||
if (new_data == nullptr)
|
||||
return Error::from_errno(ENOMEM);
|
||||
|
||||
if (m_size)
|
||||
memcpy(new_data, data(), m_size + 1);
|
||||
|
||||
if (has_sso())
|
||||
{
|
||||
m_storage.general_storage = GeneralStorage();
|
||||
m_has_sso = false;
|
||||
}
|
||||
else
|
||||
deallocator(m_storage.general_storage.data);
|
||||
|
||||
auto& storage = m_storage.general_storage;
|
||||
storage.capacity = new_size;
|
||||
storage.data = new_data;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool has_sso() const { return m_has_sso; }
|
||||
|
||||
bool fits_in_sso() const { return fits_in_sso(m_size); }
|
||||
static bool fits_in_sso(size_type size) { return size < sso_capacity; }
|
||||
ErrorOr<void> copy_impl(StringView);
|
||||
void move_impl(String&&);
|
||||
|
||||
private:
|
||||
struct SSOStorage
|
||||
{
|
||||
char data[sso_capacity + 1] {};
|
||||
};
|
||||
struct GeneralStorage
|
||||
{
|
||||
size_type capacity { 0 };
|
||||
char* data { nullptr };
|
||||
};
|
||||
|
||||
private:
|
||||
union {
|
||||
SSOStorage sso_storage;
|
||||
GeneralStorage general_storage;
|
||||
} m_storage { .sso_storage = SSOStorage() };
|
||||
size_type m_size : sizeof(size_type) * 8 - 1 { 0 };
|
||||
size_type m_has_sso : 1 { true };
|
||||
char* m_data = nullptr;
|
||||
size_type m_capacity = 0;
|
||||
size_type m_size = 0;
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
String String::formatted(const char* format, const Args&... args)
|
||||
{
|
||||
String result;
|
||||
BAN::Formatter::print([&](char c){ MUST(result.push_back(c)); }, format, args...);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<>
|
||||
struct hash<String>
|
||||
{
|
||||
@@ -353,9 +117,10 @@ namespace BAN::Formatter
|
||||
{
|
||||
|
||||
template<typename F>
|
||||
void print_argument(F putc, const String& string, const ValueFormat& format)
|
||||
void print_argument(F putc, const String& string, const ValueFormat&)
|
||||
{
|
||||
print_argument(putc, string.sv(), format);
|
||||
for (String::size_type i = 0; i < string.size(); i++)
|
||||
putc(string[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Formatter.h>
|
||||
#include <BAN/ForwardList.h>
|
||||
#include <BAN/Hash.h>
|
||||
#include <BAN/Formatter.h>
|
||||
#include <BAN/Iterators.h>
|
||||
#include <BAN/Optional.h>
|
||||
#include <BAN/Vector.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
@@ -14,245 +11,55 @@ namespace BAN
|
||||
{
|
||||
public:
|
||||
using size_type = size_t;
|
||||
using value_type = char;
|
||||
using const_iterator = ConstIteratorSimple<char, StringView>;
|
||||
|
||||
public:
|
||||
constexpr StringView() {}
|
||||
constexpr StringView(const char* string, size_type len = -1)
|
||||
{
|
||||
if (len == size_type(-1))
|
||||
for (len = 0; string[len];)
|
||||
len++;
|
||||
m_data = string;
|
||||
m_size = len;
|
||||
}
|
||||
StringView();
|
||||
StringView(const String&);
|
||||
StringView(const char*, size_type = -1);
|
||||
|
||||
constexpr const_iterator begin() const { return const_iterator(m_data); }
|
||||
constexpr const_iterator end() const { return const_iterator(m_data + m_size); }
|
||||
const_iterator begin() const { return const_iterator(m_data); }
|
||||
const_iterator end() const { return const_iterator(m_data + m_size); }
|
||||
|
||||
constexpr char operator[](size_type index) const
|
||||
{
|
||||
ASSERT(index < m_size);
|
||||
return m_data[index];
|
||||
}
|
||||
char operator[](size_type) const;
|
||||
|
||||
constexpr bool operator==(StringView other) const
|
||||
{
|
||||
if (m_size != other.m_size)
|
||||
return false;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (m_data[i] != other.m_data[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator==(const String&) const;
|
||||
bool operator==(StringView) const;
|
||||
bool operator==(const char*) const;
|
||||
|
||||
constexpr bool operator==(const char* other) const
|
||||
{
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (m_data[i] != other[i])
|
||||
return false;
|
||||
return other[m_size] == '\0';
|
||||
}
|
||||
StringView substring(size_type, size_type = -1) const;
|
||||
|
||||
constexpr StringView substring(size_type index, size_type len = -1) const
|
||||
{
|
||||
ASSERT(index <= m_size);
|
||||
if (len == size_type(-1))
|
||||
len = m_size - index;
|
||||
ASSERT(len <= m_size - index); // weird order to avoid overflow
|
||||
StringView result;
|
||||
result.m_data = m_data + index;
|
||||
result.m_size = len;
|
||||
return result;
|
||||
}
|
||||
ErrorOr<Vector<StringView>> split(char, bool = false);
|
||||
ErrorOr<Vector<StringView>> split(bool(*comp)(char), bool = false);
|
||||
|
||||
ErrorOr<Vector<StringView>> split(char delim, bool allow_empties = false) const
|
||||
{
|
||||
size_type count = 0;
|
||||
{
|
||||
size_type start = 0;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
{
|
||||
if (m_data[i] == delim)
|
||||
{
|
||||
if (allow_empties || start != i)
|
||||
count++;
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
if (start != m_size)
|
||||
count++;
|
||||
}
|
||||
char back() const;
|
||||
char front() const;
|
||||
|
||||
Vector<StringView> result;
|
||||
TRY(result.reserve(count));
|
||||
bool contains(char) const;
|
||||
size_type count(char) const;
|
||||
|
||||
size_type start = 0;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
{
|
||||
if (m_data[i] == delim)
|
||||
{
|
||||
if (allow_empties || start != i)
|
||||
TRY(result.push_back(this->substring(start, i - start)));
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
if (start < m_size || (start == m_size && allow_empties))
|
||||
TRY(result.push_back(this->substring(start)));
|
||||
return result;
|
||||
}
|
||||
|
||||
ErrorOr<Vector<StringView>> split(bool(*comp)(char), bool allow_empties = false) const
|
||||
{
|
||||
size_type count = 0;
|
||||
{
|
||||
size_type start = 0;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
{
|
||||
if (comp(m_data[i]))
|
||||
{
|
||||
if (allow_empties || start != i)
|
||||
count++;
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
if (start != m_size)
|
||||
count++;
|
||||
}
|
||||
|
||||
Vector<StringView> result;
|
||||
TRY(result.reserve(count));
|
||||
|
||||
size_type start = 0;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
{
|
||||
if (comp(m_data[i]))
|
||||
{
|
||||
if (allow_empties || start != i)
|
||||
TRY(result.push_back(this->substring(start, i - start)));
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
if (start < m_size || (start == m_size && allow_empties))
|
||||
TRY(result.push_back(this->substring(start)));
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr char back() const
|
||||
{
|
||||
ASSERT(m_size > 0);
|
||||
return m_data[m_size - 1];
|
||||
}
|
||||
|
||||
constexpr char front() const
|
||||
{
|
||||
ASSERT(m_size > 0);
|
||||
return m_data[0];
|
||||
}
|
||||
|
||||
BAN::Optional<size_type> find(char ch) const
|
||||
{
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (m_data[i] == ch)
|
||||
return i;
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::Optional<size_type> find(bool(*comp)(char)) const
|
||||
{
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (comp(m_data[i]))
|
||||
return i;
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::Optional<size_type> rfind(char ch) const
|
||||
{
|
||||
for (size_type i = m_size; i > 0; i--)
|
||||
if (m_data[i - 1] == ch)
|
||||
return i - 1;
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::Optional<size_type> rfind(bool(*comp)(char)) const
|
||||
{
|
||||
for (size_type i = m_size; i > 0; i--)
|
||||
if (comp(m_data[i - 1]))
|
||||
return i - 1;
|
||||
return {};
|
||||
}
|
||||
|
||||
constexpr bool starts_with(BAN::StringView target) const
|
||||
{
|
||||
if (target.size() > m_size)
|
||||
return false;
|
||||
for (size_type i = 0; i < target.size(); i++)
|
||||
if (m_data[i] != target[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool contains(char ch) const
|
||||
{
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (m_data[i] == ch)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr size_type count(char ch) const
|
||||
{
|
||||
size_type result = 0;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (m_data[i] == ch)
|
||||
result++;
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr bool empty() const { return m_size == 0; }
|
||||
constexpr size_type size() const { return m_size; }
|
||||
constexpr const char* data() const { return m_data; }
|
||||
bool empty() const;
|
||||
size_type size() const;
|
||||
|
||||
const char* data() const;
|
||||
|
||||
private:
|
||||
const char* m_data = nullptr;
|
||||
size_type m_size = 0;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<StringView>
|
||||
{
|
||||
hash_t operator()(StringView string) const
|
||||
{
|
||||
constexpr hash_t FNV_offset_basis = 0x811c9dc5;
|
||||
constexpr hash_t FNV_prime = 0x01000193;
|
||||
|
||||
hash_t hash = FNV_offset_basis;
|
||||
for (StringView::size_type i = 0; i < string.size(); i++)
|
||||
{
|
||||
hash *= FNV_prime;
|
||||
hash ^= (uint8_t)string[i];
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
inline constexpr BAN::StringView operator""_sv(const char* str, BAN::StringView::size_type len) { return BAN::StringView(str, len); }
|
||||
inline BAN::StringView operator""sv(const char* str, BAN::StringView::size_type len) { return BAN::StringView(str, len); }
|
||||
|
||||
namespace BAN::Formatter
|
||||
{
|
||||
|
||||
template<typename F>
|
||||
void print_argument(F putc, const StringView& sv, const ValueFormat& format)
|
||||
void print_argument(F putc, const StringView& sv, const ValueFormat&)
|
||||
{
|
||||
for (StringView::size_type i = 0; i < sv.size(); i++)
|
||||
putc(sv[i]);
|
||||
for (int i = sv.size(); i < format.fill; i++)
|
||||
putc(' ');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Move.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
void swap(T& lhs, T& rhs)
|
||||
{
|
||||
T tmp = move(lhs);
|
||||
lhs = move(rhs);
|
||||
rhs = move(tmp);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,4 +34,4 @@ namespace BAN::Formatter
|
||||
print(putc, "{} {} {} {2}:{2}:{2} GMT+0 {4}", week_days[time.week_day], months[time.month], time.day, time.hour, time.minute, time.second, time.year);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,10 @@
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename T> struct remove_reference { using type = T; };
|
||||
template<typename T> struct remove_reference<T&> { using type = T; };
|
||||
template<typename T> struct remove_reference<T&&> { using type = T; };
|
||||
template<typename T> using remove_reference_t = typename remove_reference<T>::type;
|
||||
template<typename T> struct remove_refenrece { using type = T; };
|
||||
template<typename T> struct remove_refenrece<T&> { using type = T; };
|
||||
template<typename T> struct remove_refenrece<T&&> { using type = T; };
|
||||
template<typename T> using remove_reference_t = typename remove_refenrece<T>::type;
|
||||
|
||||
template<typename T> struct remove_const { using type = T; };
|
||||
template<typename T> struct remove_const<const T> { using type = T; };
|
||||
@@ -34,36 +34,18 @@ namespace BAN
|
||||
template<typename T1, typename T2> struct either_or<true, T1, T2> { using type = T1; };
|
||||
template<bool B, typename T1, typename T2> using either_or_t = typename either_or<B, T1, T2>::type;
|
||||
|
||||
template<typename T, T V> struct integral_constant { static constexpr T value = V; };
|
||||
template<typename T, T V > inline constexpr T integral_constant_v = integral_constant<T, V>::value;
|
||||
using true_type = integral_constant<bool, true>;
|
||||
using false_type = integral_constant<bool, false>;
|
||||
struct true_type { static constexpr bool value = true; };
|
||||
struct false_type { static constexpr bool value = false; };
|
||||
|
||||
template<typename T, typename S> struct is_same : false_type {};
|
||||
template<typename T> struct is_same<T, T> : true_type {};
|
||||
template<typename T, typename S> inline constexpr bool is_same_v = is_same<T, S>::value;
|
||||
template<typename T, typename S> concept same_as = BAN::is_same_v<T, S>;
|
||||
|
||||
template<typename T> struct is_lvalue_reference : false_type {};
|
||||
template<typename T> struct is_lvalue_reference<T&> : true_type {};
|
||||
template<typename T> inline constexpr bool is_lvalue_reference_v = is_lvalue_reference<T>::value;
|
||||
template<typename T> concept lvalue_reference = is_lvalue_reference_v<T>;
|
||||
|
||||
template<typename T, typename... Args> struct is_constructible { static constexpr bool value = __is_constructible(T, Args...); };
|
||||
template<typename T, typename... Args> inline constexpr bool is_constructible_v = is_constructible<T, Args...>::value;
|
||||
|
||||
template<typename T> struct is_default_constructible { static constexpr bool value = is_constructible_v<T>; };
|
||||
template<typename T> inline constexpr bool is_default_constructible_v = is_default_constructible<T>::value;
|
||||
|
||||
template<typename T> struct is_copy_constructible { static constexpr bool value = is_constructible_v<T, const T&>; };
|
||||
template<typename T> inline constexpr bool is_copy_constructible_v = is_copy_constructible<T>::value;
|
||||
|
||||
template<typename T> struct is_move_constructible { static constexpr bool value = is_constructible_v<T, T&&>; };
|
||||
template<typename T> inline constexpr bool is_move_constructible_v = is_move_constructible<T>::value;
|
||||
|
||||
template<typename T> struct is_trivially_copyable { static constexpr bool value = __is_trivially_copyable(T); };
|
||||
template<typename T> inline constexpr bool is_trivially_copyable_v = is_trivially_copyable<T>::value;
|
||||
|
||||
template<typename T> struct is_integral { static constexpr bool value = requires (T t, T* p, void (*f)(T)) { reinterpret_cast<T>(t); f(0); p + t; }; };
|
||||
template<typename T> inline constexpr bool is_integral_v = is_integral<T>::value;
|
||||
template<typename T> concept integral = is_integral_v<T>;
|
||||
@@ -90,64 +72,16 @@ namespace BAN
|
||||
template<typename T> struct is_arithmetic { static constexpr bool value = is_integral_v<T> || is_floating_point_v<T>; };
|
||||
template<typename T> inline constexpr bool is_arithmetic_v = is_arithmetic<T>::value;
|
||||
|
||||
template<typename Base, typename Derived> struct is_base_of { static constexpr bool value = __is_base_of(Base, Derived); };
|
||||
template<typename Base, typename Derived> inline constexpr bool is_base_of_v = is_base_of<Base, Derived>::value;
|
||||
|
||||
template<typename T> struct is_pod { static constexpr bool value = __is_pod(T); };
|
||||
template<typename T> inline constexpr bool is_pod_v = is_pod<T>::value;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template<typename T, bool = is_arithmetic_v<T>> struct is_signed { static constexpr bool value = T(-1) < T(0); };
|
||||
template<typename T> struct is_signed<T, false> : false_type {};
|
||||
|
||||
template<typename T, bool = is_arithmetic_v<T>> struct is_unsigned { static constexpr bool value = T(0) < T(-1); };
|
||||
template<typename T> struct is_unsigned<T, false> : false_type {};
|
||||
}
|
||||
template<typename T> struct is_signed : detail::is_signed<T> {};
|
||||
template<typename T> inline constexpr bool is_signed_v = is_signed<T>::value;
|
||||
template<typename T> concept signed_integral = is_signed_v<T> && is_integral_v<T>;
|
||||
|
||||
template<typename T> struct is_unsigned : detail::is_unsigned<T> {};
|
||||
template<typename T> inline constexpr bool is_unsigned_v = is_unsigned<T>::value;
|
||||
template<typename T> concept unsigned_integral = is_unsigned_v<T> && is_integral_v<T>;
|
||||
|
||||
#define __BAN_TRAITS_MAKE_UNSIGNED_CV(__type) \
|
||||
template<> struct make_unsigned<__type> { using type = unsigned __type; }; \
|
||||
template<> struct make_unsigned<const __type> { using type = unsigned const __type; }; \
|
||||
template<> struct make_unsigned<volatile __type> { using type = unsigned volatile __type; }; \
|
||||
template<> struct make_unsigned<const volatile __type> { using type = unsigned const volatile __type; };
|
||||
|
||||
template<typename T> requires is_arithmetic_v<T> struct make_unsigned { using type = T; };
|
||||
__BAN_TRAITS_MAKE_UNSIGNED_CV(char)
|
||||
__BAN_TRAITS_MAKE_UNSIGNED_CV(short)
|
||||
__BAN_TRAITS_MAKE_UNSIGNED_CV(int)
|
||||
__BAN_TRAITS_MAKE_UNSIGNED_CV(long)
|
||||
__BAN_TRAITS_MAKE_UNSIGNED_CV(long long)
|
||||
template<typename T> using make_unsigned_t = typename make_unsigned<T>::type;
|
||||
#undef __BAN_TRAITS_MAKE_UNSIGNED_CV
|
||||
|
||||
#define __BAN_TRAITS_MAKE_SIGNED_CV(__type) \
|
||||
template<> struct make_signed<unsigned __type> { using type = __type; }; \
|
||||
template<> struct make_signed<unsigned const __type> { using type = const __type; }; \
|
||||
template<> struct make_signed<unsigned volatile __type> { using type = volatile __type; }; \
|
||||
template<> struct make_signed<unsigned const volatile __type> { using type = const volatile __type; };
|
||||
|
||||
template<typename T> requires is_arithmetic_v<T> struct make_signed { using type = T; };
|
||||
__BAN_TRAITS_MAKE_SIGNED_CV(char)
|
||||
__BAN_TRAITS_MAKE_SIGNED_CV(short)
|
||||
__BAN_TRAITS_MAKE_SIGNED_CV(int)
|
||||
__BAN_TRAITS_MAKE_SIGNED_CV(long)
|
||||
__BAN_TRAITS_MAKE_SIGNED_CV(long long)
|
||||
template<typename T> using make_signed_t = typename make_signed<T>::type;
|
||||
#undef __BAN_TRAITS_MAKE_SIGNED_CV
|
||||
|
||||
template<typename T> struct it_value_type { using value_type = T::value_type; };
|
||||
template<typename T> struct it_value_type<T*> { using value_type = T; };
|
||||
template<typename T> using it_value_type_t = typename it_value_type<T>::value_type;
|
||||
|
||||
template<typename T> struct less { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; } };
|
||||
template<typename T> struct equal { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs == rhs; } };
|
||||
template<typename T> struct greater { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs > rhs; } };
|
||||
|
||||
}
|
||||
}
|
||||
@@ -18,24 +18,23 @@ namespace BAN::UTF8
|
||||
return 3;
|
||||
if ((first_byte & 0xF8) == 0xF0)
|
||||
return 4;
|
||||
return UTF8::invalid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename T> requires (sizeof(T) == 1)
|
||||
constexpr uint32_t to_codepoint(const T* bytes)
|
||||
constexpr uint32_t to_codepoint(uint8_t* bytes)
|
||||
{
|
||||
uint32_t length = byte_length(bytes[0]);
|
||||
|
||||
for (uint32_t i = 1; i < length; i++)
|
||||
if (((uint8_t)bytes[i] & 0xC0) != 0x80)
|
||||
if ((bytes[i] & 0xC0) != 0x80)
|
||||
return UTF8::invalid;
|
||||
|
||||
|
||||
switch (length)
|
||||
{
|
||||
case 1: return (((uint8_t)bytes[0] & 0x80) != 0x00) ? UTF8::invalid : (uint8_t)bytes[0];
|
||||
case 2: return (((uint8_t)bytes[0] & 0xE0) != 0xC0) ? UTF8::invalid : (((uint8_t)bytes[0] & 0x1F) << 6) | ((uint8_t)bytes[1] & 0x3F);
|
||||
case 3: return (((uint8_t)bytes[0] & 0xF0) != 0xE0) ? UTF8::invalid : (((uint8_t)bytes[0] & 0x0F) << 12) | (((uint8_t)bytes[1] & 0x3F) << 6) | ((uint8_t)bytes[2] & 0x3F);
|
||||
case 4: return (((uint8_t)bytes[0] & 0xF8) != 0xF0) ? UTF8::invalid : (((uint8_t)bytes[0] & 0x07) << 18) | (((uint8_t)bytes[1] & 0x3F) << 12) | (((uint8_t)bytes[2] & 0x3F) << 6) | ((uint8_t)bytes[3] & 0x3F);
|
||||
case 1: return ((bytes[0] & 0x80) != 0x00) ? UTF8::invalid : bytes[0];
|
||||
case 2: return ((bytes[0] & 0xE0) != 0xC0) ? UTF8::invalid : ((bytes[0] & 0x1F) << 6) | (bytes[1] & 0x3F);
|
||||
case 3: return ((bytes[0] & 0xF0) != 0xE0) ? UTF8::invalid : ((bytes[0] & 0x0F) << 12) | ((bytes[1] & 0x3F) << 6) | (bytes[2] & 0x3F);
|
||||
case 4: return ((bytes[0] & 0xF8) != 0xF0) ? UTF8::invalid : ((bytes[0] & 0x07) << 18) | ((bytes[1] & 0x3F) << 12) | ((bytes[2] & 0x3F) << 6) | (bytes[3] & 0x3F);
|
||||
}
|
||||
|
||||
return UTF8::invalid;
|
||||
@@ -76,9 +75,7 @@ namespace BAN::UTF8
|
||||
}
|
||||
}
|
||||
|
||||
*ptr = '\0';
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -33,9 +33,8 @@ namespace BAN
|
||||
return uniq;
|
||||
}
|
||||
|
||||
// NOTE: don't use is_constructible_v<T, Args...> as UniqPtr<T> is allowed with friends
|
||||
template<typename... Args>
|
||||
static BAN::ErrorOr<UniqPtr> create(Args&&... args) requires requires(Args&&... args) { T(forward<Args>(args)...); }
|
||||
static BAN::ErrorOr<UniqPtr> create(Args&&... args)
|
||||
{
|
||||
UniqPtr uniq;
|
||||
uniq.m_pointer = new T(BAN::forward<Args>(args)...);
|
||||
@@ -68,7 +67,7 @@ namespace BAN
|
||||
T* operator->()
|
||||
{
|
||||
ASSERT(m_pointer);
|
||||
return m_pointer;
|
||||
return m_pointer;
|
||||
}
|
||||
|
||||
const T* operator->() const
|
||||
@@ -96,4 +95,4 @@ namespace BAN
|
||||
friend class UniqPtr;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <BAN/Assert.h>
|
||||
#include <BAN/Math.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/PlacementNew.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@@ -43,10 +42,9 @@ namespace BAN
|
||||
void destruct(size_t index, uint8_t* data)
|
||||
{
|
||||
if (index == 0)
|
||||
{
|
||||
if constexpr(!is_lvalue_reference_v<T>)
|
||||
reinterpret_cast<T*>(data)->~T();
|
||||
}
|
||||
else;
|
||||
else if constexpr(sizeof...(Ts) > 0)
|
||||
destruct<Ts...>(index - 1, data);
|
||||
else
|
||||
@@ -126,30 +124,28 @@ namespace BAN
|
||||
Variant(Variant&& other)
|
||||
: m_index(other.m_index)
|
||||
{
|
||||
if (other.has_value())
|
||||
detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage);
|
||||
detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage);
|
||||
other.clear();
|
||||
}
|
||||
|
||||
Variant(const Variant& other)
|
||||
: m_index(other.m_index)
|
||||
{
|
||||
if (other.has_value())
|
||||
detail::copy_construct<Ts...>(other.m_index, other.m_storage, m_storage);
|
||||
detail::copy_construct<Ts...>(other.m_index, other.m_storage, m_storage);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Variant(T&& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||
: m_index(detail::index<T, Ts...>())
|
||||
{
|
||||
new (m_storage) T(move(value));
|
||||
new (m_storage) T(move(value));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Variant(const T& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||
: m_index(detail::index<T, Ts...>())
|
||||
{
|
||||
new (m_storage) T(value);
|
||||
new (m_storage) T(value);
|
||||
}
|
||||
|
||||
~Variant()
|
||||
@@ -159,13 +155,12 @@ namespace BAN
|
||||
|
||||
Variant& operator=(Variant&& other)
|
||||
{
|
||||
if (m_index == other.m_index && m_index != invalid_index())
|
||||
if (m_index == other.m_index)
|
||||
detail::move_assign<Ts...>(m_index, other.m_storage, m_storage);
|
||||
else
|
||||
{
|
||||
clear();
|
||||
if (other.has_value())
|
||||
detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage);
|
||||
detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage);
|
||||
m_index = other.m_index;
|
||||
}
|
||||
other.clear();
|
||||
@@ -174,13 +169,12 @@ namespace BAN
|
||||
|
||||
Variant& operator=(const Variant& other)
|
||||
{
|
||||
if (m_index == other.m_index && m_index != invalid_index())
|
||||
if (m_index == other.m_index)
|
||||
detail::copy_assign<Ts...>(m_index, other.m_storage, m_storage);
|
||||
else
|
||||
{
|
||||
clear();
|
||||
if (other.has_value())
|
||||
detail::copy_construct<Ts...>(other.m_index, other.m_storage, m_storage);
|
||||
detail::copy_construct<Ts...>(other.m_index, other.m_storage, m_storage);
|
||||
m_index = other.m_index;
|
||||
}
|
||||
return *this;
|
||||
@@ -220,14 +214,6 @@ namespace BAN
|
||||
return m_index == detail::index<T, Ts...>();
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
void emplace(Args&&... args) requires (can_have<T>() && is_constructible_v<T, Args...>)
|
||||
{
|
||||
clear();
|
||||
m_index = detail::index<T, Ts...>();
|
||||
new (m_storage) T(BAN::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void set(T&& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||
{
|
||||
@@ -290,16 +276,6 @@ namespace BAN
|
||||
return **reinterpret_cast<const remove_reference_t<T>**>(m_storage);
|
||||
}
|
||||
|
||||
bool has_value() const
|
||||
{
|
||||
return m_index != invalid_index();
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return has_value();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
if (m_index != invalid_index())
|
||||
@@ -314,4 +290,4 @@ namespace BAN
|
||||
size_t m_index { invalid_index() };
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,7 @@
|
||||
#include <BAN/Math.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/New.h>
|
||||
#include <BAN/PlacementNew.h>
|
||||
#include <BAN/Span.h>
|
||||
#include <BAN/Swap.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
@@ -35,12 +33,12 @@ namespace BAN
|
||||
ErrorOr<void> push_back(T&&);
|
||||
ErrorOr<void> push_back(const T&);
|
||||
template<typename... Args>
|
||||
ErrorOr<void> emplace_back(Args&&...) requires is_constructible_v<T, Args...>;
|
||||
ErrorOr<void> emplace_back(Args&&...);
|
||||
template<typename... Args>
|
||||
ErrorOr<void> emplace(size_type, Args&&...) requires is_constructible_v<T, Args...>;
|
||||
ErrorOr<void> emplace(size_type, Args&&...);
|
||||
ErrorOr<void> insert(size_type, T&&);
|
||||
ErrorOr<void> insert(size_type, const T&);
|
||||
|
||||
|
||||
iterator begin() { return iterator(m_data); }
|
||||
iterator end() { return iterator(m_data + m_size); }
|
||||
const_iterator begin() const { return const_iterator(m_data); }
|
||||
@@ -56,7 +54,7 @@ namespace BAN
|
||||
bool contains(const T&) const;
|
||||
|
||||
Span<T> span() { return Span(m_data, m_size); }
|
||||
Span<const T> span() const { return Span(m_data, m_size); }
|
||||
const Span<T> span() const { return Span(m_data, m_size); }
|
||||
|
||||
const T& operator[](size_type) const;
|
||||
T& operator[](size_type);
|
||||
@@ -66,10 +64,7 @@ namespace BAN
|
||||
const T& front() const;
|
||||
T& front();
|
||||
|
||||
void reverse();
|
||||
|
||||
ErrorOr<void> resize(size_type) requires is_default_constructible_v<T>;
|
||||
ErrorOr<void> resize(size_type, const T&) requires is_copy_constructible_v<T>;
|
||||
ErrorOr<void> resize(size_type, const T& = T());
|
||||
ErrorOr<void> reserve(size_type);
|
||||
ErrorOr<void> shrink_to_fit();
|
||||
|
||||
@@ -141,13 +136,10 @@ namespace BAN
|
||||
template<typename T>
|
||||
Vector<T>& Vector<T>::operator=(const Vector<T>& other)
|
||||
{
|
||||
clear();
|
||||
MUST(ensure_capacity(other.size()));
|
||||
for (size_type i = 0; i < BAN::Math::min(size(), other.size()); i++)
|
||||
m_data[i] = other.m_data[i];
|
||||
for (size_type i = size(); i < other.size(); i++)
|
||||
for (size_type i = 0; i < other.size(); i++)
|
||||
new (m_data + i) T(other[i]);
|
||||
for (size_type i = other.size(); i < size(); i++)
|
||||
m_data[i].~T();
|
||||
m_size = other.m_size;
|
||||
return *this;
|
||||
}
|
||||
@@ -169,7 +161,7 @@ namespace BAN
|
||||
|
||||
template<typename T>
|
||||
template<typename... Args>
|
||||
ErrorOr<void> Vector<T>::emplace_back(Args&&... args) requires is_constructible_v<T, Args...>
|
||||
ErrorOr<void> Vector<T>::emplace_back(Args&&... args)
|
||||
{
|
||||
TRY(ensure_capacity(m_size + 1));
|
||||
new (m_data + m_size) T(forward<Args>(args)...);
|
||||
@@ -179,7 +171,7 @@ namespace BAN
|
||||
|
||||
template<typename T>
|
||||
template<typename... Args>
|
||||
ErrorOr<void> Vector<T>::emplace(size_type index, Args&&... args) requires is_constructible_v<T, Args...>
|
||||
ErrorOr<void> Vector<T>::emplace(size_type index, Args&&... args)
|
||||
{
|
||||
ASSERT(index <= m_size);
|
||||
TRY(ensure_capacity(m_size + 1));
|
||||
@@ -305,28 +297,7 @@ namespace BAN
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Vector<T>::reverse()
|
||||
{
|
||||
for (size_type i = 0; i < m_size / 2; i++)
|
||||
BAN::swap(m_data[i], m_data[m_size - i - 1]);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ErrorOr<void> Vector<T>::resize(size_type size) requires is_default_constructible_v<T>
|
||||
{
|
||||
TRY(ensure_capacity(size));
|
||||
if (size < m_size)
|
||||
for (size_type i = size; i < m_size; i++)
|
||||
m_data[i].~T();
|
||||
if (size > m_size)
|
||||
for (size_type i = m_size; i < size; i++)
|
||||
new (m_data + i) T();
|
||||
m_size = size;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ErrorOr<void> Vector<T>::resize(size_type size, const T& value) requires is_copy_constructible_v<T>
|
||||
ErrorOr<void> Vector<T>::resize(size_type size, const T& value)
|
||||
{
|
||||
TRY(ensure_capacity(size));
|
||||
if (size < m_size)
|
||||
@@ -381,46 +352,19 @@ namespace BAN
|
||||
template<typename T>
|
||||
ErrorOr<void> Vector<T>::ensure_capacity(size_type size)
|
||||
{
|
||||
static_assert(alignof(T) <= alignof(max_align_t), "over aligned types not supported");
|
||||
|
||||
if (m_capacity >= size)
|
||||
return {};
|
||||
|
||||
const size_type new_cap = BAN::Math::max<size_type>(size, m_capacity * 2);
|
||||
|
||||
if constexpr (BAN::is_trivially_copyable_v<T>)
|
||||
size_type new_cap = BAN::Math::max<size_type>(size, m_capacity * 2);
|
||||
T* new_data = (T*)BAN::allocator(new_cap * sizeof(T));
|
||||
if (new_data == nullptr)
|
||||
return Error::from_errno(ENOMEM);
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
{
|
||||
if constexpr (BAN::reallocator)
|
||||
{
|
||||
auto* new_data = static_cast<T*>(BAN::reallocator(m_data, new_cap * sizeof(T)));
|
||||
if (new_data == nullptr)
|
||||
return Error::from_errno(ENOMEM);
|
||||
m_data = new_data;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* new_data = static_cast<T*>(BAN::allocator(new_cap * sizeof(T)));
|
||||
if (new_data == nullptr)
|
||||
return Error::from_errno(ENOMEM);
|
||||
memcpy(new_data, m_data, m_size * sizeof(T));
|
||||
BAN::deallocator(m_data);
|
||||
m_data = new_data;
|
||||
}
|
||||
new (new_data + i) T(move(m_data[i]));
|
||||
m_data[i].~T();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* new_data = static_cast<T*>(BAN::allocator(new_cap * sizeof(T)));
|
||||
if (new_data == nullptr)
|
||||
return Error::from_errno(ENOMEM);
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
{
|
||||
new (new_data + i) T(move(m_data[i]));
|
||||
m_data[i].~T();
|
||||
}
|
||||
BAN::deallocator(m_data);
|
||||
m_data = new_data;
|
||||
}
|
||||
|
||||
BAN::deallocator(m_data);
|
||||
m_data = new_data;
|
||||
m_capacity = new_cap;
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
|
||||
#include <BAN/RefPtr.h>
|
||||
|
||||
#if __is_kernel
|
||||
#include <kernel/Lock/SpinLock.h>
|
||||
#endif
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
@@ -15,37 +11,22 @@ namespace BAN
|
||||
template<typename T>
|
||||
class WeakPtr;
|
||||
|
||||
// FIXME: Write this without using locks...
|
||||
template<typename T>
|
||||
class WeakLink : public RefCounted<WeakLink<T>>
|
||||
{
|
||||
public:
|
||||
RefPtr<T> try_lock() const
|
||||
{
|
||||
#if __is_kernel
|
||||
Kernel::SpinLockGuard _(m_weak_lock);
|
||||
#endif
|
||||
if (m_ptr && m_ptr->try_ref())
|
||||
return RefPtr<T>::adopt(m_ptr);
|
||||
return nullptr;
|
||||
}
|
||||
RefPtr<T> lock() { ASSERT(m_ptr); return raw_ptr(); }
|
||||
T* raw_ptr() { return m_ptr; }
|
||||
|
||||
bool valid() const { return m_ptr; }
|
||||
void invalidate()
|
||||
{
|
||||
#if __is_kernel
|
||||
Kernel::SpinLockGuard _(m_weak_lock);
|
||||
#endif
|
||||
m_ptr = nullptr;
|
||||
}
|
||||
void invalidate() { m_ptr = nullptr; }
|
||||
|
||||
private:
|
||||
WeakLink(T* ptr) : m_ptr(ptr) {}
|
||||
|
||||
private:
|
||||
T* m_ptr;
|
||||
#if __is_kernel
|
||||
mutable Kernel::SpinLock m_weak_lock;
|
||||
#endif
|
||||
|
||||
friend class RefPtr<WeakLink<T>>;
|
||||
};
|
||||
|
||||
@@ -53,7 +34,7 @@ namespace BAN
|
||||
class Weakable
|
||||
{
|
||||
public:
|
||||
virtual ~Weakable()
|
||||
~Weakable()
|
||||
{
|
||||
if (m_link)
|
||||
m_link->invalidate();
|
||||
@@ -99,10 +80,10 @@ namespace BAN
|
||||
return *this;
|
||||
}
|
||||
|
||||
RefPtr<T> lock() const
|
||||
RefPtr<T> lock()
|
||||
{
|
||||
if (m_link)
|
||||
return m_link->try_lock();
|
||||
if (m_link->valid())
|
||||
return m_link->lock();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -110,8 +91,6 @@ namespace BAN
|
||||
|
||||
bool valid() const { return m_link && m_link->valid(); }
|
||||
|
||||
explicit operator bool() const { return valid(); }
|
||||
|
||||
private:
|
||||
WeakPtr(const RefPtr<WeakLink<T>>& link)
|
||||
: m_link(link)
|
||||
@@ -125,4 +104,4 @@ namespace BAN
|
||||
friend class Weakable<T>;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
154
CMakeLists.txt
154
CMakeLists.txt
@@ -1,54 +1,118 @@
|
||||
cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "banan-os")
|
||||
message(FATAL_ERROR "CMAKE_SYSTEM_NAME is not banan-os")
|
||||
endif ()
|
||||
if(DEFINED ENV{BANAN_ARCH})
|
||||
set(BANAN_ARCH $ENV{BANAN_ARCH})
|
||||
else()
|
||||
set(BANAN_ARCH x86_64)
|
||||
endif()
|
||||
|
||||
project(banan-os CXX C ASM)
|
||||
set(TOOLCHAIN_PREFIX ${CMAKE_SOURCE_DIR}/toolchain/local)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}/bin/${BANAN_ARCH}-banan_os-g++)
|
||||
set(CMAKE_CXX_COMPILER_WORKS True)
|
||||
|
||||
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}/bin/${BANAN_ARCH}-banan_os-gcc)
|
||||
set(CMAKE_C_COMPILER_WORKS True)
|
||||
|
||||
if(NOT EXISTS ${CMAKE_CXX_COMPILER})
|
||||
set(CMAKE_CXX_COMPILER g++)
|
||||
endif()
|
||||
|
||||
if(DEFINED QEMU_ACCEL)
|
||||
set(QEMU_ACCEL -accel ${QEMU_ACCEL})
|
||||
endif()
|
||||
|
||||
add_compile_options(-mno-sse -mno-sse2)
|
||||
add_compile_definitions(__enable_sse=0)
|
||||
|
||||
project(banan-os CXX)
|
||||
|
||||
set(BANAN_BASE_SYSROOT ${CMAKE_SOURCE_DIR}/base-sysroot.tar.gz)
|
||||
set(BANAN_SYSROOT ${CMAKE_BINARY_DIR}/sysroot)
|
||||
set(BANAN_INCLUDE ${BANAN_SYSROOT}/usr/include)
|
||||
set(BANAN_LIB ${BANAN_SYSROOT}/usr/lib)
|
||||
set(BANAN_BIN ${BANAN_SYSROOT}/usr/bin)
|
||||
set(BANAN_ETC ${BANAN_SYSROOT}/usr/etc)
|
||||
set(BANAN_SHARE ${BANAN_SYSROOT}/usr/share)
|
||||
set(BANAN_BOOT ${BANAN_SYSROOT}/boot)
|
||||
|
||||
set(CMAKE_INSTALL_BINDIR ${BANAN_BIN})
|
||||
set(CMAKE_INSTALL_SBINDIR ${BANAN_BIN})
|
||||
set(CMAKE_INSTALL_LIBDIR ${BANAN_LIB})
|
||||
set(CMAKE_INSTALL_INCLUDEDIR ${BANAN_INCLUDE})
|
||||
set(CMAKE_INSTALL_SYSCONF ${BANAN_ETC})
|
||||
set(CMAKE_INSTALL_MESSAGE NEVER)
|
||||
set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY True)
|
||||
|
||||
set(CMAKE_STATIC_LIBRARY_PREFIX "")
|
||||
set(CMAKE_SHARED_LIBRARY_PREFIX "")
|
||||
set(BUILD_SHARED_LIBS True)
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
# include headers of ${library} to ${target}
|
||||
function(banan_include_headers target library)
|
||||
target_include_directories(${target} PUBLIC $<TARGET_PROPERTY:${library},SOURCE_DIR>/include)
|
||||
endfunction()
|
||||
|
||||
# include headers and link ${library} to ${target}
|
||||
function(banan_link_library target library)
|
||||
target_link_libraries(${target} PUBLIC ${library})
|
||||
banan_include_headers(${target} ${library})
|
||||
endfunction()
|
||||
|
||||
# add install step for all header files of target
|
||||
function(banan_install_headers target)
|
||||
file(GLOB_RECURSE headers RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/include *.h)
|
||||
foreach(header ${headers})
|
||||
get_filename_component(subdirectory ${header} DIRECTORY)
|
||||
install(FILES include/${header} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${subdirectory})
|
||||
endforeach()
|
||||
target_include_directories(${target} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
endfunction()
|
||||
set(BANAN_LIB ${BANAN_SYSROOT}/usr/lib)
|
||||
set(BANAN_BIN ${BANAN_SYSROOT}/usr/bin)
|
||||
set(BANAN_BOOT ${BANAN_SYSROOT}/boot)
|
||||
set(DISK_IMAGE_PATH ${CMAKE_BINARY_DIR}/banan-os.img)
|
||||
|
||||
add_subdirectory(kernel)
|
||||
add_subdirectory(bootloader)
|
||||
add_subdirectory(BAN)
|
||||
add_subdirectory(libc)
|
||||
add_subdirectory(LibELF)
|
||||
add_subdirectory(userspace)
|
||||
|
||||
add_custom_target(sysroot
|
||||
COMMAND mkdir -p ${BANAN_SYSROOT}
|
||||
COMMAND cd ${BANAN_SYSROOT} && sudo tar xf ${BANAN_BASE_SYSROOT}
|
||||
USES_TERMINAL
|
||||
)
|
||||
|
||||
add_custom_target(headers
|
||||
DEPENDS kernel-headers
|
||||
DEPENDS ban-headers
|
||||
DEPENDS libc-headers
|
||||
DEPENDS libelf-headers
|
||||
)
|
||||
|
||||
add_custom_target(toolchain
|
||||
COMMAND ${CMAKE_COMMAND} -E env SYSROOT="${BANAN_SYSROOT}" PREFIX="${TOOLCHAIN_PREFIX}" ARCH="${BANAN_ARCH}" ${CMAKE_SOURCE_DIR}/toolchain/build.sh
|
||||
DEPENDS headers
|
||||
USES_TERMINAL
|
||||
)
|
||||
|
||||
add_custom_target(libstdc++
|
||||
COMMAND ${CMAKE_COMMAND} -E env LIBSTDCPP="1" ${CMAKE_SOURCE_DIR}/toolchain/build.sh
|
||||
DEPENDS libc-install
|
||||
USES_TERMINAL
|
||||
)
|
||||
|
||||
add_custom_target(image
|
||||
COMMAND ${CMAKE_COMMAND} -E env SYSROOT="${BANAN_SYSROOT}" DISK_IMAGE_PATH="${DISK_IMAGE_PATH}" ${CMAKE_SOURCE_DIR}/image.sh
|
||||
DEPENDS kernel-install
|
||||
DEPENDS ban-install
|
||||
DEPENDS libc-install
|
||||
DEPENDS userspace-install
|
||||
DEPENDS libelf-install
|
||||
USES_TERMINAL
|
||||
)
|
||||
|
||||
add_custom_target(image-full
|
||||
COMMAND ${CMAKE_COMMAND} -E env SYSROOT="${BANAN_SYSROOT}" DISK_IMAGE_PATH="${DISK_IMAGE_PATH}" ${CMAKE_SOURCE_DIR}/image-full.sh
|
||||
DEPENDS kernel-install
|
||||
DEPENDS ban-install
|
||||
DEPENDS libc-install
|
||||
DEPENDS userspace-install
|
||||
DEPENDS libelf-install
|
||||
USES_TERMINAL
|
||||
)
|
||||
|
||||
add_custom_target(check-fs
|
||||
COMMAND ${CMAKE_COMMAND} -E env DISK_IMAGE_PATH="${DISK_IMAGE_PATH}" ${CMAKE_SOURCE_DIR}/check-fs.sh
|
||||
USES_TERMINAL
|
||||
)
|
||||
|
||||
add_custom_target(qemu
|
||||
COMMAND ${CMAKE_COMMAND} -E env BANAN_ARCH="${BANAN_ARCH}" DISK_IMAGE_PATH="${DISK_IMAGE_PATH}" ${CMAKE_SOURCE_DIR}/qemu.sh -serial stdio ${QEMU_ACCEL}
|
||||
DEPENDS image
|
||||
USES_TERMINAL
|
||||
)
|
||||
|
||||
add_custom_target(qemu-nographic
|
||||
COMMAND ${CMAKE_COMMAND} -E env BANAN_ARCH="${BANAN_ARCH}" DISK_IMAGE_PATH="${DISK_IMAGE_PATH}" ${CMAKE_SOURCE_DIR}/qemu.sh -nographic ${QEMU_ACCEL}
|
||||
DEPENDS image
|
||||
USES_TERMINAL
|
||||
)
|
||||
|
||||
add_custom_target(qemu-debug
|
||||
COMMAND ${CMAKE_COMMAND} -E env BANAN_ARCH="${BANAN_ARCH}" DISK_IMAGE_PATH="${DISK_IMAGE_PATH}" ${CMAKE_SOURCE_DIR}/qemu.sh -serial stdio -d int -no-reboot
|
||||
DEPENDS image
|
||||
USES_TERMINAL
|
||||
)
|
||||
|
||||
add_custom_target(bochs
|
||||
COMMAND ${CMAKE_COMMAND} -E env DISK_IMAGE_PATH="${DISK_IMAGE_PATH}" ${CMAKE_SOURCE_DIR}/bochs.sh
|
||||
DEPENDS image
|
||||
USES_TERMINAL
|
||||
)
|
||||
|
||||
13
LibELF/CMakeLists.txt
Normal file
13
LibELF/CMakeLists.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
project(LibELF CXX)
|
||||
|
||||
add_custom_target(libelf-headers
|
||||
COMMAND sudo rsync -a ${CMAKE_CURRENT_SOURCE_DIR}/include/ ${BANAN_INCLUDE}/
|
||||
DEPENDS sysroot
|
||||
USES_TERMINAL
|
||||
)
|
||||
|
||||
add_custom_target(libelf-install
|
||||
DEPENDS libelf-headers
|
||||
)
|
||||
406
LibELF/LibELF/ELF.cpp
Normal file
406
LibELF/LibELF/ELF.cpp
Normal file
@@ -0,0 +1,406 @@
|
||||
#include <BAN/ScopeGuard.h>
|
||||
#include <LibELF/ELF.h>
|
||||
#include <LibELF/Values.h>
|
||||
|
||||
#ifdef __is_kernel
|
||||
#include <kernel/FS/VirtualFileSystem.h>
|
||||
#include <kernel/Memory/PageTableScope.h>
|
||||
#include <kernel/Process.h>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#define ELF_PRINT_HEADERS 0
|
||||
|
||||
#ifdef __is_kernel
|
||||
extern uint8_t g_kernel_end[];
|
||||
using namespace Kernel;
|
||||
#endif
|
||||
|
||||
namespace LibELF
|
||||
{
|
||||
|
||||
#ifdef __is_kernel
|
||||
BAN::ErrorOr<BAN::UniqPtr<ELF>> ELF::load_from_file(BAN::RefPtr<Inode> inode)
|
||||
{
|
||||
BAN::Vector<uint8_t> buffer;
|
||||
TRY(buffer.resize(inode->size()));
|
||||
|
||||
TRY(inode->read(0, buffer.data(), inode->size()));
|
||||
|
||||
ELF* elf_ptr = new ELF(BAN::move(buffer));
|
||||
if (elf_ptr == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
|
||||
auto elf = BAN::UniqPtr<ELF>::adopt(elf_ptr);
|
||||
TRY(elf->load());
|
||||
|
||||
return BAN::move(elf);
|
||||
}
|
||||
#else
|
||||
BAN::ErrorOr<ELF*> ELF::load_from_file(BAN::StringView file_path)
|
||||
{
|
||||
ELF* elf = nullptr;
|
||||
|
||||
{
|
||||
BAN::Vector<uint8_t> data;
|
||||
|
||||
int fd = TRY(Kernel::Process::current().open(file_path, O_RDONLY));
|
||||
BAN::ScopeGuard _([fd] { MUST(Kernel::Process::current().close(fd)); });
|
||||
|
||||
struct stat st;
|
||||
TRY(Kernel::Process::current().fstat(fd, &st));
|
||||
|
||||
TRY(data.resize(st.st_size));
|
||||
|
||||
TRY(Kernel::Process::current().read(fd, data.data(), data.size()));
|
||||
|
||||
elf = new ELF(BAN::move(data));
|
||||
ASSERT(elf);
|
||||
}
|
||||
|
||||
if (auto res = elf->load(); res.is_error())
|
||||
{
|
||||
delete elf;
|
||||
return res.error();
|
||||
}
|
||||
|
||||
return elf;
|
||||
}
|
||||
#endif
|
||||
|
||||
BAN::ErrorOr<void> ELF::load()
|
||||
{
|
||||
if (m_data.size() < EI_NIDENT)
|
||||
{
|
||||
dprintln("Too small ELF file");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
if (m_data[EI_MAG0] != ELFMAG0 ||
|
||||
m_data[EI_MAG1] != ELFMAG1 ||
|
||||
m_data[EI_MAG2] != ELFMAG2 ||
|
||||
m_data[EI_MAG3] != ELFMAG3)
|
||||
{
|
||||
dprintln("Invalid ELF header");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
if (m_data[EI_DATA] != ELFDATA2LSB)
|
||||
{
|
||||
dprintln("Only little-endian is supported");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
if (m_data[EI_VERSION] != EV_CURRENT)
|
||||
{
|
||||
dprintln("Invalid ELF version");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
if (m_data[EI_CLASS] == ELFCLASS64)
|
||||
{
|
||||
if (m_data.size() <= sizeof(Elf64FileHeader))
|
||||
{
|
||||
dprintln("Too small ELF file");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
auto& header = file_header64();
|
||||
if (!parse_elf64_file_header(header))
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
|
||||
for (size_t i = 0; i < header.e_phnum; i++)
|
||||
{
|
||||
auto& program_header = program_header64(i);
|
||||
if (!parse_elf64_program_header(program_header))
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
for (size_t i = 1; i < header.e_shnum; i++)
|
||||
{
|
||||
auto& section_header = section_header64(i);
|
||||
if (!parse_elf64_section_header(section_header))
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
}
|
||||
else if (m_data[EI_CLASS] == ELFCLASS32)
|
||||
{
|
||||
if (m_data.size() <= sizeof(Elf32FileHeader))
|
||||
{
|
||||
dprintln("Too small ELF file");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
auto& header = file_header32();
|
||||
if (!parse_elf32_file_header(header))
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
|
||||
for (size_t i = 0; i < header.e_phnum; i++)
|
||||
{
|
||||
auto& program_header = program_header32(i);
|
||||
if (!parse_elf32_program_header(program_header))
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
for (size_t i = 1; i < header.e_shnum; i++)
|
||||
{
|
||||
auto& section_header = section_header32(i);
|
||||
if (!parse_elf32_section_header(section_header))
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool ELF::is_x86_32() const { return m_data[EI_CLASS] == ELFCLASS32; }
|
||||
bool ELF::is_x86_64() const { return m_data[EI_CLASS] == ELFCLASS64; }
|
||||
|
||||
/*
|
||||
|
||||
64 bit ELF
|
||||
|
||||
*/
|
||||
|
||||
const char* ELF::lookup_section_name64(uint32_t offset) const
|
||||
{
|
||||
return lookup_string64(file_header64().e_shstrndx, offset);
|
||||
}
|
||||
|
||||
const char* ELF::lookup_string64(size_t table_index, uint32_t offset) const
|
||||
{
|
||||
if (table_index == SHN_UNDEF)
|
||||
return nullptr;
|
||||
auto& section_header = section_header64(table_index);
|
||||
return (const char*)m_data.data() + section_header.sh_offset + offset;
|
||||
}
|
||||
|
||||
bool ELF::parse_elf64_file_header(const Elf64FileHeader& header)
|
||||
{
|
||||
if (header.e_type != ET_EXEC)
|
||||
{
|
||||
dprintln("Only executable files are supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.e_version != EV_CURRENT)
|
||||
{
|
||||
dprintln("Invalid ELF version");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ELF::parse_elf64_program_header(const Elf64ProgramHeader& header)
|
||||
{
|
||||
#if ELF_PRINT_HEADERS
|
||||
dprintln("program header");
|
||||
dprintln(" type {H}", header.p_type);
|
||||
dprintln(" flags {H}", header.p_flags);
|
||||
dprintln(" offset {H}", header.p_offset);
|
||||
dprintln(" vaddr {H}", header.p_vaddr);
|
||||
dprintln(" paddr {H}", header.p_paddr);
|
||||
dprintln(" filesz {}", header.p_filesz);
|
||||
dprintln(" memsz {}", header.p_memsz);
|
||||
dprintln(" align {}", header.p_align);
|
||||
#endif
|
||||
(void)header;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ELF::parse_elf64_section_header(const Elf64SectionHeader& header)
|
||||
{
|
||||
#if ELF_PRINT_HEADERS
|
||||
if (auto* name = lookup_section_name64(header.sh_name))
|
||||
dprintln("{}", name);
|
||||
|
||||
switch (header.sh_type)
|
||||
{
|
||||
case SHT_NULL:
|
||||
dprintln(" SHT_NULL");
|
||||
break;
|
||||
case SHT_PROGBITS:
|
||||
dprintln(" SHT_PROGBITS");
|
||||
break;
|
||||
case SHT_SYMTAB:
|
||||
for (size_t i = 1; i < header.sh_size / header.sh_entsize; i++)
|
||||
{
|
||||
auto& symbol = ((const Elf64Symbol*)(m_data.data() + header.sh_offset))[i];
|
||||
if (auto* name = lookup_string64(header.sh_link, symbol.st_name))
|
||||
dprintln(" {}", name);
|
||||
}
|
||||
break;
|
||||
case SHT_STRTAB:
|
||||
dprintln(" SHT_STRTAB");
|
||||
break;
|
||||
case SHT_RELA:
|
||||
dprintln(" SHT_RELA");
|
||||
break;
|
||||
case SHT_NOBITS:
|
||||
dprintln(" SHT_NOBITS");
|
||||
break;
|
||||
case SHT_REL:
|
||||
dprintln(" SHT_REL");
|
||||
break;
|
||||
case SHT_SHLIB:
|
||||
dprintln(" SHT_SHLIB");
|
||||
break;
|
||||
case SHT_DYNSYM:
|
||||
dprintln(" SHT_DYNSYM");
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
}
|
||||
#endif
|
||||
(void)header;
|
||||
return true;
|
||||
}
|
||||
|
||||
const Elf64FileHeader& ELF::file_header64() const
|
||||
{
|
||||
ASSERT(is_x86_64());
|
||||
return *(const Elf64FileHeader*)m_data.data();
|
||||
}
|
||||
|
||||
const Elf64ProgramHeader& ELF::program_header64(size_t index) const
|
||||
{
|
||||
ASSERT(is_x86_64());
|
||||
const auto& file_header = file_header64();
|
||||
ASSERT(index < file_header.e_phnum);
|
||||
return *(const Elf64ProgramHeader*)(m_data.data() + file_header.e_phoff + file_header.e_phentsize * index);
|
||||
}
|
||||
|
||||
const Elf64SectionHeader& ELF::section_header64(size_t index) const
|
||||
{
|
||||
ASSERT(is_x86_64());
|
||||
const auto& file_header = file_header64();
|
||||
ASSERT(index < file_header.e_shnum);
|
||||
return *(const Elf64SectionHeader*)(m_data.data() + file_header.e_shoff + file_header.e_shentsize * index);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
32 bit ELF
|
||||
|
||||
*/
|
||||
|
||||
const char* ELF::lookup_section_name32(uint32_t offset) const
|
||||
{
|
||||
return lookup_string32(file_header32().e_shstrndx, offset);
|
||||
}
|
||||
|
||||
const char* ELF::lookup_string32(size_t table_index, uint32_t offset) const
|
||||
{
|
||||
if (table_index == SHN_UNDEF)
|
||||
return nullptr;
|
||||
auto& section_header = section_header32(table_index);
|
||||
return (const char*)m_data.data() + section_header.sh_offset + offset;
|
||||
}
|
||||
|
||||
bool ELF::parse_elf32_file_header(const Elf32FileHeader& header)
|
||||
{
|
||||
if (header.e_type != ET_EXEC)
|
||||
{
|
||||
dprintln("Only executable files are supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.e_version != EV_CURRENT)
|
||||
{
|
||||
dprintln("Invalid ELF version");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ELF::parse_elf32_program_header(const Elf32ProgramHeader& header)
|
||||
{
|
||||
#if ELF_PRINT_HEADERS
|
||||
dprintln("program header");
|
||||
dprintln(" type {H}", header.p_type);
|
||||
dprintln(" flags {H}", header.p_flags);
|
||||
dprintln(" offset {H}", header.p_offset);
|
||||
dprintln(" vaddr {H}", header.p_vaddr);
|
||||
dprintln(" paddr {H}", header.p_paddr);
|
||||
dprintln(" filesz {}", header.p_filesz);
|
||||
dprintln(" memsz {}", header.p_memsz);
|
||||
dprintln(" align {}", header.p_align);
|
||||
#endif
|
||||
(void)header;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ELF::parse_elf32_section_header(const Elf32SectionHeader& header)
|
||||
{
|
||||
#if ELF_PRINT_HEADERS
|
||||
if (auto* name = lookup_section_name32(header.sh_name))
|
||||
dprintln("{}", name);
|
||||
|
||||
switch (header.sh_type)
|
||||
{
|
||||
case SHT_NULL:
|
||||
dprintln(" SHT_NULL");
|
||||
break;
|
||||
case SHT_PROGBITS:
|
||||
dprintln(" SHT_PROGBITS");
|
||||
break;
|
||||
case SHT_SYMTAB:
|
||||
for (size_t i = 1; i < header.sh_size / header.sh_entsize; i++)
|
||||
{
|
||||
auto& symbol = ((const Elf32Symbol*)(m_data.data() + header.sh_offset))[i];
|
||||
if (auto* name = lookup_string32(header.sh_link, symbol.st_name))
|
||||
dprintln(" {}", name);
|
||||
}
|
||||
break;
|
||||
case SHT_STRTAB:
|
||||
dprintln(" SHT_STRTAB");
|
||||
break;
|
||||
case SHT_RELA:
|
||||
dprintln(" SHT_RELA");
|
||||
break;
|
||||
case SHT_NOBITS:
|
||||
dprintln(" SHT_NOBITS");
|
||||
break;
|
||||
case SHT_REL:
|
||||
dprintln(" SHT_REL");
|
||||
break;
|
||||
case SHT_SHLIB:
|
||||
dprintln(" SHT_SHLIB");
|
||||
break;
|
||||
case SHT_DYNSYM:
|
||||
dprintln(" SHT_DYNSYM");
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
}
|
||||
#endif
|
||||
(void)header;
|
||||
return true;
|
||||
}
|
||||
|
||||
const Elf32FileHeader& ELF::file_header32() const
|
||||
{
|
||||
ASSERT(is_x86_32());
|
||||
return *(const Elf32FileHeader*)m_data.data();
|
||||
}
|
||||
|
||||
const Elf32ProgramHeader& ELF::program_header32(size_t index) const
|
||||
{
|
||||
ASSERT(is_x86_32());
|
||||
const auto& file_header = file_header32();
|
||||
ASSERT(index < file_header.e_phnum);
|
||||
return *(const Elf32ProgramHeader*)(m_data.data() + file_header.e_phoff + file_header.e_phentsize * index);
|
||||
}
|
||||
|
||||
const Elf32SectionHeader& ELF::section_header32(size_t index) const
|
||||
{
|
||||
ASSERT(is_x86_32());
|
||||
const auto& file_header = file_header32();
|
||||
ASSERT(index < file_header.e_shnum);
|
||||
return *(const Elf32SectionHeader*)(m_data.data() + file_header.e_shoff + file_header.e_shentsize * index);
|
||||
}
|
||||
|
||||
}
|
||||
297
LibELF/LibELF/LoadableELF.cpp
Normal file
297
LibELF/LibELF/LoadableELF.cpp
Normal file
@@ -0,0 +1,297 @@
|
||||
#include <BAN/ScopeGuard.h>
|
||||
#include <kernel/Memory/Heap.h>
|
||||
#include <kernel/LockGuard.h>
|
||||
#include <LibELF/LoadableELF.h>
|
||||
#include <LibELF/Values.h>
|
||||
|
||||
namespace LibELF
|
||||
{
|
||||
|
||||
using namespace Kernel;
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> LoadableELF::load_from_inode(PageTable& page_table, BAN::RefPtr<Inode> inode)
|
||||
{
|
||||
auto* elf_ptr = new LoadableELF(page_table, inode);
|
||||
if (elf_ptr == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
auto elf = BAN::UniqPtr<LoadableELF>::adopt(elf_ptr);
|
||||
TRY(elf->initialize());
|
||||
return BAN::move(elf);
|
||||
}
|
||||
|
||||
LoadableELF::LoadableELF(PageTable& page_table, BAN::RefPtr<Inode> inode)
|
||||
: m_inode(inode)
|
||||
, m_page_table(page_table)
|
||||
{
|
||||
}
|
||||
|
||||
LoadableELF::~LoadableELF()
|
||||
{
|
||||
for (const auto& program_header : m_program_headers)
|
||||
{
|
||||
switch (program_header.p_type)
|
||||
{
|
||||
case PT_NULL:
|
||||
continue;
|
||||
case PT_LOAD:
|
||||
{
|
||||
vaddr_t start = program_header.p_vaddr & PAGE_ADDR_MASK;
|
||||
size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
|
||||
for (size_t i = 0; i < pages; i++)
|
||||
{
|
||||
paddr_t paddr = m_page_table.physical_address_of(start + i * PAGE_SIZE);
|
||||
if (paddr != 0)
|
||||
Heap::get().release_page(paddr);
|
||||
}
|
||||
m_page_table.unmap_range(start, pages * PAGE_SIZE);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> LoadableELF::initialize()
|
||||
{
|
||||
if ((size_t)m_inode->size() < sizeof(ElfNativeFileHeader))
|
||||
{
|
||||
dprintln("Too small file");
|
||||
return BAN::Error::from_errno(ENOEXEC);
|
||||
}
|
||||
|
||||
size_t nread = TRY(m_inode->read(0, &m_file_header, sizeof(m_file_header)));
|
||||
ASSERT(nread == sizeof(m_file_header));
|
||||
|
||||
if (m_file_header.e_ident[EI_MAG0] != ELFMAG0 ||
|
||||
m_file_header.e_ident[EI_MAG1] != ELFMAG1 ||
|
||||
m_file_header.e_ident[EI_MAG2] != ELFMAG2 ||
|
||||
m_file_header.e_ident[EI_MAG3] != ELFMAG3)
|
||||
{
|
||||
dprintln("Invalid magic in header");
|
||||
return BAN::Error::from_errno(ENOEXEC);
|
||||
}
|
||||
|
||||
if (m_file_header.e_ident[EI_DATA] != ELFDATA2LSB)
|
||||
{
|
||||
dprintln("Only little-endian is supported");
|
||||
return BAN::Error::from_errno(ENOEXEC);
|
||||
}
|
||||
|
||||
if (m_file_header.e_ident[EI_VERSION] != EV_CURRENT)
|
||||
{
|
||||
dprintln("Invalid version");
|
||||
return BAN::Error::from_errno(ENOEXEC);
|
||||
}
|
||||
|
||||
#if ARCH(i386)
|
||||
if (m_file_header.e_ident[EI_CLASS] != ELFCLASS32)
|
||||
#elif ARCH(x86_64)
|
||||
if (m_file_header.e_ident[EI_CLASS] != ELFCLASS64)
|
||||
#endif
|
||||
{
|
||||
dprintln("Not in native format");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
if (m_file_header.e_type != ET_EXEC)
|
||||
{
|
||||
dprintln("Only executable files are supported");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
if (m_file_header.e_version != EV_CURRENT)
|
||||
{
|
||||
dprintln("Unsupported version");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
ASSERT(m_file_header.e_phentsize <= sizeof(ElfNativeProgramHeader));
|
||||
|
||||
TRY(m_program_headers.resize(m_file_header.e_phnum));
|
||||
for (size_t i = 0; i < m_file_header.e_phnum; i++)
|
||||
{
|
||||
TRY(m_inode->read(m_file_header.e_phoff + m_file_header.e_phentsize * i, &m_program_headers[i], m_file_header.e_phentsize));
|
||||
|
||||
const auto& pheader = m_program_headers[i];
|
||||
if (pheader.p_type != PT_NULL && pheader.p_type != PT_LOAD)
|
||||
{
|
||||
dprintln("Unsupported program header type {}", pheader.p_type);
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
if (pheader.p_memsz < pheader.p_filesz)
|
||||
{
|
||||
dprintln("Invalid program header");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
m_virtual_page_count += BAN::Math::div_round_up<size_t>((pheader.p_vaddr % PAGE_SIZE) + pheader.p_memsz, PAGE_SIZE);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
vaddr_t LoadableELF::entry_point() const
|
||||
{
|
||||
return m_file_header.e_entry;
|
||||
}
|
||||
|
||||
bool LoadableELF::contains(vaddr_t address) const
|
||||
{
|
||||
for (const auto& program_header : m_program_headers)
|
||||
{
|
||||
switch (program_header.p_type)
|
||||
{
|
||||
case PT_NULL:
|
||||
continue;
|
||||
case PT_LOAD:
|
||||
if (program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz)
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LoadableELF::reserve_address_space()
|
||||
{
|
||||
for (const auto& program_header : m_program_headers)
|
||||
{
|
||||
switch (program_header.p_type)
|
||||
{
|
||||
case PT_NULL:
|
||||
break;
|
||||
case PT_LOAD:
|
||||
{
|
||||
vaddr_t page_vaddr = program_header.p_vaddr & PAGE_ADDR_MASK;
|
||||
size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
|
||||
ASSERT(m_page_table.reserve_range(page_vaddr, pages * PAGE_SIZE));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> LoadableELF::load_page_to_memory(vaddr_t address)
|
||||
{
|
||||
for (const auto& program_header : m_program_headers)
|
||||
{
|
||||
switch (program_header.p_type)
|
||||
{
|
||||
case PT_NULL:
|
||||
break;
|
||||
case PT_LOAD:
|
||||
{
|
||||
if (!(program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz))
|
||||
continue;
|
||||
|
||||
PageTable::flags_t flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present;
|
||||
if (program_header.p_flags & LibELF::PF_W)
|
||||
flags |= PageTable::Flags::ReadWrite;
|
||||
if (program_header.p_flags & LibELF::PF_X)
|
||||
flags |= PageTable::Flags::Execute;
|
||||
|
||||
vaddr_t vaddr = address & PAGE_ADDR_MASK;
|
||||
paddr_t paddr = Heap::get().take_free_page();
|
||||
if (paddr == 0)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
|
||||
m_page_table.map_page_at(paddr, vaddr, flags);
|
||||
m_physical_page_count++;
|
||||
|
||||
memset((void*)vaddr, 0x00, PAGE_SIZE);
|
||||
|
||||
if (vaddr / PAGE_SIZE < BAN::Math::div_round_up<size_t>(program_header.p_vaddr + program_header.p_filesz, PAGE_SIZE))
|
||||
{
|
||||
size_t vaddr_offset = 0;
|
||||
if (vaddr < program_header.p_vaddr)
|
||||
vaddr_offset = program_header.p_vaddr - vaddr;
|
||||
|
||||
size_t file_offset = 0;
|
||||
if (vaddr > program_header.p_vaddr)
|
||||
file_offset = vaddr - program_header.p_vaddr;
|
||||
|
||||
size_t bytes = BAN::Math::min<size_t>(PAGE_SIZE - vaddr_offset, program_header.p_filesz - file_offset);
|
||||
TRY(m_inode->read(program_header.p_offset + file_offset, (void*)(vaddr + vaddr_offset), bytes));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> LoadableELF::clone(Kernel::PageTable& new_page_table)
|
||||
{
|
||||
auto* elf_ptr = new LoadableELF(new_page_table, m_inode);
|
||||
if (elf_ptr == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
auto elf = BAN::UniqPtr<LoadableELF>::adopt(elf_ptr);
|
||||
|
||||
memcpy(&elf->m_file_header, &m_file_header, sizeof(ElfNativeFileHeader));
|
||||
|
||||
TRY(elf->m_program_headers.resize(m_program_headers.size()));
|
||||
memcpy(elf->m_program_headers.data(), m_program_headers.data(), m_program_headers.size() * sizeof(ElfNativeProgramHeader));
|
||||
|
||||
elf->reserve_address_space();
|
||||
|
||||
ASSERT(&PageTable::current() == &m_page_table);
|
||||
LockGuard _(m_page_table);
|
||||
ASSERT(m_page_table.is_page_free(0));
|
||||
|
||||
for (const auto& program_header : m_program_headers)
|
||||
{
|
||||
switch (program_header.p_type)
|
||||
{
|
||||
case PT_NULL:
|
||||
break;
|
||||
case PT_LOAD:
|
||||
{
|
||||
if (!(program_header.p_flags & LibELF::PF_W))
|
||||
continue;
|
||||
|
||||
PageTable::flags_t flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present;
|
||||
if (program_header.p_flags & LibELF::PF_W)
|
||||
flags |= PageTable::Flags::ReadWrite;
|
||||
if (program_header.p_flags & LibELF::PF_X)
|
||||
flags |= PageTable::Flags::Execute;
|
||||
|
||||
vaddr_t start = program_header.p_vaddr & PAGE_ADDR_MASK;
|
||||
size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
|
||||
|
||||
for (size_t i = 0; i < pages; i++)
|
||||
{
|
||||
if (m_page_table.physical_address_of(start + i * PAGE_SIZE) == 0)
|
||||
continue;
|
||||
|
||||
paddr_t paddr = Heap::get().take_free_page();
|
||||
if (paddr == 0)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
|
||||
m_page_table.map_page_at(paddr, 0, PageTable::Flags::ReadWrite | PageTable::Flags::Present);
|
||||
memcpy((void*)0, (void*)(start + i * PAGE_SIZE), PAGE_SIZE);
|
||||
m_page_table.unmap_page(0);
|
||||
|
||||
new_page_table.map_page_at(paddr, start + i * PAGE_SIZE, flags);
|
||||
elf->m_physical_page_count++;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
return elf;
|
||||
}
|
||||
|
||||
}
|
||||
89
LibELF/include/LibELF/ELF.h
Normal file
89
LibELF/include/LibELF/ELF.h
Normal file
@@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __is_kernel
|
||||
#include <kernel/FS/Inode.h>
|
||||
#include <kernel/Memory/VirtualRange.h>
|
||||
#endif
|
||||
|
||||
#include <BAN/StringView.h>
|
||||
#include <BAN/UniqPtr.h>
|
||||
#include <BAN/Vector.h>
|
||||
#include <kernel/Arch.h>
|
||||
#include "Types.h"
|
||||
|
||||
namespace LibELF
|
||||
{
|
||||
|
||||
class ELF
|
||||
{
|
||||
public:
|
||||
#ifdef __is_kernel
|
||||
static BAN::ErrorOr<BAN::UniqPtr<ELF>> load_from_file(BAN::RefPtr<Kernel::Inode>);
|
||||
#else
|
||||
static BAN::ErrorOr<BAN::UniqPtr<ELF>> load_from_file(BAN::StringView);
|
||||
#endif
|
||||
|
||||
const Elf64FileHeader& file_header64() const;
|
||||
const Elf64ProgramHeader& program_header64(size_t) const;
|
||||
const Elf64SectionHeader& section_header64(size_t) const;
|
||||
const char* lookup_section_name64(uint32_t) const;
|
||||
const char* lookup_string64(size_t, uint32_t) const;
|
||||
#if ARCH(x86_64)
|
||||
const Elf64FileHeader& file_header_native() const { return file_header64(); }
|
||||
const Elf64ProgramHeader& program_header_native(size_t index) const { return program_header64(index); }
|
||||
const Elf64SectionHeader& section_header_native(size_t index) const { return section_header64(index); }
|
||||
const char* lookup_section_name_native(uint32_t offset) const { return lookup_section_name64(offset); }
|
||||
const char* lookup_string_native(size_t table_index, uint32_t offset) const { return lookup_string64(table_index, offset); }
|
||||
bool is_native() const { return is_x86_64(); }
|
||||
#endif
|
||||
|
||||
const Elf32FileHeader& file_header32() const;
|
||||
const Elf32ProgramHeader& program_header32(size_t) const;
|
||||
const Elf32SectionHeader& section_header32(size_t) const;
|
||||
const char* lookup_section_name32(uint32_t) const;
|
||||
const char* lookup_string32(size_t, uint32_t) const;
|
||||
#if ARCH(i386)
|
||||
const Elf32FileHeader& file_header_native() const { return file_header32(); }
|
||||
const Elf32ProgramHeader& program_header_native(size_t index) const { return program_header32(index); }
|
||||
const Elf32SectionHeader& section_header_native(size_t index) const { return section_header32(index); }
|
||||
const char* lookup_section_name_native(uint32_t offset) const { return lookup_section_name32(offset); }
|
||||
const char* lookup_string_native(size_t table_index, uint32_t offset) const { return lookup_string32(table_index, offset); }
|
||||
bool is_native() const { return is_x86_32(); }
|
||||
#endif
|
||||
|
||||
const uint8_t* data() const { return m_data.data(); }
|
||||
|
||||
bool is_x86_32() const;
|
||||
bool is_x86_64() const;
|
||||
|
||||
private:
|
||||
//#ifdef __is_kernel
|
||||
// ELF(BAN::UniqPtr<Kernel::VirtualRange>&& storage, size_t size)
|
||||
// : m_storage(BAN::move(storage))
|
||||
// , m_data((const uint8_t*)m_storage->vaddr(), size)
|
||||
// {}
|
||||
//#else
|
||||
ELF(BAN::Vector<uint8_t>&& data)
|
||||
: m_data(BAN::move(data))
|
||||
{}
|
||||
//#endif
|
||||
BAN::ErrorOr<void> load();
|
||||
|
||||
bool parse_elf64_file_header(const Elf64FileHeader&);
|
||||
bool parse_elf64_program_header(const Elf64ProgramHeader&);
|
||||
bool parse_elf64_section_header(const Elf64SectionHeader&);
|
||||
|
||||
bool parse_elf32_file_header(const Elf32FileHeader&);
|
||||
bool parse_elf32_program_header(const Elf32ProgramHeader&);
|
||||
bool parse_elf32_section_header(const Elf32SectionHeader&);
|
||||
|
||||
private:
|
||||
//#ifdef __is_kernel
|
||||
// BAN::UniqPtr<Kernel::VirtualRange> m_storage;
|
||||
// BAN::Span<const uint8_t> m_data;
|
||||
//#else
|
||||
const BAN::Vector<uint8_t> m_data;
|
||||
//#endif
|
||||
};
|
||||
|
||||
}
|
||||
52
LibELF/include/LibELF/LoadableELF.h
Normal file
52
LibELF/include/LibELF/LoadableELF.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __is_kernel
|
||||
#error "This is kernel only header"
|
||||
#endif
|
||||
|
||||
#include <BAN/UniqPtr.h>
|
||||
#include <BAN/Vector.h>
|
||||
|
||||
#include <kernel/FS/Inode.h>
|
||||
#include <kernel/Memory/PageTable.h>
|
||||
|
||||
#include <LibELF/Types.h>
|
||||
|
||||
namespace LibELF
|
||||
{
|
||||
|
||||
class LoadableELF
|
||||
{
|
||||
BAN_NON_COPYABLE(LoadableELF);
|
||||
BAN_NON_MOVABLE(LoadableELF);
|
||||
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> load_from_inode(Kernel::PageTable&, BAN::RefPtr<Kernel::Inode>);
|
||||
~LoadableELF();
|
||||
|
||||
Kernel::vaddr_t entry_point() const;
|
||||
|
||||
bool contains(Kernel::vaddr_t address) const;
|
||||
void reserve_address_space();
|
||||
|
||||
BAN::ErrorOr<void> load_page_to_memory(Kernel::vaddr_t address);
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> clone(Kernel::PageTable&);
|
||||
|
||||
size_t virtual_page_count() const { return m_virtual_page_count; }
|
||||
size_t physical_page_count() const { return m_physical_page_count; }
|
||||
|
||||
private:
|
||||
LoadableELF(Kernel::PageTable&, BAN::RefPtr<Kernel::Inode>);
|
||||
BAN::ErrorOr<void> initialize();
|
||||
|
||||
private:
|
||||
BAN::RefPtr<Kernel::Inode> m_inode;
|
||||
Kernel::PageTable& m_page_table;
|
||||
ElfNativeFileHeader m_file_header;
|
||||
BAN::Vector<ElfNativeProgramHeader> m_program_headers;
|
||||
size_t m_virtual_page_count = 0;
|
||||
size_t m_physical_page_count = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -80,16 +80,6 @@ namespace LibELF
|
||||
Elf32Word p_align;
|
||||
};
|
||||
|
||||
struct Elf32Dynamic
|
||||
{
|
||||
Elf32Sword d_tag;
|
||||
union
|
||||
{
|
||||
Elf32Word d_val;
|
||||
Elf32Addr d_ptr;
|
||||
} d_un;
|
||||
};
|
||||
|
||||
using Elf64Addr = uint64_t;
|
||||
using Elf64Off = uint64_t;
|
||||
using Elf64Half = uint16_t;
|
||||
@@ -165,29 +155,21 @@ namespace LibELF
|
||||
Elf64Xword p_align;
|
||||
};
|
||||
|
||||
struct Elf64Dynamic
|
||||
{
|
||||
Elf64Sxword d_tag;
|
||||
union
|
||||
{
|
||||
Elf64Xword d_val;
|
||||
Elf64Addr d_ptr;
|
||||
} d_un;
|
||||
};
|
||||
|
||||
#if ARCH(i686)
|
||||
#if ARCH(i386)
|
||||
using ElfNativeAddr = Elf32Addr;
|
||||
using ElfNativeOff = Elf32Off;
|
||||
using ElfNativeHalf = Elf32Half;
|
||||
using ElfNativeWord = Elf32Word;
|
||||
using ElfNativeSword = Elf32Sword;
|
||||
using ElfNativeXword = Elf32Xword;
|
||||
using ElfNativeSxword = Elf32Sxword;
|
||||
using ElfNativeFileHeader = Elf32FileHeader;
|
||||
using ElfNativeSectionHeader = Elf32SectionHeader;
|
||||
using ElfNativeSymbol = Elf32Symbol;
|
||||
using ElfNativeRelocation = Elf32Relocation;
|
||||
using ElfNativeRelocationA = Elf32RelocationA;
|
||||
using ElfNativeProgramHeader = Elf32ProgramHeader;
|
||||
using ElfNativeDynamic = Elf32Dynamic;
|
||||
#elif ARCH(x86_64)
|
||||
using ElfNativeAddr = Elf64Addr;
|
||||
using ElfNativeOff = Elf64Off;
|
||||
@@ -202,7 +184,6 @@ namespace LibELF
|
||||
using ElfNativeRelocation = Elf64Relocation;
|
||||
using ElfNativeRelocationA = Elf64RelocationA;
|
||||
using ElfNativeProgramHeader = Elf64ProgramHeader;
|
||||
using ElfNativeDynamic = Elf64Dynamic;
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
140
LibELF/include/LibELF/Values.h
Normal file
140
LibELF/include/LibELF/Values.h
Normal file
@@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
|
||||
namespace LibELF
|
||||
{
|
||||
|
||||
enum ELF_Ident
|
||||
{
|
||||
ELFMAG0 = 0x7F,
|
||||
ELFMAG1 = 'E',
|
||||
ELFMAG2 = 'L',
|
||||
ELFMAG3 = 'F',
|
||||
|
||||
ELFCLASSNONE = 0,
|
||||
ELFCLASS32 = 1,
|
||||
ELFCLASS64 = 2,
|
||||
|
||||
ELFDATANONE = 0,
|
||||
ELFDATA2LSB = 1,
|
||||
ELFDATA2MSB = 2,
|
||||
};
|
||||
|
||||
enum ELF_EI
|
||||
{
|
||||
EI_MAG0 = 0,
|
||||
EI_MAG1 = 1,
|
||||
EI_MAG2 = 2,
|
||||
EI_MAG3 = 3,
|
||||
EI_CLASS = 4,
|
||||
EI_DATA = 5,
|
||||
EI_VERSION = 6,
|
||||
EI_OSABI = 7,
|
||||
EI_ABIVERSION = 8,
|
||||
EI_NIDENT = 16,
|
||||
};
|
||||
|
||||
enum ELF_ET
|
||||
{
|
||||
ET_NONE = 0,
|
||||
ET_REL = 1,
|
||||
ET_EXEC = 2,
|
||||
ET_DYN = 3,
|
||||
ET_CORE = 4,
|
||||
ET_LOOS = 0xfe00,
|
||||
ET_HIOS = 0xfeff,
|
||||
ET_LOPROC = 0xff00,
|
||||
ET_HIPROC = 0xffff,
|
||||
};
|
||||
|
||||
enum ELF_EV
|
||||
{
|
||||
EV_NONE = 0,
|
||||
EV_CURRENT = 1,
|
||||
};
|
||||
|
||||
enum ELF_SHT
|
||||
{
|
||||
SHT_NULL = 0,
|
||||
SHT_PROGBITS = 1,
|
||||
SHT_SYMTAB = 2,
|
||||
SHT_STRTAB = 3,
|
||||
SHT_RELA = 4,
|
||||
SHT_NOBITS = 8,
|
||||
SHT_REL = 9,
|
||||
SHT_SHLIB = 10,
|
||||
SHT_DYNSYM = 11,
|
||||
SHT_LOOS = 0x60000000,
|
||||
SHT_HIOS = 0x6FFFFFFF,
|
||||
SHT_LOPROC = 0x70000000,
|
||||
SHT_HIPROC = 0x7FFFFFFF,
|
||||
};
|
||||
|
||||
enum ELF_SHF
|
||||
{
|
||||
SHF_WRITE = 0x1,
|
||||
SHF_ALLOC = 0x2,
|
||||
SHF_EXECINSTR = 0x4,
|
||||
SHF_MASKOS = 0x0F000000,
|
||||
SHF_MASKPROC = 0xF0000000,
|
||||
};
|
||||
|
||||
enum ELF_SHN
|
||||
{
|
||||
SHN_UNDEF = 0,
|
||||
SHN_LOPROC = 0xFF00,
|
||||
SHN_HIPROC = 0xFF1F,
|
||||
SHN_LOOS = 0xFF20,
|
||||
SHN_HIOS = 0xFF3F,
|
||||
SHN_ABS = 0xFFF1,
|
||||
SHN_COMMON = 0xFFF2,
|
||||
};
|
||||
|
||||
enum ELF_STB
|
||||
{
|
||||
STB_LOCAL = 0,
|
||||
STB_GLOBAL = 1,
|
||||
STB_WEAK = 2,
|
||||
STB_LOOS = 10,
|
||||
STB_HIOS = 12,
|
||||
STB_LOPROC = 13,
|
||||
STB_HIPROC = 15,
|
||||
};
|
||||
|
||||
enum ELF_STT
|
||||
{
|
||||
STT_NOTYPE = 0,
|
||||
STT_OBJECT = 1,
|
||||
STT_FUNC = 2,
|
||||
STT_SECTION = 3,
|
||||
STT_FILE = 4,
|
||||
STT_LOOS = 10,
|
||||
STT_HIOS = 12,
|
||||
STT_LOPROC = 13,
|
||||
STT_HIPROC = 15,
|
||||
};
|
||||
|
||||
enum ELF_PT
|
||||
{
|
||||
PT_NULL = 0,
|
||||
PT_LOAD = 1,
|
||||
PT_DYNAMIC = 2,
|
||||
PT_INTERP = 3,
|
||||
PT_NOTE = 4,
|
||||
PT_SHLIB = 5,
|
||||
PT_PHDR = 6,
|
||||
PT_LOOS = 0x60000000,
|
||||
PT_HIOS = 0x6FFFFFFF,
|
||||
PT_LOPROC = 0x70000000,
|
||||
PT_HIPROC = 0x7FFFFFFF,
|
||||
};
|
||||
|
||||
enum ELF_PF
|
||||
{
|
||||
PF_X = 0x1,
|
||||
PF_W = 0x2,
|
||||
PF_R = 0x4,
|
||||
PF_MASKOS = 0x00FF0000,
|
||||
PF_MASKPROC = 0xFF000000,
|
||||
};
|
||||
|
||||
}
|
||||
1
PreLoad.cmake
Normal file
1
PreLoad.cmake
Normal file
@@ -0,0 +1 @@
|
||||
set(CMAKE_GENERATOR "Ninja" CACHE INTERNAL "" FORCE)
|
||||
132
README.md
132
README.md
@@ -1,77 +1,8 @@
|
||||
[](https://git.bananymous.com/Bananymous/banan-os)
|
||||
[](https://git.bananymous.com/Bananymous/banan-os)
|
||||
[](https://git.bananymous.com/Bananymous/banan-os/src/branch/main/LICENSE)
|
||||
[](https://discord.gg/ehjGySwYdK)
|
||||

|
||||
|
||||
# banan-os
|
||||
|
||||
This is my hobby operating system written in C++. Currently supports x86\_64 and i686 architectures.
|
||||
|
||||
You can find a live demo [here](https://bananymous.com/banan-os)
|
||||
|
||||
If you want to try out DOOM, you should first enter the GUI environment using the `start-gui` command. Then you can run `doom` in the GUI terminal.
|
||||
|
||||
### Features
|
||||
|
||||
#### General
|
||||
- [x] Ring3 userspace
|
||||
- [x] SMP (multiprocessing)
|
||||
- [x] Linear framebuffer (VESA and GOP)
|
||||
- [x] Network stack
|
||||
- [x] ELF executable loading
|
||||
- [x] AML interpreter (partial)
|
||||
- [x] Basic graphical environment
|
||||
- [x] Terminal emulator
|
||||
- [x] Status bar
|
||||
- [x] Program launcher
|
||||
- [ ] Some nice apps
|
||||
- [x] ELF dynamic linking
|
||||
- [x] copy-on-write memory
|
||||
- [x] file mappings
|
||||
- [ ] anonymous mappings
|
||||
|
||||
#### Drivers
|
||||
- [x] NVMe disks
|
||||
- [x] ATA (IDE, SATA) disks
|
||||
- [x] E1000 and E1000E NICs
|
||||
- [x] RTL8111/8168/8211/8411 NICs
|
||||
- [x] PS2 keyboard (all scancode sets)
|
||||
- [x] PS2 mouse
|
||||
- [x] USB
|
||||
- [x] xHCI
|
||||
- [ ] EHCI
|
||||
- [ ] OHCI
|
||||
- [ ] UHCI
|
||||
- [x] Keyboard
|
||||
- [x] Mouse
|
||||
- [x] Mass storage
|
||||
- [x] Hubs
|
||||
- [ ] ...
|
||||
- [ ] virtio devices (network, storage)
|
||||
|
||||
#### Network
|
||||
- [x] ARP
|
||||
- [x] ICMP
|
||||
- [x] IPv4
|
||||
- [x] UDP
|
||||
- [x] TCP (partial and buggy)
|
||||
- [x] Unix domain sockets
|
||||
- [ ] SSL
|
||||
|
||||
#### Filesystems
|
||||
- [x] Virtual filesystem
|
||||
- [x] Ext2
|
||||
- [x] FAT12/16/32
|
||||
- [x] Dev
|
||||
- [x] Ram
|
||||
- [x] Proc
|
||||
- [ ] Sys
|
||||
- [ ] 9P
|
||||
|
||||
#### Bootloader support
|
||||
- [x] GRUB
|
||||
- [x] Custom BIOS bootloader
|
||||
- [ ] Custom UEFI bootloader
|
||||
This is my hobby operating system written in C++. Currently supports only x86_64 architecture. We have a read-only ext2 filesystem, read-write ramfs, IDE disk drivers in ATA PIO mode, userspace processes, executable loading from ELF format, linear VBE graphics and multithreaded processing on single core.
|
||||
|
||||

|
||||
|
||||
@@ -81,61 +12,44 @@ Each major component and library has its own subdirectory (kernel, userspace, li
|
||||
|
||||
## Building
|
||||
|
||||
### Needed packages
|
||||
There does not exist a complete list of needed packages for building. From the top of my head I can say that *cmake*, *ninja*, *make*, *grub*, *rsync* and emulator (*qemu* or *bochs*) are needed.
|
||||
|
||||
#### apt (tested on ubuntu 22.04)
|
||||
```# apt install build-essential git ninja-build texinfo bison flex libgmp-dev libmpfr-dev libmpc-dev parted qemu-system-x86 cpu-checker```
|
||||
You can and *should* pass cmake variable QEMU_ACCEL set to proper accelerator to cmake commands. For example on Linux this means adding -DQEMU_ACCEL=kvm to the end of all cmake commands.
|
||||
|
||||
#### pacman
|
||||
```# pacman -S --needed base-devel git wget cmake ninja parted qemu-system-x86```
|
||||
|
||||
|
||||
### Compilation
|
||||
Create the build directory and cofigure cmake
|
||||
```sh
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
```
|
||||
|
||||
To build the toolchain for this os. You can run the following command.
|
||||
> ***NOTE:*** The following step has to be done only once. This might take a long time since we are compiling binutils and gcc.
|
||||
```sh
|
||||
./bos toolchain
|
||||
ninja toolchain
|
||||
cmake --fresh .. # We need to reconfigure cmake to use the new compiler
|
||||
ninja libstdc++
|
||||
```
|
||||
|
||||
To build the os itself you can run one of the following commands. You will need root access for disk image creation/modification.
|
||||
To build the os itself you can run either of the following commands. You will need root access since the sysroot has "proper" permissions.
|
||||
```sh
|
||||
./bos qemu
|
||||
./bos qemu-nographic
|
||||
./bos qemu-debug
|
||||
./bos bochs
|
||||
ninja qemu
|
||||
ninja bochs
|
||||
```
|
||||
|
||||
You can also build the kernel or disk image without running it:
|
||||
```sh
|
||||
./bos kernel
|
||||
./bos image
|
||||
ninja kernel
|
||||
ninja image
|
||||
```
|
||||
|
||||
To build for other architectures set environment variable BANAN\_ARCH=*arch* (e.g. BANAN\_ARCH=i686).
|
||||
|
||||
To change the bootloader you can set environment variable BANAN\_BOOTLOADER; supported values are BANAN (my custom bootloader) and GRUB.
|
||||
|
||||
To run with UEFI set environment variable BANAN\_UEFI\_BOOT=1. You will also have to set OVMF\_PATH to the correct OVMF (default */usr/share/ovmf/x64/OVMF.fd*).
|
||||
|
||||
To build an image with no physical root filesystem, but an initrd set environment variable BANAN\_INITRD=1. This can be used when testing on hardware with unsupported USB controller.
|
||||
|
||||
If you have corrupted your disk image or want to create new one, you can either manually delete *build/banan-os.img* and build system will automatically create you a new one or you can run the following command.
|
||||
If you have corrupted your disk image or want to create new one, you can either manually delete *banan-os.img* and cmake will automatically create you a new one or you can run the following command.
|
||||
```sh
|
||||
./bos image-full
|
||||
ninja image-full
|
||||
```
|
||||
|
||||
I have also created shell completion script for zsh. You can either copy the file in _script/shell-completion/zsh/\_bos_ to _/usr/share/zsh/site-functions/_ or add the _script/shell-completion/zsh_ to your fpath in _.zshrc_.
|
||||
> ***NOTE*** ```ninja clean``` has to be ran with root permissions, since it deletes the root filesystem.
|
||||
|
||||
## Contributing
|
||||
### Contributing
|
||||
|
||||
As the upstream is hosted on my server https://git.bananymous.com/Bananymous/banan-os, merging contributions is not as trivial as it would be on GitHub. You can still send PRs in GitHub in which case I should be able to download the diff and apply it manually. If you want, I can also provide you an account to my git server. In this case please contact me ([email](mailto:oskari.alaranta@bananymous.com), [discord](https://discord.gg/ehjGySwYdK)).
|
||||
|
||||
As this is mostly a learning experience for me, I would appreciate if you first contacted me about adding new features (email, discord, issue, ...). If you send a PR about something I was planning on doing myself and you didn't ask me, I will probably just close it. Bug fixes are always welcome!
|
||||
|
||||
Commit message should be formatted followingly
|
||||
|
||||
1. First line is of the form "_Subject: Description_", where _Subject_ tells the area touched (Kernel, Shell, BuildSystem, ...) and _Description_ is brief description of the change done. First line should fit fully in 72 characters.
|
||||
2. Body of the message should further describe the change and reasoning behind the change.
|
||||
|
||||
All commits should pass the pre-commit hook defined in _.pre-commit-config.yaml_. For instructions on how to setup pre-commit, please see https://pre-commit.com/#install.
|
||||
Currently I don't accept contributions to this repository unless explicitly told otherwise. This is a learning project for me and I want to do everything myself. Feel free to fork/clone this repo and tinker with it yourself.
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 384 KiB After Width: | Height: | Size: 11 KiB |
Binary file not shown.
@@ -1,4 +1,5 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
BOCHS_CONFIG_FILE=bochsrc
|
||||
COM1_TERMINAL=kitty
|
||||
@@ -13,7 +14,7 @@ COM1_DEVICE=$(cat $COM1_DEVICE_FILE)
|
||||
rm $COM1_DEVICE_FILE
|
||||
|
||||
cat > $BOCHS_CONFIG_FILE << EOF
|
||||
ata0-master: type=disk, path=$BANAN_DISK_IMAGE_PATH, status=inserted
|
||||
ata0-master: type=disk, path=$DISK_IMAGE_PATH, status=inserted
|
||||
boot: disk
|
||||
clock: sync=realtime, time0=local
|
||||
display_library: x, options="gui_debug"
|
||||
@@ -24,4 +25,4 @@ EOF
|
||||
|
||||
bochs -qf $BOCHS_CONFIG_FILE
|
||||
kill $COM1_TERM_PID
|
||||
rm $BOCHS_CONFIG_FILE
|
||||
rm $BOCHS_CONFIG_FILE
|
||||
@@ -1,3 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
add_subdirectory(bios)
|
||||
@@ -1,20 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
project(bootloader ASM)
|
||||
|
||||
set(BOOTLOADER_SOURCES
|
||||
a20_line.S
|
||||
boot.S
|
||||
command_line.S
|
||||
disk.S
|
||||
elf.S
|
||||
ext2.S
|
||||
framebuffer.S
|
||||
memory_map.S
|
||||
utils.S
|
||||
)
|
||||
|
||||
add_executable(bootloader ${BOOTLOADER_SOURCES})
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_options(bootloader PRIVATE LINKER:-T,${CMAKE_CURRENT_SOURCE_DIR}/linker.ld)
|
||||
target_link_options(bootloader PRIVATE -nostdlib)
|
||||
@@ -1,168 +0,0 @@
|
||||
.code16
|
||||
.section .stage2
|
||||
|
||||
# checks whether A20 line is enabled or disabled
|
||||
# return
|
||||
# ax: 1 if enabled, 0 otherwise
|
||||
check_a20:
|
||||
pushf
|
||||
pushw %si
|
||||
pushw %di
|
||||
pushw %ds
|
||||
pushw %es
|
||||
|
||||
cli
|
||||
|
||||
xorw %ax, %ax
|
||||
movw %ax, %es
|
||||
notw %ax
|
||||
movw %ax, %ds
|
||||
|
||||
movw $0x0500, %di
|
||||
movw $0x0510, %si
|
||||
|
||||
movb %es:(%di), %al
|
||||
pushw %ax
|
||||
|
||||
movb %ds:(%si), %al
|
||||
pushw %ax
|
||||
|
||||
movb $0x00, %es:(%di)
|
||||
movb $0xFF, %ds:(%si)
|
||||
|
||||
cmpb $0xFF, %es:(%di)
|
||||
|
||||
pop %ax
|
||||
movb %al, %ds:(%si)
|
||||
|
||||
pop %ax
|
||||
movb %al, %es:(%di)
|
||||
|
||||
movw $0, %ax
|
||||
je .check_a20_done
|
||||
|
||||
movw $1, %ax
|
||||
|
||||
.check_a20_done:
|
||||
popw %es
|
||||
popw %ds
|
||||
popw %di
|
||||
popw %si
|
||||
popf
|
||||
ret
|
||||
|
||||
|
||||
# Try to enable A20 using PS2 controller
|
||||
enable_a20_ps2:
|
||||
pushf
|
||||
pushw %ax
|
||||
|
||||
cli
|
||||
|
||||
# disable first port
|
||||
call .enable_a20_ps2_wait1
|
||||
movb $0xAD, %al
|
||||
outb %al, $0x64
|
||||
|
||||
# read controller output
|
||||
call .enable_a20_ps2_wait1
|
||||
movb $0xD0, %al
|
||||
outb %al, $0x64
|
||||
|
||||
call .enable_a20_ps2_wait2
|
||||
inb $0x60, %al
|
||||
pushw %ax
|
||||
|
||||
# write controller output
|
||||
call .enable_a20_ps2_wait1
|
||||
movb $0xD1, %al
|
||||
outb %al, $0x64
|
||||
|
||||
call .enable_a20_ps2_wait1
|
||||
popw %ax
|
||||
orw $2, %ax
|
||||
outb %al, $0x60
|
||||
|
||||
# enable first port
|
||||
call .enable_a20_ps2_wait1
|
||||
movb $0xAE, %al
|
||||
outb %al, $0x64
|
||||
|
||||
call .enable_a20_ps2_wait1
|
||||
|
||||
popw %ax
|
||||
popf
|
||||
ret
|
||||
|
||||
.enable_a20_ps2_wait1:
|
||||
inb $0x64, %al
|
||||
test $2, %al
|
||||
jnz .enable_a20_ps2_wait1
|
||||
ret
|
||||
|
||||
.enable_a20_ps2_wait2:
|
||||
inb $0x64, %al
|
||||
test $1, %al
|
||||
jnz .enable_a20_ps2_wait1
|
||||
ret
|
||||
|
||||
|
||||
# Check if A20 line is disabled. If it is, try to enable it
|
||||
.global enable_a20
|
||||
enable_a20:
|
||||
pushw %ax
|
||||
pushw %si
|
||||
|
||||
call check_a20
|
||||
testw %ax, %ax
|
||||
jnz .enable_a20_done
|
||||
|
||||
movw $a20_line_disabled_msg, %si
|
||||
call puts; call print_newline
|
||||
|
||||
# Try to enable A20 line using bios interrupt
|
||||
movw $0x2401, %ax
|
||||
int $0x15
|
||||
call check_a20
|
||||
testw %ax, %ax
|
||||
jnz .enable_a20_done
|
||||
|
||||
# Try to enable A20 line using ps2 controller
|
||||
call enable_a20_ps2
|
||||
call check_a20
|
||||
testw %ax, %ax
|
||||
jnz .enable_a20_done
|
||||
|
||||
# Try to enable A20 line using fast A20 gate
|
||||
inb $0x92, %al
|
||||
testb $2, %al
|
||||
jnz .enable_a20_fast_done
|
||||
orb $2, %al
|
||||
outb %al, $0x92
|
||||
.enable_a20_fast_done:
|
||||
|
||||
call check_a20
|
||||
testw %ax, %ax
|
||||
jnz .enable_a20_done
|
||||
|
||||
movw $a20_could_not_enable_msg, %si
|
||||
call print_and_halt
|
||||
|
||||
.enable_a20_done:
|
||||
movw $a20_line_enabled_msg, %si
|
||||
call puts; call print_newline
|
||||
|
||||
popw %si
|
||||
popw %ax
|
||||
ret
|
||||
|
||||
.section .data
|
||||
|
||||
a20_line_disabled_msg:
|
||||
.asciz "A20 line disabled. Trying to enable it"
|
||||
|
||||
a20_line_enabled_msg:
|
||||
.asciz "A20 line enabled"
|
||||
|
||||
a20_could_not_enable_msg:
|
||||
.asciz "Could not enable A20 line"
|
||||
@@ -1,176 +0,0 @@
|
||||
.include "common.S"
|
||||
|
||||
.code16
|
||||
|
||||
#########################################
|
||||
#
|
||||
# STAGE 1 BOOTLOADER
|
||||
#
|
||||
# its sole purpose is to load stage2 from
|
||||
# bios boot partition
|
||||
#
|
||||
#########################################
|
||||
|
||||
.section .stage1
|
||||
|
||||
.global stage1_main
|
||||
stage1_main:
|
||||
# setup segments and stack
|
||||
xorw %ax, %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %ss
|
||||
movl $0x7C00, %esp
|
||||
|
||||
# save boot disk number
|
||||
call read_stage2_into_memory
|
||||
|
||||
jmp stage2_main
|
||||
|
||||
.global print_and_halt
|
||||
print_and_halt:
|
||||
call puts
|
||||
halt:
|
||||
hlt
|
||||
jmp halt
|
||||
|
||||
|
||||
#########################################
|
||||
#
|
||||
# STAGE 2 BOOTLOADER
|
||||
#
|
||||
#########################################
|
||||
|
||||
.section .stage2
|
||||
|
||||
stage2_main:
|
||||
# clear screen and enter 80x25 text mode
|
||||
movb $0x03, %al
|
||||
movb $0x00, %ah
|
||||
int $0x10
|
||||
|
||||
# print hello message
|
||||
movw $hello_msg, %si
|
||||
call puts; call print_newline
|
||||
|
||||
lgdt gdtr
|
||||
|
||||
call enter_unreal_mode
|
||||
movw $unreal_enter_msg, %si
|
||||
call puts; call print_newline
|
||||
|
||||
call enable_a20
|
||||
|
||||
call get_memory_map
|
||||
|
||||
call print_newline
|
||||
call read_user_command_line
|
||||
|
||||
call print_newline
|
||||
|
||||
movw $start_kernel_load_msg, %si
|
||||
call puts; call print_newline
|
||||
|
||||
call print_memory_map
|
||||
|
||||
call find_root_disk
|
||||
call find_root_partition
|
||||
|
||||
call print_root_partition_info
|
||||
call print_newline
|
||||
|
||||
call has_ext2_filesystem
|
||||
testb %al, %al
|
||||
jz print_and_halt
|
||||
|
||||
call ext2_find_kernel
|
||||
movl $ext2_inode_read_bytes, %esi
|
||||
|
||||
call elf_read_kernel_to_memory
|
||||
|
||||
call vesa_set_video_mode
|
||||
|
||||
cli
|
||||
|
||||
# kernel entry point
|
||||
movl %eax, %ecx
|
||||
|
||||
# setup kernel parameters
|
||||
movl $0xD3C60CFF, %eax
|
||||
movl $banan_boot_info, %ebx
|
||||
|
||||
# setup protected mode
|
||||
movl %cr0, %edx
|
||||
orb $1, %dl
|
||||
movl %edx, %cr0
|
||||
|
||||
# jump to protected mode
|
||||
ljmpl $GDT_CODE32, $protected_mode
|
||||
|
||||
.code32
|
||||
protected_mode:
|
||||
# setup protected mode segments
|
||||
movw $GDT_DATA32, %dx
|
||||
movw %dx, %ds
|
||||
movw %dx, %es
|
||||
movw %dx, %fs
|
||||
movw %dx, %gs
|
||||
movw %dx, %ss
|
||||
|
||||
# jump to kernel entry
|
||||
jmp *%ecx
|
||||
|
||||
|
||||
.code16
|
||||
enter_unreal_mode:
|
||||
cli
|
||||
pushw %ds
|
||||
|
||||
movl %cr0, %eax
|
||||
orb $1, %al
|
||||
movl %eax, %cr0
|
||||
ljmpl $GDT_CODE16, $.enter_unreal_mode_pmode
|
||||
|
||||
.enter_unreal_mode_pmode:
|
||||
movw $GDT_DATA32, %bx
|
||||
movw %bx, %ds
|
||||
|
||||
andb $0xFE, %al
|
||||
movl %eax, %cr0
|
||||
ljmpl $0x00, $.enter_unreal_mode_unreal
|
||||
|
||||
.enter_unreal_mode_unreal:
|
||||
popw %ds
|
||||
sti
|
||||
|
||||
ret
|
||||
|
||||
.section .data
|
||||
|
||||
hello_msg:
|
||||
.asciz "This is banan-os bootloader"
|
||||
|
||||
unreal_enter_msg:
|
||||
.asciz "Entered unreal mode"
|
||||
|
||||
start_kernel_load_msg:
|
||||
.asciz "Starting to load kernel"
|
||||
|
||||
gdt:
|
||||
.quad 0x0000000000000000
|
||||
.quad 0x008F9A000000FFFF # 16-bit code
|
||||
.quad 0x00CF92000000FFFF # 32-bit data
|
||||
.quad 0x00CF9A000000FFFF # 32-bit code
|
||||
gdtr:
|
||||
.short . - gdt - 1
|
||||
.long gdt
|
||||
|
||||
banan_boot_info:
|
||||
boot_command_line:
|
||||
.long command_line
|
||||
boot_framebuffer:
|
||||
.long framebuffer
|
||||
boot_memory_map:
|
||||
.long memory_map
|
||||
boot_kernel_paddr:
|
||||
.long 0
|
||||
@@ -1,87 +0,0 @@
|
||||
.code16
|
||||
|
||||
.section .stage2
|
||||
|
||||
# fills command line buffer
|
||||
# NO REGISTERS SAVED
|
||||
.global read_user_command_line
|
||||
read_user_command_line:
|
||||
# print initial command line
|
||||
movw $command_line_enter_msg, %si
|
||||
call puts
|
||||
movw $command_line_buffer, %si
|
||||
call puts
|
||||
|
||||
# prepare registers for input
|
||||
movw $command_line_enter_msg, %si
|
||||
movw $command_line_buffer, %di
|
||||
.read_user_command_line_goto_end:
|
||||
cmpb $0, (%di)
|
||||
jz .read_user_command_line_loop
|
||||
incw %di
|
||||
jmp .read_user_command_line_goto_end
|
||||
|
||||
.read_user_command_line_loop:
|
||||
call getc
|
||||
|
||||
cmpb $'\b', %al
|
||||
je .read_user_command_line_backspace
|
||||
cmpb $0x7F, %al
|
||||
je .read_user_command_line_backspace
|
||||
|
||||
# Not sure if some BIOSes return '\n' as enter, but check it just in case
|
||||
cmpb $'\r', %al
|
||||
je .read_user_command_line_done
|
||||
cmpb $'\n', %al
|
||||
je .read_user_command_line_done
|
||||
|
||||
pushw %ax
|
||||
|
||||
call isprint
|
||||
testb %al, %al
|
||||
jz .read_user_command_line_loop
|
||||
|
||||
popw %ax
|
||||
|
||||
# put byte to buffer
|
||||
movb %al, (%di)
|
||||
incw %di
|
||||
|
||||
# print byte
|
||||
call putc
|
||||
|
||||
jmp .read_user_command_line_loop
|
||||
|
||||
.read_user_command_line_backspace:
|
||||
# don't do anything if at the beginning
|
||||
cmpw $command_line_buffer, %di
|
||||
je .read_user_command_line_loop
|
||||
|
||||
# decrement buffer pointer
|
||||
decw %di
|
||||
|
||||
# erase byte in display
|
||||
call print_backspace
|
||||
|
||||
jmp .read_user_command_line_loop
|
||||
|
||||
.read_user_command_line_done:
|
||||
# null terminate command line
|
||||
movb $0, (%di)
|
||||
|
||||
call print_newline
|
||||
|
||||
ret
|
||||
|
||||
|
||||
.section .data
|
||||
|
||||
command_line_enter_msg:
|
||||
.asciz "cmdline: "
|
||||
|
||||
.global command_line
|
||||
command_line:
|
||||
# 100 character command line
|
||||
command_line_buffer:
|
||||
.ascii "root=/dev/sda2"
|
||||
.skip 100 - (. - command_line_buffer)
|
||||
@@ -1,3 +0,0 @@
|
||||
.set GDT_CODE16, 0x08
|
||||
.set GDT_DATA32, 0x10
|
||||
.set GDT_CODE32, 0x18
|
||||
@@ -1,518 +0,0 @@
|
||||
# FIXME: don't assume 512 byte sectors
|
||||
.set SECTOR_SIZE_SHIFT, 9
|
||||
.set SECTOR_SIZE, 1 << SECTOR_SIZE_SHIFT
|
||||
|
||||
.code16
|
||||
|
||||
.section .stage1
|
||||
|
||||
.global stage2_start
|
||||
.global stage2_end
|
||||
|
||||
# check that drive has int13 ext
|
||||
# dl: drive number
|
||||
# returns only if drive does have the extension
|
||||
drive_has_int13_ext:
|
||||
pusha
|
||||
|
||||
movb $0x41, %ah
|
||||
movw $0x55AA, %bx
|
||||
int $0x13
|
||||
jc .drive_has_int13_ext_no_int13_ext
|
||||
|
||||
popa
|
||||
ret
|
||||
|
||||
.drive_has_int13_ext_no_int13_ext:
|
||||
mov $no_int13_ext_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
|
||||
# read sectors from disk
|
||||
# bx:eax: lba start
|
||||
# cx: lba count (has to less than 0x80)
|
||||
# dl: drive number
|
||||
# ds:di: physical address
|
||||
# returns only on success
|
||||
.global read_from_disk
|
||||
read_from_disk:
|
||||
pusha
|
||||
|
||||
call drive_has_int13_ext
|
||||
|
||||
# prepare disk read packet
|
||||
movw $disk_address_packet, %si
|
||||
movb $0x10, 0x00(%si) # packet size
|
||||
movb $0x00, 0x01(%si) # always 0
|
||||
movw %cx, 0x02(%si) # lba count
|
||||
movw %di, 0x04(%si) # offset
|
||||
movw %ds, 0x06(%si) # segment
|
||||
movl %eax, 0x08(%si) # 32 bit lower lba
|
||||
movw %bx, 0x0C(%si) # 16 bit upper lba
|
||||
movw $0, 0x0E(%si) # zero
|
||||
|
||||
# issue read command
|
||||
mov $0x42, %ah
|
||||
int $0x13
|
||||
jc .read_from_disk_failed
|
||||
|
||||
popa
|
||||
ret
|
||||
|
||||
.read_from_disk_failed:
|
||||
mov $read_from_disk_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
|
||||
# Reads GPT header into gpt_header buffer
|
||||
# dl: drive number
|
||||
# return:
|
||||
# ax: 1 if has GPT header, 0 otherwise
|
||||
.global read_gpt_header
|
||||
read_gpt_header:
|
||||
pushw %bx
|
||||
pushw %cx
|
||||
pushw %di
|
||||
|
||||
xorw %bx, %bx
|
||||
movl $1, %eax
|
||||
movw $1, %cx
|
||||
movw $gpt_header, %di
|
||||
call read_from_disk
|
||||
|
||||
xorw %bx, %bx
|
||||
movw $1, %ax
|
||||
|
||||
# check if header starts with 'EFI PART'
|
||||
cmpl $0x20494645, (gpt_header + 0)
|
||||
cmovnew %bx, %ax
|
||||
cmpl $0x54524150, (gpt_header + 4)
|
||||
cmovnew %bx, %ax
|
||||
|
||||
popw %di
|
||||
popw %cx
|
||||
popw %bx
|
||||
ret
|
||||
|
||||
|
||||
# Find bios boot partition from boot drive
|
||||
# returns:
|
||||
# bx:eax: first lba
|
||||
# cx: sector count
|
||||
find_stage2_partition:
|
||||
# read boot disk GPT header
|
||||
movb (boot_disk_number), %dl
|
||||
call read_gpt_header
|
||||
|
||||
testb %al, %al
|
||||
jz .find_stage2_partition_not_gpt
|
||||
|
||||
# eax := entry_count
|
||||
movl (gpt_header + 80), %eax
|
||||
test %eax, %eax
|
||||
jz .find_stage2_partition_not_found
|
||||
|
||||
# edx:eax := eax * entry_size
|
||||
mull (gpt_header + 84)
|
||||
test %edx, %edx
|
||||
jnz .find_stage2_partition_too_big_entries
|
||||
|
||||
# FIXME: read one entry array section at a time
|
||||
|
||||
# sector count := (arr_size + SECTOR_SIZE - 1) / SECTOR_SIZE
|
||||
movl %eax, %ecx
|
||||
shrl $SECTOR_SIZE_SHIFT, %ecx
|
||||
|
||||
# start lba
|
||||
movl (gpt_header + 72), %eax
|
||||
movw (gpt_header + 76), %bx
|
||||
|
||||
movw $gpt_entry_data, %di
|
||||
movw $bios_boot_guid, %si
|
||||
movb (boot_disk_number), %dl
|
||||
|
||||
call read_from_disk
|
||||
|
||||
# NOTE: 'only' 0xFFFF partitions supported,
|
||||
# although read will fail with more than 0x80
|
||||
movw (gpt_header + 80), %cx
|
||||
|
||||
.find_stage2_partition_loop_gpt_entries:
|
||||
pushw %cx
|
||||
movw $16, %cx
|
||||
call memcmp
|
||||
popw %cx
|
||||
|
||||
testb %al, %al
|
||||
jnz .find_stage2_partition_found
|
||||
|
||||
# add entry size to entry pointer
|
||||
addw (gpt_header + 84), %di
|
||||
|
||||
loop .find_stage2_partition_loop_gpt_entries
|
||||
|
||||
# fall through to not found case
|
||||
.find_stage2_partition_not_found:
|
||||
movw $no_bios_boot_partition_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
.find_stage2_partition_not_gpt:
|
||||
movw $not_gpt_partition_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
.find_stage2_partition_too_big_entries:
|
||||
movw $too_gpt_big_entries_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
.find_stage2_partition_found:
|
||||
# first lba
|
||||
movl 32(%di), %eax
|
||||
movw 36(%di), %bx
|
||||
|
||||
# count := last lba - first lba + 1
|
||||
movl 40(%di), %ecx
|
||||
subl %eax, %ecx
|
||||
incl %ecx
|
||||
|
||||
ret
|
||||
|
||||
# reads stage2 into memory
|
||||
# dl: boot drive number
|
||||
# returns only on success
|
||||
.global read_stage2_into_memory
|
||||
read_stage2_into_memory:
|
||||
movb %dl, (boot_disk_number)
|
||||
|
||||
# push stage2 sector count
|
||||
movl $stage2_end, %eax
|
||||
subl $stage2_start, %eax
|
||||
addl $(SECTOR_SIZE - 1), %eax
|
||||
movl $SECTOR_SIZE, %ecx
|
||||
xorl %edx, %edx
|
||||
divl %ecx
|
||||
pushl %eax
|
||||
|
||||
call find_stage2_partition
|
||||
|
||||
movb (boot_disk_number), %dl
|
||||
popl %ecx # FIXME: validate that partition has enough sectors
|
||||
movw $stage2_start, %di
|
||||
call read_from_disk
|
||||
|
||||
ret
|
||||
|
||||
# 21686148-6449-6E6F-744E-656564454649
|
||||
.align 4
|
||||
bios_boot_guid:
|
||||
.long 0x21686148 # little endian
|
||||
.word 0x6449 # little endian
|
||||
.word 0x6E6F # little endian
|
||||
.word 0x4E74 # big endian
|
||||
.quad 0x494645646565 # big endian
|
||||
|
||||
boot_disk_number:
|
||||
.skip 1
|
||||
|
||||
read_from_disk_msg:
|
||||
.asciz "read error"
|
||||
|
||||
no_int13_ext_msg:
|
||||
.asciz "no INT13 ext"
|
||||
|
||||
no_bios_boot_partition_msg:
|
||||
.asciz "no bios boot"
|
||||
|
||||
too_gpt_big_entries_msg:
|
||||
.asciz "too big GPT array"
|
||||
|
||||
not_gpt_partition_msg:
|
||||
.asciz "not GPT"
|
||||
|
||||
|
||||
.section .stage2
|
||||
|
||||
# check if drive exists
|
||||
# dl: drive number
|
||||
# return:
|
||||
# al: 1 if disk is usable, 0 otherwise
|
||||
drive_exists:
|
||||
pusha
|
||||
|
||||
movb $0x48, %ah
|
||||
movw $disk_drive_parameters, %si
|
||||
movw $0x1A, (disk_drive_parameters) # set buffer size
|
||||
|
||||
int $0x13
|
||||
jc .drive_exists_nope
|
||||
|
||||
popa
|
||||
movb $1, %al
|
||||
ret
|
||||
|
||||
.drive_exists_nope:
|
||||
popa
|
||||
movb $0, %al
|
||||
ret
|
||||
|
||||
# find root disk and populate root_disk_drive_number field
|
||||
# NO REGISTERS SAVED
|
||||
.global find_root_disk
|
||||
find_root_disk:
|
||||
movb $0x80, %dl
|
||||
|
||||
.find_root_disk_loop:
|
||||
call drive_exists
|
||||
testb %al, %al
|
||||
jz .find_root_disk_not_found
|
||||
|
||||
# read GPT header
|
||||
xorw %bx, %bx
|
||||
movl $1, %eax
|
||||
movw $1, %cx
|
||||
movw $gpt_header, %di
|
||||
call read_from_disk
|
||||
|
||||
# confirm header (starts with 'EFI PART')
|
||||
cmpl $0x20494645, (gpt_header + 0)
|
||||
jne .find_root_disk_next_disk
|
||||
cmpl $0x54524150, (gpt_header + 4)
|
||||
jne .find_root_disk_next_disk
|
||||
|
||||
# compare disk GUID
|
||||
movw $root_disk_guid, %si
|
||||
movw $(gpt_header + 56), %di
|
||||
movw $16, %cx
|
||||
call memcmp
|
||||
testb %al, %al
|
||||
jz .find_root_disk_next_disk
|
||||
|
||||
movw $root_disk_found_msg, %si
|
||||
call puts; call print_newline
|
||||
|
||||
movb %dl, (root_disk_drive_number)
|
||||
ret
|
||||
|
||||
.find_root_disk_next_disk:
|
||||
incb %dl
|
||||
jmp .find_root_disk_loop
|
||||
|
||||
.find_root_disk_not_found:
|
||||
movw $root_disk_not_found_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
|
||||
# finds root partition from root disk
|
||||
# fills root_partition_entry data structure
|
||||
# NOTE: assumes GPT header is in `gpt_header`
|
||||
# NO REGISTERS SAVED
|
||||
# return:
|
||||
# dl: drive number
|
||||
# ecx: sector count (capped at 0xFFFFFFFF)
|
||||
# bx:eax: first sector
|
||||
.global find_root_partition
|
||||
find_root_partition:
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
subl $16, %esp
|
||||
|
||||
# esp + 0: 8 byte entry array lba
|
||||
movl (gpt_header + 72), %eax
|
||||
movl %eax, 0(%esp)
|
||||
movl (gpt_header + 76), %eax
|
||||
movl %eax, 4(%esp)
|
||||
# FIXME: check that bits 48-63 are zero
|
||||
|
||||
# esp + 8: 4 byte entries per sector
|
||||
xorl %edx, %edx
|
||||
movl $SECTOR_SIZE, %eax
|
||||
divl (gpt_header + 84)
|
||||
movl %eax, 8(%esp)
|
||||
|
||||
# esp + 12: 4 byte entries remaining
|
||||
movl (gpt_header + 80), %eax
|
||||
testl %eax, %eax
|
||||
jz .find_root_partition_not_found
|
||||
movl %eax, 12(%esp)
|
||||
|
||||
.find_root_partition_read_entry_section:
|
||||
movl 0(%esp), %eax
|
||||
movl 4(%esp), %ebx
|
||||
movw $1, %cx
|
||||
movb (root_disk_drive_number), %dl
|
||||
movw $sector_buffer, %di
|
||||
call read_from_disk
|
||||
|
||||
# ecx: min(entries per section, entries remaining)
|
||||
movl 8(%esp), %ecx
|
||||
cmpl 12(%esp), %ecx
|
||||
jae .find_root_partition_got_entry_count
|
||||
movl 12(%esp), %ecx
|
||||
|
||||
.find_root_partition_got_entry_count:
|
||||
# update entries remaining
|
||||
subl %ecx, 12(%esp)
|
||||
|
||||
# si: entry pointer
|
||||
movw $sector_buffer, %si
|
||||
|
||||
.find_root_partition_loop_entries:
|
||||
# temporarily save cx in dx
|
||||
movw %cx, %dx
|
||||
|
||||
# check that entry is used
|
||||
movw $16, %cx
|
||||
movw $zero_guid, %di
|
||||
call memcmp
|
||||
test %al, %al
|
||||
jnz .find_root_partition_next_entry
|
||||
|
||||
# compare entry guid to root guid
|
||||
movw $16, %cx
|
||||
addw $16, %si
|
||||
movw $root_partition_guid, %di
|
||||
call memcmp
|
||||
subw $16, %si
|
||||
|
||||
testb %al, %al
|
||||
jnz .find_root_partition_found
|
||||
|
||||
.find_root_partition_next_entry:
|
||||
|
||||
# restore cx
|
||||
movw %dx, %cx
|
||||
|
||||
# entry pointer += entry size
|
||||
addw (gpt_header + 84), %si
|
||||
loop .find_root_partition_loop_entries
|
||||
|
||||
# entry not found in this sector
|
||||
|
||||
# increment 8 byte entry array lba
|
||||
incl 0(%esp)
|
||||
adcl $0, 4(%esp)
|
||||
|
||||
# loop to read next section if entries remaining
|
||||
cmpl $0, 12(%esp)
|
||||
jnz .find_root_partition_read_entry_section
|
||||
|
||||
.find_root_partition_not_found:
|
||||
movw $root_partition_not_found_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
.find_root_partition_found:
|
||||
# copy entry to buffer
|
||||
movw $root_partition_entry, %di
|
||||
movw $128, %cx
|
||||
rep movsb
|
||||
|
||||
movw $root_partition_found_msg, %si
|
||||
call puts; call print_newline
|
||||
|
||||
# ebx:eax := last lba
|
||||
movl (root_partition_entry + 44), %ebx
|
||||
movl (root_partition_entry + 40), %eax
|
||||
|
||||
# ebx:eax -= first lba - 1
|
||||
subl (root_partition_entry + 36), %ebx
|
||||
movl (root_partition_entry + 32), %ecx
|
||||
decl %ecx
|
||||
subl %ecx, %eax
|
||||
sbbl $0, %ebx
|
||||
|
||||
# ecx: min(partition count, 0xFFFFFFFF)
|
||||
movl $0xFFFFFFFF, %edx
|
||||
movl %eax, %ecx
|
||||
testl %ebx, %ebx
|
||||
cmovnzl %edx, %ecx
|
||||
|
||||
# ebx:eax := first lba
|
||||
# FIXME: confirm ebx bits 16:31 are zero
|
||||
movl (root_partition_entry + 36), %ebx
|
||||
movl (root_partition_entry + 32), %eax
|
||||
|
||||
movb (root_disk_drive_number), %dl
|
||||
|
||||
leavel
|
||||
ret
|
||||
|
||||
|
||||
# print information about root partition
|
||||
.global print_root_partition_info
|
||||
print_root_partition_info:
|
||||
pushw %ax
|
||||
pushw %bx
|
||||
pushw %cx
|
||||
pushw %si
|
||||
|
||||
movw $root_partition_info_start_msg, %si
|
||||
call puts;
|
||||
|
||||
movw $16, %bx
|
||||
movw $2, %cx
|
||||
movw (root_partition_entry + 38), %ax; call print_number
|
||||
movw (root_partition_entry + 36), %ax; call print_number
|
||||
movw (root_partition_entry + 34), %ax; call print_number
|
||||
movw (root_partition_entry + 32), %ax; call print_number
|
||||
|
||||
movb $'-', %al; call putc
|
||||
movb $'>', %al; call putc
|
||||
|
||||
movw (root_partition_entry + 46), %ax; call print_number
|
||||
movw (root_partition_entry + 44), %ax; call print_number
|
||||
movw (root_partition_entry + 42), %ax; call print_number
|
||||
movw (root_partition_entry + 40), %ax; call print_number
|
||||
|
||||
call print_newline
|
||||
|
||||
popw %si
|
||||
popw %cx
|
||||
popw %bx
|
||||
popw %ax
|
||||
ret
|
||||
|
||||
.section .data
|
||||
|
||||
# These will be patched during bootloader installation
|
||||
root_disk_guid:
|
||||
.ascii "root disk guid "
|
||||
root_partition_guid:
|
||||
.ascii "root part guid "
|
||||
zero_guid:
|
||||
.skip 16, 0
|
||||
|
||||
root_disk_found_msg:
|
||||
.asciz "Root disk found!"
|
||||
root_disk_not_found_msg:
|
||||
.asciz "Root disk not found"
|
||||
|
||||
root_partition_found_msg:
|
||||
.asciz "Root partition found!"
|
||||
root_partition_not_found_msg:
|
||||
.asciz "Root partition not found"
|
||||
|
||||
root_partition_info_start_msg:
|
||||
.asciz "Root partition: "
|
||||
|
||||
.section .bss
|
||||
|
||||
.align SECTOR_SIZE
|
||||
gpt_header:
|
||||
.skip SECTOR_SIZE
|
||||
gpt_entry_data:
|
||||
.skip SECTOR_SIZE
|
||||
sector_buffer:
|
||||
.skip SECTOR_SIZE
|
||||
|
||||
disk_address_packet:
|
||||
.skip 16
|
||||
|
||||
disk_drive_parameters:
|
||||
.skip 0x1A
|
||||
.skip 2 # padding
|
||||
|
||||
root_disk_drive_number:
|
||||
.skip 1
|
||||
.skip 3 # padding
|
||||
|
||||
root_partition_entry:
|
||||
.skip 128
|
||||
@@ -1,320 +0,0 @@
|
||||
.set SECTOR_SIZE, 512
|
||||
|
||||
# file header field offsets
|
||||
.set e_type, 16
|
||||
.set e_machine, 18
|
||||
.set e_version, 20
|
||||
.set e_entry, 24
|
||||
|
||||
.set e32_phoff, 28
|
||||
.set e32_shoff, 32
|
||||
.set e32_flags, 36
|
||||
.set e32_ehsize, 40
|
||||
.set e32_phentsize, 42
|
||||
.set e32_phnum, 44
|
||||
.set e32_shentsize, 46
|
||||
.set e32_shnum, 48
|
||||
.set e32_shstrndx, 50
|
||||
|
||||
.set e64_phoff, 32
|
||||
.set e64_shoff, 40
|
||||
.set e64_flags, 48
|
||||
.set e64_ehsize, 52
|
||||
.set e64_phentsize, 54
|
||||
.set e64_phnum, 56
|
||||
.set e64_shentsize, 58
|
||||
.set e64_shnum, 60
|
||||
.set e64_shstrndx, 62
|
||||
|
||||
# e_ident offsets
|
||||
.set EI_CLASS, 4
|
||||
.set EI_DATA, 5
|
||||
.set EI_VERSION, 6
|
||||
|
||||
# e_ident constants
|
||||
.set ELFMAGIC, 0x464C457F
|
||||
.set ELFCLASS32, 1
|
||||
.set ELFCLASS64, 2
|
||||
.set ELFDATA2LSB, 1
|
||||
.set EV_CURRENT, 1
|
||||
|
||||
# e_type constants
|
||||
.set ET_EXEC, 2
|
||||
|
||||
# program header field offsets
|
||||
.set p_type, 0
|
||||
|
||||
.set p32_offset, 4
|
||||
.set p32_vaddr, 8
|
||||
.set p32_paddr, 12
|
||||
.set p32_filesz, 16
|
||||
.set p32_memsz, 20
|
||||
.set p32_flags, 24
|
||||
.set p32_align, 28
|
||||
|
||||
.set p64_flags, 4
|
||||
.set p64_offset, 8
|
||||
.set p64_vaddr, 16
|
||||
.set p64_paddr, 24
|
||||
.set p64_filesz, 32
|
||||
.set p64_memsz, 40
|
||||
.set p64_align, 48
|
||||
|
||||
# p_type constants
|
||||
.set PT_NULL, 0
|
||||
.set PT_LOAD, 1
|
||||
|
||||
# mask for entry point and segment loading
|
||||
.set LOAD_MASK, 0x07FFFFFF
|
||||
|
||||
.code16
|
||||
.section .stage2
|
||||
|
||||
# Validate file header stored in elf_file_header
|
||||
# returns only on success
|
||||
elf_validate_file_header:
|
||||
cmpl $ELFMAGIC, (elf_file_header)
|
||||
jne .elf_validate_file_header_invalid_magic
|
||||
|
||||
cmpb $ELFCLASS32, (elf_file_header + EI_CLASS)
|
||||
je .elf_validate_file_header_class_valid
|
||||
cmpb $ELFCLASS64, (elf_file_header + EI_CLASS)
|
||||
je .elf_validate_file_header_class_valid
|
||||
jmp .elf_validate_file_header_invalid_class
|
||||
.elf_validate_file_header_class_valid:
|
||||
|
||||
cmpb $ELFDATA2LSB, (elf_file_header + EI_DATA)
|
||||
jne .elf_validate_file_header_only_little_endian_supported
|
||||
|
||||
cmpb $EV_CURRENT, (elf_file_header + EI_VERSION)
|
||||
jne .elf_validate_file_header_not_current_version
|
||||
|
||||
cmpl $EV_CURRENT, (elf_file_header + e_version)
|
||||
jne .elf_validate_file_header_not_current_version
|
||||
|
||||
cmpw $ET_EXEC, (elf_file_header + e_type)
|
||||
jne .elf_validate_file_header_not_executable
|
||||
|
||||
ret
|
||||
|
||||
.elf_validate_file_header_invalid_magic:
|
||||
movw $elf_validate_file_header_invalid_magic_msg, %si
|
||||
jmp print_and_halt
|
||||
.elf_validate_file_header_invalid_class:
|
||||
movw $elf_validate_file_header_invalid_class_msg, %si
|
||||
jmp print_and_halt
|
||||
.elf_validate_file_header_only_little_endian_supported:
|
||||
movw $elf_validate_file_header_only_little_endian_supported_msg, %si
|
||||
jmp print_and_halt
|
||||
.elf_validate_file_header_not_current_version:
|
||||
movw $elf_validate_file_header_not_current_version_msg, %si
|
||||
jmp print_and_halt
|
||||
.elf_validate_file_header_not_executable:
|
||||
movw $elf_validate_file_header_not_executable_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
# reads memory specified by 32 bit elf_program_header to memory
|
||||
elf_read_program_header32_to_memory:
|
||||
pushal
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
|
||||
# memset p_filesz -> p_memsz to 0
|
||||
movl (elf_program_header + p32_filesz), %ebx
|
||||
movl (elf_program_header + p32_vaddr), %edi
|
||||
andl $LOAD_MASK, %edi
|
||||
addl %ebx, %edi
|
||||
movl (elf_program_header + p32_memsz), %ecx
|
||||
subl %ebx, %ecx
|
||||
xorb %al, %al; call memset32
|
||||
|
||||
# read file specified in program header to memory
|
||||
movl (elf_program_header + p32_offset), %eax
|
||||
movl (elf_program_header + p32_vaddr), %edi
|
||||
andl $LOAD_MASK, %edi
|
||||
movl (elf_program_header + p32_filesz), %ecx
|
||||
call *%esi
|
||||
|
||||
leavel
|
||||
popal
|
||||
ret
|
||||
|
||||
|
||||
# reads memory specified by 64 bit elf_program_header to memory
|
||||
elf_read_program_header64_to_memory:
|
||||
pushal
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
|
||||
# memset p_filesz -> p_memsz to 0
|
||||
movl (elf_program_header + p64_filesz), %ebx
|
||||
movl (elf_program_header + p64_vaddr), %edi
|
||||
andl $LOAD_MASK, %edi
|
||||
addl %ebx, %edi
|
||||
movl (elf_program_header + p64_memsz), %ecx
|
||||
subl %ebx, %ecx
|
||||
xorb %al, %al; call memset32
|
||||
|
||||
# read file specified in program header to memory
|
||||
movl (elf_program_header + p64_offset), %eax
|
||||
movl (elf_program_header + p64_vaddr), %edi
|
||||
andl $LOAD_MASK, %edi
|
||||
movl (elf_program_header + p64_filesz), %ecx
|
||||
call *%esi
|
||||
|
||||
leavel
|
||||
popal
|
||||
ret
|
||||
|
||||
|
||||
# read callback format
|
||||
# eax: first byte
|
||||
# ecx: byte count
|
||||
# edi: buffer
|
||||
# returns only on success
|
||||
|
||||
|
||||
# reads kernel to memory
|
||||
# esi: callback for reading from kernel image
|
||||
# return:
|
||||
# eax: kernel entry address
|
||||
.global elf_read_kernel_to_memory
|
||||
elf_read_kernel_to_memory:
|
||||
pushal
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
subl $2, %esp
|
||||
|
||||
# read start of file header
|
||||
movl $0, %eax
|
||||
movl $24, %ecx
|
||||
movl $elf_file_header, %edi
|
||||
call *%esi
|
||||
|
||||
call elf_validate_file_header
|
||||
|
||||
# determine file header size
|
||||
movl $52, %ecx
|
||||
movl $64, %edx
|
||||
cmpb $ELFCLASS64, (elf_file_header + EI_CLASS)
|
||||
cmovel %edx, %ecx
|
||||
|
||||
# read full file header
|
||||
movl $0, %eax
|
||||
movl $elf_file_header, %edi
|
||||
call *%esi
|
||||
|
||||
# verify that e_phoff fits in 32 bits
|
||||
cmpb $ELFCLASS64, (elf_file_header + EI_CLASS)
|
||||
jne .elf_read_kernel_to_memory_valid_offset
|
||||
cmpl $0, (elf_file_header + e64_phoff + 4)
|
||||
jnz .elf_read_kernel_to_memory_unsupported_offset
|
||||
.elf_read_kernel_to_memory_valid_offset:
|
||||
|
||||
# read architecture phentsize and phnum to fixed locations
|
||||
movw (elf_file_header + e32_phentsize), %ax
|
||||
movw (elf_file_header + e32_phnum), %bx
|
||||
movl (elf_file_header + e32_phoff), %ecx
|
||||
cmpb $ELFCLASS64, (elf_file_header + EI_CLASS)
|
||||
cmovew (elf_file_header + e64_phentsize), %ax
|
||||
cmovew (elf_file_header + e64_phnum), %bx
|
||||
cmovel (elf_file_header + e64_phoff), %ecx
|
||||
movw %ax, (elf_file_header_phentsize)
|
||||
movw %bx, (elf_file_header_phnum)
|
||||
movl %ecx, (elf_file_header_phoff)
|
||||
|
||||
# current program header
|
||||
movw $0, -2(%ebp)
|
||||
|
||||
.elf_read_kernel_to_memory_loop_program_headers:
|
||||
movw -2(%ebp), %cx
|
||||
cmpw (elf_file_header_phnum), %cx
|
||||
jae .elf_read_kernel_to_memory_done
|
||||
|
||||
# eax := program_header_index * e_phentsize + e_phoff
|
||||
xorl %eax, %eax
|
||||
movw %cx, %ax
|
||||
xorl %ebx, %ebx
|
||||
movw (elf_file_header_phentsize), %bx
|
||||
mull %ebx
|
||||
addl (elf_file_header_phoff), %eax
|
||||
jc .elf_read_kernel_to_memory_unsupported_offset
|
||||
|
||||
# determine program header size
|
||||
movl $32, %ecx
|
||||
movl $56, %edx
|
||||
cmpb $ELFCLASS64, (elf_file_header + EI_CLASS)
|
||||
cmovel %edx, %ecx
|
||||
|
||||
# read program header
|
||||
movl $elf_program_header, %edi
|
||||
call *%esi
|
||||
|
||||
# test if program header is NULL header
|
||||
cmpl $PT_NULL, (elf_program_header + p_type)
|
||||
je .elf_read_kernel_to_memory_null_program_header
|
||||
|
||||
# confirm that the program header is loadable
|
||||
cmpl $PT_LOAD, (elf_program_header + p_type)
|
||||
jne .elf_read_kernel_to_memory_not_loadable_header
|
||||
|
||||
# read program header to memory
|
||||
movl $elf_read_program_header32_to_memory, %eax
|
||||
movl $elf_read_program_header64_to_memory, %ebx
|
||||
cmpb $ELFCLASS64, (elf_file_header + EI_CLASS)
|
||||
cmovel %ebx, %eax
|
||||
call *%eax
|
||||
|
||||
.elf_read_kernel_to_memory_null_program_header:
|
||||
incw -2(%ebp)
|
||||
jmp .elf_read_kernel_to_memory_loop_program_headers
|
||||
|
||||
.elf_read_kernel_to_memory_done:
|
||||
leavel
|
||||
popal
|
||||
|
||||
# set kernel entry address
|
||||
movl (elf_file_header + e_entry), %eax
|
||||
andl $LOAD_MASK, %eax
|
||||
|
||||
ret
|
||||
|
||||
.elf_read_kernel_to_memory_unsupported_offset:
|
||||
movw $elf_read_kernel_to_memory_unsupported_offset_msg, %si
|
||||
jmp print_and_halt
|
||||
.elf_read_kernel_to_memory_not_loadable_header:
|
||||
movw $elf_read_kernel_to_memory_not_loadable_header_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
.section .data
|
||||
|
||||
elf_validate_file_header_invalid_magic_msg:
|
||||
.asciz "ELF: file has invalid ELF magic"
|
||||
elf_validate_file_header_invalid_class_msg:
|
||||
.asciz "ELF: file has invalid ELF class"
|
||||
elf_validate_file_header_only_little_endian_supported_msg:
|
||||
.asciz "ELF: file is not in little endian format"
|
||||
elf_validate_file_header_not_current_version_msg:
|
||||
.asciz "ELF: file is not in current ELF version"
|
||||
elf_validate_file_header_not_executable_msg:
|
||||
.asciz "ELF: file is not an executable"
|
||||
|
||||
elf_read_kernel_to_memory_unsupported_offset_msg:
|
||||
.asciz "ELF: unsupported offset (only 32 bit offsets supported)"
|
||||
elf_read_kernel_to_memory_not_loadable_header_msg:
|
||||
.asciz "ELF: kernel contains non-loadable program header"
|
||||
|
||||
.section .bss
|
||||
|
||||
elf_file_header:
|
||||
.skip 64
|
||||
|
||||
elf_file_header_phentsize:
|
||||
.skip 2
|
||||
elf_file_header_phnum:
|
||||
.skip 2
|
||||
elf_file_header_phoff:
|
||||
.skip 4 # NOTE: only 32 bit offsets are supported
|
||||
|
||||
elf_program_header:
|
||||
.skip 56
|
||||
@@ -1,749 +0,0 @@
|
||||
# FIXME: don't assume 512 byte sectors
|
||||
.set SECTOR_SHIFT, 9
|
||||
.set SECTOR_SIZE, 1 << SECTOR_SHIFT
|
||||
|
||||
.set EXT2_MAX_BLOCK_SIZE, 4096
|
||||
.set EXT2_SUPERBLOCK_SIZE, 264
|
||||
.set EXT2_BGD_SHIFT, 5
|
||||
.set EXT2_BGD_SIZE, 1 << EXT2_BGD_SHIFT
|
||||
.set EXT2_INODE_SIZE_MAX, 256
|
||||
.set EXT2_ROOT_INO, 2
|
||||
.set EXT2_GOOD_OLD_REV, 0
|
||||
|
||||
# inode types
|
||||
.set EXT2_S_IMASK, 0xF000
|
||||
.set EXT2_S_IFDIR, 0x4000
|
||||
.set EXT2_S_IFREG, 0x8000
|
||||
|
||||
# superblock offsets
|
||||
.set s_first_data_block, 20
|
||||
.set s_log_block_size, 24
|
||||
.set s_inodes_per_group, 40
|
||||
.set s_magic, 56
|
||||
.set s_rev_level, 76
|
||||
.set s_inode_size, 88
|
||||
|
||||
# block group descriptor offsets
|
||||
.set bg_inode_table, 8
|
||||
|
||||
# inode offsets
|
||||
.set i_mode, 0
|
||||
.set i_size, 4
|
||||
.set i_block, 40
|
||||
|
||||
|
||||
.code16
|
||||
.section .stage2
|
||||
|
||||
# checks whether partition contains ext2 filesystem.
|
||||
# fills ext2_superblock_buffer
|
||||
# dl: drive number
|
||||
# ecx: sector count
|
||||
# bx:eax: first sector
|
||||
# return:
|
||||
# al: 1 if is ext2, 0 otherwise
|
||||
# si: error message on error
|
||||
.global has_ext2_filesystem
|
||||
has_ext2_filesystem:
|
||||
pushl %ecx
|
||||
pushw %bx
|
||||
pushw %di
|
||||
|
||||
# fill ext2_partition_first_sector
|
||||
movw $0, (ext2_partition_first_sector + 6)
|
||||
movw %bx, (ext2_partition_first_sector + 4)
|
||||
movl %eax, (ext2_partition_first_sector + 0)
|
||||
|
||||
# fill ext2_drive_number
|
||||
movb %dl, (ext2_drive_number)
|
||||
|
||||
cmpl $3, %ecx
|
||||
jb .has_ext2_filesystem_does_not_fit
|
||||
|
||||
# one sector
|
||||
movw $1, %cx
|
||||
|
||||
# from byte offset 1024
|
||||
addl $(1024 / SECTOR_SIZE), %eax
|
||||
adcw $0, %bx
|
||||
|
||||
# into sector buffer
|
||||
movw $ext2_block_buffer, %di
|
||||
|
||||
call read_from_disk
|
||||
|
||||
# copy superblock to its buffer
|
||||
movw $ext2_block_buffer, %si
|
||||
movw $ext2_superblock_buffer, %di
|
||||
movw $EXT2_SUPERBLOCK_SIZE, %cx
|
||||
rep movsb
|
||||
|
||||
# verify magic
|
||||
cmpw $0xEF53, (ext2_superblock_buffer + s_magic)
|
||||
jne .has_ext2_filesystem_invalid_magic
|
||||
|
||||
# verify block size
|
||||
# verify shift fits in one byte
|
||||
movl (ext2_superblock_buffer + s_log_block_size), %ecx
|
||||
testl $0xFFFFFF00, %ecx
|
||||
jnz .has_ext2_filesystem_unsupported_block_size
|
||||
# verify 1024 << s_log_block_size <= EXT2_MAX_BLOCK_SIZE
|
||||
movl $1024, %eax
|
||||
shll %cl, %eax
|
||||
cmpl $EXT2_MAX_BLOCK_SIZE, %eax
|
||||
ja .has_ext2_filesystem_unsupported_block_size
|
||||
|
||||
# fill block size and shift
|
||||
movl %eax, (ext2_block_size)
|
||||
addl $10, %ecx
|
||||
movl %ecx, (ext2_block_shift)
|
||||
|
||||
# fill inode size
|
||||
movl $128, %eax
|
||||
cmpl $EXT2_GOOD_OLD_REV, (ext2_superblock_buffer + s_rev_level)
|
||||
cmovnel (ext2_superblock_buffer + s_inode_size), %eax
|
||||
movl %eax, (ext2_inode_size)
|
||||
|
||||
movb $1, %al
|
||||
jmp .has_ext2_filesystem_done
|
||||
|
||||
.has_ext2_filesystem_does_not_fit:
|
||||
movw $root_partition_does_not_fit_ext2_filesystem_msg, %si
|
||||
movb $0, %al
|
||||
jmp .has_ext2_filesystem_done
|
||||
|
||||
.has_ext2_filesystem_invalid_magic:
|
||||
movw $root_partition_has_invalid_ext2_magic_msg, %si
|
||||
movb $0, %al
|
||||
jmp .has_ext2_filesystem_done
|
||||
|
||||
.has_ext2_filesystem_unsupported_block_size:
|
||||
movw $root_partition_has_unsupported_ext2_block_size_msg, %si
|
||||
movb $0, %al
|
||||
jmp .has_ext2_filesystem_done
|
||||
|
||||
.has_ext2_filesystem_done:
|
||||
popw %di
|
||||
popw %bx
|
||||
popl %ecx
|
||||
ret
|
||||
|
||||
|
||||
# reads block in to ext2_block_buffer
|
||||
# eax: block number
|
||||
ext2_read_block:
|
||||
pushal
|
||||
|
||||
# ecx := sectors_per_block := block_size / sector_size
|
||||
movl (ext2_block_size), %ecx
|
||||
shrl $SECTOR_SHIFT, %ecx
|
||||
|
||||
# ebx:eax := block * sectors_per_block + (ext2_partition_first_sector)
|
||||
xorl %ebx, %ebx
|
||||
mull %ecx
|
||||
addl (ext2_partition_first_sector + 0), %eax
|
||||
adcl (ext2_partition_first_sector + 4), %ebx
|
||||
|
||||
movw $ext2_block_buffer, %di
|
||||
movb (ext2_drive_number), %dl
|
||||
call read_from_disk
|
||||
|
||||
popal
|
||||
ret
|
||||
|
||||
|
||||
# reads block group descrtiptor into ext2_block_group_descriptor
|
||||
# eax: block group
|
||||
ext2_read_block_group_descriptor:
|
||||
pushal
|
||||
|
||||
# ebx := bgd_block_byte_offset := (s_first_data_block + 1) * block_size
|
||||
# := (s_first_data_block + 1) << ext2_block_shift
|
||||
movl (ext2_superblock_buffer + s_first_data_block), %ebx
|
||||
incl %ebx
|
||||
movb (ext2_block_shift), %cl
|
||||
shll %cl, %ebx
|
||||
|
||||
# eax := bgd_byte_offset := bgd_block_byte_offset + EXT2_BGD_SIZE * block_group;
|
||||
# := bgd_block_byte_offset + (block_group << EXT2_BGD_SHIFT)
|
||||
movb $EXT2_BGD_SHIFT, %cl
|
||||
shll %cl, %eax
|
||||
addl %ebx, %eax
|
||||
|
||||
# eax: bgd_block := bgd_byte_offset / block_size
|
||||
# ebx: bgd_offset := bgd_byte_offset % block_size
|
||||
xorl %edx, %edx
|
||||
divl (ext2_block_size)
|
||||
movl %edx, %ebx
|
||||
|
||||
call ext2_read_block
|
||||
|
||||
# esi := &ext2_block_buffer + bgd_offset := ebx + &ext2_block_buffer
|
||||
# edi := &ext2_block_group_descriptor_buffer
|
||||
movl %ebx, %esi
|
||||
addl $ext2_block_buffer, %esi
|
||||
movl $ext2_block_group_descriptor_buffer, %edi
|
||||
movw $EXT2_BGD_SIZE, %cx
|
||||
rep movsb
|
||||
|
||||
popal
|
||||
ret
|
||||
|
||||
|
||||
# reads inode into ext2_inode_buffer
|
||||
# eax: ino
|
||||
ext2_read_inode:
|
||||
pushal
|
||||
|
||||
# eax := block_group = (ino - 1) / s_inodes_per_group
|
||||
# ebx := inode_index = (ino - 1) % s_inodes_per_group
|
||||
xorl %edx, %edx
|
||||
decl %eax
|
||||
divl (ext2_superblock_buffer + s_inodes_per_group)
|
||||
movl %edx, %ebx
|
||||
|
||||
call ext2_read_block_group_descriptor
|
||||
|
||||
# eax := inode_table_block := (inode_index * inode_size) / block_size
|
||||
# ebx := inode_table_offset := (inode_index * inode_size) % block_size
|
||||
movl %ebx, %eax
|
||||
mull (ext2_inode_size)
|
||||
divl (ext2_block_size)
|
||||
movl %edx, %ebx
|
||||
|
||||
# eax := filesystem_block := eax + bg_inode_table
|
||||
addl (ext2_block_group_descriptor_buffer + bg_inode_table), %eax
|
||||
|
||||
movb (ext2_drive_number), %dl
|
||||
call ext2_read_block
|
||||
|
||||
# copy inode memory
|
||||
# esi := inode_table_offset + ext2_block_buffer := edx + ext2_block_buffer
|
||||
movl %ebx, %esi
|
||||
addl $ext2_block_buffer, %esi
|
||||
# edi := ext2_inode_buffer
|
||||
movl $ext2_inode_buffer, %edi
|
||||
# ecx := inode_size
|
||||
movl (ext2_inode_size), %ecx
|
||||
rep movsb
|
||||
|
||||
# reset indirect cache to zero
|
||||
movl $0, (ext2_inode_indirect_number)
|
||||
|
||||
.ext2_read_inode_done:
|
||||
popal
|
||||
ret
|
||||
|
||||
|
||||
# gets block index from n'th data block in inode stored in ext2_inode_buffer
|
||||
# eax: data block index
|
||||
# return:
|
||||
# eax: block index
|
||||
ext2_data_block_index:
|
||||
pushl %ebx
|
||||
pushl %ecx
|
||||
pushl %edx
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
|
||||
# ebx := max_data_blocks := (file_size + block_size - 1) / block_size
|
||||
# := (i_size + ext2_block_size - 1) >> ext2_block_shift
|
||||
# cl := ext2_block_shift
|
||||
movl (ext2_inode_buffer + i_size), %ebx
|
||||
addl (ext2_block_size), %ebx
|
||||
decl %ebx
|
||||
movb (ext2_block_shift), %cl
|
||||
shrl %cl, %ebx
|
||||
|
||||
# verify data block is within bounds
|
||||
cmpl %ebx, %eax
|
||||
jae .ext2_data_block_index_out_of_bounds
|
||||
|
||||
# check if this is direct block access
|
||||
cmpl $12, %eax
|
||||
jb .ext2_data_block_index_direct
|
||||
subl $12, %eax
|
||||
|
||||
# cl := indices_per_block_shift := ext2_block_shift - 2
|
||||
# ebx := comp
|
||||
subb $2, %cl
|
||||
movl $1, %ebx
|
||||
shll %cl, %ebx
|
||||
|
||||
# check if this is singly indirect block access
|
||||
cmpl %ebx, %eax
|
||||
jb .ext2_data_block_index_singly_indirect
|
||||
subl %ebx, %eax
|
||||
shll %cl, %ebx
|
||||
|
||||
# check if this is doubly indirect block access
|
||||
cmpl %ebx, %eax
|
||||
jb .ext2_data_block_index_doubly_indirect
|
||||
subl %ebx, %eax
|
||||
shll %cl, %ebx
|
||||
|
||||
# check if this is triply indirect block access
|
||||
cmpl %ebx, %eax
|
||||
jb .ext2_data_block_index_triply_indirect
|
||||
|
||||
# otherwise this is invalid access
|
||||
jmp .ext2_data_block_index_invalid
|
||||
|
||||
.ext2_data_block_index_direct:
|
||||
movl $(ext2_inode_buffer + i_block), %esi
|
||||
movl (%esi, %eax, 4), %eax
|
||||
jmp .ext2_data_block_index_done
|
||||
|
||||
.ext2_data_block_index_singly_indirect:
|
||||
movl %eax, %ebx
|
||||
movl (ext2_inode_buffer + i_block + 12 * 4), %eax
|
||||
movw $1, %cx
|
||||
jmp .ext2_data_block_index_indirect
|
||||
|
||||
.ext2_data_block_index_doubly_indirect:
|
||||
movl %eax, %ebx
|
||||
movl (ext2_inode_buffer + i_block + 13 * 4), %eax
|
||||
movw $2, %cx
|
||||
jmp .ext2_data_block_index_indirect
|
||||
|
||||
.ext2_data_block_index_triply_indirect:
|
||||
movl %eax, %ebx
|
||||
movl (ext2_inode_buffer + i_block + 14 * 4), %eax
|
||||
movw $3, %cx
|
||||
jmp .ext2_data_block_index_indirect
|
||||
|
||||
# eax := current block
|
||||
# ebx := index
|
||||
# cx := depth
|
||||
.ext2_data_block_index_indirect:
|
||||
# edx := cache index := (index & ~(block_size / 4 - 1)) | depth
|
||||
# := (index & -(block_size >> 2)) | depth
|
||||
movl (ext2_block_size), %edx
|
||||
shrl $2, %edx
|
||||
negl %edx
|
||||
andl %ebx, %edx
|
||||
orw %cx, %dx
|
||||
|
||||
# check whether this block is already cached
|
||||
cmpl $0, (ext2_inode_indirect_number)
|
||||
je .ext2_data_block_index_indirect_no_cache
|
||||
cmpl %edx, (ext2_inode_indirect_number)
|
||||
je .ext2_data_block_index_indirect_cached
|
||||
|
||||
.ext2_data_block_index_indirect_no_cache:
|
||||
# update cache block number, will be cached when found
|
||||
movl %edx, (ext2_inode_indirect_number)
|
||||
|
||||
# eax := current block
|
||||
# ebx := index
|
||||
# cx := depth
|
||||
.ext2_data_block_index_indirect_loop:
|
||||
call ext2_read_block
|
||||
|
||||
# store depth and index
|
||||
pushw %cx
|
||||
pushl %ebx
|
||||
|
||||
cmpw $1, %cx
|
||||
jbe .ext2_data_block_index_no_shift
|
||||
|
||||
# cl := shift
|
||||
movb (ext2_block_shift), %al
|
||||
subb $2, %al
|
||||
decb %cl
|
||||
mulb %cl
|
||||
movb %al, %cl
|
||||
|
||||
# ebx := ebx >> shift
|
||||
shrl %cl, %ebx
|
||||
|
||||
.ext2_data_block_index_no_shift:
|
||||
# edx := index of next block (ebx & (block_size / 4 - 1))
|
||||
movl (ext2_block_size), %edx
|
||||
shrl $2, %edx
|
||||
decl %edx
|
||||
andl %ebx, %edx
|
||||
|
||||
# eax := next block
|
||||
movl $ext2_block_buffer, %esi
|
||||
movl (%esi, %edx, 4), %eax
|
||||
|
||||
# restore depth and index
|
||||
popl %ebx
|
||||
popw %cx
|
||||
|
||||
loop .ext2_data_block_index_indirect_loop
|
||||
|
||||
# cache last read block
|
||||
movw $ext2_block_buffer, %si
|
||||
movw $ext2_inode_indirect_buffer, %di
|
||||
movw (ext2_block_size), %cx
|
||||
rep movsb
|
||||
|
||||
jmp .ext2_data_block_index_done
|
||||
|
||||
.ext2_data_block_index_out_of_bounds:
|
||||
movw $ext2_data_block_index_out_of_bounds_msg, %si
|
||||
call puts; call print_newline
|
||||
movl $0, %eax
|
||||
jmp .ext2_data_block_index_done
|
||||
|
||||
.ext2_data_block_index_invalid:
|
||||
movw $ext2_data_block_index_invalid_msg, %si
|
||||
call puts; call print_newline
|
||||
movl $0, %eax
|
||||
jmp .ext2_data_block_index_done
|
||||
|
||||
.ext2_data_block_index_indirect_cached:
|
||||
movl $ext2_inode_indirect_buffer, %esi
|
||||
movl (ext2_block_size), %edx
|
||||
shrl $2, %edx
|
||||
decl %edx
|
||||
andl %edx, %ebx
|
||||
movl (%esi, %ebx, 4), %eax
|
||||
|
||||
.ext2_data_block_index_done:
|
||||
popl %edi
|
||||
popl %esi
|
||||
popl %edx
|
||||
popl %ecx
|
||||
popl %ebx
|
||||
ret
|
||||
|
||||
|
||||
# read bytes from inode (implements read callback)
|
||||
# eax: first byte
|
||||
# ecx: byte count
|
||||
# edi: buffer
|
||||
# returns only on success
|
||||
.global ext2_inode_read_bytes
|
||||
ext2_inode_read_bytes:
|
||||
pushal
|
||||
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
subl $8, %esp
|
||||
|
||||
# save read info
|
||||
movl %eax, 0(%esp)
|
||||
movl %ecx, 4(%esp)
|
||||
|
||||
# eax := first_byte / block_size
|
||||
# edx := first_byte % block_size
|
||||
# when edx == 0, no partial read needed
|
||||
xorl %edx, %edx
|
||||
divl (ext2_block_size)
|
||||
testl %edx, %edx
|
||||
jz .ext2_inode_read_bytes_no_partial_start
|
||||
|
||||
# get data block index and read block
|
||||
call ext2_data_block_index
|
||||
call ext2_read_block
|
||||
|
||||
# ecx := byte count (min(block_size - edx, remaining_bytes))
|
||||
movl (ext2_block_size), %ecx
|
||||
subl %edx, %ecx
|
||||
cmpl %ecx, 4(%esp)
|
||||
cmovbl 4(%esp), %ecx
|
||||
|
||||
# update remaining read info
|
||||
addl %ecx, 0(%esp)
|
||||
subl %ecx, 4(%esp)
|
||||
|
||||
# esi := start sector data (block_buffer + index * SECTOR_SIZE)
|
||||
movl $ext2_block_buffer, %esi
|
||||
addl %edx, %esi
|
||||
|
||||
call memcpy32
|
||||
|
||||
# check if all sectors are read
|
||||
cmpl $0, 4(%esp)
|
||||
je .ext2_inode_read_bytes_done
|
||||
|
||||
.ext2_inode_read_bytes_no_partial_start:
|
||||
# eax := data block index (byte_start / block_size)
|
||||
movl 0(%esp), %eax
|
||||
movb (ext2_block_shift), %cl
|
||||
shrl %cl, %eax
|
||||
|
||||
# get data block index and read block
|
||||
call ext2_data_block_index
|
||||
call ext2_read_block
|
||||
|
||||
# calculate bytes to copy (min(block_size, remaining_bytes))
|
||||
movl (ext2_block_size), %ecx
|
||||
cmpl %ecx, 4(%esp)
|
||||
cmovbl 4(%esp), %ecx
|
||||
|
||||
# update remaining read info
|
||||
addl %ecx, 0(%esp)
|
||||
subl %ecx, 4(%esp)
|
||||
|
||||
movl $ext2_block_buffer, %esi
|
||||
call memcpy32
|
||||
|
||||
# read next block if more sectors remaining
|
||||
cmpl $0, 4(%esp)
|
||||
jnz .ext2_inode_read_bytes_no_partial_start
|
||||
|
||||
.ext2_inode_read_bytes_done:
|
||||
leavel
|
||||
popal
|
||||
ret
|
||||
|
||||
|
||||
# find inode in inside directory inode stored in ext2_inode_buffer
|
||||
# store the found inode in ext2_inode_buffer
|
||||
# si: name string
|
||||
# cx: name length
|
||||
# return:
|
||||
# eax: ino if inode was found, 0 otherwise
|
||||
ext2_directory_find_inode:
|
||||
pushl %ebx
|
||||
pushw %cx
|
||||
pushw %dx
|
||||
pushw %si
|
||||
pushw %di
|
||||
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
subl $8, %esp
|
||||
|
||||
# 0(%esp) := name length
|
||||
movw %cx, 0(%esp)
|
||||
|
||||
# 2(%esp) := name string
|
||||
movw %si, 2(%esp)
|
||||
|
||||
# verify that the name is <= 0xFF bytes
|
||||
cmpw $0xFF, %cx
|
||||
ja .ext2_directory_find_inode_not_found
|
||||
|
||||
# ebx := max data blocks: ceil(i_size / block_size)
|
||||
movl (ext2_inode_buffer + i_size), %ebx
|
||||
addl (ext2_block_size), %ebx
|
||||
decl %ebx
|
||||
movb (ext2_block_shift), %cl
|
||||
shrl %cl, %ebx
|
||||
jz .ext2_directory_find_inode_not_found
|
||||
|
||||
# 4(%esp) := current block
|
||||
movl $0, 4(%esp)
|
||||
|
||||
.ext2_directory_find_inode_block_read_loop:
|
||||
# get next block index
|
||||
movl 4(%esp), %eax
|
||||
call ext2_data_block_index
|
||||
test %eax, %eax
|
||||
jz .ext2_directory_find_inode_next_block
|
||||
|
||||
# read current block
|
||||
call ext2_read_block
|
||||
|
||||
# dx := current entry pointer
|
||||
movw $ext2_block_buffer, %si
|
||||
|
||||
.ext2_directory_find_inode_loop_entries:
|
||||
# temporarily store entry pointer in dx
|
||||
movw %si, %dx
|
||||
|
||||
# check if name length matches
|
||||
# cx := name length
|
||||
movw 0(%esp), %cx
|
||||
cmpb 6(%si), %cl
|
||||
jne .ext2_directory_find_inode_next_entry
|
||||
|
||||
# si := entry name
|
||||
addw $8, %si
|
||||
|
||||
# di := asked name
|
||||
movw 2(%esp), %di
|
||||
|
||||
# check if name matches
|
||||
call memcmp
|
||||
test %al, %al
|
||||
# NOTE: dx contains entry pointer
|
||||
jnz .ext2_directory_find_inode_found
|
||||
|
||||
.ext2_directory_find_inode_next_entry:
|
||||
# restore si
|
||||
movw %dx, %si
|
||||
|
||||
# go to next entry if this block contains one
|
||||
addw 4(%si), %si
|
||||
movw $ext2_block_buffer, %di
|
||||
addw (ext2_block_size), %di
|
||||
cmpw %di, %si
|
||||
jb .ext2_directory_find_inode_loop_entries
|
||||
|
||||
.ext2_directory_find_inode_next_block:
|
||||
incl 4(%esp)
|
||||
cmpl %ebx, 4(%esp)
|
||||
jb .ext2_directory_find_inode_block_read_loop
|
||||
|
||||
.ext2_directory_find_inode_not_found:
|
||||
xorb %al, %al
|
||||
jmp .ext2_directory_find_inode_done
|
||||
|
||||
.ext2_directory_find_inode_found:
|
||||
# extract ino and read it to ext2_inode_buffer
|
||||
movw %dx, %si
|
||||
movl 0(%si), %eax
|
||||
call ext2_read_inode
|
||||
|
||||
.ext2_directory_find_inode_done:
|
||||
leavel
|
||||
popw %di
|
||||
popw %si
|
||||
popw %dx
|
||||
popw %cx
|
||||
popl %ebx
|
||||
ret
|
||||
|
||||
|
||||
# search for kernel file from filesystem
|
||||
# returns only on success
|
||||
.global ext2_find_kernel
|
||||
ext2_find_kernel:
|
||||
pushl %eax
|
||||
pushw %cx
|
||||
pushw %di
|
||||
pushw %si
|
||||
|
||||
movl $EXT2_ROOT_INO, %eax
|
||||
call ext2_read_inode
|
||||
|
||||
movw $kernel_path, %di
|
||||
.ext2_find_kernel_loop:
|
||||
movw (%di), %si
|
||||
|
||||
# check if this list is done
|
||||
testw %si, %si
|
||||
jz .ext2_find_kernel_loop_done
|
||||
|
||||
# check that current part is directory
|
||||
movw (ext2_inode_buffer + i_mode), %ax
|
||||
andw $EXT2_S_IMASK, %ax
|
||||
cmpw $EXT2_S_IFDIR, %ax
|
||||
jne .ext2_find_kernel_part_not_dir
|
||||
|
||||
# prepare registers for directory finding
|
||||
movw 0(%si), %cx
|
||||
addw $2, %si
|
||||
|
||||
# print search path
|
||||
pushw %si
|
||||
movw $ext2_looking_for_msg, %si
|
||||
call puts
|
||||
popw %si
|
||||
call puts; call print_newline
|
||||
|
||||
# search current directory for this file
|
||||
call ext2_directory_find_inode
|
||||
testl %eax, %eax
|
||||
jz .ext2_find_kernel_part_not_found
|
||||
|
||||
# loop to next part
|
||||
addw $2, %di
|
||||
jmp .ext2_find_kernel_loop
|
||||
|
||||
.ext2_find_kernel_loop_done:
|
||||
|
||||
# check that kernel is a regular file
|
||||
movw (ext2_inode_buffer + i_mode), %ax
|
||||
andw $EXT2_S_IMASK, %ax
|
||||
cmpw $EXT2_S_IFREG, %ax
|
||||
jne .ext2_find_kernel_not_reg
|
||||
|
||||
movw $ext2_kernel_found_msg, %si
|
||||
call puts; call print_newline
|
||||
|
||||
popw %si
|
||||
popw %di
|
||||
popw %cx
|
||||
popl %eax
|
||||
ret
|
||||
|
||||
.ext2_find_kernel_part_not_dir:
|
||||
movw $ext2_part_not_dir_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
.ext2_find_kernel_part_not_found:
|
||||
movw $ext2_part_not_found_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
.ext2_find_kernel_not_reg:
|
||||
movw $ext2_kernel_not_reg_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
.section .data
|
||||
|
||||
kernel_path:
|
||||
.short kernel_path1
|
||||
.short kernel_path2
|
||||
.short 0
|
||||
kernel_path1:
|
||||
.short 4
|
||||
.asciz "boot"
|
||||
kernel_path2:
|
||||
.short 15
|
||||
.asciz "banan-os.kernel"
|
||||
|
||||
root_partition_does_not_fit_ext2_filesystem_msg:
|
||||
.asciz "Root partition is too small to contain ext2 filesystem"
|
||||
root_partition_has_invalid_ext2_magic_msg:
|
||||
.asciz "Root partition doesn't contain ext2 magic number"
|
||||
root_partition_has_unsupported_ext2_block_size_msg:
|
||||
.asciz "Root partition has unsupported ext2 block size (1 KiB, 2 KiB and 4 KiB are supported)"
|
||||
|
||||
ext2_part_not_dir_msg:
|
||||
.asciz "inode in root path is not directory"
|
||||
ext2_part_not_found_msg:
|
||||
.asciz " not found"
|
||||
ext2_kernel_not_reg_msg:
|
||||
.asciz "kernel is not a regular file"
|
||||
ext2_kernel_found_msg:
|
||||
.asciz "kernel found!"
|
||||
|
||||
ext2_data_block_index_out_of_bounds_msg:
|
||||
.asciz "data block index out of bounds"
|
||||
ext2_data_block_index_invalid_msg:
|
||||
.asciz "data block index is invalid"
|
||||
|
||||
ext2_looking_for_msg:
|
||||
.asciz "looking for "
|
||||
|
||||
.section .bss
|
||||
|
||||
.align SECTOR_SIZE
|
||||
ext2_block_buffer:
|
||||
.skip EXT2_MAX_BLOCK_SIZE
|
||||
|
||||
ext2_inode_indirect_buffer:
|
||||
.skip EXT2_MAX_BLOCK_SIZE
|
||||
ext2_inode_indirect_number:
|
||||
.skip 4
|
||||
|
||||
ext2_partition_first_sector:
|
||||
.skip 8
|
||||
|
||||
ext2_drive_number:
|
||||
.skip 1
|
||||
.skip 3 # padding
|
||||
|
||||
# NOTE: fits in 2 bytes
|
||||
ext2_inode_size:
|
||||
.skip 4
|
||||
ext2_block_size:
|
||||
.skip 4
|
||||
ext2_block_shift:
|
||||
.skip 4
|
||||
|
||||
ext2_superblock_buffer:
|
||||
.skip EXT2_SUPERBLOCK_SIZE
|
||||
|
||||
ext2_block_group_descriptor_buffer:
|
||||
.skip EXT2_BGD_SIZE
|
||||
|
||||
ext2_inode_buffer:
|
||||
.skip EXT2_INODE_SIZE_MAX
|
||||
@@ -1,218 +0,0 @@
|
||||
.code16
|
||||
.section .stage2
|
||||
|
||||
# kernel framebuffer information format
|
||||
# .align 8
|
||||
# .long 0xBABAB007
|
||||
# .long -(0xBABAB007 + width + height + bpp)
|
||||
# .long width (2 bytes used, 4 bytes for ease of calculation)
|
||||
# .long height (2 bytes used, 4 bytes for ease of calculation)
|
||||
# .long bpp (1 bytes used, 4 bytes for ease of calculation)
|
||||
|
||||
# scan memory 0x100000 -> 0x200000 for framebuffer information
|
||||
# return:
|
||||
# ax: target width
|
||||
# bx: target height
|
||||
# cx: target bpp
|
||||
vesa_scan_kernel_image:
|
||||
pushl %edx
|
||||
pushl %esi
|
||||
|
||||
movl $0x100000, %esi
|
||||
|
||||
.vesa_scan_kernel_image_loop:
|
||||
# check magic
|
||||
cmpl $0xBABAB007, (%esi)
|
||||
jne .vesa_scan_kernel_image_next_addr
|
||||
|
||||
# check checksum
|
||||
movl 0x00(%esi), %edx
|
||||
addl 0x04(%esi), %edx
|
||||
addl 0x08(%esi), %edx
|
||||
addl 0x0C(%esi), %edx
|
||||
addl 0x10(%esi), %edx
|
||||
testl %edx, %edx
|
||||
jnz .vesa_scan_kernel_image_next_addr
|
||||
|
||||
# set return registers
|
||||
movw 0x08(%esi), %ax
|
||||
movw 0x0C(%esi), %bx
|
||||
movw 0x10(%esi), %cx
|
||||
jmp .vesa_scan_kernel_image_done
|
||||
|
||||
.vesa_scan_kernel_image_next_addr:
|
||||
addl $8, %esi
|
||||
cmpl $0x200000, %esi
|
||||
jb .vesa_scan_kernel_image_loop
|
||||
|
||||
# zero out return registers
|
||||
xorw %ax, %ax
|
||||
xorw %bx, %bx
|
||||
xorw %cx, %cx
|
||||
|
||||
.vesa_scan_kernel_image_done:
|
||||
popl %esi
|
||||
popl %edx
|
||||
ret
|
||||
|
||||
# Find suitable video mode and save it in (vesa_target_mode)
|
||||
vesa_find_video_mode:
|
||||
pushal
|
||||
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
subl $6, %esp
|
||||
|
||||
# clear target mode and frame buffer
|
||||
movw $0, (vesa_target_mode)
|
||||
movl $0, (framebuffer + 0)
|
||||
movl $0, (framebuffer + 4)
|
||||
movl $0, (framebuffer + 8)
|
||||
movl $0, (framebuffer + 12)
|
||||
movw $0, (framebuffer + 16)
|
||||
|
||||
call vesa_scan_kernel_image
|
||||
testw %ax, %ax
|
||||
jz .vesa_find_video_mode_loop_modes_done
|
||||
|
||||
# save arguments in stack
|
||||
movw %ax, -2(%ebp)
|
||||
movw %bx, -4(%ebp)
|
||||
movw %cx, -6(%ebp)
|
||||
|
||||
# get vesa information
|
||||
movw $0x4F00, %ax
|
||||
movw $vesa_info_buffer, %di
|
||||
pushl %ebp; int $0x10; popl %ebp # BOCHS doesn't seem to reserve ebp
|
||||
cmpb $0x4F, %al; jne .vesa_unsupported
|
||||
cmpb $0x00, %ah; jne .vesa_error
|
||||
|
||||
# confirm that response starts with 'VESA'
|
||||
cmpl $0x41534556, (vesa_info_buffer)
|
||||
jne .vesa_error
|
||||
|
||||
# confirm that version is atleast 2.0
|
||||
cmpw $0x0200, (vesa_info_buffer + 0x04)
|
||||
jb .vesa_unsupported_version
|
||||
|
||||
movl (vesa_info_buffer + 0x0E), %esi
|
||||
.vesa_find_video_mode_loop_modes:
|
||||
cmpw $0xFFFF, (%esi)
|
||||
je .vesa_find_video_mode_loop_modes_done
|
||||
|
||||
# get info of next mode
|
||||
movw $0x4F01, %ax
|
||||
movw (%esi), %cx
|
||||
movw $vesa_mode_info_buffer, %di
|
||||
pushl %ebp; int $0x10; popl %ebp # BOCHS doesn't seem to reserve ebp
|
||||
cmpb $0x4F, %al; jne .vesa_unsupported
|
||||
cmpb $0x00, %ah; jne .vesa_error
|
||||
|
||||
# check whether in graphics mode
|
||||
testb $0x10, (vesa_mode_info_buffer + 0)
|
||||
jz .vesa_find_video_mode_next_mode
|
||||
|
||||
# compare mode's dimensions
|
||||
movw -2(%ebp), %ax; cmpw %ax, (vesa_mode_info_buffer + 0x12)
|
||||
jne .vesa_find_video_mode_next_mode
|
||||
movw -4(%ebp), %ax; cmpw %ax, (vesa_mode_info_buffer + 0x14)
|
||||
jne .vesa_find_video_mode_next_mode
|
||||
movb -6(%ebp), %al; cmpb %al, (vesa_mode_info_buffer + 0x19)
|
||||
jne .vesa_find_video_mode_next_mode
|
||||
|
||||
# set address, pitch, type
|
||||
movl (vesa_mode_info_buffer + 0x28), %esi
|
||||
movl %esi, (framebuffer + 0)
|
||||
movw (vesa_mode_info_buffer + 0x10), %ax
|
||||
movw %ax, (framebuffer + 4)
|
||||
movb $1, (framebuffer + 17)
|
||||
|
||||
# set width, height, bpp
|
||||
movw -2(%ebp), %ax; movw %ax, (framebuffer + 8)
|
||||
movw -4(%ebp), %ax; movw %ax, (framebuffer + 12)
|
||||
movw -6(%ebp), %ax; movb %al, (framebuffer + 16)
|
||||
|
||||
movw %cx, (vesa_target_mode)
|
||||
jmp .vesa_find_video_mode_loop_modes_done
|
||||
|
||||
.vesa_find_video_mode_next_mode:
|
||||
addl $2, %esi
|
||||
jmp .vesa_find_video_mode_loop_modes
|
||||
|
||||
.vesa_find_video_mode_loop_modes_done:
|
||||
leavel
|
||||
popal
|
||||
ret
|
||||
|
||||
.vesa_unsupported:
|
||||
movw $vesa_unsupported_msg, %si
|
||||
jmp print_and_halt
|
||||
.vesa_unsupported_version:
|
||||
movw $vesa_unsupported_version_msg, %si
|
||||
jmp print_and_halt
|
||||
.vesa_error:
|
||||
movw $vesa_error_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
|
||||
# scan for video mode in kernel memory and set the correct one.
|
||||
# when video mode is not found or does not exists,
|
||||
# set it to 80x25 text mode to clear the screen.
|
||||
.global vesa_set_video_mode
|
||||
vesa_set_video_mode:
|
||||
pushw %ax
|
||||
pushw %bx
|
||||
|
||||
call vesa_find_video_mode
|
||||
|
||||
movw (vesa_target_mode), %bx
|
||||
testw %bx, %bx
|
||||
jz .vesa_set_target_mode_generic
|
||||
|
||||
movw $0x4F02, %ax
|
||||
orw $0x4000, %bx
|
||||
pushl %ebp; int $0x10; popl %ebp # BOCHS doesn't seem to reserve ebp
|
||||
|
||||
jmp .set_video_done
|
||||
|
||||
.vesa_set_target_mode_generic:
|
||||
movb $0x03, %al
|
||||
movb $0x00, %ah
|
||||
pushl %ebp; int $0x10; popl %ebp # BOCHS doesn't seem to reserve ebp
|
||||
|
||||
.set_video_done:
|
||||
popw %bx
|
||||
popw %ax
|
||||
ret
|
||||
|
||||
.section .data
|
||||
|
||||
vesa_error_msg:
|
||||
.asciz "VESA error"
|
||||
vesa_unsupported_msg:
|
||||
.asciz "VESA unsupported"
|
||||
vesa_unsupported_version_msg:
|
||||
.asciz "VESA unsupported version"
|
||||
vesa_success_msg:
|
||||
.asciz "VESA success"
|
||||
|
||||
.section .bss
|
||||
|
||||
vesa_info_buffer:
|
||||
.skip 512
|
||||
|
||||
vesa_mode_info_buffer:
|
||||
.skip 256
|
||||
|
||||
vesa_target_mode:
|
||||
.skip 2
|
||||
|
||||
.global framebuffer
|
||||
.align 8
|
||||
framebuffer:
|
||||
.skip 4 # address
|
||||
.skip 4 # pitch
|
||||
.skip 4 # width
|
||||
.skip 4 # height
|
||||
.skip 1 # bpp
|
||||
.skip 1 # type
|
||||
@@ -1,17 +0,0 @@
|
||||
ENTRY(stage1_main)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x7C00;
|
||||
.stage1 : { *(.stage1) }
|
||||
|
||||
. = ALIGN(512);
|
||||
stage2_start = .;
|
||||
.stage2 : { *(.stage2) }
|
||||
. = ALIGN(512);
|
||||
.data : { *(.data) }
|
||||
stage2_end = .;
|
||||
|
||||
. = ALIGN(512);
|
||||
.bss : { *(.bss) }
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
.code16
|
||||
|
||||
.section .stage2
|
||||
|
||||
# fills memory map data structure
|
||||
# doesn't return on error
|
||||
# NO REGISTERS SAVED
|
||||
.global get_memory_map
|
||||
get_memory_map:
|
||||
movl $0, (memory_map_entry_count)
|
||||
|
||||
movl $0x0000E820, %eax
|
||||
movl $0x534D4150, %edx
|
||||
xorl %ebx, %ebx
|
||||
movl $20, %ecx
|
||||
movw $memory_map_entries, %di
|
||||
|
||||
clc
|
||||
int $0x15
|
||||
# If first call returs with CF set, the call failed
|
||||
jc .get_memory_map_error
|
||||
|
||||
.get_memory_map_rest:
|
||||
cmpl $0x534D4150, %eax
|
||||
jne .get_memory_map_error
|
||||
|
||||
# FIXME: don't assume BIOS to always return 20 bytes
|
||||
cmpl $20, %ecx
|
||||
jne .get_memory_map_error
|
||||
|
||||
# increment entry count
|
||||
incl (memory_map_entry_count)
|
||||
|
||||
# increment entry pointer
|
||||
addw %cx, %di
|
||||
|
||||
# BIOS can indicate end of list by 0 in ebx
|
||||
testl %ebx, %ebx
|
||||
jz .get_memory_map_done
|
||||
|
||||
movl $0x0000E820, %eax
|
||||
movl $0x534D4150, %edx
|
||||
|
||||
clc
|
||||
int $0x15
|
||||
# BIOS can indicate end of list by setting CF
|
||||
jnc .get_memory_map_rest
|
||||
|
||||
.get_memory_map_done:
|
||||
ret
|
||||
|
||||
.get_memory_map_error:
|
||||
movw $memory_map_error_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
|
||||
# print memory map from memory_map_entries
|
||||
# NO REGISTERS SAVED
|
||||
.global print_memory_map
|
||||
print_memory_map:
|
||||
movw $memory_map_msg, %si
|
||||
call puts
|
||||
call print_newline
|
||||
|
||||
movl (memory_map_entry_count), %edx
|
||||
movw $memory_map_entries, %si
|
||||
|
||||
movw $16, %bx
|
||||
movw $4, %cx
|
||||
|
||||
.loop_memory_map:
|
||||
movb $' ', %al
|
||||
call putc; call putc; call putc; call putc
|
||||
|
||||
movw 0x06(%si), %ax
|
||||
call print_number
|
||||
movw 0x04(%si), %ax
|
||||
call print_number
|
||||
movw 0x02(%si), %ax
|
||||
call print_number
|
||||
movw 0x00(%si), %ax
|
||||
call print_number
|
||||
|
||||
movb $',', %al
|
||||
call putc
|
||||
movb $' ', %al
|
||||
call putc
|
||||
|
||||
movw 0x0E(%si), %ax
|
||||
call print_number
|
||||
movw 0x0C(%si), %ax
|
||||
call print_number
|
||||
movw 0x0A(%si), %ax
|
||||
call print_number
|
||||
movw 0x08(%si), %ax
|
||||
call print_number
|
||||
|
||||
movb $',', %al
|
||||
call putc
|
||||
movb $' ', %al
|
||||
call putc
|
||||
|
||||
movw 0x12(%si), %ax
|
||||
call print_number
|
||||
movw 0x10(%si), %ax
|
||||
call print_number
|
||||
|
||||
call print_newline
|
||||
|
||||
addw $20, %si
|
||||
|
||||
decl %edx
|
||||
jnz .loop_memory_map
|
||||
|
||||
ret
|
||||
|
||||
.section .data
|
||||
|
||||
memory_map_msg:
|
||||
.asciz "memmap:"
|
||||
memory_map_error_msg:
|
||||
.asciz "Failed to get memory map"
|
||||
|
||||
.section .bss
|
||||
|
||||
.global memory_map
|
||||
memory_map:
|
||||
memory_map_entry_count:
|
||||
.skip 4
|
||||
# 100 entries should be enough...
|
||||
memory_map_entries:
|
||||
.skip 20 * 100
|
||||
@@ -1,385 +0,0 @@
|
||||
.include "common.S"
|
||||
|
||||
.set SCREEN_WIDTH, 80
|
||||
.set SCREEN_HEIGHT, 25
|
||||
|
||||
.code16
|
||||
|
||||
.section .stage1
|
||||
|
||||
# prints character to screen
|
||||
# al: ascii character to print
|
||||
.global putc
|
||||
putc:
|
||||
pushw %ax
|
||||
pushw %bx
|
||||
movb $0x0E, %ah
|
||||
xorb %bh, %bh
|
||||
int $0x10
|
||||
popw %bx
|
||||
popw %ax
|
||||
ret
|
||||
|
||||
# prints null terminated string to screen
|
||||
# ds:si: string address
|
||||
.global puts
|
||||
puts:
|
||||
pushw %si
|
||||
pushw %bx
|
||||
pushw %ax
|
||||
|
||||
movb $0x0E, %ah
|
||||
xorb %bh, %bh
|
||||
|
||||
.puts_loop:
|
||||
lodsb
|
||||
|
||||
test %al, %al
|
||||
jz .puts_done
|
||||
|
||||
int $0x10
|
||||
jmp .puts_loop
|
||||
|
||||
.puts_done:
|
||||
popw %ax
|
||||
popw %bx
|
||||
popw %si
|
||||
ret
|
||||
|
||||
# compares memory between addresses
|
||||
# si: ptr1
|
||||
# di: ptr2
|
||||
# cx: bytes count
|
||||
# return:
|
||||
# al: 1 if equal, 0 otherwise
|
||||
.global memcmp
|
||||
memcmp:
|
||||
# NOTE: using pusha + popa to save space
|
||||
pusha
|
||||
cld
|
||||
repe cmpsb
|
||||
popa
|
||||
setzb %al
|
||||
ret
|
||||
|
||||
|
||||
.section .stage2
|
||||
|
||||
# read a character from keyboard
|
||||
# return:
|
||||
# al: ascii
|
||||
# ah: bios scan code
|
||||
.global getc
|
||||
getc:
|
||||
movb $0x00, %ah
|
||||
int $0x16
|
||||
ret
|
||||
|
||||
# prints newline to screen
|
||||
.global print_newline
|
||||
print_newline:
|
||||
pushw %ax
|
||||
movb $'\r', %al
|
||||
call putc
|
||||
movb $'\n', %al
|
||||
call putc
|
||||
pop %ax
|
||||
ret
|
||||
|
||||
# prints backspace to screen, can go back a line
|
||||
.global print_backspace
|
||||
print_backspace:
|
||||
pushw %ax
|
||||
pushw %bx
|
||||
pushw %cx
|
||||
pushw %dx
|
||||
|
||||
# get cursor position
|
||||
movb $0x03, %ah
|
||||
movb $0x00, %bh
|
||||
int $0x10
|
||||
|
||||
# don't do anyting if on first row
|
||||
testb %dh, %dh
|
||||
jz .print_backspace_done
|
||||
|
||||
# go one line up if on first column
|
||||
test %dl, %dl
|
||||
jz .print_backspace_go_line_up
|
||||
|
||||
# otherwise decrease column
|
||||
decb %dl
|
||||
jmp .print_backspace_do_print
|
||||
|
||||
.print_backspace_go_line_up:
|
||||
# decrease row and set column to the last one
|
||||
decb %dh
|
||||
movb $(SCREEN_WIDTH - 1), %dl
|
||||
|
||||
.print_backspace_do_print:
|
||||
# set cursor position
|
||||
movb $0x02, %ah
|
||||
int $0x10
|
||||
|
||||
# print 'empty' character (space)
|
||||
mov $' ', %al
|
||||
call putc
|
||||
|
||||
# set cursor position
|
||||
movb $0x02, %ah
|
||||
int $0x10
|
||||
|
||||
.print_backspace_done:
|
||||
popw %dx
|
||||
popw %cx
|
||||
popw %bx
|
||||
popw %ax
|
||||
ret
|
||||
|
||||
# print number to screen
|
||||
# ax: number to print
|
||||
# bx: number base
|
||||
# cx: min width (zero pads if shorter)
|
||||
.global print_number
|
||||
print_number:
|
||||
pusha
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
|
||||
# save min width
|
||||
subl $4, %esp
|
||||
movw %cx, (%esp)
|
||||
|
||||
movw $print_number_buffer, %si
|
||||
xorw %cx, %cx
|
||||
|
||||
.print_number_fill_loop:
|
||||
# fill buffer with all remainders ax % bx
|
||||
xorw %dx, %dx
|
||||
divw %bx
|
||||
movb %dl, (%si)
|
||||
incw %si
|
||||
incw %cx
|
||||
testw %ax, %ax
|
||||
jnz .print_number_fill_loop
|
||||
|
||||
# check if zero pad is required
|
||||
cmpw (%esp), %cx
|
||||
jae .print_number_print_loop
|
||||
|
||||
# dx: saved number count
|
||||
# cx: zero pad count
|
||||
movw %cx, %dx
|
||||
movw (%esp), %cx
|
||||
subw %dx, %cx
|
||||
movb $'0', %al
|
||||
|
||||
.print_number_pad_zeroes:
|
||||
call putc
|
||||
loop .print_number_pad_zeroes
|
||||
|
||||
# restore number count
|
||||
movw %dx, %cx
|
||||
|
||||
.print_number_print_loop:
|
||||
decw %si
|
||||
movb (%si), %al
|
||||
cmpb $10, %al
|
||||
jae .print_number_hex
|
||||
addb $'0', %al
|
||||
jmp .print_number_do_print
|
||||
.print_number_hex:
|
||||
addb $('a' - 10), %al
|
||||
.print_number_do_print:
|
||||
call putc
|
||||
loop .print_number_print_loop
|
||||
|
||||
leavel
|
||||
popa
|
||||
ret
|
||||
|
||||
# prints 8 bit hexadecimal number to screen
|
||||
# al: number to print
|
||||
.global print_hex8
|
||||
print_hex8:
|
||||
pushw %ax
|
||||
pushw %bx
|
||||
pushw %cx
|
||||
|
||||
movw $16, %bx
|
||||
movw $2, %cx
|
||||
andw $0xFF, %ax
|
||||
call print_number
|
||||
|
||||
popw %cx
|
||||
popw %bx
|
||||
popw %ax
|
||||
ret
|
||||
|
||||
# prints 16 bit hexadecimal number to screen
|
||||
# ax: number to print
|
||||
.global print_hex16
|
||||
print_hex16:
|
||||
pushw %bx
|
||||
pushw %cx
|
||||
|
||||
movw $16, %bx
|
||||
movw $4, %cx
|
||||
call print_number
|
||||
|
||||
popw %cx
|
||||
popw %bx
|
||||
ret
|
||||
|
||||
# prints 32 bit hexadecimal number to screen
|
||||
# eax: number to print
|
||||
.global print_hex32
|
||||
print_hex32:
|
||||
pushl %eax
|
||||
pushw %dx
|
||||
|
||||
movw %ax, %dx
|
||||
|
||||
shrl $16, %eax;
|
||||
call print_hex16
|
||||
|
||||
movw %dx, %ax
|
||||
call print_hex16
|
||||
|
||||
popw %dx
|
||||
popl %eax
|
||||
ret
|
||||
|
||||
# prints 64 bit hexadecimal number to screen
|
||||
# edx:eax: number to print
|
||||
.global print_hex64
|
||||
print_hex64:
|
||||
xchgl %eax, %edx
|
||||
call print_hex32
|
||||
xchgl %eax, %edx
|
||||
call print_hex32
|
||||
ret
|
||||
|
||||
# test if character is printable ascii
|
||||
# al: character to test
|
||||
# return:
|
||||
# al: 1 if is printable, 0 otherwise
|
||||
.global isprint
|
||||
isprint:
|
||||
subb $0x20, %al
|
||||
cmpb $(0x7E - 0x20), %al
|
||||
ja .isprint_not_printable
|
||||
movb $1, %al
|
||||
ret
|
||||
.isprint_not_printable:
|
||||
movb $0, %al
|
||||
ret
|
||||
|
||||
|
||||
# memset with 32 bit registers
|
||||
# edi: destination address
|
||||
# ecx: bytes count
|
||||
# al: value to set
|
||||
# return:
|
||||
# edi: destination address + bytes count
|
||||
# ecx: 0
|
||||
# other: preserved
|
||||
.global memset32
|
||||
memset32:
|
||||
testl %ecx, %ecx
|
||||
jz .memset32_done
|
||||
|
||||
pushf; cli
|
||||
pushw %es
|
||||
pushl %eax
|
||||
pushl %ebx
|
||||
pushl %edx
|
||||
|
||||
movl %cr0, %ebx
|
||||
orb $1, %bl
|
||||
movl %ebx, %cr0
|
||||
|
||||
ljmpl $GDT_CODE32, $.memset32_pmode32
|
||||
|
||||
.code32
|
||||
.memset32_pmode32:
|
||||
movw $GDT_DATA32, %dx
|
||||
movw %dx, %es
|
||||
|
||||
rep stosb %es:(%edi)
|
||||
|
||||
ljmpl $GDT_CODE16, $.memset32_pmode16
|
||||
|
||||
.code16
|
||||
.memset32_pmode16:
|
||||
andb $0xFE, %bl
|
||||
movl %ebx, %cr0
|
||||
ljmpl $0x00, $.memset32_rmode16
|
||||
|
||||
.memset32_rmode16:
|
||||
popl %edx
|
||||
popl %ebx
|
||||
popl %eax
|
||||
popw %es
|
||||
popf
|
||||
|
||||
.memset32_done:
|
||||
ret
|
||||
|
||||
# memcpy with 32 bit registers
|
||||
# esi: source address
|
||||
# edi: destination address
|
||||
# ecx: bytes count
|
||||
# return:
|
||||
# esi: source address + bytes count
|
||||
# edi: destination address + bytes count
|
||||
# ecx: 0
|
||||
# other: preserved
|
||||
.global memcpy32
|
||||
memcpy32:
|
||||
testl %ecx, %ecx
|
||||
jz .memcpy32_done
|
||||
|
||||
pushf; cli
|
||||
pushw %ds
|
||||
pushw %es
|
||||
pushl %ebx
|
||||
pushl %edx
|
||||
|
||||
movl %cr0, %ebx
|
||||
orb $1, %bl
|
||||
movl %ebx, %cr0
|
||||
|
||||
ljmpl $GDT_CODE32, $.memcpy32_pmode32
|
||||
|
||||
.code32
|
||||
.memcpy32_pmode32:
|
||||
movw $GDT_DATA32, %dx
|
||||
movw %dx, %ds
|
||||
movw %dx, %es
|
||||
|
||||
rep movsb %ds:(%esi), %es:(%edi)
|
||||
|
||||
ljmpl $GDT_CODE16, $.memcpy32_pmode16
|
||||
|
||||
.code16
|
||||
.memcpy32_pmode16:
|
||||
andb $0xFE, %bl
|
||||
movl %ebx, %cr0
|
||||
ljmpl $0x00, $.memcpy32_rmode16
|
||||
|
||||
.memcpy32_rmode16:
|
||||
popl %edx
|
||||
popl %ebx
|
||||
popw %es
|
||||
popw %ds
|
||||
popf
|
||||
|
||||
.memcpy32_done:
|
||||
ret
|
||||
|
||||
.section .bss
|
||||
|
||||
# enough for base 2 printing
|
||||
print_number_buffer:
|
||||
.skip 16
|
||||
1
bootloader/installer/.gitignore
vendored
1
bootloader/installer/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
build/
|
||||
@@ -1,22 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
if (NOT DEFINED ENV{BANAN_ARCH})
|
||||
message(FATAL_ERROR "environment variable BANAN_ARCH not defined")
|
||||
endif ()
|
||||
set(BANAN_ARCH $ENV{BANAN_ARCH})
|
||||
|
||||
project(banan_os-bootloader-installer CXX)
|
||||
|
||||
set(SOURCES
|
||||
crc32.cpp
|
||||
ELF.cpp
|
||||
GPT.cpp
|
||||
GUID.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
||||
add_executable(banan_os-bootloader-installer ${SOURCES})
|
||||
target_compile_options(banan_os-bootloader-installer PRIVATE -O2 -std=c++20)
|
||||
target_compile_definitions(banan_os-bootloader-installer PRIVATE __arch=${BANAN_ARCH})
|
||||
target_include_directories(banan_os-bootloader-installer PRIVATE ${CMAKE_SOURCE_DIR}/../../userspace/libraries/LibELF/include)
|
||||
target_include_directories(banan_os-bootloader-installer PRIVATE ${CMAKE_SOURCE_DIR}/../../kernel/include)
|
||||
@@ -1,142 +0,0 @@
|
||||
#include "ELF.h"
|
||||
|
||||
#include <LibELF/Values.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <iostream>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace LibELF;
|
||||
|
||||
ELFFile::ELFFile(std::string_view path)
|
||||
: m_path(path)
|
||||
{
|
||||
m_fd = open(m_path.c_str(), O_RDONLY);
|
||||
if (m_fd == -1)
|
||||
{
|
||||
std::cerr << "Could not open '" << m_path << "': " << std::strerror(errno) << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (fstat(m_fd, &m_stat) == -1)
|
||||
{
|
||||
std::cerr << "Could not stat '" << m_path << "': " << std::strerror(errno) << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
void* mmap_addr = mmap(nullptr, m_stat.st_size, PROT_READ, MAP_PRIVATE, m_fd, 0);
|
||||
if (mmap_addr == MAP_FAILED)
|
||||
{
|
||||
std::cerr << "Could not mmap '" << m_path << "': " << std::strerror(errno) << std::endl;
|
||||
return;
|
||||
}
|
||||
m_mmap = reinterpret_cast<uint8_t*>(mmap_addr);
|
||||
|
||||
if (!validate_elf_header())
|
||||
return;
|
||||
|
||||
m_success = true;
|
||||
}
|
||||
|
||||
ELFFile::~ELFFile()
|
||||
{
|
||||
if (m_mmap)
|
||||
munmap(m_mmap, m_stat.st_size);
|
||||
m_mmap = nullptr;
|
||||
|
||||
if (m_fd != -1)
|
||||
close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
|
||||
const ElfNativeFileHeader& ELFFile::elf_header() const
|
||||
{
|
||||
return *reinterpret_cast<LibELF::ElfNativeFileHeader*>(m_mmap);
|
||||
}
|
||||
|
||||
bool ELFFile::validate_elf_header() const
|
||||
{
|
||||
if (m_stat.st_size < sizeof(ElfNativeFileHeader))
|
||||
{
|
||||
std::cerr << m_path << " is too small to be a ELF executable" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& elf_header = this->elf_header();
|
||||
|
||||
if (
|
||||
elf_header.e_ident[EI_MAG0] != ELFMAG0 ||
|
||||
elf_header.e_ident[EI_MAG1] != ELFMAG1 ||
|
||||
elf_header.e_ident[EI_MAG2] != ELFMAG2 ||
|
||||
elf_header.e_ident[EI_MAG3] != ELFMAG3
|
||||
)
|
||||
{
|
||||
std::cerr << m_path << " doesn't have an ELF magic number" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
#if ARCH(x86_64)
|
||||
if (elf_header.e_ident[EI_CLASS] != ELFCLASS64)
|
||||
#elif ARCH(i686)
|
||||
if (elf_header.e_ident[EI_CLASS] != ELFCLASS32)
|
||||
#endif
|
||||
{
|
||||
std::cerr << m_path << " architecture doesn't match" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elf_header.e_ident[EI_DATA] != ELFDATA2LSB)
|
||||
{
|
||||
std::cerr << m_path << " is not in little endian format" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elf_header.e_ident[EI_VERSION] != EV_CURRENT)
|
||||
{
|
||||
std::cerr << m_path << " has unsupported version" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elf_header.e_type != ET_EXEC)
|
||||
{
|
||||
std::cerr << m_path << " is not an executable ELF file" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const ElfNativeSectionHeader& ELFFile::section_header(std::size_t index) const
|
||||
{
|
||||
const auto& elf_header = this->elf_header();
|
||||
assert(index < elf_header.e_shnum);
|
||||
const uint8_t* section_array_start = m_mmap + elf_header.e_shoff;
|
||||
return *reinterpret_cast<const ElfNativeSectionHeader*>(section_array_start + index * elf_header.e_shentsize);
|
||||
}
|
||||
|
||||
std::string_view ELFFile::section_name(const ElfNativeSectionHeader& section_header) const
|
||||
{
|
||||
const auto& elf_header = this->elf_header();
|
||||
assert(elf_header.e_shstrndx != SHN_UNDEF);
|
||||
const auto& section_string_table = this->section_header(elf_header.e_shstrndx);
|
||||
const char* string_table_start = reinterpret_cast<const char*>(m_mmap + section_string_table.sh_offset);
|
||||
return string_table_start + section_header.sh_name;
|
||||
}
|
||||
|
||||
std::optional<std::span<const uint8_t>> ELFFile::find_section(std::string_view name) const
|
||||
{
|
||||
const auto& elf_header = this->elf_header();
|
||||
for (std::size_t i = 0; i < elf_header.e_shnum; i++)
|
||||
{
|
||||
const auto& section_header = this->section_header(i);
|
||||
auto section_name = this->section_name(section_header);
|
||||
if (section_name != name)
|
||||
continue;
|
||||
return std::span<const uint8_t>(m_mmap + section_header.sh_offset, section_header.sh_size);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <LibELF/Types.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
|
||||
class ELFFile
|
||||
{
|
||||
public:
|
||||
ELFFile(std::string_view path);
|
||||
~ELFFile();
|
||||
|
||||
const LibELF::ElfNativeFileHeader& elf_header() const;
|
||||
std::optional<std::span<const uint8_t>> find_section(std::string_view name) const;
|
||||
|
||||
bool success() const { return m_success; }
|
||||
|
||||
std::string_view path() const { return m_path; }
|
||||
|
||||
private:
|
||||
const LibELF::ElfNativeSectionHeader& section_header(std::size_t index) const;
|
||||
std::string_view section_name(const LibELF::ElfNativeSectionHeader&) const;
|
||||
bool validate_elf_header() const;
|
||||
|
||||
private:
|
||||
const std::string m_path;
|
||||
bool m_success { false };
|
||||
int m_fd { -1 };
|
||||
struct stat m_stat { };
|
||||
uint8_t* m_mmap { nullptr };
|
||||
};
|
||||
@@ -1,250 +0,0 @@
|
||||
#include "crc32.h"
|
||||
#include "GPT.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <iostream>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// FIXME: don't assume 512 byte sectors
|
||||
#define SECTOR_SIZE 512
|
||||
|
||||
GPTFile::GPTFile(std::string_view path)
|
||||
: m_path(path)
|
||||
{
|
||||
m_fd = open(m_path.c_str(), O_RDWR);
|
||||
if (m_fd == -1)
|
||||
{
|
||||
std::cerr << "Could not open '" << m_path << "': " << std::strerror(errno) << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (fstat(m_fd, &m_stat) == -1)
|
||||
{
|
||||
std::cerr << "Could not stat '" << m_path << "': " << std::strerror(errno) << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
void* mmap_addr = mmap(nullptr, m_stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0);
|
||||
if (mmap_addr == MAP_FAILED)
|
||||
{
|
||||
std::cerr << "Could not mmap '" << m_path << "': " << std::strerror(errno) << std::endl;
|
||||
return;
|
||||
}
|
||||
m_mmap = reinterpret_cast<uint8_t*>(mmap_addr);
|
||||
|
||||
if (!validate_gpt_header())
|
||||
return;
|
||||
|
||||
m_success = true;
|
||||
}
|
||||
|
||||
GPTFile::~GPTFile()
|
||||
{
|
||||
if (m_mmap)
|
||||
munmap(m_mmap, m_stat.st_size);
|
||||
m_mmap = nullptr;
|
||||
|
||||
if (m_fd != -1)
|
||||
close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
|
||||
MBR& GPTFile::mbr()
|
||||
{
|
||||
return *reinterpret_cast<MBR*>(m_mmap);
|
||||
}
|
||||
|
||||
const GPTHeader& GPTFile::gpt_header() const
|
||||
{
|
||||
return *reinterpret_cast<GPTHeader*>(m_mmap + SECTOR_SIZE);
|
||||
}
|
||||
|
||||
bool GPTFile::install_stage1(std::span<const uint8_t> stage1)
|
||||
{
|
||||
auto& mbr = this->mbr();
|
||||
|
||||
if (stage1.size() > sizeof(mbr.boot_code))
|
||||
{
|
||||
std::cerr << m_path << ": can't fit " << stage1.size() << " bytes of boot code in mbr (max is " << sizeof(mbr.boot_code) << ")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// copy boot code
|
||||
memcpy(mbr.boot_code, stage1.data(), stage1.size());
|
||||
|
||||
// setup mbr
|
||||
mbr.unique_mbr_disk_signature = 0xdeadbeef;
|
||||
mbr.unknown = 0;
|
||||
mbr.signature = 0xAA55;
|
||||
|
||||
// setup mbr partition records
|
||||
mbr.partition_records[0].boot_indicator = 0x00;
|
||||
mbr.partition_records[0].starting_chs[0] = 0x00;
|
||||
mbr.partition_records[0].starting_chs[1] = 0x02;
|
||||
mbr.partition_records[0].starting_chs[2] = 0x00;
|
||||
mbr.partition_records[0].os_type = 0xEE;
|
||||
mbr.partition_records[0].ending_chs[0] = 0xFF;
|
||||
mbr.partition_records[0].ending_chs[1] = 0xFF;
|
||||
mbr.partition_records[0].ending_chs[2] = 0xFF;
|
||||
mbr.partition_records[0].starting_lba = 1;
|
||||
mbr.partition_records[0].size_in_lba = 0xFFFFFFFF;
|
||||
memset(&mbr.partition_records[1], 0x00, sizeof(MBRPartitionRecord));
|
||||
memset(&mbr.partition_records[2], 0x00, sizeof(MBRPartitionRecord));
|
||||
memset(&mbr.partition_records[3], 0x00, sizeof(MBRPartitionRecord));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPTFile::install_stage2(std::span<const uint8_t> stage2, std::span<const uint8_t> data, const GUID& root_partition_guid)
|
||||
{
|
||||
if (data.size() < 16)
|
||||
{
|
||||
std::cerr << m_path << ": contains invalid .data section, too small for patches" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// find GUID patch offsets
|
||||
std::size_t disk_guid_offset(-1);
|
||||
std::size_t part_guid_offset(-1);
|
||||
for (std::size_t i = 0; i < data.size() - 16; i++)
|
||||
{
|
||||
if (memcmp(data.data() + i, "root disk guid ", 16) == 0)
|
||||
{
|
||||
if (disk_guid_offset != std::size_t(-1))
|
||||
{
|
||||
std::cerr << m_path << ": contains invalid .data section, multiple patchable disk guids" << std::endl;
|
||||
return false;
|
||||
}
|
||||
disk_guid_offset = i;
|
||||
}
|
||||
if (memcmp(data.data() + i, "root part guid ", 16) == 0)
|
||||
{
|
||||
if (part_guid_offset != std::size_t(-1))
|
||||
{
|
||||
std::cerr << m_path << ": contains invalid .data section, multiple patchable partition guids" << std::endl;
|
||||
return false;
|
||||
}
|
||||
part_guid_offset = i;
|
||||
}
|
||||
}
|
||||
if (disk_guid_offset == std::size_t(-1))
|
||||
{
|
||||
std::cerr << m_path << ": contains invalid .data section, no patchable disk guid" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (part_guid_offset == std::size_t(-1))
|
||||
{
|
||||
std::cerr << m_path << ": contains invalid .data section, no patchable partition guid" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto partition = find_partition_with_type(bios_boot_guid);
|
||||
if (!partition.has_value())
|
||||
{
|
||||
std::cerr << m_path << ": could not find partition with type " << bios_boot_guid << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::size_t partition_size = (partition->ending_lba - partition->starting_lba + 1) * SECTOR_SIZE;
|
||||
|
||||
std::size_t data_offset = stage2.size();
|
||||
if (std::size_t rem = data_offset % 512)
|
||||
data_offset += 512 - rem;
|
||||
|
||||
if (data_offset + data.size() > partition_size)
|
||||
{
|
||||
std::cerr << m_path << ": can't fit " << stage2.size() + data.size() << " bytes of data to partition of size " << partition_size << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* partition_start = m_mmap + partition->starting_lba * SECTOR_SIZE;
|
||||
memcpy(partition_start, stage2.data(), stage2.size());
|
||||
memcpy(partition_start + data_offset, data.data(), data.size());
|
||||
|
||||
// patch GUIDs
|
||||
*reinterpret_cast<GUID*>(partition_start + data_offset + disk_guid_offset) = gpt_header().disk_guid;
|
||||
*reinterpret_cast<GUID*>(partition_start + data_offset + part_guid_offset) = root_partition_guid;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPTFile::install_bootloader(std::span<const uint8_t> stage1, std::span<const uint8_t> stage2, std::span<const uint8_t> data, const GUID& root_partition_guid)
|
||||
{
|
||||
if (!find_partition_with_guid(root_partition_guid).has_value())
|
||||
{
|
||||
std::cerr << m_path << ": no partition with GUID " << root_partition_guid << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!install_stage1(stage1))
|
||||
return false;
|
||||
if (!install_stage2(stage2, data, root_partition_guid))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<GPTPartitionEntry> GPTFile::find_partition_with_guid(const GUID& guid) const
|
||||
{
|
||||
const auto& gpt_header = this->gpt_header();
|
||||
const uint8_t* partition_entry_array_start = m_mmap + gpt_header.partition_entry_lba * SECTOR_SIZE;
|
||||
for (std::size_t i = 0; i < gpt_header.number_of_partition_entries; i++)
|
||||
{
|
||||
const auto& partition_entry = *reinterpret_cast<const GPTPartitionEntry*>(partition_entry_array_start + i * gpt_header.size_of_partition_entry);
|
||||
if (partition_entry.partition_guid != guid)
|
||||
continue;
|
||||
return partition_entry;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<GPTPartitionEntry> GPTFile::find_partition_with_type(const GUID& type_guid) const
|
||||
{
|
||||
const auto& gpt_header = this->gpt_header();
|
||||
const uint8_t* partition_entry_array_start = m_mmap + gpt_header.partition_entry_lba * SECTOR_SIZE;
|
||||
for (std::size_t i = 0; i < gpt_header.number_of_partition_entries; i++)
|
||||
{
|
||||
const auto& partition_entry = *reinterpret_cast<const GPTPartitionEntry*>(partition_entry_array_start + i * gpt_header.size_of_partition_entry);
|
||||
if (partition_entry.type_guid != type_guid)
|
||||
continue;
|
||||
return partition_entry;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool GPTFile::validate_gpt_header() const
|
||||
{
|
||||
if (SECTOR_SIZE + m_stat.st_size < sizeof(GPTHeader))
|
||||
{
|
||||
std::cerr << m_path << " is too small to have GPT header" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto gpt_header = this->gpt_header();
|
||||
|
||||
if (std::memcmp(gpt_header.signature, "EFI PART", 8) != 0)
|
||||
{
|
||||
std::cerr << m_path << " doesn't contain GPT partition header signature" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t header_crc32 = gpt_header.header_crc32;
|
||||
|
||||
gpt_header.header_crc32 = 0;
|
||||
if (header_crc32 != crc32_checksum(reinterpret_cast<uint8_t*>(&gpt_header), gpt_header.header_size))
|
||||
{
|
||||
std::cerr << m_path << " has non-matching header crc32" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::size_t partition_array_size = gpt_header.number_of_partition_entries * gpt_header.size_of_partition_entry;
|
||||
if (gpt_header.partition_entry_array_crc32 != crc32_checksum(m_mmap + gpt_header.partition_entry_lba * SECTOR_SIZE, partition_array_size))
|
||||
{
|
||||
std::cerr << m_path << " has non-matching partition entry crc32" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "GUID.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
|
||||
struct MBRPartitionRecord
|
||||
{
|
||||
uint8_t boot_indicator;
|
||||
uint8_t starting_chs[3];
|
||||
uint8_t os_type;
|
||||
uint8_t ending_chs[3];
|
||||
uint32_t starting_lba;
|
||||
uint32_t size_in_lba;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct MBR
|
||||
{
|
||||
uint8_t boot_code[440];
|
||||
uint32_t unique_mbr_disk_signature;
|
||||
uint16_t unknown;
|
||||
MBRPartitionRecord partition_records[4];
|
||||
uint16_t signature;
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(MBR) == 512);
|
||||
|
||||
struct GPTPartitionEntry
|
||||
{
|
||||
GUID type_guid;
|
||||
GUID partition_guid;
|
||||
uint64_t starting_lba;
|
||||
uint64_t ending_lba;
|
||||
uint64_t attributes;
|
||||
uint16_t name[36];
|
||||
};
|
||||
static_assert(sizeof(GPTPartitionEntry) == 128);
|
||||
|
||||
struct GPTHeader
|
||||
{
|
||||
char signature[8];
|
||||
uint32_t revision;
|
||||
uint32_t header_size;
|
||||
uint32_t header_crc32;
|
||||
uint32_t reserved;
|
||||
uint64_t my_lba;
|
||||
uint64_t alternate_lba;
|
||||
uint64_t first_usable_lba;
|
||||
uint64_t last_usable_lba;
|
||||
GUID disk_guid;
|
||||
uint64_t partition_entry_lba;
|
||||
uint32_t number_of_partition_entries;
|
||||
uint32_t size_of_partition_entry;
|
||||
uint32_t partition_entry_array_crc32;
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(GPTHeader) == 92);
|
||||
|
||||
class GPTFile
|
||||
{
|
||||
public:
|
||||
GPTFile(std::string_view path);
|
||||
~GPTFile();
|
||||
|
||||
bool install_bootloader(std::span<const uint8_t> stage1, std::span<const uint8_t> stage2, std::span<const uint8_t> data, const GUID& root_partition_guid);
|
||||
|
||||
const GPTHeader& gpt_header() const;
|
||||
|
||||
bool success() const { return m_success; }
|
||||
|
||||
std::string_view path() const { return m_path; }
|
||||
|
||||
private:
|
||||
MBR& mbr();
|
||||
bool validate_gpt_header() const;
|
||||
std::optional<GPTPartitionEntry> find_partition_with_guid(const GUID& guid) const;
|
||||
std::optional<GPTPartitionEntry> find_partition_with_type(const GUID& type_guid) const;
|
||||
|
||||
bool install_stage1(std::span<const uint8_t> stage1);
|
||||
bool install_stage2(std::span<const uint8_t> stage2, std::span<const uint8_t> data, const GUID& root_partition_guid);
|
||||
|
||||
private:
|
||||
const std::string m_path;
|
||||
bool m_success { false };
|
||||
int m_fd { -1 };
|
||||
struct stat m_stat { };
|
||||
uint8_t* m_mmap { nullptr };
|
||||
};
|
||||
@@ -1,74 +0,0 @@
|
||||
#include "GUID.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <cstring>
|
||||
|
||||
std::optional<uint64_t> parse_hex(std::string_view hex_string)
|
||||
{
|
||||
uint64_t result = 0;
|
||||
for (char c : hex_string)
|
||||
{
|
||||
if (!isxdigit(c))
|
||||
return {};
|
||||
|
||||
uint8_t nibble = 0;
|
||||
if ('0' <= c && c <= '9')
|
||||
nibble = c - '0';
|
||||
else if ('a' <= c && c <= 'f')
|
||||
nibble = c - 'a' + 10;
|
||||
else
|
||||
nibble = c - 'A' + 10;
|
||||
result = (result << 4) | nibble;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<GUID> GUID::from_string(std::string_view guid_string)
|
||||
{
|
||||
if (guid_string.size() != 36)
|
||||
return {};
|
||||
|
||||
if (guid_string[8] != '-' || guid_string[13] != '-' || guid_string[18] != '-' || guid_string[23] != '-')
|
||||
return {};
|
||||
|
||||
auto comp1 = parse_hex(guid_string.substr(0, 8));
|
||||
auto comp2 = parse_hex(guid_string.substr(9, 4));
|
||||
auto comp3 = parse_hex(guid_string.substr(14, 4));
|
||||
auto comp4 = parse_hex(guid_string.substr(19, 4));
|
||||
auto comp5 = parse_hex(guid_string.substr(24, 12));
|
||||
|
||||
if (!comp1.has_value() || !comp2.has_value() || !comp3.has_value() || !comp4.has_value() || !comp5.has_value())
|
||||
return {};
|
||||
|
||||
GUID result;
|
||||
result.component1 = *comp1;
|
||||
result.component2 = *comp2;
|
||||
result.component3 = *comp3;
|
||||
for (int i = 0; i < 2; i++)
|
||||
result.component45[i + 0] = *comp4 >> ((2-1) * 8 - i * 8);
|
||||
for (int i = 0; i < 6; i++)
|
||||
result.component45[i + 2] = *comp5 >> ((6-1) * 8 - i * 8);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool GUID::operator==(const GUID& other) const
|
||||
{
|
||||
return std::memcmp(this, &other, sizeof(GUID)) == 0;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const GUID& guid)
|
||||
{
|
||||
auto flags = out.flags();
|
||||
out << std::hex << std::setfill('0');
|
||||
out << std::setw(8) << guid.component1 << '-';
|
||||
out << std::setw(4) << guid.component2 << '-';
|
||||
out << std::setw(4) << guid.component3 << '-';
|
||||
|
||||
out << std::setw(2);
|
||||
for (int i = 0; i < 2; i++) out << +guid.component45[i];
|
||||
out << '-';
|
||||
for (int i = 2; i < 8; i++) out << +guid.component45[i];
|
||||
|
||||
out.flags(flags);
|
||||
return out;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <string_view>
|
||||
|
||||
struct GUID
|
||||
{
|
||||
static std::optional<GUID> from_string(std::string_view);
|
||||
|
||||
uint32_t component1;
|
||||
uint16_t component2;
|
||||
uint16_t component3;
|
||||
// last 2 components are combined so no packed needed
|
||||
uint8_t component45[8];
|
||||
|
||||
bool operator==(const GUID& other) const;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const GUID& guid);
|
||||
|
||||
// unused 00000000-0000-0000-0000-000000000000
|
||||
static constexpr GUID unused_guid = {
|
||||
0x00000000,
|
||||
0x0000,
|
||||
0x0000,
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
|
||||
};
|
||||
|
||||
// bios boot 21686148-6449-6E6F-744E-656564454649
|
||||
static constexpr GUID bios_boot_guid = {
|
||||
0x21686148,
|
||||
0x6449,
|
||||
0x6E6F,
|
||||
{ 0x74, 0x4E, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49 }
|
||||
};
|
||||
@@ -1,80 +0,0 @@
|
||||
#include "crc32.h"
|
||||
|
||||
static constexpr uint32_t crc32_table[256] =
|
||||
{
|
||||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
|
||||
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
|
||||
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
|
||||
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
|
||||
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
|
||||
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
|
||||
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
|
||||
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
|
||||
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
|
||||
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
|
||||
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
|
||||
0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
|
||||
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
|
||||
0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
|
||||
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
|
||||
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
|
||||
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
|
||||
0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
|
||||
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
|
||||
0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
|
||||
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
|
||||
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
|
||||
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
|
||||
0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
|
||||
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
|
||||
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
|
||||
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
|
||||
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
|
||||
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
|
||||
0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
|
||||
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
|
||||
0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
|
||||
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
|
||||
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
|
||||
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
|
||||
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
|
||||
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
|
||||
0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
|
||||
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
|
||||
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
|
||||
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
|
||||
0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
|
||||
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
|
||||
0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
|
||||
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
|
||||
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
|
||||
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
|
||||
0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
|
||||
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
|
||||
0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
|
||||
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
|
||||
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
|
||||
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
|
||||
0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
|
||||
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
|
||||
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
|
||||
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
|
||||
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
|
||||
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
|
||||
0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
|
||||
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
|
||||
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
|
||||
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
|
||||
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
|
||||
};
|
||||
|
||||
uint32_t crc32_checksum(const uint8_t* data, std::size_t count)
|
||||
{
|
||||
uint32_t crc32 = 0xFFFFFFFF;
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
uint8_t index = (crc32 ^ data[i]) & 0xFF;
|
||||
crc32 = (crc32 >> 8) ^ crc32_table[index];
|
||||
}
|
||||
return crc32 ^ 0xFFFFFFFF;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
uint32_t crc32_checksum(const uint8_t* data, std::size_t count);
|
||||
@@ -1,45 +0,0 @@
|
||||
#include "ELF.h"
|
||||
#include "GPT.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
if (argc != 4)
|
||||
{
|
||||
std::fprintf(stderr, "usage: %s BOOTLOADER DISK_IMAGE ROOT_PARTITION_GUID\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto root_partition_guid = GUID::from_string(argv[3]);
|
||||
if (!root_partition_guid.has_value())
|
||||
{
|
||||
std::cerr << "invalid guid '" << argv[3] << '\'' << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ELFFile bootloader(argv[1]);
|
||||
if (!bootloader.success())
|
||||
return 1;
|
||||
|
||||
auto stage1 = bootloader.find_section(".stage1"sv);
|
||||
auto stage2 = bootloader.find_section(".stage2"sv);
|
||||
auto data = bootloader.find_section(".data"sv);
|
||||
if (!stage1.has_value() || !stage2.has_value() || !data.has_value())
|
||||
{
|
||||
std::cerr << bootloader.path() << " doesn't contain .stage1, .stage2 and .data sections" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
GPTFile disk_image(argv[2]);
|
||||
if (!disk_image.success())
|
||||
return 1;
|
||||
|
||||
if (!disk_image.install_bootloader(*stage1, *stage2, *data, *root_partition_guid))
|
||||
return 1;
|
||||
std::cout << "bootloader installed" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
10
check-fs.sh
Executable file
10
check-fs.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
LOOP_DEV=$(sudo losetup -f --show $DISK_IMAGE_PATH)
|
||||
sudo partprobe $LOOP_DEV
|
||||
|
||||
sudo fsck.ext2 -fn ${LOOP_DEV}p2 || true
|
||||
|
||||
sudo losetup -d $LOOP_DEV
|
||||
41
image-full.sh
Executable file
41
image-full.sh
Executable file
@@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
DISK_SIZE=$[50 * 1024 * 1024]
|
||||
MOUNT_DIR=/mnt
|
||||
|
||||
truncate -s 0 $DISK_IMAGE_PATH
|
||||
truncate -s $DISK_SIZE $DISK_IMAGE_PATH
|
||||
|
||||
sed -e 's/\s*\([-\+[:alnum:]]*\).*/\1/' << EOF | fdisk $DISK_IMAGE_PATH > /dev/null
|
||||
g # gpt
|
||||
n # new partition
|
||||
1 # partition number 1
|
||||
# default (from the beginning of the disk)
|
||||
+1MiB # bios boot partiton size
|
||||
n # new partition
|
||||
2 # partition number 2
|
||||
# default (right after bios boot partition)
|
||||
# default (to the end of disk)
|
||||
t # set type
|
||||
1 # ... of partition 1
|
||||
4 # bios boot partition
|
||||
t # set type
|
||||
2 # ... of partition 2
|
||||
20 # Linux filesystem
|
||||
w # write changes
|
||||
EOF
|
||||
|
||||
LOOP_DEV=$(sudo losetup -f --show $DISK_IMAGE_PATH)
|
||||
sudo partprobe $LOOP_DEV
|
||||
|
||||
PARTITION1=${LOOP_DEV}p1
|
||||
PARTITION2=${LOOP_DEV}p2
|
||||
|
||||
sudo mkfs.ext2 -d $SYSROOT -b 1024 -q $PARTITION2
|
||||
|
||||
sudo mount $PARTITION2 $MOUNT_DIR
|
||||
sudo grub-install --no-floppy --target=i386-pc --modules="normal ext2 multiboot" --boot-directory=${MOUNT_DIR}/boot $LOOP_DEV
|
||||
sudo umount $MOUNT_DIR
|
||||
|
||||
sudo losetup -d $LOOP_DEV
|
||||
22
image.sh
Executable file
22
image.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
if [ ! -f $DISK_IMAGE_PATH ]; then
|
||||
$(dirname "$0")/image-full.sh
|
||||
exit 0
|
||||
fi
|
||||
|
||||
MOUNT_DIR=/mnt
|
||||
|
||||
LOOP_DEV=$(sudo losetup -f --show $DISK_IMAGE_PATH)
|
||||
sudo partprobe $LOOP_DEV
|
||||
|
||||
ROOT_PARTITON=${LOOP_DEV}p2
|
||||
|
||||
sudo mount $ROOT_PARTITON $MOUNT_DIR
|
||||
|
||||
sudo rsync -a ${SYSROOT}/* ${MOUNT_DIR}/
|
||||
|
||||
sudo umount $MOUNT_DIR
|
||||
|
||||
sudo losetup -d $LOOP_DEV
|
||||
@@ -1,130 +1,77 @@
|
||||
cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
project(kernel CXX C ASM)
|
||||
|
||||
if("${BANAN_ARCH}" STREQUAL "x86_64")
|
||||
set(ELF_FORMAT elf64-x86-64)
|
||||
elseif("${BANAN_ARCH}" STREQUAL "i386")
|
||||
set(ELF_FORMAT elf32-i386)
|
||||
endif()
|
||||
|
||||
set(KERNEL_SOURCES
|
||||
font/prefs.psf.o
|
||||
kernel/ACPI/ACPI.cpp
|
||||
kernel/ACPI/AML/Namespace.cpp
|
||||
kernel/ACPI/AML/Node.cpp
|
||||
kernel/ACPI/AML/OpRegion.cpp
|
||||
kernel/ACPI/BatterySystem.cpp
|
||||
kernel/ACPI/EmbeddedController.cpp
|
||||
kernel/ACPI.cpp
|
||||
kernel/APIC.cpp
|
||||
kernel/Audio/AC97/Controller.cpp
|
||||
kernel/Audio/Controller.cpp
|
||||
kernel/Audio/HDAudio/AudioFunctionGroup.cpp
|
||||
kernel/Audio/HDAudio/Controller.cpp
|
||||
kernel/BootInfo.cpp
|
||||
kernel/CPUID.cpp
|
||||
kernel/Credentials.cpp
|
||||
kernel/Debug.cpp
|
||||
kernel/Device/DebugDevice.cpp
|
||||
kernel/Device/Device.cpp
|
||||
kernel/Device/FramebufferDevice.cpp
|
||||
kernel/Device/NullDevice.cpp
|
||||
kernel/Device/RandomDevice.cpp
|
||||
kernel/Device/ZeroDevice.cpp
|
||||
kernel/ELF.cpp
|
||||
kernel/Epoll.cpp
|
||||
kernel/Errors.cpp
|
||||
kernel/Font.cpp
|
||||
kernel/FS/DevFS/FileSystem.cpp
|
||||
kernel/FS/EventFD.cpp
|
||||
kernel/FS/Ext2/FileSystem.cpp
|
||||
kernel/FS/Ext2/Inode.cpp
|
||||
kernel/FS/FAT/FileSystem.cpp
|
||||
kernel/FS/FAT/Inode.cpp
|
||||
kernel/FS/FileSystem.cpp
|
||||
kernel/FS/Inode.cpp
|
||||
kernel/FS/Pipe.cpp
|
||||
kernel/FS/ProcFS/FileSystem.cpp
|
||||
kernel/FS/ProcFS/Inode.cpp
|
||||
kernel/FS/TmpFS/FileSystem.cpp
|
||||
kernel/FS/TmpFS/Inode.cpp
|
||||
kernel/FS/USTARModule.cpp
|
||||
kernel/FS/RamFS/FileSystem.cpp
|
||||
kernel/FS/RamFS/Inode.cpp
|
||||
kernel/FS/VirtualFileSystem.cpp
|
||||
kernel/GDT.cpp
|
||||
kernel/IDT.cpp
|
||||
kernel/Input/InputDevice.cpp
|
||||
kernel/Input/PS2/Controller.cpp
|
||||
kernel/Input/PS2/Device.cpp
|
||||
kernel/Input/PS2/Keyboard.cpp
|
||||
kernel/Input/PS2/Keymap.cpp
|
||||
kernel/Input/PS2/Mouse.cpp
|
||||
kernel/Interruptable.cpp
|
||||
kernel/Input/PS2Controller.cpp
|
||||
kernel/Input/PS2Keyboard.cpp
|
||||
kernel/Input/PS2Keymap.cpp
|
||||
kernel/InterruptController.cpp
|
||||
kernel/kernel.cpp
|
||||
kernel/Lock/SpinLock.cpp
|
||||
kernel/Memory/ByteRingBuffer.cpp
|
||||
kernel/Memory/DMARegion.cpp
|
||||
kernel/Memory/FileBackedRegion.cpp
|
||||
kernel/Memory/GeneralAllocator.cpp
|
||||
kernel/Memory/Heap.cpp
|
||||
kernel/Memory/kmalloc.cpp
|
||||
kernel/Memory/MemoryBackedRegion.cpp
|
||||
kernel/Memory/MemoryRegion.cpp
|
||||
kernel/Memory/PhysicalRange.cpp
|
||||
kernel/Memory/SharedMemoryObject.cpp
|
||||
kernel/Memory/VirtualRange.cpp
|
||||
kernel/Networking/ARPTable.cpp
|
||||
kernel/Networking/E1000/E1000.cpp
|
||||
kernel/Networking/E1000/E1000E.cpp
|
||||
kernel/Networking/IPv4Layer.cpp
|
||||
kernel/Networking/Loopback.cpp
|
||||
kernel/Networking/NetworkInterface.cpp
|
||||
kernel/Networking/NetworkLayer.cpp
|
||||
kernel/Networking/NetworkManager.cpp
|
||||
kernel/Networking/NetworkSocket.cpp
|
||||
kernel/Networking/RTL8169/RTL8169.cpp
|
||||
kernel/Networking/TCPSocket.cpp
|
||||
kernel/Networking/UDPSocket.cpp
|
||||
kernel/Networking/UNIX/Socket.cpp
|
||||
kernel/Networking/E1000.cpp
|
||||
kernel/OpenFileDescriptorSet.cpp
|
||||
kernel/Panic.cpp
|
||||
kernel/PCI.cpp
|
||||
kernel/PIC.cpp
|
||||
kernel/Process.cpp
|
||||
kernel/Processor.cpp
|
||||
kernel/Random.cpp
|
||||
kernel/Scheduler.cpp
|
||||
kernel/Semaphore.cpp
|
||||
kernel/SpinLock.cpp
|
||||
kernel/SSP.cpp
|
||||
kernel/Storage/ATA/AHCI/Controller.cpp
|
||||
kernel/Storage/ATA/AHCI/Device.cpp
|
||||
kernel/Storage/ATA/ATABus.cpp
|
||||
kernel/Storage/ATA/ATAController.cpp
|
||||
kernel/Storage/ATA/ATADevice.cpp
|
||||
kernel/Storage/ATABus.cpp
|
||||
kernel/Storage/ATAController.cpp
|
||||
kernel/Storage/ATADevice.cpp
|
||||
kernel/Storage/DiskCache.cpp
|
||||
kernel/Storage/NVMe/Controller.cpp
|
||||
kernel/Storage/NVMe/Namespace.cpp
|
||||
kernel/Storage/NVMe/Queue.cpp
|
||||
kernel/Storage/Partition.cpp
|
||||
kernel/Storage/SCSI.cpp
|
||||
kernel/Storage/StorageDevice.cpp
|
||||
kernel/Syscall.cpp
|
||||
kernel/Terminal/FramebufferTerminal.cpp
|
||||
kernel/Terminal/PseudoTerminal.cpp
|
||||
kernel/Syscall.S
|
||||
kernel/Terminal/Serial.cpp
|
||||
kernel/Terminal/TerminalDriver.cpp
|
||||
kernel/Terminal/TextModeTerminal.cpp
|
||||
kernel/Terminal/TTY.cpp
|
||||
kernel/Terminal/VesaTerminalDriver.cpp
|
||||
kernel/Terminal/VirtualTTY.cpp
|
||||
kernel/Thread.cpp
|
||||
kernel/ThreadBlocker.cpp
|
||||
kernel/Timer/HPET.cpp
|
||||
kernel/Timer/PIT.cpp
|
||||
kernel/Timer/RTC.cpp
|
||||
kernel/Timer/Timer.cpp
|
||||
kernel/USB/Controller.cpp
|
||||
kernel/USB/Device.cpp
|
||||
kernel/USB/HID/HIDDriver.cpp
|
||||
kernel/USB/HID/Joystick.cpp
|
||||
kernel/USB/HID/Keyboard.cpp
|
||||
kernel/USB/HID/Mouse.cpp
|
||||
kernel/USB/Hub/HubDriver.cpp
|
||||
kernel/USB/MassStorage/MassStorageDriver.cpp
|
||||
kernel/USB/MassStorage/SCSIDevice.cpp
|
||||
kernel/USB/USBManager.cpp
|
||||
kernel/USB/XHCI/Controller.cpp
|
||||
kernel/USB/XHCI/Device.cpp
|
||||
icxxabi.cpp
|
||||
)
|
||||
|
||||
set(ENABLE_KERNEL_UBSAN False)
|
||||
#set(ENABLE_KERNEL_UBSAN True)
|
||||
|
||||
if(ENABLE_KERNEL_UBSAN)
|
||||
set(KERNEL_SOURCES ${KERNEL_SOURCES} ubsan.cpp)
|
||||
@@ -134,153 +81,127 @@ if("${BANAN_ARCH}" STREQUAL "x86_64")
|
||||
set(KERNEL_SOURCES
|
||||
${KERNEL_SOURCES}
|
||||
arch/x86_64/boot.S
|
||||
arch/x86_64/GDT.cpp
|
||||
arch/x86_64/IDT.cpp
|
||||
arch/x86_64/interrupts.S
|
||||
arch/x86_64/PageTable.cpp
|
||||
arch/x86_64/Signal.S
|
||||
arch/x86_64/Syscall.S
|
||||
arch/x86_64/Thread.S
|
||||
arch/x86_64/User.S
|
||||
arch/x86_64/Yield.S
|
||||
)
|
||||
elseif("${BANAN_ARCH}" STREQUAL "i686")
|
||||
elseif("${BANAN_ARCH}" STREQUAL "i386")
|
||||
set(KERNEL_SOURCES
|
||||
${KERNEL_SOURCES}
|
||||
arch/i686/boot.S
|
||||
arch/i686/interrupts.S
|
||||
arch/i686/PageTable.cpp
|
||||
arch/i686/Signal.S
|
||||
arch/i686/Syscall.S
|
||||
arch/i686/Thread.S
|
||||
arch/i686/User.S
|
||||
arch/i686/Yield.S
|
||||
arch/i386/boot.S
|
||||
arch/i386/GDT.cpp
|
||||
arch/i386/IDT.cpp
|
||||
arch/i386/MMU.cpp
|
||||
arch/i386/SpinLock.S
|
||||
arch/i386/Thread.S
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR "unsupported architecure ${BANAN_ARCH}")
|
||||
endif()
|
||||
|
||||
file(GLOB_RECURSE LAI_SOURCES
|
||||
lai/*.c
|
||||
)
|
||||
set(LAI_SOURCES
|
||||
${LAI_SOURCES}
|
||||
kernel/lai_host.cpp
|
||||
)
|
||||
|
||||
set(BAN_SOURCES
|
||||
../BAN/BAN/Assert.cpp
|
||||
../BAN/BAN/New.cpp
|
||||
../BAN/BAN/String.cpp
|
||||
../BAN/BAN/StringView.cpp
|
||||
../BAN/BAN/Time.cpp
|
||||
)
|
||||
|
||||
set(KLIBC_SOURCES
|
||||
klibc/ctype.cpp
|
||||
klibc/string.cpp
|
||||
|
||||
# Ehhh don't do this but for now libc uses the same stuff kernel can use
|
||||
# This won't work after libc starts using sse implemetations tho
|
||||
../userspace/libraries/LibC/arch/${BANAN_ARCH}/string.S
|
||||
set(LIBC_SOURCES
|
||||
../libc/ctype.cpp
|
||||
../libc/string.cpp
|
||||
)
|
||||
|
||||
set(LIBDEFLATE_SOURCE
|
||||
../userspace/libraries/LibDEFLATE/Compressor.cpp
|
||||
../userspace/libraries/LibDEFLATE/Decompressor.cpp
|
||||
../userspace/libraries/LibDEFLATE/HuffmanTree.cpp
|
||||
)
|
||||
|
||||
set(LIBFONT_SOURCES
|
||||
../userspace/libraries/LibFont/Font.cpp
|
||||
../userspace/libraries/LibFont/PSF.cpp
|
||||
)
|
||||
|
||||
set(LIBINPUT_SOURCE
|
||||
../userspace/libraries/LibInput/KeyboardLayout.cpp
|
||||
../userspace/libraries/LibInput/KeyEvent.cpp
|
||||
)
|
||||
|
||||
set(LIBQR_SOURCE
|
||||
../userspace/libraries/LibQR/QRCode.cpp
|
||||
set(LIBELF_SOURCES
|
||||
../LibELF/LibELF/LoadableELF.cpp
|
||||
)
|
||||
|
||||
set(KERNEL_SOURCES
|
||||
${KERNEL_SOURCES}
|
||||
${LAI_SOURCES}
|
||||
${BAN_SOURCES}
|
||||
${KLIBC_SOURCES}
|
||||
${LIBDEFLATE_SOURCE}
|
||||
${LIBFONT_SOURCES}
|
||||
${LIBINPUT_SOURCE}
|
||||
${LIBQR_SOURCE}
|
||||
${LIBC_SOURCES}
|
||||
${LIBELF_SOURCES}
|
||||
)
|
||||
|
||||
add_executable(kernel ${KERNEL_SOURCES})
|
||||
add_dependencies(kernel headers)
|
||||
|
||||
target_compile_definitions(kernel PRIVATE __is_kernel)
|
||||
target_compile_definitions(kernel PRIVATE __arch=${BANAN_ARCH})
|
||||
target_compile_definitions(kernel PRIVATE LIBDEFLATE_AVOID_STACK=1)
|
||||
target_compile_definitions(kernel PUBLIC __is_kernel)
|
||||
target_compile_definitions(kernel PUBLIC __arch=${BANAN_ARCH})
|
||||
|
||||
target_compile_options(kernel PRIVATE
|
||||
-O2 -g
|
||||
-fmacro-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.
|
||||
-fstack-protector
|
||||
-ffreestanding
|
||||
-fno-omit-frame-pointer
|
||||
-fstrict-volatile-bitfields
|
||||
-mgeneral-regs-only
|
||||
-Wall -Wextra -Werror -Wstack-usage=1024
|
||||
)
|
||||
|
||||
# C++ specific
|
||||
target_compile_options(kernel PRIVATE
|
||||
-Wno-literal-suffix
|
||||
-Wno-invalid-offsetof
|
||||
-fno-rtti
|
||||
-fno-exceptions
|
||||
)
|
||||
target_compile_options(kernel PUBLIC -O2 -g)
|
||||
target_compile_options(kernel PUBLIC $<$<COMPILE_LANGUAGE:CXX>:-Wno-literal-suffix -fno-rtti -fno-exceptions>)
|
||||
target_compile_options(kernel PUBLIC -fmacro-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.)
|
||||
target_compile_options(kernel PUBLIC -fstack-protector -ffreestanding -Wall -Werror=return-type -Wstack-usage=1024 -fno-omit-frame-pointer -mgeneral-regs-only)
|
||||
|
||||
if(ENABLE_KERNEL_UBSAN)
|
||||
target_compile_options(kernel PRIVATE -fsanitize=undefined)
|
||||
target_compile_options(kernel PUBLIC -fsanitize=undefined)
|
||||
endif()
|
||||
|
||||
if("${BANAN_ARCH}" STREQUAL "x86_64")
|
||||
target_compile_options(kernel PRIVATE -mcmodel=kernel -mno-red-zone)
|
||||
target_link_options(kernel PRIVATE LINKER:-z,max-page-size=4096)
|
||||
target_link_options(kernel PRIVATE LINKER:-T,${CMAKE_CURRENT_SOURCE_DIR}/arch/x86_64/linker.ld)
|
||||
elseif("${BANAN_ARCH}" STREQUAL "i686")
|
||||
target_link_options(kernel PRIVATE LINKER:-T,${CMAKE_CURRENT_SOURCE_DIR}/arch/i686/linker.ld)
|
||||
target_compile_options(kernel PUBLIC -mcmodel=kernel -mno-red-zone -mno-mmx)
|
||||
target_link_options(kernel PUBLIC LINKER:-z,max-page-size=4096)
|
||||
target_link_options(kernel PUBLIC LINKER:-T,${CMAKE_CURRENT_SOURCE_DIR}/arch/x86_64/linker.ld)
|
||||
elseif("${BANAN_ARCH}" STREQUAL "i386")
|
||||
target_link_options(kernel PUBLIC LINKER:-T,${CMAKE_CURRENT_SOURCE_DIR}/arch/i386/linker.ld)
|
||||
endif()
|
||||
|
||||
target_link_options(kernel PRIVATE -ffreestanding -nostdlib -orphan-handling=error)
|
||||
target_link_options(kernel PUBLIC -ffreestanding -nostdlib)
|
||||
|
||||
get_target_property(KERNEL_COMPILE_OPTIONS kernel COMPILE_OPTIONS)
|
||||
execute_process(COMMAND ${CMAKE_CXX_COMPILER} ${KERNEL_COMPILE_OPTIONS} -print-file-name=crtbegin.o OUTPUT_VARIABLE CRTBEGIN OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
execute_process(COMMAND ${CMAKE_CXX_COMPILER} ${KERNEL_COMPILE_OPTIONS} -print-file-name=crtend.o OUTPUT_VARIABLE CRTEND OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
add_custom_target(crt0
|
||||
COMMAND ${CMAKE_CXX_COMPILER} -c ${CMAKE_CURRENT_SOURCE_DIR}/arch/${BANAN_ARCH}/crt0.S -o ${CMAKE_CURRENT_BINARY_DIR}/crt0.o
|
||||
DEPENDS headers
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
TARGET crt0
|
||||
POST_BUILD
|
||||
COMMAND sudo cp ${CMAKE_CURRENT_BINARY_DIR}/crt0.o ${BANAN_LIB}/
|
||||
)
|
||||
|
||||
add_custom_target(kernel-headers
|
||||
COMMAND sudo rsync -a ${CMAKE_CURRENT_SOURCE_DIR}/include/ ${BANAN_INCLUDE}/
|
||||
COMMAND sudo rsync -a ${CMAKE_CURRENT_SOURCE_DIR}/lai/include/ ${BANAN_INCLUDE}/
|
||||
DEPENDS sysroot
|
||||
USES_TERMINAL
|
||||
)
|
||||
|
||||
add_custom_target(kernel-install
|
||||
COMMAND sudo cp ${CMAKE_CURRENT_BINARY_DIR}/kernel ${BANAN_BOOT}/banan-os.kernel
|
||||
DEPENDS kernel
|
||||
USES_TERMINAL
|
||||
)
|
||||
|
||||
execute_process(COMMAND ${CMAKE_CXX_COMPILER} -print-file-name=crtbegin.o OUTPUT_VARIABLE CRTBEGIN OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
execute_process(COMMAND ${CMAKE_CXX_COMPILER} -print-file-name=crtend.o OUTPUT_VARIABLE CRTEND OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
add_custom_command(
|
||||
TARGET kernel PRE_LINK
|
||||
COMMAND ${CMAKE_CXX_COMPILER} -MD -c ${CMAKE_CURRENT_SOURCE_DIR}/arch/${BANAN_ARCH}/crti.S ${COMPILE_OPTIONS}
|
||||
COMMAND ${CMAKE_CXX_COMPILER} -MD -c ${CMAKE_CURRENT_SOURCE_DIR}/arch/${BANAN_ARCH}/crtn.S ${COMPILE_OPTIONS}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CRTBEGIN} .
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CRTEND} .
|
||||
COMMAND cp ${CRTBEGIN} .
|
||||
COMMAND cp ${CRTEND} .
|
||||
)
|
||||
|
||||
#add_custom_command(
|
||||
# TARGET kernel POST_BUILD
|
||||
# COMMAND x86_64-banan_os-strip ${CMAKE_CURRENT_BINARY_DIR}/kernel
|
||||
#)
|
||||
|
||||
banan_include_headers(kernel ban)
|
||||
banan_include_headers(kernel libc)
|
||||
banan_include_headers(kernel libdeflate)
|
||||
banan_include_headers(kernel libelf)
|
||||
banan_include_headers(kernel libfont)
|
||||
banan_include_headers(kernel libinput)
|
||||
banan_include_headers(kernel libqr)
|
||||
|
||||
banan_install_headers(kernel)
|
||||
set_target_properties(kernel PROPERTIES OUTPUT_NAME banan-os.kernel)
|
||||
install(TARGETS kernel DESTINATION ${BANAN_BOOT} OPTIONAL)
|
||||
|
||||
if("${BANAN_ARCH}" STREQUAL "x86_64")
|
||||
set(ELF_FORMAT elf64-x86-64)
|
||||
elseif("${BANAN_ARCH}" STREQUAL "i686")
|
||||
set(ELF_FORMAT elf32-i386)
|
||||
endif()
|
||||
add_custom_command(
|
||||
TARGET kernel POST_BUILD
|
||||
COMMAND x86_64-banan_os-strip ${CMAKE_CURRENT_BINARY_DIR}/kernel
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT font/prefs.psf.o
|
||||
COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/font && objcopy -O ${ELF_FORMAT} -B i386 -I binary font/prefs.psf ${CMAKE_CURRENT_BINARY_DIR}/font/prefs.psf.o
|
||||
COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && objcopy -O ${ELF_FORMAT} -B i386 -I binary font/prefs.psf ${CMAKE_CURRENT_BINARY_DIR}/font/prefs.psf.o
|
||||
)
|
||||
|
||||
set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_COMPILER} <CMAKE_CXX_LINK_FLAGS> <FLAGS> <LINK_FLAGS> -o <TARGET> ${CMAKE_CURRENT_BINARY_DIR}/crti.o ${CMAKE_CURRENT_BINARY_DIR}/crtbegin.o <OBJECTS> ${CMAKE_CURRENT_BINARY_DIR}/crtend.o ${CMAKE_CURRENT_BINARY_DIR}/crtn.o -lgcc ")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user