forked from Bananymous/banan-os
Compare commits
1542 Commits
Author | SHA1 | Date |
---|---|---|
|
8ddab05ed3 | |
|
5dc441c4af | |
|
940fb0d1fd | |
|
f18c33563d | |
|
10e8a54b2e | |
|
f792976d6d | |
|
08cbd009ac | |
|
3d4219bfee | |
|
d58a569660 | |
|
fd3cf5d2b1 | |
|
1a844426c3 | |
|
42237a3bc8 | |
|
010c2c934b | |
|
48a76426e7 | |
|
0c645ba867 | |
|
f538dd5276 | |
|
31568fc5a1 | |
|
44c7fde2f7 | |
|
cb07142832 | |
|
60a05412c9 | |
|
0179f5ea09 | |
|
f671ed7e3f | |
|
2fccff5a35 | |
|
cd41d5f6dd | |
|
66905fcc08 | |
|
af4b138094 | |
|
3c57e05a65 | |
|
25099b4c98 | |
|
1ac7629459 | |
|
95681a7a05 | |
|
d7b8458a56 | |
|
67dfe0bcf3 | |
|
b1869bced4 | |
|
61aa1ea11f | |
|
20aa7c79d1 | |
|
22548a3f4a | |
|
9e1b5cbaab | |
|
1488ec5a03 | |
|
4f0457a268 | |
|
30fdc2198f | |
|
bce16cdd6e | |
|
d5daa46ab8 | |
|
ad6d95ba52 | |
|
318ce5dec8 | |
|
526d4369ce | |
|
c69919738b | |
|
1b5a01a6c9 | |
|
f233715b70 | |
|
a58ac18fa0 | |
|
f1e366d36f | |
|
301dcd78cc | |
|
65c6d62a15 | |
|
aaf7a249c6 | |
|
0bc8d49684 | |
|
17ecbca204 | |
|
78928b7eb4 | |
|
5236e1ef0d | |
|
be7ed8e74a | |
|
78bcb85679 | |
|
b98bb9eb27 | |
|
cad55a4da5 | |
|
511fc870a1 | |
|
dafd2fecf7 | |
|
9c5cca784e | |
|
1138165308 | |
|
d7eb321d58 | |
|
15f8c7014f | |
|
dd64e2060e | |
|
14d4551476 | |
|
e6549b0fe8 | |
|
157e05f57c | |
|
96efd1e8b9 | |
|
672ce40618 | |
|
05e9d76c77 | |
|
ea7fc7f6c4 | |
|
6b1d5d28be | |
|
a9b0bfa740 | |
|
cc6b80a55b | |
|
6707989cd5 | |
|
766439db6d | |
|
d4903caafa | |
|
caa0111c79 | |
|
ffacff67cf | |
|
f43a7fdfb4 | |
|
7bb1a3906d | |
|
530c259e71 | |
|
843a6851c4 | |
|
234051d6bc | |
|
981c0eb8bc | |
|
1066855532 | |
|
f2d6518311 | |
|
765ccfa18c | |
|
201aee3119 | |
|
65f299038d | |
|
bd1290706a | |
|
939cbf46e4 | |
|
aec5a09caf | |
|
6346d1b6c7 | |
|
05ee242b80 | |
|
64be3f05a3 | |
|
cfdce9be61 | |
|
446220494e | |
|
f12ffa92a0 | |
|
b2a4797d16 | |
|
8bfacb0091 | |
|
0501f3bd99 | |
|
ae3ae6fd0e | |
|
011a5f57e1 | |
|
84b3289a2a | |
|
b760892de2 | |
|
6840a8983c | |
|
a1b3490764 | |
|
076f1efecb | |
|
b23511edb1 | |
|
53e572f072 | |
|
c2b6ba0d5a | |
|
d4d530e6c8 | |
|
99270e96a9 | |
|
4bf7a08c80 | |
|
3823de6552 | |
|
30592b27ce | |
|
8bc6c2eb20 | |
|
87d52e5ebe | |
|
598a09c13d | |
|
54db4ab215 | |
|
5b5ccba247 | |
|
18e2559b1e | |
|
f5987b68ff | |
|
a1ab44d39f | |
|
8b1514e575 | |
|
2d3810874d | |
|
a4c634e4bf | |
|
2a4d986da5 | |
|
3b18730af6 | |
|
df260fe0e8 | |
|
2be4fe8404 | |
|
7db7cfe20f | |
|
cc2cc2849e | |
|
06f4b0b29a | |
|
e22821799b | |
|
14dd9294aa | |
|
83e3409bd8 | |
|
7630170ed6 | |
|
0af74fccda | |
|
e77de1804f | |
|
e00b92225d | |
|
ee8fca9439 | |
|
adc562feb8 | |
|
af8fa4014f | |
|
08415b0e8f | |
|
967f9a9174 | |
|
d255141bd4 | |
|
e7e1dd91c7 | |
|
195c5e92a4 | |
|
47dafe62b8 | |
|
26922ebb51 | |
|
7480e71801 | |
|
9ac3f48fcb | |
|
0e405755ad | |
|
693f90449f | |
|
0bf45069bd | |
|
34b10f61ce | |
|
1479b42112 | |
|
bb061d2a0a | |
|
061012a268 | |
|
a698f91db4 | |
|
30d12a76bc | |
|
687fa44eff | |
|
aefb33efff | |
|
da0c45b7ee | |
|
956335e844 | |
|
701fc600cd | |
|
e38b2cff4f | |
|
b268293402 | |
|
45b9dc8be9 | |
|
0ad7025a17 | |
|
49b7467840 | |
|
a40ef610a2 | |
|
f9943b60e4 | |
|
f97fb1b35d | |
|
b959181afd | |
|
cbc27a94ac | |
|
6a4f999b88 | |
|
7707e01352 | |
|
e667326df5 | |
|
f1b2d7530d | |
|
b6587b32b9 | |
|
b89bafa165 | |
|
9fac5f94ba | |
|
5affc73ee6 | |
|
027016ddae | |
|
8f2f98b7b4 | |
|
6b43d12469 | |
|
74940ed33c | |
|
17871bb3ca | |
|
89c4abc07a | |
|
46b5a7697c | |
|
dd8060d64f | |
|
afb1d7ef0c | |
|
93ddee5956 | |
|
0184e5beb5 | |
|
3f2e110eab | |
|
0ff68b7d66 | |
|
cdbdc1a822 | |
|
7a2be05c69 | |
|
5be38d0702 | |
|
ff203d8d34 | |
|
23fa39121c | |
|
b16e65168f | |
|
090a294017 | |
|
22bc4b4271 | |
|
e01c049401 | |
|
e7ef7a9e55 | |
|
e0011d22f2 | |
|
7d34bd8f82 | |
|
bd69cf599b | |
|
1ac7de9ee5 | |
|
397043a9c0 | |
|
43ff03e33b | |
|
fa900df5a7 | |
|
414f0f6cd9 | |
|
7ef751ba95 | |
|
f8c01418b1 | |
|
731330c6b5 | |
|
d2df55b1ac | |
|
0dd74e3c9d | |
|
9e073e9fa0 | |
|
c95a271821 | |
|
fe386fa819 | |
|
4d70322eab | |
|
d9b8391968 | |
|
6ac3681604 | |
|
b35cad0c2e | |
|
2106a9e373 | |
|
5050047cef | |
|
1b65f850ee | |
|
7c2933aae1 | |
|
96babec22a | |
|
c12d1e9bd9 | |
|
4d1f0e77f2 | |
|
d7bf34ecd0 | |
|
1943c3e7a1 | |
|
af050cc729 | |
|
84ef2161a1 | |
|
ca23360d07 | |
|
5dbe51a52e | |
|
99e30a4d7d | |
|
93975fdc45 | |
|
fbef90f7cb | |
|
a9db4dd9a3 | |
|
fc7e96fa66 | |
|
097d9a6479 | |
|
2dd0bfdece | |
|
26585bb1d9 | |
|
0d92719433 | |
|
1ab2722850 | |
|
fe17958b9f | |
|
3e4d410646 | |
|
b5aae34d86 | |
|
7f029b2713 | |
|
0424082e7b | |
|
2352c86048 | |
|
c0dff5e203 | |
|
d920785256 | |
|
45cea14165 | |
|
26ed689d30 | |
|
7ce0370b6a | |
|
aa2e53c4f8 | |
|
9ecd156622 | |
|
3c62be3f5d | |
|
d1c8273826 | |
|
7d1b7436d4 | |
|
65750586b6 | |
|
62f6128ba1 | |
|
7f5c850744 | |
|
9607b4205a | |
|
e447d5fccf | |
|
090c3c9930 | |
|
48ea9e1c1d | |
|
42469b83fe | |
|
e65bc040af | |
|
89ca4c8a8b | |
|
2323a55517 | |
|
45d6caa1d0 | |
|
55d2a64f54 | |
|
2420886c2c | |
|
e636dce919 | |
|
1a1f9b1cf2 | |
|
54d0cb47cd | |
|
23a2f8b903 | |
|
29fd682672 | |
|
efed67cbd0 | |
|
6234a5bc0b | |
|
54f64e7618 | |
|
f0105cb7fb | |
|
76b0f80169 | |
|
f84df175ce | |
|
58aca68726 | |
|
8670364f44 | |
|
418bc54f2b | |
|
9c36d7c338 | |
|
8141b9977d | |
|
c035d3c82c | |
|
1de9daa40f | |
|
efd8203232 | |
|
a667d88f93 | |
|
8d7dd577ab | |
|
054b41383f | |
|
02ad199138 | |
|
65c4f9db5b | |
|
51e38b7614 | |
|
90878a7c2b | |
|
7f028f70d5 | |
|
ec0cb5fd54 | |
|
682de62c57 | |
|
18253b6966 | |
|
21f05eb118 | |
|
d94f6388b7 | |
|
1971813336 | |
|
3c88d2aad3 | |
|
5c39903323 | |
|
6d59a2b45d | |
|
09c24088a2 | |
|
efdc4817bb | |
|
0c97abb053 | |
|
1759d247d9 | |
|
21dc64dc21 | |
|
264eff3ad0 | |
|
05c7b21b0a | |
|
40b626b0aa | |
|
6ebfe05fce | |
|
59abb5d344 | |
|
9594ee8e47 | |
|
7a4ec7f7a3 | |
|
51db1706e9 | |
|
ac9e71d9c7 | |
|
f3f5ca1bd8 | |
|
b979023b9d | |
|
915dea01c9 | |
|
566bb73897 | |
|
fb0d8d746f | |
|
1b24c4f279 | |
|
a5a041e637 | |
|
c469d9b3ff | |
|
373d166076 | |
|
3c54243ac7 | |
|
1f467580ee | |
|
1ba883719a | |
|
f73e954b28 | |
|
de629291b9 | |
|
7eb5d220fd | |
|
4cd9abdd15 | |
|
198dde8365 | |
|
b165340662 | |
|
5ad4340679 | |
|
b56fa4a29d | |
|
e946b392c9 | |
|
81689b5f02 | |
|
c18d926174 | |
|
00662bad46 | |
|
420a7b60ca | |
|
2ab3eb4109 | |
|
9314528b9b | |
|
78ef7e804f | |
|
3fc1edede0 | |
|
f50b4be162 | |
|
ccde8148a7 | |
|
b9bbf42538 | |
|
435636a655 | |
|
ba06269b14 | |
|
be01ccdb08 | |
|
b45d27593f | |
|
ff49d8b84f | |
|
5d78cd3016 | |
|
ed0b1a86aa | |
|
534b3e6a9a | |
|
d452cf4170 | |
|
f117027175 | |
|
acf79570ef | |
|
5a939cf252 | |
|
9bc7a72a25 | |
|
065ee9004c | |
|
6fb69a1dc2 | |
|
49889858fa | |
|
2424f38a62 | |
|
218456d127 | |
|
e7dd03e551 | |
|
0c8e9fe095 | |
|
5b4acec4ca | |
|
e26f360d93 | |
|
2cc9534570 | |
|
572c4052f6 | |
|
132286895f | |
|
454bee3f02 | |
|
41cad88d6e | |
|
40e341b0ee | |
|
5da59c9151 | |
|
f804e87f7d | |
|
dd3641f054 | |
|
b2291ce162 | |
|
c35ed6570b | |
|
d15cbb2d6a | |
|
b8cf6432ef | |
|
89805fb092 | |
|
692cec8458 | |
|
79897e77dc | |
|
649e9f4500 | |
|
3a6d31d3fa | |
|
2138eeb87f | |
|
102aa50a41 | |
|
5cfe249945 | |
|
a0138955cd | |
|
e1ffbb710b | |
|
c18f72ceb9 | |
|
bc1441a5eb | |
|
0f154c3173 | |
|
7b287a1d5b | |
|
4b332b5d42 | |
|
ec2f21bb9f | |
|
acd6c86f98 | |
|
ab150b458a | |
|
cf28ecd5a6 | |
|
99eed9c37a | |
|
f4e86028d0 | |
|
7cb71ec6fb | |
|
d054e5b4b7 | |
|
c69efc040c | |
|
c4bf1641bd | |
|
9213dd13bc | |
|
4273f43be1 | |
|
139bb5c2a5 | |
|
95e861bcdd | |
|
ca8e7b40bc | |
|
cc79f55817 | |
|
a1faa836c5 | |
|
1f8aaa6fba | |
|
6bfe833aa5 | |
|
0408aa9bbc | |
|
210b24b6e3 | |
|
dfe5a2d665 | |
|
3441f63298 | |
|
2cee2a85e6 | |
|
5001fa58e0 | |
|
d2cf7c7a5c | |
|
e544e6a62d | |
|
606a7cb313 | |
|
de4fdcd898 | |
|
2c471a89d0 | |
|
a5660b95b2 | |
|
54a92293da | |
|
812e9efd41 | |
|
c6130f33d7 | |
|
56a29dc176 | |
|
7e36a0be75 | |
|
7adc7e55a5 | |
|
4be726b130 | |
|
ff2486f58c | |
|
db933d5466 | |
|
8e31ab2de8 | |
|
83ca469ed7 | |
|
d2c0718f7d | |
|
c1f0704fa8 | |
|
64a63fa4be | |
|
ab39c6541a | |
|
51214ea1bf | |
|
8f89519bcf | |
|
e6d42e5c45 | |
|
961ab9768a | |
|
e4f48cbc73 | |
|
e8f853a197 | |
|
d760239748 | |
|
2fec718590 | |
|
f0cf54e194 | |
|
41ae05dd6e | |
|
40f55be587 | |
|
54c811ac2e | |
|
12a78c822e | |
|
e45b544a39 | |
|
d1e187570e | |
|
d4191c0d94 | |
|
c2957d8761 | |
|
891ced4da2 | |
|
8f8d6bddc0 | |
|
d2d12d5281 | |
|
0ba278041b | |
|
ccaa159a73 | |
|
7356a83a44 | |
|
2a68df81e2 | |
|
50ca2ac09e | |
|
b0ff2392a1 | |
|
001e95f973 | |
|
db0650cf10 | |
|
753de3d9f0 | |
|
668c4c8976 | |
|
d2bc399770 | |
|
1bd33e76e5 | |
|
9fa13079f2 | |
|
96d831c31a | |
|
07d5d3f936 | |
|
51820b15cb | |
|
9fafafb17e | |
|
7c6565880d | |
|
93a72ebd06 | |
|
4307968182 | |
|
5d83ab2289 | |
|
af80bad87a | |
|
87272f0cd7 | |
|
8b5e437936 | |
|
3939da4fb0 | |
|
d87fa1a7ea | |
|
910a57089b | |
|
861bf27e96 | |
|
36590fb5c7 | |
|
ce990c3026 | |
|
b833239a82 | |
|
6fec142760 | |
|
84b2438b3d | |
|
0e714d5eb4 | |
|
9b8e6e6629 | |
|
4146f2777b | |
|
64323c51e6 | |
|
a0200a7b10 | |
|
8add759b5d | |
|
2faf90bc2b | |
|
762d575d70 | |
|
2e77718f07 | |
|
f371fabe35 | |
|
79a15132da | |
|
bacc0db778 | |
|
3963afe343 | |
|
3b21cc90ae | |
|
4e900804b8 | |
|
9ec733904f | |
|
3352640d09 | |
|
637397dd2f | |
|
5edbb1d5c4 | |
|
f46240e879 | |
|
68627995f8 | |
|
951eac6bfa | |
|
0833d7b43f | |
|
1cd5b3c20c | |
|
c773e2ed07 | |
|
c4186bd5f0 | |
|
5f640da166 | |
|
7320104fd0 | |
|
d273c5e77c | |
|
8344f2f9ab | |
|
600bd7ee0f | |
|
adf1e54605 | |
|
a3de64f5fa | |
|
8216d09e06 | |
|
694cda6e40 | |
|
e227a87140 | |
|
6cd5763361 | |
|
0f1c740fe8 | |
|
3f3e81fcf2 | |
|
862993398d | |
|
225c7c6ab4 | |
|
58633ca373 | |
|
00d57d783e | |
|
f077e17b2a | |
|
2f8759d2d3 | |
|
a6bfbbf655 | |
|
9d8c9baa3f | |
|
c273bf98c9 | |
|
99a5b6e2ef | |
|
284a012509 | |
|
abc69fa3d5 | |
|
8b01e2d4a2 | |
|
0c3e5980d6 | |
|
951873098e | |
|
2b927b9729 | |
|
b523ccb893 | |
|
7bb3172591 | |
|
be657b9b18 | |
|
94e6b9fa65 | |
|
d8ea0eeba3 | |
|
6873244169 | |
|
805b4096e9 | |
|
0f74e123b8 | |
|
7f212106db | |
|
46c3da71b6 | |
|
e5cab047d6 | |
|
bf3e9eabd5 | |
|
19604015de | |
|
08bc0a2815 | |
|
3bc7113cc5 | |
|
24243268a6 | |
|
2e858fddb5 | |
|
12474addda | |
|
7c25e4ce5a | |
|
669d55707e | |
|
6caa9b6f95 | |
|
59ad639fa8 | |
|
43458cc74f | |
|
e935a33a4d | |
|
536bb74d53 | |
|
a872efdef2 | |
|
efd8be8207 | |
|
06a84da844 | |
|
24b71d1170 | |
|
0dc168a8c0 | |
|
76049b2e13 | |
|
393ac33e3c | |
|
deeb6d2756 | |
|
1ac831d4b1 | |
|
534969df32 | |
|
976ae64f88 | |
|
a12ffaa8a2 | |
|
ff8b3be8dc | |
|
56008869d6 | |
|
1c78671078 | |
|
cdcc36efde | |
|
336daa2cc5 | |
|
531211e09d | |
|
894065a67e | |
|
82cb2ea20b | |
|
95fc894303 | |
|
622007f2ee | |
|
cf76d2e7d9 | |
|
41f8974080 | |
|
3ed25425a3 | |
|
49f8c4268f | |
|
70c224d8ea | |
|
6a7335e5c9 | |
|
3aaa755c51 | |
|
c140dd2a65 | |
|
dabd79afa7 | |
|
6ccb1bbbf9 | |
|
9b841cb823 | |
|
c1cac43f28 | |
|
8564b59e14 | |
|
fdb6dc94ba | |
|
327b330338 | |
|
7090388c70 | |
|
ff550785a7 | |
|
d7a3aca5d4 | |
|
056586486d | |
|
42a1d26d5b | |
|
cc572af390 | |
|
4275d2ce48 | |
|
4a87d6052b | |
|
d86ecf4f61 | |
|
09b7cb2f33 | |
|
4c3da66c92 | |
|
60e755210c | |
|
ab9954fe73 | |
|
fd18071975 | |
|
b88a7e0c6b | |
|
cdf53f33f6 | |
|
25485069e6 | |
|
f80bd040c8 | |
|
bc5e8add19 | |
|
7a8fd6d04a | |
|
b749963b62 | |
|
6a068fb9f9 | |
|
19ed0cb9bf | |
|
d08e876319 | |
|
f2a6f213dd | |
|
f7a5bfbccd | |
|
6624821f55 | |
|
328acd894f | |
|
01b17eaadc | |
|
fd16d6802c | |
|
fed2738805 | |
|
9ad2ea8205 | |
|
f8f7e2208f | |
|
8630f71f0c | |
|
9e44e8be75 | |
|
cadb56d8ba | |
|
cd646a1ab7 | |
|
c9e9cfd361 | |
|
ac96ea3370 | |
|
ef53aab24a | |
|
cb5a5d3ed1 | |
|
d1444761a3 | |
|
95af728e39 | |
|
24d87acec4 | |
|
84040e64b8 | |
|
641a2dec00 | |
|
9e69053e64 | |
|
1a415a380a | |
|
a19c5c672b | |
|
3bcbc7c018 | |
|
b371abade5 | |
|
9d4101e0c5 | |
|
c791a1c200 | |
|
bd3f2bb61c | |
|
7ca9a961b3 | |
|
cbb9422ee0 | |
|
2c2ee6636f | |
|
3beebd721f | |
|
048bbf874a | |
|
d2970b5b8d | |
|
0004d3b435 | |
|
d5aa08baa5 | |
|
5362962d9a | |
|
bfe6d60e9e | |
|
c084ce8b01 | |
|
381cfdad77 | |
|
cfc7313451 | |
|
bc8fd1285f | |
|
8e5224ef53 | |
|
fda0dfec30 | |
|
d6ae1bcf36 | |
|
5044810451 | |
|
147cd93ed3 | |
|
07b5920f3f | |
|
2bcf934389 | |
|
1405712f26 | |
|
a8ffe6b3a6 | |
|
613d6640fe | |
|
885ed218fa | |
|
b1f431d962 | |
|
06e176e6b9 | |
|
b7771e95ac | |
|
3e33fc156b | |
|
438f01a856 | |
|
5dd8189048 | |
|
ff6b127c3a | |
|
c3ed700324 | |
|
ba2f09a4e6 | |
|
c92c1b8e2b | |
|
c27d20abd8 | |
|
99d7b0917d | |
|
8b4e129fc1 | |
|
240d57f4b4 | |
|
f6c312a6b3 | |
|
ca34ece8ef | |
|
71ecdd7245 | |
|
b80b59ce24 | |
|
640d27748f | |
|
42772611ce | |
|
6e676ee8c5 | |
|
066fb44105 | |
|
3f638f125e | |
|
35f8f44510 | |
|
0c590821ed | |
|
3c311efb81 | |
|
6ef153b6ca | |
|
130a69bac6 | |
|
b5d873dfae | |
|
d5e0900cbb | |
|
a63006afaf | |
|
7c6832cee4 | |
|
0b5fcb3f88 | |
|
f33c0bad99 | |
|
18f61c5427 | |
|
229082a1b2 | |
|
0db17e9d39 | |
|
2d34b2b8a3 | |
|
76f48f095c | |
|
d98f84f9d3 | |
|
240a687d8f | |
|
35b46e37bb | |
|
14d24bce98 | |
|
ea0d7156a4 | |
|
38c267b4c8 | |
|
81228d3401 | |
|
87ff38664a | |
|
b7007016c0 | |
|
3566ddab00 | |
|
6ee4d10651 | |
|
e9b7cf332d | |
|
4ee2f0f789 | |
|
18e90d305d | |
|
fd1b331b86 | |
|
5c51d09309 | |
|
c445ea6266 | |
|
e685f38fd1 | |
|
627b8cc140 | |
|
657f95d3f0 | |
|
07f8c972b3 | |
|
fc8a000e46 | |
|
6f6ccbfa04 | |
|
c71ac588b2 | |
|
2d0da93ac4 | |
|
fb801044ec | |
|
ce87e0a605 | |
|
f3d9da9549 | |
|
71cad2fc38 | |
|
61c7a68f4a | |
|
c17cdb39d5 | |
|
69a39b7077 | |
|
19696bdad3 | |
|
00dd7d85ce | |
|
0c88c74b76 | |
|
c4a640bcb6 | |
|
0fdf8b6f68 | |
|
37bc52988c | |
|
1312a9dad2 | |
|
73b9c28457 | |
|
29db8d0d40 | |
|
5bf0f9742a | |
|
fc5bfa2cb3 | |
|
f7e38ccfe4 | |
|
128202a55a | |
|
89d74a9088 | |
|
63f64619bc | |
|
518fd3fad0 | |
|
74b77ba2ac | |
|
888e44ce0b | |
|
b3b8376cca | |
|
9c143d18b9 | |
|
9a6cc0dc2d | |
|
8c792f9c6d | |
|
adc5ff9e99 | |
|
bde9ff9319 | |
|
75fdf4c3c6 | |
|
b723ed5fd2 | |
|
89c975350d | |
|
f27974dd3c | |
|
744ff40ba0 | |
|
d4c9f371a6 | |
|
2aabf43ace | |
|
a6ca9fd453 | |
|
799aab02f5 | |
|
0fae2c7309 | |
|
7ba72b1507 | |
|
f21d4e794c | |
|
27eb5af6f0 | |
|
68a913c838 | |
|
a084db6817 | |
|
bc4d272c4f | |
|
57605c2b3e | |
|
1cd99631e1 | |
|
f67ed9a66e | |
|
6c11dac113 | |
|
39bc6c43dc | |
|
5ee3506474 | |
|
c5119cda97 | |
|
785de5f9b9 | |
|
8f630a97df | |
|
56bb419884 | |
|
d7a00e8cc2 | |
|
9f0797047f | |
|
e8a0eeb0b4 | |
|
3e5645d453 | |
|
2301654c4e | |
|
0b93fce923 | |
|
6304388100 | |
|
96b1186c19 | |
|
43c23db4a6 | |
|
8ff4e1f8c8 | |
|
b57811b371 | |
|
23f429e23b | |
|
7b1c573ad0 | |
|
4ee759aa3b | |
|
48096b18c2 | |
|
06af9f3187 | |
|
16eb055737 | |
|
a34f2e6e0d | |
|
8357dd7b3d | |
|
5bdf7d313f | |
|
5b6569f2c9 | |
|
58f4ed22c4 | |
|
245f58cc3a | |
|
15cd59b8ce | |
|
d9a5e471f5 | |
|
d7ecf94347 | |
|
6eda65eea6 | |
|
d45bf363f1 | |
|
1abf787596 | |
|
3b283cb860 | |
|
4a01e4c3b4 | |
|
61694268e2 | |
|
399d5338c6 | |
|
a8d74f604e | |
|
cb76f1ea75 | |
|
459afef89e | |
|
8621d8a155 | |
|
1c26a402b0 | |
|
b573701625 | |
|
a69e5fb288 | |
|
1e6930a3bc | |
|
11db49e2d3 | |
|
c33e658f98 | |
|
22252cfcf0 | |
|
976114fde1 | |
|
9d55cf1d80 | |
|
ef68e12125 | |
|
669b2ace4e | |
|
0e67c6318b | |
|
959fb64752 | |
|
ffa80d0466 | |
|
e7ca83ecb2 | |
|
f6261e5dc9 | |
|
d1bbbf48f6 | |
|
3ba15b41a3 | |
|
7a7c5e433e | |
|
6bb2c80bdd | |
|
af4af1cae9 | |
|
b9c779ff7e | |
|
2a469241b2 | |
|
83b165ebb0 | |
|
c5f9f0c307 | |
|
9d827037ca | |
|
58506c5bd1 | |
|
dd0b8c4140 | |
|
d1183f0bf6 | |
|
11717f90c1 | |
|
a740bf8df4 | |
|
63dc2b6aa6 | |
|
eee0537053 | |
|
7ec860a3d4 | |
|
9b9a6b2cfc | |
|
f178fa6d5b | |
|
9ebf29991d | |
|
8fb5f97a18 | |
|
921d95d18f | |
|
1fcf122c50 | |
|
dd9af56e21 | |
|
39a5c52088 | |
|
660f7cbfeb | |
|
1abf54d652 | |
|
3750d29b2b | |
|
e75522a005 | |
|
5972c73950 | |
|
41757b5f6c | |
|
c33c7f8b3b | |
|
4648f6718e | |
|
b30af0edca | |
|
5e1725abb2 | |
|
6ad2f23259 | |
|
0d725f68e3 | |
|
0770ba0936 | |
|
c13b5aecfe | |
|
3892579049 | |
|
ba7e1b9ca5 | |
|
f0772e385c | |
|
3fd94b1acb | |
|
93c5755012 | |
|
323de3c866 | |
|
52b9fddfd7 | |
|
e4041ce5ec | |
|
04cfbca336 | |
|
7184514b5d | |
|
dfb18d38f7 | |
|
9901f95d78 | |
|
b2139c0b1e | |
|
6f002c926a | |
|
a711462ef4 | |
|
f31c6b847f | |
|
195ccf4f53 | |
|
5786ffe7d2 | |
|
073edd0b8e | |
|
2c136dae2d | |
|
250789aa20 | |
|
8032824054 | |
|
7aaea786c2 | |
|
ce8e8e68f4 | |
|
80e7a89f67 | |
|
b780df8be0 | |
|
c4210b5810 | |
|
38e72019c7 | |
|
d745fca86a | |
|
60a2185ee6 | |
|
d634fec8dc | |
|
e33bf62bba | |
|
2dcd4ed131 | |
|
c9243f0d1e | |
|
0f6c19a1b7 | |
|
642929f071 | |
|
2746419c8c | |
|
b5a7246ba7 | |
|
c1d82282d9 | |
|
0abe30af38 | |
|
089da2608c | |
|
85c6149138 | |
|
79f3aa5419 | |
|
ed5f4d64a8 | |
|
569e76a848 | |
|
3a36c30e80 | |
|
a09232a555 | |
|
5a94818210 | |
|
2441f208c6 | |
|
db2eca697e | |
|
81d79cca61 | |
|
871c792976 | |
|
9ab7e76a3b | |
|
8480ffe108 | |
|
e1400f9680 | |
|
77ff585248 | |
|
9cb50cba33 | |
|
6e5bce3c57 | |
|
f75adab9d8 | |
|
5d0a6e7b08 | |
|
b0c8a9cdc4 | |
|
ebe0adb3b5 | |
|
4842d5e2b9 | |
|
0b8396b1a6 | |
|
55ef793a74 | |
|
96c0ad0d3d | |
|
6fcb191ca0 | |
|
3df97c36e6 | |
|
c732297623 | |
|
ca5a097ef5 | |
|
1f8a5f0ce5 | |
|
1fa7a1cac4 | |
|
ea4a70c3b3 | |
|
aa0929614a | |
|
3c31fc9c4b | |
|
7eb2c140fe | |
|
659adb89a6 | |
|
dd17124c77 | |
|
453a5387cb | |
|
643e87a076 | |
|
20eafb4cc4 | |
|
e715d52f80 | |
|
3139391e06 | |
|
64ad752e73 | |
|
dddfa308d7 | |
|
a78a7ed156 | |
|
b4eddf04c4 | |
|
2a851b52f1 | |
|
b245a55ea0 | |
|
c4f6c859c1 | |
|
834bf33e57 | |
|
1a6100f083 | |
|
de927b6b05 | |
|
1d7795e22c | |
|
abe8810d47 | |
|
acf125c853 | |
|
925df39107 | |
|
9279bbbd19 | |
|
9fd9a8b5b1 | |
|
3c6be319b1 | |
|
104894c0c7 | |
|
2f52001c6d | |
|
f609170a6a | |
|
9f75b04714 | |
|
7b4a2fe3d1 | |
|
f6ee4b3496 | |
|
1ef0534b69 | |
|
b9dd1895bb | |
|
be47743dfa | |
|
57798e5d76 | |
|
cd64c1cfec | |
|
f76d921e25 | |
|
a2bf474013 | |
|
9729e5a05b | |
|
2dce0a0415 | |
|
d560137ae6 | |
|
c12f4fb40f | |
|
10169d773d | |
|
b78596dcf4 | |
|
40f7c6b8fa | |
|
8063700d7b | |
|
70f89ed1fc | |
|
c2cf98e32f | |
|
a1db032ba9 | |
|
5babd7b4f2 | |
|
c0029a2fa2 | |
|
4e35b8b49d | |
|
21c6135ae2 | |
|
b02c486ad0 | |
|
08cd285ca6 | |
|
94af856db0 | |
|
bf5d74b8bc | |
|
fd9210ba9e | |
|
66d9260257 | |
|
5d2bfc858e | |
|
8b34880064 | |
|
98c698d9ec | |
|
9ea2c1d932 | |
|
297e65f19a | |
|
65559a3e44 | |
|
c3a71e94a9 | |
|
fb4b363a16 | |
|
4154f43b49 | |
|
b1fcb0b58f | |
|
00409ffa60 | |
|
16acd50559 | |
|
5df48804e1 | |
|
9eab6710ce | |
|
65424a6769 | |
|
12d53ac233 | |
|
9bcfb34524 | |
|
f88b9ae4f2 | |
|
8cd91f5a6a | |
|
f65e5f4190 | |
|
f521a98157 | |
|
ee4ef6638c | |
|
0910958c04 | |
|
d6408bcf17 | |
|
178fc00905 | |
|
89d4fa4d9b | |
|
46e1419e70 | |
|
f7c4bc908e | |
|
a0ecbed726 | |
|
74fc0aa308 | |
|
d5f0448e48 | |
|
51e4b11890 | |
|
d713f252aa | |
|
5ec2d85081 | |
|
0d132ee518 | |
|
fabbb9f531 | |
|
80c8d52dc5 | |
|
b6c4a2dbf1 | |
|
a2f5ad7bed | |
|
a337f414fc | |
|
404b3dd44c | |
|
d04b031e30 | |
|
07fec6e211 | |
|
4cd72992c8 | |
|
cdcb395640 | |
|
5a8eb51968 | |
|
a74422281f | |
|
a45f9ee76b | |
|
f19dc114d6 | |
|
d2aabb669b | |
|
9c3f4039a5 | |
|
1fb305fa45 | |
|
4086d7c3be | |
|
60fe5a656c | |
|
7d254c26bc | |
|
328d67f551 | |
|
84ecf861cd | |
|
46a6daccfe | |
|
3df3c37bad | |
|
191a24110a | |
|
275a730485 | |
|
9a7b2587af | |
|
09c824b90e | |
|
57e59d998f | |
|
44fb3945df | |
|
95c4e608de | |
|
b9603d9d23 | |
|
16e5f96b1d | |
|
d0a0e3bdef | |
|
5dcc53bcf2 | |
|
23543b15ca | |
|
2253c45feb | |
|
9f9a70713e | |
|
cd57adc856 | |
|
b4b892148c | |
|
048183ddb5 | |
|
f09b82c4b5 | |
|
7aeb8e4d36 | |
|
58ec4d6a31 | |
|
25f8343269 | |
|
e9cb844c28 | |
|
30ac046232 | |
|
f67f8ccbe0 | |
|
e9217b3484 | |
|
11b68f6a53 | |
|
3fe67e4882 | |
|
9288537949 | |
|
78536f9678 | |
|
cce2f3e19a | |
|
297141f321 | |
|
c7ec19c25c | |
|
3181ea7b4d | |
|
1cf464d5c7 | |
|
83a6e6f637 | |
|
64890bb640 | |
|
caca6dc701 | |
|
68ec443e07 | |
|
aa86125f2b | |
|
c423164066 | |
|
14aeb8de10 | |
|
691b17e0cc | |
|
eae6119039 | |
|
30f81e1696 | |
|
01311b470f | |
|
223d2ac3a6 | |
|
4cdeb98897 | |
|
47c69e9def | |
|
9728947d5d | |
|
66fe48e94b | |
|
c0fe4756cb | |
|
5032e79be3 | |
|
1aec3fcedd | |
|
aec63081e2 | |
|
801025ad7b | |
|
59b10c4d25 | |
|
841584ccbd | |
|
24993f6020 | |
|
55ea5c5488 | |
|
290b81dedc | |
|
c6b5cc1e07 | |
|
357081346e | |
|
896b919c9d | |
|
6b73f4d187 | |
|
08a806e75d | |
|
73c11c3d29 | |
|
5af77dcfb9 | |
|
a7dc7ecb90 | |
|
cf88142856 | |
|
efb355c7be | |
|
d1ad38c8d4 | |
|
69b94dad00 | |
|
4c40aa6b38 | |
|
5cf3221364 | |
|
40055f023c | |
|
9fbb2b9369 | |
|
a3aca67eef | |
|
977652ad57 | |
|
5bf7ca1c80 | |
|
1b1f22c35e | |
|
479817231a | |
|
e3c5477df4 | |
|
b403155ca9 | |
|
9fd3111011 | |
|
5f339d585c | |
|
b168462b43 | |
|
68691faca1 | |
|
1e075c248f | |
|
d201e65810 | |
|
3a79880e69 | |
|
9228df0f23 | |
|
402ad85583 | |
|
53e91ba98a | |
|
fd153f3762 | |
|
813d0f1b5f | |
|
2b901abfb1 | |
|
d4289f9e74 | |
|
3a5d6914ba | |
|
e96efea2fb | |
|
2df7f8a1e5 | |
|
8645c4c653 | |
|
88e92eec9e | |
|
a41b8e416f | |
|
8716c8baf4 | |
|
2d67a7153b | |
|
3fcc7c6768 | |
|
5d2a062b36 | |
|
e517ff6b6d | |
|
7296846a81 | |
|
6abcb0de9b | |
|
40f9d9d9bc | |
|
7aea8c45f7 | |
|
7bbdee6cc4 | |
|
efb3271588 | |
|
ff548bd898 | |
|
835d32814d | |
|
79d1f665f2 | |
|
9c818d3da0 | |
|
a378e59432 | |
|
a75a3f7a5f | |
|
0f412e570c | |
|
1daa1f5611 | |
|
24a190d1f7 | |
|
b48b239882 | |
|
5fb69300ca | |
|
dcb23f686f | |
|
650570e57d | |
|
1f5a36f074 | |
|
aeaf2cd3f1 | |
|
959c0fc572 | |
|
e3d3788f28 | |
|
850b7f27a2 | |
|
0939f23b04 | |
|
67886b0c5a | |
|
56eb6fb4ce | |
|
81cf389754 | |
|
0907965dc5 | |
|
79315d318c | |
|
e8f820ef8d | |
|
210d90ec79 | |
|
8bea18a6f2 | |
|
861966eed6 | |
|
5bb1f2a48c | |
|
fb17af4844 | |
|
9a8512887f | |
|
b8ec8918b7 | |
|
ff83f967d8 | |
|
998999a755 | |
|
09666adc53 | |
|
ec73db0057 | |
|
833642d405 | |
|
f04399c3a0 | |
|
f2d767b799 | |
|
3e93dae53c | |
|
187bb046aa | |
|
dd4973ac35 | |
|
b021d3eebd | |
|
15842db83e | |
|
869de7283f | |
|
a2ee543fa1 | |
|
eb24d32383 | |
|
565e3db22d | |
|
faf14b880e | |
|
faa7bc6043 | |
|
03c64b950b | |
|
27147790fd | |
|
1658e925f2 | |
|
82dcec9576 | |
|
80d9f6131b | |
|
e0a72defa2 | |
|
e0a7e242f8 | |
|
6acb86c14b | |
|
8ec675cca6 | |
|
591d03de95 | |
|
ec316391c5 | |
|
7543fadfa8 | |
|
bbaf1223f3 | |
|
177b205c48 | |
|
729ff267d7 | |
|
d9be14e1fb | |
|
8a9f9b07e7 | |
|
c989a01913 | |
|
5188efcc57 | |
|
c05a5b796b | |
|
f0058e67c2 | |
|
508d6311de | |
|
7c6bf40d0d | |
|
a74343c589 | |
|
d188576ef3 | |
|
d922c5e1d0 | |
|
0adf24fcad | |
|
80e13965d9 | |
|
36707ec87a | |
|
5f89f083a2 | |
|
0d9422ead8 | |
|
cae0a1cc60 | |
|
480d92fce5 | |
|
49fe3d0d4f | |
|
ff2e2937a5 | |
|
0cc1fb53d5 | |
|
512be884ed | |
|
f1667b398a | |
|
b0ec0f1a1a | |
|
05046d6e93 | |
|
054c5450df | |
|
12e42f40c5 | |
|
890aa9aa15 | |
|
bcfd838131 | |
|
9c07add00f | |
|
ab8aac7dcf | |
|
f36b94d039 | |
|
92daa831d1 | |
|
e7a170c89f | |
|
3c5d3eb8ad | |
|
55bb0084aa | |
|
acf5660090 | |
|
9d7530662f | |
|
9cf09165b5 | |
|
e1a6e7c3ac | |
|
3537d53d5c | |
|
c20ba3064d | |
|
a22caa38d2 | |
|
dc0f8b383f | |
|
f1a0625b61 | |
|
02b961fd7e | |
|
1d42b26fce | |
|
6e01e04922 | |
|
79812b34b0 | |
|
b7c2ea8d46 | |
|
cd74b2167d | |
|
9b2a577fc3 | |
|
2dd09163e6 | |
|
850ff93940 | |
|
fb6add2b4a | |
|
48445f12ac | |
|
792fad2a03 | |
|
cc04990ce3 | |
|
7530482cc2 | |
|
33d8c518e9 | |
|
e0ce2394fe | |
|
fadce063a7 | |
|
ef0263e32d | |
|
4588e25d27 | |
|
ff8c0086e2 | |
|
dc1b7cf08f | |
|
26fe6ad898 | |
|
fc71d2f7c4 | |
|
35e949ef5e | |
|
9c506ef85b | |
|
b1c7af38d0 | |
|
ea0c9b639f | |
|
0030f035be | |
|
c26529ae86 | |
|
7d57d2fcfb | |
|
e8a0df54b1 | |
|
33393335c8 | |
|
c5b02bb9f5 | |
|
a1047918d2 | |
|
fd81e31050 | |
|
160315c4d0 | |
|
d9b7747fc5 | |
|
dcce18799f | |
|
a9a15ea2c0 | |
|
d63716db96 | |
|
3ca623349a | |
|
121f4bc1dd | |
|
8e31ef9905 | |
|
2c52e0aad8 | |
|
06916f56be | |
|
c3df0bd15e | |
|
b41a8e2829 | |
|
96ac072166 | |
|
295a27f16a | |
|
88f60b5e41 | |
|
1c22e90fa0 | |
|
d23604b0d5 | |
|
c5347e6707 | |
|
bf0c7b9ae8 | |
|
0374b68fa1 | |
|
633929629c | |
|
6a3b3213cf | |
|
fdb4eb6042 | |
|
998ae511a3 | |
|
c897b90c28 | |
|
3f9d6f0311 | |
|
8ee63f8264 | |
|
34358b8471 | |
|
5b1af4ec47 | |
|
071d7af58a | |
|
3201c3654e | |
|
6ed3023725 | |
|
892a63aec5 | |
|
3fe889d4a4 | |
|
c35e7368f2 | |
|
cafd546ce8 | |
|
5810a77cbf | |
|
6f7045ead2 | |
|
40f9a42c00 | |
|
9abe1f27bb | |
|
aa2aee684b | |
|
cfa025acae | |
|
781cc78a1f | |
|
f924ac9265 | |
|
df6e8a6562 | |
|
3f01f936a1 | |
|
25ddc24754 | |
|
989f9ec5fe | |
|
3fbee2c835 | |
|
b10b3cbe3b | |
|
77e94e1d3b | |
|
4a3bfaff90 | |
|
2a8dc58b6a | |
|
8a8793fd2d | |
|
7ab9e420ac | |
|
43ca62de47 | |
|
a5830c5424 | |
|
b6896a6d85 | |
|
9e0b4132ee | |
|
68158324f4 | |
|
a420be6b20 | |
|
f281543255 | |
|
67ff01e915 | |
|
dcf0969e2d | |
|
4f522d337a | |
|
af0979ec32 | |
|
63e863ad35 | |
|
7f95444bb5 | |
|
783842bac2 | |
|
e1534ad25b | |
|
ee1f3623ce | |
|
dffdb51713 | |
|
daa35aaf7d | |
|
b87dae7e7c | |
|
86d777e2eb | |
|
2ffee63889 | |
|
c936aac777 | |
|
46d65471d9 | |
|
dcc174b62e | |
|
bdc7a99c59 | |
|
2aad357c18 | |
|
ccf51cec5c | |
|
4c5176f751 | |
|
d71f1f24e4 | |
|
cbb0f6be9a | |
|
a60f8098ee | |
|
8bffbf64b9 | |
|
d91a5bccf4 | |
|
1ca0d3a3c1 | |
|
653f688567 | |
|
8b5d8d9f8a | |
|
c45ada6ccb | |
|
db65cfeb8a | |
|
a3ba6da6f3 | |
|
b048630e5b | |
|
dcee92a6bc | |
|
c2e3b422cc | |
|
7b7f4eb141 | |
|
d4c03d3939 | |
|
5d459130a7 | |
|
8e68d2e3ea | |
|
30c33b55e3 | |
|
e2791e5260 | |
|
88a8bd659d | |
|
f9cc114907 | |
|
9a4d603a62 | |
|
78ea4b2207 | |
|
ab3cdea548 | |
|
10e0c90fde | |
|
dd84a2175f | |
|
ae05ad3f38 | |
|
62fb233eb1 | |
|
cd0d10b64e | |
|
426b1482dd | |
|
16d9d29971 | |
|
a4fb805315 | |
|
fa8e921ee8 | |
|
e5c3486826 | |
|
8e626b8d1f | |
|
ea900ad744 | |
|
6a6717cdd3 | |
|
c7286396d8 | |
|
0cebf248a3 | |
|
686b425eb9 | |
|
666051fd34 | |
|
e55860eb6b | |
|
32c3aca52f | |
|
9fd17ef73c | |
|
1fade1aa9e | |
|
814f0b215d | |
|
5fd26b4ea8 | |
|
e5eab8bae4 | |
|
75c4f35e85 | |
|
1be8b2f514 | |
|
633414bd20 | |
|
7ca6cd61be | |
|
d6e4430692 | |
|
efe73caf1b | |
|
3ef72e8a7b | |
|
e9da63ad79 | |
|
9b56801c3d | |
|
5f6c58ffd2 | |
|
74949401bd | |
|
4ffc69a6e4 | |
|
61ac9833be | |
|
dbf7d9f19e | |
|
a6e5a0b704 | |
|
337463ec16 | |
|
072954480d | |
|
3652d11059 | |
|
54824aec74 | |
|
a61cf61fd1 | |
|
75e85def83 | |
|
f9ae1f0023 | |
|
896b4c280c | |
|
ce3f268075 | |
|
8edabaea55 | |
|
ec22b86e00 | |
|
e4bcd98904 | |
|
cf2be54e8f | |
|
3ab62e83d3 | |
|
3570764448 | |
|
6188023fb5 |
|
@ -1,7 +1,5 @@
|
||||||
*.img
|
|
||||||
isodir
|
|
||||||
sysroot
|
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
bochsrc
|
build/
|
||||||
bx_enh_dbg.ini
|
base/
|
||||||
|
script/fakeroot-context
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
# .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
|
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "banan-os",
|
||||||
|
"includePath": [
|
||||||
|
"${workspaceFolder}/BAN/include",
|
||||||
|
"${workspaceFolder}/kernel/include",
|
||||||
|
"${workspaceFolder}/userspace/libraries/*/include"
|
||||||
|
],
|
||||||
|
"defines": [
|
||||||
|
"__arch=x86_64"
|
||||||
|
],
|
||||||
|
"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"
|
||||||
|
],
|
||||||
|
"compilerPath": "${workspaceFolder}/toolchain/local/bin/x86_64-banan_os-gcc",
|
||||||
|
"cStandard": "c17",
|
||||||
|
"cppStandard": "gnu++20",
|
||||||
|
"intelliSenseMode": "linux-gcc-x64"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 4
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"cmake.configureOnOpen": false,
|
||||||
|
"editor.tabSize": 4,
|
||||||
|
"editor.insertSpaces": false,
|
||||||
|
"editor.detectIndentation": false
|
||||||
|
}
|
|
@ -1,3 +0,0 @@
|
||||||
*.a
|
|
||||||
*.d
|
|
||||||
*.o
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
#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
|
|
@ -1,9 +0,0 @@
|
||||||
#include <BAN/Memory.h>
|
|
||||||
|
|
||||||
void* operator new(size_t size) { return BAN::allocator(size); }
|
|
||||||
void* operator new[](size_t size) { return BAN::allocator(size); }
|
|
||||||
|
|
||||||
void operator delete(void* addr) { BAN::deallocator(addr); }
|
|
||||||
void operator delete[](void* addr) { BAN::deallocator(addr); }
|
|
||||||
void operator delete(void* addr, size_t) { BAN::deallocator(addr); }
|
|
||||||
void operator delete[](void* addr, size_t) { BAN::deallocator(addr); }
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
#include <BAN/New.h>
|
||||||
|
|
||||||
|
void* operator new(size_t size) { return BAN::allocator(size); }
|
||||||
|
void* operator new[](size_t size) { return BAN::allocator(size); }
|
||||||
|
|
||||||
|
void operator delete(void* addr) { BAN::deallocator(addr); }
|
||||||
|
void operator delete[](void* addr) { BAN::deallocator(addr); }
|
||||||
|
void operator delete(void* addr, size_t) { BAN::deallocator(addr); }
|
||||||
|
void operator delete[](void* addr, size_t) { BAN::deallocator(addr); }
|
|
@ -1,262 +0,0 @@
|
||||||
#include <BAN/Errors.h>
|
|
||||||
#include <BAN/Math.h>
|
|
||||||
#include <BAN/Memory.h>
|
|
||||||
#include <BAN/Move.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,143 +1,11 @@
|
||||||
#include <BAN/String.h>
|
#include <BAN/String.h>
|
||||||
#include <BAN/StringView.h>
|
#include <BAN/StringView.h>
|
||||||
#include <BAN/Vector.h>
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
StringView::StringView()
|
|
||||||
{ }
|
|
||||||
|
|
||||||
StringView::StringView(const String& other)
|
StringView::StringView(const String& other)
|
||||||
: StringView(other.data(), other.size())
|
: 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)
|
|
||||||
{
|
|
||||||
// FIXME: Won't work while multithreading
|
|
||||||
static char s_delim = delim;
|
|
||||||
return split([](char c){ return c == s_delim; }, allow_empties);
|
|
||||||
}
|
|
||||||
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
#include <BAN/Time.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
static constexpr bool is_leap_year(uint64_t year)
|
||||||
|
{
|
||||||
|
if (year % 400 == 0)
|
||||||
|
return true;
|
||||||
|
if (year % 100 == 0)
|
||||||
|
return false;
|
||||||
|
if (year % 4 == 0)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr uint64_t leap_days_since_epoch(const BAN::Time& time)
|
||||||
|
{
|
||||||
|
uint64_t leap_years = 0;
|
||||||
|
for (uint32_t year = 1970; year < time.year; year++)
|
||||||
|
if (is_leap_year(year))
|
||||||
|
leap_years++;
|
||||||
|
if (is_leap_year(time.year) && time.month >= 3)
|
||||||
|
leap_years++;
|
||||||
|
return leap_years;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr uint64_t month_days[] { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
|
||||||
|
|
||||||
|
uint64_t to_unix_time(const BAN::Time& time)
|
||||||
|
{
|
||||||
|
uint64_t years = time.year - 1970;
|
||||||
|
uint64_t days = years * 365 + month_days[time.month - 1] + leap_days_since_epoch(time) + (time.day - 1);
|
||||||
|
uint64_t hours = days * 24 + time.hour;
|
||||||
|
uint64_t minutes = hours * 60 + time.minute;
|
||||||
|
uint64_t seconds = minutes * 60 + time.second;
|
||||||
|
return seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::Time from_unix_time(uint64_t unix_time)
|
||||||
|
{
|
||||||
|
BAN::Time time {};
|
||||||
|
|
||||||
|
time.second = unix_time % 60; unix_time /= 60;
|
||||||
|
time.minute = unix_time % 60; unix_time /= 60;
|
||||||
|
time.hour = unix_time % 24; unix_time /= 24;
|
||||||
|
|
||||||
|
uint64_t total_days = unix_time;
|
||||||
|
|
||||||
|
time.week_day = (total_days + 4) % 7 + 1;
|
||||||
|
|
||||||
|
time.year = 1970;
|
||||||
|
while (total_days >= 365U + is_leap_year(time.year))
|
||||||
|
{
|
||||||
|
total_days -= 365U + is_leap_year(time.year);
|
||||||
|
time.year++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_leap_day = is_leap_year(time.year) && total_days == month_days[2];
|
||||||
|
bool had_leap_day = is_leap_year(time.year) && total_days > month_days[2];
|
||||||
|
|
||||||
|
for (time.month = 1; time.month < 12; time.month++)
|
||||||
|
if (total_days < month_days[time.month] + (is_leap_day || had_leap_day))
|
||||||
|
break;
|
||||||
|
|
||||||
|
time.day = total_days - month_days[time.month - 1] + !had_leap_day;
|
||||||
|
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
set(BAN_SOURCES
|
||||||
|
BAN/Assert.cpp
|
||||||
|
BAN/New.cpp
|
||||||
|
BAN/StringView.cpp
|
||||||
|
BAN/Time.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(ban ${BAN_SOURCES})
|
||||||
|
banan_link_library(ban libc)
|
||||||
|
|
||||||
|
banan_install_headers(ban)
|
||||||
|
install(TARGETS ban OPTIONAL)
|
89
BAN/Makefile
89
BAN/Makefile
|
@ -1,89 +0,0 @@
|
||||||
DEFAULT_HOST!=../default-host.sh
|
|
||||||
HOST?=DEFAULT_HOST
|
|
||||||
HOSTARCH!=../target-triplet-to-arch.sh $(HOST)
|
|
||||||
|
|
||||||
CFLAGS?=-O2 -g
|
|
||||||
CPPFLAGS?=
|
|
||||||
LDFLAGS?=
|
|
||||||
LIBS?=
|
|
||||||
|
|
||||||
DESTDIR?=
|
|
||||||
PREFIX?=/usr/local
|
|
||||||
EXEC_PREFIX?=$(PREFIX)
|
|
||||||
INCLUDEDIR?=$(PREFIX)/include
|
|
||||||
LIBDIR?=$(EXEC_PREFIX)/lib
|
|
||||||
|
|
||||||
CFLAGS:=$(CFLAGS) -Iinclude -ffreestanding -Wall -Wextra -Werror=return-type
|
|
||||||
CPPFLAGS:=$(CPPFLAGS)
|
|
||||||
LIBBANK_CFLAGS:=$(CFLAGS) -D__is_kernel -Iinclude -ffreestanding -Wall -Wextra
|
|
||||||
LIBBANK_CPPFLAGS:=$(CPPFLAGS) -fno-rtti -fno-exceptions
|
|
||||||
|
|
||||||
ARCHDIR=arch/$(HOSTARCH)
|
|
||||||
|
|
||||||
include $(ARCHDIR)/make.config
|
|
||||||
|
|
||||||
CFLAGS:=$(CFLAGS) $(ARCH_CFLAGS)
|
|
||||||
CPPFLAGS:=$(CPPFLAGS) $(ARCH_CPPFLAGS)
|
|
||||||
LIBBANK_CFLAGS:=$(LIBBANK_CFLAGS) $(KERNEL_ARCH_CFLAGS)
|
|
||||||
LIBBANK_CPPFLAGS:=$(LIBBANK_CPPFLAGS) $(KERNEL_ARCH_CPPFLAGS)
|
|
||||||
|
|
||||||
BUILDDIR=$(abspath build)
|
|
||||||
|
|
||||||
FREEOBJS= \
|
|
||||||
$(ARCH_FREEOBJS) \
|
|
||||||
BAN/Memory.o \
|
|
||||||
BAN/String.o \
|
|
||||||
BAN/StringView.o \
|
|
||||||
|
|
||||||
HOSTEDOBJS=\
|
|
||||||
$(ARCH_HOSTEDOBJS) \
|
|
||||||
|
|
||||||
OBJS=\
|
|
||||||
$(FREEOBJS) \
|
|
||||||
$(HOSTEDOBJS) \
|
|
||||||
|
|
||||||
LIBBANK_OBJS=$(FREEOBJS:.o=.bank.o)
|
|
||||||
|
|
||||||
BINARIES=libbank.a
|
|
||||||
|
|
||||||
.PHONY: all always clean install install-headers install-libs
|
|
||||||
.SUFFIXES: .o .bank.o .cpp .S
|
|
||||||
|
|
||||||
all: $(BINARIES)
|
|
||||||
|
|
||||||
libban.a: always $(OBJS)
|
|
||||||
cd $(BUILDDIR) && $(AR) rcs $@ $(OBJS)
|
|
||||||
|
|
||||||
libbank.a: always $(LIBBANK_OBJS)
|
|
||||||
cd $(BUILDDIR) && $(AR) rcs $@ $(LIBBANK_OBJS)
|
|
||||||
|
|
||||||
.cpp.o:
|
|
||||||
$(CXX) -MD -c $< -o $(BUILDDIR)/$@ $(CFLAGS) $(CPPFLAGS)
|
|
||||||
|
|
||||||
.S.o:
|
|
||||||
$(CXX) -MD -c $< -o $(BUILDDIR)/$@ $(CFLAGS) $(CPPFLAGS)
|
|
||||||
|
|
||||||
.cpp.bank.o:
|
|
||||||
$(CXX) -MD -c $< -o $(BUILDDIR)/$@ $(LIBBANK_CFLAGS) $(LIBBANK_CPPFLAGS)
|
|
||||||
|
|
||||||
.S.bank.o:
|
|
||||||
$(CXX) -MD -c $< -o $(BUILDDIR)/$@ $(LIBBANK_CFLAGS) $(LIBBANK_CPPFLAGS)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf $(BUILDDIR)
|
|
||||||
|
|
||||||
always:
|
|
||||||
mkdir -p $(BUILDDIR)/BAN
|
|
||||||
|
|
||||||
install: install-headers install-libs
|
|
||||||
|
|
||||||
install-headers:
|
|
||||||
mkdir -p $(DESTDIR)$(INCLUDEDIR)
|
|
||||||
cp -R --preserve=timestamps include/. $(DESTDIR)$(INCLUDEDIR)/.
|
|
||||||
|
|
||||||
install-libs: $(BINARIES)
|
|
||||||
mkdir -p $(DESTDIR)$(LIBDIR)
|
|
||||||
cp $(BUILDDIR)/$(BINARIES) $(DESTDIR)$(LIBDIR)
|
|
||||||
|
|
||||||
-include $(OBJS:.o=.d)
|
|
||||||
-include $(LIBBANK_OBJS:.o=.d)
|
|
|
@ -1,8 +0,0 @@
|
||||||
ARCH_CFLAGS=
|
|
||||||
ARCH_CPPFLAGS=
|
|
||||||
KERNEL_ARCH_CFLAGS=
|
|
||||||
KERNEL_ARCH_CPPFLAGS=
|
|
||||||
|
|
||||||
ARCH_FREEOBJS=\
|
|
||||||
|
|
||||||
ARCH_HOSTEDOBJS=\
|
|
|
@ -1,8 +0,0 @@
|
||||||
ARCH_CFLAGS=
|
|
||||||
ARCH_CPPFLAGS=
|
|
||||||
KERNEL_ARCH_CFLAGS=
|
|
||||||
KERNEL_ARCH_CPPFLAGS=
|
|
||||||
|
|
||||||
ARCH_FREEOBJS=\
|
|
||||||
|
|
||||||
ARCH_HOSTEDOBJS=\
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Iterators.h>
|
||||||
|
#include <BAN/Span.h>
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
@ -13,11 +14,18 @@ namespace BAN
|
||||||
public:
|
public:
|
||||||
using size_type = decltype(S);
|
using size_type = decltype(S);
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
|
using iterator = IteratorSimple<T, Array>;
|
||||||
|
using const_iterator = ConstIteratorSimple<T, Array>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Array();
|
Array() = default;
|
||||||
Array(const T&);
|
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()); }
|
||||||
|
|
||||||
const T& operator[](size_type) const;
|
const T& operator[](size_type) const;
|
||||||
T& operator[](size_type);
|
T& operator[](size_type);
|
||||||
|
|
||||||
|
@ -26,18 +34,17 @@ namespace BAN
|
||||||
const T& front() const;
|
const T& front() const;
|
||||||
T& front();
|
T& front();
|
||||||
|
|
||||||
|
Span<T> span() { return Span(m_data, size()); }
|
||||||
|
const Span<T> span() const { return Span(m_data, size()); }
|
||||||
|
|
||||||
constexpr size_type size() const;
|
constexpr size_type size() const;
|
||||||
|
|
||||||
private:
|
const T* data() const { return m_data; }
|
||||||
T m_data[S];
|
T* data() { return m_data; }
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T, size_t S>
|
private:
|
||||||
Array<T, S>::Array()
|
T m_data[S] {};
|
||||||
{
|
};
|
||||||
for (size_type i = 0; i < S; i++)
|
|
||||||
m_data[i] = T();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, size_t S>
|
template<typename T, size_t S>
|
||||||
Array<T, S>::Array(const T& value)
|
Array<T, S>::Array(const T& value)
|
||||||
|
@ -94,4 +101,4 @@ namespace BAN
|
||||||
return S;
|
return S;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#if defined(__is_kernel)
|
#define __ban_assert_stringify_helper(s) #s
|
||||||
#include <kernel/Panic.h>
|
#define __ban_assert_stringify(s) __ban_assert_stringify_helper(s)
|
||||||
#define ASSERT(cond) do { if (!(cond)) Kernel::panic("ASSERT("#cond") failed"); } while(false)
|
|
||||||
#define ASSERT_NOT_REACHED() Kernel::panic("ASSERT_NOT_REACHED() failed")
|
#define ASSERT(cond) \
|
||||||
#else
|
(__builtin_expect(!(cond), 0) \
|
||||||
#include <assert.h>
|
? __ban_assertion_failed(__FILE__ ":" __ban_assert_stringify(__LINE__), "ASSERT(" #cond ") failed") \
|
||||||
#define ASSERT(cond) assert((cond) && "ASSERT("#cond") failed")
|
: (void)0)
|
||||||
#define ASSERT_NOT_REACHED() assert(false && "ASSERT_NOT_REACHED() failed")
|
|
||||||
#endif
|
#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);
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
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, MemoryOrder MEM_ORDER = MemoryOrder::memory_order_seq_cst>
|
||||||
|
requires requires { __atomic_always_lock_free(sizeof(T), 0); }
|
||||||
|
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_n(&m_value, mem_order); }
|
||||||
|
inline void store(T val, MemoryOrder mem_order = MEM_ORDER) volatile { __atomic_store_n(&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_n(&m_value, &expected, desired, false, mem_order, mem_order); }
|
||||||
|
inline T exchange(T desired, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_exchange_n(&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_nfetch_and(&m_value, val, mem_order); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
T m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename To, typename From>
|
||||||
|
constexpr To bit_cast(const From& from)
|
||||||
|
{
|
||||||
|
return __builtin_bit_cast(To, from);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,159 @@
|
||||||
|
#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)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
ByteSpanGeneral(ByteSpanGeneral& other)
|
||||||
|
: m_data(other.data())
|
||||||
|
, m_size(other.size())
|
||||||
|
{ }
|
||||||
|
ByteSpanGeneral(ByteSpanGeneral&& other)
|
||||||
|
: m_data(other.data())
|
||||||
|
, m_size(other.size())
|
||||||
|
{
|
||||||
|
other.m_data = nullptr;
|
||||||
|
other.m_size = 0;
|
||||||
|
}
|
||||||
|
template<bool C2>
|
||||||
|
ByteSpanGeneral(const ByteSpanGeneral<C2>& other) requires(CONST)
|
||||||
|
: m_data(other.data())
|
||||||
|
, m_size(other.size())
|
||||||
|
{ }
|
||||||
|
template<bool C2>
|
||||||
|
ByteSpanGeneral(ByteSpanGeneral<C2>&& other) requires(CONST)
|
||||||
|
: m_data(other.data())
|
||||||
|
, m_size(other.size())
|
||||||
|
{
|
||||||
|
other.m_data = nullptr;
|
||||||
|
other.m_size = 0;
|
||||||
|
}
|
||||||
|
ByteSpanGeneral(Span<uint8_t> other)
|
||||||
|
: m_data(other.data())
|
||||||
|
, m_size(other.size())
|
||||||
|
{ }
|
||||||
|
ByteSpanGeneral(const Span<const uint8_t>& other) requires(CONST)
|
||||||
|
: m_data(other.data())
|
||||||
|
, m_size(other.size())
|
||||||
|
{ }
|
||||||
|
|
||||||
|
ByteSpanGeneral& operator=(ByteSpanGeneral other)
|
||||||
|
{
|
||||||
|
m_data = other.data();
|
||||||
|
m_size = other.size();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
template<bool C2>
|
||||||
|
ByteSpanGeneral& operator=(const ByteSpanGeneral<C2>& other) requires(CONST)
|
||||||
|
{
|
||||||
|
m_data = other.data();
|
||||||
|
m_size = other.size();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ByteSpanGeneral& operator=(Span<uint8_t> other)
|
||||||
|
{
|
||||||
|
m_data = other.data();
|
||||||
|
m_size = other.size();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ByteSpanGeneral& operator=(const Span<const uint8_t>& other) requires(CONST)
|
||||||
|
{
|
||||||
|
m_data = other.data();
|
||||||
|
m_size = other.size();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
requires(CONST || !is_const_v<S>)
|
||||||
|
static ByteSpanGeneral from(S& value)
|
||||||
|
{
|
||||||
|
return ByteSpanGeneral(reinterpret_cast<value_type*>(&value), sizeof(S));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
requires(!CONST && !is_const_v<S>)
|
||||||
|
S& as()
|
||||||
|
{
|
||||||
|
ASSERT(m_data);
|
||||||
|
ASSERT(m_size >= sizeof(S));
|
||||||
|
return *reinterpret_cast<S*>(m_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
requires(is_const_v<S>)
|
||||||
|
S& as() const
|
||||||
|
{
|
||||||
|
ASSERT(m_data);
|
||||||
|
ASSERT(m_size >= sizeof(S));
|
||||||
|
return *reinterpret_cast<S*>(m_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
requires(!CONST && !is_const_v<S>)
|
||||||
|
Span<S> as_span()
|
||||||
|
{
|
||||||
|
ASSERT(m_data);
|
||||||
|
return Span<S>(reinterpret_cast<S*>(m_data), m_size / sizeof(S));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
const Span<S> as_span() const
|
||||||
|
{
|
||||||
|
ASSERT(m_data);
|
||||||
|
return Span<S>(reinterpret_cast<S*>(m_data), m_size / sizeof(S));
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteSpanGeneral slice(size_type offset, size_type length = size_type(-1))
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
ASSERT(offset < m_size);
|
||||||
|
return m_data[offset];
|
||||||
|
}
|
||||||
|
const value_type& operator[](size_type offset) const
|
||||||
|
{
|
||||||
|
ASSERT(offset < m_size);
|
||||||
|
return m_data[offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type* data() { return m_data; }
|
||||||
|
const value_type* data() const { return m_data; }
|
||||||
|
|
||||||
|
size_type size() const { return m_size; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
value_type* m_data { nullptr };
|
||||||
|
size_type m_size { 0 };
|
||||||
|
|
||||||
|
friend class ByteSpanGeneral<!CONST>;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ByteSpan = ByteSpanGeneral<false>;
|
||||||
|
using ConstByteSpan = ByteSpanGeneral<true>;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Assert.h>
|
||||||
|
#include <BAN/Move.h>
|
||||||
|
#include <BAN/PlacementNew.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
class CircularQueue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using size_type = size_t;
|
||||||
|
using value_type = T;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CircularQueue() = default;
|
||||||
|
~CircularQueue();
|
||||||
|
|
||||||
|
void push(const T&);
|
||||||
|
void push(T&&);
|
||||||
|
template<typename... Args>
|
||||||
|
void emplace(Args&&... args);
|
||||||
|
|
||||||
|
void pop();
|
||||||
|
|
||||||
|
const T& front() const;
|
||||||
|
T& front();
|
||||||
|
|
||||||
|
const T& back() const;
|
||||||
|
T& back();
|
||||||
|
|
||||||
|
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:
|
||||||
|
T* element_at(size_type);
|
||||||
|
const T* element_at(size_type) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
alignas(T) uint8_t m_storage[sizeof(T) * capacity()];
|
||||||
|
size_type m_first { 0 };
|
||||||
|
size_type m_size { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
CircularQueue<T, S>::~CircularQueue()
|
||||||
|
{
|
||||||
|
for (size_type i = 0; i < m_size; i++)
|
||||||
|
element_at((m_first + i) % capacity())->~T();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
void CircularQueue<T, S>::push(const T& value)
|
||||||
|
{
|
||||||
|
emplace(BAN::move(T(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
void CircularQueue<T, S>::push(T&& value)
|
||||||
|
{
|
||||||
|
emplace(BAN::move(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
template<typename... Args>
|
||||||
|
void CircularQueue<T, S>::emplace(Args&&... args)
|
||||||
|
{
|
||||||
|
ASSERT(!full());
|
||||||
|
new (element_at(((m_first + m_size) % capacity()))) T(BAN::forward<Args>(args)...);
|
||||||
|
m_size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
void CircularQueue<T, S>::pop()
|
||||||
|
{
|
||||||
|
ASSERT(!empty());
|
||||||
|
element_at(m_first)->~T();
|
||||||
|
m_first = (m_first + 1) % capacity();
|
||||||
|
m_size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
const T& CircularQueue<T, S>::front() const
|
||||||
|
{
|
||||||
|
ASSERT(!empty());
|
||||||
|
return *element_at(m_first);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
T& CircularQueue<T, S>::front()
|
||||||
|
{
|
||||||
|
ASSERT(!empty());
|
||||||
|
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>::element_at(size_type index) const
|
||||||
|
{
|
||||||
|
ASSERT(index < capacity());
|
||||||
|
return (const T*)(m_storage + index * sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t S>
|
||||||
|
T* CircularQueue<T, S>::element_at(size_type index)
|
||||||
|
{
|
||||||
|
ASSERT(index < capacity());
|
||||||
|
return (T*)(m_storage + index * sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if __is_kernel
|
||||||
|
|
||||||
|
#include <kernel/Debug.h>
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <BAN/Formatter.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define __debug_putchar [](int c) { putc(c, stddbg); }
|
||||||
|
|
||||||
|
#define dprintln(...) \
|
||||||
|
do { \
|
||||||
|
BAN::Formatter::print(__debug_putchar, __VA_ARGS__); \
|
||||||
|
BAN::Formatter::print(__debug_putchar,"\r\n"); \
|
||||||
|
fflush(stddbg); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
#define dwarnln(...) \
|
||||||
|
do { \
|
||||||
|
BAN::Formatter::print(__debug_putchar, "\e[33m"); \
|
||||||
|
BAN::Formatter::print(__debug_putchar, __VA_ARGS__); \
|
||||||
|
BAN::Formatter::print(__debug_putchar, "\e[m\r\n"); \
|
||||||
|
fflush(stddbg); \
|
||||||
|
} while(false)
|
||||||
|
|
||||||
|
#define derrorln(...) \
|
||||||
|
do { \
|
||||||
|
BAN::Formatter::print(__debug_putchar, "\e[31m"); \
|
||||||
|
BAN::Formatter::print(__debug_putchar, __VA_ARGS__); \
|
||||||
|
BAN::Formatter::print(__debug_putchar, "\e[m\r\n"); \
|
||||||
|
fflush(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
|
|
@ -0,0 +1,117 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Traits.h>
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<integral T>
|
||||||
|
constexpr T swap_endianness(T value)
|
||||||
|
{
|
||||||
|
if constexpr(sizeof(T) == 1)
|
||||||
|
return value;
|
||||||
|
if constexpr(sizeof(T) == 2)
|
||||||
|
return (((value >> 8) & 0xFF) << 0)
|
||||||
|
| (((value >> 0) & 0xFF) << 8);
|
||||||
|
if constexpr(sizeof(T) == 4)
|
||||||
|
return (((value >> 24) & 0xFF) << 0)
|
||||||
|
| (((value >> 16) & 0xFF) << 8)
|
||||||
|
| (((value >> 8) & 0xFF) << 16)
|
||||||
|
| (((value >> 0) & 0xFF) << 24);
|
||||||
|
if constexpr(sizeof(T) == 8)
|
||||||
|
return (((value >> 56) & 0xFF) << 0)
|
||||||
|
| (((value >> 48) & 0xFF) << 8)
|
||||||
|
| (((value >> 40) & 0xFF) << 16)
|
||||||
|
| (((value >> 32) & 0xFF) << 24)
|
||||||
|
| (((value >> 24) & 0xFF) << 32)
|
||||||
|
| (((value >> 16) & 0xFF) << 40)
|
||||||
|
| (((value >> 8) & 0xFF) << 48)
|
||||||
|
| (((value >> 0) & 0xFF) << 56);
|
||||||
|
T result { 0 };
|
||||||
|
for (size_t i = 0; i < sizeof(T); i++)
|
||||||
|
result |= ((value >> (i * 8)) & 0xFF) << ((sizeof(T) - i - 1) * 8);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<integral T>
|
||||||
|
constexpr T host_to_little_endian(T value)
|
||||||
|
{
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
return value;
|
||||||
|
#else
|
||||||
|
return swap_endianness(value);
|
||||||
|
#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)
|
||||||
|
{
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
return value;
|
||||||
|
#else
|
||||||
|
return swap_endianness(value);
|
||||||
|
#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(T value)
|
||||||
|
{
|
||||||
|
raw = host_to_little_endian(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr operator T() const
|
||||||
|
{
|
||||||
|
return host_to_little_endian(raw);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
T raw;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<integral T>
|
||||||
|
struct BigEndian
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,57 +6,72 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#if defined(__is_kernel)
|
#ifdef __is_kernel
|
||||||
#include <kernel/Panic.h>
|
#include <kernel/Panic.h>
|
||||||
#define MUST(expr) ({ auto e = expr; if (e.is_error()) Kernel::panic("{}", e.error()); e.release_value(); })
|
#include <kernel/Errors.h>
|
||||||
|
#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
|
#else
|
||||||
#define MUST(expr) ({ auto e = expr; assert(!e.is_error()); 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
|
#endif
|
||||||
|
|
||||||
#define TRY(expr) ({ auto e = expr; 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
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
class Error
|
class Error
|
||||||
{
|
{
|
||||||
|
#ifdef __is_kernel
|
||||||
|
private:
|
||||||
|
static constexpr uint64_t kernel_error_mask = uint64_t(1) << 63;
|
||||||
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static Error from_c_string(const char* message)
|
#ifdef __is_kernel
|
||||||
|
static Error from_error_code(Kernel::ErrorCode error)
|
||||||
{
|
{
|
||||||
Error result;
|
return Error((uint64_t)error | kernel_error_mask);
|
||||||
strncpy(result.m_message, message, sizeof(Error::m_message));
|
|
||||||
result.m_message[sizeof(Error::m_message) - 1] = '\0';
|
|
||||||
result.m_error_code = 0xFF;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
template<typename... Args>
|
|
||||||
static Error from_format(const char* format, Args&&... args)
|
|
||||||
{
|
|
||||||
char buffer[sizeof(Error::m_message)] {};
|
|
||||||
size_t index = 0;
|
|
||||||
auto putc = [&](char ch)
|
|
||||||
{
|
|
||||||
if (index < sizeof(buffer) - 1)
|
|
||||||
buffer[index++] = ch;
|
|
||||||
};
|
|
||||||
Formatter::print(putc, format, forward<Args>(args)...);
|
|
||||||
return from_c_string(buffer);
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
static Error from_errno(int error)
|
static Error from_errno(int error)
|
||||||
{
|
{
|
||||||
Error result;
|
return Error(error);
|
||||||
strncpy(result.m_message, strerror(error), sizeof(Error::m_message));
|
|
||||||
result.m_message[sizeof(Error::m_message) - 1] = '\0';
|
|
||||||
result.m_error_code = error;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t get_error_code() const { return m_error_code; }
|
#ifdef __is_kernel
|
||||||
const char* get_message() const { return m_message; }
|
Kernel::ErrorCode kernel_error() const
|
||||||
|
{
|
||||||
|
return (Kernel::ErrorCode)(m_error_code & ~kernel_error_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_kernel_error() const
|
||||||
|
{
|
||||||
|
return m_error_code & kernel_error_mask;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint64_t get_error_code() const { return m_error_code; }
|
||||||
|
const char* get_message() const
|
||||||
|
{
|
||||||
|
#ifdef __is_kernel
|
||||||
|
if (m_error_code & kernel_error_mask)
|
||||||
|
return Kernel::error_string(kernel_error());
|
||||||
|
#endif
|
||||||
|
if (auto* desc = strerrordesc_np(m_error_code))
|
||||||
|
return desc;
|
||||||
|
return "Unknown error";
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
char m_message[128];
|
Error(uint64_t error)
|
||||||
uint8_t m_error_code;
|
: m_error_code(error)
|
||||||
|
{}
|
||||||
|
|
||||||
|
uint64_t m_error_code;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -76,19 +91,46 @@ namespace BAN
|
||||||
: m_data(move(error))
|
: m_data(move(error))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool is_error() const { return m_data.template is<Error>(); }
|
bool is_error() const { return m_data.template has<Error>(); }
|
||||||
const Error& error() const { return m_data.template get<Error>(); }
|
const Error& error() const { return m_data.template get<Error>(); }
|
||||||
Error& error() { return m_data.template get<Error>(); }
|
Error& error() { return m_data.template get<Error>(); }
|
||||||
const T& value() const { return m_data.template get<T>(); }
|
const T& value() const { return m_data.template get<T>(); }
|
||||||
T& value() { return m_data.template get<T>(); }
|
T& value() { return m_data.template get<T>(); }
|
||||||
|
|
||||||
Error release_error() { return move(error()); m_data.clear(); }
|
Error release_error() { return move(error()); m_data.clear(); }
|
||||||
T release_value() { return move(value()); m_data.clear(); }
|
T release_value() { return move(value()); m_data.clear(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Variant<Error, T> m_data;
|
Variant<Error, T> m_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<lvalue_reference T>
|
||||||
|
class [[nodiscard]] ErrorOr<T>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ErrorOr(T value)
|
||||||
|
{
|
||||||
|
m_data.template set<T>(value);
|
||||||
|
}
|
||||||
|
ErrorOr(Error&& error)
|
||||||
|
: m_data(move(error))
|
||||||
|
{ }
|
||||||
|
ErrorOr(const Error& error)
|
||||||
|
: m_data(error)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
bool is_error() const { return m_data.template has<Error>(); }
|
||||||
|
Error& error() { return m_data.template get<Error>(); }
|
||||||
|
const Error& error() const { return m_data.template get<Error>(); }
|
||||||
|
T value() { return m_data.template get<T>(); }
|
||||||
|
|
||||||
|
Error release_error() { return move(error()); m_data.clear(); }
|
||||||
|
T release_value() { return value(); m_data.clear(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Variant<Error, T> m_data;
|
||||||
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
class [[nodiscard]] ErrorOr<void>
|
class [[nodiscard]] ErrorOr<void>
|
||||||
{
|
{
|
||||||
|
@ -102,12 +144,12 @@ namespace BAN
|
||||||
const Error& error() const { return m_data; }
|
const Error& error() const { return m_data; }
|
||||||
void value() { }
|
void value() { }
|
||||||
|
|
||||||
Error release_error() { return move(m_data); m_data = Error(); }
|
Error release_error() { return move(m_data); }
|
||||||
void release_value() { m_data = Error(); }
|
void release_value() { }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Error m_data;
|
Error m_data { Error::from_errno(0) };
|
||||||
bool m_has_error = false;
|
bool m_has_error { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -115,11 +157,8 @@ namespace BAN
|
||||||
namespace BAN::Formatter
|
namespace BAN::Formatter
|
||||||
{
|
{
|
||||||
template<typename F>
|
template<typename F>
|
||||||
void print_argument(F putc, const Error& error, const ValueFormat&)
|
void print_argument(F putc, const Error& error, const ValueFormat& format)
|
||||||
{
|
{
|
||||||
if (error.get_error_code() == 0xFF)
|
print_argument(putc, error.get_message(), format);
|
||||||
print(putc, error.get_message());
|
|
||||||
else
|
|
||||||
print(putc, "{} ({})", error.get_message(), error.get_error_code());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,9 +28,8 @@ namespace BAN::Formatter
|
||||||
static 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
IMPLEMENTATION
|
IMPLEMENTATION
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
@ -42,7 +41,7 @@ namespace BAN::Formatter
|
||||||
int fill = 0;
|
int fill = 0;
|
||||||
bool upper = false;
|
bool upper = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename F>
|
template<typename F>
|
||||||
void print(F putc, const char* format)
|
void print(F putc, const char* format)
|
||||||
{
|
{
|
||||||
|
@ -193,7 +192,7 @@ namespace BAN::Formatter
|
||||||
|
|
||||||
if (sign)
|
if (sign)
|
||||||
*(--ptr) = '-';
|
*(--ptr) = '-';
|
||||||
|
|
||||||
print(putc, ptr);
|
print(putc, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,10 +205,10 @@ namespace BAN::Formatter
|
||||||
frac_part = -frac_part;
|
frac_part = -frac_part;
|
||||||
|
|
||||||
print_integer(putc, int_part, format);
|
print_integer(putc, int_part, format);
|
||||||
|
|
||||||
if (format.percision > 0)
|
if (format.percision > 0)
|
||||||
putc('.');
|
putc('.');
|
||||||
|
|
||||||
for (int i = 0; i < format.percision; i++)
|
for (int i = 0; i < format.percision; i++)
|
||||||
{
|
{
|
||||||
frac_part *= format.base;
|
frac_part *= format.base;
|
||||||
|
@ -232,7 +231,7 @@ namespace BAN::Formatter
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
TEMPLATE SPECIALIZATIONS
|
TEMPLATE SPECIALIZATIONS
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
@ -243,10 +242,7 @@ namespace BAN::Formatter
|
||||||
|
|
||||||
template<typename F> void print_argument(F putc, char value, const ValueFormat&) { 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, 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, const char* value, const ValueFormat&) { print(putc, value); }
|
||||||
|
template<typename F> void print_argument(F putc, char* value, const ValueFormat&) { print(putc, value); }
|
||||||
//template<typename F> void print_argument(F putc, signed char value, const ValueFormat& format) { detail::print_integer(putc, value, format); }
|
|
||||||
//template<typename F> void print_argument(F putc, unsigned char value, const ValueFormat& format) { detail::print_integer(putc, value, format); }
|
|
||||||
//template<typename F, typename T> void print_argument(F putc, T* value, const ValueFormat& format) { detail::print_pointer(putc, (void*)value, format); }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Traits.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
template<typename, size_t> class Array;
|
template<typename, size_t> class Array;
|
||||||
template<typename> class ErrorOr;
|
template<typename> class ErrorOr;
|
||||||
template<typename> class Function;
|
template<typename> class Function;
|
||||||
|
@ -14,5 +15,6 @@ namespace BAN
|
||||||
class StringView;
|
class StringView;
|
||||||
template<typename> class Vector;
|
template<typename> class Vector;
|
||||||
template<typename> class LinkedList;
|
template<typename> class LinkedList;
|
||||||
|
template<typename... Ts> requires (!is_const_v<Ts> && ...) class Variant;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
#include <BAN/Move.h>
|
#include <BAN/Move.h>
|
||||||
#include <BAN/Memory.h>
|
#include <BAN/PlacementNew.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
@ -32,7 +32,7 @@ namespace BAN
|
||||||
new (m_storage) CallableMemberConst<Own>(function, owner);
|
new (m_storage) CallableMemberConst<Own>(function, owner);
|
||||||
}
|
}
|
||||||
template<typename Lambda>
|
template<typename Lambda>
|
||||||
Function(Lambda lambda)
|
Function(Lambda lambda) requires requires(Lambda lamda, Args&&... args) { { lambda(forward<Args>(args)...) } -> BAN::same_as<Ret>; }
|
||||||
{
|
{
|
||||||
static_assert(sizeof(CallableLambda<Lambda>) <= m_size);
|
static_assert(sizeof(CallableLambda<Lambda>) <= m_size);
|
||||||
new (m_storage) CallableLambda<Lambda>(lambda);
|
new (m_storage) CallableLambda<Lambda>(lambda);
|
||||||
|
@ -43,10 +43,10 @@ namespace BAN
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ret operator()(Args... args)
|
Ret operator()(Args... args) const
|
||||||
{
|
{
|
||||||
ASSERT(*this);
|
ASSERT(*this);
|
||||||
return reinterpret_cast<CallableBase*>(m_storage)->call(forward<Args>(args)...);
|
return reinterpret_cast<const CallableBase*>(m_storage)->call(forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
operator bool() const
|
operator bool() const
|
||||||
|
@ -56,7 +56,7 @@ namespace BAN
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear()
|
void clear()
|
||||||
{
|
{
|
||||||
if (*this)
|
if (*this)
|
||||||
|
@ -70,7 +70,7 @@ namespace BAN
|
||||||
struct CallableBase
|
struct CallableBase
|
||||||
{
|
{
|
||||||
virtual ~CallableBase() {}
|
virtual ~CallableBase() {}
|
||||||
virtual Ret call(Args...) = 0;
|
virtual Ret call(Args...) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CallablePointer : public CallableBase
|
struct CallablePointer : public CallableBase
|
||||||
|
@ -79,7 +79,7 @@ namespace BAN
|
||||||
: m_function(function)
|
: m_function(function)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
virtual Ret call(Args... args) override
|
virtual Ret call(Args... args) const override
|
||||||
{
|
{
|
||||||
return m_function(forward<Args>(args)...);
|
return m_function(forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ namespace BAN
|
||||||
, m_function(function)
|
, m_function(function)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
virtual Ret call(Args... args) override
|
virtual Ret call(Args... args) const override
|
||||||
{
|
{
|
||||||
return (m_owner->*m_function)(forward<Args>(args)...);
|
return (m_owner->*m_function)(forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,7 @@ namespace BAN
|
||||||
, m_function(function)
|
, m_function(function)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
virtual Ret call(Args... args) override
|
virtual Ret call(Args... args) const override
|
||||||
{
|
{
|
||||||
return (m_owner->*m_function)(forward<Args>(args)...);
|
return (m_owner->*m_function)(forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
@ -131,7 +131,7 @@ namespace BAN
|
||||||
: m_lambda(lambda)
|
: m_lambda(lambda)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
virtual Ret call(Args... args) override
|
virtual Ret call(Args... args) const override
|
||||||
{
|
{
|
||||||
return m_lambda(forward<Args>(args)...);
|
return m_lambda(forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
@ -141,8 +141,8 @@ namespace BAN
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr size_t m_size = sizeof(void*) * 4;
|
static constexpr size_t m_size = sizeof(void*) * 8;
|
||||||
alignas(CallableBase) uint8_t m_storage[m_size] { 0 };
|
alignas(CallableBase) uint8_t m_storage[m_size] { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Optional.h>
|
||||||
|
#include <BAN/StringView.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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static_assert(sizeof(GUID) == 16);
|
||||||
|
|
||||||
|
}
|
|
@ -47,4 +47,4 @@ namespace BAN
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,25 @@ namespace BAN
|
||||||
template<typename Key, typename T, typename HASH = BAN::hash<Key>>
|
template<typename Key, typename T, typename HASH = BAN::hash<Key>>
|
||||||
class HashMap
|
class HashMap
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
struct Entry
|
||||||
|
{
|
||||||
|
template<typename... Args>
|
||||||
|
Entry(const Key& key, Args&&... args)
|
||||||
|
: key(key)
|
||||||
|
, value(forward<Args>(args)...)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Key key;
|
||||||
|
T value;
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using size_type = size_t;
|
using size_type = size_t;
|
||||||
using key_type = Key;
|
using key_type = Key;
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
|
using iterator = IteratorDouble<Entry, Vector, LinkedList, HashMap>;
|
||||||
|
using const_iterator = ConstIteratorDouble<Entry, Vector, LinkedList, HashMap>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
HashMap() = default;
|
HashMap() = default;
|
||||||
|
@ -29,40 +44,39 @@ namespace BAN
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
ErrorOr<void> emplace(const Key&, 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()); }
|
||||||
|
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()); }
|
||||||
|
|
||||||
ErrorOr<void> reserve(size_type);
|
ErrorOr<void> reserve(size_type);
|
||||||
|
|
||||||
void remove(const Key&);
|
void remove(const Key&);
|
||||||
|
void remove(iterator it);
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
T& operator[](const Key&);
|
T& operator[](const Key&);
|
||||||
const T& operator[](const Key&) const;
|
const T& operator[](const Key&) const;
|
||||||
|
|
||||||
|
iterator find(const Key& key);
|
||||||
|
const_iterator find(const Key& key) const;
|
||||||
bool contains(const Key&) const;
|
bool contains(const Key&) const;
|
||||||
|
|
||||||
bool empty() const;
|
bool empty() const;
|
||||||
size_type size() const;
|
size_type size() const;
|
||||||
|
|
||||||
private:
|
|
||||||
struct Entry
|
|
||||||
{
|
|
||||||
template<typename... Args>
|
|
||||||
Entry(const Key& key, Args&&... args)
|
|
||||||
: key(key)
|
|
||||||
, value(forward<Args>(args)...)
|
|
||||||
{}
|
|
||||||
|
|
||||||
Key key;
|
|
||||||
T value;
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ErrorOr<void> rebucket(size_type);
|
ErrorOr<void> rebucket(size_type);
|
||||||
LinkedList<Entry>& get_bucket(const Key&);
|
LinkedList<Entry>& get_bucket(const Key&);
|
||||||
const LinkedList<Entry>& get_bucket(const Key&) const;
|
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:
|
private:
|
||||||
Vector<LinkedList<Entry>> m_buckets;
|
Vector<LinkedList<Entry>> m_buckets;
|
||||||
size_type m_size = 0;
|
size_type m_size = 0;
|
||||||
|
|
||||||
|
friend iterator;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH>
|
||||||
|
@ -121,9 +135,7 @@ namespace BAN
|
||||||
ASSERT(!contains(key));
|
ASSERT(!contains(key));
|
||||||
TRY(rebucket(m_size + 1));
|
TRY(rebucket(m_size + 1));
|
||||||
auto& bucket = get_bucket(key);
|
auto& bucket = get_bucket(key);
|
||||||
auto result = bucket.emplace_back(key, forward<Args>(args)...);
|
TRY(bucket.emplace_back(key, forward<Args>(args)...));
|
||||||
if (result.is_error())
|
|
||||||
return Error::from_errno(ENOMEM);
|
|
||||||
m_size++;
|
m_size++;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -138,17 +150,16 @@ namespace BAN
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH>
|
||||||
void HashMap<Key, T, HASH>::remove(const Key& key)
|
void HashMap<Key, T, HASH>::remove(const Key& key)
|
||||||
{
|
{
|
||||||
if (empty()) return;
|
auto it = find(key);
|
||||||
auto& bucket = get_bucket(key);
|
if (it != end())
|
||||||
for (auto it = bucket.begin(); it != bucket.end(); it++)
|
remove(it);
|
||||||
{
|
}
|
||||||
if (it->key == key)
|
|
||||||
{
|
template<typename Key, typename T, typename HASH>
|
||||||
bucket.remove(it);
|
void HashMap<Key, T, HASH>::remove(iterator it)
|
||||||
m_size--;
|
{
|
||||||
return;
|
it.outer_current()->remove(it.inner_current());
|
||||||
}
|
m_size--;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH>
|
||||||
|
@ -180,15 +191,34 @@ namespace BAN
|
||||||
ASSERT(false);
|
ASSERT(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH>
|
||||||
bool HashMap<Key, T, HASH>::contains(const Key& key) const
|
bool HashMap<Key, T, HASH>::contains(const Key& key) const
|
||||||
{
|
{
|
||||||
if (empty()) return false;
|
return find(key) != end();
|
||||||
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>
|
template<typename Key, typename T, typename HASH>
|
||||||
|
@ -211,18 +241,14 @@ namespace BAN
|
||||||
|
|
||||||
size_type new_bucket_count = BAN::Math::max<size_type>(bucket_count, m_buckets.size() * 2);
|
size_type new_bucket_count = BAN::Math::max<size_type>(bucket_count, m_buckets.size() * 2);
|
||||||
Vector<LinkedList<Entry>> new_buckets;
|
Vector<LinkedList<Entry>> new_buckets;
|
||||||
if (new_buckets.resize(new_bucket_count).is_error())
|
TRY(new_buckets.resize(new_bucket_count));
|
||||||
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& bucket : m_buckets)
|
||||||
{
|
{
|
||||||
for (Entry& entry : bucket)
|
for (auto it = bucket.begin(); it != bucket.end();)
|
||||||
{
|
{
|
||||||
size_type bucket_index = HASH()(entry.key) % new_buckets.size();
|
size_type new_bucket_index = HASH()(it->key) % new_buckets.size();
|
||||||
if (new_buckets[bucket_index].push_back(entry).is_error())
|
it = bucket.move_element_to_other_linked_list(new_buckets[new_bucket_index], new_buckets[new_bucket_index].end(), it);
|
||||||
return Error::from_errno(ENOMEM);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,17 +259,29 @@ namespace BAN
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH>
|
||||||
LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key)
|
LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key)
|
||||||
{
|
{
|
||||||
ASSERT(!m_buckets.empty());
|
return *get_bucket_iterator(key);
|
||||||
auto index = HASH()(key) % m_buckets.size();
|
|
||||||
return m_buckets[index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
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
|
const LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key) const
|
||||||
{
|
{
|
||||||
ASSERT(!m_buckets.empty());
|
return *get_bucket_iterator(key);
|
||||||
auto index = HASH()(key) % m_buckets.size();
|
|
||||||
return m_buckets[index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
#include <BAN/Hash.h>
|
#include <BAN/Hash.h>
|
||||||
|
#include <BAN/Iterators.h>
|
||||||
|
#include <BAN/LinkedList.h>
|
||||||
#include <BAN/Math.h>
|
#include <BAN/Math.h>
|
||||||
#include <BAN/Move.h>
|
#include <BAN/Move.h>
|
||||||
#include <BAN/Vector.h>
|
#include <BAN/Vector.h>
|
||||||
|
@ -9,24 +11,22 @@
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
|
||||||
class HashSetIterator;
|
|
||||||
|
|
||||||
template<typename T, typename HASH = hash<T>>
|
template<typename T, typename HASH = hash<T>>
|
||||||
class HashSet
|
class HashSet
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
using size_type = hash_t;
|
using size_type = size_t;
|
||||||
using const_iterator = HashSetIterator<T, HASH>;
|
using iterator = IteratorDouble<T, Vector, LinkedList, HashSet>;
|
||||||
|
using const_iterator = ConstIteratorDouble<T, Vector, LinkedList, HashSet>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
HashSet() = default;
|
HashSet() = default;
|
||||||
HashSet(const HashSet<T, HASH>&);
|
HashSet(const HashSet&);
|
||||||
HashSet(HashSet<T, HASH>&&);
|
HashSet(HashSet&&);
|
||||||
|
|
||||||
HashSet<T, HASH>& operator=(const HashSet<T, HASH>&);
|
HashSet& operator=(const HashSet&);
|
||||||
HashSet<T, HASH>& operator=(HashSet<T, HASH>&&);
|
HashSet& operator=(HashSet&&);
|
||||||
|
|
||||||
ErrorOr<void> insert(const T&);
|
ErrorOr<void> insert(const T&);
|
||||||
ErrorOr<void> insert(T&&);
|
ErrorOr<void> insert(T&&);
|
||||||
|
@ -35,8 +35,10 @@ namespace BAN
|
||||||
|
|
||||||
ErrorOr<void> reserve(size_type);
|
ErrorOr<void> reserve(size_type);
|
||||||
|
|
||||||
const_iterator begin() const { return const_iterator(this, m_buckets.begin()); }
|
iterator begin() { return iterator(m_buckets.end(), m_buckets.begin()); }
|
||||||
const_iterator end() const { return const_iterator(this, m_buckets.end()); }
|
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()); }
|
||||||
|
|
||||||
bool contains(const T&) const;
|
bool contains(const T&) const;
|
||||||
|
|
||||||
|
@ -45,57 +47,23 @@ namespace BAN
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ErrorOr<void> rebucket(size_type);
|
ErrorOr<void> rebucket(size_type);
|
||||||
Vector<T>& get_bucket(const T&);
|
LinkedList<T>& get_bucket(const T&);
|
||||||
const Vector<T>& get_bucket(const T&) const;
|
const LinkedList<T>& get_bucket(const T&) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Vector<Vector<T>> m_buckets;
|
Vector<LinkedList<T>> m_buckets;
|
||||||
size_type m_size = 0;
|
size_type m_size = 0;
|
||||||
|
|
||||||
friend class HashSetIterator<T, HASH>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH>
|
||||||
class HashSetIterator
|
HashSet<T, HASH>::HashSet(const HashSet& other)
|
||||||
{
|
|
||||||
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_buckets(other.m_buckets)
|
||||||
, m_size(other.m_size)
|
, m_size(other.m_size)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH>
|
||||||
HashSet<T, HASH>::HashSet(HashSet<T, HASH>&& other)
|
HashSet<T, HASH>::HashSet(HashSet&& other)
|
||||||
: m_buckets(move(other.m_buckets))
|
: m_buckets(move(other.m_buckets))
|
||||||
, m_size(other.m_size)
|
, m_size(other.m_size)
|
||||||
{
|
{
|
||||||
|
@ -103,7 +71,7 @@ namespace BAN
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH>
|
||||||
HashSet<T, HASH>& HashSet<T, HASH>::operator=(const HashSet<T, HASH>& other)
|
HashSet<T, HASH>& HashSet<T, HASH>::operator=(const HashSet& other)
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
m_buckets = other.m_buckets;
|
m_buckets = other.m_buckets;
|
||||||
|
@ -112,7 +80,7 @@ namespace BAN
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH>
|
||||||
HashSet<T, HASH>& HashSet<T, HASH>::operator=(HashSet<T, HASH>&& other)
|
HashSet<T, HASH>& HashSet<T, HASH>::operator=(HashSet&& other)
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
m_buckets = move(other.m_buckets);
|
m_buckets = move(other.m_buckets);
|
||||||
|
@ -143,15 +111,15 @@ namespace BAN
|
||||||
void HashSet<T, HASH>::remove(const T& key)
|
void HashSet<T, HASH>::remove(const T& key)
|
||||||
{
|
{
|
||||||
if (empty()) return;
|
if (empty()) return;
|
||||||
Vector<T>& bucket = get_bucket(key);
|
auto& bucket = get_bucket(key);
|
||||||
for (size_type i = 0; i < bucket.size(); i++)
|
for (auto it = bucket.begin(); it != bucket.end(); it++)
|
||||||
{
|
{
|
||||||
if (bucket[i] == key)
|
if (*it == key)
|
||||||
{
|
{
|
||||||
bucket.remove(i);
|
bucket.remove(it);
|
||||||
m_size--;
|
m_size--;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,20 +162,17 @@ namespace BAN
|
||||||
if (m_buckets.size() >= bucket_count)
|
if (m_buckets.size() >= bucket_count)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
size_type new_bucket_count = BAN::Math::max<size_type>(bucket_count, m_buckets.size() * 2);
|
size_type new_bucket_count = Math::max<size_type>(bucket_count, m_buckets.size() * 2);
|
||||||
Vector<Vector<T>> new_buckets;
|
Vector<LinkedList<T>> new_buckets;
|
||||||
if (new_buckets.resize(new_bucket_count).is_error())
|
if (new_buckets.resize(new_bucket_count).is_error())
|
||||||
return Error::from_errno(ENOMEM);
|
return Error::from_errno(ENOMEM);
|
||||||
|
|
||||||
// NOTE: We have to copy the old keys to the new keys and not move
|
for (auto& bucket : m_buckets)
|
||||||
// since we might run out of memory half way through.
|
|
||||||
for (Vector<T>& bucket : m_buckets)
|
|
||||||
{
|
{
|
||||||
for (T& key : bucket)
|
for (auto it = bucket.begin(); it != bucket.end();)
|
||||||
{
|
{
|
||||||
size_type bucket_index = HASH()(key) % new_buckets.size();
|
size_type new_bucket_index = HASH()(*it) % new_buckets.size();
|
||||||
if (new_buckets[bucket_index].push_back(key).is_error())
|
it = bucket.move_element_to_other_linked_list(new_buckets[new_bucket_index], new_buckets[new_bucket_index].end(), it);
|
||||||
return Error::from_errno(ENOMEM);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,7 +181,7 @@ namespace BAN
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH>
|
||||||
Vector<T>& HashSet<T, HASH>::get_bucket(const T& key)
|
LinkedList<T>& HashSet<T, HASH>::get_bucket(const T& key)
|
||||||
{
|
{
|
||||||
ASSERT(!m_buckets.empty());
|
ASSERT(!m_buckets.empty());
|
||||||
size_type index = HASH()(key) % m_buckets.size();
|
size_type index = HASH()(key) % m_buckets.size();
|
||||||
|
@ -224,85 +189,11 @@ namespace BAN
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH>
|
||||||
const Vector<T>& HashSet<T, HASH>::get_bucket(const T& key) const
|
const LinkedList<T>& HashSet<T, HASH>::get_bucket(const T& key) const
|
||||||
{
|
{
|
||||||
ASSERT(!m_buckets.empty());
|
ASSERT(!m_buckets.empty());
|
||||||
size_type index = HASH()(key) % m_buckets.size();
|
size_type index = HASH()(key) % m_buckets.size();
|
||||||
return m_buckets[index];
|
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Endianness.h>
|
||||||
|
#include <BAN/Formatter.h>
|
||||||
|
#include <BAN/Hash.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
struct IPv4Address
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class Iteration
|
||||||
|
{
|
||||||
|
Continue,
|
||||||
|
Break
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,330 @@
|
||||||
|
#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;
|
||||||
|
template<bool CONST2, typename = enable_if_t<CONST2 == CONST || CONST>>
|
||||||
|
constexpr IteratorSimpleGeneral(const IteratorSimpleGeneral<T, Container, CONST2>& other)
|
||||||
|
: m_pointer(other.m_pointer)
|
||||||
|
, m_valid(other.m_valid)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const T& operator*() const
|
||||||
|
{
|
||||||
|
ASSERT(m_pointer);
|
||||||
|
return *m_pointer;
|
||||||
|
}
|
||||||
|
template<bool CONST2 = CONST>
|
||||||
|
constexpr enable_if_t<!CONST2, T&> operator*()
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
ASSERT(m_pointer);
|
||||||
|
return *m_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const T* operator->() const
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
ASSERT(m_pointer);
|
||||||
|
return m_pointer;
|
||||||
|
}
|
||||||
|
template<bool CONST2 = CONST>
|
||||||
|
constexpr enable_if_t<!CONST2, T*> operator->()
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
ASSERT(m_pointer);
|
||||||
|
return m_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr IteratorSimpleGeneral& operator++()
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
ASSERT(m_pointer);
|
||||||
|
++m_pointer;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
constexpr IteratorSimpleGeneral operator++(int)
|
||||||
|
{
|
||||||
|
auto temp = *this;
|
||||||
|
++(*this);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr IteratorSimpleGeneral& operator--()
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
ASSERT(m_pointer);
|
||||||
|
--m_pointer;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
constexpr IteratorSimpleGeneral operator--(int)
|
||||||
|
{
|
||||||
|
auto temp = *this;
|
||||||
|
--(*this);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_t 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
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr explicit operator bool() const
|
||||||
|
{
|
||||||
|
return m_valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
constexpr 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, template<typename> typename OuterContainer, template<typename> typename InnerContainer, typename Container, bool CONST>
|
||||||
|
class IteratorDoubleGeneral
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Inner = InnerContainer<T>;
|
||||||
|
using Outer = OuterContainer<Inner>;
|
||||||
|
|
||||||
|
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;
|
||||||
|
template<bool CONST2, typename = enable_if_t<CONST2 == CONST || CONST>>
|
||||||
|
constexpr 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
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
ASSERT(m_outer_current != m_outer_end);
|
||||||
|
ASSERT(m_inner_current);
|
||||||
|
return m_inner_current.operator*();
|
||||||
|
}
|
||||||
|
template<bool CONST2 = CONST>
|
||||||
|
constexpr enable_if_t<!CONST2, T&> operator*()
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
ASSERT(m_outer_current != m_outer_end);
|
||||||
|
ASSERT(m_inner_current);
|
||||||
|
return m_inner_current.operator*();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const T* operator->() const
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
ASSERT(m_outer_current != m_outer_end);
|
||||||
|
ASSERT(m_inner_current);
|
||||||
|
return m_inner_current.operator->();
|
||||||
|
}
|
||||||
|
template<bool CONST2 = CONST>
|
||||||
|
constexpr enable_if_t<!CONST2, T*> operator->()
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
ASSERT(m_outer_current != m_outer_end);
|
||||||
|
ASSERT(m_inner_current);
|
||||||
|
return m_inner_current.operator->();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr IteratorDoubleGeneral& operator++()
|
||||||
|
{
|
||||||
|
ASSERT(*this);
|
||||||
|
ASSERT(m_outer_current != m_outer_end);
|
||||||
|
ASSERT(m_inner_current);
|
||||||
|
m_inner_current++;
|
||||||
|
find_valid_or_end();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
constexpr IteratorDoubleGeneral operator++(int)
|
||||||
|
{
|
||||||
|
auto temp = *this;
|
||||||
|
++(*this);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator==(const IteratorDoubleGeneral& other) const
|
||||||
|
{
|
||||||
|
ASSERT(*this && other);
|
||||||
|
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
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr explicit operator bool() const
|
||||||
|
{
|
||||||
|
return !!m_outer_current;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
constexpr IteratorDoubleGeneral(const OuterIterator& outer_end, const OuterIterator& outer_current)
|
||||||
|
: m_outer_end(outer_end)
|
||||||
|
, m_outer_current(outer_current)
|
||||||
|
{
|
||||||
|
if (outer_current != outer_end)
|
||||||
|
{
|
||||||
|
m_inner_current = m_outer_current->begin();
|
||||||
|
find_valid_or_end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
while (m_inner_current == m_outer_current->end())
|
||||||
|
{
|
||||||
|
m_outer_current++;
|
||||||
|
if (m_outer_current == m_outer_end)
|
||||||
|
break;
|
||||||
|
m_inner_current = m_outer_current->begin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
InnerIterator m_inner_current;
|
||||||
|
|
||||||
|
friend class IteratorDoubleGeneral<T, OuterContainer, InnerContainer, Container, !CONST>;
|
||||||
|
friend Container;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename Container>
|
||||||
|
using IteratorSimple = IteratorSimpleGeneral<T, Container, false>;
|
||||||
|
|
||||||
|
template<typename T, typename Container>
|
||||||
|
using ConstIteratorSimple = IteratorSimpleGeneral<T, Container, true>;
|
||||||
|
|
||||||
|
template<typename T, template<typename> typename OuterContainer, template<typename> typename InnerContainer, typename Container>
|
||||||
|
using IteratorDouble = IteratorDoubleGeneral<T, OuterContainer, InnerContainer, Container, false>;
|
||||||
|
|
||||||
|
template<typename T, template<typename> typename OuterContainer, template<typename> typename InnerContainer, typename Container>
|
||||||
|
using ConstIteratorDouble = IteratorDoubleGeneral<T, OuterContainer, InnerContainer, Container, true>;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,156 @@
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -1,15 +1,16 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
#include <BAN/Memory.h>
|
|
||||||
#include <BAN/Move.h>
|
#include <BAN/Move.h>
|
||||||
|
#include <BAN/New.h>
|
||||||
|
#include <BAN/PlacementNew.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
template<typename T, bool CONST>
|
template<typename T, bool CONST>
|
||||||
class LinkedListIterator;
|
class LinkedListIterator;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class LinkedList
|
class LinkedList
|
||||||
{
|
{
|
||||||
|
@ -21,11 +22,11 @@ namespace BAN
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LinkedList() = default;
|
LinkedList() = default;
|
||||||
LinkedList(const LinkedList<T>& other) { *this = other; }
|
LinkedList(const LinkedList<T>& other) requires is_copy_constructible_v<T> { *this = other; }
|
||||||
LinkedList(LinkedList<T>&& other) { *this = move(other); }
|
LinkedList(LinkedList<T>&& other) { *this = move(other); }
|
||||||
~LinkedList() { clear(); }
|
~LinkedList() { clear(); }
|
||||||
|
|
||||||
LinkedList<T>& operator=(const LinkedList<T>&);
|
LinkedList<T>& operator=(const LinkedList<T>&) requires is_copy_constructible_v<T>;
|
||||||
LinkedList<T>& operator=(LinkedList<T>&&);
|
LinkedList<T>& operator=(LinkedList<T>&&);
|
||||||
|
|
||||||
ErrorOr<void> push_back(const T&);
|
ErrorOr<void> push_back(const T&);
|
||||||
|
@ -38,9 +39,11 @@ namespace BAN
|
||||||
ErrorOr<void> emplace(iterator, Args&&...);
|
ErrorOr<void> emplace(iterator, Args&&...);
|
||||||
|
|
||||||
void pop_back();
|
void pop_back();
|
||||||
void remove(iterator);
|
iterator remove(iterator);
|
||||||
void clear();
|
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()); }
|
iterator begin() { return iterator(m_data, empty()); }
|
||||||
const_iterator begin() const { return const_iterator(m_data, empty()); }
|
const_iterator begin() const { return const_iterator(m_data, empty()); }
|
||||||
iterator end() { return iterator(m_last, true); }
|
iterator end() { return iterator(m_last, true); }
|
||||||
|
@ -64,7 +67,11 @@ namespace BAN
|
||||||
Node* prev;
|
Node* prev;
|
||||||
};
|
};
|
||||||
|
|
||||||
ErrorOr<Node*> allocate_node() const;
|
template<typename... Args>
|
||||||
|
ErrorOr<Node*> allocate_node(Args&&...) const;
|
||||||
|
|
||||||
|
Node* remove_node(iterator);
|
||||||
|
void insert_node(iterator, Node*);
|
||||||
|
|
||||||
Node* m_data = nullptr;
|
Node* m_data = nullptr;
|
||||||
Node* m_last = nullptr;
|
Node* m_last = nullptr;
|
||||||
|
@ -114,10 +121,8 @@ namespace BAN
|
||||||
friend class LinkedListIterator<T, !CONST>;
|
friend class LinkedListIterator<T, !CONST>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
LinkedList<T>& LinkedList<T>::operator=(const LinkedList<T>& other)
|
LinkedList<T>& LinkedList<T>::operator=(const LinkedList<T>& other) requires is_copy_constructible_v<T>
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
for (const T& elem : other)
|
for (const T& elem : other)
|
||||||
|
@ -138,6 +143,31 @@ namespace BAN
|
||||||
return *this;
|
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>
|
template<typename T>
|
||||||
ErrorOr<void> LinkedList<T>::push_back(const T& value)
|
ErrorOr<void> LinkedList<T>::push_back(const T& value)
|
||||||
{
|
{
|
||||||
|
@ -159,15 +189,8 @@ namespace BAN
|
||||||
template<typename T>
|
template<typename T>
|
||||||
ErrorOr<void> LinkedList<T>::insert(iterator iter, T&& value)
|
ErrorOr<void> LinkedList<T>::insert(iterator iter, T&& value)
|
||||||
{
|
{
|
||||||
Node* next = iter.m_past_end ? nullptr : iter.m_current;
|
Node* new_node = TRY(allocate_node(move(value)));
|
||||||
Node* prev = next ? next->prev : m_last;
|
insert_node(iter, new_node);
|
||||||
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,36 +205,27 @@ namespace BAN
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
ErrorOr<void> LinkedList<T>::emplace(iterator iter, Args&&... args)
|
ErrorOr<void> LinkedList<T>::emplace(iterator iter, Args&&... args)
|
||||||
{
|
{
|
||||||
Node* next = iter.m_past_end ? nullptr : iter.m_current;
|
Node* new_node = TRY(allocate_node(forward<Args>(args)...));
|
||||||
Node* prev = next ? next->prev : m_last;
|
insert_node(iter, new_node);
|
||||||
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void LinkedList<T>::pop_back()
|
void LinkedList<T>::pop_back()
|
||||||
{
|
{
|
||||||
return remove(m_last);
|
ASSERT(!empty());
|
||||||
|
remove(iterator(m_last, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void LinkedList<T>::remove(iterator iter)
|
LinkedList<T>::iterator LinkedList<T>::remove(iterator iter)
|
||||||
{
|
{
|
||||||
ASSERT(!empty() && iter);
|
ASSERT(!empty() && iter);
|
||||||
Node* node = iter.m_current;
|
Node* node = remove_node(iter);
|
||||||
Node* prev = node->prev;
|
|
||||||
Node* next = node->next;
|
Node* next = node->next;
|
||||||
node->value.~T();
|
node->value.~T();
|
||||||
BAN::deallocator(node);
|
BAN::deallocator(node);
|
||||||
(prev ? prev->next : m_data) = next;
|
return next ? iterator(next, false) : iterator(m_last, true);
|
||||||
(next ? next->prev : m_last) = prev;
|
|
||||||
m_size--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -230,6 +244,16 @@ namespace BAN
|
||||||
m_size = 0;
|
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>
|
template<typename T>
|
||||||
const T& LinkedList<T>::back() const
|
const T& LinkedList<T>::back() const
|
||||||
{
|
{
|
||||||
|
@ -284,16 +308,16 @@ namespace BAN
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
ErrorOr<typename LinkedList<T>::Node*> LinkedList<T>::allocate_node() const
|
template<typename... Args>
|
||||||
|
ErrorOr<typename LinkedList<T>::Node*> LinkedList<T>::allocate_node(Args&&... args) const
|
||||||
{
|
{
|
||||||
Node* node = (Node*)BAN::allocator(sizeof(Node));
|
Node* node = (Node*)BAN::allocator(sizeof(Node));
|
||||||
if (node == nullptr)
|
if (node == nullptr)
|
||||||
return Error::from_errno(ENOMEM);
|
return Error::from_errno(ENOMEM);
|
||||||
|
new (&node->value) T(forward<Args>(args)...);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template<typename T, bool CONST>
|
template<typename T, bool CONST>
|
||||||
template<bool C>
|
template<bool C>
|
||||||
LinkedListIterator<T, CONST>::LinkedListIterator(const LinkedListIterator<T, C>& other, enable_if_t<C == CONST || !C>*)
|
LinkedListIterator<T, CONST>::LinkedListIterator(const LinkedListIterator<T, C>& other, enable_if_t<C == CONST || !C>*)
|
||||||
|
@ -378,7 +402,7 @@ namespace BAN
|
||||||
ASSERT(m_current);
|
ASSERT(m_current);
|
||||||
return &m_current->value;
|
return &m_current->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, bool CONST>
|
template<typename T, bool CONST>
|
||||||
bool LinkedListIterator<T, CONST>::operator==(const LinkedListIterator<T, CONST>& other) const
|
bool LinkedListIterator<T, CONST>::operator==(const LinkedListIterator<T, CONST>& other) const
|
||||||
{
|
{
|
||||||
|
@ -399,4 +423,4 @@ namespace BAN
|
||||||
return m_current;
|
return m_current;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
#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,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Limits.h>
|
||||||
#include <BAN/Traits.h>
|
#include <BAN/Traits.h>
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
@ -8,6 +9,12 @@
|
||||||
namespace BAN::Math
|
namespace BAN::Math
|
||||||
{
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline constexpr T abs(T val)
|
||||||
|
{
|
||||||
|
return val < 0 ? -val : val;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline constexpr T min(T a, T b)
|
inline constexpr T min(T a, T b)
|
||||||
{
|
{
|
||||||
|
@ -52,21 +59,86 @@ namespace BAN::Math
|
||||||
}
|
}
|
||||||
|
|
||||||
template<integral T>
|
template<integral T>
|
||||||
inline constexpr T little_endian_to_host(const uint8_t* bytes)
|
inline constexpr bool is_power_of_two(T value)
|
||||||
{
|
{
|
||||||
T result = 0;
|
if (value == 0)
|
||||||
for (size_t i = 0; i < sizeof(T); i++)
|
return false;
|
||||||
result |= (T)bytes[i] << (i * 8);
|
return (value & (value - 1)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<BAN::integral T>
|
||||||
|
static constexpr bool will_multiplication_overflow(T a, T b)
|
||||||
|
{
|
||||||
|
if (a == 0 || b == 0)
|
||||||
|
return false;
|
||||||
|
if ((a > 0) == (b > 0))
|
||||||
|
return a > BAN::numeric_limits<T>::max() / b;
|
||||||
|
else
|
||||||
|
return a < BAN::numeric_limits<T>::min() / b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<BAN::integral T>
|
||||||
|
static constexpr bool will_addition_overflow(T a, T b)
|
||||||
|
{
|
||||||
|
if (a > 0 && b > 0)
|
||||||
|
return a > BAN::numeric_limits<T>::max() - b;
|
||||||
|
if (a < 0 && b < 0)
|
||||||
|
return a < BAN::numeric_limits<T>::min() - b;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 value)
|
||||||
|
{
|
||||||
|
if constexpr(is_same_v<T, unsigned int>)
|
||||||
|
return sizeof(T) * 8 - __builtin_clz(value) - 1;
|
||||||
|
if constexpr(is_same_v<T, unsigned long>)
|
||||||
|
return sizeof(T) * 8 - __builtin_clzl(value) - 1;
|
||||||
|
return sizeof(T) * 8 - __builtin_clzll(value) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T log2(T value)
|
||||||
|
{
|
||||||
|
T result;
|
||||||
|
asm volatile("fyl2x" : "=t"(result) : "0"(value), "u"((T)1.0) : "st(1)");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<integral T>
|
template<floating_point T>
|
||||||
inline constexpr T big_endian_to_host(const uint8_t* bytes)
|
inline constexpr T log10(T value)
|
||||||
{
|
{
|
||||||
T result = 0;
|
constexpr T INV_LOG_2_10 = 0.3010299956639811952137388947244930267681898814621085413104274611;
|
||||||
for (size_t i = 0; i < sizeof(T); i++)
|
T result;
|
||||||
result |= (T)bytes[i] << (8 * (sizeof(T) - i - 1));
|
asm volatile("fyl2x" : "=t"(result) : "0"(value), "u"(INV_LOG_2_10) : "st(1)");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
template<floating_point T>
|
||||||
|
inline constexpr T log(T value, T base)
|
||||||
|
{
|
||||||
|
return log2(value) / log2(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<floating_point T>
|
||||||
|
inline constexpr T pow(T base, T exp)
|
||||||
|
{
|
||||||
|
T result;
|
||||||
|
asm volatile(
|
||||||
|
"fyl2x;"
|
||||||
|
"fld1;"
|
||||||
|
"fld %%st(1);"
|
||||||
|
"fprem;"
|
||||||
|
"f2xm1;"
|
||||||
|
"faddp;"
|
||||||
|
"fscale;"
|
||||||
|
"fxch %%st(1);"
|
||||||
|
"fstp %%st;"
|
||||||
|
: "=t"(result)
|
||||||
|
: "0"(base), "u"(exp)
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include <BAN/Traits.h>
|
#include <BAN/Traits.h>
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -24,4 +26,4 @@ namespace BAN
|
||||||
return static_cast<T&&>(arg);
|
return static_cast<T&&>(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined(__is_kernel)
|
||||||
|
#include <kernel/Memory/kmalloc.h>
|
||||||
|
#else
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
#if defined(__is_kernel)
|
||||||
|
static constexpr void*(&allocator)(size_t) = kmalloc;
|
||||||
|
static constexpr void(&deallocator)(void*) = kfree;
|
||||||
|
#else
|
||||||
|
static constexpr void*(&allocator)(size_t) = malloc;
|
||||||
|
static constexpr void(&deallocator)(void*) = free;
|
||||||
|
#endif
|
||||||
|
}
|
|
@ -0,0 +1,196 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Assert.h>
|
||||||
|
#include <BAN/Move.h>
|
||||||
|
#include <BAN/PlacementNew.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class Optional
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
constexpr Optional();
|
||||||
|
constexpr Optional(Optional&&);
|
||||||
|
constexpr Optional(const Optional&);
|
||||||
|
constexpr Optional(const T&);
|
||||||
|
constexpr Optional(T&&);
|
||||||
|
template<typename... Args>
|
||||||
|
constexpr Optional(Args&&...);
|
||||||
|
|
||||||
|
~Optional();
|
||||||
|
|
||||||
|
constexpr Optional& operator=(Optional&&);
|
||||||
|
constexpr Optional& operator=(const Optional&);
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
constexpr Optional& emplace(Args&&...);
|
||||||
|
|
||||||
|
constexpr T* operator->();
|
||||||
|
constexpr const T* operator->() const;
|
||||||
|
|
||||||
|
constexpr T& operator*();
|
||||||
|
constexpr const T& operator*() const;
|
||||||
|
|
||||||
|
constexpr bool has_value() const;
|
||||||
|
|
||||||
|
constexpr T release_value();
|
||||||
|
constexpr T& value();
|
||||||
|
constexpr const T& value() const;
|
||||||
|
|
||||||
|
constexpr void clear();
|
||||||
|
|
||||||
|
private:
|
||||||
|
alignas(T) uint8_t m_storage[sizeof(T)];
|
||||||
|
bool m_has_value { false };
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr 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)
|
||||||
|
: m_has_value(true)
|
||||||
|
{
|
||||||
|
new (m_storage) T(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr Optional<T>::Optional(T&& value)
|
||||||
|
: m_has_value(true)
|
||||||
|
{
|
||||||
|
new (m_storage) T(move(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
template<typename... Args>
|
||||||
|
constexpr Optional<T>::Optional(Args&&... args)
|
||||||
|
: m_has_value(true)
|
||||||
|
{
|
||||||
|
new (m_storage) T(forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Optional<T>::~Optional()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr Optional<T>& Optional<T>::operator=(Optional&& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_has_value = other.has_value();
|
||||||
|
if (other.has_value())
|
||||||
|
new (m_storage) T(move(other.release_value()));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr Optional<T>& Optional<T>::operator=(const Optional& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_has_value = other.has_value();
|
||||||
|
if (other.has_value)
|
||||||
|
new (m_storage) T(other.value());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
template<typename... Args>
|
||||||
|
constexpr Optional<T>& Optional<T>::emplace(Args&&... args)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_has_value = true;
|
||||||
|
new (m_storage) T(forward<Args>(args)...);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr T* Optional<T>::operator->()
|
||||||
|
{
|
||||||
|
ASSERT(has_value());
|
||||||
|
return &value();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr const T* Optional<T>::operator->() const
|
||||||
|
{
|
||||||
|
ASSERT(has_value());
|
||||||
|
return &value();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr T& Optional<T>::operator*()
|
||||||
|
{
|
||||||
|
ASSERT(has_value());
|
||||||
|
return value();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr const T& Optional<T>::operator*() const
|
||||||
|
{
|
||||||
|
ASSERT(has_value());
|
||||||
|
return value();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr bool Optional<T>::has_value() const
|
||||||
|
{
|
||||||
|
return m_has_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr T Optional<T>::release_value()
|
||||||
|
{
|
||||||
|
ASSERT(has_value());
|
||||||
|
T released_value = move(value());
|
||||||
|
value().~T();
|
||||||
|
m_has_value = false;
|
||||||
|
return move(released_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr T& Optional<T>::value()
|
||||||
|
{
|
||||||
|
ASSERT(has_value());
|
||||||
|
return (T&)m_storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr const T& Optional<T>::value() const
|
||||||
|
{
|
||||||
|
ASSERT(has_value());
|
||||||
|
return (const T&)m_storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr void Optional<T>::clear()
|
||||||
|
{
|
||||||
|
if (m_has_value)
|
||||||
|
value().~T();
|
||||||
|
m_has_value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
inline void* operator new(size_t, void* addr) { return addr; }
|
||||||
|
inline void* operator new[](size_t, void* addr) { return addr; }
|
|
@ -1,9 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
|
#include <BAN/Iterators.h>
|
||||||
#include <BAN/Math.h>
|
#include <BAN/Math.h>
|
||||||
#include <BAN/Memory.h>
|
|
||||||
#include <BAN/Move.h>
|
#include <BAN/Move.h>
|
||||||
|
#include <BAN/New.h>
|
||||||
|
#include <BAN/PlacementNew.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
@ -14,6 +16,8 @@ namespace BAN
|
||||||
public:
|
public:
|
||||||
using size_type = size_t;
|
using size_type = size_t;
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
|
using iterator = IteratorSimple<T, Queue>;
|
||||||
|
using const_iterator = ConstIteratorSimple<T, Queue>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Queue() = default;
|
Queue() = default;
|
||||||
|
@ -32,10 +36,16 @@ namespace BAN
|
||||||
ErrorOr<void> reserve(size_type);
|
ErrorOr<void> reserve(size_type);
|
||||||
ErrorOr<void> shrink_to_fit();
|
ErrorOr<void> shrink_to_fit();
|
||||||
|
|
||||||
|
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); }
|
||||||
|
|
||||||
void pop();
|
void pop();
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
bool empty() const;
|
bool empty() const;
|
||||||
|
size_type capacity() const;
|
||||||
size_type size() const;
|
size_type size() const;
|
||||||
|
|
||||||
const T& front() const;
|
const T& front() const;
|
||||||
|
@ -177,6 +187,12 @@ namespace BAN
|
||||||
return m_size == 0;
|
return m_size == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename Queue<T>::size_type Queue<T>::capacity() const
|
||||||
|
{
|
||||||
|
return m_capacity;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
typename Queue<T>::size_type Queue<T>::size() const
|
typename Queue<T>::size_type Queue<T>::size() const
|
||||||
{
|
{
|
||||||
|
@ -217,4 +233,4 @@ namespace BAN
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Atomic.h>
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
#include <BAN/Move.h>
|
#include <BAN/Move.h>
|
||||||
#include <BAN/NoCopyMove.h>
|
#include <BAN/NoCopyMove.h>
|
||||||
|
|
||||||
#if defined(__is_kernel)
|
|
||||||
#include <kernel/kmalloc.h>
|
|
||||||
#else
|
|
||||||
#include <stdlib.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
#if defined(__is_kernel)
|
|
||||||
static constexpr void*(&allocator)(size_t) = kmalloc;
|
|
||||||
static constexpr void(&deallocator)(void*) = kfree;
|
|
||||||
#else
|
|
||||||
static constexpr void*(&allocator)(size_t) = malloc;
|
|
||||||
static constexpr void(&deallocator)(void*) = free;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class RefCounted
|
class RefCounted
|
||||||
|
@ -37,24 +23,36 @@ namespace BAN
|
||||||
|
|
||||||
void ref() const
|
void ref() const
|
||||||
{
|
{
|
||||||
ASSERT(m_ref_count > 0);
|
uint32_t old = m_ref_count.fetch_add(1, MemoryOrder::memory_order_relaxed);
|
||||||
m_ref_count++;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void unref() const
|
void unref() const
|
||||||
{
|
{
|
||||||
ASSERT(m_ref_count > 0);
|
uint32_t old = m_ref_count.fetch_sub(1);
|
||||||
m_ref_count--;
|
ASSERT(old > 0);
|
||||||
if (m_ref_count == 0)
|
if (old == 1)
|
||||||
delete (const T*)this;
|
delete (const T*)this;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
RefCounted() = default;
|
RefCounted() = default;
|
||||||
~RefCounted() { ASSERT(m_ref_count == 0); }
|
virtual ~RefCounted() { ASSERT(m_ref_count == 0); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutable uint32_t m_ref_count = 1;
|
mutable Atomic<uint32_t> m_ref_count = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -68,7 +66,6 @@ namespace BAN
|
||||||
if (m_pointer)
|
if (m_pointer)
|
||||||
m_pointer->ref();
|
m_pointer->ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
~RefPtr() { clear(); }
|
~RefPtr() { clear(); }
|
||||||
|
|
||||||
template<typename U>
|
template<typename U>
|
||||||
|
@ -90,6 +87,10 @@ namespace BAN
|
||||||
|
|
||||||
RefPtr(const RefPtr& other) { *this = other; }
|
RefPtr(const RefPtr& other) { *this = other; }
|
||||||
RefPtr(RefPtr&& other) { *this = move(other); }
|
RefPtr(RefPtr&& other) { *this = move(other); }
|
||||||
|
template<typename U>
|
||||||
|
RefPtr(const RefPtr<U>& other) { *this = other; }
|
||||||
|
template<typename U>
|
||||||
|
RefPtr(RefPtr<U>&& other) { *this = move(other); }
|
||||||
|
|
||||||
RefPtr& operator=(const RefPtr& other)
|
RefPtr& operator=(const RefPtr& other)
|
||||||
{
|
{
|
||||||
|
@ -108,6 +109,25 @@ namespace BAN
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
RefPtr& operator=(const RefPtr<U>& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_pointer = other.m_pointer;
|
||||||
|
if (m_pointer)
|
||||||
|
m_pointer->ref();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
RefPtr& operator=(RefPtr<U>&& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_pointer = other.m_pointer;
|
||||||
|
other.m_pointer = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
T* ptr() { ASSERT(!empty()); return m_pointer; }
|
T* ptr() { ASSERT(!empty()); return m_pointer; }
|
||||||
const T* ptr() const { ASSERT(!empty()); return m_pointer; }
|
const T* ptr() const { ASSERT(!empty()); return m_pointer; }
|
||||||
|
|
||||||
|
@ -117,8 +137,11 @@ namespace BAN
|
||||||
T* operator->() { return ptr(); }
|
T* operator->() { return ptr(); }
|
||||||
const T* operator->() const { 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; }
|
bool empty() const { return m_pointer == nullptr; }
|
||||||
operator bool() const { return m_pointer; }
|
explicit operator bool() const { return m_pointer; }
|
||||||
|
|
||||||
void clear()
|
void clear()
|
||||||
{
|
{
|
||||||
|
@ -129,9 +152,9 @@ namespace BAN
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T* m_pointer = nullptr;
|
T* m_pointer = nullptr;
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
friend class RefPtr;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void* operator new(size_t, void* addr) { return addr; }
|
|
||||||
inline void* operator new[](size_t, void* addr) { return addr; }
|
|
|
@ -13,10 +13,16 @@ namespace BAN
|
||||||
{ }
|
{ }
|
||||||
~ScopeGuard()
|
~ScopeGuard()
|
||||||
{
|
{
|
||||||
m_func();
|
if (m_enabled)
|
||||||
|
m_func();
|
||||||
|
}
|
||||||
|
void disable()
|
||||||
|
{
|
||||||
|
m_enabled = false;
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
BAN::Function<void()> m_func;
|
BAN::Function<void()> m_func;
|
||||||
|
bool m_enabled { true };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,240 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#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<typename It::value_type>>
|
||||||
|
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<typename It::value_type>>
|
||||||
|
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<typename It::value_type>>
|
||||||
|
void insertion_sort(It begin, It end, Comp comp = {})
|
||||||
|
{
|
||||||
|
if (distance(begin, end) <= 1)
|
||||||
|
return;
|
||||||
|
for (It it1 = next(begin, 1); it1 != end; ++it1)
|
||||||
|
{
|
||||||
|
typename It::value_type x = move(*it1);
|
||||||
|
It it2 = it1;
|
||||||
|
for (; it2 != begin && comp(x, *prev(it2, 1)); --it2)
|
||||||
|
*it2 = move(*prev(it2, 1));
|
||||||
|
*it2 = move(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename It, typename Comp>
|
||||||
|
void push_heap(It begin, size_t hole_index, size_t top_index, typename It::value_type value, Comp comp)
|
||||||
|
{
|
||||||
|
size_t parent = (hole_index - 1) / 2;
|
||||||
|
while (hole_index > top_index && comp(*next(begin, parent), value))
|
||||||
|
{
|
||||||
|
*next(begin, hole_index) = move(*next(begin, parent));
|
||||||
|
hole_index = parent;
|
||||||
|
parent = (hole_index - 1) / 2;
|
||||||
|
}
|
||||||
|
*next(begin, hole_index) = move(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename It, typename Comp>
|
||||||
|
void adjust_heap(It begin, size_t hole_index, size_t len, typename It::value_type value, Comp comp)
|
||||||
|
{
|
||||||
|
const size_t top_index = hole_index;
|
||||||
|
size_t child = hole_index;
|
||||||
|
while (child < (len - 1) / 2)
|
||||||
|
{
|
||||||
|
child = 2 * (child + 1);
|
||||||
|
if (comp(*next(begin, child), *next(begin, child - 1)))
|
||||||
|
child--;
|
||||||
|
*next(begin, hole_index) = move(*next(begin, child));
|
||||||
|
hole_index = child;
|
||||||
|
}
|
||||||
|
if (len % 2 == 0 && child == (len - 2) / 2)
|
||||||
|
{
|
||||||
|
child = 2 * (child + 1);
|
||||||
|
*next(begin, hole_index) = move(*next(begin, child - 1));
|
||||||
|
hole_index = child - 1;
|
||||||
|
}
|
||||||
|
push_heap(begin, hole_index, top_index, move(value), comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename It, typename Comp = less<typename It::value_type>>
|
||||||
|
void make_heap(It begin, It end, Comp comp = {})
|
||||||
|
{
|
||||||
|
const size_t len = distance(begin, end);
|
||||||
|
if (len <= 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
size_t parent = (len - 2) / 2;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
detail::adjust_heap(begin, parent, len, move(*next(begin, parent)), comp);
|
||||||
|
|
||||||
|
if (parent == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
parent--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename It, typename Comp = less<typename It::value_type>>
|
||||||
|
void sort_heap(It begin, It end, Comp comp = {})
|
||||||
|
{
|
||||||
|
const size_t len = distance(begin, end);
|
||||||
|
if (len <= 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
size_t last = len;
|
||||||
|
while (last > 1)
|
||||||
|
{
|
||||||
|
last--;
|
||||||
|
typename It::value_type x = move(*next(begin, last));
|
||||||
|
*next(begin, last) = move(*begin);
|
||||||
|
detail::adjust_heap(begin, 0, last, move(x), comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename It, typename Comp = less<typename It::value_type>>
|
||||||
|
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<typename It::value_type>>
|
||||||
|
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<typename It::value_type> && (radix > 0 && (radix & (radix - 1)) == 0)
|
||||||
|
BAN::ErrorOr<void> radix_sort(It begin, It end)
|
||||||
|
{
|
||||||
|
using value_type = typename It::value_type;
|
||||||
|
|
||||||
|
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<typename It::value_type>>
|
||||||
|
void sort(It begin, It end, Comp comp = {})
|
||||||
|
{
|
||||||
|
return intro_sort(begin, end, comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,134 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Assert.h>
|
||||||
|
#include <BAN/Iterators.h>
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class Span
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using value_type = T;
|
||||||
|
using size_type = size_t;
|
||||||
|
using iterator = IteratorSimple<T, Span>;
|
||||||
|
using const_iterator = ConstIteratorSimple<T, Span>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Span() = default;
|
||||||
|
Span(T*, size_type);
|
||||||
|
Span(Span<T>&);
|
||||||
|
template<typename S>
|
||||||
|
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); }
|
||||||
|
|
||||||
|
T& operator[](size_type);
|
||||||
|
const T& operator[](size_type) const;
|
||||||
|
|
||||||
|
T* data();
|
||||||
|
const T* data() const;
|
||||||
|
|
||||||
|
bool empty() const;
|
||||||
|
size_type size() const;
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
Span slice(size_type, size_type = ~size_type(0));
|
||||||
|
|
||||||
|
Span<const T> as_const() const { return Span<const T>(m_data, m_size); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
T* m_data = nullptr;
|
||||||
|
size_type m_size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
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(m_size - start >= length);
|
||||||
|
return Span(m_data + start, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,8 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <BAN/ForwardList.h>
|
#include <BAN/Errors.h>
|
||||||
#include <BAN/Formatter.h>
|
#include <BAN/Formatter.h>
|
||||||
#include <BAN/Hash.h>
|
#include <BAN/Hash.h>
|
||||||
|
#include <BAN/Iterators.h>
|
||||||
|
#include <BAN/New.h>
|
||||||
|
#include <BAN/StringView.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
@ -11,72 +14,319 @@ namespace BAN
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using size_type = size_t;
|
using size_type = size_t;
|
||||||
|
using iterator = IteratorSimple<char, String>;
|
||||||
|
using const_iterator = ConstIteratorSimple<char, String>;
|
||||||
|
static constexpr size_type sso_capacity = 15;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
String();
|
String() {}
|
||||||
String(const String&);
|
String(const String& other) { *this = other; }
|
||||||
String(String&&);
|
String(String&& other) { *this = move(other); }
|
||||||
String(StringView);
|
String(StringView other) { *this = other; }
|
||||||
~String();
|
~String() { clear(); }
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
static String formatted(const char* format, const Args&... 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)...);
|
||||||
|
|
||||||
String& operator=(const String&);
|
String result;
|
||||||
String& operator=(String&&);
|
TRY(result.reserve(length));
|
||||||
String& operator=(StringView);
|
BAN::Formatter::print([&](char c){ MUST(result.push_back(c)); }, format, BAN::forward<Args>(args)...);
|
||||||
|
|
||||||
ErrorOr<void> push_back(char);
|
return result;
|
||||||
ErrorOr<void> insert(char, size_type);
|
}
|
||||||
ErrorOr<void> insert(StringView, size_type);
|
|
||||||
ErrorOr<void> append(StringView);
|
|
||||||
ErrorOr<void> append(const String&);
|
|
||||||
|
|
||||||
void pop_back();
|
String& operator=(const String& other)
|
||||||
void remove(size_type);
|
{
|
||||||
void erase(size_type, size_type);
|
clear();
|
||||||
|
MUST(ensure_capacity(other.size()));
|
||||||
|
memcpy(data(), other.data(), other.size() + 1);
|
||||||
|
m_size = other.size();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
void clear();
|
String& operator=(String&& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
|
||||||
char operator[](size_type) const;
|
if (other.has_sso())
|
||||||
char& operator[](size_type);
|
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;
|
||||||
|
|
||||||
bool operator==(const String&) const;
|
other.m_size = 0;
|
||||||
bool operator==(StringView) const;
|
other.m_storage.sso_storage = SSOStorage();
|
||||||
bool operator==(const char*) const;
|
other.m_has_sso = true;
|
||||||
|
|
||||||
ErrorOr<void> resize(size_type, char = '\0');
|
return *this;
|
||||||
ErrorOr<void> reserve(size_type);
|
}
|
||||||
ErrorOr<void> shrink_to_fit();
|
|
||||||
|
|
||||||
StringView sv() const;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
bool empty() const;
|
ErrorOr<void> push_back(char c)
|
||||||
size_type size() const;
|
{
|
||||||
size_type capacity() const;
|
TRY(ensure_capacity(m_size + 1));
|
||||||
|
data()[m_size] = c;
|
||||||
|
m_size++;
|
||||||
|
data()[m_size] = '\0';
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
const char* data() 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> 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 {};
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop_back()
|
||||||
|
{
|
||||||
|
ASSERT(m_size > 0);
|
||||||
|
m_size--;
|
||||||
|
data()[m_size] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(size_type index)
|
||||||
|
{
|
||||||
|
ASSERT(index < m_size);
|
||||||
|
memcpy(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;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ErrorOr<void> ensure_capacity(size_type);
|
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> copy_impl(StringView);
|
char* new_data = (char*)allocator(new_size + 1);
|
||||||
void move_impl(String&&);
|
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; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
char* m_data = nullptr;
|
struct SSOStorage
|
||||||
size_type m_capacity = 0;
|
{
|
||||||
size_type m_size = 0;
|
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 };
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
String String::formatted(const char* format, const Args&... args)
|
|
||||||
{
|
|
||||||
String result;
|
|
||||||
BAN::Formatter::print([&](char c){ result.push_back(c); }, format, args...);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
struct hash<String>
|
struct hash<String>
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <BAN/ForwardList.h>
|
|
||||||
#include <BAN/Formatter.h>
|
#include <BAN/Formatter.h>
|
||||||
|
#include <BAN/ForwardList.h>
|
||||||
|
#include <BAN/Hash.h>
|
||||||
|
#include <BAN/Iterators.h>
|
||||||
|
#include <BAN/Optional.h>
|
||||||
|
#include <BAN/Vector.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
@ -10,41 +14,237 @@ namespace BAN
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using size_type = size_t;
|
using size_type = size_t;
|
||||||
|
using const_iterator = ConstIteratorSimple<char, StringView>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StringView();
|
constexpr StringView() {}
|
||||||
|
constexpr StringView(const char* string, size_type len = -1)
|
||||||
|
{
|
||||||
|
if (len == size_type(-1))
|
||||||
|
len = strlen(string);
|
||||||
|
m_data = string;
|
||||||
|
m_size = len;
|
||||||
|
}
|
||||||
StringView(const String&);
|
StringView(const String&);
|
||||||
StringView(const char*, size_type = -1);
|
|
||||||
|
|
||||||
char operator[](size_type) const;
|
constexpr const_iterator begin() const { return const_iterator(m_data); }
|
||||||
|
constexpr const_iterator end() const { return const_iterator(m_data + m_size); }
|
||||||
|
|
||||||
bool operator==(const String&) const;
|
constexpr char operator[](size_type index) const
|
||||||
bool operator==(StringView) const;
|
{
|
||||||
bool operator==(const char*) const;
|
ASSERT(index < m_size);
|
||||||
|
return m_data[index];
|
||||||
|
}
|
||||||
|
|
||||||
StringView substring(size_type, size_type = -1) 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;
|
||||||
|
}
|
||||||
|
|
||||||
ErrorOr<Vector<StringView>> split(char, bool = false);
|
constexpr bool operator==(const char* other) const
|
||||||
ErrorOr<Vector<StringView>> split(bool(*comp)(char), bool = false);
|
{
|
||||||
|
for (size_type i = 0; i < m_size; i++)
|
||||||
|
if (m_data[i] != other[i])
|
||||||
|
return false;
|
||||||
|
return other[m_size] == '\0';
|
||||||
|
}
|
||||||
|
|
||||||
char back() const;
|
constexpr StringView substring(size_type index, size_type len = -1) const
|
||||||
char front() 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;
|
||||||
|
}
|
||||||
|
|
||||||
size_type count(char) const;
|
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++;
|
||||||
|
}
|
||||||
|
|
||||||
bool empty() const;
|
Vector<StringView> result;
|
||||||
size_type size() const;
|
TRY(result.reserve(count));
|
||||||
|
|
||||||
const char* data() 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 < m_size - target.size(); i++)
|
||||||
|
{
|
||||||
|
bool valid = true;
|
||||||
|
for (size_type j = 0; j < target.size() && valid; j++)
|
||||||
|
valid = (m_data[i + j] == target[j]);
|
||||||
|
if (valid)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const char* m_data = nullptr;
|
const char* m_data = nullptr;
|
||||||
size_type m_size = 0;
|
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 BAN::StringView operator""sv(const char* str, BAN::StringView::size_type len) { return BAN::StringView(str, len); }
|
inline constexpr BAN::StringView operator""_sv(const char* str, BAN::StringView::size_type len) { return BAN::StringView(str, len); }
|
||||||
|
|
||||||
namespace BAN::Formatter
|
namespace BAN::Formatter
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -9,15 +9,18 @@ namespace BAN
|
||||||
|
|
||||||
struct Time
|
struct Time
|
||||||
{
|
{
|
||||||
uint8_t second;
|
uint32_t year;
|
||||||
uint8_t minute;
|
|
||||||
uint8_t hour;
|
|
||||||
uint8_t week_day;
|
|
||||||
uint8_t day;
|
|
||||||
uint8_t month;
|
uint8_t month;
|
||||||
int year;
|
uint8_t day;
|
||||||
|
uint8_t hour;
|
||||||
|
uint8_t minute;
|
||||||
|
uint8_t second;
|
||||||
|
uint8_t week_day;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
uint64_t to_unix_time(const BAN::Time&);
|
||||||
|
BAN::Time from_unix_time(uint64_t);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace BAN::Formatter
|
namespace BAN::Formatter
|
||||||
|
@ -31,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);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
template<typename T> struct remove_refenrece { using type = T; };
|
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> struct remove_refenrece<T&&> { using type = T; };
|
template<typename T> struct remove_refenrece<T&&> { using type = T; };
|
||||||
|
@ -30,16 +30,36 @@ namespace BAN
|
||||||
template<typename T> struct maybe_const<true, T> { using type = const T; };
|
template<typename T> struct maybe_const<true, T> { using type = const T; };
|
||||||
template<bool B, typename T> using maybe_const_t = typename maybe_const<B, T>::type;
|
template<bool B, typename T> using maybe_const_t = typename maybe_const<B, T>::type;
|
||||||
|
|
||||||
struct true_type { static constexpr bool value = true; };
|
template<bool B, typename T1, typename T2> struct either_or { using type = T2; };
|
||||||
struct false_type { static constexpr bool value = false; };
|
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>;
|
||||||
|
|
||||||
template<typename T, typename S> struct is_same : false_type {};
|
template<typename T, typename S> struct is_same : false_type {};
|
||||||
template<typename T> struct is_same<T, T> : true_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> 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 : false_type {};
|
||||||
template<typename T> struct is_lvalue_reference<T&> : true_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> 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_integral { static constexpr bool value = requires (T t, T* p, void (*f)(T)) { reinterpret_cast<T>(t); f(0); p + t; }; };
|
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> inline constexpr bool is_integral_v = is_integral<T>::value;
|
||||||
|
@ -60,8 +80,64 @@ namespace BAN
|
||||||
template<typename T> inline constexpr bool is_pointer_v = is_pointer<T>::value;
|
template<typename T> inline constexpr bool is_pointer_v = is_pointer<T>::value;
|
||||||
template<typename T> concept pointer = is_pointer_v<T>;
|
template<typename T> concept pointer = is_pointer_v<T>;
|
||||||
|
|
||||||
|
template<typename T> struct is_const : false_type {};
|
||||||
|
template<typename T> struct is_const<const T> : true_type {};
|
||||||
|
template<typename T> inline constexpr bool is_const_v = is_const<T>::value;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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 less { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; } };
|
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 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; } };
|
template<typename T> struct greater { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs > rhs; } };
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,26 +3,80 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN::UTF8
|
||||||
{
|
{
|
||||||
|
|
||||||
static constexpr uint16_t utf8_to_codepoint(uint8_t* bytes, size_t count)
|
static constexpr uint32_t invalid = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
constexpr uint32_t byte_length(uint8_t first_byte)
|
||||||
{
|
{
|
||||||
if (count > 3)
|
if ((first_byte & 0x80) == 0x00)
|
||||||
return 0xFFFF;
|
return 1;
|
||||||
|
if ((first_byte & 0xE0) == 0xC0)
|
||||||
for (size_t i = 1; i < count; i++)
|
return 2;
|
||||||
if ((bytes[i] & 0xC0) != 0x80)
|
if ((first_byte & 0xF0) == 0xE0)
|
||||||
return 0xFFFF;
|
return 3;
|
||||||
|
if ((first_byte & 0xF8) == 0xF0)
|
||||||
switch (count)
|
return 4;
|
||||||
{
|
return 0;
|
||||||
case 1: return bytes[0];
|
|
||||||
case 2: return ((bytes[0] & 0x1F) << 6) | (bytes[1] & 0x3F);
|
|
||||||
case 3: return ((bytes[0] & 0x1F) << 12) | ((bytes[1] & 0x3F) << 6) | (bytes[2] & 0x3F);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0xFFFF;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
template<typename T> requires (sizeof(T) == 1)
|
||||||
|
constexpr uint32_t to_codepoint(const T* bytes)
|
||||||
|
{
|
||||||
|
uint32_t length = byte_length(bytes[0]);
|
||||||
|
|
||||||
|
for (uint32_t i = 1; i < length; i++)
|
||||||
|
if (((uint8_t)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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return UTF8::invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr bool from_codepoints(const T* codepoints, size_t count, char* out)
|
||||||
|
{
|
||||||
|
uint8_t* ptr = (uint8_t*)out;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if (codepoints[i] < 0x80)
|
||||||
|
{
|
||||||
|
*ptr++ = codepoints[i];
|
||||||
|
}
|
||||||
|
else if (codepoints[i] < 0x800)
|
||||||
|
{
|
||||||
|
*ptr++ = 0xC0 | ((codepoints[i] >> 6) & 0x1F);
|
||||||
|
*ptr++ = 0x80 | ((codepoints[i] >> 0) & 0x3F);
|
||||||
|
}
|
||||||
|
else if (codepoints[i] < 0x10000)
|
||||||
|
{
|
||||||
|
*ptr++ = 0xE0 | ((codepoints[i] >> 12) & 0x0F);
|
||||||
|
*ptr++ = 0x80 | ((codepoints[i] >> 6) & 0x3F);
|
||||||
|
*ptr++ = 0x80 | ((codepoints[i] >> 0) & 0x3F);
|
||||||
|
}
|
||||||
|
else if (codepoints[i] < 0x110000)
|
||||||
|
{
|
||||||
|
*ptr++ = 0xF0 | ((codepoints[i] >> 18) & 0x07);
|
||||||
|
*ptr++ = 0x80 | ((codepoints[i] >> 12) & 0x3F);
|
||||||
|
*ptr++ = 0x80 | ((codepoints[i] >> 6) & 0x3F);
|
||||||
|
*ptr++ = 0x80 | ((codepoints[i] >> 0) & 0x3F);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Errors.h>
|
||||||
|
#include <BAN/NoCopyMove.h>
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class UniqPtr
|
||||||
|
{
|
||||||
|
BAN_NON_COPYABLE(UniqPtr);
|
||||||
|
|
||||||
|
public:
|
||||||
|
UniqPtr() = default;
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
UniqPtr(UniqPtr<U>&& other)
|
||||||
|
{
|
||||||
|
m_pointer = other.m_pointer;
|
||||||
|
other.m_pointer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
~UniqPtr()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
static UniqPtr adopt(T* pointer)
|
||||||
|
{
|
||||||
|
UniqPtr uniq;
|
||||||
|
uniq.m_pointer = pointer;
|
||||||
|
return uniq;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
static BAN::ErrorOr<UniqPtr> create(Args&&... args)
|
||||||
|
{
|
||||||
|
UniqPtr uniq;
|
||||||
|
uniq.m_pointer = new T(BAN::forward<Args>(args)...);
|
||||||
|
if (uniq.m_pointer == nullptr)
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
return uniq;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
UniqPtr& operator=(UniqPtr<U>&& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_pointer = other.m_pointer;
|
||||||
|
other.m_pointer = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
T& operator*()
|
||||||
|
{
|
||||||
|
ASSERT(m_pointer);
|
||||||
|
return *m_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T& operator*() const
|
||||||
|
{
|
||||||
|
ASSERT(m_pointer);
|
||||||
|
return *m_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* operator->()
|
||||||
|
{
|
||||||
|
ASSERT(m_pointer);
|
||||||
|
return m_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T* operator->() const
|
||||||
|
{
|
||||||
|
ASSERT(m_pointer);
|
||||||
|
return m_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* ptr() { return m_pointer; }
|
||||||
|
const T* ptr() const { return m_pointer; }
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
if (m_pointer)
|
||||||
|
delete m_pointer;
|
||||||
|
m_pointer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator bool() const { return m_pointer != nullptr; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
T* m_pointer = nullptr;
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
friend class UniqPtr;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -3,145 +3,301 @@
|
||||||
#include <BAN/Assert.h>
|
#include <BAN/Assert.h>
|
||||||
#include <BAN/Math.h>
|
#include <BAN/Math.h>
|
||||||
#include <BAN/Move.h>
|
#include <BAN/Move.h>
|
||||||
|
#include <BAN/PlacementNew.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
template<typename T1, typename T2>
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr size_t size_ref_as_ptr() { return is_lvalue_reference_v<T> ? sizeof(remove_reference_t<T>*) : sizeof(T); }
|
||||||
|
template<typename T>
|
||||||
|
constexpr size_t align_ref_as_ptr() { return is_lvalue_reference_v<T> ? alignof(remove_reference_t<T>*) : alignof(T); }
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr size_t max_size_ref_as_ptr() { return size_ref_as_ptr<T>(); }
|
||||||
|
template<typename T0, typename T1, typename... Ts>
|
||||||
|
constexpr size_t max_size_ref_as_ptr() { return size_ref_as_ptr<T0>() > size_ref_as_ptr<T1>() ? max_size_ref_as_ptr<T0, Ts...>() : max_size_ref_as_ptr<T1, Ts...>(); }
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr size_t max_align_ref_as_ptr() { return align_ref_as_ptr<T>(); }
|
||||||
|
template<typename T0, typename T1, typename... Ts>
|
||||||
|
constexpr size_t max_align_ref_as_ptr() { return align_ref_as_ptr<T0>() > align_ref_as_ptr<T1>() ? max_align_ref_as_ptr<T0, Ts...>() : max_align_ref_as_ptr<T1, Ts...>(); }
|
||||||
|
|
||||||
|
template<typename T, typename T0, typename... Ts>
|
||||||
|
constexpr size_t index()
|
||||||
|
{
|
||||||
|
if constexpr(is_same_v<T, T0>)
|
||||||
|
return 0;
|
||||||
|
else if constexpr(sizeof...(Ts) == 0)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return index<T, Ts...>() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Ts>
|
||||||
|
void destruct(size_t index, uint8_t* data)
|
||||||
|
{
|
||||||
|
if (index == 0)
|
||||||
|
{
|
||||||
|
if constexpr(!is_lvalue_reference_v<T>)
|
||||||
|
reinterpret_cast<T*>(data)->~T();
|
||||||
|
}
|
||||||
|
else if constexpr(sizeof...(Ts) > 0)
|
||||||
|
destruct<Ts...>(index - 1, data);
|
||||||
|
else
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Ts>
|
||||||
|
void move_construct(size_t index, uint8_t* source, uint8_t* target)
|
||||||
|
{
|
||||||
|
if (index == 0)
|
||||||
|
if constexpr(!is_lvalue_reference_v<T>)
|
||||||
|
new (target) T(move(*reinterpret_cast<T*>(source)));
|
||||||
|
else
|
||||||
|
memcpy(target, source, sizeof(remove_reference_t<T>*));
|
||||||
|
else if constexpr(sizeof...(Ts) > 0)
|
||||||
|
move_construct<Ts...>(index - 1, source, target);
|
||||||
|
else
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Ts>
|
||||||
|
void copy_construct(size_t index, const uint8_t* source, uint8_t* target)
|
||||||
|
{
|
||||||
|
if (index == 0)
|
||||||
|
if constexpr(!is_lvalue_reference_v<T>)
|
||||||
|
new (target) T(*reinterpret_cast<const T*>(source));
|
||||||
|
else
|
||||||
|
memcpy(target, source, sizeof(remove_reference_t<T>*));
|
||||||
|
else if constexpr(sizeof...(Ts) > 0)
|
||||||
|
copy_construct<Ts...>(index - 1, source, target);
|
||||||
|
else
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Ts>
|
||||||
|
void move_assign(size_t index, uint8_t* source, uint8_t* target)
|
||||||
|
{
|
||||||
|
if (index == 0)
|
||||||
|
if constexpr(!is_lvalue_reference_v<T>)
|
||||||
|
*reinterpret_cast<T*>(target) = move(*reinterpret_cast<T*>(source));
|
||||||
|
else
|
||||||
|
memcpy(target, source, sizeof(remove_reference_t<T>*));
|
||||||
|
else if constexpr(sizeof...(Ts) > 0)
|
||||||
|
move_assign<Ts...>(index - 1, source, target);
|
||||||
|
else
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Ts>
|
||||||
|
void copy_assign(size_t index, const uint8_t* source, uint8_t* target)
|
||||||
|
{
|
||||||
|
if (index == 0)
|
||||||
|
if constexpr(!is_lvalue_reference_v<T>)
|
||||||
|
*reinterpret_cast<T*>(target) = *reinterpret_cast<const T*>(source);
|
||||||
|
else
|
||||||
|
memcpy(target, source, sizeof(remove_reference_t<T>*));
|
||||||
|
else if constexpr(sizeof...(Ts) > 0)
|
||||||
|
copy_assign<Ts...>(index - 1, source, target);
|
||||||
|
else
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Ts>
|
||||||
|
requires (!is_const_v<Ts> && ...)
|
||||||
class Variant
|
class Variant
|
||||||
{
|
{
|
||||||
public:
|
private:
|
||||||
static_assert(!is_same_v<T1, T2>);
|
template<typename T>
|
||||||
|
static constexpr bool can_have() { return detail::index<T, Ts...>() != invalid_index(); }
|
||||||
|
static constexpr size_t invalid_index() { return sizeof...(Ts); }
|
||||||
|
|
||||||
|
public:
|
||||||
Variant() = default;
|
Variant() = default;
|
||||||
|
|
||||||
Variant(const T1& value) { set(value); }
|
Variant(Variant&& other)
|
||||||
Variant(T1&& value) { set(move(value)); }
|
: m_index(other.m_index)
|
||||||
Variant(const T2& value) { set(value); }
|
{
|
||||||
Variant(T2&& value) { set(move(value)); }
|
detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage);
|
||||||
|
other.clear();
|
||||||
|
}
|
||||||
|
|
||||||
Variant(const Variant<T1, T2>& other) { *this = other; }
|
Variant(const Variant& other)
|
||||||
Variant(Variant<T1, T2>&& other) { *this = move(other); }
|
: m_index(other.m_index)
|
||||||
|
{
|
||||||
|
detail::copy_construct<Ts...>(other.m_index, other.m_storage, m_storage);
|
||||||
|
}
|
||||||
|
|
||||||
~Variant() { clear(); }
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
Variant<T1, T2>& operator=(const Variant<T1, T2>& other);
|
template<typename T>
|
||||||
Variant<T1, T2>& operator=(Variant<T1, T2>&& other);
|
Variant(const T& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||||
|
: m_index(detail::index<T, Ts...>())
|
||||||
|
{
|
||||||
|
new (m_storage) T(value);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename U>
|
~Variant()
|
||||||
bool is() const;
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
template<typename U>
|
Variant& operator=(Variant&& other)
|
||||||
void set(U&&);
|
{
|
||||||
template<typename U>
|
if (m_index == other.m_index)
|
||||||
void set(const U& value) { set(move(U(value))); }
|
detail::move_assign<Ts...>(m_index, other.m_storage, m_storage);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage);
|
||||||
|
m_index = other.m_index;
|
||||||
|
}
|
||||||
|
other.clear();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename U>
|
Variant& operator=(const Variant& other)
|
||||||
const U& get() const;
|
{
|
||||||
template<typename U>
|
if (m_index == other.m_index)
|
||||||
U& get();
|
detail::copy_assign<Ts...>(m_index, other.m_storage, m_storage);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
detail::copy_construct<Ts...>(other.m_index, other.m_storage, m_storage);
|
||||||
|
m_index = other.m_index;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
void clear();
|
template<typename T>
|
||||||
|
Variant& operator=(T&& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||||
|
{
|
||||||
|
if (size_t index = detail::index<T, Ts...>(); index == m_index)
|
||||||
|
get<T>() = move(value);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
new (m_storage) T(move(value));
|
||||||
|
m_index = index;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Variant& operator=(const T& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||||
|
{
|
||||||
|
if (size_t index = detail::index<T, Ts...>(); index == m_index)
|
||||||
|
get<T>() = value;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
new (m_storage) T(value);
|
||||||
|
m_index = index;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool has() const requires (can_have<T>())
|
||||||
|
{
|
||||||
|
return m_index == detail::index<T, Ts...>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
void emplace(Args&&... args) requires (can_have<T>())
|
||||||
|
{
|
||||||
|
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>)
|
||||||
|
{
|
||||||
|
if (has<T>())
|
||||||
|
get<T>() = move(value);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_index = detail::index<T, Ts...>();
|
||||||
|
new (m_storage) T(move(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void set(const T& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||||
|
{
|
||||||
|
if (has<T>())
|
||||||
|
get<T>() = value;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_index = detail::index<T, Ts...>();
|
||||||
|
new (m_storage) T(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void set(T value) requires (can_have<T>() && is_lvalue_reference_v<T>)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_index = detail::index<T, Ts...>();
|
||||||
|
*reinterpret_cast<remove_reference_t<T>**>(m_storage) = &value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T& get() requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||||
|
{
|
||||||
|
ASSERT(has<T>());
|
||||||
|
return *reinterpret_cast<T*>(m_storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
const T& get() const requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||||
|
{
|
||||||
|
ASSERT(has<T>());
|
||||||
|
return *reinterpret_cast<const T*>(m_storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T get() requires (can_have<T>() && is_lvalue_reference_v<T>)
|
||||||
|
{
|
||||||
|
ASSERT(has<T>());
|
||||||
|
return **reinterpret_cast<remove_reference_t<T>**>(m_storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
const T get() const requires (can_have<T>() && is_lvalue_reference_v<T>)
|
||||||
|
{
|
||||||
|
ASSERT(has<T>());
|
||||||
|
return **reinterpret_cast<const remove_reference_t<T>**>(m_storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
if (m_index != invalid_index())
|
||||||
|
{
|
||||||
|
detail::destruct<Ts...>(m_index, m_storage);
|
||||||
|
m_index = invalid_index();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr uint32_t m_size = Math::max(sizeof(T1), sizeof(T2));
|
alignas(detail::max_align_ref_as_ptr<Ts...>()) uint8_t m_storage[detail::max_size_ref_as_ptr<Ts...>()] {};
|
||||||
uint8_t m_storage[m_size] = {};
|
size_t m_index { invalid_index() };
|
||||||
uint32_t m_index = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T1, typename T2>
|
}
|
||||||
Variant<T1, T2>& Variant<T1, T2>::operator=(const Variant<T1, T2>& other)
|
|
||||||
{
|
|
||||||
clear();
|
|
||||||
if (other.is<T1>())
|
|
||||||
set(other.get<T1>());
|
|
||||||
if (other.is<T2>())
|
|
||||||
set(other.get<T2>());
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T1, typename T2>
|
|
||||||
Variant<T1, T2>& Variant<T1, T2>::operator=(Variant<T1, T2>&& other)
|
|
||||||
{
|
|
||||||
clear();
|
|
||||||
if (other.is<T1>())
|
|
||||||
set(move(other.get<T1>()));
|
|
||||||
if (other.is<T2>())
|
|
||||||
set(move(other.get<T2>()));
|
|
||||||
other.clear();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T1, typename T2>
|
|
||||||
template<typename U>
|
|
||||||
bool Variant<T1, T2>::is() const
|
|
||||||
{
|
|
||||||
if constexpr(is_same_v<T1, U>)
|
|
||||||
return m_index == 1;
|
|
||||||
if constexpr(is_same_v<T2, U>)
|
|
||||||
return m_index == 2;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template<typename T1, typename T2>
|
|
||||||
template<typename U>
|
|
||||||
void Variant<T1, T2>::set(U&& value)
|
|
||||||
{
|
|
||||||
static_assert(is_same_v<T1, U> || is_same_v<T2, U>);
|
|
||||||
clear();
|
|
||||||
if constexpr(is_same_v<T1, U>)
|
|
||||||
{
|
|
||||||
new (m_storage) T1(move(value));
|
|
||||||
m_index = 1;
|
|
||||||
}
|
|
||||||
if constexpr(is_same_v<T2, U>)
|
|
||||||
{
|
|
||||||
new (m_storage) T2(move(value));
|
|
||||||
m_index = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T1, typename T2>
|
|
||||||
template<typename U>
|
|
||||||
const U& Variant<T1, T2>::get() const
|
|
||||||
{
|
|
||||||
static_assert(is_same_v<T1, U> || is_same_v<T2, U>);
|
|
||||||
if constexpr(is_same_v<T1, U>)
|
|
||||||
{
|
|
||||||
ASSERT(m_index == 1);
|
|
||||||
return *(T1*)m_storage;
|
|
||||||
}
|
|
||||||
if constexpr(is_same_v<T2, U>)
|
|
||||||
{
|
|
||||||
ASSERT(m_index == 2);
|
|
||||||
return *(T2*)m_storage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T1, typename T2>
|
|
||||||
template<typename U>
|
|
||||||
U& Variant<T1, T2>::get()
|
|
||||||
{
|
|
||||||
static_assert(is_same_v<T1, U> || is_same_v<T2, U>);
|
|
||||||
if constexpr(is_same_v<T1, U>)
|
|
||||||
{
|
|
||||||
ASSERT(m_index == 1);
|
|
||||||
return *(T1*)m_storage;
|
|
||||||
}
|
|
||||||
if constexpr(is_same_v<T2, U>)
|
|
||||||
{
|
|
||||||
ASSERT(m_index == 2);
|
|
||||||
return *(T2*)m_storage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T1, typename T2>
|
|
||||||
void Variant<T1, T2>::clear()
|
|
||||||
{
|
|
||||||
if (is<T1>()) ((T1*)m_storage)->~T1();
|
|
||||||
if (is<T2>()) ((T2*)m_storage)->~T2();
|
|
||||||
m_index = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
|
#include <BAN/Iterators.h>
|
||||||
#include <BAN/Math.h>
|
#include <BAN/Math.h>
|
||||||
#include <BAN/Memory.h>
|
|
||||||
#include <BAN/Move.h>
|
#include <BAN/Move.h>
|
||||||
|
#include <BAN/New.h>
|
||||||
|
#include <BAN/PlacementNew.h>
|
||||||
|
#include <BAN/Span.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
template<typename T, bool CONST>
|
|
||||||
class VectorIterator;
|
|
||||||
|
|
||||||
// T must be move assignable, move constructable (and copy constructable for some functions)
|
// T must be move assignable, move constructable (and copy constructable for some functions)
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class Vector
|
class Vector
|
||||||
|
@ -18,8 +18,8 @@ namespace BAN
|
||||||
public:
|
public:
|
||||||
using size_type = size_t;
|
using size_type = size_t;
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
using iterator = VectorIterator<T, false>;
|
using iterator = IteratorSimple<T, Vector>;
|
||||||
using const_iterator = VectorIterator<T, true>;
|
using const_iterator = ConstIteratorSimple<T, Vector>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Vector() = default;
|
Vector() = default;
|
||||||
|
@ -39,11 +39,11 @@ namespace BAN
|
||||||
ErrorOr<void> emplace(size_type, Args&&...);
|
ErrorOr<void> emplace(size_type, Args&&...);
|
||||||
ErrorOr<void> insert(size_type, T&&);
|
ErrorOr<void> insert(size_type, T&&);
|
||||||
ErrorOr<void> insert(size_type, const T&);
|
ErrorOr<void> insert(size_type, const T&);
|
||||||
|
|
||||||
iterator begin() { return iterator (m_data); }
|
iterator begin() { return iterator(m_data); }
|
||||||
const_iterator begin() const { return const_iterator(m_data); }
|
iterator end() { return iterator(m_data + m_size); }
|
||||||
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); }
|
const_iterator end() const { return const_iterator(m_data + m_size); }
|
||||||
|
|
||||||
void pop_back();
|
void pop_back();
|
||||||
void remove(size_type);
|
void remove(size_type);
|
||||||
|
@ -54,6 +54,9 @@ namespace BAN
|
||||||
|
|
||||||
bool contains(const T&) const;
|
bool contains(const T&) const;
|
||||||
|
|
||||||
|
Span<T> span() { return Span(m_data, m_size); }
|
||||||
|
const Span<T> span() const { return Span(m_data, m_size); }
|
||||||
|
|
||||||
const T& operator[](size_type) const;
|
const T& operator[](size_type) const;
|
||||||
T& operator[](size_type);
|
T& operator[](size_type);
|
||||||
|
|
||||||
|
@ -62,7 +65,8 @@ namespace BAN
|
||||||
const T& front() const;
|
const T& front() const;
|
||||||
T& front();
|
T& front();
|
||||||
|
|
||||||
ErrorOr<void> resize(size_type);
|
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> reserve(size_type);
|
ErrorOr<void> reserve(size_type);
|
||||||
ErrorOr<void> shrink_to_fit();
|
ErrorOr<void> shrink_to_fit();
|
||||||
|
|
||||||
|
@ -76,54 +80,9 @@ namespace BAN
|
||||||
private:
|
private:
|
||||||
T* m_data = nullptr;
|
T* m_data = nullptr;
|
||||||
size_type m_capacity = 0;
|
size_type m_capacity = 0;
|
||||||
size_type m_size = 0;
|
size_type m_size = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T, bool CONST>
|
|
||||||
class VectorIterator
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using value_type = T;
|
|
||||||
using data_type = maybe_const_t<CONST, T>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
VectorIterator() = default;
|
|
||||||
template<bool C>
|
|
||||||
VectorIterator(const VectorIterator<T, C>& other, enable_if_t<C == CONST || !C>)
|
|
||||||
: m_data(other.m_data)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
VectorIterator<T, CONST>& operator++() { m_data++; return *this; }
|
|
||||||
VectorIterator<T, CONST>& operator--() { m_data--; return *this; }
|
|
||||||
VectorIterator<T, CONST> operator++(int) { auto temp = *this; ++(*this); return temp; }
|
|
||||||
VectorIterator<T, CONST> operator--(int) { auto temp = *this; --(*this); return temp; }
|
|
||||||
|
|
||||||
template<bool ENABLE = !CONST>
|
|
||||||
enable_if_t<ENABLE, T&> operator*() { ASSERT(m_data); return *m_data; }
|
|
||||||
const T& operator*() const { ASSERT(m_data); return *m_data; }
|
|
||||||
|
|
||||||
template<bool ENABLE = !CONST>
|
|
||||||
enable_if_t<ENABLE, T*> operator->() { ASSERT(m_data); return m_data; }
|
|
||||||
const T* operator->() const { ASSERT(m_data); return m_data; }
|
|
||||||
|
|
||||||
bool operator==(const VectorIterator<T, CONST>& other) const { return m_data == other.m_data; }
|
|
||||||
bool operator!=(const VectorIterator<T, CONST>& other) const { return !(*this == other); }
|
|
||||||
|
|
||||||
operator bool() const { return m_data; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
VectorIterator(data_type* data) : m_data(data) { }
|
|
||||||
|
|
||||||
private:
|
|
||||||
data_type* m_data = nullptr;
|
|
||||||
|
|
||||||
friend class Vector<T>;
|
|
||||||
friend class VectorIterator<T, !CONST>;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
Vector<T>::Vector(Vector<T>&& other)
|
Vector<T>::Vector(Vector<T>&& other)
|
||||||
{
|
{
|
||||||
|
@ -179,10 +138,13 @@ namespace BAN
|
||||||
template<typename T>
|
template<typename T>
|
||||||
Vector<T>& Vector<T>::operator=(const Vector<T>& other)
|
Vector<T>& Vector<T>::operator=(const Vector<T>& other)
|
||||||
{
|
{
|
||||||
clear();
|
|
||||||
MUST(ensure_capacity(other.size()));
|
MUST(ensure_capacity(other.size()));
|
||||||
for (size_type i = 0; i < other.size(); i++)
|
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++)
|
||||||
new (m_data + i) T(other[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;
|
m_size = other.m_size;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
@ -256,7 +218,7 @@ namespace BAN
|
||||||
template<typename T>
|
template<typename T>
|
||||||
ErrorOr<void> Vector<T>::insert(size_type index, const T& value)
|
ErrorOr<void> Vector<T>::insert(size_type index, const T& value)
|
||||||
{
|
{
|
||||||
return insert(move(T(value)), index);
|
return insert(index, move(T(value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -296,7 +258,7 @@ namespace BAN
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
const T& Vector<T>::operator[](size_type index) const
|
const T& Vector<T>::operator[](size_type index) const
|
||||||
{
|
{
|
||||||
|
@ -340,7 +302,7 @@ namespace BAN
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
ErrorOr<void> Vector<T>::resize(size_type size)
|
ErrorOr<void> Vector<T>::resize(size_type size) requires is_default_constructible_v<T>
|
||||||
{
|
{
|
||||||
TRY(ensure_capacity(size));
|
TRY(ensure_capacity(size));
|
||||||
if (size < m_size)
|
if (size < m_size)
|
||||||
|
@ -353,6 +315,20 @@ namespace BAN
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ErrorOr<void> Vector<T>::resize(size_type size, const T& value) requires is_copy_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(value);
|
||||||
|
m_size = size;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
ErrorOr<void> Vector<T>::reserve(size_type size)
|
ErrorOr<void> Vector<T>::reserve(size_type size)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/RefPtr.h>
|
||||||
|
|
||||||
|
#if __is_kernel
|
||||||
|
#include <kernel/Lock/SpinLock.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class Weakable;
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
#if __is_kernel
|
||||||
|
Kernel::SpinLockGuard _(m_weak_lock);
|
||||||
|
#endif
|
||||||
|
if (m_ptr && m_ptr->try_ref())
|
||||||
|
return RefPtr<T>::adopt(m_ptr);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
bool valid() const { return m_ptr; }
|
||||||
|
void invalidate()
|
||||||
|
{
|
||||||
|
#if __is_kernel
|
||||||
|
Kernel::SpinLockGuard _(m_weak_lock);
|
||||||
|
#endif
|
||||||
|
m_ptr = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
WeakLink(T* ptr) : m_ptr(ptr) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T* m_ptr;
|
||||||
|
#if __is_kernel
|
||||||
|
Kernel::SpinLock m_weak_lock;
|
||||||
|
#endif
|
||||||
|
friend class RefPtr<WeakLink<T>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class Weakable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~Weakable()
|
||||||
|
{
|
||||||
|
if (m_link)
|
||||||
|
m_link->invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<WeakPtr<T>> get_weak_ptr() const
|
||||||
|
{
|
||||||
|
if (!m_link)
|
||||||
|
m_link = TRY(RefPtr<WeakLink<T>>::create((T*)this));
|
||||||
|
return WeakPtr<T>(m_link);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable RefPtr<WeakLink<T>> m_link;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class WeakPtr
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WeakPtr() = default;
|
||||||
|
WeakPtr(WeakPtr&& other) { *this = move(other); }
|
||||||
|
WeakPtr(const WeakPtr& other) { *this = other; }
|
||||||
|
WeakPtr(const RefPtr<T>& other) { *this = other; }
|
||||||
|
|
||||||
|
WeakPtr& operator=(WeakPtr&& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_link = move(other.m_link);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
WeakPtr& operator=(const WeakPtr& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_link = other.m_link;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
WeakPtr& operator=(const RefPtr<T>& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
if (other)
|
||||||
|
m_link = MUST(other->get_weak_ptr()).move_link();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<T> lock()
|
||||||
|
{
|
||||||
|
if (m_link)
|
||||||
|
return m_link->try_lock();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() { m_link.clear(); }
|
||||||
|
|
||||||
|
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)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
RefPtr<WeakLink<T>>&& move_link() { return move(m_link); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
RefPtr<WeakLink<T>> m_link;
|
||||||
|
|
||||||
|
friend class Weakable<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
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 ()
|
||||||
|
|
||||||
|
#add_compile_options(-mno-sse -mno-sse2)
|
||||||
|
add_compile_definitions(__enable_sse=1)
|
||||||
|
|
||||||
|
project(banan-os CXX C ASM)
|
||||||
|
|
||||||
|
set(BANAN_BASE_SYSROOT ${CMAKE_SOURCE_DIR}/base-sysroot.tar.gz)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
# include headers of ${library} to ${target}
|
||||||
|
function(banan_include_headers target library)
|
||||||
|
target_include_directories(${target} PRIVATE $<TARGET_PROPERTY:${library},SOURCE_DIR>/include)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# include headers and link ${library} to ${target}
|
||||||
|
function(banan_link_library target library)
|
||||||
|
target_link_libraries(${target} PRIVATE ${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()
|
||||||
|
|
||||||
|
add_subdirectory(kernel)
|
||||||
|
add_subdirectory(bootloader)
|
||||||
|
add_subdirectory(BAN)
|
||||||
|
add_subdirectory(userspace)
|
||||||
|
|
||||||
|
add_custom_target(sysroot
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory ${BANAN_SYSROOT}
|
||||||
|
COMMAND cd ${BANAN_SYSROOT} && tar xf ${BANAN_BASE_SYSROOT}
|
||||||
|
)
|
|
@ -0,0 +1,24 @@
|
||||||
|
BSD 2-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2023, Oskari Alaranta
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,120 @@
|
||||||
|
[![](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fbananymous.com%2Fbanan-os%2Ftokei.json&query=%24.lines&label=total%20lines)](https://git.bananymous.com/Bananymous/banan-os)
|
||||||
|
[![](https://img.shields.io/github/commit-activity/m/Bananymous/banan-os)](https://git.bananymous.com/Bananymous/banan-os)
|
||||||
|
[![](https://img.shields.io/github/license/bananymous/banan-os)](https://git.bananymous.com/Bananymous/banan-os/src/branch/main/LICENSE)
|
||||||
|
[![](https://img.shields.io/discord/1242165176032297040?logo=discord&label=discord)](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)
|
||||||
|
|
||||||
|
### 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)
|
||||||
|
- [ ] ELF dynamic linking
|
||||||
|
- [ ] Graphical desktop
|
||||||
|
- [ ] copy-on-write memory
|
||||||
|
|
||||||
|
#### Drivers
|
||||||
|
- [x] NVMe disks
|
||||||
|
- [x] ATA (IDE, SATA) disks
|
||||||
|
- [x] E1000 and E1000E NICs
|
||||||
|
- [x] PS2 keyboard (all scancode sets)
|
||||||
|
- [x] PS2 mouse
|
||||||
|
- [ ] USB
|
||||||
|
- [ ] virtio devices (network, storage)
|
||||||
|
|
||||||
|
#### Network
|
||||||
|
- [x] ARP
|
||||||
|
- [x] ICMP
|
||||||
|
- [x] IPv4
|
||||||
|
- [x] UDP
|
||||||
|
- [x] TCP (partial and buggy)
|
||||||
|
- [x] Unix domain sockets
|
||||||
|
|
||||||
|
#### 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
|
||||||
|
|
||||||
|
![screenshot from qemu running banan-os](assets/banan-os.png)
|
||||||
|
|
||||||
|
## Code structure
|
||||||
|
|
||||||
|
Each major component and library has its own subdirectory (kernel, userspace, libc, ...). Each directory contains directory *include*, which has **all** of the header files of the component. Every header is included by its absolute path.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
### Needed packages
|
||||||
|
|
||||||
|
#### 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```
|
||||||
|
|
||||||
|
#### pacman
|
||||||
|
```# pacman -S --needed base-devel git wget cmake ninja parted qemu-system-x86```
|
||||||
|
|
||||||
|
|
||||||
|
### Compilation
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
To build the os itself you can run one of the following commands. You will need root access for disk image creation/modification.
|
||||||
|
```sh
|
||||||
|
./bos qemu
|
||||||
|
./bos qemu-nographic
|
||||||
|
./bos qemu-debug
|
||||||
|
./bos bochs
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also build the kernel or disk image without running it:
|
||||||
|
```sh
|
||||||
|
./bos kernel
|
||||||
|
./bos 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*).
|
||||||
|
|
||||||
|
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.
|
||||||
|
```sh
|
||||||
|
./bos 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_.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
As the upstream is hosted on my server https://git.bananymous.com/Bananymous/banan-os, please contact me about account creation ([email](mailto:oskari.alaranta@bananymous.com), [discord](https://discord.gg/xMXKt9Wf)) and I will add a account for you. This is done to limit the people with access to the server.
|
||||||
|
|
||||||
|
As this is mostly a learning experience for me, I would appreciate if you first contacted me about adding new features (email, discord, issue, ...). 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 70 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.
|
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
@ -0,0 +1,3 @@
|
||||||
|
cmake_minimum_required(VERSION 3.26)
|
||||||
|
|
||||||
|
add_subdirectory(bios)
|
|
@ -0,0 +1,20 @@
|
||||||
|
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)
|
|
@ -0,0 +1,168 @@
|
||||||
|
.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"
|
|
@ -0,0 +1,174 @@
|
||||||
|
.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
|
|
@ -0,0 +1,87 @@
|
||||||
|
.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 - 28
|
|
@ -0,0 +1,3 @@
|
||||||
|
.set GDT_CODE16, 0x08
|
||||||
|
.set GDT_DATA32, 0x10
|
||||||
|
.set GDT_CODE32, 0x18
|
|
@ -0,0 +1,518 @@
|
||||||
|
# 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
|
|
@ -0,0 +1,320 @@
|
||||||
|
.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
|
|
@ -0,0 +1,749 @@
|
||||||
|
# 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
|
|
@ -0,0 +1,218 @@
|
||||||
|
.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
|
|
@ -0,0 +1,17 @@
|
||||||
|
ENTRY(stage1_main)
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = 0x7C00;
|
||||||
|
.stage1 : { *(.stage1) }
|
||||||
|
|
||||||
|
. = ALIGN(512);
|
||||||
|
stage2_start = .;
|
||||||
|
.stage2 : { *(.stage2) }
|
||||||
|
. = ALIGN(512);
|
||||||
|
.data : { *(.data) }
|
||||||
|
stage2_end = .;
|
||||||
|
|
||||||
|
. = ALIGN(512);
|
||||||
|
.bss : { *(.bss) }
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
.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
|
|
@ -0,0 +1,403 @@
|
||||||
|
.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
|
||||||
|
|
||||||
|
movl %ecx, %edx
|
||||||
|
|
||||||
|
andl $3, %ecx
|
||||||
|
rep stosb %es:(%edi)
|
||||||
|
|
||||||
|
movl %edx, %ecx
|
||||||
|
shrl $2, %ecx
|
||||||
|
|
||||||
|
movb %al, %ah
|
||||||
|
movw %ax, %dx
|
||||||
|
shll $16, %eax
|
||||||
|
movw %dx, %ax
|
||||||
|
rep stosl %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
|
||||||
|
|
||||||
|
movl %ecx, %edx
|
||||||
|
andl $3, %ecx
|
||||||
|
rep movsb %ds:(%esi), %es:(%edi)
|
||||||
|
|
||||||
|
movl %edx, %ecx
|
||||||
|
shrl $2, %ecx
|
||||||
|
rep movsl %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
|
|
@ -0,0 +1 @@
|
||||||
|
build/
|
|
@ -0,0 +1,22 @@
|
||||||
|
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)
|
|
@ -0,0 +1,142 @@
|
||||||
|
#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 {};
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
#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 };
|
||||||
|
};
|
|
@ -0,0 +1,250 @@
|
||||||
|
#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;
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
#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 };
|
||||||
|
};
|
|
@ -0,0 +1,74 @@
|
||||||
|
#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;
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
#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 }
|
||||||
|
};
|
|
@ -0,0 +1,80 @@
|
||||||
|
#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;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
uint32_t crc32_checksum(const uint8_t* data, std::size_t count);
|
|
@ -0,0 +1,45 @@
|
||||||
|
#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;
|
||||||
|
}
|
7
build.sh
7
build.sh
|
@ -1,7 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
set -e
|
|
||||||
. ./headers.sh
|
|
||||||
|
|
||||||
for PROJECT in $PROJECTS; do
|
|
||||||
(cd $PROJECT && DESTDIR="$SYSROOT" $MAKE install)
|
|
||||||
done
|
|
11
clean.sh
11
clean.sh
|
@ -1,11 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
set -e
|
|
||||||
. ./config.sh
|
|
||||||
|
|
||||||
for PROJECT in $PROJECTS; do
|
|
||||||
(cd $PROJECT && $MAKE clean)
|
|
||||||
done
|
|
||||||
|
|
||||||
rm -rf sysroot
|
|
||||||
rm -rf isodir
|
|
||||||
rm -rf banan-os.img
|
|
33
config.sh
33
config.sh
|
@ -1,33 +0,0 @@
|
||||||
SYSTEM_HEADER_PROJECTS="libc BAN kernel"
|
|
||||||
PROJECTS="libc BAN kernel"
|
|
||||||
|
|
||||||
export MAKE=${MAKE:-make}
|
|
||||||
export HOST=${HOST:-$(./default-host.sh)}
|
|
||||||
|
|
||||||
export AR=${HOST}-ar
|
|
||||||
export AS=${HOST}-as
|
|
||||||
export CC=${HOST}-gcc
|
|
||||||
export CXX=${HOST}-g++
|
|
||||||
|
|
||||||
export PREFIX=/usr
|
|
||||||
export EXEC_PREFIX=$PREFIX
|
|
||||||
export BOOTDIR=/boot
|
|
||||||
export LIBDIR=$EXEC_PREFIX/lib
|
|
||||||
export INCLUDEDIR=$PREFIX/include
|
|
||||||
|
|
||||||
export CFLAGS='-O2 -g'
|
|
||||||
export CPPFLAGS='--std=c++20 -Wno-literal-suffix'
|
|
||||||
|
|
||||||
export UBSAN=0
|
|
||||||
|
|
||||||
# Configure the cross-compiler to use the desired system root.
|
|
||||||
export SYSROOT="$(pwd)/sysroot"
|
|
||||||
export CC="$CC --sysroot=$SYSROOT"
|
|
||||||
export CXX="$CXX --sysroot=$SYSROOT"
|
|
||||||
|
|
||||||
# Work around that the -elf gcc targets doesn't have a system include directory
|
|
||||||
# because it was configured with --without-headers rather than --with-sysroot.
|
|
||||||
if echo "$HOST" | grep -Eq -- '-elf($|-)'; then
|
|
||||||
export CC="$CC -isystem=$INCLUDEDIR"
|
|
||||||
export CXX="$CXX -isystem=$INCLUDEDIR"
|
|
||||||
fi
|
|
|
@ -1,2 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
echo x86_64-elf
|
|
67
disk.sh
67
disk.sh
|
@ -1,67 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
set -e
|
|
||||||
. ./build.sh
|
|
||||||
|
|
||||||
DISK_NAME=banan-os.img
|
|
||||||
DISK_SIZE=$[50 * 1024 * 1024]
|
|
||||||
MOUNT_DIR=/mnt
|
|
||||||
|
|
||||||
dd if=/dev/zero of=$DISK_NAME bs=512 count=$[$DISK_SIZE / 512]
|
|
||||||
|
|
||||||
sed -e 's/\s*\([-\+[:alnum:]]*\).*/\1/' << EOF | fdisk $DISK_NAME
|
|
||||||
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
|
|
||||||
x # expert menu
|
|
||||||
n # partition name
|
|
||||||
2 # ... of partition 2
|
|
||||||
banan-root
|
|
||||||
r # back to main menu
|
|
||||||
w # write changes
|
|
||||||
EOF
|
|
||||||
|
|
||||||
LOOP_DEV=$(sudo losetup -f --show $DISK_NAME)
|
|
||||||
sudo partprobe $LOOP_DEV
|
|
||||||
|
|
||||||
PARTITION1=${LOOP_DEV}p1
|
|
||||||
PARTITION2=${LOOP_DEV}p2
|
|
||||||
|
|
||||||
sudo mkfs.ext2 $PARTITION2
|
|
||||||
|
|
||||||
sudo mount $PARTITION2 $MOUNT_DIR
|
|
||||||
|
|
||||||
sudo cp -r ${SYSROOT}/* ${MOUNT_DIR}/
|
|
||||||
sudo mkdir -p ${MOUNT_DIR}/usr/share/
|
|
||||||
sudo cp -r fonts ${MOUNT_DIR}/usr/share/
|
|
||||||
|
|
||||||
sudo grub-install --no-floppy --target=i386-pc --modules="normal ext2 multiboot" --boot-directory=${MOUNT_DIR}/boot $LOOP_DEV
|
|
||||||
|
|
||||||
echo -e '
|
|
||||||
menuentry "banan-os" {
|
|
||||||
multiboot /boot/banan-os.kernel
|
|
||||||
}
|
|
||||||
menuentry "banan-os (no serial)" {
|
|
||||||
multiboot /boot/banan-os.kernel noserial
|
|
||||||
}
|
|
||||||
menuentry "banan-os (no apic)" {
|
|
||||||
multiboot /boot/banan-os.kernel noapic
|
|
||||||
}
|
|
||||||
menuentry "banan-os (no apic, no serial)" {
|
|
||||||
multiboot /boot/banan-os.kernel noapic noserial
|
|
||||||
}
|
|
||||||
' | sudo tee ${MOUNT_DIR}/boot/grub/grub.cfg
|
|
||||||
|
|
||||||
sudo umount $MOUNT_DIR
|
|
||||||
sudo losetup -d $LOOP_DEV
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,9 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
set -e
|
|
||||||
. ./config.sh
|
|
||||||
|
|
||||||
mkdir -p "$SYSROOT"
|
|
||||||
|
|
||||||
for PROJECT in $SYSTEM_HEADER_PROJECTS; do
|
|
||||||
(cd $PROJECT && DESTDIR="$SYSROOT" $MAKE install-headers)
|
|
||||||
done
|
|
|
@ -1,8 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
set -e
|
|
||||||
. ./disk.sh
|
|
||||||
|
|
||||||
SIZE=$(stat -c '%s' banan-os.img | numfmt --to=iec)
|
|
||||||
|
|
||||||
echo Writing ${SIZE}iB
|
|
||||||
sudo dd if=banan-os.img of=/dev/sda status=progress
|
|
|
@ -1,3 +0,0 @@
|
||||||
*.d
|
|
||||||
*.kernel
|
|
||||||
*.o
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue