Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d5c2380ed | ||
|
|
a8b5c3ff4e | ||
|
|
16587aeb93 | ||
|
|
629a3a3dc1 | ||
|
|
ea46379d24 | ||
|
|
ee0c77edc8 | ||
|
|
c0610b2d60 | ||
|
|
13ab545246 | ||
|
|
d85d6bcb13 | ||
|
|
138662d6a0 | ||
|
|
e45437e19f | ||
|
|
725846e155 | ||
|
|
74aa514903 | ||
|
|
7f7230be90 | ||
|
|
d4a6e8c47a | ||
|
|
9c19ed4471 | ||
|
|
357c37c69d | ||
|
|
7c2d9c8643 | ||
|
|
69d699b80e | ||
|
|
40832dd727 | ||
|
|
31c3baa42b | ||
|
|
0ed03224c2 | ||
|
|
f8d82ae74a | ||
|
|
16234bde82 | ||
|
|
31a5d7ee3b | ||
|
|
f908c7e04d | ||
|
|
eb20f66bd9 | ||
|
|
3b3e853cf8 | ||
|
|
998ae05a5b | ||
|
|
155d01b8c3 | ||
|
|
1a4cce7efd | ||
|
|
3a0dd2ffe7 | ||
|
|
68b705b16a | ||
|
|
fe245e76cf | ||
|
|
618e37b07c | ||
|
|
3dde8d45c1 | ||
|
|
4691f95b73 | ||
|
|
02d4bf0e09 | ||
|
|
5ecf4f37c0 | ||
|
|
15f6484dd2 | ||
|
|
2ead3905e5 | ||
|
|
99d883253c | ||
|
|
ed4d4f9f23 | ||
|
|
d76f9565b2 | ||
|
|
1c637f1182 | ||
|
|
4dbe5de79c | ||
|
|
afcb88d04c | ||
|
|
1ea1deef94 | ||
|
|
08d2af22db | ||
|
|
70179bb1d7 | ||
|
|
83226c5ce8 | ||
|
|
3d6b737c46 | ||
|
|
fff511101c | ||
|
|
6672becdd5 | ||
|
|
bd315c7e43 | ||
|
|
fbfe28c087 | ||
|
|
adbafc0b02 | ||
|
|
4e6dad939a | ||
|
|
12bf9c0f59 | ||
|
|
f7f7d7bc38 | ||
|
|
c169c05bba | ||
|
|
19d48740cd | ||
|
|
7a067a4b90 | ||
|
|
13f3dab23a | ||
|
|
fd9d308b28 | ||
|
|
1047d842d0 | ||
|
|
77385e53f7 | ||
|
|
f7b3de2750 | ||
|
|
a65e6de66b | ||
|
|
41cf523dfc | ||
|
|
3774f8fd11 | ||
|
|
b9e26b1c2c | ||
|
|
609175713b | ||
|
|
d56b794177 | ||
|
|
608a929fcb | ||
|
|
4bf045e3d6 | ||
|
|
6b5434d64f | ||
|
|
abb5480890 | ||
|
|
95069b0d4c | ||
|
|
8f15b50b5f | ||
|
|
c7a1505f9b | ||
|
|
ff098c91f4 | ||
|
|
fb40a4f904 | ||
|
|
c87696f5c1 | ||
|
|
4f3dad4bea | ||
|
|
46c2e33096 | ||
|
|
66ee266b90 | ||
|
|
14883b1e26 | ||
|
|
33f74988b1 | ||
|
|
78b157ff7f | ||
|
|
546b756932 | ||
|
|
5b74d54c64 | ||
|
|
fc608b21ee | ||
|
|
2b6a266b93 | ||
|
|
7cb5092010 | ||
|
|
a1c3681002 | ||
|
|
91f35aa3bb | ||
|
|
49a6715dfe | ||
|
|
b799725bef | ||
|
|
1ada22462f | ||
|
|
b0ec9680a1 | ||
|
|
7a8a6b1bb1 | ||
|
|
1380a91bf1 | ||
|
|
83208f74ec | ||
|
|
5f571418d0 | ||
|
|
a7d8ff208f | ||
|
|
1046f3e220 | ||
|
|
6406f97f56 | ||
|
|
e0c456490a | ||
|
|
6a1b2a3964 | ||
|
|
3452b72add | ||
|
|
2e3b312fab | ||
|
|
8f67ca339b | ||
|
|
1679b7b5cf | ||
|
|
757049543d | ||
|
|
38b098b547 | ||
|
|
60478e3f61 | ||
|
|
6325f01122 | ||
|
|
74c12e6ea2 | ||
|
|
fd3453bb2a | ||
|
|
2ff7631c9c | ||
|
|
7a6b0ab641 | ||
|
|
8fd0003548 | ||
|
|
bb8de993b6 | ||
|
|
7c83d733cd | ||
|
|
6f49a1905c | ||
|
|
d0378aadeb | ||
|
|
4834318742 | ||
|
|
1838a490ba | ||
|
|
de3cbe45b4 | ||
|
|
a34cb6c89d | ||
|
|
9c0724ddb9 | ||
|
|
ce948bd9ce | ||
|
|
5eb5d2420e | ||
|
|
7510e36f6b | ||
|
|
8bfdf2bd5f | ||
|
|
2174c28aa2 | ||
|
|
7a55667a5d | ||
|
|
79d0452427 | ||
|
|
ad5c141a7c | ||
|
|
d4cd327ea3 | ||
|
|
27b29d5ffe | ||
|
|
b52222cad9 | ||
|
|
822c27399c | ||
|
|
0e717389c1 | ||
|
|
2197c4ce81 | ||
|
|
b894d64063 | ||
|
|
d95a11365a | ||
|
|
dbcade3087 | ||
|
|
7ac54c0eb8 | ||
|
|
91c74b863d | ||
|
|
8c0b6f1e9d | ||
|
|
539f388e66 | ||
|
|
b03c312b4e | ||
|
|
71db5f9a79 | ||
|
|
e33e77595f | ||
|
|
d4016b310d | ||
|
|
a41177a110 | ||
|
|
f8600296f0 | ||
|
|
e1147fb33d | ||
|
|
ec0d020669 | ||
|
|
e8eab86546 | ||
|
|
c9ebedb01f | ||
|
|
af5742ce2d | ||
|
|
8f85c71654 | ||
|
|
70753c1082 | ||
|
|
2ab2b9efd6 | ||
|
|
dbb34b75fd | ||
|
|
7447492c76 | ||
|
|
492e4c33db | ||
|
|
cbb08bdcfc | ||
|
|
0de829741a | ||
|
|
0fa7aed1c4 | ||
|
|
3b0e940284 | ||
|
|
48a970ceb9 | ||
|
|
c7b533f21e | ||
|
|
839d4da191 | ||
|
|
b76ddfe93c | ||
|
|
4ba0156ad8 | ||
|
|
6ef9936e24 | ||
|
|
e835dbc7c6 | ||
|
|
5c8429a7bb | ||
|
|
824011657a | ||
|
|
8e2031bf5a | ||
|
|
8bb5f5475e | ||
|
|
eb4f249a54 | ||
|
|
3aa3b87709 | ||
|
|
0046157862 | ||
|
|
acbf1efad6 | ||
|
|
5d1eb45d5c | ||
|
|
c52c41c885 | ||
|
|
822f9aacf6 | ||
|
|
7d5608ca0e | ||
|
|
937bbef818 | ||
|
|
c506443ed0 | ||
|
|
5f42f26587 | ||
|
|
f84e96d790 | ||
|
|
ede57a20be | ||
|
|
df8306c721 | ||
|
|
1bb144e256 | ||
|
|
7d83603646 | ||
|
|
7e4d51bab0 | ||
|
|
4ef1b079cb | ||
|
|
e95d13adb1 | ||
|
|
92d7009804 | ||
|
|
354327e40b | ||
|
|
34d56c54c8 | ||
|
|
143a5fdf4f | ||
|
|
030510613c | ||
|
|
984b8e7da0 | ||
|
|
aba1ac1646 | ||
|
|
6b93d7644a | ||
|
|
68c737dcef | ||
|
|
b8f8ffe0a2 | ||
|
|
02e8681f76 | ||
|
|
603fb9db62 | ||
|
|
b01459d1ef | ||
|
|
667f1f5951 | ||
|
|
660c5a2db4 | ||
|
|
8a4d013354 | ||
|
|
faedca81d1 | ||
|
|
166d408f5e | ||
|
|
017f25d9d5 | ||
|
|
6a70eba83f | ||
|
|
ef13ab6938 | ||
|
|
75cb61fd36 | ||
|
|
9c31f8aa92 | ||
|
|
27a08da430 | ||
|
|
fdada9661f | ||
|
|
f1a52439bb | ||
|
|
d13849c173 | ||
|
|
d8a0e73899 | ||
|
|
b6a3387f8a | ||
|
|
04dd708941 | ||
|
|
39ffe1c95a | ||
|
|
7b5ae3c15b | ||
|
|
7678625071 | ||
|
|
dbe8817d95 | ||
|
|
09e117cd57 | ||
|
|
4b8779d8d3 | ||
|
|
79158dabbc | ||
|
|
f962bc8da5 | ||
|
|
28e8dde06d | ||
|
|
6c3d41a124 | ||
|
|
9c2967f8df | ||
|
|
99311364e7 | ||
|
|
16e86b8291 | ||
|
|
5741af8fd3 | ||
|
|
6bdd47a557 | ||
|
|
750ba93c14 | ||
|
|
18be930507 | ||
|
|
be26176828 | ||
|
|
540e01f1fe | ||
|
|
7cfb6b16e4 | ||
|
|
d5a8b9648f | ||
|
|
1657d7af7d | ||
|
|
318f1a2818 | ||
|
|
8b0b115709 | ||
|
|
7d2494b7f9 | ||
|
|
3b642ffeb3 | ||
|
|
815bf5f0e0 | ||
|
|
4af48724ce | ||
|
|
001272820c | ||
|
|
4aca0664ef | ||
|
|
6721a86768 | ||
|
|
5025b3690d | ||
|
|
42b37a718f | ||
|
|
fcdfd836ec | ||
|
|
b2f005239b | ||
|
|
bc3fa890bd | ||
|
|
95dbbf6e75 | ||
|
|
22f2ae7341 | ||
|
|
96202aa78f | ||
|
|
cef8a13cc2 | ||
|
|
54271231a0 | ||
|
|
d1bb651596 | ||
|
|
10b7f65437 | ||
|
|
3a70324d43 | ||
|
|
21b72cd174 | ||
|
|
ce8656f1b2 | ||
|
|
9277dc967b | ||
|
|
67b1e65d97 | ||
|
|
79fe9da2da | ||
|
|
3164e2698d | ||
|
|
ffae33e6f7 | ||
|
|
8dafe3b7a6 | ||
|
|
e23616169b | ||
|
|
9d7fc942f7 | ||
|
|
982d8cf19d | ||
|
|
aae460623d | ||
|
|
df14e5bcb6 | ||
|
|
15d5a83c16 | ||
|
|
4b0f7543ee | ||
|
|
362c64aa9c | ||
|
|
56c497cf2c | ||
|
|
e0dc2da80a | ||
|
|
41362f12ea | ||
|
|
3765c0b0fb | ||
|
|
d1f2b62f66 | ||
|
|
26b8c1d0a2 | ||
|
|
aa195f9f6f | ||
|
|
cd1d59264e | ||
|
|
923e009fd3 | ||
|
|
70a4fd99f4 | ||
|
|
feb4011f57 | ||
|
|
bf2a04ff66 | ||
|
|
e16d352e71 | ||
|
|
817724ce39 | ||
|
|
0fda117ffe | ||
|
|
e5be1e1f05 | ||
|
|
7d7af8cf59 | ||
|
|
cbc09f83d4 | ||
|
|
31913ae80e | ||
|
|
522632bea6 | ||
|
|
397f75a4db | ||
|
|
0bf84a321b | ||
|
|
5915208dee | ||
|
|
2167931e3f | ||
|
|
2d18171914 | ||
|
|
28f6a7d399 | ||
|
|
8e042435e5 | ||
|
|
cddf8712e4 | ||
|
|
88fd96e022 | ||
|
|
17e715f408 | ||
|
|
c821d9ea65 | ||
|
|
5cb8a9ddd0 | ||
|
|
f0131b2c39 | ||
|
|
bc3c68a3cb | ||
|
|
b81d387778 | ||
|
|
0afbe72672 | ||
|
|
5fc63485f3 | ||
|
|
1bc84c98f8 | ||
|
|
737af45c85 | ||
|
|
e08fbc7d86 | ||
|
|
91090d20de | ||
|
|
05a22e20b8 | ||
|
|
7c43f343a3 | ||
|
|
029e6aa251 | ||
|
|
2f26d4ffd3 | ||
|
|
d306bae035 | ||
|
|
34dc8b32cb | ||
|
|
fe38244d7c | ||
|
|
25e8c9f637 | ||
|
|
337b568b5e | ||
|
|
e696192ef7 | ||
|
|
928fc077b8 | ||
|
|
2a448b932c | ||
|
|
d40d23a50e | ||
|
|
c4548c4355 | ||
|
|
50fdea813b | ||
|
|
0ff163e541 | ||
|
|
38cd8847a2 | ||
|
|
e62ae9fb94 | ||
|
|
187b6cf072 | ||
|
|
a3fad5966a | ||
|
|
74350f3638 | ||
|
|
ee2c7caac2 | ||
|
|
c915a6912e | ||
|
|
d5cd232441 | ||
|
|
c4c14b0fc6 | ||
|
|
e5f5563249 | ||
|
|
2a6893efb4 | ||
|
|
9fd920945a | ||
|
|
a116daa7a1 | ||
|
|
0b8759380b | ||
|
|
00c5e3544f | ||
|
|
53a79e3170 | ||
|
|
5810bf9acd | ||
|
|
7815feddba | ||
|
|
f529a5eea8 | ||
|
|
40e9550b29 | ||
|
|
64d0202f95 | ||
|
|
da9483aa3a | ||
|
|
0221e555fe | ||
|
|
494ee2c099 | ||
|
|
dc92ad9c02 | ||
|
|
8a4a019973 | ||
|
|
a1a49ebf4b | ||
|
|
45263a6261 | ||
|
|
7ccede36b1 | ||
|
|
482e1631fa | ||
|
|
d0fe7f3e25 | ||
|
|
86083e3a92 | ||
|
|
281f567d97 | ||
|
|
7f887720cc | ||
|
|
717268add9 | ||
|
|
2f7e3b1596 | ||
|
|
1a3cd2f626 | ||
|
|
2fbea43a5c | ||
|
|
70788e932e | ||
|
|
949d12052e | ||
|
|
2af35673bb | ||
|
|
4a64ca58ac | ||
|
|
815c594fa6 | ||
|
|
fc4dba4569 | ||
|
|
1d8df98339 | ||
|
|
745aabcfd5 | ||
|
|
be100790f1 | ||
|
|
afc5e82906 | ||
|
|
2951d4b7fa | ||
|
|
dcd300211f | ||
|
|
f796d27bb4 | ||
|
|
47a2553356 | ||
|
|
67bf199254 | ||
|
|
4b1ed25ef3 | ||
|
|
a5ac43e5f3 | ||
|
|
0557527bfe | ||
|
|
dc1445e0d6 | ||
|
|
431401bc96 | ||
|
|
fded6c404a | ||
|
|
680a850618 | ||
|
|
7d08a7ba1a | ||
|
|
e9f603fa30 | ||
|
|
1a064e033b | ||
|
|
ebcfcf9bfa | ||
|
|
cf063b75ff | ||
|
|
951a9d7989 | ||
|
|
37191a53c3 | ||
|
|
42d420db6a | ||
|
|
122cf63a3f | ||
|
|
fafce60f9e | ||
|
|
e855f27c8c | ||
|
|
f01a593ff7 | ||
|
|
6af34b1c16 | ||
|
|
188b329eae | ||
|
|
7b0f71cc99 | ||
|
|
73f8baf464 | ||
|
|
3a09efde91 | ||
|
|
4efa8c1ef9 | ||
|
|
9f178208f2 | ||
|
|
0f2113ee12 | ||
|
|
139e003b0b | ||
|
|
1bf80cf315 | ||
|
|
bcf182f515 | ||
|
|
c34fb94e77 | ||
|
|
64080fadd3 | ||
|
|
1009cd6ed4 | ||
|
|
839c1af124 | ||
|
|
6b24842704 | ||
|
|
2ad8ae3451 | ||
|
|
b1597f6f61 | ||
|
|
47a49263ae | ||
|
|
9fed336c9e | ||
|
|
ac5b689950 | ||
|
|
3d43bb260a | ||
|
|
2eaeb26d4c | ||
|
|
34d0f8ec81 | ||
|
|
ddc1c7e98f | ||
|
|
e41475f672 | ||
|
|
e954622925 | ||
|
|
fae899e52e | ||
|
|
7b11f5afbc | ||
|
|
40c0bf79b1 | ||
|
|
df2df593b6 | ||
|
|
1dc59d610d | ||
|
|
8fc8decd09 | ||
|
|
08f44845ce | ||
|
|
4fbffc4788 | ||
|
|
8cf4050d3d | ||
|
|
31c18fc56d | ||
|
|
005207cfd6 | ||
|
|
c8d668ad96 | ||
|
|
a39c4ba9a4 | ||
|
|
f4deea05bf | ||
|
|
1c18133603 | ||
|
|
15566b4b65 | ||
|
|
737185752c | ||
|
|
a12e3534c6 | ||
|
|
51575b341b | ||
|
|
06b330ec51 | ||
|
|
e0a6bce5b0 | ||
|
|
da07a5b613 | ||
|
|
fbbca37b55 | ||
|
|
7fa1c2264e | ||
|
|
dcfbe53713 | ||
|
|
8b2993c979 | ||
|
|
131da660c8 | ||
|
|
f3de34a62b | ||
|
|
928d6bafcb | ||
|
|
704fb0cbd9 | ||
|
|
3be27aefa3 | ||
|
|
8be81ac09e | ||
|
|
e2fc9c3699 | ||
|
|
472eaff6f0 | ||
|
|
805b016aee | ||
|
|
624a13ba1a | ||
|
|
9692bf3991 | ||
|
|
3f5406f1d4 | ||
|
|
b684a62f8b | ||
|
|
6bb94db22f | ||
|
|
d70a971d2e | ||
|
|
b05b515f3d | ||
|
|
12b15e0037 | ||
|
|
2c9169308b | ||
|
|
74a3d50c88 | ||
|
|
6b8bb33221 | ||
|
|
175fb46508 | ||
|
|
2f65400641 | ||
|
|
b5f4f2fc4b | ||
|
|
8c34f44dae | ||
|
|
d97f46133b | ||
|
|
a94a5feb84 | ||
|
|
ead5d7511a | ||
|
|
1cd7446013 | ||
|
|
40747a463b | ||
|
|
8e20118ab6 | ||
|
|
19f330e670 | ||
|
|
20d549b5e1 | ||
|
|
e09fa89781 | ||
|
|
70b1421114 | ||
|
|
b80a30785a | ||
|
|
c3245ac301 | ||
|
|
085d5b411c | ||
|
|
8c26d6d0b5 | ||
|
|
76f5085e84 | ||
|
|
80d78e3270 | ||
|
|
654521b74c | ||
|
|
353d2d4cc9 | ||
|
|
b38d84419a | ||
|
|
0d085771e4 | ||
|
|
f3b2b6b41e | ||
|
|
fb35ad7ab3 | ||
|
|
cf6e721994 | ||
|
|
9faea5ff15 | ||
|
|
fe18009bc0 | ||
|
|
10f900ea3e | ||
|
|
63874694f8 | ||
|
|
9b9e246e66 | ||
|
|
e6a6eb9be5 | ||
|
|
050bb5dbfa | ||
|
|
b005ec1845 | ||
|
|
f158a94e0a | ||
|
|
a166620a6d | ||
|
|
47fb467878 | ||
|
|
67af80c772 | ||
|
|
1f44155127 | ||
|
|
87a3ac4a76 | ||
|
|
73354a04d5 | ||
|
|
cb3038e73c | ||
|
|
b6ec31fe11 | ||
|
|
c7059a60ee | ||
|
|
af49aaf8b1 | ||
|
|
4149b8388b | ||
|
|
df0d723714 | ||
|
|
44209ed93e | ||
|
|
930a3c8637 | ||
|
|
d2f5698397 | ||
|
|
42d9fc3284 | ||
|
|
91db494898 | ||
|
|
75217d4914 | ||
|
|
ceaf004719 | ||
|
|
27b7380433 | ||
|
|
7ca254ade9 | ||
|
|
c91864c2a7 | ||
|
|
4925297a35 | ||
|
|
d7ece50ed0 | ||
|
|
3ef4a9f413 | ||
|
|
9768b5b37a | ||
|
|
e4cd999bd0 | ||
|
|
8e335535cc | ||
|
|
2f53d98806 | ||
|
|
5d99331358 | ||
|
|
316022cde4 | ||
|
|
8d1abca8de | ||
|
|
1b2e73eaa2 | ||
|
|
6127371920 | ||
|
|
7936dbbf8c | ||
|
|
8aa45309a2 | ||
|
|
d75237ba66 | ||
|
|
9909877771 | ||
|
|
64ef1f3aba | ||
|
|
d670e1a6bb | ||
|
|
7082a4dc24 | ||
|
|
e6c9314c91 | ||
|
|
3dccd4484e | ||
|
|
170ed98701 | ||
|
|
54fa2c8635 | ||
|
|
bc83fe43e5 | ||
|
|
18710567f2 | ||
|
|
9a98ed5cb6 | ||
|
|
35a98fd3b5 | ||
|
|
cd74fdf234 | ||
|
|
6cccbfc73a | ||
|
|
e59308a9a3 | ||
|
|
85a3be4ae3 | ||
|
|
d873693fe7 | ||
|
|
2df967d5c2 | ||
|
|
20989a7ab5 | ||
|
|
8e114d6fe4 | ||
|
|
479ad2d4ba | ||
|
|
d05bb49f5d | ||
|
|
fc9755379d | ||
|
|
a85db0c383 | ||
|
|
d19ec5bf49 | ||
|
|
ba72e4ebbf | ||
|
|
5936e25ce4 | ||
|
|
d82dc196e0 | ||
|
|
c174b368e8 | ||
|
|
81e3515f40 | ||
|
|
bffec6206b | ||
|
|
d269c23948 | ||
|
|
f2b2335080 | ||
|
|
5dded0d9aa | ||
|
|
33142c3a0d | ||
|
|
06e256f77a | ||
|
|
3f13cca666 | ||
|
|
73117708e5 | ||
|
|
f88b53dce5 | ||
|
|
3e609bab04 | ||
|
|
a6bf530c5f | ||
|
|
5d003b7678 | ||
|
|
a5cbc3f34a | ||
|
|
52e92e1ebf | ||
|
|
5307b6c097 | ||
|
|
dd4f4761de | ||
|
|
96f3533e46 | ||
|
|
774e1720b7 | ||
|
|
83f8eadce9 | ||
|
|
f8e75761ef | ||
|
|
cbb881df86 | ||
|
|
103e5304e8 | ||
|
|
e152ff0377 | ||
|
|
65f39ab22e | ||
|
|
df51c6dc64 | ||
|
|
b7c86ee01e | ||
|
|
992bb6a73a | ||
|
|
557dd3e0d5 | ||
|
|
c8d5b5440f | ||
|
|
0900e72070 | ||
|
|
dcdc64f974 | ||
|
|
58898adbcd | ||
|
|
ff2050466b | ||
|
|
dd209c05b9 | ||
|
|
12a4806b85 | ||
|
|
82575962ed | ||
|
|
1db2813aa3 | ||
|
|
c71613dcfe | ||
|
|
762d8a8e74 | ||
|
|
b7966f6b61 | ||
|
|
b1443533e0 | ||
|
|
6f12ac1d19 | ||
|
|
0295eea145 | ||
|
|
171402f99f | ||
|
|
3d57d35536 | ||
|
|
bacf2b9eec | ||
|
|
fd885bdb19 | ||
|
|
a497c07b0b | ||
|
|
ffd5f23406 | ||
|
|
97e3cc35c2 | ||
|
|
44d8363292 | ||
|
|
e540a95ad0 | ||
|
|
db63b0d515 | ||
|
|
7637c2fb58 | ||
|
|
5b40b43435 | ||
|
|
6ec277ec99 | ||
|
|
1befa63c9b | ||
|
|
3d3e052f23 | ||
|
|
aea816dcaf | ||
|
|
d258fb3eff | ||
|
|
1463d83b9f | ||
|
|
10c947d9d4 | ||
|
|
2da51ea6e9 | ||
|
|
d930c0a4ad | ||
|
|
d0e7cc41bf | ||
|
|
8c610adee5 | ||
|
|
a1e791d1e9 | ||
|
|
264249fb8b | ||
|
|
e2b00d7c73 | ||
|
|
ff3964e2eb | ||
|
|
2ed95bfe16 | ||
|
|
958936319f | ||
|
|
39fca33958 | ||
|
|
eb4cbe863e | ||
|
|
d146ea6a63 | ||
|
|
f4b2ba8322 | ||
|
|
acb1aa8104 | ||
|
|
03f7a75e54 | ||
|
|
2dc71cc5fd | ||
|
|
4a92419bbe | ||
|
|
92699501f6 | ||
|
|
b49ed97be0 | ||
|
|
aec661edea | ||
|
|
675fa2cf85 | ||
|
|
b442d72ef7 | ||
|
|
2ff197c670 | ||
|
|
5c9a610823 | ||
|
|
8d871256ed | ||
|
|
3d59fb0d9a | ||
|
|
057d783158 | ||
|
|
ffb8a2a00a | ||
|
|
b32b93340c | ||
|
|
35a3f7c02a | ||
|
|
6de4c4092f | ||
|
|
c5de5e07d8 | ||
|
|
0176071011 | ||
|
|
3f2a641cdf | ||
|
|
20be2d8850 | ||
|
|
b37cd7dba7 | ||
|
|
e274a49ffd | ||
|
|
cba37abfae | ||
|
|
be198a7040 | ||
|
|
29d181c113 | ||
|
|
3d5914f389 | ||
|
|
c78c75d89a | ||
|
|
a4753be5ae | ||
|
|
3bda360196 | ||
|
|
2b4de98aee | ||
|
|
bbd887fd2c | ||
|
|
84a5add410 | ||
|
|
c999563ecd | ||
|
|
80c7cdbdd2 | ||
|
|
9c7b453732 | ||
|
|
45982c4b01 | ||
|
|
438704be37 | ||
|
|
1753ddc4d6 | ||
|
|
96178194b3 | ||
|
|
57fb2192be | ||
|
|
546a78be33 | ||
|
|
29acb67566 | ||
|
|
0afad9a825 | ||
|
|
c99b3de902 | ||
|
|
e8cf1d6b9a | ||
|
|
a9aa1e13ee | ||
|
|
f5857c8aab | ||
|
|
67856a98af | ||
|
|
c71de15cb0 | ||
|
|
73f15c1980 | ||
|
|
646df4f34e | ||
|
|
8489d0df97 | ||
|
|
86dfad860e | ||
|
|
c97a68029b | ||
|
|
08a4dfbe4c | ||
|
|
adcacf3cf9 | ||
|
|
7950b69136 | ||
|
|
5d75416948 | ||
|
|
2e96a00fde | ||
|
|
1f08bcf0f8 | ||
|
|
a3e2e75021 | ||
|
|
603bdd2941 | ||
|
|
6fb7a4f24b | ||
|
|
0c2d290835 | ||
|
|
210e05f7fc | ||
|
|
f4b443c33e | ||
|
|
52ffa09f73 | ||
|
|
52bb23bb6f | ||
|
|
2089af8fd3 | ||
|
|
778bc4072b | ||
|
|
2b2ebecae7 | ||
|
|
69cbd61915 | ||
|
|
4d41774345 | ||
|
|
ff6772aaa7 | ||
|
|
bbe9d43532 | ||
|
|
6549bea3e0 | ||
|
|
9d727589aa | ||
|
|
aede0547d3 | ||
|
|
55332d61fd | ||
|
|
83eeb5d6d0 | ||
|
|
5eaf796aab | ||
|
|
d46c40d3fa | ||
|
|
2461afa573 | ||
|
|
6acacf5efe | ||
|
|
075a74ea0c | ||
|
|
f9ddc43349 | ||
|
|
1c9a232a9d | ||
|
|
9c727a8d8c | ||
|
|
7c241c65de | ||
|
|
4f3823bfeb | ||
|
|
3f7b16c736 | ||
|
|
362fbb39d4 | ||
|
|
38fe24085f | ||
|
|
af3d5cb273 | ||
|
|
9200b742c5 | ||
|
|
f39c8ebaba | ||
|
|
bbb1831c5f | ||
|
|
ecae3d0e1e |
No files matched your search
@@ -7,6 +7,8 @@
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
depend
|
||||
*.depend
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
@@ -31,3 +33,7 @@
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
|
||||
# Others
|
||||
other/
|
||||
a*.dat
|
||||
@@ -1,6 +1,6 @@
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Patrick Pelissier
|
||||
Copyright (c) 2017-2018, Patrick Pelissier
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
||||
@@ -1,6 +1,26 @@
|
||||
PREFIX=/usr/local
|
||||
# Environnement variable. To customize if needed.
|
||||
RM=rm -f
|
||||
MKDIR=mkdir -p
|
||||
INSTALL=install
|
||||
INSTALL_PROGRAM=${INSTALL}
|
||||
INSTALL_DATA=${INSTALL} -m 644
|
||||
|
||||
# Installation directory
|
||||
PREFIX=/usr/local
|
||||
|
||||
# Package name & version.
|
||||
PACKAGE=m*lib-$(VERSION)
|
||||
VERSION=0.2.0
|
||||
|
||||
# Define the contain of the distribution tarball
|
||||
# TODO: Get theses lists from GIT itself.
|
||||
HEADER=m-algo.h m-array.h m-atomic.h m-bitset.h m-bptree.h m-buffer.h m-core.h m-deque.h m-dict.h m-genint.h m-i-list.h m-i-shared.h m-list.h m-mempool.h m-mutex.h m-prioqueue.h m-rbtree.h m-shared.h m-snapshot.h m-string.h m-tuple.h m-variant.h m-worker.h
|
||||
DOC1=LICENSE README.md
|
||||
DOC2=doc/API.txt doc/Container.html doc/Container.ods doc/DEV.md doc/ISSUES.org doc/depend.png doc/oplist.png
|
||||
EXAMPLE=example/ex-array01.c example/ex-array04.c example/ex-dict02.c example/ex-grep01.c example/ex-multi01.c example/ex-rbtree01.c example/ex-array02.c example/ex-buffer01.c example/ex-dict03.c example/ex-list01.c example/ex-multi02.c example/Makefile example/ex-array03.c example/ex-dict01.c example/ex-dict04.c example/ex-mph.c example/ex-multi03.c
|
||||
TEST=tests/coverage.h tests/test-malgo.c tests/test-mbptree.c tests/test-mdeque.c tests/test-milist.c tests/test-mmutex.c tests/test-mshared.c tests/test-mtuple.c tests/test-obj.h tests/tgen-mdict.c tests/tgen-openmp.c tests/wip-mregister.c tests/dict.txt tests/test-marray.c tests/test-mbuffer.c tests/test-mdict.c tests/test-mlist.c tests/test-mprioqueue.c tests/test-msnapshot.c tests/test-mvariant.c tests/tgen-bitset.c tests/tgen-mlist.c tests/tgen-queue.c tests/Makefile tests/test-mbitset.c tests/test-mcore.c tests/test-mgenint.c tests/test-mmempool.c tests/test-mrbtree.c tests/test-mstring.c tests/test-mworker.c tests/tgen-marray.c tests/tgen-mstring.c tests/tgen-shared.c
|
||||
|
||||
.PHONY: all test check doc clean distclean depend install uninstall dist
|
||||
|
||||
all:
|
||||
@echo "Nothing to be done."
|
||||
@@ -11,21 +31,47 @@ check:
|
||||
cd tests && $(MAKE) check
|
||||
cd example && $(MAKE) all
|
||||
|
||||
doc: README.md
|
||||
html: doc
|
||||
doc: README.md doc/depend.png
|
||||
markdown < README.md > README.html
|
||||
|
||||
clean:
|
||||
cd tests && $(MAKE) clean
|
||||
cd example && $(MAKE) clean
|
||||
cd bench && $(MAKE) clean
|
||||
$(RM) -f README.html
|
||||
$(RM) README.html depend.dot
|
||||
$(RM) -r 'm*lib-$(VERSION)'
|
||||
$(RM) 'm*lib-$(VERSION).tar.bz2'
|
||||
|
||||
distclean:
|
||||
distclean: clean
|
||||
cd tests && $(MAKE) distclean
|
||||
|
||||
depend:
|
||||
cd tests && $(MAKE) depend
|
||||
|
||||
doc/depend.png: $(HEADER)
|
||||
(echo "digraph g { " ; for i in *.h ; do list=$$(grep "include \"" $$i |cut -f2 -d\") ; for j in $$list ; do echo "\"$$i\" -> \"$$j\" ;" ; done ; done ; echo "}" )> depend.dot
|
||||
dot -Tpng depend.dot -o doc/depend.png
|
||||
optipng -o7 doc/depend.png
|
||||
|
||||
dist: $(HEADER) $(DOC1) $(DOC2) $(EXAMPLE) $(TEST) Makefile clean
|
||||
$(MKDIR) '$(PACKAGE)'
|
||||
$(MKDIR) '$(PACKAGE)/doc'
|
||||
$(MKDIR) '$(PACKAGE)/tests'
|
||||
$(MKDIR) '$(PACKAGE)/example'
|
||||
cp $(HEADER) $(DOC1) Makefile '$(PACKAGE)'
|
||||
cp $(EXAMPLE) '$(PACKAGE)/example'
|
||||
cp $(TEST) '$(PACKAGE)/tests'
|
||||
cp $(DOC2) '$(PACKAGE)/doc'
|
||||
cd '$(PACKAGE)/tests' && $(MAKE) depend
|
||||
tar jcf '$(PACKAGE).tar.bz2' '$(PACKAGE)'
|
||||
|
||||
distcheck: dist
|
||||
cd '$(PACKAGE)' && make check
|
||||
|
||||
install:
|
||||
$(MKDIR) $(DESTDIR)$(PREFIX)/include
|
||||
cp m-*.h $(DESTDIR)$(PREFIX)/include
|
||||
$(INSTALL_DATA) $(HEADER) $(DESTDIR)$(PREFIX)/include
|
||||
|
||||
uninstall:
|
||||
for i in $(HEADER) ; do $(RM) $(DESTDIR)$(PREFIX)/include/$$i ; done
|
||||
+54
-12
@@ -5,6 +5,8 @@ CPPFLAGS=-I..
|
||||
N=4
|
||||
BENCH_DEF=
|
||||
|
||||
.PHONY: all container queue mempool string plain bench-mlib bench-mlib-mempool bench-stl bench-qt bench-glib bench-klib bench-libdynamic bench-sparsepp bench-collectionc bench-tommyds bench-flathashmap bench-hopscotchmap bench-mlib-thread bench-liblfds bench-concurrentqueue bench-boost bench-mempool bench-string bench-plain
|
||||
|
||||
all: container queue mempool string plain
|
||||
|
||||
#########################################################################
|
||||
@@ -32,29 +34,39 @@ plain: bench-plain
|
||||
bench-mlib:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -pthread bench-mlib.c -o bench-mlib.exe
|
||||
@./bench-mlib.exe 10
|
||||
@./bench-mlib.exe 11
|
||||
@./bench-mlib.exe 20
|
||||
@./bench-mlib.exe 30
|
||||
@./bench-mlib.exe 31
|
||||
@./bench-mlib.exe 40
|
||||
@./bench-mlib.exe 41
|
||||
@./bench-mlib.exe 42
|
||||
@./bench-mlib.exe 43
|
||||
@./bench-mlib.exe 50
|
||||
@./bench-mlib.exe 51
|
||||
|
||||
bench-mlib-mempool:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) bench-mlib.c -DUSE_MEMPOOL -pthread -o bench-mlib-mempool.exe
|
||||
@./bench-mlib-mempool.exe 10
|
||||
@./bench-mlib-mempool.exe 11
|
||||
@./bench-mlib-mempool.exe 20
|
||||
@./bench-mlib-mempool.exe 30
|
||||
@./bench-mlib-mempool.exe 31
|
||||
@./bench-mlib-mempool.exe 40
|
||||
@./bench-mlib-mempool.exe 41
|
||||
@./bench-mlib-mempool.exe 42
|
||||
@./bench-mlib-mempool.exe 43
|
||||
@./bench-mlib-mempool.exe 50
|
||||
@./bench-mlib-mempool.exe 51
|
||||
|
||||
bench-mlib-thread:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) bench-mlib.c -DMULTI_THREAD_MEASURE -pthread -o bench-mlib-thread.exe
|
||||
@./bench-mlib-thread.exe 60
|
||||
@./bench-mlib-thread.exe 61
|
||||
@./bench-mlib-thread.exe 62
|
||||
@./bench-mlib-thread.exe 63
|
||||
@./bench-mlib-thread.exe 64
|
||||
@./bench-mlib-thread.exe 65
|
||||
|
||||
bench-stl:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) bench-stl.cpp -o bench-stl.exe
|
||||
@@ -66,10 +78,13 @@ bench-stl:
|
||||
@./bench-stl.exe 42
|
||||
@./bench-stl.exe 43
|
||||
@./bench-stl.exe 50
|
||||
@./bench-stl.exe 51
|
||||
|
||||
# QT is supposed to be installed
|
||||
# QT shall point to the the directory containing the file .pc are stored
|
||||
bench-qt:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) -fPIC `pkg-config --libs --cflags Qt5Core` bench-qt.cpp -o bench-qt.exe
|
||||
@if test -n "$${QT}" ; then $(MAKE) bench-qt0 ; else echo "Nothing to be done for QT." ; fi
|
||||
bench-qt0:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) -fPIC `PKG_CONFIG_PATH="$${QT}" pkg-config --libs --cflags Qt5Core` bench-qt.cpp -o bench-qt.exe
|
||||
@./bench-qt.exe 10
|
||||
@./bench-qt.exe 20
|
||||
@./bench-qt.exe 30
|
||||
@@ -78,9 +93,11 @@ bench-qt:
|
||||
@./bench-qt.exe 42
|
||||
@./bench-qt.exe 50
|
||||
|
||||
#GLIB is supposed to be installed
|
||||
# GLIB shall point to the the directory containing the file .pc are stored
|
||||
bench-glib:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) bench-glib.c -o bench-glib.exe `pkg-config --libs --cflags glib-2.0`
|
||||
@if test -n "$${GLIB}" ; then $(MAKE) bench-glib0 ; else echo "Nothing to be done for GLIB." ; fi
|
||||
bench-glib0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) bench-glib.c -o bench-glib.exe `PKG_CONFIG_PATH="$${GLIB}" pkg-config --libs --cflags glib-2.0`
|
||||
@./bench-glib.exe 10
|
||||
@./bench-glib.exe 20
|
||||
@./bench-glib.exe 30
|
||||
@@ -164,7 +181,7 @@ bench-flathashmap:
|
||||
@if test -n "$${FLATHASHMAP}" ; then $(MAKE) bench-flathashmap0 ; else echo "Nothing to be done for FLATHASHMAP." ; fi
|
||||
|
||||
bench-flathashmap0:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${FLATHASHMAP}" bench-flathashmap.cpp -o bench-flathashmap.exe
|
||||
$(CXX) -std=c++14 $(CFLAGS) $(CPPFLAGS) -I "$${FLATHASHMAP}" bench-flathashmap.cpp -o bench-flathashmap.exe
|
||||
@./bench-flathashmap.exe 41
|
||||
@./bench-flathashmap.exe 42
|
||||
@./bench-flathashmap.exe 43
|
||||
@@ -222,9 +239,8 @@ bench-libsrt0:
|
||||
# The library shall be built.
|
||||
bench-liblfds:
|
||||
@if test -n "$${LIBLFDS}" ; then $(MAKE) bench-liblfds0 ; else echo "Nothing to be done for LIBLFDS." ; fi
|
||||
|
||||
bench-liblfds0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${LIBLFDS}/inc" -Wno-unknown-pragmas bench-liblfds.c -o bench-liblfds.exe $${LIBLFDS}/bin/liblfds711.so -lpthread
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${LIBLFDS}/inc" -Wno-unknown-pragmas bench-liblfds.c -o bench-liblfds.exe $${LIBLFDS}/bin/liblfds711.a -lpthread
|
||||
@LD_LIBRARY_PATH="$${LIBLFDS}/bin:$${LD_LIBRARY_PATH}" ./bench-liblfds.exe 60
|
||||
|
||||
|
||||
@@ -232,17 +248,33 @@ bench-liblfds0:
|
||||
# The library shall be built.
|
||||
bench-concurrentqueue:
|
||||
@if test -n "$${CONCURRENTQUEUE}" ; then $(MAKE) bench-concurrentqueue0 ; else echo "Nothing to be done for CONCURRENTQUEUE." ; fi
|
||||
|
||||
bench-concurrentqueue0:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${CONCURRENTQUEUE}/" bench-concurrentqueue.cpp -o bench-concurrentqueue.exe -lpthread
|
||||
@./bench-concurrentqueue.exe 60
|
||||
|
||||
|
||||
# BOOST is assumed to be installed in the system
|
||||
# XXHASH shall point to the source directory of xxhash
|
||||
bench-xxhash:
|
||||
@if test -n "$${XXHASH}" ; then $(MAKE) bench-xxhash0 ; else echo "Nothing to be done for XXHASH." ; fi
|
||||
bench-xxhash0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${XXHASH}/" bench-xxhash.c "$${XXHASH}/libxxhash.a" -o bench-xxhash.exe
|
||||
@./bench-xxhash.exe 70
|
||||
|
||||
# BOOST shall point to the header location.
|
||||
bench-boost:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) bench-boost.cpp -o bench-boost.exe -lpthread
|
||||
@if test -n "$${BOOST}" ; then $(MAKE) bench-boost0 ; else echo "Nothing to be done for BOOST." ; fi
|
||||
bench-boost0:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${BOOST}" bench-boost.cpp -o bench-boost.exe -lpthread
|
||||
@./bench-boost.exe 60
|
||||
|
||||
# NEDTRIES shall point to the header location.
|
||||
bench-nedtries:
|
||||
@if test -n "$${NEDTRIES}" ; then $(MAKE) bench-nedtries0 ; else echo "Nothing to be done for NEDTRIES." ; fi
|
||||
bench-nedtries0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${NEDTRIES}" bench-nedtries.c -o bench-nedtries.exe
|
||||
@./bench-nedtries.exe 40
|
||||
@./bench-nedtries.exe 45
|
||||
|
||||
############################################################################
|
||||
|
||||
bench-plain:
|
||||
@@ -252,7 +284,9 @@ bench-plain:
|
||||
############################################################################
|
||||
|
||||
bench-mempool:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) pool.cc `pkg-config --libs --cflags glib-2.0` -lboost_system -o bench-mempool.exe
|
||||
@if test -n "$${BOOST}" ; then if test -n "$${GLIB}" ; then make bench-mempool0 BENCH_DEF="-DUSE_BOOST -DUSE_GLIB `pkg-config --libs --cflags glib-2.0` -lboost_system" ; else make bench-mempool0 BENCH_DEF="-DUSE_BOOST -lboost_system" ; fi ; else if test -n "$${GLIB}" ; then make bench-mempool0 BENCH_DEF="-DUSE_GLIB `pkg-config --libs --cflags glib-2.0`" ; else make bench-mempool0 ; fi ; fi
|
||||
bench-mempool0:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) pool.cc $(BENCH_DEF) -o bench-mempool.exe
|
||||
@./bench-mempool.exe
|
||||
|
||||
############################################################################
|
||||
@@ -269,7 +303,14 @@ bench-string:
|
||||
@if test -n "$${BSTRLIB}" ; then $(MAKE) bench-string-with BENCH_DEF="-DBENCH_CAN_USE_BSTRLIB -I ${BSTRLIB} $${BSTRLIB}/obj/bstrlib.o $${BSTRLIB}/obj/bstraux.o $${BSTRLIB}/obj/bstrwrap.o" ; else $(MAKE) bench-string-with ; fi
|
||||
|
||||
bench-string-with:
|
||||
@if test -n "$${LIBSRT}" ; then $(MAKE) bench-string-exe BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_LIBSRT -I$${LIBSRT}/src $${LIBSRT}/libsrt.a" ; else $(MAKE) bench-string-exe BENCH_DEF="$(BENCH_DEF)"; fi
|
||||
@if test -n "$${LIBSRT}" ; then $(MAKE) bench-string-with2 BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_LIBSRT -I$${LIBSRT}/src $${LIBSRT}/libsrt.a" ; else $(MAKE) bench-string-with2 BENCH_DEF="$(BENCH_DEF)"; fi
|
||||
|
||||
#NOTE: Need to build by hand sds.o since it is not build by default and sds.c is not compatible with C++
|
||||
bench-string-with2:
|
||||
@if test -n "$${SDS}" ; then $(MAKE) bench-string-with3 BENCH_DEF="$(BENCH_DEF) -fpermissive -DBENCH_CAN_USE_SDS -I$${SDS}/ $${SDS}/sds.o" ; else $(MAKE) bench-string-with3 BENCH_DEF="$(BENCH_DEF)"; fi
|
||||
|
||||
bench-string-with3:
|
||||
@if test -n "$${RAPIDSTRING}" ; then $(MAKE) bench-string-exe BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_RAPIDSTRING -I$${RAPIDSTRING}/ " ; else $(MAKE) bench-string-exe BENCH_DEF="$(BENCH_DEF)"; fi
|
||||
|
||||
bench-string-exe:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${BSTRLIB}" -DBENCH_CAN_USE_STL -DBENCH_CAN_USE_MSTARLIB bench-string.cpp -o bench-string.exe $(BENCH_DEF)
|
||||
@@ -280,6 +321,7 @@ bench-string-exe:
|
||||
|
||||
clean:
|
||||
rm -f *.o bench-*.exe bench-*.s *~ massif.out.*
|
||||
cd nucleotide && $(MAKE) clean
|
||||
|
||||
############################################################################
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ static void test_rbtree(size_t n)
|
||||
stat = treetable_new(compare, &tree);
|
||||
if (stat != CC_OK) abort();
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
void *key = (void*)(uintptr_t) i;
|
||||
void *key = (void*)(uintptr_t) rand_get();
|
||||
stat = treetable_add(tree, key, key);
|
||||
if (stat != CC_OK) abort();
|
||||
}
|
||||
@@ -122,11 +122,11 @@ static int equal_func(const void *key1, const void *key2)
|
||||
{
|
||||
const unsigned long pa = (uintptr_t) key1;
|
||||
const unsigned long pb = (uintptr_t) key2;
|
||||
return pa == pb;
|
||||
return (pa < pb) ? -1 : pa > pb;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict(unsigned long n)
|
||||
test_dict(size_t n)
|
||||
{
|
||||
HashTable *dict;
|
||||
enum cc_stat stat;
|
||||
@@ -138,8 +138,8 @@ test_dict(unsigned long n)
|
||||
if (stat != CC_OK) abort();
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
void *key = (void*)(uintptr_t) rand_get();
|
||||
void *value = (void*)(uintptr_t) rand_get();
|
||||
void *key = (void*)(uintptr_t) rand_get();
|
||||
stat = hashtable_add(dict, key, value );
|
||||
if (stat != CC_OK) abort();
|
||||
}
|
||||
@@ -164,7 +164,7 @@ static int char_equal (const void* a, const void * b)
|
||||
{
|
||||
const char_array_t *pa = (const char_array_t *)a;
|
||||
const char_array_t *pb = (const char_array_t *)b;
|
||||
return strcmp(*pa,*pb)==0;
|
||||
return strcmp(*pa,*pb);
|
||||
}
|
||||
|
||||
static size_t char_hash(const void *a, int l, uint32_t seed)
|
||||
@@ -178,7 +178,7 @@ static size_t char_hash(const void *a, int l, uint32_t seed)
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
HashTable *dict;
|
||||
enum cc_stat stat;
|
||||
@@ -189,9 +189,14 @@ test_dict_big(unsigned long n)
|
||||
stat = hashtable_new_conf(&htc, &dict);
|
||||
if (stat != CC_OK) abort();
|
||||
|
||||
char_array_t **tab = malloc (sizeof(char_array_t *)*2*n);
|
||||
if (!tab) abort();
|
||||
size_t cpt = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t *key = malloc (sizeof (char_array_t));
|
||||
char_array_t *value = malloc (sizeof (char_array_t));
|
||||
tab[cpt++] = key;
|
||||
tab[cpt++] = value;
|
||||
sprintf(*key, "%u", rand_get());
|
||||
sprintf(*value, "%u", rand_get());
|
||||
stat = hashtable_add(dict, key, value );
|
||||
@@ -209,6 +214,9 @@ test_dict_big(unsigned long n)
|
||||
}
|
||||
g_result = s;
|
||||
hashtable_destroy(dict);
|
||||
if (cpt != 2*n) abort();
|
||||
for(size_t i = 0; i < 2*n;i++)
|
||||
free(tab[i]);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
@@ -25,7 +25,7 @@ using namespace std;
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict2(unsigned long n)
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
dense_hash_map<unsigned long, unsigned long> dict;
|
||||
dict.set_empty_key(-1);
|
||||
@@ -75,7 +75,7 @@ namespace std {
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
dense_hash_map<char_array_s, char_array_s> dict;
|
||||
struct char_array_s empty, del;
|
||||
@@ -104,7 +104,7 @@ test_dict_big(unsigned long n)
|
||||
|
||||
/********************************************************************************************/
|
||||
static void
|
||||
test_dict_str(unsigned long n)
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
dense_hash_map<string, string> dict;
|
||||
string empty, del;
|
||||
|
||||
@@ -26,7 +26,7 @@ using namespace std;
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict2(unsigned long n)
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
flat_hash_map<unsigned long, unsigned long> dict;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
@@ -62,7 +62,7 @@ namespace std {
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
flat_hash_map<char_array_s, char_array_s> dict;
|
||||
|
||||
@@ -86,7 +86,7 @@ test_dict_big(unsigned long n)
|
||||
|
||||
/********************************************************************************************/
|
||||
static void
|
||||
test_dict_str(unsigned long n)
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
flat_hash_map<string, string> dict;
|
||||
|
||||
|
||||
+2
-2
@@ -97,7 +97,7 @@ static gboolean equal_func(gconstpointer a, gconstpointer b)
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict(unsigned long n)
|
||||
test_dict(size_t n)
|
||||
{
|
||||
GHashTable *dict = g_hash_table_new_full(hash_func, equal_func, free, free);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
@@ -140,7 +140,7 @@ static guint char_hash(gconstpointer a)
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
GHashTable *dict = g_hash_table_new_full(char_hash, char_equal, free, free);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
|
||||
@@ -25,7 +25,7 @@ using namespace std;
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict2(unsigned long n)
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
tsl::hopscotch_map<unsigned long, unsigned long> dict;
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace std {
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
hopscotch_map<char_array_s, char_array_s> dict;
|
||||
|
||||
@@ -87,7 +87,7 @@ test_dict_big(unsigned long n)
|
||||
|
||||
/********************************************************************************************/
|
||||
static void
|
||||
test_dict_str(unsigned long n)
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
hopscotch_map<string, string> dict;
|
||||
|
||||
|
||||
+4
-4
@@ -82,7 +82,7 @@ static void test_rbtree(size_t n)
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long j = 0;
|
||||
unsigned long j = rand_get();
|
||||
if (kb_getp(kTree, tree, &j) != 0)
|
||||
s+= j;
|
||||
}
|
||||
@@ -98,7 +98,7 @@ static void test_rbtree(size_t n)
|
||||
KHASH_INIT(iun, unsigned long, unsigned long, 1, hash_func, hash_eq)
|
||||
|
||||
static void
|
||||
test_dict(unsigned long n)
|
||||
test_dict(size_t n)
|
||||
{
|
||||
khash_t(iun) *dict = kh_init(iun);
|
||||
|
||||
@@ -137,7 +137,7 @@ static inline size_t char_hash(const char_array_t a) {
|
||||
KHASH_INIT(iub, char_array_t, char_array_t, 1, char_hash, char_equal_p)
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
khash_t(iub) *dict = kh_init(iub);
|
||||
|
||||
@@ -195,7 +195,7 @@ int main(int argc, const char *argv[])
|
||||
if (n == 30)
|
||||
test_function("B-tree time", 1000000, test_rbtree);
|
||||
if (n == 40)
|
||||
test_function("Dict time", 1000000, test_dict);
|
||||
test_function("DictOA time", 1000000, test_dict);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 50)
|
||||
|
||||
+11
-10
@@ -5,6 +5,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "dynamic.h"
|
||||
|
||||
@@ -29,26 +30,26 @@ static void test_array(size_t n)
|
||||
s += *(unsigned int*) vector_at(&a1, i) * *(unsigned int*) vector_at(&a2, i);
|
||||
}
|
||||
g_result = s;
|
||||
vector_destruct(&a1);
|
||||
vector_destruct(&a2);
|
||||
vector_destruct(&a1, NULL);
|
||||
vector_destruct(&a2, NULL);
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static size_t hash_func (map *m, void *a)
|
||||
static size_t hash_func (void *a)
|
||||
{
|
||||
return hash_data(a, sizeof (unsigned long));
|
||||
}
|
||||
|
||||
static int equal_func(map *m, void *a, void *b)
|
||||
static int equal_func(void *a, void *b)
|
||||
{
|
||||
const unsigned long *pa = a;
|
||||
const unsigned long *pb = b;
|
||||
return *pa == *pb;
|
||||
}
|
||||
|
||||
static void set_func(map *m, void *a, void *b)
|
||||
static void set_func(void *a, void *b)
|
||||
{
|
||||
unsigned long *pa = a;
|
||||
const unsigned long *pb = b;
|
||||
@@ -63,7 +64,7 @@ struct map_element
|
||||
};
|
||||
|
||||
static void
|
||||
test_dict(unsigned long n)
|
||||
test_dict(size_t n)
|
||||
{
|
||||
map dict;
|
||||
map_construct(&dict, sizeof(map_element), (map_element[]) {{.key = -1}}, set_func);
|
||||
@@ -86,20 +87,20 @@ test_dict(unsigned long n)
|
||||
|
||||
typedef char char_array_t[256];
|
||||
|
||||
static int char_equal (map *m, void *a, void *b)
|
||||
static int char_equal (void *a, void *b)
|
||||
{
|
||||
const char_array_t *pa = (const char_array_t *)a;
|
||||
const char_array_t *pb = (const char_array_t *)b;
|
||||
return strcmp(*pa,*pb)==0;
|
||||
}
|
||||
|
||||
static size_t char_hash(map *m, void *a)
|
||||
static size_t char_hash(void *a)
|
||||
{
|
||||
char_array_t *pa = (char_array_t *)a;
|
||||
return hash_string(*pa);
|
||||
}
|
||||
|
||||
static void char_set(map *m, void *a, void *b)
|
||||
static void char_set(void *a, void *b)
|
||||
{
|
||||
char_array_t *pa = (char_array_t *)a;
|
||||
const char_array_t *pb = (const char_array_t *)b;
|
||||
@@ -114,7 +115,7 @@ struct big_map_element
|
||||
};
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
map dict;
|
||||
big_map_element empty;
|
||||
|
||||
+295
-45
@@ -7,12 +7,15 @@
|
||||
#include "m-array.h"
|
||||
#include "m-list.h"
|
||||
#include "m-rbtree.h"
|
||||
#include "m-bptree.h"
|
||||
#include "m-dict.h"
|
||||
#include "m-algo.h"
|
||||
#include "m-mempool.h"
|
||||
#include "m-string.h"
|
||||
#include "m-buffer.h"
|
||||
#include "m-mutex.h"
|
||||
#include "m-deque.h"
|
||||
#include "m-concurrent.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
@@ -68,6 +71,37 @@ static void test_list (size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
// Two benched mode: with or without mempool
|
||||
#ifdef USE_MEMPOOL
|
||||
LIST_DUAL_PUSH_DEF(dlist_uint, unsigned int, (MEMPOOL( dlist_mpool), MEMPOOL_LINKAGE(static)))
|
||||
#else
|
||||
LIST_DUAL_PUSH_DEF(dlist_uint, unsigned int)
|
||||
#endif
|
||||
|
||||
static void test_dlist (size_t n)
|
||||
{
|
||||
#ifdef USE_MEMPOOL
|
||||
dlist_uint_mempool_init(dlist_mpool);
|
||||
#endif
|
||||
M_LET(a1, a2, LIST_OPLIST(dlist_uint)) {
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
dlist_uint_push_back(a1, rand_get() );
|
||||
dlist_uint_push_back(a2, rand_get() );
|
||||
}
|
||||
unsigned int s = 0;
|
||||
dlist_uint_it_t it1, it2;
|
||||
for(dlist_uint_it(it1, a1), dlist_uint_it(it2,a2); !dlist_uint_end_p(it1); dlist_uint_next(it1), dlist_uint_next(it2)) {
|
||||
s += *dlist_uint_cref(it1) * *dlist_uint_cref(it2);
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
#ifdef USE_MEMPOOL
|
||||
dlist_uint_mempool_clear(dlist_mpool);
|
||||
#endif
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#ifdef USE_MEMPOOL
|
||||
RBTREE_DEF(rbtree_ulong, unsigned long, (MEMPOOL( rbtree_mpool), MEMPOOL_LINKAGE(static)))
|
||||
#else
|
||||
@@ -99,6 +133,27 @@ static void test_rbtree(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
BPTREE_DEF(bptree_ulong, 21, unsigned long, M_DEFAULT_OPLIST)
|
||||
|
||||
static void test_bptree(size_t n)
|
||||
{
|
||||
M_LET(tree, BPTREE_OPLIST(bptree_ulong)) {
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
bptree_ulong_push(tree, rand_get());
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long *p = bptree_ulong_get(tree, rand_get());
|
||||
if (p)
|
||||
s += *p;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#ifdef USE_MEMPOOL
|
||||
DICT_DEF2(dict_ulong, unsigned long, M_OPEXTEND(M_DEFAULT_OPLIST, MEMPOOL(dict_mpool), MEMPOOL_LINKAGE(static)), unsigned long, M_DEFAULT_OPLIST)
|
||||
#else
|
||||
@@ -106,15 +161,16 @@ DICT_DEF2(dict_ulong, unsigned long, M_DEFAULT_OPLIST, unsigned long, M_DEFAULT_
|
||||
#endif
|
||||
|
||||
static void
|
||||
test_dict(unsigned long n)
|
||||
test_dict(size_t n)
|
||||
{
|
||||
#ifdef USE_MEMPOOL
|
||||
dict_ulong_list_pair_mempool_init(dict_mpool);
|
||||
#endif
|
||||
M_LET(dict, DICT_OPLIST(dict_ulong)) {
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long value = rand_get();
|
||||
unsigned long key = rand_get();
|
||||
dict_ulong_set_at(dict, key, rand_get() );
|
||||
dict_ulong_set_at(dict, key, value);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
@@ -147,7 +203,7 @@ DICT_OA_DEF2(dict_oa_ulong,
|
||||
unsigned long, M_DEFAULT_OPLIST)
|
||||
|
||||
static void
|
||||
test_dict_oa(unsigned long n)
|
||||
test_dict_oa(size_t n)
|
||||
{
|
||||
M_LET(dict, DICT_OPLIST(dict_oa_ulong)) {
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
@@ -167,23 +223,20 @@ test_dict_oa(unsigned long n)
|
||||
/********************************************************************************************/
|
||||
|
||||
typedef char char_array_t[256];
|
||||
|
||||
static void char_init (char_array_t a) { a[0] = 0; }
|
||||
static void char_set (char_array_t a, const char_array_t b) { strcpy(a, b); }
|
||||
static bool char_equal_p (const char_array_t a, const char_array_t b) { return strcmp(a,b)==0; }
|
||||
static size_t char_hash(const char_array_t a) { return m_core_hash (a, strlen(a)); }
|
||||
|
||||
// NOTE: Can't use the name OPLIST as a macro!
|
||||
#define CHAR_OPLIST (INIT(char_init), INIT_SET(char_set), SET(char_set), CLEAR(char_init), HASH(char_hash), EQUAL(char_equal_p))
|
||||
#define M_OPL_char_array_t() (INIT(char_init), INIT_SET(char_set), SET(char_set), CLEAR(char_init), HASH(char_hash), EQUAL(char_equal_p))
|
||||
|
||||
#ifdef USE_MEMPOOL
|
||||
DICT_DEF2(dict_char, char_array_t, M_OPEXTEND(CHAR_OPLIST,MEMPOOL(dict_mpool2), MEMPOOL_LINKAGE(static)), char_array_t, CHAR_OPLIST)
|
||||
DICT_STOREHASH_DEF2(dict_char, char_array_t, M_OPEXTEND(M_OPL_char_array_t(),MEMPOOL(dict_mpool2), MEMPOOL_LINKAGE(static)), char_array_t, M_OPL_char_array_t())
|
||||
#else
|
||||
DICT_DEF2(dict_char, char_array_t, CHAR_OPLIST, char_array_t, CHAR_OPLIST)
|
||||
DICT_STOREHASH_DEF2(dict_char, char_array_t, M_OPL_char_array_t(), char_array_t, M_OPL_char_array_t())
|
||||
#endif
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
#ifdef USE_MEMPOOL
|
||||
dict_char_list_pair_mempool_init(dict_mpool2);
|
||||
@@ -220,7 +273,7 @@ DICT_DEF2(dict_str, string_t, STRING_OPLIST, string_t, STRING_OPLIST)
|
||||
#endif
|
||||
|
||||
static void
|
||||
test_dict_str(unsigned long n)
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
#ifdef USE_MEMPOOL
|
||||
dict_str_list_pair_mempool_init(dict_mpool3);
|
||||
@@ -263,75 +316,89 @@ static void test_sort(size_t n)
|
||||
}
|
||||
}
|
||||
|
||||
static void test_stable_sort(size_t n)
|
||||
{
|
||||
M_LET(a1, ARRAY_OPLIST(array_float)) {
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
array_float_push_back(a1, rand_get() );
|
||||
}
|
||||
array_float_special_stable_sort(a1);
|
||||
g_result = *array_float_get(a1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#define SIZE_LIMIT (UINT_MAX/2)
|
||||
|
||||
BUFFER_DEF(buffer_uint, unsigned int, 0, BUFFER_QUEUE|BUFFER_BLOCKING)
|
||||
buffer_uint_t g_buff;
|
||||
buffer_uint_t g_buff_lock;
|
||||
|
||||
BUFFER_DEF(buffer_ull, unsigned long long, 0, BUFFER_QUEUE|BUFFER_BLOCKING)
|
||||
buffer_ull_t g_final;
|
||||
buffer_ull_t g_final_lock;
|
||||
|
||||
static void final(void *arg)
|
||||
static void final_lock(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long j, s = 0;
|
||||
for(int i = 0; i < n;i++) {
|
||||
buffer_ull_pop(&j, g_final);
|
||||
buffer_ull_pop(&j, g_final_lock);
|
||||
s += j;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void conso(void *arg)
|
||||
static void conso_lock(void *arg)
|
||||
{
|
||||
unsigned int j;
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long s = 0;
|
||||
for(int i = 0; i < n;i++) {
|
||||
buffer_uint_pop(&j, g_buff);
|
||||
buffer_uint_pop(&j, g_buff_lock);
|
||||
s += j;
|
||||
}
|
||||
buffer_ull_push(g_final, s);
|
||||
buffer_ull_push(g_final_lock, s);
|
||||
}
|
||||
|
||||
static void prod(void *arg)
|
||||
static void prod_lock(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
size_t r = n;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
buffer_uint_push(g_buff, r );
|
||||
buffer_uint_push(g_buff_lock, r );
|
||||
r = r * 31421U + 6927U;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_buffer(size_t n)
|
||||
{
|
||||
const int cpu_count = get_cpu_count();
|
||||
const int cpu_count = n > SIZE_LIMIT ? 2 : get_cpu_count();
|
||||
const int prod_count = cpu_count/2;
|
||||
const int conso_count = cpu_count - prod_count;
|
||||
if (cpu_count < 2) {
|
||||
fprintf(stderr, "WARNING: Can not measure Buffer performance.\n");
|
||||
return;
|
||||
}
|
||||
n = n > SIZE_LIMIT ? n - SIZE_LIMIT : n;
|
||||
// Init
|
||||
buffer_uint_init(g_buff, 64*cpu_count);
|
||||
buffer_ull_init (g_final, 64*cpu_count);
|
||||
buffer_uint_init(g_buff_lock, 64*cpu_count);
|
||||
buffer_ull_init (g_final_lock, 64*cpu_count);
|
||||
|
||||
// Create thread
|
||||
m_thread_t idx_p[prod_count];
|
||||
m_thread_t idx_c[conso_count];
|
||||
m_thread_t idx_final;
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_create (idx_p[i], prod, &n);
|
||||
m_thread_create (idx_p[i], prod_lock, &n);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_create (idx_c[i], conso, &n);
|
||||
m_thread_create (idx_c[i], conso_lock, &n);
|
||||
}
|
||||
size_t n2 = conso_count;
|
||||
m_thread_create(idx_final, final, &n2);
|
||||
m_thread_create(idx_final, final_lock, &n2);
|
||||
|
||||
// Wait for jobs to be done.
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
@@ -343,79 +410,80 @@ static void test_buffer(size_t n)
|
||||
m_thread_join(idx_final);
|
||||
|
||||
// Clear & quit
|
||||
buffer_ull_clear(g_final);
|
||||
buffer_uint_clear(g_buff);
|
||||
buffer_ull_clear(g_final_lock);
|
||||
buffer_uint_clear(g_buff_lock);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
QUEUE_MPMC_DEF(queue_uint, unsigned int, BUFFER_QUEUE)
|
||||
queue_uint_t g_buff2;
|
||||
queue_uint_t g_buff_mpmc;
|
||||
|
||||
QUEUE_MPMC_DEF(queue_ull, unsigned long long, BUFFER_QUEUE)
|
||||
queue_ull_t g_final2;
|
||||
queue_ull_t g_final_mpmc;
|
||||
|
||||
static void final2(void *arg)
|
||||
static void final_mpmc(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long j, s = 0;
|
||||
for(int i = 0; i < n;i++) {
|
||||
while (!queue_ull_pop(&j, g_final2));
|
||||
while (!queue_ull_pop(&j, g_final_mpmc)) m_thread_yield();
|
||||
s += j;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void conso2(void *arg)
|
||||
static void conso_mpmc(void *arg)
|
||||
{
|
||||
unsigned int j;
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long s = 0;
|
||||
for(int i = 0; i < n;i++) {
|
||||
while (!queue_uint_pop(&j, g_buff2));
|
||||
while (!queue_uint_pop(&j, g_buff_mpmc));
|
||||
s += j;
|
||||
}
|
||||
while (!queue_ull_push(g_final2, s));
|
||||
while (!queue_ull_push(g_final_mpmc, s));
|
||||
}
|
||||
|
||||
static void prod2(void *arg)
|
||||
static void prod_mpmc(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
size_t r = n;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
while (!queue_uint_push(g_buff2, r ));
|
||||
while (!queue_uint_push(g_buff_mpmc, r ));
|
||||
r = r * 31421U + 6927U;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_queue(size_t n)
|
||||
{
|
||||
const int cpu_count = get_cpu_count();
|
||||
const int cpu_count = n > SIZE_LIMIT ? 2 : get_cpu_count();
|
||||
const int prod_count = cpu_count/2;
|
||||
const int conso_count = cpu_count - prod_count;
|
||||
if (cpu_count < 2) {
|
||||
fprintf(stderr, "WARNING: Can not measure Queue performance.\n");
|
||||
return;
|
||||
}
|
||||
n = n > SIZE_LIMIT ? n - SIZE_LIMIT : n;
|
||||
// Init
|
||||
queue_uint_init(g_buff2, 64*cpu_count);
|
||||
queue_ull_init (g_final2, 64*cpu_count);
|
||||
queue_uint_init(g_buff_mpmc, 64*cpu_count);
|
||||
queue_ull_init (g_final_mpmc, 64*cpu_count);
|
||||
|
||||
// Create thread
|
||||
m_thread_t idx_p[prod_count];
|
||||
m_thread_t idx_c[conso_count];
|
||||
m_thread_t idx_final;
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_create (idx_p[i], prod2, &n);
|
||||
m_thread_create (idx_p[i], prod_mpmc, &n);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_create (idx_c[i], conso2, &n);
|
||||
m_thread_create (idx_c[i], conso_mpmc, &n);
|
||||
}
|
||||
size_t n2 = conso_count;
|
||||
m_thread_create(idx_final, final2, &n2);
|
||||
m_thread_create(idx_final, final_mpmc, &n2);
|
||||
|
||||
// Wait for jobs to be done.
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
@@ -427,8 +495,176 @@ static void test_queue(size_t n)
|
||||
m_thread_join(idx_final);
|
||||
|
||||
// Clear & quit
|
||||
queue_ull_clear(g_final2);
|
||||
queue_uint_clear(g_buff2);
|
||||
queue_ull_clear(g_final_mpmc);
|
||||
queue_uint_clear(g_buff_mpmc);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
QUEUE_SPSC_DEF(queue_single_uint, unsigned int, BUFFER_QUEUE)
|
||||
queue_single_uint_t g_buff_spsc;
|
||||
|
||||
QUEUE_SPSC_DEF(queue_single_ull, unsigned long long, BUFFER_QUEUE)
|
||||
queue_single_ull_t g_final_spsc;
|
||||
|
||||
static void final_spsc(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long j, s = 0;
|
||||
for(int i = 0; i < n;i++) {
|
||||
while (!queue_single_ull_pop(&j, g_final_spsc)) m_thread_yield();
|
||||
s += j;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void conso_spsc(void *arg)
|
||||
{
|
||||
unsigned int j;
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long s = 0;
|
||||
for(int i = 0; i < n;i++) {
|
||||
while (!queue_single_uint_pop(&j, g_buff_spsc));
|
||||
s += j;
|
||||
}
|
||||
while (!queue_single_ull_push(g_final_spsc, s));
|
||||
}
|
||||
|
||||
static void prod_spsc(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
size_t r = n;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
while (!queue_single_uint_push(g_buff_spsc, r ));
|
||||
r = r * 31421U + 6927U;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_queue_single(size_t n)
|
||||
{
|
||||
const int cpu_count = 2;
|
||||
const int prod_count = cpu_count/2;
|
||||
const int conso_count = cpu_count - prod_count;
|
||||
// Init
|
||||
queue_single_uint_init(g_buff_spsc, 64*cpu_count);
|
||||
queue_single_ull_init (g_final_spsc, 64*cpu_count);
|
||||
|
||||
// Create thread
|
||||
m_thread_t idx_p[prod_count];
|
||||
m_thread_t idx_c[conso_count];
|
||||
m_thread_t idx_final;
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_create (idx_p[i], prod_spsc, &n);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_create (idx_c[i], conso_spsc, &n);
|
||||
}
|
||||
size_t n2 = conso_count;
|
||||
m_thread_create(idx_final, final_spsc, &n2);
|
||||
|
||||
// Wait for jobs to be done.
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_join(idx_p[i]);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_join(idx_c[i]);
|
||||
}
|
||||
m_thread_join(idx_final);
|
||||
|
||||
// Clear & quit
|
||||
queue_single_ull_clear(g_final_spsc);
|
||||
queue_single_uint_clear(g_buff_spsc);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
DEQUE_DEF(deque_uint, unsigned int)
|
||||
CONCURRENT_DEF(cdeque_uint, deque_uint_t, M_OPEXTEND(DEQUE_OPLIST(deque_uint, M_DEFAULT_OPLIST), PUSH(deque_uint_push_front)))
|
||||
|
||||
DEQUE_DEF(deque_ull, unsigned long long)
|
||||
CONCURRENT_DEF(cdeque_ull, deque_ull_t, M_OPEXTEND(DEQUE_OPLIST(deque_ull, M_DEFAULT_OPLIST), PUSH(deque_ull_push_front)))
|
||||
|
||||
cdeque_uint_t g_buff_conc;
|
||||
cdeque_ull_t g_final_conc;
|
||||
|
||||
static void final_conc(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long j, s = 0;
|
||||
for(int i = 0; i < n;i++) {
|
||||
cdeque_ull_pop_blocking(&j, g_final_conc, true);
|
||||
s += j;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void conso_conc(void *arg)
|
||||
{
|
||||
unsigned int j;
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long s = 0;
|
||||
for(int i = 0; i < n;i++) {
|
||||
cdeque_uint_pop_blocking(&j, g_buff_conc, true);
|
||||
s += j;
|
||||
}
|
||||
cdeque_ull_push(g_final_conc, s);
|
||||
}
|
||||
|
||||
static void prod_conc(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
size_t r = n;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
cdeque_uint_push(g_buff_conc, r );
|
||||
r = r * 31421U + 6927U;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_queue_concurrent(size_t n)
|
||||
{
|
||||
const int cpu_count = n > SIZE_LIMIT ? 2 : get_cpu_count();
|
||||
const int prod_count = cpu_count/2;
|
||||
const int conso_count = cpu_count - prod_count;
|
||||
if (cpu_count < 2) {
|
||||
fprintf(stderr, "WARNING: Can not measure CONCURRENT performance.\n");
|
||||
return;
|
||||
}
|
||||
n = n > SIZE_LIMIT ? n - SIZE_LIMIT : n;
|
||||
// Init
|
||||
cdeque_uint_init(g_buff_conc);
|
||||
cdeque_ull_init (g_final_conc);
|
||||
|
||||
// Create thread
|
||||
m_thread_t idx_p[prod_count];
|
||||
m_thread_t idx_c[conso_count];
|
||||
m_thread_t idx_final;
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_create (idx_p[i], prod_conc, &n);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_create (idx_c[i], conso_conc, &n);
|
||||
}
|
||||
size_t n2 = conso_count;
|
||||
m_thread_create(idx_final, final_conc, &n2);
|
||||
|
||||
// Wait for jobs to be done.
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_join(idx_p[i]);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_join(idx_c[i]);
|
||||
}
|
||||
m_thread_join(idx_final);
|
||||
|
||||
// Clear & quit
|
||||
cdeque_ull_clear(g_final_conc);
|
||||
cdeque_uint_clear(g_buff_conc);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
@@ -471,10 +707,14 @@ int main(int argc, const char *argv[])
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 10)
|
||||
test_function("List time",10000000, test_list);
|
||||
if (n == 11)
|
||||
test_function("DPList time",10000000, test_dlist);
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
if (n == 30)
|
||||
test_function("Rbtree time", 1000000, test_rbtree);
|
||||
if (n == 31)
|
||||
test_function("B+tree time", 1000000, test_bptree);
|
||||
if (n == 40)
|
||||
test_function("Dict time", 1000000, test_dict);
|
||||
if (n == 42)
|
||||
@@ -485,10 +725,20 @@ int main(int argc, const char *argv[])
|
||||
test_function("DictS time", 1000000, test_dict_str);
|
||||
if (n == 50)
|
||||
test_function("Sort time", 10000000, test_sort);
|
||||
if (n == 51)
|
||||
test_function("Stable Sort time", 10000000, test_stable_sort);
|
||||
if (n == 60)
|
||||
test_function("Buffer time", 1000000, test_buffer);
|
||||
if (n == 61)
|
||||
test_function("Queue MPMC time", 1000000, test_queue);
|
||||
if (n == 62)
|
||||
test_function("Buffer time (P2)", SIZE_LIMIT+1000000, test_buffer);
|
||||
if (n == 63)
|
||||
test_function("Queue MPMC time (P2)", SIZE_LIMIT+1000000, test_queue);
|
||||
if (n == 64)
|
||||
test_function("Queue SPSC time", 1000000, test_queue_single);
|
||||
if (n == 65)
|
||||
test_function("Queue CONCURRENT time", 1000000, test_queue_concurrent);
|
||||
if (n == 70) {
|
||||
n = (argc > 2) ? atoi(argv[2]) : 100000000;
|
||||
test_hash_prepare(n);
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
#define NDEBUG
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "nedtrie.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
typedef struct foo_s {
|
||||
NEDTRIE_ENTRY(foo_s) link;
|
||||
unsigned long key;
|
||||
unsigned long value;
|
||||
} foo_t;
|
||||
typedef struct foo_tree_s foo_tree_t;
|
||||
NEDTRIE_HEAD(foo_tree_s, foo_s);
|
||||
|
||||
static inline size_t fookeyfunct(const foo_t *r)
|
||||
{
|
||||
return r->key;
|
||||
}
|
||||
|
||||
NEDTRIE_GENERATE(static, foo_tree_s, foo_s, link, fookeyfunct, NEDTRIE_NOBBLEZEROS(foo_tree_s))
|
||||
|
||||
static void
|
||||
test_dict(size_t n)
|
||||
{
|
||||
foo_tree_t footree;
|
||||
NEDTRIE_INIT(&footree);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long key = rand_get();
|
||||
unsigned long val = rand_get();
|
||||
foo_t * p = malloc(sizeof(foo_t));
|
||||
if (p == NULL) abort();
|
||||
p->key = key;
|
||||
p->value = val;
|
||||
NEDTRIE_INSERT(foo_tree_s, &footree, p);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
foo_t key = { .key = rand_get() };
|
||||
foo_t *p = NEDTRIE_FIND(foo_tree_s, &footree, &key);
|
||||
if (p)
|
||||
s += p->value;
|
||||
}
|
||||
g_result = s;
|
||||
foo_t *r, *next;
|
||||
NEDTRIE_FOREACH_SAFE(r, foo_tree_s, &footree, next) {
|
||||
// Remove is needed, otherwise it segfault
|
||||
NEDTRIE_REMOVE(foo_tree_s, &footree, r);
|
||||
free(r);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_noalloc(size_t n)
|
||||
{
|
||||
foo_tree_t footree;
|
||||
NEDTRIE_INIT(&footree);
|
||||
|
||||
foo_t *tab = malloc(n*sizeof(foo_t));
|
||||
if (tab == NULL) abort();
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long key = rand_get();
|
||||
unsigned long val = rand_get();
|
||||
tab[i].key = key;
|
||||
tab[i].value = val;
|
||||
NEDTRIE_INSERT(foo_tree_s, &footree, &tab[i]);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
foo_t key = { .key = rand_get() };
|
||||
foo_t *p = NEDTRIE_FIND(foo_tree_s, &footree, &key);
|
||||
if (p)
|
||||
s += p->value;
|
||||
}
|
||||
g_result = s;
|
||||
free(tab);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 40)
|
||||
test_function("Dict time", 1000000, test_dict);
|
||||
if (n == 45)
|
||||
test_function("Dict No Alloc time", 1000000, test_dict_noalloc);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#define NDEBUG
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
+3
-3
@@ -72,7 +72,7 @@ static void test_rbtree(size_t n)
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict1(unsigned long n)
|
||||
test_dict1(size_t n)
|
||||
{
|
||||
QMap<unsigned long, unsigned long> dict;
|
||||
|
||||
@@ -92,7 +92,7 @@ test_dict1(unsigned long n)
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict2(unsigned long n)
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
QHash<unsigned long, unsigned long> dict;
|
||||
|
||||
@@ -126,7 +126,7 @@ uint qHash(const char_array_s &k)
|
||||
};
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
QHash<char_array_s, char_array_s> dict;
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ using namespace std;
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict2(unsigned long n)
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
sparse_hash_map<unsigned long, unsigned long> dict;
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace std {
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
sparse_hash_map<char_array_s, char_array_s> dict;
|
||||
|
||||
@@ -87,7 +87,7 @@ test_dict_big(unsigned long n)
|
||||
|
||||
/********************************************************************************************/
|
||||
static void
|
||||
test_dict_str(unsigned long n)
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
sparse_hash_map<string, string> dict;
|
||||
|
||||
|
||||
+23
-11
@@ -73,7 +73,7 @@ static void test_rbtree(size_t n)
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict1(unsigned long n)
|
||||
test_dict1(size_t n)
|
||||
{
|
||||
map<unsigned long, unsigned long> dict;
|
||||
|
||||
@@ -93,7 +93,7 @@ test_dict1(unsigned long n)
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict2(unsigned long n)
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
unordered_map<unsigned long, unsigned long> dict;
|
||||
|
||||
@@ -131,7 +131,7 @@ namespace std {
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
unordered_map<char_array_s, char_array_s> dict;
|
||||
|
||||
@@ -155,19 +155,19 @@ test_dict_big(unsigned long n)
|
||||
|
||||
/********************************************************************************************/
|
||||
static void
|
||||
test_dict_str(unsigned long n)
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
unordered_map<string, string> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s2 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s1 = to_string(rand_get());
|
||||
string s2 = to_string(rand_get());
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s1 = to_string (rand_get());
|
||||
unordered_map<string, string>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
@@ -176,19 +176,19 @@ test_dict_str(unsigned long n)
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_str2(unsigned long n)
|
||||
test_dict_str2(size_t n)
|
||||
{
|
||||
map<string, string> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s2 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s1 = to_string(rand_get());
|
||||
string s2 = to_string(rand_get());
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s1 = to_string(rand_get());
|
||||
map<string, string>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
@@ -208,6 +208,16 @@ static void test_sort(size_t n)
|
||||
g_result = a1[0];
|
||||
}
|
||||
|
||||
static void test_stable_sort(size_t n)
|
||||
{
|
||||
vector<float> a1;
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
a1.push_back(rand_get() );
|
||||
}
|
||||
stable_sort(a1.begin(), a1.end());
|
||||
g_result = a1[0];
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
@@ -231,5 +241,7 @@ int main(int argc, const char *argv[])
|
||||
test_function("DictS(m) time", 1000000, test_dict_str2);
|
||||
if (n == 50)
|
||||
test_function("Sort time", 10000000, test_sort);
|
||||
if (n == 51)
|
||||
test_function("Stable Sort time", 10000000, test_stable_sort);
|
||||
exit(0);
|
||||
}
|
||||
+269
-49
@@ -35,7 +35,15 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Update of integrating M*LIB done by Patrick Pelissier */
|
||||
/* Update of integrating M*LIB, LIBSRT & SDS done by Patrick Pelissier */
|
||||
|
||||
/*
|
||||
* Benchmark based on the features tested by benchmark seen here:
|
||||
* http://www.utilitycode.com/str/performance.aspx
|
||||
*
|
||||
* The idea is to measure the performance empty constructors, char *
|
||||
* constructors, assignment, concatenation and scanning.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
@@ -56,53 +64,52 @@
|
||||
#include "sstring.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Benchmark based on the features tested by benchmark seen here:
|
||||
* http://www.utilitycode.com/str/performance.aspx
|
||||
*
|
||||
* The idea is to measure the performance empty constructors, char *
|
||||
* constructors, assignment, concatenation and scanning. But rather than
|
||||
* comparing MFC's CString to Str Library, we are going to compare STL to
|
||||
* CBString.
|
||||
*
|
||||
* The results are interesting. CBString basically beats std::string quite
|
||||
* severely, except for the empty constructor.
|
||||
*/
|
||||
#ifdef BENCH_CAN_USE_SDS
|
||||
extern "C" {
|
||||
#include "sds.h"
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined (BENCH_CAN_USE_STL)
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
#if defined (BENCH_CAN_USE_RAPIDSTRING)
|
||||
#define NDEBUG
|
||||
#include "rapidstring.h"
|
||||
#endif
|
||||
|
||||
#define TEST_SECONDS (5)
|
||||
|
||||
int timeTest (double &res, int (*testfn) (int count), int count) {
|
||||
clock_t t0, t1;
|
||||
int p = 0;
|
||||
double x, c = 0;
|
||||
|
||||
t0 = clock ();
|
||||
|
||||
do {
|
||||
p += testfn (count);
|
||||
t1 = clock ();
|
||||
|
||||
c += count;
|
||||
if (t1 == t0) {
|
||||
if (count < (INT_MAX / 2)) count += count;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t1 - t0 >= TEST_SECONDS*CLOCKS_PER_SEC) break;
|
||||
|
||||
x = (TEST_SECONDS*CLOCKS_PER_SEC - (t1 - t0)) * c / (t1 - t0);
|
||||
|
||||
if (x > INT_MAX) x = INT_MAX;
|
||||
count = (int) x;
|
||||
if (count < 1000) count = 1000;
|
||||
|
||||
} while (1);
|
||||
|
||||
res = c * CLOCKS_PER_SEC / (t1 - t0);
|
||||
return p;
|
||||
clock_t t0, t1;
|
||||
int p = 0;
|
||||
double x, c = 0;
|
||||
|
||||
t0 = clock ();
|
||||
|
||||
do {
|
||||
p += testfn (count);
|
||||
t1 = clock ();
|
||||
|
||||
c += count;
|
||||
if (t1 == t0) {
|
||||
if (count < (INT_MAX / 2)) count += count;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t1 - t0 >= TEST_SECONDS*CLOCKS_PER_SEC) break;
|
||||
|
||||
x = (TEST_SECONDS*CLOCKS_PER_SEC - (t1 - t0)) * c / (t1 - t0);
|
||||
|
||||
if (x > INT_MAX) x = INT_MAX;
|
||||
count = (int) x;
|
||||
if (count < 1000) count = 1000;
|
||||
|
||||
} while (1);
|
||||
|
||||
res = c * CLOCKS_PER_SEC / (t1 - t0);
|
||||
return p;
|
||||
}
|
||||
|
||||
#define TESTSTRING1 ("<sometag name=\"John Doe\" position=\"Executive VP Marketing\"/>")
|
||||
@@ -280,7 +287,7 @@ int testCBS_replace (int count) {
|
||||
int testSRT_emptyCtor (int count) {
|
||||
int i, c = 0;
|
||||
for (c=i=0; i < count; i++) {
|
||||
ss_t *b = ss_alloc(0);
|
||||
srt_string *b = ss_alloc(0);
|
||||
c += ss_size(b) ^i;
|
||||
ss_free(&b);
|
||||
}
|
||||
@@ -290,7 +297,7 @@ int testSRT_emptyCtor (int count) {
|
||||
int testSRT_nonemptyCtor (int count) {
|
||||
int i, c = 0;
|
||||
for (c=i=0; i < count; i++) {
|
||||
ss_t *b = ss_dup_c(TESTSTRING1);
|
||||
srt_string *b = ss_dup_c(TESTSTRING1);
|
||||
c += ss_size(b) ^i;
|
||||
ss_free(&b);
|
||||
}
|
||||
@@ -299,7 +306,7 @@ int testSRT_nonemptyCtor (int count) {
|
||||
|
||||
int testSRT_cstrAssignment (int count) {
|
||||
int i, c = 0;
|
||||
ss_t *b = ss_alloc(0);
|
||||
srt_string *b = ss_alloc(0);
|
||||
for (c=i=0; i < count; i++) {
|
||||
ss_cpy_cn(&b, TESTSTRING1, strlen(TESTSTRING1));
|
||||
c += ss_size(b) ^i;
|
||||
@@ -310,7 +317,7 @@ int testSRT_cstrAssignment (int count) {
|
||||
|
||||
int testSRT_extraction (int count) {
|
||||
int i, c = 0;
|
||||
ss_t *b = ss_dup_c(TESTSTRING1);
|
||||
srt_string *b = ss_dup_c(TESTSTRING1);
|
||||
for (c=i=0; i < count; i++) {
|
||||
c += ss_at(b,(i & 7));
|
||||
c += ss_at(b,(i & 7) ^ 8);
|
||||
@@ -322,7 +329,7 @@ int testSRT_extraction (int count) {
|
||||
|
||||
int testSRT_scan (int count) {
|
||||
int i, c = 0;
|
||||
ss_t *b = ss_dup_c("Dot. 123. Some more data.");
|
||||
srt_string *b = ss_dup_c("Dot. 123. Some more data.");
|
||||
for (c=i=0; i < count; i++) {
|
||||
c += ss_findc (b, 0, '.');
|
||||
c += ss_find_cn (b, 0, "123", strlen("123"));
|
||||
@@ -336,8 +343,8 @@ int testSRT_scan (int count) {
|
||||
|
||||
int testSRT_concat (int count) {
|
||||
int i, j, c = 0;
|
||||
ss_t *a = ss_dup_c(TESTSTRING1);
|
||||
ss_t *accum = ss_alloc(0);
|
||||
srt_string *a = ss_dup_c(TESTSTRING1);
|
||||
srt_string *accum = ss_alloc(0);
|
||||
|
||||
for (j=0; j < count; j++) {
|
||||
ss_cpy_c(&accum, "");
|
||||
@@ -455,6 +462,168 @@ int testMLIB_replace (int count) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BENCH_CAN_USE_SDS
|
||||
int testSDS_emptyCtor (int count) {
|
||||
int i, c = 0;
|
||||
for (c=i=0; i < count; i++) {
|
||||
sds b;
|
||||
b = sdsempty();
|
||||
c += sdslen(b) ^ i;
|
||||
sdsfree(b);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int testSDS_nonemptyCtor (int count) {
|
||||
int i, c = 0;
|
||||
for (c=i=0; i < count; i++) {
|
||||
sds b;
|
||||
b = sdsnew(TESTSTRING1);
|
||||
c += sdslen(b) ^i;
|
||||
sdsfree(b);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int testSDS_cstrAssignment (int count) {
|
||||
int i, c = 0;
|
||||
sds b;
|
||||
b = sdsempty();
|
||||
for (c=i=0; i < count; i++) {
|
||||
b = sdscpy(b, TESTSTRING1);
|
||||
c += sdslen(b) ^i;
|
||||
}
|
||||
sdsfree(b);
|
||||
return c;
|
||||
}
|
||||
|
||||
int testSDS_extraction (int count) {
|
||||
int i, c = 0;
|
||||
|
||||
sds b;
|
||||
b = sdsnew(TESTSTRING1);
|
||||
|
||||
for (c=i=0; i < count; i++) {
|
||||
c += b[(i & 7)];
|
||||
c += b[(i & 7) ^ 8];
|
||||
c += b[(i & 7) ^ 4] ^i;
|
||||
}
|
||||
sdsfree(b);
|
||||
return c;
|
||||
}
|
||||
|
||||
int testSDS_scan (int count) {
|
||||
int i, c = 0;
|
||||
sds b;
|
||||
b = sdsnew("Dot. 123. Some more data.");
|
||||
|
||||
for (c=i=0; i < count; i++) {
|
||||
c += (intptr_t) strchr(b, '.');
|
||||
c += (intptr_t) strstr (b, "123");
|
||||
c += (intptr_t) strpbrk (b, "sm") ^i;
|
||||
}
|
||||
sdsfree(b);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
int testSDS_concat (int count) {
|
||||
int i, j, c = 0;
|
||||
sds a, accum;
|
||||
a = sdsnew(TESTSTRING1);
|
||||
accum = sdsempty();
|
||||
|
||||
for (j=0; j < count; j++) {
|
||||
accum = sdscpy(accum, "");
|
||||
for (i=0; i < 250; i++) {
|
||||
accum = sdscat(accum, a);
|
||||
accum = sdscat(accum, "!!");
|
||||
c += sdslen(accum) ^i;
|
||||
}
|
||||
}
|
||||
sdsfree(a);
|
||||
sdsfree(accum);
|
||||
return c;
|
||||
}
|
||||
|
||||
// Not found
|
||||
//int testSDS_replace (int count) {
|
||||
#endif
|
||||
|
||||
#ifdef BENCH_CAN_USE_RAPIDSTRING
|
||||
int testRAPIDSTRING_emptyCtor (int count) {
|
||||
int i, c = 0;
|
||||
for (c=i=0; i < count; i++) {
|
||||
rapidstring b;
|
||||
rs_init(&b);
|
||||
c += rs_len(&b) ^i;
|
||||
rs_free(&b);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int testRAPIDSTRING_nonemptyCtor (int count) {
|
||||
int i, c = 0;
|
||||
for (c=i=0; i < count; i++) {
|
||||
rapidstring b;
|
||||
rs_init_w(&b, TESTSTRING1);
|
||||
c += rs_len(&b) ^i;
|
||||
rs_free(&b);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int testRAPIDSTRING_cstrAssignment (int count) {
|
||||
int i, c = 0;
|
||||
rapidstring b;
|
||||
rs_init(&b);
|
||||
for (c=i=0; i < count; i++) {
|
||||
rs_cpy(&b, TESTSTRING1);
|
||||
c += rs_len(&b) ^i;
|
||||
}
|
||||
rs_free(&b);
|
||||
return c;
|
||||
}
|
||||
|
||||
int testRAPIDSTRING_extraction (int count) {
|
||||
int i, c = 0;
|
||||
|
||||
rapidstring b;
|
||||
rs_init_w(&b, TESTSTRING1);
|
||||
|
||||
for (c=i=0; i < count; i++) {
|
||||
c += rs_data_c(&b)[i & 7];
|
||||
c += rs_data_c(&b)[(i & 7) ^ 8];
|
||||
c += rs_data_c(&b)[(i & 7) ^ 4] ^i;
|
||||
}
|
||||
rs_free(&b);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int testRAPIDSTRING_concat (int count) {
|
||||
int i, j, c = 0;
|
||||
rapidstring a, accum;
|
||||
rs_init_w(&a, TESTSTRING1);
|
||||
rs_init (&accum);
|
||||
|
||||
for (j=0; j < count; j++) {
|
||||
rs_cpy(&accum, "");
|
||||
for (i=0; i < 250; i++) {
|
||||
rs_cat_rs(&accum, &a);
|
||||
rs_cat(&accum, "!!");
|
||||
c += rs_len(&accum) ^i;
|
||||
}
|
||||
}
|
||||
rs_free(&a);
|
||||
rs_free(&accum);
|
||||
return c;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#define NTESTS 7
|
||||
struct flags {
|
||||
int runtest[NTESTS];
|
||||
@@ -583,7 +752,58 @@ int benchTest (const struct flags * runflags) {
|
||||
printf ("M*LIB string replace: %20.1f per second\n", cps);
|
||||
}
|
||||
#endif
|
||||
return c;
|
||||
|
||||
#ifdef BENCH_CAN_USE_SDS
|
||||
if (runflags->runtest[0]) {
|
||||
c += timeTest (cps, testSDS_emptyCtor, 100000);
|
||||
printf ("SDS string empty constructor: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[1]) {
|
||||
c += timeTest (cps, testSDS_nonemptyCtor, 100000);
|
||||
printf ("SDS string non-empty constructor: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[2]) {
|
||||
c += timeTest (cps, testSDS_cstrAssignment, 100000);
|
||||
printf ("SDS string char * assignment: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[3]) {
|
||||
c += timeTest (cps, testSDS_extraction, 100000);
|
||||
printf ("SDS string char extraction: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[4]) {
|
||||
c += timeTest (cps, testSDS_scan, 100000);
|
||||
printf ("SDS string scan: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[5]) {
|
||||
c += timeTest (cps, testSDS_concat, 10);
|
||||
printf ("SDS string concatenation: %20.1f per second\n", cps * 250);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BENCH_CAN_USE_RAPIDSTRING
|
||||
if (runflags->runtest[0]) {
|
||||
c += timeTest (cps, testRAPIDSTRING_emptyCtor, 100000);
|
||||
printf ("RAPIDSTRING empty constructor: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[1]) {
|
||||
c += timeTest (cps, testRAPIDSTRING_nonemptyCtor, 100000);
|
||||
printf ("RAPIDSTRING non-empty constructor: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[2]) {
|
||||
c += timeTest (cps, testRAPIDSTRING_cstrAssignment, 100000);
|
||||
printf ("RAPIDSTRING char * assignment: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[3]) {
|
||||
c += timeTest (cps, testRAPIDSTRING_extraction, 100000);
|
||||
printf ("RAPIDSTRING char extraction: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[5]) {
|
||||
c += timeTest (cps, testRAPIDSTRING_concat, 10);
|
||||
printf ("RAPIDSTRING concatenation: %20.1f per second\n", cps * 250);
|
||||
}
|
||||
#endif
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
int main (int argc, char * argv[]) {
|
||||
|
||||
@@ -124,7 +124,7 @@ static int equal_func(const void* arg, const void *obj_p)
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict(unsigned long n)
|
||||
test_dict(size_t n)
|
||||
{
|
||||
tommy_hashlin dict;
|
||||
tommy_hashlin_init(&dict);
|
||||
@@ -174,7 +174,7 @@ static size_t char_hash(const char *s)
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
tommy_hashlin dict;
|
||||
tommy_hashlin_init(&dict);
|
||||
|
||||
@@ -85,7 +85,7 @@ struct hash_i_node_s {
|
||||
};
|
||||
|
||||
static void
|
||||
test_dict(unsigned long n)
|
||||
test_dict(size_t n)
|
||||
{
|
||||
struct hash_i_node_s *dict = NULL;
|
||||
|
||||
@@ -131,7 +131,7 @@ struct hash_b_node_s {
|
||||
};
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
struct hash_b_node_s *dict = NULL;
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "xxhash.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
static unsigned long *g_p;
|
||||
|
||||
static void test_hash_prepare(size_t n)
|
||||
{
|
||||
g_p = malloc (n * sizeof(unsigned long));
|
||||
if (g_p == NULL) abort();
|
||||
for(size_t i = 0; i < n ; i++)
|
||||
g_p[i] = ((rand_get()*RAND_MAX + rand_get())*RAND_MAX+rand_get())*RAND_MAX + rand_get();
|
||||
}
|
||||
|
||||
|
||||
static void test_hash_final(void)
|
||||
{
|
||||
free(g_p);
|
||||
}
|
||||
|
||||
static void test_hash(size_t n)
|
||||
{
|
||||
g_result = XXH64(g_p, sizeof(unsigned long)*n, 0);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 70) {
|
||||
n = (argc > 2) ? atoi(argv[2]) : 100000000;
|
||||
test_hash_prepare(n);
|
||||
test_function("XXHASH64 time", n, test_hash);
|
||||
test_hash_final();
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - common functions for bench module
|
||||
*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* MLIB - TEST module
|
||||
*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
CC=cc -std=c11
|
||||
CXX=c++ -std=c++11
|
||||
CFLAGS=-O2 -march=native -Wall
|
||||
CPPFLAGS=-I../..
|
||||
|
||||
all: k-nucleotide
|
||||
|
||||
k-nucleotide: k-nucleotide.exe data.txt
|
||||
./k-nucleotide.exe < data.txt
|
||||
|
||||
k-nucleotide.exe: nucleotide.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) nucleotide.c -o k-nucleotide.exe -lpthread
|
||||
|
||||
data.txt: fasta.exe
|
||||
./fasta.exe 25000000 > data.txt
|
||||
|
||||
fasta.exe: fasta.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -fopenmp fasta.c -o fasta.exe
|
||||
|
||||
clean:
|
||||
rm -f *.exe *~ *.o data.txt
|
||||
|
||||
@@ -0,0 +1,344 @@
|
||||
// The Computer Language Benchmarks Game
|
||||
// http://benchmarksgame.alioth.debian.org/
|
||||
//
|
||||
// contributed by Jeremy Zerfas
|
||||
// rewritten by Аноним Легионов, inspired by fasta Rust #2 program
|
||||
// use two OpenMP locks instead of one critical section
|
||||
// decouples IO activity from random number generation
|
||||
//
|
||||
// modified by Josh Goldfoot, adding use of a buffer for fasta_repeat
|
||||
|
||||
|
||||
// This controls the width of lines that are output by this program.
|
||||
#define MAXIMUM_LINE_WIDTH 60
|
||||
|
||||
// This program will generate the random nucleotide sequences in parallel which
|
||||
// are worked on in blocks of lines. The number of lines in those blocks is
|
||||
// controlled by this setting.
|
||||
#define LINES_PER_BLOCK 1024
|
||||
|
||||
#define CHARACTERS_PER_BLOCK (MAXIMUM_LINE_WIDTH*LINES_PER_BLOCK)
|
||||
|
||||
#define THREADS_TO_USE 4
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
// intptr_t should be the native integer type on most sane systems.
|
||||
typedef intptr_t intnative_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char letter;
|
||||
float probability;
|
||||
} nucleotide_info;
|
||||
|
||||
|
||||
// Repeatedly print string_To_Repeat until it has printed
|
||||
// number_Of_Characters_To_Create. The output is also wrapped to
|
||||
// MAXIMUM_LINE_WIDTH columns.
|
||||
static char* make_Sequence_Buffer(const char string_To_Repeat[])
|
||||
{
|
||||
const intnative_t string_To_Repeat_Length = strlen(string_To_Repeat);
|
||||
|
||||
// JG: Changed to that this writes a sequence to a buffer, which is used
|
||||
intnative_t number_Of_Characters_To_Create = string_To_Repeat_Length * MAXIMUM_LINE_WIDTH;
|
||||
char* buffer = (char*) malloc(number_Of_Characters_To_Create +
|
||||
number_Of_Characters_To_Create / MAXIMUM_LINE_WIDTH + 1);
|
||||
if (buffer == NULL)
|
||||
exit(-1);
|
||||
char* bufferOffset = buffer;
|
||||
|
||||
// Create an extended_String_To_Repeat which is a copy of string_To_Repeat
|
||||
// but extended with another copy of the first MAXIMUM_LINE_WIDTH characters
|
||||
// of string_To_Repeat appended to the end. Later on this allows us to
|
||||
// generate a line of output just by doing simple memory copies using an
|
||||
// appropriate offset into extended_String_To_Repeat.
|
||||
char extended_String_To_Repeat[string_To_Repeat_Length + MAXIMUM_LINE_WIDTH];
|
||||
for (intnative_t column = 0; column<string_To_Repeat_Length + MAXIMUM_LINE_WIDTH;
|
||||
column++)
|
||||
extended_String_To_Repeat[column] =
|
||||
string_To_Repeat[column%string_To_Repeat_Length];
|
||||
intnative_t offset = 0;
|
||||
|
||||
char line[MAXIMUM_LINE_WIDTH + 1];
|
||||
line[MAXIMUM_LINE_WIDTH] = '\n';
|
||||
|
||||
for (intnative_t current_Number_Of_Characters_To_Create =
|
||||
number_Of_Characters_To_Create;
|
||||
current_Number_Of_Characters_To_Create>0;)
|
||||
{
|
||||
// Figure out the length of the line we need to write. If it's less than
|
||||
// MAXIMUM_LINE_WIDTH then we also need to add a line feed in the right
|
||||
// spot too.
|
||||
intnative_t line_Length = MAXIMUM_LINE_WIDTH;
|
||||
if (current_Number_Of_Characters_To_Create<MAXIMUM_LINE_WIDTH)
|
||||
{
|
||||
line_Length = current_Number_Of_Characters_To_Create;
|
||||
line[line_Length] = '\n';
|
||||
}
|
||||
|
||||
memcpy(line, extended_String_To_Repeat + offset, line_Length);
|
||||
|
||||
// Update the offset, reducing it by string_To_Repeat_Length if
|
||||
// necessary.
|
||||
offset += line_Length;
|
||||
if (offset>string_To_Repeat_Length)
|
||||
offset -= string_To_Repeat_Length;
|
||||
|
||||
// "Output" that line to our buffer and update the
|
||||
// current_Number_Of_Characters_To_Create.
|
||||
memcpy(bufferOffset, line, line_Length + 1); // JG: used to be fwrite(line, line_Length + 1, 1, stdout);
|
||||
bufferOffset += line_Length + 1;
|
||||
current_Number_Of_Characters_To_Create -= line_Length;
|
||||
}
|
||||
*bufferOffset = 0;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void repeat_And_Wrap_String(const char string_To_Repeat[], intnative_t number_Of_Characters_To_Create)
|
||||
{
|
||||
/* JG: fasta_repeat repeats every len(alu) * line-length = 287 * 61 = 17507 characters.
|
||||
So, calculate this once, then just print that buffer over and over. */
|
||||
char* sequence = make_Sequence_Buffer(string_To_Repeat);
|
||||
intnative_t sequenceLen = (intnative_t) strlen(sequence);
|
||||
intnative_t outputBytes = number_Of_Characters_To_Create + number_Of_Characters_To_Create / 60;
|
||||
while (outputBytes >= sequenceLen) {
|
||||
fwrite(sequence, sequenceLen, 1, stdout);
|
||||
outputBytes -= sequenceLen;
|
||||
}
|
||||
if (outputBytes > 0) {
|
||||
fwrite(sequence, outputBytes, 1, stdout);
|
||||
printf("\n");
|
||||
}
|
||||
free(sequence);
|
||||
}
|
||||
|
||||
|
||||
// Generate a pseudorandom number from 0 to max using a linear
|
||||
// congruential generator.
|
||||
#define IM 139968
|
||||
#define IA 3877
|
||||
#define IC 29573
|
||||
uint32_t seed = 42;
|
||||
|
||||
int rng_tid; //Thread ID
|
||||
int rng_tnum = 1; //Thread number
|
||||
intnative_t rng_cnt = 0;
|
||||
|
||||
#ifdef _OPENMP
|
||||
omp_lock_t rng_lock;
|
||||
#define RNG_LOCK_INIT() omp_init_lock(&rng_lock)
|
||||
#define RNG_LOCK() omp_set_lock(&rng_lock)
|
||||
#define RNG_FREE() omp_unset_lock(&rng_lock)
|
||||
#else
|
||||
#define RNG_LOCK_INIT() do{}while(0)
|
||||
#define RNG_LOCK() do{}while(0)
|
||||
#define RNG_FREE() do{}while(0)
|
||||
#endif
|
||||
|
||||
static void rng_init(void)
|
||||
{
|
||||
RNG_LOCK_INIT();
|
||||
rng_tid = 0;
|
||||
}
|
||||
|
||||
static intnative_t rng_gen_blk(uint32_t * buf, intnative_t len, int curr_tid)
|
||||
{
|
||||
intnative_t gen_cnt = -1;//Error by default
|
||||
RNG_LOCK();
|
||||
|
||||
if (rng_tid == curr_tid)
|
||||
{
|
||||
if (++rng_tid >= rng_tnum)
|
||||
{
|
||||
rng_tid = 0;
|
||||
}
|
||||
|
||||
gen_cnt = (len<rng_cnt) ? len : rng_cnt;
|
||||
rng_cnt -= gen_cnt;
|
||||
|
||||
len = gen_cnt;
|
||||
|
||||
while (0 != len--)
|
||||
{
|
||||
seed = (seed*IA + IC) % IM;
|
||||
*(buf++) = seed;//This is stupid actually!
|
||||
}
|
||||
}
|
||||
|
||||
RNG_FREE();
|
||||
return gen_cnt;
|
||||
}
|
||||
|
||||
int out_tid; //Thread ID
|
||||
int out_tnum = 1; //Thread number
|
||||
#ifdef _OPENMP
|
||||
omp_lock_t out_lock;
|
||||
#define OUT_LOCK_INIT() omp_init_lock(&out_lock)
|
||||
#define OUT_LOCK() omp_set_lock(&out_lock)
|
||||
#define OUT_FREE() omp_unset_lock(&out_lock)
|
||||
#else
|
||||
#define OUT_LOCK_INIT() do{}while(0)
|
||||
#define OUT_LOCK() do{}while(0)
|
||||
#define OUT_FREE() do{}while(0)
|
||||
#endif
|
||||
|
||||
static void out_init(void)
|
||||
{
|
||||
OUT_LOCK_INIT();
|
||||
rng_tid = 0;
|
||||
}
|
||||
|
||||
static intnative_t out_write(char * buf, intnative_t len, int curr_tid)
|
||||
{
|
||||
intnative_t wr_cnt = -1;//Error by default
|
||||
OUT_LOCK();
|
||||
|
||||
if (out_tid == curr_tid)
|
||||
{
|
||||
if (++out_tid >= out_tnum)
|
||||
{
|
||||
out_tid = 0;
|
||||
}
|
||||
wr_cnt = fwrite(buf, len, 1, stdout);
|
||||
}
|
||||
|
||||
OUT_FREE();
|
||||
return wr_cnt; //-1 - thread error, 0 - IO error, 1 - ОК
|
||||
}
|
||||
|
||||
static void generate_And_Wrap_Pseudorandom_DNA_Sequence(
|
||||
const nucleotide_info nucl_info[],
|
||||
const intnative_t nucl_num,
|
||||
const intnative_t char_num)
|
||||
{
|
||||
uint32_t cumul_p[nucl_num];
|
||||
float cumul_acc = 0.0;
|
||||
|
||||
for (intnative_t i = 0; i<nucl_num; i++)
|
||||
{
|
||||
cumul_acc += nucl_info[i].probability;
|
||||
cumul_p[i] = 1ul + (uint32_t)(cumul_acc*(float)IM); //Compensate rounding errors on test file
|
||||
}
|
||||
|
||||
#ifdef _OPENMP
|
||||
intnative_t tnum = omp_get_num_procs();
|
||||
if (tnum>THREADS_TO_USE) tnum = THREADS_TO_USE;
|
||||
omp_set_num_threads(tnum);
|
||||
rng_tnum = tnum;
|
||||
out_tnum = tnum;
|
||||
#endif
|
||||
rng_tid = 0;
|
||||
out_tid = 0;
|
||||
rng_cnt = char_num;
|
||||
|
||||
#pragma omp parallel
|
||||
{
|
||||
char block[CHARACTERS_PER_BLOCK + LINES_PER_BLOCK];
|
||||
char * line;
|
||||
uint32_t rnd[CHARACTERS_PER_BLOCK], r;
|
||||
intnative_t cnt, col, prid, nid, ncnt;
|
||||
int cur_tid;
|
||||
#ifdef _OPENMP
|
||||
cur_tid = omp_get_thread_num();
|
||||
#else
|
||||
cur_tid = 0;
|
||||
#endif
|
||||
while (1)
|
||||
{
|
||||
do
|
||||
{
|
||||
cnt = rng_gen_blk(rnd, CHARACTERS_PER_BLOCK, cur_tid);
|
||||
} while (-1 == cnt);
|
||||
|
||||
if (0 == cnt)
|
||||
{
|
||||
break;//Work finished!
|
||||
}
|
||||
|
||||
line = block;
|
||||
|
||||
for (col = 0, prid = 0; prid < cnt; prid++)
|
||||
{
|
||||
r = rnd[prid];
|
||||
|
||||
ncnt = 0;
|
||||
for (nid = 0; nid < nucl_num; nid++)
|
||||
{
|
||||
if (cumul_p[nid] <= r)
|
||||
{
|
||||
ncnt++;
|
||||
}
|
||||
}
|
||||
|
||||
*line++ = nucl_info[ncnt].letter;
|
||||
if (++col >= MAXIMUM_LINE_WIDTH)
|
||||
{
|
||||
col = 0;
|
||||
*line++ = '\n';
|
||||
}
|
||||
}
|
||||
//Check if we need to end the line
|
||||
if (0 != col)
|
||||
{
|
||||
//Last iteration didn't end the line, so finish the job.
|
||||
*line++ = '\n';
|
||||
}
|
||||
//Print results
|
||||
do
|
||||
{
|
||||
cnt = out_write(block, line - block, cur_tid);
|
||||
} while (-1 == cnt);
|
||||
//Check fot IO error
|
||||
if (0 == cnt)
|
||||
{
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
const intnative_t n = atoi(argv[1]);
|
||||
|
||||
fputs(">ONE Homo sapiens alu\n", stdout);
|
||||
const char homo_Sapiens_Alu[] =
|
||||
"GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTC"
|
||||
"AGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCG"
|
||||
"TGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGG"
|
||||
"AGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA";
|
||||
repeat_And_Wrap_String(homo_Sapiens_Alu, 2 * n);
|
||||
|
||||
rng_init();
|
||||
out_init();
|
||||
|
||||
fputs(">TWO IUB ambiguity codes\n", stdout);
|
||||
nucleotide_info iub_Nucleotides_Information[] =
|
||||
{
|
||||
{ 'a', 0.27 },{ 'c', 0.12 },{ 'g', 0.12 },{ 't', 0.27 },{ 'B', 0.02 },
|
||||
{ 'D', 0.02 },{ 'H', 0.02 },{ 'K', 0.02 },{ 'M', 0.02 },{ 'N', 0.02 },
|
||||
{ 'R', 0.02 },{ 'S', 0.02 },{ 'V', 0.02 },{ 'W', 0.02 },{ 'Y', 0.02 }
|
||||
};
|
||||
generate_And_Wrap_Pseudorandom_DNA_Sequence(iub_Nucleotides_Information,
|
||||
sizeof(iub_Nucleotides_Information) / sizeof(nucleotide_info), 3 * n);
|
||||
|
||||
fputs(">THREE Homo sapiens frequency\n", stdout);
|
||||
nucleotide_info homo_Sapien_Nucleotides_Information[] =
|
||||
{
|
||||
{ 'a', 0.3029549426680 },{ 'c', 0.1979883004921 },
|
||||
{ 'g', 0.1975473066391 },{ 't', 0.3015094502008 }
|
||||
};
|
||||
generate_And_Wrap_Pseudorandom_DNA_Sequence(homo_Sapien_Nucleotides_Information,
|
||||
sizeof(homo_Sapien_Nucleotides_Information) / sizeof(nucleotide_info), 5 * n);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
// The Computer Language Benchmarks Game
|
||||
// http://benchmarksgame.alioth.debian.org/
|
||||
//
|
||||
// Original version Contributed by Jeremy Zerfas
|
||||
// M*LIB port contributed by Patrick Pélissier
|
||||
#define NDEBUG
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "m-worker.h"
|
||||
#include "m-dict.h"
|
||||
#include "m-bptree.h"
|
||||
|
||||
// This controls the maximum length for each set of oligonucleotide frequencies
|
||||
// and each oligonucleotide count output by this program.
|
||||
#define MAXIMUM_OUTPUT_LENGTH 4096
|
||||
|
||||
// Number of outputs to display
|
||||
#define MAXMUM_NUMBER_OUTPUT 7
|
||||
|
||||
/* Define the needed containers */
|
||||
/* Define Open Addressing dictionary of uint64_t --> uint32_t. Need to define how to handle out of bound data. */
|
||||
static inline bool my_oor_equal_p(uint64_t k, unsigned char n)
|
||||
{
|
||||
return k == ~(uint64_t) n;
|
||||
}
|
||||
static inline void my_oor_set(uint64_t *k, unsigned char n)
|
||||
{
|
||||
*k = ~(uint64_t)n;
|
||||
}
|
||||
static inline size_t my_hash(uint64_t key)
|
||||
{
|
||||
return ((key) ^ (key)>>7);
|
||||
}
|
||||
static inline void update_val(uint32_t *p, uint32_t v) { *p += v;}
|
||||
DICT_OA_DEF2(dict_oligonucleotide,
|
||||
uint64_t, M_OPEXTEND(M_DEFAULT_OPLIST, HASH(my_hash), OOR_EQUAL(my_oor_equal_p), OOR_SET(my_oor_set M_IPTR)),
|
||||
uint32_t, M_OPEXTEND(M_DEFAULT_OPLIST, UPDATE(update_val M_IPTR)))
|
||||
#define M_OPL_dict_oligonucleotide_t() DICT_OPLIST(dict_oligonucleotide, M_OPEXTEND(M_DEFAULT_OPLIST, HASH(my_hash), OOR_EQUAL(my_oor_equal_p), OOR_SET(my_oor_set M_IPTR)), M_DEFAULT_OPLIST)
|
||||
|
||||
// Define a B*TREE-21 of uint32_t --> uint64_t and register its oplist
|
||||
#define TREE_CMP(a,b) ((a) > (b) ? -1 : (a) < (b)) /* reverse natural order */
|
||||
BPTREE_DEF2(tree_dict_oligonucleotide, 21, uint32_t, M_OPEXTEND(M_DEFAULT_OPLIST, CMP(TREE_CMP)), uint64_t, M_DEFAULT_OPLIST)
|
||||
#define M_OPL_tree_dict_oligonucleotide_t() BPTREE_OPLIST2(tree_dict_oligonucleotide, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
/* Define the data to analyse (a plain array) */
|
||||
ARRAY_DEF(polynucleotide, unsigned char)
|
||||
|
||||
// Macro to convert a nucleotide character to a code. Note that upper and lower
|
||||
// case ASCII letters only differ in the fifth bit from the right and we only
|
||||
// need the three least significant bits to differentiate the letters 'A', 'C',
|
||||
// 'G', and 'T'. Spaces in this array/string will never be used as long as
|
||||
// characters other than 'A', 'C', 'G', and 'T' aren't used.
|
||||
#define CODE_FOR_NUCLEOTIDE(nucleotide) (" \0 \1\3 \2"[nucleotide & 0x7])
|
||||
|
||||
// And one more macro to convert the codes back to nucleotide characters.
|
||||
#define NUCLEOTIDE_FOR_CODE(code) ("ACGT"[code & 0x3])
|
||||
|
||||
static worker_t worker;
|
||||
|
||||
// Read from the given stream the data and fill in the array with the converted one.
|
||||
static void polynucleotide_init_stream(polynucleotide_t p, FILE *stream)
|
||||
{
|
||||
char buffer[MAXIMUM_OUTPUT_LENGTH], c;
|
||||
|
||||
polynucleotide_init(p);
|
||||
polynucleotide_reserve(p, MAXIMUM_OUTPUT_LENGTH);
|
||||
// Find the start of the third polynucleotide.
|
||||
while(fgets(buffer, sizeof(buffer), stream) && memcmp(">THREE", buffer, 6));
|
||||
// Start reading and encoding the third polynucleotide.
|
||||
while(fgets(buffer, sizeof(buffer), stream) && buffer[0]!='>'){
|
||||
for(size_t i = 0; (c = buffer[i]) !='\0'; i++) {
|
||||
if (c != '\n') {
|
||||
polynucleotide_push_back(p, CODE_FOR_NUCLEOTIDE(c));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is the function which does 95% of the work.
|
||||
/* NOTE: paralelisation is not done by cutting the input set in two.
|
||||
This is not efficient as the size of the output table will be the same
|
||||
as for the whole table. */
|
||||
static void init_hash(dict_oligonucleotide_t hash_Table, const size_t size, const unsigned char tab[size], const size_t desiredLength, const size_t offset, const size_t skip)
|
||||
{
|
||||
uint64_t key = 0;
|
||||
const uint64_t mask = (1ULL<<(2*desiredLength))-1;
|
||||
assert (2*desiredLength < 64 && desiredLength < size);
|
||||
assert ((1 == skip || 4 == skip) && offset < size);
|
||||
|
||||
// For the first several nucleotides we only need to append them to key in
|
||||
// preparation for the insertion of complete oligonucleotides to hash.
|
||||
for(size_t i = 0; i < desiredLength - 1 + offset; i++)
|
||||
key = (key<<2) | tab[i];
|
||||
key &= mask;
|
||||
|
||||
dict_oligonucleotide_t hash;
|
||||
dict_oligonucleotide_init(hash);
|
||||
|
||||
// Add all the complete oligonucleotides of
|
||||
// desiredLength to hash, updating their count
|
||||
if (skip == 1) {
|
||||
for(size_t i = desiredLength-1+offset ; i < size; i++) {
|
||||
key = (key<<2) | tab[i+0];
|
||||
key &= mask;
|
||||
uint32_t *p = dict_oligonucleotide_get(hash, key);
|
||||
if (p != NULL) {
|
||||
(*p) ++;
|
||||
} else {
|
||||
dict_oligonucleotide_set_at(hash, key, 1);
|
||||
}
|
||||
}
|
||||
} else { // skip = 4
|
||||
for(size_t i = desiredLength-1+offset ; i < size; i+=4) {
|
||||
for (size_t j = 0; j < 4; j++)
|
||||
key = (key<<2) | tab[i+j];
|
||||
key &= mask;
|
||||
uint32_t *p = dict_oligonucleotide_get(hash, key);
|
||||
if (p != NULL) {
|
||||
(*p) ++;
|
||||
} else {
|
||||
dict_oligonucleotide_set_at(hash, key, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
dict_oligonucleotide_init_move(hash_Table, hash);
|
||||
}
|
||||
|
||||
static void compute_hash(dict_oligonucleotide_t hash_Table, const polynucleotide_t p, const size_t desiredLength)
|
||||
{
|
||||
size_t size = polynucleotide_size(p);
|
||||
unsigned char *tab = polynucleotide_get(p, 0);
|
||||
if (desiredLength < 8) {
|
||||
init_hash(hash_Table, size, tab, desiredLength, 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
worker_sync_t block; // Define a synchronization block
|
||||
worker_start(block, worker);
|
||||
dict_oligonucleotide_t hash_Table3, *p_hash_Table3 = &hash_Table3;
|
||||
dict_oligonucleotide_t hash_Table2, *p_hash_Table2 = &hash_Table2;
|
||||
dict_oligonucleotide_t hash_Table1, *p_hash_Table1 = &hash_Table1;
|
||||
WORKER_SPAWN(block, (p_hash_Table1, size, tab, desiredLength), {
|
||||
init_hash(*p_hash_Table1, size, tab, desiredLength, 3, 4);
|
||||
}, (/*no output*/));
|
||||
WORKER_SPAWN(block, (p_hash_Table2, size, tab, desiredLength), {
|
||||
init_hash(*p_hash_Table2, size, tab, desiredLength, 2, 4);
|
||||
}, (/*no output*/));
|
||||
WORKER_SPAWN(block, (p_hash_Table3, size, tab, desiredLength), {
|
||||
init_hash(*p_hash_Table3, size, tab, desiredLength, 1, 4);
|
||||
}, (/*no output*/));
|
||||
init_hash(hash_Table, size, tab, desiredLength, 0, 4);
|
||||
worker_sync(block);
|
||||
|
||||
// Merge all hashtables
|
||||
dict_oligonucleotide_splice(hash_Table, hash_Table1);
|
||||
dict_oligonucleotide_splice(hash_Table, hash_Table2);
|
||||
dict_oligonucleotide_splice(hash_Table, hash_Table3);
|
||||
// Clear extra hashtables
|
||||
dict_oligonucleotide_clear(hash_Table3);
|
||||
dict_oligonucleotide_clear(hash_Table2);
|
||||
dict_oligonucleotide_clear(hash_Table1);
|
||||
}
|
||||
|
||||
// Generate frequencies for all oligonucleotides in polynucleotide that are of
|
||||
// desiredLength and then save it to output.
|
||||
static void compute_freq(const polynucleotide_t p, const size_t desiredLength, char output[])
|
||||
{
|
||||
dict_oligonucleotide_t hash_Table;
|
||||
compute_hash(hash_Table, p, desiredLength);
|
||||
|
||||
// Order key is the count frequency (inverse value & key)
|
||||
tree_dict_oligonucleotide_t tree;
|
||||
tree_dict_oligonucleotide_init(tree);
|
||||
for M_EACH(item, hash_Table, dict_oligonucleotide_t) {
|
||||
tree_dict_oligonucleotide_set_at (tree, item->value, item->key);
|
||||
}
|
||||
|
||||
// Create Output
|
||||
char oligonucleotide[64];
|
||||
const double invSum = 100.0 / (double) (polynucleotide_size(p) - desiredLength + 1);
|
||||
int output_Position = 0;
|
||||
for M_EACH(item, tree, tree_dict_oligonucleotide_t) {
|
||||
// Convert the key for the oligonucleotide to a string.
|
||||
uint64_t freq = *item->value_ptr;
|
||||
uint32_t count = *item->key_ptr;
|
||||
for(size_t j = desiredLength-1; j < desiredLength; j--){
|
||||
oligonucleotide[j]=NUCLEOTIDE_FOR_CODE(freq);
|
||||
freq>>=2;
|
||||
}
|
||||
oligonucleotide[desiredLength]='\0';
|
||||
// Output the frequency for oligonucleotide to output.
|
||||
output_Position+=snprintf(output+output_Position, MAXIMUM_OUTPUT_LENGTH-output_Position,
|
||||
"%s %.3f\n", oligonucleotide, invSum * count);
|
||||
}
|
||||
|
||||
dict_oligonucleotide_clear(hash_Table);
|
||||
tree_dict_oligonucleotide_clear(tree);
|
||||
}
|
||||
|
||||
// Generate a count for the number of times oligonucleotide appears in
|
||||
// polynucleotide and then save it to output.
|
||||
static void compute_count(polynucleotide_t p, const char oligonucleotide[], char output[])
|
||||
{
|
||||
const size_t oligonucleotide_Length = strlen(oligonucleotide);
|
||||
dict_oligonucleotide_t hash_Table;
|
||||
compute_hash(hash_Table, p, oligonucleotide_Length);
|
||||
|
||||
// Generate the key for oligonucleotide.
|
||||
uint64_t key=0;
|
||||
for(size_t i=0; i<oligonucleotide_Length; i++)
|
||||
key = (key<<2) | CODE_FOR_NUCLEOTIDE(oligonucleotide[i]);
|
||||
|
||||
// Output the count for oligonucleotide to output.
|
||||
uint32_t *val = dict_oligonucleotide_get(hash_Table, key);
|
||||
uintmax_t count= val== NULL ? 0 : *val;
|
||||
snprintf(output, MAXIMUM_OUTPUT_LENGTH, "%ju\t%s", count, oligonucleotide);
|
||||
|
||||
dict_oligonucleotide_clear(hash_Table);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// Initialize the different worker threads by auto-detecting the number of cores of the system
|
||||
worker_init(worker, -1, 0, NULL);
|
||||
|
||||
// Read the input
|
||||
polynucleotide_t p, *ptr = &p;
|
||||
polynucleotide_init_stream(p, stdin);
|
||||
|
||||
// Prepare outputs
|
||||
char outputBuffer[MAXMUM_NUMBER_OUTPUT][MAXIMUM_OUTPUT_LENGTH];
|
||||
char (*output)[MAXIMUM_OUTPUT_LENGTH] = outputBuffer;
|
||||
|
||||
// Do the following functions in parallel.
|
||||
worker_sync_t block; // Define a synchronization block
|
||||
worker_start(block, worker);
|
||||
|
||||
WORKER_SPAWN(block, (ptr, output), {
|
||||
compute_count(*ptr, "GGTATTTTAATTTATAGT", output[6]);
|
||||
}, (/*no output*/));
|
||||
WORKER_SPAWN(block, (ptr, output), {
|
||||
compute_count(*ptr, "GGTATTTTAATT", output[5]);
|
||||
}, (/*no output*/));
|
||||
WORKER_SPAWN(block, (ptr, output), {
|
||||
compute_count(*ptr, "GGTATT", output[4]);
|
||||
}, (/*no output*/));
|
||||
WORKER_SPAWN(block, (ptr, output), {
|
||||
compute_count(*ptr, "GGTA", output[3]);
|
||||
}, (/*no output*/));
|
||||
compute_count(*ptr, "GGT", output[2]);
|
||||
compute_freq(*ptr, 2, output[1]);
|
||||
compute_freq(p, 1, output[0]);
|
||||
worker_sync(block);
|
||||
|
||||
// Output the results to stdout.
|
||||
for(int i=0; i<MAXMUM_NUMBER_OUTPUT; i++)
|
||||
printf("%s\n", outputBuffer[i]);
|
||||
|
||||
polynucleotide_clear(p);
|
||||
worker_clear(worker);
|
||||
|
||||
return 0;
|
||||
}
|
||||
+18
-5
@@ -51,9 +51,13 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#ifdef USE_BOOST
|
||||
#include <boost/pool/object_pool.hpp>
|
||||
#endif
|
||||
|
||||
#ifdef USE_GLIB
|
||||
#include <glib.h>
|
||||
#endif
|
||||
|
||||
static const size_t PAGE_SIZE = 4096;
|
||||
|
||||
@@ -115,6 +119,8 @@ benchmark(std::string &&desc, std::function<void ()> cb)
|
||||
* Boost::pool
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
#ifdef USE_BOOST
|
||||
|
||||
template<class T>
|
||||
void
|
||||
benchmark_boost_pool()
|
||||
@@ -166,6 +172,7 @@ benchmark_boost_pool_create_and_destroy()
|
||||
}
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------------------------
|
||||
@@ -220,6 +227,7 @@ benchmark_mallocfree_free()
|
||||
* GLib slices
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
#ifdef USE_GLIB
|
||||
template<class T>
|
||||
void
|
||||
benchmark_glib_slices()
|
||||
@@ -262,6 +270,7 @@ benchmark_glib_slices_free()
|
||||
}
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------------------------
|
||||
@@ -798,7 +807,7 @@ benchmark_mempool_small()
|
||||
mempool_small_init (p);
|
||||
mempool_small_t *ptr = &p; // C++ lambda !
|
||||
|
||||
benchmark(std::move(Small() + "mempool"), [=]() {
|
||||
benchmark(std::move(Small() + "M*LIB mempool"), [=]() {
|
||||
for (size_t i = 0; i < N * sizeof(Small) / sizeof(Small); ++i) {
|
||||
Small *o = mempool_small_alloc(*ptr);
|
||||
touch_obj(o);
|
||||
@@ -815,7 +824,7 @@ benchmark_mempool_big()
|
||||
mempool_big_init (p);
|
||||
mempool_big_t *ptr = &p; // C++ lambda !
|
||||
|
||||
benchmark(std::move(Big() + "mempool"), [=]() {
|
||||
benchmark(std::move(Big() + "M*LIB mempool"), [=]() {
|
||||
for (size_t i = 0; i < N * sizeof(Small) / sizeof(Big); ++i) {
|
||||
Big *o = mempool_big_alloc(*ptr);
|
||||
touch_obj(o);
|
||||
@@ -836,7 +845,7 @@ benchmark_mempool_mix_free()
|
||||
mempool_big_init (b);
|
||||
mempool_huge_init (h);
|
||||
|
||||
benchmark(std::move(std::string("mempool w/ free (Mix)")), [=]() {
|
||||
benchmark(std::move(std::string("M*LIB mempool w/ free (Mix)")), [=]() {
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
if (__builtin_expect(!(i & 0xfff), 0)) {
|
||||
Huge *o = mempool_huge_alloc (*ptr_h);
|
||||
@@ -892,19 +901,23 @@ main()
|
||||
benchmark_mallocfree_free<Big>();
|
||||
std::cout << std::endl;
|
||||
|
||||
#ifdef USE_GLIB
|
||||
benchmark_glib_slices<Small>();
|
||||
benchmark_glib_slices_free<Small>();
|
||||
benchmark_glib_slices<Big>();
|
||||
benchmark_glib_slices_free<Big>();
|
||||
std::cout << std::endl;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_BOOST
|
||||
benchmark_boost_pool<Small>();
|
||||
benchmark_boost_pool_free<Small>();
|
||||
benchmark_boost_pool<Big>();
|
||||
benchmark_boost_pool_free<Big>();
|
||||
benchmark_boost_pool_create_and_destroy();
|
||||
std::cout << std::endl;
|
||||
|
||||
#endif
|
||||
|
||||
benchmark_ngx_pool<Small>();
|
||||
benchmark_ngx_pool<Big>();
|
||||
benchmark_ngx_pool_mix_free();
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
CC= gcc
|
||||
CXX= g++
|
||||
CFLAGS= -g -Wall -O2 -fomit-frame-pointer -march=native -lm -include stddef.h
|
||||
CXXFLAGS= $(CFLAGS)
|
||||
LDFLAGS=
|
||||
OBJS=
|
||||
LIBS= -lm
|
||||
|
||||
all: run
|
||||
|
||||
benchmarks:
|
||||
git clone https://github.com/attractivechaos/benchmarks.git
|
||||
|
||||
build: benchmarks
|
||||
mkdir -p benchmarks/udb/mlib
|
||||
cp test-mlib.c benchmarks/udb/mlib/test.c
|
||||
cp Makefile-mlib benchmarks/udb/mlib/Makefile
|
||||
mkdir -p benchmarks/udb/mlib-oa
|
||||
cp test-mlib-oa.c benchmarks/udb/mlib-oa/test.c
|
||||
cp Makefile-mlib benchmarks/udb/mlib-oa/Makefile
|
||||
touch build
|
||||
|
||||
all-recur: build
|
||||
cd benchmarks/udb; \
|
||||
wdir=`pwd`; \
|
||||
list_c=`find . -name 'test.c'`; \
|
||||
list_cc=`find . -name 'test.cc'`; \
|
||||
for t in $$list_c $$list_cc ; do \
|
||||
subdir=`dirname $$t`; \
|
||||
cd $$subdir; \
|
||||
$(MAKE) CC="$(CC)" CXX="$(CXX)" DFLAGS="$(DFLAGS)" CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" || exit 1; \
|
||||
cd $$wdir; \
|
||||
done;
|
||||
|
||||
run: all-recur
|
||||
@cd benchmarks/udb; \
|
||||
echo ; echo "LIBRARY | INT TIME | STR TIME" ; \
|
||||
for i in `find . -name 'test'` ; do \
|
||||
printf "%s|" `dirname $$i|sed -e 's+./++g'`; \
|
||||
$$i -is | awk ' \
|
||||
/\[int data\] # element/ { nbInt= $$5 } \
|
||||
/\[int data\] CPU time/ {timeInt= $$5 } \
|
||||
/\[str data\] # element/ { nbStr= $$5 } \
|
||||
/\[str data\] CPU time/ {timeStr= $$5 } \
|
||||
END { \
|
||||
if (nbInt != 624444) timeInt="BRK" ; \
|
||||
if (nbStr != 624444) timeStr="BRK" ; \
|
||||
print timeInt,"|" ,timeStr \
|
||||
}' ; \
|
||||
done
|
||||
|
||||
clean:
|
||||
rm -rf benchmarks build
|
||||
@@ -0,0 +1,22 @@
|
||||
CC= gcc
|
||||
CFLAGS= -g -Wall -O2 -march=native
|
||||
OBJS= ../benchmark/benchmark.o ../runit/runlib.o test.o
|
||||
DFLAGS=
|
||||
INCLUDES= -I../benchmark -I../runit -I../../../../..
|
||||
LIBS=
|
||||
|
||||
.SUFFIXES:.c .o
|
||||
|
||||
.c.o:
|
||||
$(CC) -std=gnu11 -DNDEBUG -c $(INCLUDES) $(CFLAGS) $(DFLAGS) $< -o $@
|
||||
|
||||
all:test
|
||||
|
||||
test:$(OBJS)
|
||||
$(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS)
|
||||
|
||||
cleanlocal:
|
||||
rm -f *.o a.out *~ test
|
||||
|
||||
clean:cleanlocal
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
#ifndef NDEBUG
|
||||
#define NDEBUG
|
||||
#endif
|
||||
#undef NDEBUG
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "benchmark.h"
|
||||
#include "m-dict.h"
|
||||
#include "m-mempool.h"
|
||||
|
||||
static inline bool oor_equal_p(int k, unsigned char n)
|
||||
{
|
||||
return k == (int)-n - 1;
|
||||
}
|
||||
|
||||
static inline void oor_set(int *k, unsigned char n)
|
||||
{
|
||||
*k = (int)-n - 1;
|
||||
}
|
||||
|
||||
DICT_OA_DEF2(dict_int,
|
||||
int, M_OPEXTEND(M_DEFAULT_OPLIST, OOR_EQUAL(oor_equal_p), OOR_SET(oor_set M_IPTR)),
|
||||
int, M_DEFAULT_OPLIST)
|
||||
|
||||
static const char *const oor[2] = { "EMPTY", "DELETED" };
|
||||
|
||||
static inline bool str_oor_equal_p(const char *k, unsigned char n)
|
||||
{
|
||||
return k == oor[n];
|
||||
}
|
||||
|
||||
static inline void str_oor_set(const char * *k, unsigned char n)
|
||||
{
|
||||
*k = oor[n];
|
||||
}
|
||||
|
||||
DICT_OA_DEF2(dict_str, const char *,
|
||||
M_OPEXTEND (M_CSTR_OPLIST, OOR_EQUAL(str_oor_equal_p), OOR_SET(str_oor_set M_IPTR)),
|
||||
int, M_DEFAULT_OPLIST)
|
||||
|
||||
|
||||
int test_int(int N, const unsigned *data)
|
||||
{
|
||||
int ret;
|
||||
M_LET(h, DICT_OPLIST(dict_int)) {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
if (dict_int_get (h, data[i]) == NULL) {
|
||||
dict_int_set_at (h, data[i], i);
|
||||
} else {
|
||||
dict_int_remove(h, data[i]);
|
||||
}
|
||||
}
|
||||
ret = (int)dict_int_size(h);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int test_str(int N, char * const *data)
|
||||
{
|
||||
int ret;
|
||||
M_LET(h, DICT_OPLIST(dict_str)) {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
if (dict_str_get (h, data[i]) == NULL) {
|
||||
dict_str_set_at (h, data[i], i);
|
||||
} else {
|
||||
dict_str_remove(h, data[i]);
|
||||
}
|
||||
}
|
||||
ret = (int)dict_str_size(h);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return udb_benchmark(argc, argv, test_int, test_str);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "benchmark.h"
|
||||
#include "m-dict.h"
|
||||
#include "m-mempool.h"
|
||||
|
||||
DICT_DEF2(dict_int,
|
||||
int, M_OPEXTEND(M_DEFAULT_OPLIST, MEMPOOL(mpool_int), MEMPOOL_LINKAGE(static)),
|
||||
int, M_DEFAULT_OPLIST)
|
||||
DICT_DEF2(dict_str,
|
||||
const char *, M_OPEXTEND (M_CSTR_OPLIST, MEMPOOL(mpool_str), MEMPOOL_LINKAGE(static)),
|
||||
int, M_DEFAULT_OPLIST)
|
||||
|
||||
int test_int(int N, const unsigned *data)
|
||||
{
|
||||
int ret;
|
||||
dict_int_list_pair_mempool_init(mpool_int);
|
||||
M_LET(h, DICT_OPLIST(dict_int)) {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
if (dict_int_get (h, data[i]) == NULL) {
|
||||
dict_int_set_at (h, data[i], i);
|
||||
} else {
|
||||
dict_int_remove(h, data[i]);
|
||||
}
|
||||
}
|
||||
ret = (int)dict_int_size(h);
|
||||
}
|
||||
dict_int_list_pair_mempool_clear(mpool_int);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int test_str(int N, char * const *data)
|
||||
{
|
||||
int ret;
|
||||
dict_str_list_pair_mempool_init(mpool_str);
|
||||
M_LET(h, DICT_OPLIST(dict_str)) {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
if (dict_str_get (h, data[i]) == NULL) {
|
||||
dict_str_set_at (h, data[i], i);
|
||||
} else {
|
||||
dict_str_remove(h, data[i]);
|
||||
}
|
||||
}
|
||||
ret = (int)dict_str_size(h);
|
||||
}
|
||||
dict_str_list_pair_mempool_clear(mpool_str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return udb_benchmark(argc, argv, test_int, test_str);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
CC= gcc
|
||||
CXX= g++
|
||||
CFLAGS= -g -Wall -O2 -fomit-frame-pointer -march=native -lm -include stddef.h
|
||||
CXXFLAGS= $(CFLAGS)
|
||||
LDFLAGS=
|
||||
OBJS=
|
||||
LIBS=
|
||||
|
||||
all: run
|
||||
|
||||
udb2:
|
||||
git clone https://github.com/attractivechaos/udb2.git
|
||||
|
||||
build: udb2
|
||||
mkdir -p udb2/mlib
|
||||
cp test-mlib.c udb2/mlib/test.c
|
||||
cp Makefile-mlib udb2/mlib/Makefile
|
||||
touch build
|
||||
|
||||
all-recur: build
|
||||
cd udb2/stb && make CC="gcc -lm"
|
||||
cd udb2 && for dir in `ls | egrep -v '^(Makefile|common.c|_)'`; do cd $$dir && make && cd -; done
|
||||
|
||||
run: all-recur
|
||||
@cd udb2; \
|
||||
echo ; echo RUNNING BENCH ; echo ; \
|
||||
for i in `find . -name 'run-test'` ; do \
|
||||
echo ; echo `dirname $$i|sed -e 's+./++g'`; \
|
||||
$$i ; \
|
||||
done
|
||||
|
||||
clean:
|
||||
rm -rf udb2 build *~
|
||||
@@ -0,0 +1,8 @@
|
||||
all:run-test
|
||||
|
||||
run-test:test.c ../common.c
|
||||
$(CC) -std=gnu99 -O2 -Wall -I../../../.. $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f run-test* *~
|
||||
rm -rf mlib
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "../common.c"
|
||||
#include "m-dict.h"
|
||||
|
||||
static inline bool oor_equal_p(unsigned int k, unsigned char n)
|
||||
{
|
||||
return k == (unsigned int)n;
|
||||
}
|
||||
static inline void oor_set(unsigned int *k, unsigned char n)
|
||||
{
|
||||
*k = (unsigned int)n;
|
||||
}
|
||||
|
||||
DICT_OA_DEF2(dict_oa_uint,
|
||||
unsigned int, M_OPEXTEND(M_DEFAULT_OPLIST, OOR_EQUAL(oor_equal_p), OOR_SET(oor_set M_IPTR)),
|
||||
unsigned int, M_DEFAULT_OPLIST)
|
||||
|
||||
void test_int(uint32_t n, uint32_t x0)
|
||||
{
|
||||
uint32_t i, x, z = 0;
|
||||
dict_oa_uint_t h;
|
||||
dict_oa_uint_init(h);
|
||||
for (i = 0, x = x0; i < n; ++i) {
|
||||
x = hash32(x);
|
||||
unsigned int key = get_key(n, x);
|
||||
unsigned int *ptr = dict_oa_uint_get(h, key);
|
||||
if (ptr) { (*ptr)++; z+= *ptr; }
|
||||
else { dict_oa_uint_set_at(h, key, 1); z+=1; }
|
||||
}
|
||||
fprintf(stderr, "# unique keys: %d; checksum: %u\n", (int) dict_oa_uint_size(h), z);
|
||||
dict_oa_uint_clear(h);
|
||||
}
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 91 KiB |
+37
@@ -0,0 +1,37 @@
|
||||
API incompatibility
|
||||
-------------------
|
||||
|
||||
V0.1.*: * First formal version of API
|
||||
|
||||
V0.2.*: * worker_start takes a 'worker_t' as an additional parameter
|
||||
(This enables to perform a wait-on-event instead of a busy waiting).
|
||||
* worker_spawn doesn't take into a 'worker_t' anymore.
|
||||
* worker_init takes an int as numWorker instead of an unsigned int.
|
||||
* RBTREE pop method has been renamed into _pop_at (as it takes a key parameter).
|
||||
* SNAPSHOT_DEF has been removed. use SNAPSHOT_SPSC_DEF
|
||||
* array's _set_at2 has been replaced by array's _get_at.
|
||||
The interface is similar to dict _get_at, making the API cleaner and more generic.
|
||||
_set_at2(a,i,x) ==> set(*_get_at(a,i),x)
|
||||
* DICT_SET:
|
||||
- Rename method _set_at as _push
|
||||
Since there is no key, it is a push method, not a _set_at method.
|
||||
- Rename DICT_SET_DEF2 as DICT_SET_DEF
|
||||
since it only requires one argument.
|
||||
- The method _ref returns a pointer to the data,
|
||||
instead of a pointer to a single tuple like other DICT_SET interface.
|
||||
- The format of the output to character string as changed.
|
||||
* ALGO:
|
||||
- Rename _contains as _contain_p
|
||||
since it is a predicate '_p' and it shall be in infinitive form.
|
||||
- Rename _map as _for_each (Standard name).
|
||||
* M_PRINT_ARG & M_FPRINT_ARG don't put a final semicolon at the end
|
||||
of their expansion anymore (cleaner and no longer needed with the
|
||||
new available tools).
|
||||
* For the TUPLE and the VARIANT
|
||||
change name of _get_ ## field into _get_at_ ## fields
|
||||
Since there is a name collision with other methods
|
||||
if a field is named 'str'.
|
||||
* Rename method _remove into _erase for dictionary & B+TREE (adding a compatibility layer) for API uniformization.
|
||||
The provided _remove doesn't use an iterator but a 'key', incompatible with the one of array or list.
|
||||
But is like the one provided by the priority queue.
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN" "http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><!--This file was converted to xhtml by LibreOffice - see http://cgit.freedesktop.org/libreoffice/core/tree/filter/source/xslt for the code.--><head profile="http://dublincore.org/documents/dcmi-terms/"><meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8"/><title xml:lang="en-US">- no title specified</title><meta name="DCTERMS.title" content="" xml:lang="en-US"/><meta name="DCTERMS.language" content="en-US" scheme="DCTERMS.RFC4646"/><meta name="DCTERMS.source" content="http://xml.openoffice.org/odf2xhtml"/><meta name="DCTERMS.issued" content="2018-07-01T11:06:00.978729880" scheme="DCTERMS.W3CDTF"/><meta name="DCTERMS.provenance" content="" xml:lang="en-US"/><meta name="DCTERMS.subject" content="," xml:lang="en-US"/><link rel="schema.DC" href="http://purl.org/dc/elements/1.1/" hreflang="en"/><link rel="schema.DCTERMS" href="http://purl.org/dc/terms/" hreflang="en"/><link rel="schema.DCTYPE" href="http://purl.org/dc/dcmitype/" hreflang="en"/><link rel="schema.DCAM" href="http://purl.org/dc/dcam/" hreflang="en"/><style type="text/css">
|
||||
@page { }
|
||||
table { border-collapse:collapse; border-spacing:0; empty-cells:show }
|
||||
td, th { vertical-align:top; font-size:10pt;}
|
||||
h1, h2, h3, h4, h5, h6 { clear:both }
|
||||
ol, ul { margin:0; padding:0;}
|
||||
li { list-style: none; margin:0; padding:0;}
|
||||
<!-- "li span.odfLiEnd" - IE 7 issue-->
|
||||
li span. { clear: both; line-height:0; width:0; height:0; margin:0; padding:0; }
|
||||
span.footnodeNumber { padding-right:1em; }
|
||||
span.annotation_style_by_filter { font-size:95%; font-family:Arial; background-color:#fff000; margin:0; border:0; padding:0; }
|
||||
* { margin:0;}
|
||||
.ta1 { writing-mode:lr-tb; }
|
||||
.Default { font-family:Liberation Sans; }
|
||||
.ce100 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce101 { font-family:Liberation Sans; border-bottom-style:none; background-color:#ffff00; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce102 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#ffff00; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce103 { font-family:Liberation Sans; border-bottom-style:none; background-color:#ffff00; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; }
|
||||
.ce104 { font-family:Liberation Sans; background-color:#e6e6e6; font-size:11pt; text-decoration:underline; font-weight:bold; }
|
||||
.ce57 { font-family:Liberation Sans; background-color:transparent; font-size:11pt; text-decoration:none ! important; font-weight:bold; }
|
||||
.ce58 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:transparent; border-left-style:none; border-right-style:none; border-top-style:none; font-size:11pt; text-decoration:none ! important; font-weight:bold; }
|
||||
.ce59 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; font-size:11pt; text-decoration:underline; font-weight:bold; }
|
||||
.ce60 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-style:none; font-size:11pt; text-decoration:none ! important; font-weight:bold; }
|
||||
.ce61 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#e6e6e6; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-style:none; font-size:11pt; text-decoration:none ! important; font-weight:bold; }
|
||||
.ce62 { font-family:Liberation Sans; background-color:#e6e6e6; font-size:11pt; text-decoration:none ! important; font-weight:bold; }
|
||||
.ce63 { font-family:Liberation Sans; background-color:#e6e6e6; border-width:0,0621cm; border-style:solid; border-color:#000000; text-align:center ! important; margin-left:0cm; font-size:11pt; font-weight:bold; }
|
||||
.ce64 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; font-weight:bold; }
|
||||
.ce65 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; font-weight:bold; }
|
||||
.ce66 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#e6e6e6; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; font-weight:bold; }
|
||||
.ce67 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6ff; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; font-weight:bold; }
|
||||
.ce68 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#e6e6ff; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; font-weight:bold; }
|
||||
.ce69 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6ff; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; font-weight:bold; }
|
||||
.ce70 { font-family:Liberation Sans; background-color:#e6e6e6; font-weight:bold; }
|
||||
.ce71 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; font-size:11pt; text-decoration:underline; font-weight:bold; }
|
||||
.ce72 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#e6e6e6; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; font-weight:bold; }
|
||||
.ce73 { font-family:Liberation Sans; border-bottom-style:none; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; }
|
||||
.ce74 { font-family:Liberation Sans; border-bottom-style:none; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce75 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce76 { font-family:Liberation Sans; border-bottom-style:none; background-color:transparent; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce77 { font-family:Liberation Sans; text-align:center ! important; margin-left:0cm; }
|
||||
.ce78 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-style:none; border-right-style:none; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; font-size:11pt; text-decoration:underline; font-weight:bold; }
|
||||
.ce79 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#e6e6e6; border-left-style:none; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; font-weight:bold; }
|
||||
.ce80 { font-family:Liberation Sans; border-bottom-style:none; border-left-style:none; border-right-style:none; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; }
|
||||
.ce81 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; border-left-style:none; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce82 { font-family:Liberation Sans; background-color:transparent; text-align:center ! important; margin-left:0cm; }
|
||||
.ce83 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:transparent; border-left-style:none; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce84 { font-family:Liberation Sans; background-color:#ffff00; text-align:center ! important; margin-left:0cm; }
|
||||
.ce85 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#ffff00; border-left-style:none; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce86 { font-family:Liberation Sans; border-bottom-style:none; background-color:#ffff00; border-left-style:none; border-right-style:none; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; }
|
||||
.ce87 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; font-size:11pt; text-decoration:underline; font-weight:bold; }
|
||||
.ce88 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#e6e6e6; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; font-weight:bold; }
|
||||
.ce89 { font-family:Liberation Sans; border-bottom-style:none; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; }
|
||||
.ce90 { font-family:Liberation Sans; border-bottom-style:none; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce91 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce92 { font-family:Liberation Sans; border-bottom-style:none; background-color:#ffff00; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce93 { font-family:Liberation Sans; border-bottom-style:none; background-color:#ffff00; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce94 { font-family:Liberation Sans; border-bottom-style:none; background-color:transparent; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce95 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:transparent; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce96 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; font-size:11pt; text-decoration:none ! important; font-weight:bold; }
|
||||
.ce97 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#e6e6e6; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; font-weight:bold; }
|
||||
.ce98 { font-family:Liberation Sans; border-bottom-style:none; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; }
|
||||
.ce99 { font-family:Liberation Sans; border-bottom-style:none; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.co1 { width:3.399cm; }
|
||||
.co10 { width:1.304cm; }
|
||||
.co11 { width:1.942cm; }
|
||||
.co12 { width:1.815cm; }
|
||||
.co13 { width:1.348cm; }
|
||||
.co14 { width:1.432cm; }
|
||||
.co15 { width:2.258cm; }
|
||||
.co2 { width:6.281cm; }
|
||||
.co3 { width:1.475cm; }
|
||||
.co4 { width:1.277cm; }
|
||||
.co5 { width:1.602cm; }
|
||||
.co6 { width:1.39cm; }
|
||||
.co7 { width:1.686cm; }
|
||||
.co8 { width:1.644cm; }
|
||||
.co9 { width:2.452cm; }
|
||||
.ro1 { height:0.487cm; }
|
||||
.ro2 { height:0.885cm; }
|
||||
.ro3 { height:0.452cm; }
|
||||
<!-- ODF styles with no properties representable as CSS -->
|
||||
{ }
|
||||
</style></head><body dir="ltr" style="writing-mode:lr-tb; "><table border="0" cellspacing="0" cellpadding="0" class="ta1"><colgroup><col width="149"/><col width="274"/><col width="64"/><col width="56"/><col width="70"/><col width="61"/><col width="74"/><col width="72"/><col width="107"/><col width="57"/><col width="107"/><col width="107"/><col width="74"/><col width="85"/><col width="79"/><col width="79"/><col width="59"/><col width="61"/><col width="63"/><col width="99"/></colgroup><tr class="ro1"><td style="text-align:left;width:3.399cm; " class="ce57"> </td><td style="text-align:left;width:6.281cm; " class="ce57"> </td><td colspan="6" style="text-align:left;width:1.475cm; " class="ce71"><p>Sequence Containers</p></td><td colspan="4" style="text-align:left;width:2.452cm; " class="ce71"><p>Associative Containers</p></td><td style="text-align:left;width:1.686cm; " class="ce96"> </td><td colspan="6" style="text-align:left;width:1.942cm; " class="ce71"><p>Synchronization Containers</p></td><td style="text-align:left;width:2.258cm; " class="ce104"> </td></tr><tr class="ro2"><td style="text-align:left;width:3.399cm; " class="ce58"> </td><td style="text-align:left;width:6.281cm; " class="ce63"><p>Method</p></td><td style="text-align:left;width:1.475cm; " class="ce72"><p>ARRAY</p></td><td style="text-align:left;width:1.277cm; " class="ce79"><p>LIST</p></td><td style="text-align:left;width:1.602cm; " class="ce79"><p>DUALP</p><p>LIST</p></td><td style="text-align:left;width:1.39cm; " class="ce79"><p>I-LIST</p></td><td style="text-align:left;width:1.686cm; " class="ce79"><p>DEQUE</p></td><td style="text-align:left;width:1.644cm; " class="ce88"><p>BITSET</p></td><td style="text-align:left;width:2.452cm; " class="ce72"><p>B+TREE</p><p>DICT/SET</p></td><td style="text-align:left;width:1.304cm; " class="ce79"><p>R/B</p><p>TREE</p></td><td style="text-align:left;width:2.452cm; " class="ce79"><p>[S]HASH</p><p>DICT / SET</p></td><td style="text-align:left;width:2.452cm; " class="ce88"><p>OA DICT</p></td><td style="text-align:left;width:1.686cm; " class="ce97"><p>PRIO </p><p>QUEUE</p></td><td style="text-align:left;width:1.942cm; " class="ce72"><p>BUFFER</p></td><td style="text-align:left;width:1.815cm; " class="ce79"><p>QUEUE </p><p>MPMC</p></td><td style="text-align:left;width:1.815cm; " class="ce79"><p>QUEUE </p><p>SPSC</p></td><td style="text-align:left;width:1.348cm; " class="ce79"><p>SNAP</p><p>SPSC</p></td><td style="text-align:left;width:1.39cm; " class="ce79"><p>SNAP</p><p>SPMC</p></td><td style="text-align:left;width:1.432cm; " class="ce88"><p>SNAP </p><p>MPMC</p></td><td style="text-align:left;width:2.258cm; " class="ce70"> </td></tr><tr class="ro1"><td style="text-align:left;width:3.399cm; " class="ce59"><p>CONSTRUCTOR</p></td><td style="text-align:left;width:6.281cm; " class="ce64"><p>_init</p></td><td style="text-align:left;width:1.475cm; " class="ce73"><p>X</p></td><td style="text-align:left;width:1.277cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.602cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.39cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.686cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.644cm; " class="ce89"><p>X</p></td><td style="text-align:left;width:2.452cm; " class="ce73"><p>X</p></td><td style="text-align:left;width:1.304cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:2.452cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:2.452cm; " class="ce89"><p>X</p></td><td style="text-align:left;width:1.686cm; " class="ce98"><p>X</p></td><td style="text-align:left;width:1.942cm; " class="ce73"><p>X</p></td><td style="text-align:left;width:1.815cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.815cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.348cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.39cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.432cm; " class="ce89"><p>X</p></td><td style="text-align:left;width:2.258cm; " class="Default"> </td></tr><tr class="ro1"><td style="text-align:left;width:3.399cm; " class="ce60"> </td><td style="text-align:left;width:6.281cm; " class="ce65"><p>_set</p></td><td style="text-align:left;width:1.475cm; " class="ce74"><p>X</p></td><td style="text-align:left;width:1.277cm; " class="ce77"><p>X</p></td><td style="text-align:left;width:1.602cm; " class="ce77"><p>X</p></td><td style="text-align:left;width:1.39cm; " class="ce77"> </td><td style="text-align:left;width:1.686cm; " class="ce77"><p>X</p></td><td style="text-align:left;width:1.644cm; " class="ce90"><p>X</p></td><td style="text-align:left;width:2.452cm; " class="ce74"><p>X</p></td><td style="text-align:left;width:1.304cm; " class="ce77"><p>X</p></td><td style="text-align:left;width:2.452cm; " class="ce77"><p>X</p></td><td style="text-align:left;width:2.452cm; " class="ce90"><p>X</p></td><td style="text-align:left;width:1.686cmLine truncated
|
||||
Binary file not shown.
+49
-20
@@ -2,34 +2,63 @@ How to make a release?
|
||||
======================
|
||||
|
||||
1) Check that all test pass on all primary systems.
|
||||
Run 'make check' on all primary systems.
|
||||
Run 'make check' or 'make checkall' on all primary systems.
|
||||
No error shall be reported.
|
||||
|
||||
2) Check that no memory leak is reported by valgrind:
|
||||
Run 'make valgrind' in tests folder on a system.
|
||||
|
||||
3) Check that no undefined behavior, nor data race, ... is reported by sanitizer:
|
||||
2) Check that no undefined behavior, nor data race, ... is reported by sanitizer:
|
||||
Run 'make sanitize' in tests folder on a system.
|
||||
This needs at least GCC 7
|
||||
|
||||
4) Check that no new warning is reported by static analyser:
|
||||
Run 'make scan-build' in tests folder on a system.
|
||||
This needs at least clang 4.
|
||||
Currently there is a false positive detected in m-shared:
|
||||
"Argument to free() is offset by 8 bytes from the start of memory allocated by malloc()"
|
||||
scan-build is not able to properly track that this case cannot open.
|
||||
|
||||
5) Check that coverage is reasonnable.
|
||||
3) Check that coverage is reasonable.
|
||||
Run 'make coverage' and analyze result.
|
||||
|
||||
In case of problem, open a problem error.
|
||||
4) Publish the release
|
||||
|
||||
List of primary systems
|
||||
In case of problem, open a problem report.
|
||||
|
||||
|
||||
List of tested systems
|
||||
=======================
|
||||
|
||||
* gcc 4.9 on linux/x86-64
|
||||
* gcc 7 on linux/x86-64
|
||||
* clang 3.5 on linux/x86-64
|
||||
* clang 4 on linux/x86-64
|
||||
* g++ 7 on linux/x86-64
|
||||
* gcc 4.9, 7 & 8 on linux/x86-64
|
||||
* clang 3.5, 4 & 6 on linux/x86-64
|
||||
* g++ 7 & 8 on linux/x86-64
|
||||
* tcc on linux/x86-64
|
||||
* gcc 7 on windows/x86-64
|
||||
* gcc 4.9 on linux/powerpc
|
||||
* gcc 6 on linux/armv7l
|
||||
* clang 3.8 on linux/armv7l
|
||||
|
||||
|
||||
Cross compilation
|
||||
=================
|
||||
|
||||
There is no need to cross compile the library as it is a header only library.
|
||||
|
||||
However if you want to run the test with a cross-compiler, do like the following
|
||||
command. It performs a cross-compilation of the test suite from linux to windows
|
||||
and run the test suite with wine:
|
||||
|
||||
make CC="i586-mingw32msvc-gcc -std=c99 -DWINVER=0x600 -D_WIN32_WINNT=0x600" LOG_COMPILER=wine check
|
||||
|
||||
|
||||
Handling issues
|
||||
===============
|
||||
|
||||
Issues are opened in the bug tracker of the project (typically https://github.com/P-p-H-d/mlib/issues )
|
||||
|
||||
If the issue cannot be closed and taken into account in less than (typically) one month,
|
||||
or before a release,
|
||||
it shall be moved into the long term issues included in the repository:
|
||||
https://github.com/P-p-H-d/mlib/blob/master/doc/ISSUES.org
|
||||
This is a file in ORG mode: the issues are created with the proper tags.
|
||||
|
||||
The original issue in the tracker can then be closed with the issuer agreement.
|
||||
|
||||
This will have the following gains:
|
||||
|
||||
- no hard dependencies on any bug tracking infrastructure (can be migrated easily).
|
||||
- a release will automatically contain all its open problem reports.
|
||||
|
||||
Once one issue of ISSUES.org is taken into account,
|
||||
its state shall be changed to DONE (the issue shall not be removed).
|
||||
+263
@@ -0,0 +1,263 @@
|
||||
*Long term issues*
|
||||
------------------
|
||||
|
||||
* TODO #21: Generic Binary serialization :ENHANCEMENT:
|
||||
|
||||
Based on issue #26 of https://github.com/P-p-H-d/mlib/issues/26
|
||||
|
||||
Some kind of "binary serialization" on the model of get_str/parse_str
|
||||
could be possible. It would be a great feature from the application
|
||||
point-of-view: binary representation is more bandwidth-efficient if
|
||||
used on network communications.
|
||||
|
||||
It will be good to have import/export methods to the
|
||||
XML/JSON/MSGPACK/PROTOBUF/BINARY format.
|
||||
However, adding all of them on by one in the M*LIB containers
|
||||
doesn't seem satisfactory.
|
||||
|
||||
Instead, adding a generic interface for the serialization of data
|
||||
that may be customized by the user to perform the import/export of
|
||||
objects in whatever format they want into what they want (FILE/memory/...).
|
||||
To simplify it, this interface could only support one kind of import/export
|
||||
per compilation unit.
|
||||
|
||||
Problem:
|
||||
the interface is either too high level (limiting too much what we can do)
|
||||
or too low level (exporting too many internal things of the library).
|
||||
|
||||
|
||||
* TODO #20: Bucket priority queue :ENHANCEMENT:
|
||||
|
||||
Add a new kind of priority queue.
|
||||
See https://en.wikipedia.org/wiki/Bucket_queue
|
||||
|
||||
Check if it will be better as intrusive or non-intrusive container.
|
||||
|
||||
To test if a bucket is empty or not, a bitfield can be used to check if
|
||||
the bit associated to the bucket is set or not. To get the highest bucket
|
||||
non empty, we can perform a CLZ of the bitset, which shall be much faster than
|
||||
performing a linear search of the buckets (algorithm complexity is the same,
|
||||
except that we can scan 64 entries at a time).
|
||||
|
||||
Check if we can use BITSET, or introduce fixed size BITSET or use ad-hock
|
||||
implementation.
|
||||
|
||||
|
||||
* TODO #19: Intrusive Red Black Tree :ENHANCEMENT:
|
||||
|
||||
Add intrusive red black tree.
|
||||
|
||||
|
||||
* TODO #18: Missing methods :ENHANCEMENT:
|
||||
|
||||
Some containers don't have all the methods they should.
|
||||
See the cells in yellow here:
|
||||
http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Container.html
|
||||
|
||||
|
||||
* TODO #17: Ressource handler :ENHANCEMENT:
|
||||
|
||||
A ressouce handler which shall associated a unique handle to a ressource.
|
||||
The handle shall be unique for the ressource and shall not be reused.
|
||||
It is typically a 64 bits integers always incremented (even the program
|
||||
creates one billion ressources per second, the counter won't overflow
|
||||
until 585 years).
|
||||
|
||||
The ressource handler shall also keep count on:
|
||||
|
||||
- how much real owners claim to own the ressource
|
||||
(the ressource is only owned by the ressource handler, however
|
||||
it acts as delegate of the real owner),
|
||||
- how much users keep a pointer to the ressource.
|
||||
|
||||
This may be a better alternative than shared_ptr & weak_ptr:
|
||||
|
||||
- reduce fragmentation,
|
||||
- no cycle dependencies,
|
||||
- shared_ref & weak_ref becomes only HANDLE,
|
||||
- all ressources can be freed in one pass.
|
||||
|
||||
Needs lock free dictionnary.
|
||||
|
||||
|
||||
* TODO #16: Lock Free List :ENHANCEMENT:
|
||||
|
||||
Implement a lock free list. Most of the difficulty is the memory reclamation part.
|
||||
Typically this lock free list shall be compatible with RCU method.
|
||||
|
||||
A node can be in the following state:
|
||||
|
||||
- FREE: Can be used for allocation.
|
||||
- IN_USE: The node is currently used in the list.
|
||||
- TO_BE_RECLAIMED: The node is semantically removed, but some others may still be used it.
|
||||
- CAN_BE_RECLAIMED: The node is semantically removed and no one still use it.
|
||||
|
||||
So separate lists shall be used for the memory handler.
|
||||
It shall also be compatible with NUMA and avoid putting all nodes in the same lists if possible:
|
||||
otherwise the head will become a hot spot and slow down everything.
|
||||
|
||||
The ABA problem shall also be taken into account. Normaly with the provided state of the nodes,
|
||||
it should not be a problem.
|
||||
|
||||
Concurrent insertion / insertion and insertion / deletion and deletion / deletion shall be taken into account.
|
||||
|
||||
|
||||
|
||||
* TODO #15: Constructor / Destructor Prologue / Epilogue for Stack Exception Handling :ENHANCEMENT:
|
||||
|
||||
Constructor (and destructor) need to use user-defined prologue / epilogue.
|
||||
This is in order to register the constructed object into a proper Stack Exception Handling so that exceptions throwing may work reliably.
|
||||
|
||||
Proposal:
|
||||
|
||||
- M_CONSTRUCTOR_PROLOGUE(object, oplist);
|
||||
- M_CONSTRUCTOR_EPILOGUE(object, oplist);
|
||||
- M_DESTRUCTOR_PROLOGUE(object, oplist);
|
||||
- M_DESTRUCTOR_EPILOGUE(object, oplist);
|
||||
|
||||
Object creation will need to add all sub-objects into the stack, then unstack all to push instead the root object (which will recursively remove them).
|
||||
|
||||
See also http://freetype.sourceforge.net/david/reliable-c.html#cseh
|
||||
|
||||
|
||||
|
||||
* TODO #14: Memory allocation enhancement :ENHANCEMENT:
|
||||
|
||||
Enhancement of the memory allocation scheme to find way to deal properly with advance allocators:
|
||||
|
||||
- non-default alignment requirements for types,
|
||||
- instance-based allocator (may need instance based variable access),
|
||||
- expected life of created type (temporary or permanent),
|
||||
- stack based allocator,
|
||||
- global variable access for allocator,
|
||||
- maximum allocation before failure.
|
||||
|
||||
Most of theses are already more or less supported. Examples shall be created to show how to deal with this:
|
||||
|
||||
- alignement shall be implemented with the attributes of <stdalign.h>
|
||||
|
||||
However I sill don't know how to implement "instance-based allocator" which is what is missing.
|
||||
The problem is how to give to methods context local information store within the container itself.
|
||||
|
||||
Update:
|
||||
|
||||
API transformation support enables "instance-based allocator" to be made easily.
|
||||
Needs some formal operator in the oplist to support it fully and an example.
|
||||
|
||||
|
||||
* TODO #12: Atomic shared pointer :ENHANCEMENT:
|
||||
|
||||
Add an extension to the SHARED_PTR API:
|
||||
|
||||
- New type atomic_shared_ptr
|
||||
- name_init_atomic_set (&atomic_shared_ptr, shared_ptr);
|
||||
- name_init_set_atomic (shared_ptr, &atomic_shared_ptr);
|
||||
- name_init_atomic_set_atomic (&atomic_shared_ptr, &atomic_shared_ptr);
|
||||
- name_atomic_set (&atomic_shared_ptr, shared_ptr);
|
||||
- name_set_atomic (shared_ptr, &atomic_shared_ptr);
|
||||
- name_atomic_set_atomic (&atomic_shared_ptr, &atomic_shared_ptr);
|
||||
- name_atomic_clear
|
||||
|
||||
No _ref or direct _init: we need to init first a normal shared_ptr then the atomic (TBC)
|
||||
|
||||
** _atomic_set method:
|
||||
|
||||
It can be implemented by incrementing the non atomic shared pointer reference, then performs a compare_and_swap to the data of the atomic shared pointer, finally decrement and dec/free the swapped previous data of the atomic .
|
||||
|
||||
** _set_atomic method:
|
||||
|
||||
It needs to perform the following atomic operation : <read the pointer, deref pointer and increment the pointed value> I don't known how to do it properly.
|
||||
|
||||
See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4162.pdf
|
||||
|
||||
Proposition for _set_atomic we store temporary NULL to the atomic_ptr struct to request an exclusive access to the data (this looks like a lock and other operations need to handle NULL) :
|
||||
|
||||
#+BEGIN_SRC C
|
||||
void shared_ptr_set_atomic(ptr a, atomic_ptr *ptr)
|
||||
{
|
||||
// Get exclusive access to the data
|
||||
p = atomic_load(ptr);
|
||||
do {
|
||||
if (p == NULL) {
|
||||
// TODO: exponential backoff
|
||||
p = atomic_load(ptr);
|
||||
continue;
|
||||
}
|
||||
} while (!atomic_compare_exchange_weak(ptr, &p, NULL));
|
||||
// p has exclusive access to the pointer
|
||||
p->data->cpt ++;
|
||||
a->data = p->data;
|
||||
atomic_exchange (ptr, p);
|
||||
}
|
||||
#+END_SRC
|
||||
|
||||
This prevents using NULL which obliges atomic shared pointer to point to a created object...
|
||||
|
||||
Other alternative solution is to use the bit 0 to mark the pointer as being updated, preventing other from using it (TBC only clear):
|
||||
|
||||
#+BEGIN_SRC C
|
||||
void shared_ptr_set_atomic(ptr a, atomic_ptr *ptr)
|
||||
{
|
||||
// Get exclusive access to the data
|
||||
p = atomic_load(ptr);
|
||||
do {
|
||||
if ( (p&1) != 0) {
|
||||
// TODO: exponential backoff
|
||||
p = atomic_load(ptr);
|
||||
continue;
|
||||
}
|
||||
} while (!atomic_compare_exchange_weak(ptr, &p, p|1));
|
||||
// Exclusive access (kind of lock).
|
||||
p->data->cpt ++;
|
||||
a->data = p->data;
|
||||
atomic_set (ptr, p);
|
||||
}
|
||||
#+END_SRC
|
||||
|
||||
Other implementation seems to have it hard to be lock-free: cf. https://github.com/llvm-mirror/libcxx/commit/5fec82dc0db3623546038e4a86baa44f749e554f
|
||||
|
||||
|
||||
|
||||
* TODO #5: Concurrent dictionary Container :ENHANCEMENT:
|
||||
|
||||
Implement a more efficient dictionary than lock + std dictionary for all operations when dealing with threads.
|
||||
See https://msdn.microsoft.com/en-us/library/dd287191(v=vs.110).aspx
|
||||
|
||||
** Multiple locks within the dictionnary
|
||||
|
||||
A potential implementation may be to request at initialization time the number of concurrent thread N.
|
||||
Create a static array of N dictionnary with N mutex. Then to access the data will perform :
|
||||
|
||||
- compute hash of object,
|
||||
- access high bits of hash and select which dictionnary shall have the data,
|
||||
- lock it,
|
||||
- perform classic access to the data (check if the compiler can properly optimize the hash computation),
|
||||
- unlock it.
|
||||
|
||||
The property of the hash shall allow a good dispersion of the data across multiple locks, reducing the constraints on the lock. This implementation could be build easily upon the already existent dictionary.
|
||||
|
||||
To test.
|
||||
|
||||
See also https://github.com/simonhf/sharedhashfile
|
||||
|
||||
** Lock Free dictionnary
|
||||
|
||||
Evaluate also lock-free dictionary (easier with open addressing).
|
||||
It needs a complete rewrite of the inner loop through. The hard part is the dynamic resizing of the internal array (see http://preshing.com/20160222/a-resizable-concurrent-map/ for a potential solution and http://www.cs.toronto.edu/~tomhart/papers/tomhart_thesis.pdf for memory reclamation techniques). See also https://www.research.ibm.com/people/m/michael/spaa-2002.pdf
|
||||
https://www.kernel.org/pub/linux/kernel/people/paulmck/perfbook/perfbook.2017.11.22a.pdf persents different techniques used by linux kernel.
|
||||
It needs before lock-free list: http://www.cse.yorku.ca/~ruppert/papers/lfll.pdf http://www.cse.yorku.ca/~ruppert/Mikhail.pdf
|
||||
|
||||
** Context
|
||||
|
||||
The best parallel algorithm is still when there is as few synchronization as possible. A concurrent dictionary will fail at this and will result in average performance at best.
|
||||
The typical best case will be in RCU context (a lot of readers, few writers), so the interface shall be compatible with such structure.
|
||||
|
||||
** Linked list
|
||||
|
||||
Another solution is to create a huge list of items which is:
|
||||
|
||||
- atomically updated,
|
||||
- in reverse order of the hash (bit 0 is highest bit, Bit 63 is the lowest bit).
|
||||
|
||||
Hash table will only give quick access to items to this list. Expanding the table won't change the order of the list (so the items will remain at the same place and always accessible by other threads) but just add sentinel in the sentinel at the right place (there won't be any reallocation of the list). Inserting will need to insert an item at the right place.
|
||||
Not sure it will really help. To analyse.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 81 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
+27
-14
@@ -5,11 +5,13 @@
|
||||
#include "m-mutex.h"
|
||||
|
||||
/* Global variable to stop scheduling */
|
||||
bool continue_threading_g;
|
||||
atomic_bool continue_threading_g;
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
/* Define a random big data structure */
|
||||
/* Define a custom random big data structure
|
||||
with its constructor and its destructor.
|
||||
*/
|
||||
|
||||
#define BIGDATA_SIZE 1000
|
||||
#define BIGDATA_NUM 10
|
||||
@@ -28,6 +30,7 @@ static void bigdata_init(bigdata_t d)
|
||||
{
|
||||
memset(d->bigdata, 0, BIGDATA_SIZE);
|
||||
d->ptr = malloc (BIGDATA_HEIGHT*BIGDATA_WIDTH);
|
||||
if (!d->ptr) abort();
|
||||
d->count = count_g++;
|
||||
printf ("Init image %d\n", d->count);
|
||||
}
|
||||
@@ -38,19 +41,25 @@ static void bigdata_clear(bigdata_t d)
|
||||
free(d->ptr);
|
||||
}
|
||||
|
||||
/* Let's register the oplist of our custom type globally */
|
||||
#define M_OPL_bigdata_t() (INIT(bigdata_init), CLEAR(bigdata_clear))
|
||||
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
/* Perform some random works on this big data */
|
||||
/* Perform some random works on this custom type */
|
||||
|
||||
static void perform_acquisition(bigdata_t p)
|
||||
{
|
||||
printf ("Acquiring image %d\n", p->count);
|
||||
// Let's say that rand is our acquisition function.
|
||||
for(int i = 0 ; i < BIGDATA_SIZE;i++)
|
||||
p->bigdata[i] = rand();
|
||||
for(int i = 0 ; i < BIGDATA_WIDTH*BIGDATA_HEIGHT;i++)
|
||||
p->ptr[i] = rand();
|
||||
// Let's stop arbitrary after 20 acquisitions
|
||||
if (p->count == 20)
|
||||
continue_threading_g = false;
|
||||
atomic_store(&continue_threading_g, false);
|
||||
}
|
||||
|
||||
static void perform_computation2(bigdata_t p)
|
||||
@@ -69,7 +78,7 @@ static void perform_computation3(bigdata_t p)
|
||||
|
||||
static void perform_computation4(bigdata_t p)
|
||||
{
|
||||
printf ("Computation 4 on image %d\n", p->count);
|
||||
printf ("Reduction on image %d\n", p->count);
|
||||
unsigned long s = 0;
|
||||
for(int i = 0 ; i < BIGDATA_WIDTH*BIGDATA_HEIGHT;i++)
|
||||
s += p->ptr[i];
|
||||
@@ -81,11 +90,12 @@ static void perform_computation4(bigdata_t p)
|
||||
/**************************************************************/
|
||||
|
||||
/* Let's define a shared pointer interface around the bigdata structure */
|
||||
SHARED_PTR_DEF(shared_bigdata, bigdata_t, (INIT(bigdata_init), CLEAR(bigdata_clear)))
|
||||
SHARED_PTR_DEF(shared_bigdata, bigdata_t)
|
||||
|
||||
/* Let's define the communication queue between the thread:
|
||||
* - size of queue is 10,
|
||||
* - it is a queue BUFFER_QUEUE,
|
||||
* - it store shared pointer to our bigdata_t,
|
||||
* - we change the semantics of _push / _pop into buffer as push a new item and pop it as a move (ideal semantics for shared_ptr interface).
|
||||
*/
|
||||
#define MY_QUEUE_SIZE 10
|
||||
@@ -94,11 +104,11 @@ BUFFER_DEF(buffer_bigdata, shared_bigdata_t, MY_QUEUE_SIZE,
|
||||
|
||||
/* Let's build a Thread synchros tree:
|
||||
*
|
||||
* Thread1 perform the simulation acquisition of the data.
|
||||
* Thread2 & Thread3 perform independet computation.
|
||||
* Thread1 perform the simulated acquisition of the data.
|
||||
* Thread2 & Thread3 perform independent computation.
|
||||
* Thread4 finalizes the computation :
|
||||
*
|
||||
* Thread1 ---> Thread 2
|
||||
* Thread1 ---> Thread 2 +
|
||||
* +-> Thread 3 --> Thread 4
|
||||
*/
|
||||
buffer_bigdata_t buf_t1tot2;
|
||||
@@ -123,7 +133,7 @@ static void clearbuf(void)
|
||||
static void thread1 (void *arg)
|
||||
{
|
||||
(void) arg;
|
||||
while (continue_threading_g) {
|
||||
while (atomic_load(&continue_threading_g)) {
|
||||
shared_bigdata_t ptr;
|
||||
shared_bigdata_init_new (ptr);
|
||||
perform_acquisition(*shared_bigdata_ref(ptr));
|
||||
@@ -135,7 +145,7 @@ static void thread1 (void *arg)
|
||||
static void thread2 (void *arg)
|
||||
{
|
||||
(void) arg;
|
||||
while (continue_threading_g) {
|
||||
while (atomic_load(&continue_threading_g)) {
|
||||
shared_bigdata_t ptr;
|
||||
buffer_bigdata_pop(&ptr, buf_t1tot2); // NOTE: Pop has been configured to init ptr
|
||||
perform_computation2(*shared_bigdata_ref(ptr));
|
||||
@@ -145,7 +155,7 @@ static void thread2 (void *arg)
|
||||
static void thread3 (void *arg)
|
||||
{
|
||||
(void) arg;
|
||||
while (continue_threading_g) {
|
||||
while (atomic_load(&continue_threading_g)) {
|
||||
shared_bigdata_t ptr;
|
||||
buffer_bigdata_pop(&ptr, buf_t1tot3); // NOTE: Pop has been configured to init ptr
|
||||
perform_computation3(*shared_bigdata_ref(ptr));
|
||||
@@ -156,7 +166,7 @@ static void thread3 (void *arg)
|
||||
static void thread4 (void *arg)
|
||||
{
|
||||
(void) arg;
|
||||
while (continue_threading_g) {
|
||||
while (atomic_load(&continue_threading_g)) {
|
||||
shared_bigdata_t ptr;
|
||||
buffer_bigdata_pop(&ptr, buf_t3tot4); // NOTE: Pop has been configured to init ptr
|
||||
perform_computation4(*shared_bigdata_ref(ptr));
|
||||
@@ -164,11 +174,12 @@ static void thread4 (void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
static void init(void)
|
||||
{
|
||||
initbuf();
|
||||
continue_threading_g = true;
|
||||
atomic_init(&continue_threading_g, true);
|
||||
m_thread_create(t1, thread1, NULL);
|
||||
m_thread_create(t2, thread2, NULL);
|
||||
m_thread_create(t3, thread3, NULL);
|
||||
@@ -184,6 +195,8 @@ static void clear(void)
|
||||
clearbuf();
|
||||
}
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
init();
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@ int main(void) {
|
||||
char *k = dict_m32_get(h, 10); // No 10 in h, so k == NULL
|
||||
int is_missing = (k != NULL); // true
|
||||
assert (is_missing);
|
||||
dict_m32_remove(h, 5); // h is now empty
|
||||
dict_m32_erase(h, 5); // h is now empty
|
||||
dict_m32_it_t it; // iterate over all dictionnary
|
||||
for (dict_m32_it (it, h) ; !dict_m32_end_p (it); dict_m32_next(it)) {
|
||||
struct dict_m32_pair_s *item = dict_m32_ref(it);
|
||||
@@ -33,7 +33,7 @@ int main(void) {
|
||||
char *k = dict_m32_get(h, 10); // k == NULL
|
||||
int is_missing = (k != NULL); // true
|
||||
assert (is_missing);
|
||||
dict_m32_remove(h, 5); // h is now empty
|
||||
dict_m32_erase(h, 5); // h is now empty
|
||||
for M_EACH(item, h, M32_OPLIST) { // traverse each item
|
||||
item->value = 1; // Set its value to 1
|
||||
}
|
||||
|
||||
+2
-2
@@ -50,11 +50,11 @@ int main(void)
|
||||
printf("Enter the mapped string to be deleted: ");
|
||||
string_fgets (str, stdin, STRING_READ_PURE_LINE);
|
||||
s = atoi(string_get_cstr(str));
|
||||
dict_map_remove(mp, s);
|
||||
dict_map_erase(mp, s);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
printf("Size of Map: %lu\n", (unsigned long) dict_map_size(mp) );
|
||||
printf("Size of Map: %zu\n", dict_map_size(mp) );
|
||||
break;
|
||||
|
||||
case 4:
|
||||
|
||||
+3
-3
@@ -5,14 +5,14 @@
|
||||
/* program that reads text from standard-input,
|
||||
and prints the total number of distinct words found in the text.
|
||||
*/
|
||||
DICT_SET_DEF2(dict_str, string_t, STRING_OPLIST)
|
||||
DICT_SET_DEF(dict_str, string_t, STRING_OPLIST)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
M_LET(word, STRING_OPLIST)
|
||||
M_LET(wordcount, DICT_SET_OPLIST(dict_str, STRING_OPLIST)) {
|
||||
while (string_fget_word(word, " \t\n,.!;:?", stdin))
|
||||
dict_str_set_at(wordcount, word);
|
||||
printf ("Words: %lu\n", dict_str_size(wordcount));
|
||||
dict_str_push(wordcount, word);
|
||||
printf ("Words: %zu\n", dict_str_size(wordcount));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#include <stdio.h>
|
||||
#include "m-string.h"
|
||||
#include "m-array.h"
|
||||
#include "m-dict.h"
|
||||
|
||||
ARRAY_DEF(array_size, size_t)
|
||||
#define M_OPL_array_size_t() ARRAY_OPLIST(array_size)
|
||||
DICT_DEF2(map_pos, string_t, array_size_t)
|
||||
#define M_OPL_map_pos_t() DICT_OPLIST(map_pos, STRING_OPLIST, ARRAY_OPLIST(array_size))
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
if (argc == 2) {
|
||||
M_LET(word, string_t)
|
||||
M_LET(positions, map_pos_t) {
|
||||
FILE *f = fopen(argv[1], "rt");
|
||||
if (!f) return 1;
|
||||
while (string_fget_word(word, " \t\n\r\"(),=", f)) {
|
||||
array_size_t *array = map_pos_get_at(positions, word);
|
||||
array_size_push_back(*array, (size_t)ftell(f) - string_size(word));
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
for M_EACH(pair, positions, map_pos_t) {
|
||||
string_fputs(stdout, pair->key);
|
||||
for M_EACH(pos, pair->value, array_size_t)
|
||||
printf(" %zu", *pos);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -38,7 +38,7 @@ int main(void)
|
||||
list_string_get_str (tmpstr, grow, false);
|
||||
|
||||
// print out
|
||||
printf("Number of elements = %lu\n", (unsigned long) list_string_size(grow));
|
||||
printf("Number of elements = %zu\n", list_string_size(grow));
|
||||
printf("Final string = %s\n", string_get_cstr(tmpstr));
|
||||
|
||||
// Let's iterate over each element of the list
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "m-array.h"
|
||||
#include "m-dict.h"
|
||||
#include "m-string.h"
|
||||
#include "m-algo.h"
|
||||
|
||||
/* Based on:
|
||||
* Easy Perfect Minimal Hashing
|
||||
* By Steve Hanov. Released to the public domain.
|
||||
* http://stevehanov.ca/blog/index.php?id=119
|
||||
*
|
||||
* Based on:
|
||||
* Edward A. Fox, Lenwood S. Heath, Qi Fan Chen and Amjad M. Daoud,
|
||||
* "Practical minimal perfect hash functions for large databases", CACM, 35(1):105-121
|
||||
*
|
||||
* also a good reference:
|
||||
* Compress, Hash, and Displace algorithm by Djamal Belazzougui,
|
||||
* Fabiano C. Botelho, and Martin Dietzfelbinger
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* Define the needed data structure : */
|
||||
|
||||
// Define a dictionnary of constant string --> integer
|
||||
DICT_DEF2(dict_mph, const char *, M_CSTR_OPLIST, uint32_t, M_DEFAULT_OPLIST)
|
||||
#define M_OPL_dict_mph_t() DICT_OPLIST(dict_mph, M_CSTR_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
// Defin an array of integer
|
||||
ARRAY_DEF(array_value, uint32_t) // Start from 1. 0 is 'None'
|
||||
#define M_OPL_array_value_t() ARRAY_OPLIST(array_value)
|
||||
|
||||
// Defin an array of integer
|
||||
ARRAY_DEF(array_seed, int32_t)
|
||||
#define M_OPL_array_seed_t() ARRAY_OPLIST(array_seed)
|
||||
ALGO_DEF(array_seed, array_seed_t)
|
||||
|
||||
// Define an array of constant string that are ordered by their size.
|
||||
ARRAY_DEF(array_cstr, const char *, M_CSTR_OPLIST)
|
||||
static inline int array_cstr_cmp(const array_cstr_t a, const array_cstr_t b) {
|
||||
size_t sa = array_cstr_size(a);
|
||||
size_t sb = array_cstr_size(b);
|
||||
return (sa > sb) ? -1 : (sa < sb);
|
||||
}
|
||||
#define M_OPL_array_cstr_t() M_OPEXTEND(ARRAY_OPLIST(array_cstr, M_CSTR_OPLIST), CMP(array_cstr_cmp))
|
||||
|
||||
// Define an array of array of constant string, and some algo over this container
|
||||
ARRAY_DEF(array_bucket, array_cstr_t)
|
||||
#define M_OPL_array_bucket_t() ARRAY_OPLIST(array_bucket, M_OPL_array_cstr_t() )
|
||||
ALGO_DEF(array_bucket, array_bucket_t)
|
||||
|
||||
// Define an array of string to save the input.
|
||||
ARRAY_DEF(array_string, string_t)
|
||||
#define M_OPL_array_string_t() ARRAY_OPLIST(array_string, M_OPL_string_t())
|
||||
|
||||
|
||||
|
||||
|
||||
/* The algorithm may not converge on all inputs if the hash function
|
||||
* doesn't randomly distribute the inputs:
|
||||
* str1 != str2 ==> \exist seed \such_as hash(seed,str1) != hash(seed, str2)
|
||||
* The first hash function below doesn't provide the needed property for
|
||||
* inputs with two words 'a' and 'c'
|
||||
* The second one is more robust but may still have this issue.
|
||||
*/
|
||||
static uint32_t hash(int32_t seed, const char str[])
|
||||
{
|
||||
#if 0
|
||||
if (seed == 0) seed = 0x01000193;
|
||||
while (*str) {
|
||||
seed = ((seed * 0x01000193) ^ *str);
|
||||
str++;
|
||||
}
|
||||
return seed;
|
||||
#else
|
||||
if (seed == 0) seed = 0x811C9DC5;
|
||||
while (*str) {
|
||||
seed = (seed ^ *str) * 16777619;
|
||||
str++;
|
||||
}
|
||||
return seed ^ (seed >> 16);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Step 1: place all key keys into buckets
|
||||
static void
|
||||
CreateMinimalPerfectHash1(array_seed_t seed, array_value_t value,
|
||||
array_bucket_t bucket,
|
||||
const dict_mph_t dict)
|
||||
{
|
||||
size_t size = dict_mph_size (dict);
|
||||
|
||||
array_value_resize(value, size);
|
||||
array_seed_resize(seed, size);
|
||||
array_bucket_resize(bucket, size);
|
||||
|
||||
for M_EACH(item, dict, dict_mph_t) {
|
||||
size_t i = hash(0, item->key) % size;
|
||||
array_cstr_push_back (*array_bucket_get (bucket, i), item->key);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Sort the buckets and process the ones with the most items first.
|
||||
static void
|
||||
CreateMinimalPerfectHash2(array_seed_t seed, array_value_t value,
|
||||
array_bucket_t bucket,
|
||||
const dict_mph_t dict)
|
||||
{
|
||||
size_t size = dict_mph_size (dict);
|
||||
array_bucket_sort(bucket);
|
||||
M_LET(slots, array_seed_t) {
|
||||
for M_EACH(b, bucket, array_bucket_t) {
|
||||
if (array_cstr_size(*b) <= 1)
|
||||
break;
|
||||
size_t d = 1;
|
||||
/* Repeatedly try different values of d until we find a hash function
|
||||
that places all items in the bucket into free slots.
|
||||
This may loop forever on some inputs if the hash function is not good
|
||||
enought. */
|
||||
for(bool cont = true; cont ; ) {
|
||||
cont = false;
|
||||
array_seed_clean(slots);
|
||||
for M_EACH(it, *b, array_cstr_t) {
|
||||
size_t s = hash(d, *it) % size;
|
||||
if (*array_value_get(value, s) != 0 || array_seed_contain_p(slots, s)){
|
||||
d++;
|
||||
cont = true;
|
||||
break;
|
||||
}
|
||||
array_seed_push_back (slots, s);
|
||||
}
|
||||
}
|
||||
// Fill in the seed & value with the computed value
|
||||
array_seed_set_at(seed, hash(0, *array_cstr_get(*b, 0)) % size, d);
|
||||
d = 0;
|
||||
for M_EACH(it, *b, array_cstr_t) {
|
||||
array_value_set_at(value,
|
||||
*array_seed_get(slots, d),
|
||||
*dict_mph_get(dict, *it));
|
||||
d++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 3:
|
||||
* Only buckets with 1 item remain. Process them more quickly by directly
|
||||
* placing them into a free slot. Use a negative value of d to indicate
|
||||
* this.
|
||||
*/
|
||||
static void
|
||||
CreateMinimalPerfectHash3(array_seed_t seed, array_value_t value,
|
||||
array_bucket_t bucket,
|
||||
const dict_mph_t dict)
|
||||
{
|
||||
size_t size = dict_mph_size (dict);
|
||||
|
||||
M_LET(freelist, array_seed_t) {
|
||||
// Get a list of available slot
|
||||
for(size_t i = 0; i < size; i++)
|
||||
if (*array_value_get(value, i) == 0)
|
||||
array_seed_push_back(freelist, i);
|
||||
for M_EACH(b, bucket, array_bucket_t) {
|
||||
if (array_cstr_size(*b) != 1)
|
||||
continue;
|
||||
int32_t slot;
|
||||
array_seed_pop_back(&slot, freelist);
|
||||
/*We subtract one to ensure it's negative even if the zeroeth slot was
|
||||
used. */
|
||||
array_seed_set_at (seed, hash(0, *array_cstr_get(*b, 0)) % size,
|
||||
-slot-1);
|
||||
array_value_set_at(value, slot, *dict_mph_get(dict, *array_cstr_get(*b, 0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
CreateMinimalPerfectHash(array_seed_t seed, array_value_t value,
|
||||
const dict_mph_t dict)
|
||||
{
|
||||
M_LET(bucket, array_bucket_t) {
|
||||
CreateMinimalPerfectHash1(seed, value, bucket, dict);
|
||||
CreateMinimalPerfectHash2(seed, value, bucket, dict);
|
||||
CreateMinimalPerfectHash3(seed, value, bucket, dict);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dict_read_from_file(dict_mph_t dict, array_string_t arr, const char filename[])
|
||||
{
|
||||
FILE *f = fopen(filename, "rt");
|
||||
if (!f) abort();
|
||||
uint32_t line = 1;
|
||||
M_LET(str, string_t) {
|
||||
while (!feof(f) && !ferror(f)) {
|
||||
string_fgets(str, f, STRING_READ_PURE_LINE);
|
||||
string_strim(str);
|
||||
if (!string_empty_p(str)) {
|
||||
// Not clean as items of 'dict' points are linked to items of 'arr'
|
||||
// and the containers are not aware of this link.
|
||||
array_string_push_back (arr, str);
|
||||
dict_mph_set_at(dict, string_get_cstr(*array_string_back(arr)), line);
|
||||
line++;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
perfect_hash_lockup(array_seed_t seed, array_value_t value,
|
||||
const char key[])
|
||||
{
|
||||
int32_t s = *array_seed_get(seed, hash(0, key) % array_seed_size(seed));
|
||||
if (s < 0)
|
||||
return *array_value_get(value, -s-1);
|
||||
else
|
||||
return *array_value_get(value, hash(s, key) % array_value_size(value));
|
||||
}
|
||||
|
||||
static void
|
||||
test(array_seed_t seed, array_value_t value, dict_mph_t dict)
|
||||
{
|
||||
for M_EACH(item, dict, dict_mph_t) {
|
||||
const char *key = item->key;
|
||||
uint32_t v = perfect_hash_lockup(seed, value, key);
|
||||
if (v != item->value) {
|
||||
fprintf(stderr, "ERROR for %s: %u VS %u\n", key, v, item->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
M_LET(arr, array_string_t) M_LET(dict, dict_mph_t) M_LET(seed, array_seed_t)
|
||||
M_LET(value, array_value_t) {
|
||||
const char *filename = (argc > 1) ? argv[1] :"/usr/share/dict/words";
|
||||
dict_read_from_file(dict, arr, filename);
|
||||
CreateMinimalPerfectHash(seed, value, dict);
|
||||
test(seed, value, dict);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -47,8 +47,8 @@ static void program_init(program_t p, string_t text) {
|
||||
int left;
|
||||
array_int_pop_back(&left, leftstack);
|
||||
int right = pc;
|
||||
array_int_set_at2 (p->bracket_map, left, right);
|
||||
array_int_set_at2 (p->bracket_map, right, left);
|
||||
*array_int_get_at (p->bracket_map, left) = right;
|
||||
*array_int_get_at (p->bracket_map, right) = left;
|
||||
}
|
||||
|
||||
pc++;
|
||||
|
||||
@@ -12,7 +12,9 @@ unsigned long long rand_seed;
|
||||
#include "m-variant.h"
|
||||
|
||||
/* This example will show how to create JSON-like files.
|
||||
This is a JSON **like** syntax, not exactly JSON format */
|
||||
This is a JSON **like** syntax, not exactly JSON format.
|
||||
For an example with JSON format, see https://github.com/P-p-H-d/mjsonlib
|
||||
*/
|
||||
|
||||
/* First let's handle the OUT/IN functions of boolean */
|
||||
static inline void boolean_out_str(FILE *f, bool b)
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "m-tuple.h"
|
||||
#include "m-array.h"
|
||||
#include "m-string.h"
|
||||
#include "m-list.h"
|
||||
#include "m-buffer.h"
|
||||
|
||||
/* This example chains the structure in different ways in
|
||||
order to show/test the good chaining of the structures */
|
||||
|
||||
TUPLE_DEF2(symbol, (type, int), (name, string_t))
|
||||
#define M_OPL_symbol_t() TUPLE_OPLIST(symbol, M_DEFAULT_OPLIST, STRING_OPLIST)
|
||||
|
||||
ARRAY_DEF(symbol_list, symbol_t)
|
||||
#define M_OPL_symbol_list_t() ARRAY_OPLIST(symbol_list, M_OPL_symbol_t())
|
||||
|
||||
TUPLE_DEF2(record, (fields, symbol_list_t))
|
||||
#define M_OPL_record_t() TUPLE_OPLIST(record, M_OPL_symbol_list_t())
|
||||
|
||||
TUPLE_DEF2(my_tuple, (name, string_t), (value, int))
|
||||
#define M_OPL_my_tuple_t() TUPLE_OPLIST(my_tuple)
|
||||
|
||||
ARRAY_DEF(my_list_of_tuple, my_tuple_t)
|
||||
#define M_OPL_my_list_of_tuple_t() ARRAY_OPLIST(my_list_of_tuple)
|
||||
|
||||
TUPLE_DEF2(my_tuple_of_tuple, (name, string_t), (inner_tuple, my_tuple_t))
|
||||
#define M_OPL_my_tuple_of_tuple_t() TUPLE_OPLIST(my_tuple_of_tuple)
|
||||
|
||||
LIST_DEF(list_of_tuple_of_tuple, my_tuple_of_tuple_t)
|
||||
#define M_OPL_list_of_tuple_of_tuple_t() LIST_OPLIST(list_of_tuple_of_tuple, M_OPL_my_tuple_of_tuple_t() )
|
||||
|
||||
TUPLE_DEF2(my_triple_tuple, (name, string_t), (inner_tuple, my_tuple_t), (list, list_of_tuple_of_tuple_t) )
|
||||
#define M_OPL_my_triple_tuple_t() TUPLE_OPLIST(my_triple_tuple, STRING_OPLIST, M_OPL_my_tuple_t(), M_OPL_list_of_tuple_of_tuple_t())
|
||||
|
||||
BUFFER_DEF(trbuff, my_triple_tuple_t, 100, BUFFER_QUEUE)
|
||||
#define M_OPL_trbuff_t() BUFFER_OPLIST(trbuff, M_OPL_my_triple_tuple_t())
|
||||
|
||||
TUPLE_DEF2(my_junction, (active, bool), (buffer, trbuff_t))
|
||||
#define M_OPL_my_junction_t() TUPLE_OPLIST(my_junction, M_DEFAULT_OPLIST, M_OPL_trbuff_t())
|
||||
|
||||
TUPLE_DEF2(mlib_point, (x, int), (y, int))
|
||||
#define M_OPL_mlib_point_t() \
|
||||
TUPLE_OPLIST(mlib_point, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
TUPLE_DEF2(mlib_points, (first_ref, mlib_point_ptr, M_PTR_OPLIST),
|
||||
(second_ref, mlib_point_ptr, M_PTR_OPLIST),
|
||||
(third, mlib_point_t))
|
||||
#define M_OPL_mlib_points_t() \
|
||||
TUPLE_OPLIST(mlib_points, M_PTR_OPLIST, M_PTR_OPLIST, M_OPL_mlib_point_t())
|
||||
|
||||
static void let2(void)
|
||||
{
|
||||
M_LET((p1, 4, 4), (p2, 5, 5), mlib_point_t) {
|
||||
M_LET(pair, mlib_points_t) {
|
||||
pair->first_ref = p1;
|
||||
pair->second_ref = p2;
|
||||
pair->third->x = pair->third->y = 6;
|
||||
|
||||
assert(pair->first_ref->x == 4);
|
||||
p1->x = 10;
|
||||
assert(pair->first_ref->x == 10);
|
||||
|
||||
assert(pair->third->x == 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void let(void)
|
||||
{
|
||||
M_LET(s, symbol_t)
|
||||
M_LET(l, symbol_list_t)
|
||||
M_LET(r,record_t)
|
||||
M_LET(tup, my_tuple_t)
|
||||
M_LET(l2, my_list_of_tuple_t)
|
||||
M_LET(tuptup, my_tuple_of_tuple_t)
|
||||
M_LET(ltuptup, list_of_tuple_of_tuple_t)
|
||||
M_LET(tuptuptup, my_triple_tuple_t)
|
||||
M_LET(junc, my_junction_t)
|
||||
{
|
||||
printf("Init ok\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
let();
|
||||
let2();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - dynamic ARRAY module
|
||||
*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -22,33 +22,38 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __M_ARRAY_H
|
||||
#define __M_ARRAY_H
|
||||
#ifndef MSTARLIB_ARRAY_H
|
||||
#define MSTARLIB_ARRAY_H
|
||||
|
||||
#include "m-core.h"
|
||||
|
||||
/* Define an array of a given type.
|
||||
/* Define a dynamic array of the given type and its associated functions.
|
||||
USAGE: ARRAY_DEF(name, type [, oplist_of_the_type]) */
|
||||
#define ARRAY_DEF(name, ...) \
|
||||
ARRAYI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__), M_C(name,_t), M_C(name,_it_t) ), \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name,_it_t) ), \
|
||||
(name, __VA_ARGS__, M_C(name,_t), M_C(name,_it_t))))
|
||||
|
||||
/* Define the oplist of an array of type.
|
||||
/* Define the oplist of a dynamic array given its name and its oplist.
|
||||
USAGE: ARRAY_OPLIST(name[, oplist of the type]) */
|
||||
#define ARRAY_OPLIST(...) \
|
||||
ARRAYI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__) ), \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
|
||||
// Deferred evaluation for the oplist definition.
|
||||
#define ARRAYI_OPLIST(arg) ARRAYI_OPLIST2 arg
|
||||
|
||||
/* OPLIST definition of a dynamic array */
|
||||
/* FIXME: Do we want to export some methods as they are slow and
|
||||
are not fit to be used for building other methods (like _remove)? */
|
||||
#define ARRAYI_OPLIST2(name, oplist) \
|
||||
(INIT(M_C(name, _init)) \
|
||||
(INIT(M_C(name, _init)) \
|
||||
,INIT_SET(M_C(name, _init_set)) \
|
||||
,INIT_WITH(API_1(M_INIT_VAI)) \
|
||||
,SET(M_C(name, _set)) \
|
||||
,CLEAR(M_C(name, _clear)) \
|
||||
,INIT_MOVE(M_C(name, _init_move)) \
|
||||
@@ -56,6 +61,7 @@
|
||||
,SWAP(M_C(name, _swap)) \
|
||||
,TYPE(M_C(name,_t)) \
|
||||
,SUBTYPE(M_C(name, _type_t)) \
|
||||
,TEST_EMPTY(M_C(name,_empty_p)) \
|
||||
,IT_TYPE(M_C(name,_it_t)) \
|
||||
,IT_FIRST(M_C(name,_it)) \
|
||||
,IT_LAST(M_C(name,_it_last)) \
|
||||
@@ -68,13 +74,25 @@
|
||||
,IT_PREVIOUS(M_C(name,_previous)) \
|
||||
,IT_REF(M_C(name,_ref)) \
|
||||
,IT_CREF(M_C(name,_cref)) \
|
||||
,IT_REMOVE(M_C(name,_remove)) \
|
||||
,IT_INSERT(M_C(name,_insert)) \
|
||||
,IT_REMOVE(M_C(name,_remove)) \
|
||||
,CLEAN(M_C(name,_clean)) \
|
||||
,KEY_TYPE(size_t) \
|
||||
,VALUE_TYPE(M_C(name, _type_t)) \
|
||||
,KEY_OPLIST(M_DEFAULT_OPLIST) \
|
||||
,VALUE_OPLIST(oplist) \
|
||||
,SET_KEY(M_C(name, _set_at)) \
|
||||
,GET_KEY(M_C(name, _get)) \
|
||||
,M_IF_METHOD(INIT, oplist)(GET_SET_KEY(M_C(name, _get_at)) ,) \
|
||||
,ERASE_KEY(M_C(name, _erase)) \
|
||||
,PUSH(M_C(name,_push_back)) \
|
||||
,POP(M_C(name,_pop_back)) \
|
||||
,PUSH_MOVE(M_C(name,_push_move)) \
|
||||
,POP_MOVE(M_C(name,_pop_move)) \
|
||||
,OPLIST(oplist) \
|
||||
,M_IF_METHOD(CMP, oplist)(SORT(M_C(name, _special_sort)),) \
|
||||
,M_IF_METHOD(GET_STR, oplist)(GET_STR(M_C(name, _get_str)),) \
|
||||
,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_C(name, _parse_str)),) \
|
||||
,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_C(name, _out_str)),) \
|
||||
,M_IF_METHOD(IN_STR, oplist)(IN_STR(M_C(name, _in_str)),) \
|
||||
,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_C(name, _equal_p)),) \
|
||||
@@ -84,22 +102,26 @@
|
||||
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
|
||||
)
|
||||
|
||||
/* Define the contract of an array */
|
||||
#define ARRAYI_CONTRACT(a) do { \
|
||||
assert (a != NULL); \
|
||||
assert (a->size <= a->alloc); \
|
||||
assert (a->size == 0 || a->ptr != NULL); \
|
||||
} while (0)
|
||||
|
||||
// Deferred evaluation.
|
||||
// Deferred evaluation for the array definition.
|
||||
#define ARRAYI_DEF(arg) ARRAYI_DEF2 arg
|
||||
|
||||
// Internal definition.
|
||||
#define ARRAYI_DEF2(name, type, oplist, array_t, array_it_t) \
|
||||
\
|
||||
typedef struct M_C(name, _s) { \
|
||||
size_t size, alloc; \
|
||||
type *ptr; \
|
||||
size_t size; /* Number of elements in the array */ \
|
||||
size_t alloc; /* Allocated size for the array */ \
|
||||
type *ptr; /* Pointer to the array */ \
|
||||
} array_t[1]; \
|
||||
typedef struct M_C(name, _s) *M_C(name, _ptr); \
|
||||
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
|
||||
\
|
||||
typedef type M_C(name, _type_t); \
|
||||
\
|
||||
@@ -112,8 +134,10 @@
|
||||
M_C(name, _init)(array_t v) \
|
||||
{ \
|
||||
assert (v != NULL); \
|
||||
v->size = v->alloc = 0; \
|
||||
v->ptr = NULL; \
|
||||
/* Initialy, the array is empty with nothing allocated */ \
|
||||
v->size = 0; \
|
||||
v->alloc = 0; \
|
||||
v->ptr = NULL; \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
} \
|
||||
\
|
||||
@@ -122,7 +146,7 @@
|
||||
{ \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
for(size_t i = 0; i < v->size; i++) \
|
||||
M_GET_CLEAR oplist(v->ptr[i]); \
|
||||
M_CALL_CLEAR(oplist, v->ptr[i]); \
|
||||
v->size = 0; \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
} \
|
||||
@@ -132,7 +156,7 @@
|
||||
{ \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
M_C(name, _clean)(v); \
|
||||
M_GET_FREE oplist(v->ptr); \
|
||||
M_CALL_FREE(oplist, v->ptr); \
|
||||
v->alloc = 0; \
|
||||
v->ptr = NULL; \
|
||||
} \
|
||||
@@ -145,7 +169,7 @@
|
||||
if (M_UNLIKELY (d == s)) return; \
|
||||
if (s->size > d->alloc) { \
|
||||
const size_t alloc = s->size; \
|
||||
type *ptr = M_GET_REALLOC oplist (type, d->ptr, alloc); \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, d->ptr, alloc); \
|
||||
if (M_UNLIKELY (ptr == NULL)) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return ; \
|
||||
@@ -155,11 +179,11 @@
|
||||
} \
|
||||
size_t i, step1 = M_MIN(s->size, d->size); \
|
||||
for(i = 0; i < step1; i++) \
|
||||
M_GET_SET oplist (d->ptr[i], s->ptr[i]); \
|
||||
M_CALL_SET(oplist, d->ptr[i], s->ptr[i]); \
|
||||
for( ; i < s->size; i++) \
|
||||
M_GET_INIT_SET oplist (d->ptr[i], s->ptr[i]); \
|
||||
M_CALL_INIT_SET(oplist, d->ptr[i], s->ptr[i]); \
|
||||
for( ; i < d->size; i++) \
|
||||
M_GET_CLEAR oplist (d->ptr[i]); \
|
||||
M_CALL_CLEAR(oplist, d->ptr[i]); \
|
||||
d->size = s->size; \
|
||||
ARRAYI_CONTRACT(d); \
|
||||
} \
|
||||
@@ -180,7 +204,7 @@
|
||||
d->size = s->size; \
|
||||
d->alloc = s->alloc; \
|
||||
d->ptr = s->ptr; \
|
||||
s->size = s->alloc = 0; \
|
||||
s->alloc = 0; \
|
||||
s->ptr = NULL; \
|
||||
ARRAYI_CONTRACT(d); \
|
||||
} \
|
||||
@@ -198,10 +222,10 @@
|
||||
{ \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
assert(v->size > 0 && v->ptr != NULL && i < v->size); \
|
||||
M_GET_SET oplist(v->ptr[i], x); \
|
||||
M_CALL_SET(oplist, v->ptr[i], x); \
|
||||
} \
|
||||
\
|
||||
static inline const type * \
|
||||
static inline type const * \
|
||||
M_C(name, _back)(array_t v) \
|
||||
{ \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
@@ -215,13 +239,13 @@
|
||||
ARRAYI_CONTRACT(v); \
|
||||
if (M_UNLIKELY (v->size >= v->alloc)) { \
|
||||
assert(v->size == v->alloc); \
|
||||
size_t alloc = M_GET_INC_ALLOC oplist (v->alloc); \
|
||||
size_t alloc = M_CALL_INC_ALLOC(oplist, v->alloc); \
|
||||
if (M_UNLIKELY (alloc <= v->alloc)) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return NULL; \
|
||||
} \
|
||||
assert (alloc > v->size); \
|
||||
type *ptr = M_GET_REALLOC oplist (type, v->ptr, alloc); \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
|
||||
if (M_UNLIKELY (ptr == NULL) ) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return NULL; \
|
||||
@@ -242,17 +266,29 @@
|
||||
type *data = M_C(name, _push_raw)(v); \
|
||||
if (M_UNLIKELY (data == NULL) ) \
|
||||
return; \
|
||||
M_GET_INIT_SET oplist(*data, x); \
|
||||
M_CALL_INIT_SET(oplist, *data, x); \
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
static inline type * \
|
||||
M_C(name, _push_new)(array_t v) \
|
||||
{ \
|
||||
type *data = M_C(name, _push_raw)(v); \
|
||||
if (M_UNLIKELY (data == NULL) ) \
|
||||
return NULL; \
|
||||
M_GET_INIT oplist(*data); \
|
||||
M_CALL_INIT(oplist, *data); \
|
||||
return data; \
|
||||
} \
|
||||
, /* No INIT */ ) \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _push_move)(array_t v, type *x) \
|
||||
{ \
|
||||
assert (x != NULL); \
|
||||
type *data = M_C(name, _push_raw)(v); \
|
||||
if (M_UNLIKELY (data == NULL) ) \
|
||||
return; \
|
||||
M_DO_INIT_MOVE (oplist, *data, *x); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
@@ -262,13 +298,13 @@
|
||||
assert (key <= v->size); \
|
||||
if (M_UNLIKELY (v->size >= v->alloc) ) { \
|
||||
assert(v->size == v->alloc); \
|
||||
size_t alloc = M_GET_INC_ALLOC oplist (v->alloc); \
|
||||
size_t alloc = M_CALL_INC_ALLOC(oplist, v->alloc); \
|
||||
if (M_UNLIKELY (alloc <= v->alloc)) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return ; \
|
||||
} \
|
||||
assert (alloc > v->size); \
|
||||
type *ptr = M_GET_REALLOC oplist (type, v->ptr, alloc); \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
|
||||
if (M_UNLIKELY (ptr == NULL) ) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return; \
|
||||
@@ -279,10 +315,11 @@
|
||||
assert(v->ptr != NULL); \
|
||||
memmove(&v->ptr[key+1], &v->ptr[key], (v->size-key)*sizeof(type)); \
|
||||
v->size++; \
|
||||
M_GET_INIT_SET oplist (v->ptr[key], x); \
|
||||
M_CALL_INIT_SET(oplist, v->ptr[key], x); \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _resize)(array_t v, size_t size) \
|
||||
{ \
|
||||
@@ -290,13 +327,13 @@
|
||||
if (v->size > size) { \
|
||||
/* Decrease size of array */ \
|
||||
for(size_t i = size ; i < v->size; i++) \
|
||||
M_GET_CLEAR oplist(v->ptr[i]); \
|
||||
M_CALL_CLEAR(oplist, v->ptr[i]); \
|
||||
v->size = size; \
|
||||
} else if (v->size < size) { \
|
||||
/* Increase size of array */ \
|
||||
if (size > v->alloc) { \
|
||||
size_t alloc = size ; \
|
||||
type *ptr = M_GET_REALLOC oplist (type, v->ptr, alloc); \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
|
||||
if (M_UNLIKELY (ptr == NULL) ) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return; \
|
||||
@@ -305,11 +342,12 @@
|
||||
v->alloc = alloc; \
|
||||
} \
|
||||
for(size_t i = v->size ; i < size; i++) \
|
||||
M_GET_INIT oplist(v->ptr[i]); \
|
||||
M_CALL_INIT(oplist, v->ptr[i]); \
|
||||
v->size = size; \
|
||||
} \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
} \
|
||||
, /* No INIT */ ) \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _reserve)(array_t v, size_t alloc) \
|
||||
@@ -320,11 +358,11 @@
|
||||
alloc = v->size; \
|
||||
} \
|
||||
if (M_UNLIKELY (alloc == 0)) { \
|
||||
M_GET_FREE oplist (v->ptr); \
|
||||
M_CALL_FREE(oplist, v->ptr); \
|
||||
v->size = v->alloc = 0; \
|
||||
v->ptr = NULL; \
|
||||
} else { \
|
||||
type *ptr = M_GET_REALLOC oplist (type, v->ptr, alloc); \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
|
||||
if (M_UNLIKELY (ptr == NULL) ) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return; \
|
||||
@@ -335,49 +373,76 @@
|
||||
ARRAYI_CONTRACT(v); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _set_at2)(array_t v, size_t idx, type x) \
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
static inline type * \
|
||||
M_C(name, _get_at)(array_t v, size_t idx) \
|
||||
{ \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
const size_t size = idx + 1; \
|
||||
/* NOTE: set at and resize if needed */ \
|
||||
/* resize if needed */ \
|
||||
if (v->size <= size) { \
|
||||
/* Increase size of array */ \
|
||||
if (M_UNLIKELY (size > v->alloc) ) { \
|
||||
size_t alloc = M_GET_INC_ALLOC oplist (size) ; \
|
||||
size_t alloc = M_CALL_INC_ALLOC(oplist, size) ; \
|
||||
/* In case of overflow of size_t */ \
|
||||
if (M_UNLIKELY (alloc <= v->alloc)) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return ; \
|
||||
return NULL; \
|
||||
} \
|
||||
type *ptr = M_GET_REALLOC oplist (type, v->ptr, alloc); \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
|
||||
if (M_UNLIKELY (ptr == NULL) ) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return; \
|
||||
return NULL; \
|
||||
} \
|
||||
v->ptr = ptr; \
|
||||
v->alloc = alloc; \
|
||||
} \
|
||||
for(size_t i = v->size ; i < size; i++) \
|
||||
M_GET_INIT oplist(v->ptr[i]); \
|
||||
M_CALL_INIT(oplist, v->ptr[i]); \
|
||||
v->size = size; \
|
||||
} \
|
||||
assert (idx < v->size); \
|
||||
M_GET_SET oplist(v->ptr[idx], x); \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
return &v->ptr[idx]; \
|
||||
} \
|
||||
, /* No INIT */) \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _pop_back)(type *dest, array_t v) \
|
||||
{ \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
assert (v->size > 0 && v->ptr != NULL); \
|
||||
v->size--; \
|
||||
if (dest) \
|
||||
if (dest) { \
|
||||
M_DO_MOVE (oplist, *dest, v->ptr[v->size]); \
|
||||
else \
|
||||
M_GET_CLEAR oplist (v->ptr[v->size]); \
|
||||
} else { \
|
||||
M_CALL_CLEAR(oplist, v->ptr[v->size]); \
|
||||
} \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _pop_move)(type *dest, array_t v) \
|
||||
{ \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
assert (v->size > 0 && v->ptr != NULL); \
|
||||
assert (dest != NULL); \
|
||||
v->size--; \
|
||||
M_DO_INIT_MOVE (oplist, *dest, v->ptr[v->size]); \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _pop_until)(array_t v, array_it_t pos) \
|
||||
{ \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
assert (v == pos->array); \
|
||||
M_ASSUME (pos->index <= v->size); \
|
||||
M_C(name, _resize)(v, pos->index); \
|
||||
} \
|
||||
, /* No INIT */ ) \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _empty_p)(const array_t v) \
|
||||
{ \
|
||||
@@ -404,28 +469,40 @@
|
||||
{ \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
assert (v->size > 0 && v->ptr != NULL); \
|
||||
assert (i < v->size); \
|
||||
if (dest) \
|
||||
M_DO_MOVE (oplist, *dest, v->ptr[i]); \
|
||||
else \
|
||||
M_GET_CLEAR oplist (v->ptr[i]); \
|
||||
M_CALL_CLEAR(oplist, v->ptr[i]); \
|
||||
memmove(&v->ptr[i], &v->ptr[i+1], sizeof(type)*(v->size - i) ); \
|
||||
v->size--; \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _erase)(array_t a, size_t i) \
|
||||
{ \
|
||||
ARRAYI_CONTRACT(a); \
|
||||
if (i >= a->size) return false; \
|
||||
M_C(name, _pop_at)(NULL, a, i); \
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _insert_v)(array_t v, size_t i, size_t num) \
|
||||
{ \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
assert(i <= v->size); \
|
||||
size_t size = v->size + num; \
|
||||
if (size > v->alloc) { \
|
||||
size_t alloc = M_GET_INC_ALLOC oplist (size) ; \
|
||||
if (M_UNLIKELY (alloc <= v->alloc)) { \
|
||||
/* Avoid overflow of v->size + num */ \
|
||||
if (v->size > v->alloc-num) { \
|
||||
size_t alloc = M_CALL_INC_ALLOC(oplist, size) ; \
|
||||
if (M_UNLIKELY (size <= v->size || alloc <= v->alloc)) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return ; \
|
||||
} \
|
||||
type *ptr = M_GET_REALLOC oplist (type, v->ptr, alloc); \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
|
||||
if (M_UNLIKELY (ptr == NULL) ) { \
|
||||
M_MEMORY_FULL(sizeof (type) * alloc); \
|
||||
return; \
|
||||
@@ -435,10 +512,11 @@
|
||||
} \
|
||||
memmove(&v->ptr[i+num], &v->ptr[i], sizeof(type)*(v->size - i) ); \
|
||||
for(size_t k = i ; k < i+num; k++) \
|
||||
M_GET_INIT oplist(v->ptr[k]); \
|
||||
M_CALL_INIT(oplist, v->ptr[k]); \
|
||||
v->size = size; \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
} \
|
||||
, /* No INIT */) \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _remove_v)(array_t v, size_t i, size_t j) \
|
||||
@@ -446,7 +524,7 @@
|
||||
ARRAYI_CONTRACT(v); \
|
||||
assert(i < v->size && j <= v->size && i < j && v->ptr != NULL); \
|
||||
for(size_t k = i ; k < j; k++) \
|
||||
M_GET_CLEAR oplist(v->ptr[k]); \
|
||||
M_CALL_CLEAR(oplist, v->ptr[k]); \
|
||||
memmove(&v->ptr[i], &v->ptr[j], sizeof(type)*(v->size - j) ); \
|
||||
v->size -= (j-i); \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
@@ -477,14 +555,14 @@
|
||||
assert(i < v->size && j < v->size && v->ptr != NULL); \
|
||||
type tmp; \
|
||||
M_IF_METHOD(INIT_MOVE, oplist) ( \
|
||||
M_GET_INIT_MOVE oplist (tmp, v->ptr[i]); \
|
||||
M_GET_INIT_MOVE oplist (v->ptr[i], v->ptr[j]); \
|
||||
M_GET_INIT_MOVE oplist (v->ptr[j], tmp); \
|
||||
, \
|
||||
M_GET_INIT_SET oplist (tmp, v->ptr[i]); \
|
||||
M_GET_SET oplist(v->ptr[i], v->ptr[j]); \
|
||||
M_GET_SET oplist(v->ptr[j], tmp); \
|
||||
M_GET_CLEAR oplist(tmp); \
|
||||
M_CALL_INIT_MOVE(oplist, tmp, v->ptr[i]); \
|
||||
M_CALL_INIT_MOVE(oplist, v->ptr[i], v->ptr[j]); \
|
||||
M_CALL_INIT_MOVE(oplist, v->ptr[j], tmp); \
|
||||
, \
|
||||
M_CALL_INIT_SET(oplist, tmp, v->ptr[i]); \
|
||||
M_CALL_SET(oplist, v->ptr[i], v->ptr[j]); \
|
||||
M_CALL_SET(oplist, v->ptr[j], tmp); \
|
||||
M_CALL_CLEAR(oplist, tmp); \
|
||||
) /* IF INIT_MOVE method */ \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
} \
|
||||
@@ -497,7 +575,7 @@
|
||||
return &v->ptr[i]; \
|
||||
} \
|
||||
\
|
||||
static inline const type * \
|
||||
static inline type const * \
|
||||
M_C(name, _cget)(const array_t v, size_t i) \
|
||||
{ \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
@@ -505,6 +583,14 @@
|
||||
return M_CONST_CAST(type, &v->ptr[i]); \
|
||||
} \
|
||||
\
|
||||
static inline type const * \
|
||||
M_C(name, _front)(const array_t v) \
|
||||
{ \
|
||||
ARRAYI_CONTRACT(v); \
|
||||
assert (v->size > 0); \
|
||||
return M_C(name, _cget)(v, 0); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it)(array_it_t it, const array_t v) \
|
||||
{ \
|
||||
@@ -588,7 +674,7 @@
|
||||
return M_C(name, _get)(it->array, it->index); \
|
||||
} \
|
||||
\
|
||||
static inline const type * \
|
||||
static inline type const * \
|
||||
M_C(name, _cref)(const array_it_t it) \
|
||||
{ \
|
||||
assert(it != NULL); \
|
||||
@@ -613,7 +699,7 @@
|
||||
M_IF_METHOD(CMP, oplist) \
|
||||
( \
|
||||
static inline void M_C(name, _special_sort)(array_t l, \
|
||||
int (*func_type) (const type *a, const type *b)) \
|
||||
int (*func_type) (type const *a, type const *b)) \
|
||||
{ \
|
||||
/* Using qsort is more compact but slower than a full templated \
|
||||
version which can be twice faster */ \
|
||||
@@ -622,27 +708,116 @@
|
||||
func_void = (int (*)(const void*, const void*))func_type; \
|
||||
qsort (l->ptr, l->size, sizeof(type), func_void); \
|
||||
} \
|
||||
,) /* IF CMP oplist */ \
|
||||
\
|
||||
M_IF_METHOD(SWAP, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _special_stable_sort_noalloc) (type tab[], size_t size, type tmp[]) \
|
||||
{ \
|
||||
size_t th = 4; \
|
||||
M_IF_DEBUG(type *org_tab = tab;) \
|
||||
assert (size > 1); \
|
||||
/* Let's select the threshold of the pass 1 to be sure \
|
||||
the final result is in tab.*/ \
|
||||
if (m_core_clz64(size-1) & 1) \
|
||||
th += th; \
|
||||
\
|
||||
/* Pass 1: insertion sort (stable) */ \
|
||||
for(size_t k = 0 ; k < size; ) { \
|
||||
size_t max = size - k < 2*th ? size - k : th; \
|
||||
M_ASSUME(max >= th); \
|
||||
for(size_t i = 1; i < max; i++) { \
|
||||
size_t j = i; \
|
||||
while (j > 0 && M_CALL_CMP(oplist, tab[k+j-1], tab[k+j]) > 0) { \
|
||||
M_CALL_SWAP(oplist, tab[k+j-1], tab[k+j]); \
|
||||
j = j - 1; \
|
||||
} \
|
||||
} \
|
||||
k += max; \
|
||||
} \
|
||||
\
|
||||
/* N Pass of merge */ \
|
||||
while (th < size) { \
|
||||
type *dest = tmp; \
|
||||
/* Pass n: Merge */ \
|
||||
for(size_t k = 0 ; k < size; ) { \
|
||||
type *el1 = &tab[k]; \
|
||||
type *el2 = &tab[k+th]; \
|
||||
size_t n1 = th; \
|
||||
size_t n2 = size-k <= 3*th ? size-k-th : th; \
|
||||
assert (size-k > th); \
|
||||
assert (0 < n1 && n1 <= size); \
|
||||
assert (0 < n2 && n2 <= size); \
|
||||
k += n1+n2; \
|
||||
for (;;) { \
|
||||
if (M_CALL_CMP(oplist, *el1, *el2) <= 0) { \
|
||||
M_CALL_SET(oplist, *dest, *el1); \
|
||||
dest++; \
|
||||
el1++; \
|
||||
if (-- n1 == 0) { \
|
||||
if (n2 > 0) { \
|
||||
memcpy (dest, el2, n2 * sizeof (type)); \
|
||||
dest += n2; \
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
} else { \
|
||||
M_CALL_SET(oplist, *dest, *el2); \
|
||||
dest++; \
|
||||
el2++; \
|
||||
if (-- n2 == 0) { \
|
||||
if (n1 > 0) { \
|
||||
memcpy (dest, el1, n1 * sizeof (type)); \
|
||||
dest += n1; \
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
/* Swap t & tab */ \
|
||||
M_SWAP(type *, tab, tmp); \
|
||||
/* Increase th for next pass */ \
|
||||
th += th; \
|
||||
} \
|
||||
assert (org_tab == tab); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _special_stable_sort)(array_t l) \
|
||||
{ \
|
||||
if (M_UNLIKELY (l->size < 2)) \
|
||||
return; \
|
||||
type *temp = M_CALL_REALLOC(oplist, type, NULL, l->size); \
|
||||
if (temp == NULL) { \
|
||||
M_MEMORY_FULL(sizeof (type) * l->size); \
|
||||
return ; \
|
||||
} \
|
||||
M_C(name, _special_stable_sort_noalloc)(l->ptr, l->size, temp); \
|
||||
M_CALL_FREE(oplist, temp); \
|
||||
} \
|
||||
,) /* IF SWAP method */ \
|
||||
\
|
||||
,) /* IF CMP oplist */ \
|
||||
\
|
||||
M_IF_METHOD(GET_STR, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _get_str)(string_t str, array_t array, \
|
||||
M_C(name, _get_str)(string_t str, array_t const array, \
|
||||
bool append) \
|
||||
{ \
|
||||
STRING_CONTRACT(str); \
|
||||
STRINGI_CONTRACT(str); \
|
||||
ARRAYI_CONTRACT(array); \
|
||||
(append ? string_cat_str : string_set_str) (str, "["); \
|
||||
array_it_t it; \
|
||||
for (M_C(name, _it)(it, array) ; \
|
||||
!M_C(name, _end_p)(it); \
|
||||
M_C(name, _next)(it)){ \
|
||||
const type *item = M_C(name, _cref)(it); \
|
||||
M_GET_GET_STR oplist (str, *item, true); \
|
||||
type const *item = M_C(name, _cref)(it); \
|
||||
M_CALL_GET_STR(oplist, str, *item, true); \
|
||||
if (!M_C(name, _last_p)(it)) \
|
||||
string_push_back (str, M_GET_SEPARATOR oplist); \
|
||||
} \
|
||||
string_push_back (str, ']'); \
|
||||
STRING_CONTRACT(str); \
|
||||
STRINGI_CONTRACT(str); \
|
||||
} \
|
||||
, /* no GET_STR */ ) \
|
||||
\
|
||||
@@ -654,8 +829,8 @@
|
||||
assert (file != NULL); \
|
||||
fputc ('[', file); \
|
||||
for (size_t i = 0; i < array->size; i++) { \
|
||||
const type *item = M_C(name, _cget)(array, i); \
|
||||
M_GET_OUT_STR oplist (file, *item); \
|
||||
type const *item = M_C(name, _cget)(array, i); \
|
||||
M_CALL_OUT_STR(oplist, file, *item); \
|
||||
if (i != array->size-1) \
|
||||
fputc (M_GET_SEPARATOR oplist, file); \
|
||||
} \
|
||||
@@ -663,7 +838,38 @@
|
||||
} \
|
||||
, /* no OUT_STR */ ) \
|
||||
\
|
||||
M_IF_METHOD(IN_STR, oplist)( \
|
||||
M_IF_METHOD2(PARSE_STR, INIT, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _parse_str)(array_t array, const char str[], const char**endp) \
|
||||
{ \
|
||||
ARRAYI_CONTRACT(array); \
|
||||
assert (str != NULL); \
|
||||
M_C(name,_clean)(array); \
|
||||
bool success = false; \
|
||||
int c = *str++; \
|
||||
if (M_UNLIKELY (c != '[')) goto exit; \
|
||||
c = *str++; \
|
||||
if (M_UNLIKELY (c == ']')) { success = true; goto exit; } \
|
||||
if (M_UNLIKELY (c == 0)) goto exit; \
|
||||
str--; \
|
||||
type item; \
|
||||
M_CALL_INIT(oplist, item); \
|
||||
do { \
|
||||
bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \
|
||||
do { c = *str++; } while (isspace(c)); \
|
||||
if (b == false || c == 0) { goto exit; } \
|
||||
M_C(name, _push_back)(array, item); \
|
||||
} while (c == M_GET_SEPARATOR oplist); \
|
||||
M_CALL_CLEAR(oplist, item); \
|
||||
ARRAYI_CONTRACT(array); \
|
||||
success = (c == ']'); \
|
||||
exit: \
|
||||
if (endp) *endp = str; \
|
||||
return success; \
|
||||
} \
|
||||
, /* no PARSE_STR & INIT */ ) \
|
||||
\
|
||||
M_IF_METHOD2(IN_STR, INIT, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _in_str)(array_t array, FILE *file) \
|
||||
{ \
|
||||
@@ -677,18 +883,18 @@
|
||||
if (M_UNLIKELY (c == EOF)) return false; \
|
||||
ungetc(c, file); \
|
||||
type item; \
|
||||
M_GET_INIT oplist (item); \
|
||||
M_CALL_INIT(oplist, item); \
|
||||
do { \
|
||||
bool b = M_GET_IN_STR oplist (item, file); \
|
||||
c = fgetc(file); \
|
||||
bool b = M_CALL_IN_STR(oplist, item, file); \
|
||||
do { c = fgetc(file); } while (isspace(c)); \
|
||||
if (b == false || c == EOF) { break; } \
|
||||
M_C(name, _push_back)(array, item); \
|
||||
} while (c == M_GET_SEPARATOR oplist); \
|
||||
M_GET_CLEAR oplist (item); \
|
||||
M_CALL_CLEAR(oplist, item); \
|
||||
ARRAYI_CONTRACT(array); \
|
||||
return c == ']'; \
|
||||
} \
|
||||
, /* no IN_STR */ ) \
|
||||
, /* no IN_STR & INIT */ ) \
|
||||
\
|
||||
M_IF_METHOD(EQUAL, oplist)( \
|
||||
static inline bool \
|
||||
@@ -700,9 +906,9 @@
|
||||
if (array1->size != array2->size) return false; \
|
||||
size_t i; \
|
||||
for(i = 0; i < array1->size; i++) { \
|
||||
const type *item1 = M_C(name, _cget)(array1, i); \
|
||||
const type *item2 = M_C(name, _cget)(array2, i); \
|
||||
bool b = M_GET_EQUAL oplist (*item1, *item2); \
|
||||
type const *item1 = M_C(name, _cget)(array1, i); \
|
||||
type const *item2 = M_C(name, _cget)(array2, i); \
|
||||
bool b = M_CALL_EQUAL(oplist, *item1, *item2); \
|
||||
if (!b) return false; \
|
||||
} \
|
||||
return i == array1->size; \
|
||||
@@ -716,11 +922,32 @@
|
||||
ARRAYI_CONTRACT(array); \
|
||||
M_HASH_DECL(hash); \
|
||||
for(size_t i = 0 ; i < array->size; i++) { \
|
||||
size_t hi = M_GET_HASH oplist (array->ptr[i]); \
|
||||
size_t hi = M_CALL_HASH(oplist, array->ptr[i]); \
|
||||
M_HASH_UP(hash, hi); \
|
||||
} \
|
||||
return M_HASH_FINAL (hash); \
|
||||
} \
|
||||
, /* no HASH */ ) \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _splice)(array_t a1, array_t a2) \
|
||||
{ \
|
||||
ARRAYI_CONTRACT(a1); \
|
||||
ARRAYI_CONTRACT(a2); \
|
||||
if (M_LIKELY (a2->size > 0)) { \
|
||||
size_t newSize = a1->size + a2->size; \
|
||||
if (newSize > a1->alloc) { \
|
||||
type *ptr = M_CALL_REALLOC(oplist, type, a1->ptr, newSize); \
|
||||
if (M_UNLIKELY (ptr == NULL) ) { \
|
||||
M_MEMORY_FULL(sizeof (type) * newSize); \
|
||||
} \
|
||||
a1->ptr = ptr; \
|
||||
a1->alloc = newSize; \
|
||||
} \
|
||||
memcpy(&a1->ptr[a1->size], &a2->ptr[0], a2->size * sizeof (type)); \
|
||||
a2->size = 0; \
|
||||
a1->size = newSize; \
|
||||
} \
|
||||
} \
|
||||
|
||||
#endif
|
||||
+49
-42
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - Thin stdatomic wrapper for C++ compatibility
|
||||
*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -22,8 +22,8 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __M_ATOMIC_H
|
||||
#define __M_ATOMIC_H
|
||||
#ifndef MSTARLIB_ATOMIC_H
|
||||
#define MSTARLIB_ATOMIC_H
|
||||
|
||||
/* NOTE: Due to the C++ not having recognized stdatomic.h officialy,
|
||||
it is hard to use this header directly with a C++ compiler like
|
||||
@@ -89,6 +89,13 @@ using std::atomic_flag_test_and_set_explicit;
|
||||
using std::atomic_flag_clear;
|
||||
using std::atomic_flag_clear_explicit;
|
||||
|
||||
using std::memory_order_relaxed;
|
||||
using std::memory_order_consume;
|
||||
using std::memory_order_acquire;
|
||||
using std::memory_order_release;
|
||||
using std::memory_order_acq_rel;
|
||||
using std::memory_order_seq_cst;
|
||||
|
||||
#define _Atomic(T) std::atomic< T >
|
||||
|
||||
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) \
|
||||
@@ -111,20 +118,20 @@ using std::atomic_flag_clear_explicit;
|
||||
#include "m-mutex.h"
|
||||
|
||||
/* The structure is quite large:
|
||||
__val : value of the atomic type,
|
||||
__zero : zero value of the atomic type (constant),
|
||||
__previous: temporary value used within the mutex lock,
|
||||
__lock : the mutex lock.
|
||||
_val : value of the atomic type,
|
||||
_zero : zero value of the atomic type (constant),
|
||||
_previous: temporary value used within the mutex lock,
|
||||
_lock : the mutex lock.
|
||||
*/
|
||||
#define _Atomic(T) \
|
||||
struct { \
|
||||
volatile T __val; \
|
||||
T __zero; \
|
||||
T __previous; \
|
||||
m_mutex_t __lock; \
|
||||
volatile T _val; \
|
||||
T _zero; \
|
||||
T _previous; \
|
||||
m_mutex_t _lock; \
|
||||
}
|
||||
|
||||
/* Even memory order is defined, only the strongest constraint is used */
|
||||
/* Even if memory order is defined, only the strongest constraint is used */
|
||||
typedef enum {
|
||||
memory_order_relaxed,
|
||||
memory_order_consume,
|
||||
@@ -152,7 +159,7 @@ typedef _Atomic(size_t) atomic_size_t;
|
||||
typedef _Atomic(ptrdiff_t) atomic_ptrdiff_t;
|
||||
|
||||
/* Define the minimum size supported by the architecture
|
||||
for atomic read or write.
|
||||
for an atomic read or write.
|
||||
This can help a lot since it avoids locking for atomic_load and
|
||||
atomic_store.
|
||||
*/
|
||||
@@ -183,16 +190,16 @@ static inline long long atomic_fetch_unlock (m_mutex_t *lock, long long val)
|
||||
lock the atomic value, read it and returns the value.
|
||||
In order to avoid any compiler extension, we need to transform the
|
||||
atomic type into 'long long' then convert it back to its value.
|
||||
This is because __previous can't be read after the lock, and we can't
|
||||
This is because _previous can't be read after the lock, and we can't
|
||||
generate temporary variable within a macro.
|
||||
The trick is computing _val - _zero within the lock, then
|
||||
returns retvalue + _zero after the lock.
|
||||
*/
|
||||
#define atomic_fetch_op(ptr, val, op) \
|
||||
(m_mutex_lock((ptr)->__lock), \
|
||||
(ptr)->__previous = (ptr)->__val, \
|
||||
(ptr)->__val op (val), \
|
||||
atomic_fetch_unlock(&(ptr)->__lock, (ptr)->__previous-(ptr)->__zero)+(ptr)->__zero)
|
||||
(m_mutex_lock((ptr)->_lock), \
|
||||
(ptr)->_previous = (ptr)->_val, \
|
||||
(ptr)->_val op (val), \
|
||||
atomic_fetch_unlock(&(ptr)->_lock, (ptr)->_previous-(ptr)->_zero)+(ptr)->_zero)
|
||||
|
||||
#define atomic_fetch_add(ptr, val) atomic_fetch_op(ptr, val, +=)
|
||||
#define atomic_fetch_sub(ptr, val) atomic_fetch_op(ptr, val, -=)
|
||||
@@ -201,41 +208,41 @@ static inline long long atomic_fetch_unlock (m_mutex_t *lock, long long val)
|
||||
#define atomic_fetch_and(ptr, val) atomic_fetch_op(ptr, val, &=)
|
||||
#define atomic_exchange(ptr, val) atomic_fetch_op(ptr, val, =)
|
||||
|
||||
#define ATOMIC_VAR_INIT(val) { val, 0, 0, 0, M_MUTEXI_INIT_VALUE }
|
||||
#define ATOMIC_VAR_INIT(val) { val, 0, 0, M_MUTEXI_INIT_VALUE }
|
||||
|
||||
#define atomic_init(ptr, val) \
|
||||
(m_mutex_init((ptr)->__lock), (ptr)->__val = val, (ptr)->__zero = 0)
|
||||
(m_mutex_init((ptr)->_lock), (ptr)->_val = val, (ptr)->_zero = 0)
|
||||
|
||||
#define atomic_load_lock(ptr) \
|
||||
(m_mutex_lock((ptr)->__lock), \
|
||||
(ptr)->__previous = (ptr)->__val, \
|
||||
atomic_fetch_unlock(&(ptr)->__lock, (ptr)->__previous-(ptr)->__zero)+(ptr)->__zero)
|
||||
(m_mutex_lock((ptr)->_lock), \
|
||||
(ptr)->_previous = (ptr)->_val, \
|
||||
atomic_fetch_unlock(&(ptr)->_lock, (ptr)->_previous-(ptr)->_zero)+(ptr)->_zero)
|
||||
|
||||
#define atomic_store_lock(ptr, val) \
|
||||
(m_mutex_lock((ptr)->__lock), \
|
||||
(ptr)->__val = (val), \
|
||||
m_mutex_unlock((ptr)->__lock))
|
||||
(m_mutex_lock((ptr)->_lock), \
|
||||
(ptr)->_val = (val), \
|
||||
m_mutex_unlock((ptr)->_lock))
|
||||
|
||||
#define atomic_load(ptr) \
|
||||
( sizeof ((ptr)->__val) <= ATOMICI_MIN_RW_SIZE \
|
||||
? (ptr)->__val \
|
||||
( sizeof ((ptr)->_val) <= ATOMICI_MIN_RW_SIZE \
|
||||
? (ptr)->_val \
|
||||
: atomic_load_lock(ptr))
|
||||
|
||||
|
||||
#define atomic_store(ptr, val) do { \
|
||||
if ( sizeof ((ptr)->__val) <= ATOMICI_MIN_RW_SIZE) { \
|
||||
(ptr)->__val = (val); \
|
||||
if ( sizeof ((ptr)->_val) <= ATOMICI_MIN_RW_SIZE) { \
|
||||
(ptr)->_val = (val); \
|
||||
} else { \
|
||||
long long _offset = (val) - (ptr)->__zero; \
|
||||
atomic_store_lock(ptr, (ptr)->__zero + _offset); \
|
||||
long long _offset = (val) - (ptr)->_zero; \
|
||||
atomic_store_lock(ptr, (ptr)->_zero + _offset); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define atomic_compare_exchange_strong(ptr, exp, val) \
|
||||
(m_mutex_lock((ptr)->__lock), \
|
||||
atomic_fetch_unlock(&(ptr)->__lock, \
|
||||
(ptr)->__val == *(exp) \
|
||||
? ((ptr)->__val = (val), true) \
|
||||
: (*(exp) = (ptr)->__val, false)))
|
||||
#define atomic_compare_exchange_strong(ptr, exp, val) \
|
||||
(m_mutex_lock((ptr)->_lock), \
|
||||
atomic_fetch_unlock(&(ptr)->_lock, \
|
||||
(ptr)->_val == *(exp) \
|
||||
? ((ptr)->_val = (val), true) \
|
||||
: (*(exp) = (ptr)->_val, false)))
|
||||
|
||||
|
||||
#define atomic_fetch_add_explicit(ptr, val, mem) atomic_fetch_op(ptr, val, +=)
|
||||
@@ -250,9 +257,9 @@ static inline long long atomic_fetch_unlock (m_mutex_t *lock, long long val)
|
||||
#define atomic_thread_fence(mem) (void) 0
|
||||
#define atomic_signal_fence(mem) (void) 0
|
||||
#define atomic_is_lock_free(ptr) false
|
||||
#define atomic_compare_exchange_strong_explicit(ptr, exp, val, mem) atomic_compare_exchange_strong(ptr, exp, val)
|
||||
#define atomic_compare_exchange_weak_explicit(ptr, exp, val, mem) atomic_compare_exchange_strong(ptr, exp, val)
|
||||
#define atomic_compare_exchange_weak(ptr, exp, val) atomic_compare_exchange_strong(ptr, exp, val)
|
||||
#define atomic_compare_exchange_strong_explicit(ptr, exp, val, mem1, mem2) atomic_compare_exchange_strong(ptr, exp, val)
|
||||
#define atomic_compare_exchange_weak_explicit(ptr, exp, val, mem1, mem2) atomic_compare_exchange_strong(ptr, exp, val)
|
||||
#define atomic_compare_exchange_weak(ptr, exp, val) atomic_compare_exchange_strong(ptr, exp, val)
|
||||
|
||||
/* TODO: Missing atomic_flag. Problem: it needs to be lock free! */
|
||||
|
||||
|
||||
+81
-29
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - BITSET module
|
||||
*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -22,15 +22,16 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __M_BITSET_H
|
||||
#define __M_BITSET_H
|
||||
#ifndef MSTARLIB_BITSET_H
|
||||
#define MSTARLIB_BITSET_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "m-core.h"
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
|
||||
// Define a limb of a bitset
|
||||
typedef unsigned long bitset_limb;
|
||||
typedef uint64_t bitset_limb;
|
||||
#define BITSET_LIMB_BIT (sizeof(bitset_limb) * CHAR_BIT)
|
||||
|
||||
typedef struct bitset_s {
|
||||
@@ -38,6 +39,8 @@ typedef struct bitset_s {
|
||||
size_t alloc; // Alloc is the number of allocated limbs
|
||||
bitset_limb *ptr; // Pointer to the allocated limbs
|
||||
} bitset_t[1];
|
||||
typedef struct bitset_s *bitset_ptr;
|
||||
typedef const struct bitset_s *bitset_srcptr;
|
||||
|
||||
typedef struct bitset_it_s {
|
||||
size_t index; // index to the array of bit
|
||||
@@ -46,7 +49,7 @@ typedef struct bitset_it_s {
|
||||
} bitset_it_t[1];
|
||||
|
||||
// bitset grow policy
|
||||
#define BITSETI_INC_ALLOC_SIZE(n) ((n) < 2 ? 4 : (n) * 2)
|
||||
#define BITSETI_INC_ALLOC_SIZE(n) ((n) < 4 ? 4 : (n) * 2)
|
||||
|
||||
// Compute the number of allocated limbs needed to handle 'n' bits.
|
||||
#define BITSETI_TO_ALLOC(n) (((n) + BITSET_LIMB_BIT - 1) / BITSET_LIMB_BIT)
|
||||
@@ -70,7 +73,8 @@ bitset_init(bitset_t t)
|
||||
{
|
||||
assert (t != NULL);
|
||||
assert (M_POWEROF2_P(BITSET_LIMB_BIT));
|
||||
t->size = t->alloc = 0;
|
||||
t->size = 0;
|
||||
t->alloc = 0;
|
||||
t->ptr = NULL;
|
||||
BITSETI_CONTRACT(t);
|
||||
}
|
||||
@@ -145,11 +149,11 @@ bitset_set_at(bitset_t v, size_t i, bool x)
|
||||
{
|
||||
BITSETI_CONTRACT(v);
|
||||
assert (i < v->size && v->ptr != NULL);
|
||||
size_t offset = i / BITSET_LIMB_BIT;
|
||||
size_t index = i % BITSET_LIMB_BIT;
|
||||
// This is a branchless version as x can only be 0 or 1.
|
||||
v->ptr[offset] &= ~(((bitset_limb)1)<<index);
|
||||
v->ptr[offset] |= ((bitset_limb)x)<<index;
|
||||
const size_t offset = i / BITSET_LIMB_BIT;
|
||||
const size_t index = i % BITSET_LIMB_BIT;
|
||||
// This is a branchless version as x can only be 0 or 1 with only one variable shift.
|
||||
const bitset_limb mask = ((bitset_limb)1)<<index;
|
||||
v->ptr[offset] = (v->ptr[offset] & ~mask) | (mask & (-(bitset_limb)x));
|
||||
BITSETI_CONTRACT (v);
|
||||
}
|
||||
|
||||
@@ -183,6 +187,7 @@ bitset_push_back (bitset_t v, bool x)
|
||||
return;
|
||||
}
|
||||
// Clear allocated memory
|
||||
// FIXME: Why?
|
||||
for(size_t a = v->alloc; a < needAlloc; a++)
|
||||
ptr[a] = 0;
|
||||
v->ptr = ptr;
|
||||
@@ -272,6 +277,14 @@ bitset_pop_back(bool *dest, bitset_t v)
|
||||
BITSETI_CONTRACT (v);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
bitset_front(bitset_t v)
|
||||
{
|
||||
BITSETI_CONTRACT (v);
|
||||
assert(v->size > 0);
|
||||
return bitset_get(v, 0);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
bitset_back(bitset_t v)
|
||||
{
|
||||
@@ -407,7 +420,7 @@ bitset_equal_p (const bitset_t set1, const bitset_t set2)
|
||||
/* Compare the last limb if needed */
|
||||
const size_t index = set1->size % BITSET_LIMB_BIT;
|
||||
if (index > 0) {
|
||||
const size_t mask = (((bitset_limb)1) << index) - 1;
|
||||
const bitset_limb mask = (((bitset_limb)1) << index) - 1;
|
||||
if ((set1->ptr[limbSize] & mask) != (set2->ptr[limbSize] & mask))
|
||||
return false;
|
||||
}
|
||||
@@ -523,22 +536,32 @@ bitset_in_str(bitset_t set, FILE *file)
|
||||
}
|
||||
|
||||
static inline bool
|
||||
bitset_set_str(bitset_t set, const char str[])
|
||||
bitset_parse_str(bitset_t set, const char str[], const char **endptr)
|
||||
{
|
||||
BITSETI_CONTRACT (set);
|
||||
assert(str != NULL);
|
||||
bool success = false;
|
||||
bitset_clean(set);
|
||||
char c = *str++;
|
||||
if (c != '[') return false;
|
||||
if (c != '[') goto exit;
|
||||
c = *str++;
|
||||
do {
|
||||
if (c != '0' && c != '1') return false;
|
||||
if (c != '0' && c != '1') goto exit;
|
||||
const bool b = (c == '1');
|
||||
bitset_push_back (set, b);
|
||||
c = *str++;
|
||||
} while (c != ']' && c != 0);
|
||||
BITSETI_CONTRACT (set);
|
||||
return c == ']';
|
||||
success = (c == ']');
|
||||
exit:
|
||||
if (endptr) *endptr = str;
|
||||
return success;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
bitset_set_str(bitset_t dest, const char str[])
|
||||
{
|
||||
return bitset_parse_str(dest, str, NULL);
|
||||
}
|
||||
|
||||
static inline void
|
||||
@@ -552,7 +575,7 @@ bitset_and(bitset_t dest, const bitset_t src)
|
||||
for(size_t i = 0 ; i < n; i++)
|
||||
dest->ptr[i] &= src->ptr[i];
|
||||
if (m) {
|
||||
size_t mask = (((bitset_limb)1) << m) - 1;
|
||||
bitset_limb mask = (((bitset_limb)1) << m) - 1;
|
||||
dest->ptr[n] = (dest->ptr[n] & src->ptr[n]) & mask;
|
||||
}
|
||||
dest->size = s;
|
||||
@@ -570,7 +593,7 @@ bitset_or(bitset_t dest, const bitset_t src)
|
||||
for(size_t i = 0 ; i < n; i++)
|
||||
dest->ptr[i] |= src->ptr[i];
|
||||
if (m) {
|
||||
size_t mask = (((bitset_limb)1) << m) - 1;
|
||||
bitset_limb mask = (((bitset_limb)1) << m) - 1;
|
||||
dest->ptr[n] = (dest->ptr[n] | src->ptr[n]) & mask;
|
||||
}
|
||||
dest->size = s;
|
||||
@@ -588,7 +611,7 @@ bitset_xor(bitset_t dest, const bitset_t src)
|
||||
for(size_t i = 0 ; i < n; i++)
|
||||
dest->ptr[i] ^= src->ptr[i];
|
||||
if (m) {
|
||||
size_t mask = (((bitset_limb)1) << m) - 1;
|
||||
bitset_limb mask = (((bitset_limb)1) << m) - 1;
|
||||
dest->ptr[n] = (dest->ptr[n] ^ src->ptr[n]) & mask;
|
||||
}
|
||||
dest->size = s;
|
||||
@@ -605,7 +628,7 @@ bitset_not(bitset_t dest)
|
||||
for(size_t i = 0 ; i < n; i++)
|
||||
dest->ptr[i] = ~ (dest->ptr[i]);
|
||||
if (m) {
|
||||
size_t mask = (((bitset_limb)1) << m) - 1;
|
||||
bitset_limb mask = (((bitset_limb)1) << m) - 1;
|
||||
dest->ptr[n] = (~ dest->ptr[n]) & mask;
|
||||
}
|
||||
dest->size = s;
|
||||
@@ -613,25 +636,51 @@ bitset_not(bitset_t dest)
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
bitset_hash(const bitset_t dest)
|
||||
bitset_hash(const bitset_t set)
|
||||
{
|
||||
BITSETI_CONTRACT(dest);
|
||||
size_t s = dest->size;
|
||||
BITSETI_CONTRACT(set);
|
||||
size_t s = set->size;
|
||||
size_t n = s / BITSET_LIMB_BIT;
|
||||
size_t m = s % BITSET_LIMB_BIT;
|
||||
M_HASH_DECL(hash);
|
||||
for(size_t i = 0 ; i < n; i++)
|
||||
M_HASH_UP(hash, dest->ptr[i]);
|
||||
M_HASH_UP(hash, set->ptr[i]);
|
||||
if (m) {
|
||||
size_t mask = (((bitset_limb)1) << m) - 1;
|
||||
M_HASH_UP(hash, (dest->ptr[n] & mask));
|
||||
bitset_limb mask = (((bitset_limb)1) << m) - 1;
|
||||
M_HASH_UP(hash, (set->ptr[n] & mask));
|
||||
}
|
||||
return M_HASH_FINAL (hash);
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
bitset_clz(const bitset_t set)
|
||||
{
|
||||
BITSETI_CONTRACT(set);
|
||||
size_t s = set->size;
|
||||
assert (s > 0);
|
||||
size_t n = (s -1) / BITSET_LIMB_BIT;
|
||||
size_t m = s % BITSET_LIMB_BIT;
|
||||
bitset_limb limb = set->ptr[n];
|
||||
if (m) {
|
||||
bitset_limb mask = (((bitset_limb)1) << m) - 1;
|
||||
limb &= mask;
|
||||
} else {
|
||||
m = BITSET_LIMB_BIT;
|
||||
}
|
||||
s = 0;
|
||||
while (limb == 0 && n > 0) {
|
||||
s += m;
|
||||
limb = set->ptr[--n];
|
||||
m = BITSET_LIMB_BIT;
|
||||
}
|
||||
s += m_core_clz64(limb) - (BITSET_LIMB_BIT - m);
|
||||
return s;
|
||||
}
|
||||
|
||||
#define BITSET_OPLIST \
|
||||
(INIT(bitset_init) \
|
||||
,INIT_SET(bitset_init_set) \
|
||||
,INIT_WITH(API_1(M_INIT_VAI)) \
|
||||
,SET(bitset_set) \
|
||||
,CLEAR(bitset_clear) \
|
||||
,INIT_MOVE(bitset_init_move) \
|
||||
@@ -639,6 +688,7 @@ bitset_hash(const bitset_t dest)
|
||||
,SWAP(bitset_swap) \
|
||||
,TYPE(bitset_t) \
|
||||
,SUBTYPE(bool) \
|
||||
,TEST_EMPTY(bitset_empty_p), \
|
||||
,IT_TYPE(bitset_it_t) \
|
||||
,IT_FIRST(bitset_it) \
|
||||
,IT_SET(bitset_it_set) \
|
||||
@@ -656,10 +706,12 @@ bitset_hash(const bitset_t dest)
|
||||
,HASH(bitset_hash) \
|
||||
,GET_STR(bitset_get_str) \
|
||||
,OUT_STR(bitset_out_str) \
|
||||
,PARSE_STR(bitset_parse_str) \
|
||||
,IN_STR(bitset_in_str) \
|
||||
,EQUAL(bitset_equal_p) \
|
||||
)
|
||||
|
||||
/* Register the OPLIST as a global one */
|
||||
#define M_OPL_bitset_t() BITSET_OPLIST
|
||||
|
||||
// TODO: set_at2, insert_v, remove_v
|
||||
@@ -667,9 +719,9 @@ bitset_hash(const bitset_t dest)
|
||||
#endif
|
||||
|
||||
// NOTE: Define this function only if m-string has been included
|
||||
#ifndef __M_BITSET_STRING_H
|
||||
#ifdef __M_STRING_H
|
||||
#define __M_BITSET_STRING_H
|
||||
#ifndef MSTARLIB_BITSET_STRING_H
|
||||
#ifdef MSTARLIB_STRING_H
|
||||
#define MSTARLIB_BITSET_STRING_H
|
||||
|
||||
static inline void
|
||||
bitset_get_str(string_t str, const bitset_t set, bool append)
|
||||
|
||||
+1133
File diff suppressed because it is too large.
Load diff
+567
-139
File diff suppressed because it is too large.
Load diff
+511
@@ -0,0 +1,511 @@
|
||||
/*
|
||||
* M*LIB - Basic Protected Concurrent module over container.
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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.
|
||||
*/
|
||||
#ifndef MSTARLIB_CONCURRENT_H
|
||||
#define MSTARLIB_CONCURRENT_H
|
||||
|
||||
#include "m-core.h"
|
||||
#include "m-mutex.h"
|
||||
|
||||
/* Define a protected concurrent container and its associated functions.
|
||||
USAGE: CONCURRENT_DEF(name, type [, oplist_of_the_type]) */
|
||||
#define CONCURRENT_DEF(name, ...) \
|
||||
CONCURRENTI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name,_it_t) ), \
|
||||
(name, __VA_ARGS__, M_C(name,_t), M_C(name,_it_t))))
|
||||
|
||||
|
||||
/* Define the oplist of a protected concurrent container given its name and its oplist.
|
||||
USAGE: CONCURRENT_OPLIST(name[, oplist of the type]) */
|
||||
#define CONCURRENT_OPLIST(...) \
|
||||
CONCURRENTI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
|
||||
// Deferred evaluation for the oplist definition.
|
||||
#define CONCURRENTI_OPLIST(arg) CONCURRENTI_OPLIST2 arg
|
||||
|
||||
/* OPLIST definition
|
||||
GET_KEY is not present as its interface is not compatible with a concurrent
|
||||
container (_get returns a pointer to an internal data, data that may be
|
||||
destroyed by another thread).
|
||||
*/
|
||||
#define CONCURRENTI_OPLIST2(name, oplist) \
|
||||
(M_IF_METHOD(INIT, oplist)(INIT(M_C(name, _init)),) \
|
||||
,M_IF_METHOD(INIT_SET, oplist)(INIT_SET(M_C(name, _init_set)),) \
|
||||
,M_IF_METHOD(SET, oplist)(SET(M_C(name, _set)),) \
|
||||
,M_IF_METHOD(CLEAR, oplist)(CLEAR(M_C(name, _clear)),) \
|
||||
,M_IF_METHOD(INIT_MOVE, oplist)(INIT_MOVE(M_C(name, _init_move)),) \
|
||||
,M_IF_METHOD(MOVE, oplist)(MOVE(M_C(name, _move)),) \
|
||||
,M_IF_METHOD(SWAP,oplist)(SWAP(M_C(name, _swap)),) \
|
||||
,TYPE(M_C(name,_t)) \
|
||||
,SUBTYPE(M_C(name, _type_t)) \
|
||||
,OPLIST(oplist) \
|
||||
,M_IF_METHOD(TEST_EMPTY, oplist)(TEST_EMPTY(M_C(name,_empty_p)),) \
|
||||
,M_IF_METHOD(CLEAN, oplist)(CLEAN(M_C(name,_clean)),) \
|
||||
,M_IF_METHOD(KEY_TYPE, oplist)(KEY_TYPE(M_GET_KEY_TYPE oplist),) \
|
||||
,M_IF_METHOD(VALUE_TYPE, oplist)(VALUE_TYPE(M_GET_VALUE_TYPE oplist),) \
|
||||
,M_IF_METHOD(KEY_TYPE, oplist)(KEY_OPLIST(M_GET_KEY_OPLIST oplist),) \
|
||||
,M_IF_METHOD(VALUE_TYPE, oplist)(VALUE_OPLIST(M_GET_VALUE_OPLIST oplist), ) \
|
||||
,M_IF_METHOD(SET_KEY, oplist)(SET_KEY(M_C(name, _set_at)),) \
|
||||
,M_IF_METHOD(ERASE_KEY, oplist)(ERASE_KEY(M_C(name, _erase)),) \
|
||||
,M_IF_METHOD(PUSH, oplist)(PUSH(M_C(name,_push)),) \
|
||||
,M_IF_METHOD(POP, oplist)(POP(M_C(name,_pop)),) \
|
||||
,M_IF_METHOD(PUSH_MOVE, oplist)(PUSH_MOVE(M_C(name,_push_move)),) \
|
||||
,M_IF_METHOD(POP_MOVE, oplist)(POP_MOVE(M_C(name,_pop_move)),) \
|
||||
,M_IF_METHOD(GET_STR, oplist)(GET_STR(M_C(name, _get_str)),) \
|
||||
,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_C(name, _parse_str)),) \
|
||||
,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_C(name, _out_str)),) \
|
||||
,M_IF_METHOD(IN_STR, oplist)(IN_STR(M_C(name, _in_str)),) \
|
||||
,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_C(name, _equal_p)),) \
|
||||
,M_IF_METHOD(HASH, oplist)(HASH(M_C(name, _hash)),) \
|
||||
,M_IF_METHOD(NEW, oplist)(NEW(M_GET_NEW oplist),) \
|
||||
,M_IF_METHOD(REALLOC, oplist)(REALLOC(M_GET_REALLOC oplist),) \
|
||||
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
|
||||
)
|
||||
|
||||
#define CONCURRENTI_CONTRACT(c) do { \
|
||||
assert ((c)->self == c); \
|
||||
} while (0)
|
||||
|
||||
// Deferred evaluation for the concurrent definition.
|
||||
#define CONCURRENTI_DEF(arg) CONCURRENTI_DEF2 arg
|
||||
|
||||
// Internal definition.
|
||||
#define CONCURRENTI_DEF2(name, type, oplist, concurrent_t, concurrent_it_t) \
|
||||
\
|
||||
typedef struct M_C(name, _s) { \
|
||||
struct M_C(name, _s) *self; \
|
||||
m_mutex_t lock; \
|
||||
m_cond_t there_is_data; /* condition raised when there is data */ \
|
||||
type data; \
|
||||
} concurrent_t[1]; \
|
||||
\
|
||||
typedef struct M_C(name, _s) *M_C(name, _ptr); \
|
||||
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
|
||||
\
|
||||
typedef type M_C(name, _type_t); \
|
||||
\
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _init)(concurrent_t out) \
|
||||
{ \
|
||||
m_mutex_init(out->lock); \
|
||||
m_cond_init(out->there_is_data); \
|
||||
out->self = out; \
|
||||
M_CALL_INIT(oplist, out->data); \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(INIT_SET, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _init_set)(concurrent_t out, concurrent_t const src) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(src); \
|
||||
assert (out != src); \
|
||||
m_mutex_init(out->lock); \
|
||||
m_cond_init(out->there_is_data); \
|
||||
out->self = out; \
|
||||
m_mutex_lock (src->self->lock); \
|
||||
M_CALL_INIT_SET(oplist, out->data, src->data); \
|
||||
m_mutex_unlock(src->self->lock); \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(SET, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _set)(concurrent_t out, concurrent_t const src) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
if (out == src) return; \
|
||||
if (out < src) { \
|
||||
m_mutex_lock (out->lock); \
|
||||
m_mutex_lock (src->self->lock); \
|
||||
} else { \
|
||||
m_mutex_lock (src->self->lock); \
|
||||
m_mutex_lock (out->lock); \
|
||||
} \
|
||||
M_CALL_SET(oplist, out->data, src->data); \
|
||||
if (out < src) { \
|
||||
m_mutex_unlock (src->self->lock); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} else { \
|
||||
m_mutex_unlock (out->lock); \
|
||||
m_mutex_unlock (src->self->lock); \
|
||||
} \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(CLEAR, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _clear)(concurrent_t out) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
/* No need to lock */ \
|
||||
M_CALL_CLEAR(oplist, out->data); \
|
||||
m_mutex_clear (out->lock); \
|
||||
m_cond_clear(out->there_is_data); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(INIT_MOVE, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _init_move)(concurrent_t out, concurrent_t src) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(src); \
|
||||
assert (out != src); \
|
||||
/* No need to lock 'src' ? */ \
|
||||
m_mutex_init (out->lock); \
|
||||
m_cond_init (out->there_is_data); \
|
||||
out->self = out; \
|
||||
M_CALL_INIT_MOVE(oplist, out->data, src->data); \
|
||||
m_mutex_clear (src->lock); \
|
||||
m_cond_clear (src->there_is_data); \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(MOVE, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _move)(concurrent_t out, concurrent_t src) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
CONCURRENTI_CONTRACT(src); \
|
||||
/* No need to lock 'src' ? */ \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_CALL_MOVE(oplist, out->data, src->data); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
m_mutex_clear (src->lock); \
|
||||
m_cond_clear (src->there_is_data); \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(SWAP, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _swap)(concurrent_t out, concurrent_t src) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
CONCURRENTI_CONTRACT(src); \
|
||||
if (out < src) { \
|
||||
m_mutex_lock (out->lock); \
|
||||
m_mutex_lock (src->lock); \
|
||||
} else { \
|
||||
m_mutex_lock (src->lock); \
|
||||
m_mutex_lock (out->lock); \
|
||||
} \
|
||||
M_CALL_SWAP(oplist, out->data, src->data); \
|
||||
if (out < src) { \
|
||||
m_mutex_unlock (src->lock); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} else { \
|
||||
m_mutex_unlock (out->lock); \
|
||||
m_mutex_unlock (src->lock); \
|
||||
} \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(CLEAN, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _clean)(concurrent_t out) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_CALL_CLEAN(oplist, out->data); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(TEST_EMPTY, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _empty_p)(concurrent_t const out) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->self->lock); \
|
||||
bool b = M_CALL_TEST_EMPTY(oplist, out->data); \
|
||||
m_mutex_unlock (out->self->lock); \
|
||||
return b; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(SET_KEY, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _set_at)(concurrent_t out, M_GET_KEY_TYPE oplist const key, M_GET_VALUE_TYPE oplist const data) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_CALL_SET_KEY(oplist, out->data, key, data); \
|
||||
m_cond_broadcast(out->there_is_data); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(GET_KEY, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _get_copy)(M_GET_VALUE_TYPE oplist *out_data, concurrent_t out, M_GET_KEY_TYPE oplist const key) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
assert (out_data != NULL); \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_GET_VALUE_TYPE oplist *p = M_CALL_GET_KEY(oplist, out->data, key); \
|
||||
if (p != NULL) { \
|
||||
M_CALL_SET(M_GET_VALUE_OPLIST oplist, *out_data, *p); \
|
||||
} \
|
||||
m_mutex_unlock (out->lock); \
|
||||
return p != NULL; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(GET_SET_KEY, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _get_at_copy)(M_GET_VALUE_TYPE oplist *out_data, concurrent_t out, M_GET_KEY_TYPE oplist const key) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
assert (out_data != NULL); \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_GET_VALUE_TYPE oplist *p = M_CALL_GET_SET_KEY(oplist, out->data, key); \
|
||||
assert (p != NULL); \
|
||||
M_CALL_SET(M_GET_VALUE_OPLIST oplist, *out_data, *p); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(ERASE_KEY, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _erase)(concurrent_t out, M_GET_KEY_TYPE oplist const key) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
bool b = M_CALL_ERASE_KEY(oplist, out->data, key); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
return b; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(PUSH, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _push)(concurrent_t out, M_GET_SUBTYPE oplist const data) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_CALL_PUSH(oplist, out->data, data); \
|
||||
m_cond_broadcast(out->there_is_data); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(POP, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _pop)(M_GET_SUBTYPE oplist *p, concurrent_t out) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_CALL_POP(oplist, p, out->data); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(PUSH_MOVE, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _push_move)(concurrent_t out, M_GET_SUBTYPE oplist *data) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_CALL_PUSH_MOVE(oplist, out->data, data); \
|
||||
m_cond_broadcast(out->there_is_data); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(POP_MOVE, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _pop_move)(M_GET_SUBTYPE oplist *p, concurrent_t out) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
M_CALL_POP_MOVE(oplist, p, out->data); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(GET_STR, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _get_str)(string_t str, concurrent_t const out, bool a) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->self->lock); \
|
||||
M_CALL_GET_STR(oplist, str, out->data, a); \
|
||||
m_mutex_unlock (out->self->lock); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(OUT_STR, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _out_str)(FILE *f, concurrent_t const out) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->self->lock); \
|
||||
M_CALL_OUT_STR(oplist, f, out->data); \
|
||||
m_mutex_unlock (out->self->lock); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(PARSE_STR, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _parse_str)(concurrent_t out, const char str[], const char **e) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
bool b = M_CALL_PARSE_STR(oplist, out->data, str, e); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
return b; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(IN_STR, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _in_str)(concurrent_t out, FILE *f) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
bool b = M_CALL_IN_STR(oplist, out->data, f); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
return b; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(EQUAL, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _equal_p)(concurrent_t const out1, concurrent_t const out2) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out1); \
|
||||
CONCURRENTI_CONTRACT(out2); \
|
||||
if (out1 == out2) return true; \
|
||||
if (out1 < out2) { \
|
||||
m_mutex_lock (out1->self->lock); \
|
||||
m_mutex_lock (out2->self->lock); \
|
||||
} else { \
|
||||
m_mutex_lock (out2->self->lock); \
|
||||
m_mutex_lock (out1->self->lock); \
|
||||
} \
|
||||
bool b = M_CALL_EQUAL(oplist, out1->data, out2->data); \
|
||||
if (out1 < out2) { \
|
||||
m_mutex_unlock (out2->self->lock); \
|
||||
m_mutex_unlock (out1->self->lock); \
|
||||
} else { \
|
||||
m_mutex_unlock (out1->self->lock); \
|
||||
m_mutex_unlock (out2->self->lock); \
|
||||
} \
|
||||
return b; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(GET_KEY, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _get_blocking)(M_GET_VALUE_TYPE oplist *out_data, concurrent_t out, M_GET_KEY_TYPE oplist const key, bool blocking) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
assert (out_data != NULL); \
|
||||
bool ret = false; \
|
||||
m_mutex_lock (out->lock); \
|
||||
while (true) { \
|
||||
M_GET_VALUE_TYPE oplist *p = M_CALL_GET_KEY(oplist, out->data, key); \
|
||||
if (p != NULL) { \
|
||||
M_CALL_SET(M_GET_VALUE_OPLIST oplist, *out_data, *p); \
|
||||
ret = true; \
|
||||
break; \
|
||||
} \
|
||||
if (blocking == false) break; \
|
||||
m_cond_wait(out->there_is_data, out->lock); \
|
||||
} \
|
||||
m_mutex_unlock (out->lock); \
|
||||
return ret; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD2(POP, TEST_EMPTY, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _pop_blocking)(M_GET_SUBTYPE oplist *p, concurrent_t out, bool blocking) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
assert (p != NULL); \
|
||||
bool ret = false; \
|
||||
m_mutex_lock (out->lock); \
|
||||
while (true) { \
|
||||
if (!M_CALL_TEST_EMPTY(oplist, out->data)) { \
|
||||
M_CALL_POP(oplist, p, out->data); \
|
||||
ret = true; \
|
||||
break; \
|
||||
} \
|
||||
if (blocking == false) break; \
|
||||
m_cond_wait(out->there_is_data, out->lock); \
|
||||
} \
|
||||
m_mutex_unlock (out->lock); \
|
||||
return ret; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD2(POP_MOVE, TEST_EMPTY, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _pop_move_blocking)(M_GET_SUBTYPE oplist *p, concurrent_t out, bool blocking) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
assert (p != NULL); \
|
||||
bool ret = false; \
|
||||
m_mutex_lock (out->lock); \
|
||||
while (true) { \
|
||||
if (!M_CALL_TEST_EMPTY(oplist, out->data)) { \
|
||||
M_CALL_POP_MOVE(oplist, p, out->data); \
|
||||
ret = true; \
|
||||
break; \
|
||||
} \
|
||||
if (blocking == false) break; \
|
||||
m_cond_wait(out->there_is_data, out->lock); \
|
||||
} \
|
||||
m_mutex_unlock (out->lock); \
|
||||
return ret; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(HASH, oplist)( \
|
||||
static inline size_t \
|
||||
M_C(name, _hash)(concurrent_t const out) \
|
||||
{ \
|
||||
CONCURRENTI_CONTRACT(out); \
|
||||
m_mutex_lock (out->self->lock); \
|
||||
size_t h = M_CALL_HASH(oplist, out->data); \
|
||||
m_mutex_unlock (out->self->lock); \
|
||||
return h; \
|
||||
} \
|
||||
,) \
|
||||
|
||||
#endif
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - DEQUE module
|
||||
*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -22,30 +22,38 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __M_DEQUE_H
|
||||
#define __M_DEQUE_H
|
||||
#ifndef MSTARLIB_DEQUE_H
|
||||
#define MSTARLIB_DEQUE_H
|
||||
|
||||
#include "m-i-list.h"
|
||||
|
||||
/* Define a deque of a given type.
|
||||
/* Define a deque of a given type and its associated functions.
|
||||
USAGE: DEQUE_DEF(name, type [, oplist_of_the_type]) */
|
||||
#define DEQUE_DEF(name, ...) \
|
||||
DEQUEI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_DEFAULT_OPLIST, M_C(name,_t), M_C(name,_it_t), M_C(name, _node_t) ), \
|
||||
(name, __VA_ARGS__, M_C(name,_t), M_C(name,_it_t), M_C(name, _node_t) )))
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name,_it_t), M_C(name, _node_t) ), \
|
||||
(name, __VA_ARGS__, M_C(name,_t), M_C(name,_it_t), M_C(name, _node_t))))
|
||||
|
||||
|
||||
/* Define the oplist of a deque of type.
|
||||
/* Define the oplist of a deque of a type.
|
||||
USAGE: DEQUE_OPLIST(name[, oplist of the type]) */
|
||||
#define DEQUE_OPLIST(...) \
|
||||
DEQUEI_OPLIST (M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST ), \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
|
||||
#define DEQUEUI_DEFAULT_SIZE 8
|
||||
|
||||
/* C++ doesn't support flexible array within a structure.
|
||||
Let's define at least one element. */
|
||||
#ifdef __cplusplus
|
||||
# define DEQUEI_MIN_ARRAY_SIZE 1
|
||||
#else
|
||||
# define DEQUEI_MIN_ARRAY_SIZE
|
||||
#endif
|
||||
|
||||
#define DEQUEI_CONTRACT(d) do { \
|
||||
assert ((d) != NULL); \
|
||||
assert ((d)->default_size >= DEQUEUI_DEFAULT_SIZE); \
|
||||
@@ -66,9 +74,15 @@
|
||||
typedef struct M_C(name, _node_s) { \
|
||||
ILIST_INTERFACE(M_C(name, _node_list), struct M_C(name, _node_s)); \
|
||||
size_t size; \
|
||||
type data[]; \
|
||||
type data[DEQUEI_MIN_ARRAY_SIZE]; \
|
||||
} node_t; \
|
||||
\
|
||||
/* Each node is allocated with a variable size (so we use \
|
||||
M_GET_REALLOC for the allocation). But we need to delete the nodes \
|
||||
automatically with the intrusive list used for storing the nodes: \
|
||||
so we register as a DEL operator the FREE operator of the oplist. \
|
||||
The interfaces are compatible. \
|
||||
*/ \
|
||||
ILIST_DEF(M_C(name, _node_list), node_t, (DEL(M_GET_FREE oplist)) ) \
|
||||
\
|
||||
typedef struct M_C(name, _it2_s) { \
|
||||
@@ -83,6 +97,8 @@
|
||||
size_t default_size; \
|
||||
size_t count; \
|
||||
} deque_t[1]; \
|
||||
typedef struct M_C(name, _s) *M_C(name, _ptr); \
|
||||
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
|
||||
\
|
||||
typedef struct M_C(name, _it_s) { \
|
||||
node_t *node; \
|
||||
@@ -92,24 +108,26 @@
|
||||
\
|
||||
typedef type M_C(name, _type_t); \
|
||||
\
|
||||
static inline deque_node_t* \
|
||||
static inline M_C(name, _node_t)* \
|
||||
M_C(name, _int_new_node)(deque_t d) \
|
||||
{ \
|
||||
const size_t def = d->default_size; \
|
||||
if (M_UNLIKELY (def >SIZE_MAX / sizeof (type) - sizeof(deque_node_t))) { \
|
||||
M_MEMORY_FULL(sizeof(deque_node_t)+def * sizeof(type)); \
|
||||
size_t def = d->default_size; \
|
||||
if (M_UNLIKELY (def > SIZE_MAX / sizeof (type) - sizeof(M_C(name, _node_t)))) { \
|
||||
M_MEMORY_FULL(sizeof(M_C(name, _node_t))+def * sizeof(type)); \
|
||||
return NULL; \
|
||||
} \
|
||||
deque_node_t*n = (deque_node_t*) (void*) \
|
||||
M_GET_REALLOC oplist (char, NULL, \
|
||||
sizeof(deque_node_t)+def * sizeof(type) ); \
|
||||
M_C(name, _node_t)*n = (M_C(name, _node_t)*) (void*) \
|
||||
M_CALL_REALLOC(oplist, char, NULL, \
|
||||
sizeof(M_C(name, _node_t)) + def * sizeof(type) ); \
|
||||
if (n==NULL) { \
|
||||
M_MEMORY_FULL(sizeof(deque_node_t)+def * sizeof(type)); \
|
||||
M_MEMORY_FULL(sizeof(M_C(name, _node_t))+def * sizeof(type)); \
|
||||
return NULL; \
|
||||
} \
|
||||
n->size = def; \
|
||||
deque_node_list_init_field(n); \
|
||||
d->default_size = M_GET_INC_ALLOC oplist (def); \
|
||||
M_C(name, _node_list_init_field)(n); \
|
||||
/* Do not increase it too much if there are few items */ \
|
||||
def = M_MIN(def, d->count); \
|
||||
d->default_size = M_CALL_INC_ALLOC(oplist, def); \
|
||||
return n; \
|
||||
} \
|
||||
\
|
||||
@@ -119,9 +137,9 @@
|
||||
M_C(name, _node_list_init)(d->list); \
|
||||
d->default_size = DEQUEUI_DEFAULT_SIZE; \
|
||||
d->count = 0; \
|
||||
deque_node_t *n = M_C(name, _int_new_node)(d); \
|
||||
M_C(name, _node_t) *n = M_C(name, _int_new_node)(d); \
|
||||
if (n == NULL) return; \
|
||||
deque_node_list_push_back(d->list, n); \
|
||||
M_C(name, _node_list_push_back)(d->list, n); \
|
||||
d->front->node = n; \
|
||||
d->front->index = DEQUEUI_DEFAULT_SIZE/2; \
|
||||
d->back->node = n; \
|
||||
@@ -133,18 +151,17 @@
|
||||
M_C(name, _clean)(deque_t d) \
|
||||
{ \
|
||||
DEQUEI_CONTRACT(d); \
|
||||
M_C(name, _node_list_it_t) it; \
|
||||
deque_node_t *min_node = NULL; \
|
||||
for(M_C(name, _node_list_it)(it, d->list) ; \
|
||||
!M_C(name, _node_list_end_p)(it) ; \
|
||||
M_C(name, _node_list_next)(it) ){ \
|
||||
deque_node_t *n = M_C(name, _node_list_ref)(it); \
|
||||
size_t min = n == d->front->node ? d->back->index + 1 : 0; \
|
||||
size_t max = n == d->back->node ? d->back->index + 1: n->size; \
|
||||
M_C(name, _node_t) *min_node = NULL; \
|
||||
for(M_C(name, _node_t) *n = d->front->node; \
|
||||
n != NULL ; \
|
||||
n = (n == d->back->node) ? NULL : \
|
||||
M_C(name, _node_list_next_obj)(d->list, n) ){ \
|
||||
size_t min = n == d->front->node ? d->front->index : 0; \
|
||||
size_t max = n == d->back->node ? d->back->index : n->size; \
|
||||
for(size_t i = min; i < max; i++) { \
|
||||
M_GET_CLEAR oplist (n->data[i]); \
|
||||
M_CALL_CLEAR(oplist, n->data[i]); \
|
||||
} \
|
||||
min_node = min_node == NULL || min_node->size > n->size ? n : min_node; \
|
||||
min_node = (min_node == NULL || min_node->size > n->size) ? n : min_node; \
|
||||
} \
|
||||
assert (min_node != NULL); \
|
||||
d->front->node = min_node; \
|
||||
@@ -160,9 +177,9 @@
|
||||
{ \
|
||||
DEQUEI_CONTRACT(d); \
|
||||
M_C(name, _clean)(d); \
|
||||
/* We registered the delete operator to clear all objects */ \
|
||||
/* We have registered the delete operator to clear all objects */ \
|
||||
M_C(name, _node_list_clear)(d->list); \
|
||||
/* It is safer to clean some variables*/ \
|
||||
/* It is safer to clean some variables */ \
|
||||
d->front->node = NULL; \
|
||||
d->back->node = NULL; \
|
||||
d->count = 0; \
|
||||
@@ -172,7 +189,7 @@
|
||||
M_C(name, _push_back_raw)(deque_t d) \
|
||||
{ \
|
||||
DEQUEI_CONTRACT(d); \
|
||||
deque_node_t *n = d->back->node; \
|
||||
M_C(name, _node_t) *n = d->back->node; \
|
||||
size_t index = d->back->index; \
|
||||
if (M_UNLIKELY (n->size <= index)) { \
|
||||
n = M_C(name, _node_list_next_obj)(d->list, n); \
|
||||
@@ -197,25 +214,37 @@
|
||||
{ \
|
||||
type *p = M_C(name, _push_back_raw)(d); \
|
||||
if (M_LIKELY(p != NULL)) { \
|
||||
M_GET_INIT_SET oplist (*p, x); \
|
||||
M_CALL_INIT_SET(oplist, *p, x); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
static inline type * \
|
||||
M_C(name, _push_back_new)(deque_t d) \
|
||||
{ \
|
||||
type *p = M_C(name, _push_back_raw)(d); \
|
||||
if (M_LIKELY(p != NULL)) { \
|
||||
M_GET_INIT oplist (*p); \
|
||||
M_CALL_INIT(oplist, *p); \
|
||||
} \
|
||||
return p; \
|
||||
} \
|
||||
, ) \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _push_back_move)(deque_t d, type *x) \
|
||||
{ \
|
||||
assert (x != NULL); \
|
||||
type *p = M_C(name, _push_back_raw)(d); \
|
||||
if (M_UNLIKELY(p == NULL)) \
|
||||
return; \
|
||||
M_DO_INIT_MOVE (oplist, *p, *x); \
|
||||
} \
|
||||
\
|
||||
static inline type* \
|
||||
M_C(name, _push_front_raw)(deque_t d) \
|
||||
{ \
|
||||
DEQUEI_CONTRACT(d); \
|
||||
deque_node_t *n = d->front->node; \
|
||||
M_C(name, _node_t) *n = d->front->node; \
|
||||
size_t index = d->front->index; \
|
||||
index --; \
|
||||
/* If overflow */ \
|
||||
@@ -241,18 +270,30 @@
|
||||
{ \
|
||||
type *p = M_C(name, _push_front_raw)(d); \
|
||||
if (M_LIKELY(p != NULL)) { \
|
||||
M_GET_INIT_SET oplist (*p, x); \
|
||||
M_CALL_INIT_SET(oplist, *p, x); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
static inline type * \
|
||||
M_C(name, _push_front_new)(deque_t d) \
|
||||
{ \
|
||||
type *p = M_C(name, _push_front_raw)(d); \
|
||||
if (M_LIKELY(p != NULL)) { \
|
||||
M_GET_INIT oplist (*p); \
|
||||
M_CALL_INIT(oplist, *p); \
|
||||
} \
|
||||
return p; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _push_front_move)(deque_t d, type *x) \
|
||||
{ \
|
||||
assert (x != NULL); \
|
||||
type *p = M_C(name, _push_front_raw)(d); \
|
||||
if (M_UNLIKELY(p == NULL)) \
|
||||
return; \
|
||||
M_DO_INIT_MOVE (oplist, *p, *x); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
@@ -260,61 +301,101 @@
|
||||
{ \
|
||||
DEQUEI_CONTRACT(d); \
|
||||
assert(d->count > 0); \
|
||||
deque_node_t *n = d->back->node; \
|
||||
M_C(name, _node_t) *n = d->back->node; \
|
||||
size_t index = d->back->index; \
|
||||
index --; \
|
||||
if (M_UNLIKELY (n->size <= index)) { \
|
||||
n = deque_node_list_previous_obj(d->list, n); \
|
||||
/* If there is a next node, \
|
||||
pop the back node and push it back to the front. This \
|
||||
reduce the used memory if the deque is used as a FIFO queue.*/ \
|
||||
M_C(name, _node_t) *next = M_C(name, _node_list_next_obj)(d->list, n); \
|
||||
if (next != NULL) { \
|
||||
next = M_C(name, _node_list_pop_back)(d->list); \
|
||||
assert (next != n); \
|
||||
M_C(name, _node_list_push_front)(d->list, next); \
|
||||
} \
|
||||
n = M_C(name, _node_list_previous_obj)(d->list, n); \
|
||||
assert (n != NULL); \
|
||||
d->back->node = n; \
|
||||
index = n->size-1; \
|
||||
} \
|
||||
if (ptr != NULL) \
|
||||
M_IF_METHOD(MOVE, oplist) ( \
|
||||
M_GET_MOVE oplist(*ptr, n->data[index]); else \
|
||||
M_CALL_MOVE(oplist, *ptr, n->data[index]); else \
|
||||
, \
|
||||
M_GET_SET oplist(*ptr, n->data[index]); \
|
||||
M_CALL_SET(oplist, *ptr, n->data[index]); \
|
||||
) \
|
||||
M_GET_CLEAR oplist (n->data[index]); \
|
||||
M_CALL_CLEAR(oplist, n->data[index]); \
|
||||
d->count --; \
|
||||
d->back->index = index; \
|
||||
DEQUEI_CONTRACT(d); \
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _pop_back_move)(type *ptr, deque_t d) \
|
||||
{ \
|
||||
assert(ptr != NULL); \
|
||||
/* Note: Lazy implementation. Can be improved if needed */ \
|
||||
M_CALL_INIT(oplist, *ptr); \
|
||||
M_C(name, _pop_back)(ptr, d); \
|
||||
} \
|
||||
, ) \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _pop_front)(type *ptr, deque_t d) \
|
||||
{ \
|
||||
DEQUEI_CONTRACT(d); \
|
||||
assert(d->count > 0); \
|
||||
deque_node_t *n = d->front->node; \
|
||||
M_C(name, _node_t) *n = d->front->node; \
|
||||
size_t index = d->front->index; \
|
||||
if (M_UNLIKELY (n->size <= index)) { \
|
||||
n = deque_node_list_next_obj(d->list, n); \
|
||||
/* If there is a previous node, \
|
||||
pop the front node and push it back to the back. This \
|
||||
reduce the used memory if the deque is used as a FIFO queue.*/ \
|
||||
M_C(name,_node_t) *prev = M_C(name, _node_list_previous_obj)(d->list, n); \
|
||||
if (prev != NULL) { \
|
||||
prev = M_C(name, _node_list_pop_front)(d->list); \
|
||||
assert (prev != n); \
|
||||
M_C(name, _node_list_push_back)(d->list, prev); \
|
||||
} \
|
||||
n = M_C(name, _node_list_next_obj)(d->list, n); \
|
||||
assert (n != NULL); \
|
||||
d->front->node = n; \
|
||||
index = 0; \
|
||||
} \
|
||||
if (ptr != NULL) \
|
||||
M_IF_METHOD(MOVE, oplist) ( \
|
||||
M_GET_MOVE oplist(*ptr, n->data[index]); else \
|
||||
M_CALL_MOVE(oplist, *ptr, n->data[index]); else \
|
||||
, \
|
||||
M_GET_SET oplist(*ptr, n->data[index]); \
|
||||
M_CALL_SET(oplist, *ptr, n->data[index]); \
|
||||
) \
|
||||
M_GET_CLEAR oplist (n->data[index]); \
|
||||
M_CALL_CLEAR(oplist, n->data[index]); \
|
||||
index++; \
|
||||
d->count --; \
|
||||
d->front->index = index; \
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _pop_front_move)(type *ptr, deque_t d) \
|
||||
{ \
|
||||
assert(ptr != NULL); \
|
||||
/* Note: Lazy implementation */ \
|
||||
M_CALL_INIT(oplist, *ptr); \
|
||||
M_C(name, _pop_front)(ptr, d); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
static inline type * \
|
||||
M_C(name, _back)(const deque_t d) \
|
||||
{ \
|
||||
DEQUEI_CONTRACT(d); \
|
||||
assert (d->count > 0); \
|
||||
size_t i = d->back->index; \
|
||||
deque_node_t *n = d->back->node; \
|
||||
M_C(name, _node_t) *n = d->back->node; \
|
||||
if (M_UNLIKELY (i == 0)) { \
|
||||
n = deque_node_list_previous_obj(d->list, n); \
|
||||
n = M_C(name, _node_list_previous_obj)(d->list, n); \
|
||||
assert (n != NULL); \
|
||||
i = n->size; \
|
||||
} \
|
||||
@@ -327,9 +408,9 @@
|
||||
DEQUEI_CONTRACT(d); \
|
||||
assert (d->count > 0); \
|
||||
size_t i = d->front->index; \
|
||||
deque_node_t *n = d->front->node; \
|
||||
M_C(name, _node_t) *n = d->front->node; \
|
||||
if (M_UNLIKELY (n->size <= i)) { \
|
||||
n = deque_node_list_next_obj(d->list, n); \
|
||||
n = M_C(name, _node_list_next_obj)(d->list, n); \
|
||||
assert (n != NULL); \
|
||||
i = 0; \
|
||||
} \
|
||||
@@ -342,6 +423,40 @@
|
||||
DEQUEI_CONTRACT(d); \
|
||||
return d->count; \
|
||||
} \
|
||||
\
|
||||
static inline size_t \
|
||||
M_C(name, _capacity_back)(const deque_t v) \
|
||||
{ \
|
||||
DEQUEI_CONTRACT(v); \
|
||||
size_t s = 0; \
|
||||
for(M_C(name, _node_t) *n = M_C(name, _node_list_back)(v->list); \
|
||||
n != NULL ; \
|
||||
n = (n == v->back->node) ? NULL : \
|
||||
M_C(name, _node_list_previous_obj)(v->list, n) ){ \
|
||||
s += (n == v->back->node ? v->back->index : n->size); \
|
||||
} \
|
||||
return s; \
|
||||
} \
|
||||
\
|
||||
static inline size_t \
|
||||
M_C(name, _capacity_front)(const deque_t v) \
|
||||
{ \
|
||||
DEQUEI_CONTRACT(v); \
|
||||
size_t s = 0; \
|
||||
for(M_C(name, _node_t) *n = M_C(name, _node_list_front)(v->list); \
|
||||
n != NULL ; \
|
||||
n = (n == v->front->node) ? NULL : \
|
||||
M_C(name, _node_list_next_obj)(v->list, n) ){ \
|
||||
s += n->size - (n == v->front->node ? v->front->index : 0); \
|
||||
} \
|
||||
return s; \
|
||||
} \
|
||||
\
|
||||
static inline size_t \
|
||||
M_C(name, _capacity)(const deque_t v) \
|
||||
{ \
|
||||
return M_C(name, _capacity_back)(v)+M_C(name, _capacity_front)(v); \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _empty_p)(const deque_t d) \
|
||||
@@ -369,7 +484,7 @@
|
||||
it->index = d->back->index - 1; \
|
||||
it->deque = d; \
|
||||
if (M_UNLIKELY (it->index >= it->node->size)) { \
|
||||
it->node = deque_node_list_previous_obj(d->list, it->node); \
|
||||
it->node = M_C(name, _node_list_previous_obj)(d->list, it->node); \
|
||||
assert (it->node != NULL); \
|
||||
it->index = it->node->size-1; \
|
||||
} \
|
||||
@@ -409,10 +524,10 @@
|
||||
M_C(name, _next)(it_t it) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
deque_node_t *n = it->node; \
|
||||
M_C(name, _node_t) *n = it->node; \
|
||||
it->index ++; \
|
||||
if (M_UNLIKELY (it->index >= n->size)) { \
|
||||
n = deque_node_list_next_obj(it->deque->list, n); \
|
||||
n = M_C(name, _node_list_next_obj)(it->deque->list, n); \
|
||||
if (M_UNLIKELY (n == NULL)) { \
|
||||
/* Point to 'end' (can't undo it) */ \
|
||||
it->node = it->deque->back->node; \
|
||||
@@ -428,10 +543,10 @@
|
||||
M_C(name, _previous)(it_t it) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
deque_node_t *n = it->node; \
|
||||
M_C(name, _node_t) *n = it->node; \
|
||||
it->index --; \
|
||||
if (M_UNLIKELY (it->index >= n->size)) { \
|
||||
n = deque_node_list_previous_obj(it->deque->list, n); \
|
||||
n = M_C(name, _node_list_previous_obj)(it->deque->list, n); \
|
||||
if (M_UNLIKELY (n == NULL)) { \
|
||||
/* Point to 'end' (can't undo it) */ \
|
||||
it->node = it->deque->back->node; \
|
||||
@@ -471,7 +586,7 @@
|
||||
return &it->node->data[it->index]; \
|
||||
} \
|
||||
\
|
||||
static inline const type * \
|
||||
static inline type const * \
|
||||
M_C(name, _cref)(it_t it) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
@@ -487,10 +602,10 @@
|
||||
M_C(name, _node_list_init)(d->list); \
|
||||
d->default_size = DEQUEUI_DEFAULT_SIZE + src->count; \
|
||||
d->count = src->count; \
|
||||
deque_node_t *n = M_C(name, _int_new_node)(d); \
|
||||
M_C(name, _node_t) *n = M_C(name, _int_new_node)(d); \
|
||||
if (n == NULL) return; \
|
||||
d->default_size /= 2; \
|
||||
deque_node_list_push_back(d->list, n); \
|
||||
M_C(name, _node_list_push_back)(d->list, n); \
|
||||
d->front->node = n; \
|
||||
d->front->index = DEQUEUI_DEFAULT_SIZE/2; \
|
||||
d->back->node = n; \
|
||||
@@ -498,8 +613,8 @@
|
||||
it_t it; \
|
||||
size_t i = DEQUEUI_DEFAULT_SIZE/2; \
|
||||
for(M_C(name, _it)(it, src); !M_C(name, _end_p)(it) ; M_C(name, _next)(it)) { \
|
||||
const type *obj = M_C(name, _cref)(it); \
|
||||
M_GET_INIT_SET oplist (n->data[i], *obj); \
|
||||
type const *obj = M_C(name, _cref)(it); \
|
||||
M_CALL_INIT_SET(oplist, n->data[i], *obj); \
|
||||
i++; \
|
||||
assert (i <= d->back->index); \
|
||||
} \
|
||||
@@ -507,7 +622,7 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _set)(deque_t d, deque_t src) \
|
||||
M_C(name, _set)(deque_t d, deque_t const src) \
|
||||
{ \
|
||||
if (M_UNLIKELY (src == d)) \
|
||||
return; \
|
||||
@@ -521,7 +636,13 @@
|
||||
{ \
|
||||
DEQUEI_CONTRACT(src); \
|
||||
assert (d!= NULL); \
|
||||
memcpy(d, src, sizeof(deque_t)); \
|
||||
M_C(name,_node_list_init_move)(d->list, src->list); \
|
||||
d->front->node = src->front->node; \
|
||||
d->front->index = src->front->index; \
|
||||
d->back->node = src->back->node; \
|
||||
d->back->index = src->back->index; \
|
||||
d->default_size = src->default_size; \
|
||||
d->count = src->count; \
|
||||
memset(src, 0, sizeof(deque_t)); \
|
||||
DEQUEI_CONTRACT(d); \
|
||||
} \
|
||||
@@ -541,7 +662,7 @@
|
||||
{ \
|
||||
DEQUEI_CONTRACT(d); \
|
||||
DEQUEI_CONTRACT(e); \
|
||||
deque_node_list_swap (d->list, e->list); \
|
||||
M_C(name, _node_list_swap) (d->list, e->list); \
|
||||
M_SWAP(node_t *, d->front->node, e->front->node); \
|
||||
M_SWAP(node_t *, d->back->node, e->back->node); \
|
||||
M_SWAP(size_t, d->front->index, e->front->index); \
|
||||
@@ -559,12 +680,11 @@
|
||||
assert (key < d->count); \
|
||||
const size_t index0 = d->front->index; \
|
||||
size_t count = 0; \
|
||||
M_C(name, _node_list_it_t) it; \
|
||||
/* This loop is in log(N) since the size increase exponentially.*/ \
|
||||
for(M_C(name, _node_list_it)(it, d->list) ; \
|
||||
!M_C(name, _node_list_end_p)(it) ; \
|
||||
M_C(name, _node_list_next)(it) ){ \
|
||||
deque_node_t *n = M_C(name, _node_list_ref)(it); \
|
||||
for(M_C(name, _node_t) *n = d->front->node; \
|
||||
n != NULL ; \
|
||||
n = (n == d->back->node) ? NULL : \
|
||||
M_C(name, _node_list_next_obj)(d->list, n) ){ \
|
||||
if (index0 + key < count + n->size) { \
|
||||
return &n->data[index0 + key - count]; \
|
||||
} \
|
||||
@@ -574,7 +694,7 @@
|
||||
return NULL; \
|
||||
} \
|
||||
\
|
||||
static inline const type * \
|
||||
static inline type const * \
|
||||
M_C(name, _cget)(deque_t d, size_t key) \
|
||||
{ \
|
||||
return M_CONST_CAST(type, M_C(name, _get)(d, key)); \
|
||||
@@ -586,7 +706,7 @@
|
||||
DEQUEI_CONTRACT(d); \
|
||||
assert (key < d->count); \
|
||||
type *p = M_C(name, _get)(d, key); \
|
||||
M_GET_SET oplist (*p, x); \
|
||||
M_CALL_SET(oplist, *p, x); \
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD(EQUAL, oplist)( \
|
||||
@@ -602,9 +722,9 @@
|
||||
for(M_C(name, _it)(it1, d1), M_C(name,_it)(it2, d2); \
|
||||
!M_C(name, _end_p)(it1) ; \
|
||||
M_C(name, _next)(it1), M_C(name, _next)(it2)) { \
|
||||
const type *obj1 = M_C(name, _cref)(it1); \
|
||||
const type *obj2 = M_C(name, _cref)(it2); \
|
||||
if (M_GET_EQUAL oplist (*obj1, *obj2) == false) \
|
||||
type const *obj1 = M_C(name, _cref)(it1); \
|
||||
type const *obj2 = M_C(name, _cref)(it2); \
|
||||
if (M_CALL_EQUAL(oplist, *obj1, *obj2) == false) \
|
||||
return false; \
|
||||
} \
|
||||
assert (M_C(name, _end_p)(it2)); \
|
||||
@@ -620,8 +740,8 @@
|
||||
M_HASH_DECL(hash); \
|
||||
it_t it; \
|
||||
for(M_C(name, _it)(it, d); !M_C(name, _end_p)(it); M_C(name, _next)(it)) { \
|
||||
const type *obj = M_C(name, _cref)(it); \
|
||||
M_HASH_UP (hash, M_GET_HASH oplist (*obj)); \
|
||||
type const *obj = M_C(name, _cref)(it); \
|
||||
M_HASH_UP (hash, M_CALL_HASH(oplist, *obj)); \
|
||||
} \
|
||||
return M_HASH_FINAL(hash); \
|
||||
} \
|
||||
@@ -636,29 +756,29 @@
|
||||
assert (j < d->count); \
|
||||
type *obj1 = M_C(name, _get)(d, i); \
|
||||
type *obj2 = M_C(name, _get)(d, j); \
|
||||
M_GET_SWAP oplist (*obj1, *obj2); \
|
||||
M_CALL_SWAP(oplist, *obj1, *obj2); \
|
||||
DEQUEI_CONTRACT(d); \
|
||||
} \
|
||||
, /* NO SWAP: TODO */) \
|
||||
, /* NO SWAP */) \
|
||||
\
|
||||
M_IF_METHOD(GET_STR, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _get_str)(string_t str, deque_t deque, bool append) \
|
||||
M_C(name, _get_str)(string_t str, deque_t const deque, bool append) \
|
||||
{ \
|
||||
STRING_CONTRACT(str); \
|
||||
STRINGI_CONTRACT(str); \
|
||||
DEQUEI_CONTRACT(deque); \
|
||||
(append ? string_cat_str : string_set_str) (str, "["); \
|
||||
it_t it; \
|
||||
for (M_C(name, _it)(it, deque) ; \
|
||||
!M_C(name, _end_p)(it); \
|
||||
M_C(name, _next)(it)){ \
|
||||
const type *item = M_C(name, _cref)(it); \
|
||||
M_GET_GET_STR oplist (str, *item, true); \
|
||||
type const *item = M_C(name, _cref)(it); \
|
||||
M_CALL_GET_STR(oplist, str, *item, true); \
|
||||
if (!M_C(name, _last_p)(it)) \
|
||||
string_push_back (str, M_GET_SEPARATOR oplist); \
|
||||
} \
|
||||
string_push_back (str, ']'); \
|
||||
STRING_CONTRACT(str); \
|
||||
STRINGI_CONTRACT(str); \
|
||||
} \
|
||||
, /* no GET_STR */ ) \
|
||||
\
|
||||
@@ -669,17 +789,51 @@
|
||||
DEQUEI_CONTRACT(deque); \
|
||||
assert (file != NULL); \
|
||||
fputc ('[', file); \
|
||||
for (size_t i = 0; i < deque->size; i++) { \
|
||||
const type *item = M_C(name, _cget)(deque, i); \
|
||||
M_GET_OUT_STR oplist (file, *item); \
|
||||
if (i != deque->size-1) \
|
||||
it_t it; \
|
||||
for (M_C(name, _it)(it, deque) ; \
|
||||
!M_C(name, _end_p)(it); \
|
||||
M_C(name, _next)(it)) { \
|
||||
type const *item = M_C(name, _cref)(it); \
|
||||
M_CALL_OUT_STR(oplist, file, *item); \
|
||||
if (!M_C(name, _last_p)(it)) \
|
||||
fputc (M_GET_SEPARATOR oplist, file); \
|
||||
} \
|
||||
fputc (']', file); \
|
||||
} \
|
||||
, /* no OUT_STR */ ) \
|
||||
\
|
||||
M_IF_METHOD(IN_STR, oplist)( \
|
||||
M_IF_METHOD2(PARSE_STR, INIT, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _parse_str)(deque_t deque, const char str[], const char **endp) \
|
||||
{ \
|
||||
DEQUEI_CONTRACT(deque); \
|
||||
assert (str != NULL); \
|
||||
M_C(name,_clean)(deque); \
|
||||
bool success = false; \
|
||||
int c = *str++; \
|
||||
if (M_UNLIKELY (c != '[')) goto exit; \
|
||||
c = *str++; \
|
||||
if (M_UNLIKELY (c == ']')) {success = true; goto exit;} \
|
||||
if (M_UNLIKELY (c == 0)) goto exit; \
|
||||
str--; \
|
||||
type item; \
|
||||
M_CALL_INIT(oplist, item); \
|
||||
do { \
|
||||
bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \
|
||||
do { c = *str++; } while (isspace(c)); \
|
||||
if (b == false || c == 0) { goto exit; } \
|
||||
M_C(name, _push_back)(deque, item); \
|
||||
} while (c == M_GET_SEPARATOR oplist); \
|
||||
M_CALL_CLEAR(oplist, item); \
|
||||
DEQUEI_CONTRACT(deque); \
|
||||
success = (c == ']'); \
|
||||
exit: \
|
||||
if (endp) *endp = str; \
|
||||
return success; \
|
||||
} \
|
||||
, /* no PARSE_STR */ ) \
|
||||
\
|
||||
M_IF_METHOD2(IN_STR, INIT, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _in_str)(deque_t deque, FILE *file) \
|
||||
{ \
|
||||
@@ -693,14 +847,14 @@
|
||||
if (M_UNLIKELY (c == EOF)) return false; \
|
||||
ungetc(c, file); \
|
||||
type item; \
|
||||
M_GET_INIT oplist (item); \
|
||||
M_CALL_INIT(oplist, item); \
|
||||
do { \
|
||||
bool b = M_GET_IN_STR oplist (item, file); \
|
||||
c = fgetc(file); \
|
||||
bool b = M_CALL_IN_STR(oplist, item, file); \
|
||||
do { c = fgetc(file); } while (isspace(c)); \
|
||||
if (b == false || c == EOF) { break; } \
|
||||
M_C(name, _push_back)(deque, item); \
|
||||
} while (c == M_GET_SEPARATOR oplist); \
|
||||
M_GET_CLEAR oplist (item); \
|
||||
M_CALL_CLEAR(oplist, item); \
|
||||
DEQUEI_CONTRACT(deque); \
|
||||
return c == ']'; \
|
||||
} \
|
||||
@@ -712,6 +866,7 @@
|
||||
#define DEQUEI_OPLIST2(name, oplist) \
|
||||
(INIT(M_C(name, _init)) \
|
||||
,INIT_SET(M_C(name, _init_set)) \
|
||||
,INIT_WITH(API_1(M_INIT_VAI)) \
|
||||
,SET(M_C(name, _set)) \
|
||||
,CLEAR(M_C(name, _clear)) \
|
||||
,INIT_MOVE(M_C(name, _init_move)) \
|
||||
@@ -719,6 +874,7 @@
|
||||
,SWAP(M_C(name, _swap)) \
|
||||
,TYPE(M_C(name,_t)) \
|
||||
,SUBTYPE(M_C(name, _type_t)) \
|
||||
,TEST_EMPTY(M_C(name,_empty_p)) \
|
||||
,IT_TYPE(M_C(name,_it_t)) \
|
||||
,IT_FIRST(M_C(name,_it)) \
|
||||
,IT_LAST(M_C(name,_it_last)) \
|
||||
@@ -736,6 +892,7 @@
|
||||
,POP(M_C(name,_pop_back)) \
|
||||
,OPLIST(oplist) \
|
||||
,M_IF_METHOD(GET_STR, oplist)(GET_STR(M_C(name, _get_str)),) \
|
||||
,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_C(name, _parse_str)),) \
|
||||
,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_C(name, _out_str)),) \
|
||||
,M_IF_METHOD(IN_STR, oplist)(IN_STR(M_C(name, _in_str)),) \
|
||||
,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_C(name, _equal_p)),) \
|
||||
|
||||
+149
-52
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - GENINT module
|
||||
*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -22,8 +22,8 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __M_GENINT_H
|
||||
#define __M_GENINT_H
|
||||
#ifndef MSTARLIB_GENINT_H
|
||||
#define MSTARLIB_GENINT_H
|
||||
|
||||
#include "m-core.h"
|
||||
#include "m-atomic.h"
|
||||
@@ -31,43 +31,82 @@
|
||||
/* GENINT is a container of integers with the following properties:
|
||||
|
||||
- it stores integer from [0..N) (N is fixed).
|
||||
- an integer can have only one occurence in the container
|
||||
(makeing the container a kind of permutation of [0..N) )
|
||||
- you can atomically push in / pop out integer from this container.
|
||||
- an integer can have only one occurrence in the container.
|
||||
- you can atomically push in / pop out integer from this container
|
||||
provided that they were not already in the container.
|
||||
- there are no order (like FIFO or stack)
|
||||
|
||||
This can be used to map integers to index of ressources in a table.
|
||||
This can be used to map integers to index of resources in a table.
|
||||
At most we can support N = GENINT_LIMBSIZE * GENINT_LIMBSIZE >= 4096
|
||||
due to the master trick. For the typical usage of this container
|
||||
(mapping hardware or software limited resources), this should be
|
||||
enough.
|
||||
*/
|
||||
|
||||
typedef unsigned long genint_limb_t;
|
||||
// TO DO: We may want a specialization with constant N value.
|
||||
|
||||
// Define the limb size
|
||||
typedef unsigned long long genint_limb_t;
|
||||
|
||||
typedef struct genint_s {
|
||||
size_t n;
|
||||
atomic_ulong *data;
|
||||
unsigned int n; // size of the container
|
||||
unsigned int max; // number of allocated limb - 1
|
||||
genint_limb_t mask0; // mask of the last limb
|
||||
genint_limb_t mask_master; // mask of master
|
||||
atomic_ullong master; // master bitfield (which informs if a limb is full or not)
|
||||
atomic_ullong *data; // the bitfield which informs if an integer
|
||||
} genint_t[1];
|
||||
|
||||
// Define the max supported value
|
||||
#define GENINT_MAX_ALLOC (GENINT_LIMBSIZE * (GENINT_LIMBSIZE - GENINT_ABA_CPT))
|
||||
|
||||
// Define the size of a limb in bits.
|
||||
#define GENINT_LIMBSIZE (sizeof(genint_limb_t) * CHAR_BIT)
|
||||
|
||||
#define GENINT_CONTRACT(s) do { \
|
||||
assert (s != NULL); \
|
||||
assert (s->n > 0); \
|
||||
assert (s->data != NULL); \
|
||||
#define GENINT_CONTRACT(s) do { \
|
||||
assert (s != NULL); \
|
||||
assert (s->n > 0 && s->n <= GENINT_MAX_ALLOC); \
|
||||
assert ((s->max+1) * GENINT_LIMBSIZE >= s->n); \
|
||||
assert (s->data != NULL); \
|
||||
} while (0)
|
||||
|
||||
#define GENINT_ONE ((genint_limb_t)1)
|
||||
#define GENINT_ONE ((genint_limb_t)1)
|
||||
|
||||
static inline void genint_init(genint_t s, size_t n)
|
||||
// Value returned in case of error.
|
||||
#define GENINT_ERROR (-1U)
|
||||
|
||||
// 32 bits of master are kept for handling the ABA problems.
|
||||
#define GENINT_ABA_CPT 32
|
||||
#define GENINT_ABA_CPT_T uint32_t
|
||||
|
||||
// Set the bit i of master
|
||||
#define GENINT_MASTER_SET(master, i) \
|
||||
((((master)& (~((GENINT_ONE<< GENINT_ABA_CPT)-1))) | (GENINT_ONE << (GENINT_LIMBSIZE - 1 - i))) \
|
||||
|((GENINT_ABA_CPT_T)((master) + 1)))
|
||||
|
||||
// Reset the bit i of master
|
||||
#define GENINT_MASTER_RESET(master, i) \
|
||||
(((master) & (~((GENINT_ONE<< GENINT_ABA_CPT)-1)) & ~(GENINT_ONE << (GENINT_LIMBSIZE - 1 - i))) \
|
||||
|((GENINT_ABA_CPT_T)((master) + 1)))
|
||||
|
||||
static inline void genint_init(genint_t s, unsigned int n)
|
||||
{
|
||||
assert (s != NULL && n > 0);
|
||||
assert (s != NULL && n > 0 && n <= GENINT_MAX_ALLOC);
|
||||
const size_t alloc = (n + GENINT_LIMBSIZE - 1) / GENINT_LIMBSIZE;
|
||||
atomic_ulong *ptr = M_MEMORY_REALLOC (atomic_ulong, NULL, alloc);
|
||||
s->n = n;
|
||||
s->data = ptr;
|
||||
const unsigned int index = n % GENINT_LIMBSIZE;
|
||||
atomic_ullong *ptr = M_MEMORY_REALLOC (atomic_ullong, NULL, alloc);
|
||||
if (M_UNLIKELY (ptr == NULL)) {
|
||||
M_MEMORY_FULL(alloc);
|
||||
return;
|
||||
}
|
||||
for(size_t i = 0; i < alloc; i++)
|
||||
atomic_init(&s->data[i], 0UL);
|
||||
s->n = n;
|
||||
s->data = ptr;
|
||||
s->max = alloc-1;
|
||||
s->mask0 = (index == 0) ? -GENINT_ONE : ~((GENINT_ONE<<(GENINT_LIMBSIZE-index))-1);
|
||||
s->mask_master = (((GENINT_ONE << alloc) - 1) << (GENINT_LIMBSIZE-alloc)) >> GENINT_ABA_CPT;
|
||||
atomic_init (&s->master, (genint_limb_t)0);
|
||||
for(unsigned int i = 0; i < alloc; i++)
|
||||
atomic_init(&s->data[i], (genint_limb_t)0);
|
||||
GENINT_CONTRACT(s);
|
||||
}
|
||||
|
||||
@@ -78,47 +117,105 @@ static inline void genint_clear(genint_t s)
|
||||
s->data = NULL;
|
||||
}
|
||||
|
||||
static inline size_t genint_pop(genint_t s)
|
||||
static inline size_t genint_size(genint_t s)
|
||||
{
|
||||
GENINT_CONTRACT(s);
|
||||
const size_t max = (s->n + GENINT_LIMBSIZE - 1) / GENINT_LIMBSIZE;
|
||||
const size_t index = s->n % GENINT_LIMBSIZE;
|
||||
const genint_limb_t mask0 = (index == 0) ? -GENINT_ONE : (GENINT_ONE<<index)-1;
|
||||
// O(n) approach, but with a really low constant factor
|
||||
for(size_t i = 0; i < max; i++)
|
||||
{
|
||||
const size_t mask = (i+1 == max) ? mask0 : -GENINT_ONE;
|
||||
unsigned int bit;
|
||||
genint_limb_t next, org = atomic_load(&s->data[i]);
|
||||
do {
|
||||
if (org == mask)
|
||||
goto next_element;
|
||||
// At least one bit is free in the limb
|
||||
bit = GENINT_LIMBSIZE - m_core_clz(org);
|
||||
assert ((org & (GENINT_ONE<<bit)) == 0);
|
||||
// Set & reserve the integer as being used.
|
||||
next = org | (GENINT_ONE << bit);
|
||||
} while (!atomic_compare_exchange_weak (&s->data[i], &org, next));
|
||||
// Return the new number
|
||||
GENINT_CONTRACT(s);
|
||||
return i * GENINT_LIMBSIZE + bit;
|
||||
next_element: (void)1;
|
||||
}
|
||||
GENINT_CONTRACT(s);
|
||||
return -(size_t)1; // No more ressource available
|
||||
return s->n;
|
||||
}
|
||||
|
||||
static inline void genint_push(genint_t s, size_t n)
|
||||
// For a typical case, the amortized cost is one CAS per pop.
|
||||
static inline unsigned int genint_pop(genint_t s)
|
||||
{
|
||||
GENINT_CONTRACT(s);
|
||||
// First read master to see which limb is not full.
|
||||
genint_limb_t master = atomic_load(&s->master);
|
||||
// While master is not full
|
||||
while ((master >> GENINT_ABA_CPT) != s->mask_master) {
|
||||
// Let's get the index i of the first not full limb according to master.
|
||||
unsigned int i = m_core_clz64(~master);
|
||||
assert (i < GENINT_LIMBSIZE);
|
||||
// Let's compute the mask of this limb representing the limb as being full
|
||||
genint_limb_t mask = s->mask0;
|
||||
mask = (i == s->max) ? mask : -GENINT_ONE;
|
||||
unsigned int bit;
|
||||
// Let's load this limb,
|
||||
genint_limb_t next, org = atomic_load(&s->data[i]);
|
||||
do {
|
||||
// If it is now full, we have been preempted by another.
|
||||
if (M_UNLIKELY (org == mask))
|
||||
goto next_element;
|
||||
assert (org != -GENINT_ONE);
|
||||
// At least one bit is free in the limb. Find one.
|
||||
bit = GENINT_LIMBSIZE - 1 - m_core_clz64(~org);
|
||||
assert (bit < GENINT_LIMBSIZE);
|
||||
assert ((org & (GENINT_ONE<<bit)) == 0);
|
||||
assert (i * GENINT_LIMBSIZE + GENINT_LIMBSIZE - 1 - bit < s->n);
|
||||
// Set the integer as being used.
|
||||
next = org | (GENINT_ONE << bit);
|
||||
// Try to reserve the integer
|
||||
} while (!atomic_compare_exchange_weak (&s->data[i], &org, next));
|
||||
// We have reserved the integer.
|
||||
// If the limb is now full, try to update master
|
||||
if (M_UNLIKELY(next == mask)) {
|
||||
while (true) {
|
||||
genint_limb_t newMaster;
|
||||
if (next == mask) {
|
||||
newMaster = GENINT_MASTER_SET(master, i);
|
||||
} else {
|
||||
newMaster = GENINT_MASTER_RESET(master, i);
|
||||
}
|
||||
if (atomic_compare_exchange_weak (&s->master, &master, newMaster))
|
||||
break;
|
||||
// Fail to update. Reload limb to check if it is still full.
|
||||
next = atomic_load(&s->data[i]);
|
||||
}
|
||||
}
|
||||
// Return the new number
|
||||
GENINT_CONTRACT(s);
|
||||
return i * GENINT_LIMBSIZE + GENINT_LIMBSIZE - 1 - bit;
|
||||
next_element:
|
||||
// Reload master
|
||||
master = atomic_load(&s->master);
|
||||
}
|
||||
GENINT_CONTRACT(s);
|
||||
return GENINT_ERROR; // No more resource available
|
||||
}
|
||||
|
||||
// For a typical case, the amortized cost is one CAS per pop.
|
||||
static inline void genint_push(genint_t s, unsigned int n)
|
||||
{
|
||||
GENINT_CONTRACT(s);
|
||||
assert (n < s->n);
|
||||
size_t i = n / GENINT_LIMBSIZE;
|
||||
size_t bit = n % GENINT_LIMBSIZE;
|
||||
const unsigned int i = n / GENINT_LIMBSIZE;
|
||||
const unsigned int bit = GENINT_LIMBSIZE - 1 - (n % GENINT_LIMBSIZE);
|
||||
genint_limb_t master = atomic_load(&s->master);
|
||||
// Load the limb
|
||||
genint_limb_t next, org = atomic_load(&s->data[i]);
|
||||
do {
|
||||
assert ((org & (GENINT_ONE<<bit)) != 0);
|
||||
assert ((org & (GENINT_ONE << bit)) != 0);
|
||||
// Reset it
|
||||
next = org & (~(GENINT_ONE << bit));
|
||||
// Try to unreserve it.
|
||||
} while (!atomic_compare_exchange_weak (&s->data[i], &org, next));
|
||||
// if the limb was marked as full by master
|
||||
genint_limb_t mask = s->mask0;
|
||||
mask = (i == s->max) ? mask : -GENINT_ONE;
|
||||
if (M_UNLIKELY (next != mask)) {
|
||||
// Let's compute the mask of this limb representing the limb as being full
|
||||
// Let's try to update master to say that this limb is not full
|
||||
while (true) {
|
||||
genint_limb_t newMaster;
|
||||
if (next == mask) {
|
||||
newMaster = GENINT_MASTER_SET(master, i);
|
||||
} else {
|
||||
newMaster = GENINT_MASTER_RESET(master, i);
|
||||
}
|
||||
if (atomic_compare_exchange_weak (&s->master, &master, newMaster))
|
||||
break;
|
||||
// Fail to update. Reload limb to check if it is still full.
|
||||
next = atomic_load(&s->data[i]);
|
||||
}
|
||||
}
|
||||
GENINT_CONTRACT(s);
|
||||
}
|
||||
|
||||
|
||||
+153
-34
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - Intrusive List module
|
||||
*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -22,16 +22,17 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __M_I_LIST_H
|
||||
#define __M_I_LIST_H
|
||||
#ifndef MSTARLIB_I_LIST_H
|
||||
#define MSTARLIB_I_LIST_H
|
||||
|
||||
#include "m-core.h"
|
||||
#include "m-list.h" // For LISTI_ITBASE_DEF
|
||||
|
||||
/* Interface to add to a structure to allow intrusive support.
|
||||
/* Interface to add to a structure to enable intrusive doubly-linked support.
|
||||
name: name of the intrusive list.
|
||||
type: name of the type of the structure (aka. struct test_s) - not used currently
|
||||
USAGE:
|
||||
struct tmp_str_s {
|
||||
typedef struct tmp_str_s {
|
||||
...
|
||||
ILIST_INTERFACE(tmpstr, struct tmp_str_s);
|
||||
...
|
||||
@@ -40,20 +41,21 @@
|
||||
#define ILIST_INTERFACE(name, type) \
|
||||
struct ilist_head_s name
|
||||
|
||||
/* Define a list of a given type.
|
||||
/* Define a doubly-linked intrusive list of a given type.
|
||||
The type needs to have ILIST_INTERFACE().
|
||||
USAGE:
|
||||
LIST_DEF(name, type [, oplist_of_the_type]) */
|
||||
ILIST_DEF(name, type [, oplist_of_the_type]) */
|
||||
#define ILIST_DEF(name, ...) \
|
||||
ILISTI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__), M_C(name, _t), M_C(name, _it_t) ), \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name, _t), M_C(name, _it_t) ), \
|
||||
(name, __VA_ARGS__, M_C(name, _t), M_C(name, _it_t) )))
|
||||
|
||||
/* Define the oplist of a ilist of type.
|
||||
/* Define the oplist of a doubly-linked instrusive list of type.
|
||||
USAGE:
|
||||
LIST_OPLIST(name [, oplist_of_the_type]) */
|
||||
ILIST_OPLIST(name [, oplist_of_the_type]) */
|
||||
#define ILIST_OPLIST(...) \
|
||||
ILISTI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__) ), \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
|
||||
@@ -65,15 +67,19 @@ typedef struct ilist_head_s {
|
||||
struct ilist_head_s *prev;
|
||||
} ilist_head_t;
|
||||
|
||||
/* Indirection call to allow expanding all arguments (TBC) */
|
||||
/* Indirection call to allow expanding all arguments */
|
||||
#define ILISTI_OPLIST(arg) ILISTI_OPLIST2 arg
|
||||
|
||||
/* Define the oplist of an ilist of type */
|
||||
#define ILISTI_OPLIST2(name,oplist) \
|
||||
(INIT(M_C(name, _init)), \
|
||||
CLEAR(M_C(name, _clear)), \
|
||||
INIT_MOVE(M_C(name, _init_move)), \
|
||||
MOVE(M_C(name, _move)), \
|
||||
TYPE(M_C(name,_t)), \
|
||||
CLEAN(M_C(name,_clean)), \
|
||||
SUBTYPE(M_C(name,_type_t)), \
|
||||
TEST_EMPTY(M_C(name,_empty_p)), \
|
||||
IT_TYPE(M_C(name,_it_t)), \
|
||||
IT_FIRST(M_C(name,_it)), \
|
||||
IT_SET(M_C(name,_it_set)), \
|
||||
@@ -91,21 +97,32 @@ typedef struct ilist_head_s {
|
||||
OPLIST(oplist), \
|
||||
PUSH(M_C(name,_push_back)), \
|
||||
POP(M_C(name,_pop_back)) \
|
||||
SPLICE_BACK(M_C(name,_splice_back)) \
|
||||
,M_IF_METHOD(NEW, oplist)(NEW(M_GET_NEW oplist),) \
|
||||
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
|
||||
)
|
||||
|
||||
/* Contract respected by all intrusive lists */
|
||||
#define ILISTI_CONTRACT(name, list) do { \
|
||||
assert (list != NULL); \
|
||||
assert(list->name.next->prev == &list->name); \
|
||||
assert(list->name.prev->next == &list->name); \
|
||||
assert(list != NULL); \
|
||||
assert(list->name.prev != NULL); \
|
||||
assert(list->name.next != NULL); \
|
||||
assert(list->name.next->prev == &list->name); \
|
||||
assert(list->name.prev->next == &list->name); \
|
||||
} while (0)
|
||||
|
||||
#define ILISTI_NODE_CONTRACT(node) do { \
|
||||
assert((node) != NULL); \
|
||||
assert((node)->prev != NULL); \
|
||||
assert((node)->next != NULL); \
|
||||
assert((node)->next->prev == node); \
|
||||
assert((node)->prev->next == node); \
|
||||
} while (0)
|
||||
|
||||
/* Indirection call to allow expanding all arguments (TBC) */
|
||||
#define ILISTI_DEF(arg) ILISTI_DEF2 arg
|
||||
|
||||
/* Definition of the type and function for an intrusive list.
|
||||
/* Definition of the type and function for an intrusive doubly-linked list.
|
||||
USAGE:
|
||||
name: name of the intrusive list
|
||||
type: type of the object
|
||||
@@ -118,6 +135,8 @@ typedef struct ilist_head_s {
|
||||
typedef struct M_C(name, _s) { \
|
||||
struct ilist_head_s name; \
|
||||
} list_t[1]; \
|
||||
typedef struct M_C(name, _s) *M_C(name, _ptr); \
|
||||
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
|
||||
\
|
||||
typedef type M_C(name, _type_t); \
|
||||
\
|
||||
@@ -141,14 +160,15 @@ typedef struct ilist_head_s {
|
||||
ILISTI_CONTRACT(name, list); \
|
||||
for(struct ilist_head_s *it = list->name.next, *next ; \
|
||||
it != &list->name; it = next) { \
|
||||
/* Cannot check node contract as previous node may be deleted */ \
|
||||
type *obj = M_TYPE_FROM_FIELD(type, it, \
|
||||
struct ilist_head_s, name); \
|
||||
/* Read next now before the object is destroyed */ \
|
||||
next = it->next; \
|
||||
assert (next != NULL); \
|
||||
M_GET_CLEAR oplist (*obj); \
|
||||
M_CALL_CLEAR(oplist, *obj); \
|
||||
/* Delete also the object if a DELETE operand is registered */ \
|
||||
M_IF_METHOD(DEL, oplist)(M_GET_DEL oplist (obj), (void) 0); \
|
||||
M_IF_METHOD(DEL, oplist)(M_CALL_DEL(oplist, obj), (void) 0); \
|
||||
} \
|
||||
/* Nothing remains in the list anymore */ \
|
||||
list->name.next = &list->name; \
|
||||
@@ -165,6 +185,29 @@ typedef struct ilist_head_s {
|
||||
{ \
|
||||
ILISTI_CONTRACT(name, list); \
|
||||
return list->name.next == &list->name; \
|
||||
} \
|
||||
\
|
||||
\
|
||||
static inline void M_C(name, _init_move)(list_t list, list_t ref) \
|
||||
{ \
|
||||
ILISTI_CONTRACT(name, ref); \
|
||||
assert (list != ref); \
|
||||
M_C(name,_init)(list); \
|
||||
if (!M_C(name,_empty_p)(ref)) { \
|
||||
list->name.next = ref->name.next; \
|
||||
list->name.prev = ref->name.prev; \
|
||||
list->name.next->prev = &list->name; \
|
||||
list->name.prev->next = &list->name; \
|
||||
} \
|
||||
ref->name.next = NULL; \
|
||||
ref->name.prev = NULL; \
|
||||
ILISTI_CONTRACT(name, list); \
|
||||
} \
|
||||
\
|
||||
static inline void M_C(name, _move)(list_t list, list_t ref) \
|
||||
{ \
|
||||
M_C(name, _clear)(list); \
|
||||
M_C(name, _init_move)(list, ref); \
|
||||
} \
|
||||
\
|
||||
static inline size_t M_C(name, _size)(const list_t list) \
|
||||
@@ -172,8 +215,10 @@ typedef struct ilist_head_s {
|
||||
ILISTI_CONTRACT(name, list); \
|
||||
size_t s = 0; \
|
||||
for(const struct ilist_head_s *it = list->name.next ; \
|
||||
it != &list->name; it = it->next) \
|
||||
it != &list->name; it = it->next) { \
|
||||
ILISTI_NODE_CONTRACT(it); \
|
||||
s++; \
|
||||
} \
|
||||
return s; \
|
||||
} \
|
||||
\
|
||||
@@ -249,34 +294,37 @@ typedef struct ilist_head_s {
|
||||
} \
|
||||
\
|
||||
static inline type * \
|
||||
M_C(name, _next_obj)(const list_t list, const type *obj) \
|
||||
M_C(name, _next_obj)(const list_t list, type const *obj) \
|
||||
{ \
|
||||
ILISTI_CONTRACT(name, list); \
|
||||
assert (obj != NULL); \
|
||||
ILISTI_NODE_CONTRACT(&obj->name); \
|
||||
return obj->name.next == &list->name ? NULL : \
|
||||
M_TYPE_FROM_FIELD(type, obj->name.next, \
|
||||
struct ilist_head_s, name); \
|
||||
} \
|
||||
\
|
||||
static inline type * \
|
||||
M_C(name, _previous_obj)(const list_t list, const type *obj) \
|
||||
M_C(name, _previous_obj)(const list_t list, type const *obj) \
|
||||
{ \
|
||||
ILISTI_CONTRACT(name, list); \
|
||||
assert (obj != NULL); \
|
||||
ILISTI_NODE_CONTRACT(&obj->name); \
|
||||
return obj->name.prev == &list->name ? NULL : \
|
||||
M_TYPE_FROM_FIELD(type, obj->name.prev, \
|
||||
struct ilist_head_s, name); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it)(list_it_t it, list_t list) \
|
||||
M_C(name, _it)(list_it_t it, const list_t list) \
|
||||
{ \
|
||||
ILISTI_CONTRACT(name, list); \
|
||||
assert (it != NULL); \
|
||||
it->head = &list->name; \
|
||||
it->head = list->name.next->prev; \
|
||||
it->current = list->name.next; \
|
||||
it->next = list->name.next->next; \
|
||||
it->previous = &list->name; \
|
||||
it->previous = it->head; \
|
||||
ILISTI_NODE_CONTRACT(it->current); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
@@ -287,6 +335,7 @@ typedef struct ilist_head_s {
|
||||
it->current = cit->current; \
|
||||
it->next = cit->next; \
|
||||
it->previous = cit->previous; \
|
||||
ILISTI_NODE_CONTRACT(it->current); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
@@ -298,6 +347,7 @@ typedef struct ilist_head_s {
|
||||
it->current = list->name.prev; \
|
||||
it->next = &list->name; \
|
||||
it->previous = list->name.prev->prev; \
|
||||
ILISTI_NODE_CONTRACT(it->current); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
@@ -308,12 +358,14 @@ typedef struct ilist_head_s {
|
||||
it->current = &list->name; \
|
||||
it->next = list->name.next; \
|
||||
it->previous = list->name.prev; \
|
||||
ILISTI_NODE_CONTRACT(it->current); \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _end_p)(const list_it_t it) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
ILISTI_NODE_CONTRACT(it->current); \
|
||||
return it->current == it->head; \
|
||||
} \
|
||||
\
|
||||
@@ -321,6 +373,7 @@ typedef struct ilist_head_s {
|
||||
M_C(name, _last_p)(const list_it_t it) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
ILISTI_NODE_CONTRACT(it->current); \
|
||||
return it->next == it->head || it->current == it->head; \
|
||||
} \
|
||||
\
|
||||
@@ -328,6 +381,7 @@ typedef struct ilist_head_s {
|
||||
M_C(name, _next)(list_it_t it) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
/* Cannot check node for it->current: it may have been deleted! */ \
|
||||
/* Note: Can't set it->previous to it->current. \
|
||||
it->current may have been unlinked from the list */ \
|
||||
it->current = it->next; \
|
||||
@@ -335,12 +389,14 @@ typedef struct ilist_head_s {
|
||||
it->next = it->current->next; \
|
||||
it->previous = it->current->prev; \
|
||||
assert (it->next != NULL && it->previous != NULL); \
|
||||
ILISTI_NODE_CONTRACT(it->current); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _previous)(list_it_t it) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
/* Cannot check node for it->current: it may have been deleted! */ \
|
||||
/* Note: Can't set it->next to it->current. \
|
||||
it->current may have been unlinked from the list */ \
|
||||
it->current = it->previous; \
|
||||
@@ -348,6 +404,7 @@ typedef struct ilist_head_s {
|
||||
it->next = it->current->next; \
|
||||
it->previous = it->current->prev; \
|
||||
assert (it->next != NULL && it->previous != NULL); \
|
||||
ILISTI_NODE_CONTRACT(it->current); \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
@@ -362,6 +419,7 @@ typedef struct ilist_head_s {
|
||||
M_C(name, _ref)(const list_it_t it) \
|
||||
{ \
|
||||
assert (it != NULL && it->current != NULL); \
|
||||
ILISTI_NODE_CONTRACT(it->current); \
|
||||
/* check if 'it' was not deleted */ \
|
||||
assert (it->current->next == it->next); \
|
||||
assert (it->current->prev == it->previous); \
|
||||
@@ -370,7 +428,7 @@ typedef struct ilist_head_s {
|
||||
struct ilist_head_s, name); \
|
||||
} \
|
||||
\
|
||||
static inline const type * \
|
||||
static inline type const * \
|
||||
M_C(name, _cref)(const list_it_t it) \
|
||||
{ \
|
||||
type *ptr = M_C(name, _ref)(it); \
|
||||
@@ -381,12 +439,13 @@ typedef struct ilist_head_s {
|
||||
M_C(name, _remove)(list_t list, list_it_t it) \
|
||||
{ \
|
||||
ILISTI_CONTRACT(name, list); \
|
||||
ILISTI_NODE_CONTRACT(it->current); \
|
||||
(void)list; /* list param is not used */ \
|
||||
type *obj = M_TYPE_FROM_FIELD(type, it->current, \
|
||||
struct ilist_head_s, name); \
|
||||
M_C(name, _unlink)(obj); \
|
||||
M_GET_CLEAR oplist (obj); \
|
||||
M_IF_METHOD(DEL, oplist)(M_GET_DEL oplist (obj), (void) 0); \
|
||||
M_CALL_CLEAR(oplist, obj); \
|
||||
M_IF_METHOD(DEL, oplist)(M_CALL_DEL(oplist, obj), (void) 0); \
|
||||
M_C(name, _next)(it); \
|
||||
} \
|
||||
\
|
||||
@@ -395,12 +454,13 @@ typedef struct ilist_head_s {
|
||||
M_C(name, _insert)(list_t list, list_it_t it, type x) \
|
||||
{ \
|
||||
ILISTI_CONTRACT(name, list); \
|
||||
type *p = M_GET_NEW oplist (type); \
|
||||
ILISTI_NODE_CONTRACT(it->current); \
|
||||
type *p = M_CALL_NEW(oplist, type); \
|
||||
if (M_UNLIKELY (p == NULL)) { \
|
||||
M_MEMORY_FULL (sizeof (type)); \
|
||||
return ; \
|
||||
} \
|
||||
M_GET_INIT_SET oplist (*p, x); \
|
||||
M_CALL_INIT_SET(oplist, *p, x); \
|
||||
type *obj = M_C(name, _ref)(it); \
|
||||
M_C(name, _push_after)(obj, p); \
|
||||
(void) list; \
|
||||
@@ -452,26 +512,85 @@ typedef struct ilist_head_s {
|
||||
{ \
|
||||
ILISTI_CONTRACT(name, nv); \
|
||||
ILISTI_CONTRACT(name, ov); \
|
||||
ILISTI_NODE_CONTRACT(it->current); \
|
||||
assert (it != NULL); \
|
||||
(void) ov; \
|
||||
type *obj = M_C(name, _ref)(it); \
|
||||
M_C(name, _unlink)(obj); \
|
||||
M_C(name, _push_back)(nv, obj); \
|
||||
M_C(name, _next)(it); \
|
||||
ILISTI_CONTRACT(name, nv); \
|
||||
ILISTI_CONTRACT(name, ov); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _splice_at)(list_t nlist, list_it_t npos, \
|
||||
list_t olist, list_it_t opos) \
|
||||
{ \
|
||||
ILISTI_CONTRACT(name, nlist); \
|
||||
ILISTI_CONTRACT(name, olist); \
|
||||
assert (npos != NULL && opos != NULL); \
|
||||
assert (!M_C(name, _end_p)(opos)); \
|
||||
/* npos may be end */ \
|
||||
(void) olist, (void) nlist; \
|
||||
type *obj = M_C(name, _ref)(opos); \
|
||||
struct ilist_head_s *ref = npos->current; \
|
||||
/* Remove object */ \
|
||||
M_C(name, _unlink)(obj); \
|
||||
/* Push 'obj' after 'ref' */ \
|
||||
struct ilist_head_s *next = ref->next; \
|
||||
ref->next = &obj->name; \
|
||||
obj->name.next = next; \
|
||||
obj->name.prev = ref; \
|
||||
next->prev = &obj->name; \
|
||||
/* Move iterator in old list */ \
|
||||
M_C(name, _next)(opos); \
|
||||
/* Set npos iterator to new position of object */ \
|
||||
npos->previous = ref; \
|
||||
npos->current = &obj->name; \
|
||||
npos->next = next; \
|
||||
ILISTI_CONTRACT(name, nlist); \
|
||||
ILISTI_CONTRACT(name, olist); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _swap)(list_t d, list_t e) \
|
||||
{ \
|
||||
ILISTI_CONTRACT(name, d); \
|
||||
ILISTI_CONTRACT(name, e); \
|
||||
M_SWAP(struct ilist_head_s *, d->name.next, e->name.next); \
|
||||
M_SWAP(struct ilist_head_s *, d->name.prev, e->name.prev); \
|
||||
M_SWAP(struct ilist_head_s *, d->name.next->prev, e->name.next->prev); \
|
||||
M_SWAP(struct ilist_head_s *, d->name.prev->next, e->name.prev->next); \
|
||||
struct ilist_head_s *d_item = d->name.next; \
|
||||
struct ilist_head_s *e_item = e->name.next; \
|
||||
d->name.next = e_item == &e->name ? &d->name : e_item; \
|
||||
e->name.next = d_item == &d->name ? &e->name : d_item; \
|
||||
d_item = d->name.prev; \
|
||||
e_item = e->name.prev; \
|
||||
d->name.prev = e_item == &e->name ? &d->name : e_item; \
|
||||
e->name.prev = d_item == &d->name ? &e->name : d_item; \
|
||||
d->name.next->prev = &d->name; \
|
||||
d->name.prev->next = &d->name; \
|
||||
e->name.next->prev = &e->name; \
|
||||
e->name.prev->next = &e->name; \
|
||||
ILISTI_CONTRACT(name, d); \
|
||||
ILISTI_CONTRACT(name, e); \
|
||||
} \
|
||||
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _reverse)(list_t list) \
|
||||
{ \
|
||||
ILISTI_CONTRACT(name, list); \
|
||||
struct ilist_head_s *next, *it; \
|
||||
for(it = list->name.next ; it != &list->name; it = next) { \
|
||||
next = it->next; \
|
||||
it->next = it->prev; \
|
||||
it->prev = next; \
|
||||
} \
|
||||
next = it->next; \
|
||||
it->next = it->prev; \
|
||||
it->prev = next; \
|
||||
ILISTI_CONTRACT(name, list); \
|
||||
} \
|
||||
\
|
||||
LISTI_ITBASE_DEF(name, type, oplist, list_t, list_it_t)
|
||||
|
||||
|
||||
#endif
|
||||
+18
-18
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - INTRUSIVE SHARED PTR Module
|
||||
*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -22,8 +22,8 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __M_I_SHARED_PTR__H
|
||||
#define __M_I_SHARED_PTR__H
|
||||
#ifndef MSTARLIB_I_SHARED_PTR_H
|
||||
#define MSTARLIB_I_SHARED_PTR_H
|
||||
|
||||
#include "m-core.h"
|
||||
#include "m-atomic.h"
|
||||
@@ -32,7 +32,7 @@
|
||||
USAGE: ISHARED_OPLIST(name [, oplist_of_the_type]) */
|
||||
#define ISHARED_PTR_OPLIST(...) \
|
||||
ISHAREDI_PTR_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__) ), \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
/* Interface to add to a structure to allow intrusive support.
|
||||
@@ -46,7 +46,7 @@
|
||||
USAGE: ISHARED_PTR_DEF(name[, oplist]) */
|
||||
#define ISHARED_PTR_DEF(name, ...) \
|
||||
ISHAREDI_PTR_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__) ), \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
|
||||
(name, __VA_ARGS__ )))
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
typedef type *M_C(name,_t); \
|
||||
typedef type M_C(name, _type_t); \
|
||||
\
|
||||
static inline type * \
|
||||
static inline M_C(name,_t) \
|
||||
M_C(name, _init)(type *ptr) \
|
||||
{ \
|
||||
if (M_LIKELY (ptr != NULL)) \
|
||||
@@ -83,8 +83,8 @@
|
||||
return ptr; \
|
||||
} \
|
||||
\
|
||||
static inline type * \
|
||||
M_C(name, _init_set)(type *shared) \
|
||||
static inline M_C(name,_t) \
|
||||
M_C(name, _init_set)(M_C(name,_t) shared) \
|
||||
{ \
|
||||
if (M_LIKELY (shared != NULL)) { \
|
||||
int n = atomic_fetch_add(&(shared->M_C(name, _cpt)), 1); \
|
||||
@@ -94,45 +94,45 @@
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _init_set2)(type ** ptr, type *shared) \
|
||||
M_C(name, _init_set2)(M_C(name,_t) *ptr, M_C(name,_t) shared) \
|
||||
{ \
|
||||
assert (ptr != NULL); \
|
||||
*ptr = M_C(name, _init_set)(shared); \
|
||||
} \
|
||||
\
|
||||
static inline type * \
|
||||
M_C(name, _init_new)(void) \
|
||||
static inline M_C(name,_t) \
|
||||
M_C(name, _init_new)(void) \
|
||||
{ \
|
||||
type *ptr = M_GET_NEW oplist (type); \
|
||||
type *ptr = M_CALL_NEW(oplist, type); \
|
||||
if (ptr == NULL) { \
|
||||
M_MEMORY_FULL(sizeof(type)); \
|
||||
return NULL; \
|
||||
} \
|
||||
M_GET_INIT oplist (*ptr); \
|
||||
M_CALL_INIT(oplist, *ptr); \
|
||||
atomic_init (&ptr->M_C(name, _cpt), 1); \
|
||||
return ptr; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _clear)(type *shared) \
|
||||
M_C(name, _clear)(M_C(name,_t) shared) \
|
||||
{ \
|
||||
if (shared != NULL) { \
|
||||
if (atomic_fetch_sub(&(shared->M_C(name, _cpt)), 1) == 1) { \
|
||||
M_GET_CLEAR oplist (*shared); \
|
||||
M_IF_DISABLED_METHOD(DEL, oplist)(, M_GET_DEL oplist (shared);) \
|
||||
M_CALL_CLEAR(oplist, *shared); \
|
||||
M_IF_DISABLED_METHOD(DEL, oplist)(, M_CALL_DEL(oplist, shared);) \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _clean)(type **shared) \
|
||||
M_C(name, _clean)(M_C(name,_t) *shared) \
|
||||
{ \
|
||||
M_C(name, _clear)(*shared); \
|
||||
*shared = NULL; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _set)(type ** ptr, type *shared) \
|
||||
M_C(name, _set)(M_C(name,_t) *ptr, M_C(name,_t)shared) \
|
||||
{ \
|
||||
assert (ptr != NULL); \
|
||||
if (M_LIKELY (*ptr != shared)) { \
|
||||
|
||||
+16
-12
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - MEMPOOL module
|
||||
*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -22,14 +22,14 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __M_MEMPOOL_H
|
||||
#define __M_MEMPOOL_H
|
||||
#ifndef MSTARLIB_MEMPOOL_H
|
||||
#define MSTARLIB_MEMPOOL_H
|
||||
|
||||
#include "m-core.h"
|
||||
|
||||
/* Fast Fixed Size thread unsafe allocator.
|
||||
/* Fast Fixed Size thread unsafe allocator based on memory region.
|
||||
USAGE:
|
||||
MEMPOOL_DEF(uint, unsigned int)
|
||||
MEMPOOL_DEF(memppol_uint, unsigned int)
|
||||
...
|
||||
memppol_uint_t m;
|
||||
mempool_uint_init(m);
|
||||
@@ -50,7 +50,7 @@
|
||||
typedef struct M_C(name,_segment_s) { \
|
||||
unsigned int count; \
|
||||
struct M_C(name,_segment_s) *next; \
|
||||
M_C(name,_union_t) tab[MEMPOOLI_MAX_PER_SEGMENT(type)]; \
|
||||
M_C(name,_union_t) tab[MEMPOOL_MAX_PER_SEGMENT(type)]; \
|
||||
} M_C(name,_segment_t); \
|
||||
\
|
||||
typedef struct M_C(name, _s) { \
|
||||
@@ -99,7 +99,7 @@
|
||||
M_C(name,_segment_t) *segment = mem->current_segment; \
|
||||
assert(segment != NULL); \
|
||||
unsigned int count = segment->count; \
|
||||
if (M_UNLIKELY (count >= MEMPOOLI_MAX_PER_SEGMENT(type))) { \
|
||||
if (M_UNLIKELY (count >= MEMPOOL_MAX_PER_SEGMENT(type))) { \
|
||||
M_C(name,_segment_t) *new_segment = M_MEMORY_ALLOC (M_C(name,_segment_t)); \
|
||||
if (M_UNLIKELY (new_segment == NULL)) { \
|
||||
M_MEMORY_FULL(sizeof (M_C(name,_segment_t))); \
|
||||
@@ -129,17 +129,21 @@
|
||||
MEMPOOLI_CONTRACT(mem, type); \
|
||||
} \
|
||||
|
||||
/* User shall be able to cutomize the size of the segment and/or
|
||||
the minimun number of elements.
|
||||
*/
|
||||
#ifndef MEMPOOL_MAX_PER_SEGMENT
|
||||
#define MEMPOOL_MAX_PER_SEGMENT(type) \
|
||||
M_MAX((16*1024-sizeof(unsigned int) - 2*sizeof(void*)) / sizeof (type), 256U)
|
||||
#endif
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
|
||||
// TODO: user shall be able to cutomize the size of the segment and/or
|
||||
// the minimun number of elements... i.e. all the formula!
|
||||
#define MEMPOOLI_MAX_PER_SEGMENT(type) \
|
||||
M_MAX((16*1024-sizeof(unsigned int) - 2*sizeof(void*)) / sizeof (type), 256U)
|
||||
|
||||
#define MEMPOOLI_CONTRACT(mempool, type) do { \
|
||||
assert((mempool) != NULL); \
|
||||
assert((mempool)->current_segment != NULL); \
|
||||
assert((mempool)->current_segment->count <= MEMPOOLI_MAX_PER_SEGMENT(type)); \
|
||||
assert((mempool)->current_segment->count <= MEMPOOL_MAX_PER_SEGMENT(type)); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - Thin Mutex & Thread wrapper
|
||||
*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -22,12 +22,8 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __M_MUTEX_H
|
||||
#define __M_MUTEX_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include "m-core.h"
|
||||
#ifndef MSTARLIB_MUTEX_H
|
||||
#define MSTARLIB_MUTEX_H
|
||||
|
||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L \
|
||||
&& !defined(__STDC_NO_THREADS__)
|
||||
@@ -35,6 +31,9 @@
|
||||
/****************************** C11 version ********************************/
|
||||
|
||||
#include <threads.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include "m-core.h"
|
||||
|
||||
typedef mtx_t m_mutex_t[1];
|
||||
typedef cnd_t m_cond_t[1];
|
||||
@@ -72,11 +71,16 @@ static inline void m_cond_clear(m_cond_t c)
|
||||
cnd_destroy(c);
|
||||
}
|
||||
|
||||
static inline void m_cond_clear(m_cond_t c)
|
||||
static inline void m_cond_signal(m_cond_t c)
|
||||
{
|
||||
cnd_signal(c);
|
||||
}
|
||||
|
||||
static inline void m_cond_broadcast(m_cond_t c)
|
||||
{
|
||||
cnd_broadcast(c);
|
||||
}
|
||||
|
||||
static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
|
||||
{
|
||||
cnd_wait(c, m);
|
||||
@@ -84,16 +88,30 @@ static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
|
||||
|
||||
static inline void m_thread_create(m_thread_t t, void (*func)(void*), void* arg)
|
||||
{
|
||||
int rc = thrd_create(t, ((int)(*)(void*))func, arg);
|
||||
int rc = thrd_create(t, (int(*)(void*))(void(*)(void))func, arg);
|
||||
M_ASSERT_INIT (rc == thrd_success);
|
||||
}
|
||||
|
||||
static inline void m_thread_join(m_thread_t t)
|
||||
{
|
||||
int rc = thrd_join(t, NULL);
|
||||
int rc = thrd_join(*t, NULL);
|
||||
assert (rc == thrd_success);
|
||||
}
|
||||
|
||||
static inline void m_thread_yield(void)
|
||||
{
|
||||
thrd_yield();
|
||||
}
|
||||
|
||||
static inline bool m_thread_sleep(unsigned long long usec)
|
||||
{
|
||||
struct timespec tv;
|
||||
tv.tv_sec = usec / 1000000ULL;
|
||||
tv.tv_nsec = (usec % 1000000ULL) * 1000UL;
|
||||
int retval = thrd_sleep(&tv, NULL);
|
||||
return retval == 0;
|
||||
}
|
||||
|
||||
// Internal type, not exported.
|
||||
typedef once_flag m_oncei_t[1];
|
||||
#define M_ONCEI_INIT_VALUE { ONCE_FLAG_INIT }
|
||||
@@ -104,77 +122,70 @@ static inline void m_oncei_call(m_oncei_t o, void (*func)(void))
|
||||
|
||||
|
||||
|
||||
|
||||
#elif defined(WIN32) || defined(_WIN32)
|
||||
// MSYS2 doesn't define _WIN32 by default.
|
||||
#elif defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__)
|
||||
|
||||
/****************************** WIN32 version ******************************/
|
||||
|
||||
/* CriticalSection & ConditionVariable are available from Windows Vista */
|
||||
#ifndef WINVER
|
||||
#define WINVER _WIN32_WINNT_VISTA
|
||||
#endif
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT _WIN32_WINNT_VISTA
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include "m-core.h"
|
||||
|
||||
typedef HANDLE m_mutex_t[1];
|
||||
typedef HANDLE m_cond_t[1];
|
||||
typedef HANDLE m_thread_t[1];
|
||||
typedef CRITICAL_SECTION m_mutex_t[1];
|
||||
typedef CONDITION_VARIABLE m_cond_t[1];
|
||||
|
||||
static inline void m_mutex_init(m_mutex_t m)
|
||||
{
|
||||
*m = CreateMutex(NULL, FALSE, NULL);
|
||||
M_ASSERT_INIT(*m != NULL);
|
||||
InitializeCriticalSection(m);
|
||||
}
|
||||
|
||||
static inline void m_mutex_clear(m_mutex_t m)
|
||||
{
|
||||
CloseHandle(*m);
|
||||
DeleteCriticalSection(m);
|
||||
}
|
||||
|
||||
static inline void m_mutex_lock(m_mutex_t m)
|
||||
{
|
||||
DWORD dwWaitResult = WaitForSingleObject(*m, INFINITE);
|
||||
assert (dwWaitResult == WAIT_OBJECT_0);
|
||||
EnterCriticalSection(m);
|
||||
}
|
||||
|
||||
static inline void m_mutex_unlock(m_mutex_t m)
|
||||
{
|
||||
ReleaseMutex(*m);
|
||||
}
|
||||
|
||||
// Internal function, not exported
|
||||
#define M_MUTEXI_INIT_VALUE { NULL }
|
||||
static inline void m_mutexi_lazy_lock (m_mutex_t m)
|
||||
{
|
||||
HANDLE *addr = m;
|
||||
if (*addr == NULL) {
|
||||
/* Try to create one, affect it atomicaly and otherwise clears it */
|
||||
HANDLE h = CreateMutex(NULL, FALSE, NULL);
|
||||
if (InterlockedCompareExchangePointer((PVOID*)addr, (PVOID)h, NULL) != NULL)
|
||||
CloseHandle(h);
|
||||
/* FIXME: there is no way to clean the mutex at program exit... */
|
||||
}
|
||||
DWORD dwWaitResult = WaitForSingleObject(*addr, INFINITE);
|
||||
assert (dwWaitResult == WAIT_OBJECT_0);
|
||||
LeaveCriticalSection(m);
|
||||
}
|
||||
|
||||
static inline void m_cond_init(m_cond_t c)
|
||||
{
|
||||
*c = CreateEvent (NULL, FALSE, FALSE, NULL);
|
||||
M_ASSERT_INIT (*c != NULL);
|
||||
InitializeConditionVariable(c);
|
||||
}
|
||||
|
||||
static inline void m_cond_clear(m_cond_t c)
|
||||
{
|
||||
CloseHandle(*c);
|
||||
(void) c; // There is no destructor for this object.
|
||||
}
|
||||
|
||||
static inline void m_cond_signal(m_cond_t c)
|
||||
{
|
||||
PulseEvent(*c);
|
||||
WakeConditionVariable(c);
|
||||
}
|
||||
|
||||
static inline void m_cond_broadcast(m_cond_t c)
|
||||
{
|
||||
WakeAllConditionVariable(c);
|
||||
}
|
||||
|
||||
static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
|
||||
{
|
||||
DWORD dwWaitResult = SignalObjectAndWait(*m, *c, INFINITE,FALSE);
|
||||
assert (dwWaitResult == WAIT_OBJECT_0);
|
||||
dwWaitResult = WaitForSingleObject(*m, INFINITE);
|
||||
assert (dwWaitResult == WAIT_OBJECT_0);
|
||||
SleepConditionVariableCS(c, m, INFINITE);
|
||||
}
|
||||
|
||||
static inline void m_thread_create(m_thread_t t, void (*func)(void*), void *arg)
|
||||
@@ -186,17 +197,60 @@ static inline void m_thread_create(m_thread_t t, void (*func)(void*), void *arg)
|
||||
static inline void m_thread_join(m_thread_t t)
|
||||
{
|
||||
DWORD dwWaitResult = WaitForSingleObject(*t, INFINITE);
|
||||
(void) dwWaitResult;
|
||||
assert (dwWaitResult == WAIT_OBJECT_0);
|
||||
CloseHandle(*t);
|
||||
}
|
||||
|
||||
static inline void m_thread_yield(void)
|
||||
{
|
||||
Sleep(0);
|
||||
}
|
||||
|
||||
static inline bool m_thread_sleep(unsigned long long usec)
|
||||
{
|
||||
LARGE_INTEGER ft;
|
||||
ft.QuadPart = -(10ULL*usec);
|
||||
HANDLE hd = CreateWaitableTimer(NULL, TRUE, NULL);
|
||||
M_ASSERT_INIT (hd != NULL);
|
||||
SetWaitableTimer(hd, &ft, 0, NULL, NULL, 0);
|
||||
DWORD dwWaitResult = WaitForSingleObject(hd, INFINITE);
|
||||
CloseHandle(hd);
|
||||
return dwWaitResult == WAIT_OBJECT_0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Internal type, not exported.
|
||||
typedef INIT_ONCE m_oncei_t[1];
|
||||
#define M_ONCEI_INIT_VALUE { INIT_ONCE_STATIC_INIT }
|
||||
static inline BOOL CALLBACK m_oncei_callback( PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContext)
|
||||
{
|
||||
void (*func)(void);
|
||||
(void) InitOnce;
|
||||
(void) lpContext;
|
||||
func = (void (*)(void))(uintptr_t) Parameter;
|
||||
(*func)();
|
||||
return TRUE;
|
||||
}
|
||||
static inline void m_oncei_call(m_oncei_t o, void (*func)(void))
|
||||
{
|
||||
InitOnceExecuteOnce(o, m_oncei_callback, (void*)(intptr_t)func, NULL);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/**************************** PTHREAD version ******************************/
|
||||
|
||||
#include <pthread.h>
|
||||
#ifdef _POSIX_PRIORITY_SCHEDULING
|
||||
#include <sched.h>
|
||||
#endif
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include "m-core.h"
|
||||
|
||||
typedef pthread_mutex_t m_mutex_t[1];
|
||||
typedef pthread_cond_t m_cond_t[1];
|
||||
@@ -245,6 +299,11 @@ static inline void m_cond_signal(m_cond_t c)
|
||||
pthread_cond_signal(c);
|
||||
}
|
||||
|
||||
static inline void m_cond_broadcast(m_cond_t c)
|
||||
{
|
||||
pthread_cond_broadcast(c);
|
||||
}
|
||||
|
||||
static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
|
||||
{
|
||||
pthread_cond_wait(c, m);
|
||||
@@ -252,7 +311,7 @@ static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
|
||||
|
||||
static inline void m_thread_create(m_thread_t t, void (*func)(void*), void *arg)
|
||||
{
|
||||
int _rc = pthread_create(t, NULL, (void*(*)(void*))func, arg);
|
||||
int _rc = pthread_create(t, NULL, (void*(*)(void*))(void(*)(void))func, arg);
|
||||
M_ASSERT_INIT (_rc == 0);
|
||||
}
|
||||
|
||||
@@ -263,6 +322,24 @@ static inline void m_thread_join(m_thread_t t)
|
||||
assert (_rc == 0);
|
||||
}
|
||||
|
||||
static inline void m_thread_yield(void)
|
||||
{
|
||||
#ifdef _POSIX_PRIORITY_SCHEDULING
|
||||
sched_yield();
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool m_thread_sleep(unsigned long long usec)
|
||||
{
|
||||
struct timeval tv;
|
||||
/* We don't want to use usleep or nanosleep so that
|
||||
we remain compatible with strict C99 build */
|
||||
tv.tv_sec = usec / 1000000ULL;
|
||||
tv.tv_usec = usec % 1000000ULL;
|
||||
int retval = select(1, NULL, NULL, NULL, &tv);
|
||||
return retval == 0;
|
||||
}
|
||||
|
||||
// Internal type, not exported.
|
||||
typedef pthread_once_t m_oncei_t[1];
|
||||
#define M_ONCEI_INIT_VALUE { PTHREAD_ONCE_INIT }
|
||||
|
||||
+153
-17
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - dynamic priority queue module
|
||||
*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -22,26 +22,26 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __M_PRIOQUEUE_H
|
||||
#define __M_PRIOQUEUE_H
|
||||
#ifndef MSTARLIB_PRIOQUEUE_H
|
||||
#define MSTARLIB_PRIOQUEUE_H
|
||||
|
||||
#include "m-core.h"
|
||||
#include "m-array.h"
|
||||
|
||||
/* Priority queue based on binary heap implementation */
|
||||
|
||||
/* Define an prioqueue of a given type.
|
||||
/* Define a prioqueue of a given type and its associated functions.
|
||||
USAGE: PRIOQUEUE_DEF(name, type [, oplist_of_the_type]) */
|
||||
#define PRIOQUEUE_DEF(name, ...) \
|
||||
PRIOQUEUEI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__), M_C(name,_t) ), \
|
||||
(name, __VA_ARGS__, M_C(name,_t))))
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_C(name,_t), M_C(name,_it_t) ), \
|
||||
(name, __VA_ARGS__, M_C(name,_t), M_C(name,_it_t))))
|
||||
|
||||
/* Define the oplist of an prioqueue of type.
|
||||
/* Define the oplist of a prioqueue of type.
|
||||
USAGE: PRIOQUEUE_OPLIST(name[, oplist of the type]) */
|
||||
#define PRIOQUEUE_OPLIST(...) \
|
||||
PRIOQUEUEI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__) ), \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#define PRIOQUEUEI_OPLIST2(name, oplist) \
|
||||
(INIT(M_C(name, _init)) \
|
||||
,INIT_SET(M_C(name, _init_set)) \
|
||||
,INIT_WITH(API_1(M_INIT_VAI)) \
|
||||
,SET(M_C(name, _set)) \
|
||||
,CLEAR(M_C(name, _clear)) \
|
||||
,INIT_MOVE(M_C(name, _init_move)) \
|
||||
@@ -66,7 +67,7 @@
|
||||
,OPLIST(oplist) \
|
||||
)
|
||||
|
||||
#define PRIOQUEUEI_DEF2(name, type, oplist, prioqueue_t) \
|
||||
#define PRIOQUEUEI_DEF2(name, type, oplist, prioqueue_t, it_t) \
|
||||
ARRAY_DEF(M_C(name, _array), type, oplist) \
|
||||
\
|
||||
typedef type M_C(name, _type_t); \
|
||||
@@ -74,6 +75,10 @@
|
||||
typedef struct M_C(name, _s) { \
|
||||
M_C(name, _array_t) array; \
|
||||
} prioqueue_t[1]; \
|
||||
typedef struct M_C(name, _s) *M_C(name, _ptr); \
|
||||
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
|
||||
\
|
||||
typedef M_C(name, _array_it_t) it_t; \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _init)(prioqueue_t p) \
|
||||
@@ -145,20 +150,20 @@
|
||||
} \
|
||||
\
|
||||
static inline int \
|
||||
M_C(name, _i_cmp)(prioqueue_t p, size_t i, size_t j) \
|
||||
M_C(name, _i_cmp)(const prioqueue_t p, size_t i, size_t j) \
|
||||
{ \
|
||||
return M_GET_CMP oplist (*M_C(name, _array_get)(p->array, i), \
|
||||
*M_C(name, _array_get)(p->array, j)); \
|
||||
return M_CALL_CMP(oplist, *M_C(name, _array_cget)(p->array, i), \
|
||||
*M_C(name, _array_cget)(p->array, j)); \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _empty_p)(prioqueue_t p) \
|
||||
M_C(name, _empty_p)(const prioqueue_t p) \
|
||||
{ \
|
||||
return M_C(name, _array_empty_p)(p->array); \
|
||||
} \
|
||||
\
|
||||
static inline size_t \
|
||||
M_C(name, _size)(prioqueue_t p) \
|
||||
M_C(name, _size)(const prioqueue_t p) \
|
||||
{ \
|
||||
return M_C(name, _array_size)(p->array); \
|
||||
} \
|
||||
@@ -178,10 +183,10 @@
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static inline type * \
|
||||
static inline type const * \
|
||||
M_C(name, _front)(prioqueue_t p) \
|
||||
{ \
|
||||
return M_C(name, _array_get)(p->array, 0); \
|
||||
return M_C(name, _array_cget)(p->array, 0); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
@@ -205,6 +210,137 @@
|
||||
i = child; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it)(it_t it, const prioqueue_t v) \
|
||||
{ \
|
||||
M_C(name, _array_it)(it, v->array); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it_last)(it_t it, const prioqueue_t v) \
|
||||
{ \
|
||||
M_C(name, _array_it_last)(it, v->array); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it_end)(it_t it, const prioqueue_t v) \
|
||||
{ \
|
||||
M_C(name, _array_it_end)(it, v->array); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it_set)(it_t it, const it_t org) \
|
||||
{ \
|
||||
M_C(name, _array_it_set)(it, org); \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _end_p)(const it_t it) \
|
||||
{ \
|
||||
return M_C(name, _array_end_p)(it); \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _last_p)(const it_t it) \
|
||||
{ \
|
||||
return M_C(name, _array_last_p)(it); \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _it_equal_p)(const it_t it1, \
|
||||
const it_t it2) \
|
||||
{ \
|
||||
return M_C(name, _array_it_equal_p)(it1, it2); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _next)(it_t it) \
|
||||
{ \
|
||||
M_C(name, _array_next)(it); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _previous)(it_t it) \
|
||||
{ \
|
||||
M_C(name, _array_previous)(it); \
|
||||
} \
|
||||
\
|
||||
static inline type const * \
|
||||
M_C(name, _cref)(const it_t it) \
|
||||
{ \
|
||||
return M_C(name, _array_cref)(it); \
|
||||
} \
|
||||
\
|
||||
M_IF_METHOD(EQUAL, oplist) \
|
||||
( \
|
||||
/* EQUAL & CMP may be uncorrelated */ \
|
||||
static inline bool \
|
||||
M_C(name, _erase)(prioqueue_t p, type x) \
|
||||
{ \
|
||||
/* First pass: search for an item EQUAL to x */ \
|
||||
size_t size = M_C(name, _array_size)(p->array); \
|
||||
size_t i = 0; \
|
||||
for(i = 0; i < size; i++) { \
|
||||
if (M_CALL_EQUAL(oplist, *M_C(name, _array_cget)(p->array, i), x)) \
|
||||
break; \
|
||||
} \
|
||||
/* If x is not found, then stop */ \
|
||||
if (i >= size) \
|
||||
return false; \
|
||||
/* Swap the found item and the last element */ \
|
||||
size--; \
|
||||
M_C(name, _array_swap_at) (p->array, i, size); \
|
||||
M_C(name, _array_pop_back)(NULL, p->array); \
|
||||
/* Move back the last swapped element to its right position in the heap */ \
|
||||
while (true) { \
|
||||
size_t child = M_C(name, _i_lchild)(i); \
|
||||
if (child >= size) break; \
|
||||
size_t otherChild = M_C(name, _i_rchild)(i); \
|
||||
if (otherChild < size \
|
||||
&& M_C(name, _i_cmp)(p, otherChild, child) < 0 ) { \
|
||||
child = otherChild; \
|
||||
} \
|
||||
if (M_C(name, _i_cmp)(p, i, child) <= 0) break; \
|
||||
M_C(name, _array_swap_at) (p->array, i, child); \
|
||||
i = child; \
|
||||
} \
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _update_further)(prioqueue_t p, type xold, type xnew) \
|
||||
{ \
|
||||
/* This update assumes that the new position is further in the heap */ \
|
||||
assert (M_CALL_CMP(oplist, xold, xnew) > 0); \
|
||||
/* First pass: search for an item EQUAL to x */ \
|
||||
size_t size = M_C(name, _array_size)(p->array); \
|
||||
size_t i = 0; \
|
||||
for(i = 0; i < size; i++) { \
|
||||
if (M_CALL_EQUAL(oplist, *M_C(name, _array_cget)(p->array, i), xold)) \
|
||||
break; \
|
||||
} \
|
||||
/* We shall have found the item */ \
|
||||
assert (i < size); \
|
||||
/* Set the found item to the new element */ \
|
||||
M_C(name, _array_set_at) (p->array, i, xnew); \
|
||||
/* Move back the updated element to its right position in the heap */ \
|
||||
while (true) { \
|
||||
size_t child = M_C(name, _i_lchild)(i); \
|
||||
if (child >= size) break; \
|
||||
size_t otherChild = M_C(name, _i_rchild)(i); \
|
||||
if (otherChild < size \
|
||||
&& M_C(name, _i_cmp)(p, otherChild, child) < 0 ) { \
|
||||
child = otherChild; \
|
||||
} \
|
||||
if (M_C(name, _i_cmp)(p, i, child) <= 0) break; \
|
||||
M_C(name, _array_swap_at) (p->array, i, child); \
|
||||
i = child; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
, /* No EQUAL */ \) \
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
+105
-84
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - RED BLACK TREE module
|
||||
*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -22,8 +22,8 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __M_RBTREE_H
|
||||
#define __M_RBTREE_H
|
||||
#ifndef MSTARLIB_RBTREE_H
|
||||
#define MSTARLIB_RBTREE_H
|
||||
|
||||
#include "m-core.h"
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
USAGE: RBTREE_DEF(name, type [, oplist_of_the_type]) */
|
||||
#define RBTREE_DEF(name, ...) \
|
||||
RBTREEI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__), \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), \
|
||||
M_C(name, _t), struct M_C(name, _node_s), M_C(name, _it_t)), \
|
||||
(name, __VA_ARGS__ , \
|
||||
M_C(name, _t), struct M_C(name, _node_s), M_C(name, _it_t))))
|
||||
@@ -40,7 +40,7 @@
|
||||
USAGE: RBTREE_OPLIST(name [, oplist_of_the_type]) */
|
||||
#define RBTREE_OPLIST(...) \
|
||||
RBTREEI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__) ), \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
#define RBTREEI_OPLIST2(name, oplist) \
|
||||
(INIT(M_C(name, _init)), \
|
||||
INIT_SET(M_C(name, _init_set)), \
|
||||
INIT_WITH(API_1(M_INIT_VAI)), \
|
||||
SET(M_C(name, _set)), \
|
||||
CLEAR(M_C(name, _clear)), \
|
||||
INIT_MOVE(M_C(name, _init_move)), \
|
||||
@@ -59,6 +60,7 @@
|
||||
SWAP(M_C(name, _swap)), \
|
||||
TYPE(M_C(name,_t)), \
|
||||
SUBTYPE(M_C(name, _type_t)), \
|
||||
TEST_EMPTY(M_C(name,_empty_p)), \
|
||||
IT_TYPE(M_C(name, _it_t)), \
|
||||
IT_FIRST(M_C(name,_it)), \
|
||||
IT_SET(M_C(name,_it_set)), \
|
||||
@@ -72,10 +74,10 @@
|
||||
IT_CREF(M_C(name,_cref)), \
|
||||
CLEAN(M_C(name,_clean)), \
|
||||
PUSH(M_C(name,_push)), \
|
||||
POP(M_C(name,_pop)) \
|
||||
GET_MIN(M_C(name,_min)), \
|
||||
GET_MAX(M_C(name,_max)), \
|
||||
M_IF_METHOD(GET_STR, oplist)(GET_STR(M_C(name, _get_str)),), \
|
||||
M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_C(name, _parse_str)),), \
|
||||
M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_C(name, _out_str)),), \
|
||||
M_IF_METHOD(IN_STR, oplist)(IN_STR(M_C(name, _in_str)),), \
|
||||
M_IF_METHOD(EQUAL, oplist)(EQUAL(M_C(name, _equal_p)),), \
|
||||
@@ -88,7 +90,7 @@
|
||||
/* Max depth of the binary tree
|
||||
It is at worst twice the depth of a perfectly even tree with maximum elements.
|
||||
The maximum number of elements is the max of size_t.
|
||||
A perfectly even tree is therefore log2(max(size_t))=CHAR_BIT*sizeof(size_t)
|
||||
A perfectly even tree is of depth log2(max(size_t))=CHAR_BIT*sizeof(size_t)
|
||||
*/
|
||||
#define RBTREEI_MAX_STACK (2*CHAR_BIT*sizeof (size_t))
|
||||
|
||||
@@ -123,6 +125,8 @@ typedef enum {
|
||||
&& ((node)->child[1] == NULL || RBTREEI_IS_BLACK(node->child[1])))); \
|
||||
} while (0)
|
||||
|
||||
//TODO: UPDATE shall use a separate method than push
|
||||
|
||||
// deferred evaluation
|
||||
#define RBTREEI_DEF(arg) RBTREEI_DEF2 arg
|
||||
|
||||
@@ -138,6 +142,8 @@ typedef enum {
|
||||
size_t size; \
|
||||
node_t *node; \
|
||||
} tree_t[1]; \
|
||||
typedef struct M_C(name, _s) *M_C(name, _ptr); \
|
||||
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
|
||||
\
|
||||
typedef struct M_C(name, _it_s) { \
|
||||
node_t *stack[RBTREEI_MAX_STACK]; \
|
||||
@@ -160,10 +166,10 @@ typedef enum {
|
||||
, /* No mempool allocation */ \
|
||||
\
|
||||
static inline node_t *M_C(name,_int_new)(void) { \
|
||||
return M_GET_NEW oplist (node_t); \
|
||||
return M_CALL_NEW(oplist, node_t); \
|
||||
} \
|
||||
static inline void M_C(name,_int_del)(node_t *ptr) { \
|
||||
M_GET_DEL oplist (ptr); \
|
||||
M_CALL_DEL(oplist, ptr); \
|
||||
} ) \
|
||||
\
|
||||
RBTREEI_DEF3(name, type, oplist, tree_t, node_t, tree_it_t)
|
||||
@@ -205,7 +211,7 @@ typedef enum {
|
||||
break; \
|
||||
} \
|
||||
assert (n == stack[cpt - 1]); \
|
||||
M_GET_CLEAR oplist (n->data); \
|
||||
M_CALL_CLEAR(oplist, n->data); \
|
||||
M_C(name,_int_del) (n); \
|
||||
assert((stack[cpt-1] = NULL) == NULL); \
|
||||
cpt--; \
|
||||
@@ -235,7 +241,7 @@ typedef enum {
|
||||
M_MEMORY_FULL(sizeof (node_t)); \
|
||||
return; \
|
||||
} \
|
||||
M_GET_INIT_SET oplist (n->data, data); \
|
||||
M_CALL_INIT_SET(oplist, n->data, data); \
|
||||
n->child[0] = n->child[1] = NULL; \
|
||||
RBTREEI_SET_BLACK (n); \
|
||||
tree->node = n; \
|
||||
@@ -248,7 +254,7 @@ typedef enum {
|
||||
tab[cpt] = n; \
|
||||
while (n != NULL) { \
|
||||
RBTREEI_CONTRACT_NODE(n); \
|
||||
int cmp = M_GET_CMP oplist (n->data, data); \
|
||||
int cmp = M_CALL_CMP(oplist, n->data, data); \
|
||||
if (cmp == 0) { \
|
||||
break; \
|
||||
} else if (cmp > 0) { \
|
||||
@@ -263,8 +269,8 @@ typedef enum {
|
||||
} \
|
||||
/* If found, update the data (default is set) */ \
|
||||
if (n != NULL) { \
|
||||
M_IF_METHOD (UPDATE, oplist)(M_GET_UPDATE oplist, M_GET_SET oplist) \
|
||||
(n->data, data); \
|
||||
M_IF_METHOD (UPDATE, oplist)(M_CALL_UPDATE(oplist, n->data, data) \
|
||||
, M_CALL_SET(oplist, n->data, data)); \
|
||||
RBTREEI_CONTRACT (tree); \
|
||||
return; \
|
||||
} \
|
||||
@@ -274,7 +280,7 @@ typedef enum {
|
||||
M_MEMORY_FULL (sizeof (node_t)); \
|
||||
return; \
|
||||
} \
|
||||
M_GET_INIT_SET oplist (n->data, data); \
|
||||
M_CALL_INIT_SET(oplist, n->data, data); \
|
||||
n->child[0] = n->child[1] = NULL; \
|
||||
RBTREEI_SET_RED (n); \
|
||||
assert (tab[cpt] == NULL); \
|
||||
@@ -303,50 +309,33 @@ typedef enum {
|
||||
node_t *pp = tab[cpt-2]; \
|
||||
node_t *p = tab[cpt-1]; \
|
||||
node_t *x = tab[cpt]; \
|
||||
int i = which[cpt-2]; \
|
||||
int j = 1 - i; \
|
||||
assert (i == 0 || i == 1); \
|
||||
/* We need to do some rotations */ \
|
||||
if (which[cpt-2] == 0) { \
|
||||
/* The parent is the left child of the grand-parent */ \
|
||||
if (which[cpt-1] == 0) { \
|
||||
/* The child is the left child of its parent */ \
|
||||
/* Right rotation: cpt is the new grand-parent. \
|
||||
x is its left child, the grand-parent is the right one */ \
|
||||
pp->child[0] = p->child[1]; \
|
||||
p->child[0] = x; \
|
||||
p->child[1] = pp; \
|
||||
RBTREEI_SET_BLACK(p); \
|
||||
RBTREEI_SET_RED(pp); \
|
||||
} else { \
|
||||
/* The child is the right child of its parent */ \
|
||||
/* Left rotation */ \
|
||||
pp->child[0] = x->child[1]; \
|
||||
p->child[1] = x->child[0]; \
|
||||
x->child[0] = p; \
|
||||
x->child[1] = pp; \
|
||||
RBTREEI_SET_BLACK(x); \
|
||||
RBTREEI_SET_RED(p); \
|
||||
RBTREEI_SET_RED(pp); \
|
||||
p = x; \
|
||||
} \
|
||||
if (i == which[cpt-1]) { \
|
||||
/* The child is the left child of its parent */ \
|
||||
/* OR The child is the right child of its parent */ \
|
||||
/* Right rotation: cpt is the new grand-parent. \
|
||||
x is its left child, the grand-parent is the right one */ \
|
||||
pp->child[i] = p->child[j]; \
|
||||
p->child[i] = x; \
|
||||
p->child[j] = pp; \
|
||||
RBTREEI_SET_BLACK(p); \
|
||||
RBTREEI_SET_RED(pp); \
|
||||
} else { \
|
||||
/* The parent is the right child of the grand-parent */ \
|
||||
if (which[cpt-1] == 0) { \
|
||||
/* The child is the left child of its parent */ \
|
||||
pp->child[1] = x->child[0]; \
|
||||
p->child[0] = x->child[1]; \
|
||||
x->child[1] = p; \
|
||||
x->child[0] = pp; \
|
||||
RBTREEI_SET_BLACK(x); \
|
||||
RBTREEI_SET_RED(p); \
|
||||
RBTREEI_SET_RED(pp); \
|
||||
p = x; \
|
||||
} else { \
|
||||
/* The child is the right child of its parent */ \
|
||||
pp->child[1] = p->child[0]; \
|
||||
p->child[1] = x; \
|
||||
p->child[0] = pp; \
|
||||
RBTREEI_SET_BLACK(p); \
|
||||
RBTREEI_SET_RED(pp); \
|
||||
} \
|
||||
assert (j == which[cpt-1]); \
|
||||
/* The child is the right child of its parent */ \
|
||||
/* OR The child is the left child of its parent */ \
|
||||
/* Left rotation */ \
|
||||
pp->child[i] = x->child[j]; \
|
||||
p->child[j] = x->child[i]; \
|
||||
x->child[i] = p; \
|
||||
x->child[j] = pp; \
|
||||
RBTREEI_SET_BLACK(x); \
|
||||
RBTREEI_SET_RED(p); \
|
||||
RBTREEI_SET_RED(pp); \
|
||||
p = x; \
|
||||
} \
|
||||
/* Insert the new grand parent */ \
|
||||
if (cpt == 2) { \
|
||||
@@ -472,7 +461,7 @@ typedef enum {
|
||||
return &(it->stack[it->cpt-1]->data); \
|
||||
} \
|
||||
\
|
||||
static inline const type * \
|
||||
static inline type const * \
|
||||
M_C(name, _cref)(const tree_it_t it) \
|
||||
{ \
|
||||
return M_CONST_CAST(type, M_C(name, _ref)(it)); \
|
||||
@@ -497,7 +486,7 @@ typedef enum {
|
||||
node_t *n = tree->node; \
|
||||
it->stack[cpt] = n; \
|
||||
while (n != NULL) { \
|
||||
int cmp = M_GET_CMP oplist (n->data, data); \
|
||||
int cmp = M_CALL_CMP(oplist, n->data, data); \
|
||||
if (cmp == 0) \
|
||||
break; \
|
||||
int child = (cmp < 0); \
|
||||
@@ -516,7 +505,7 @@ typedef enum {
|
||||
if (it->cpt == 0) return true; \
|
||||
assert (it->cpt > 0 && it->cpt < RBTREEI_MAX_STACK); \
|
||||
node_t *n = it->stack[it->cpt-1]; \
|
||||
int cmp = M_GET_CMP oplist (n->data, data); \
|
||||
int cmp = M_CALL_CMP(oplist, n->data, data); \
|
||||
return (cmp >= 0); \
|
||||
} \
|
||||
\
|
||||
@@ -546,13 +535,13 @@ typedef enum {
|
||||
return &n->data; \
|
||||
} \
|
||||
\
|
||||
static inline const type * \
|
||||
static inline type const * \
|
||||
M_C(name, _cmin)(const tree_t tree) \
|
||||
{ \
|
||||
return M_CONST_CAST(type, M_C(name, _min)(tree)); \
|
||||
} \
|
||||
\
|
||||
static inline const type * \
|
||||
static inline type const * \
|
||||
M_C(name, _cmax)(const tree_t tree) \
|
||||
{ \
|
||||
return M_CONST_CAST(type, M_C(name, _max)(tree)); \
|
||||
@@ -565,7 +554,7 @@ typedef enum {
|
||||
node_t *n = tree->node; \
|
||||
while (n != NULL) { \
|
||||
RBTREEI_CONTRACT_NODE (n); \
|
||||
int cmp = M_GET_CMP oplist (n->data, data); \
|
||||
int cmp = M_CALL_CMP(oplist, n->data, data); \
|
||||
if (cmp == 0) { \
|
||||
return &n->data; \
|
||||
} else if (cmp > 0) { \
|
||||
@@ -577,7 +566,7 @@ typedef enum {
|
||||
return NULL; \
|
||||
} \
|
||||
\
|
||||
static inline const type * \
|
||||
static inline type const * \
|
||||
M_C(name, _cget)(const tree_t tree, type const data) \
|
||||
{ \
|
||||
return M_CONST_CAST(type, M_C(name, _get)(tree, data)); \
|
||||
@@ -592,7 +581,7 @@ typedef enum {
|
||||
M_MEMORY_FULL (sizeof (node_t)); \
|
||||
return NULL; \
|
||||
} \
|
||||
M_GET_INIT_SET oplist (n->data, o->data); \
|
||||
M_CALL_INIT_SET(oplist, n->data, o->data); \
|
||||
n->child[0] = M_C(name, _int_copyn)(o->child[0]); \
|
||||
n->child[1] = M_C(name, _int_copyn)(o->child[1]); \
|
||||
RBTREEI_COPY_COLOR (n, o); \
|
||||
@@ -699,7 +688,7 @@ typedef enum {
|
||||
) \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _pop)(type *data_ptr, tree_t tree, type const key) \
|
||||
M_C(name, _pop_at)(type *data_ptr, tree_t tree, type const key) \
|
||||
{ \
|
||||
RBTREEI_CONTRACT (tree); \
|
||||
node_t *tab[RBTREEI_MAX_STACK]; \
|
||||
@@ -714,7 +703,7 @@ typedef enum {
|
||||
RBTREEI_CONTRACT_NODE (n); \
|
||||
assert(M_C(name, _int_depth)(n->child[0]) \
|
||||
== M_C(name, _int_depth)(n->child[1])); \
|
||||
int cmp = M_GET_CMP oplist (n->data, key); \
|
||||
int cmp = M_CALL_CMP(oplist, n->data, key); \
|
||||
if (cmp == 0) { \
|
||||
break; \
|
||||
} \
|
||||
@@ -846,8 +835,9 @@ typedef enum {
|
||||
assert (tree->node == NULL || RBTREEI_IS_BLACK(tree->node)); \
|
||||
/* delete it */ \
|
||||
if (data_ptr != NULL) \
|
||||
M_GET_SET oplist (*data_ptr, n->data); \
|
||||
M_GET_CLEAR oplist (n->data); \
|
||||
M_DO_MOVE(oplist, *data_ptr, n->data); \
|
||||
else \
|
||||
M_CALL_CLEAR(oplist, n->data); \
|
||||
M_C(name,_int_del) (n); \
|
||||
tree->size --; \
|
||||
RBTREEI_CONTRACT (tree); \
|
||||
@@ -869,9 +859,9 @@ typedef enum {
|
||||
M_C(name, _it)(it2, t2); \
|
||||
while (!M_C(name, _end_p)(it1) \
|
||||
&& !M_C(name, _end_p)(it2)) { \
|
||||
const type *ref1 = M_C(name, _cref)(it1); \
|
||||
const type *ref2 = M_C(name, _cref)(it2); \
|
||||
if (M_GET_EQUAL oplist (*ref1, *ref2) == false) \
|
||||
type const *ref1 = M_C(name, _cref)(it1); \
|
||||
type const *ref2 = M_C(name, _cref)(it2); \
|
||||
if (M_CALL_EQUAL(oplist, *ref1, *ref2) == false) \
|
||||
return false; \
|
||||
M_C(name, _next)(it1); \
|
||||
M_C(name, _next)(it2); \
|
||||
@@ -890,8 +880,9 @@ typedef enum {
|
||||
tree_it_t it1; \
|
||||
M_C(name, _it)(it1, t1); \
|
||||
while (!M_C(name, _end_p)(it1)) { \
|
||||
const type *ref1 = M_C(name, _cref)(it1); \
|
||||
M_HASH_UP(hash, M_GET_HASH oplist (*ref1)); \
|
||||
type const *ref1 = M_C(name, _cref)(it1); \
|
||||
M_HASH_UP(hash, M_CALL_HASH(oplist, *ref1)); \
|
||||
M_C(name, _next)(it1); \
|
||||
} \
|
||||
return M_HASH_FINAL (hash); \
|
||||
} \
|
||||
@@ -899,7 +890,7 @@ typedef enum {
|
||||
\
|
||||
M_IF_METHOD(GET_STR, oplist)( \
|
||||
static inline void M_C(name, _get_str)(string_t str, \
|
||||
const tree_t t1, bool append) { \
|
||||
tree_t const t1, bool append) { \
|
||||
RBTREEI_CONTRACT(t1); \
|
||||
assert(str != NULL); \
|
||||
(append ? string_cat_str : string_set_str) (str, "["); \
|
||||
@@ -911,8 +902,8 @@ typedef enum {
|
||||
if (commaToPrint) \
|
||||
string_push_back (str, M_GET_SEPARATOR oplist); \
|
||||
commaToPrint = true; \
|
||||
const type *ref1 = M_C(name, _cref)(it1); \
|
||||
M_GET_GET_STR oplist(str, *ref1, true); \
|
||||
type const *ref1 = M_C(name, _cref)(it1); \
|
||||
M_CALL_GET_STR(oplist, str, *ref1, true); \
|
||||
M_C(name, _next)(it1); \
|
||||
} \
|
||||
string_push_back (str, ']'); \
|
||||
@@ -921,7 +912,7 @@ typedef enum {
|
||||
\
|
||||
M_IF_METHOD(OUT_STR, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _out_str)(FILE *file, tree_t rbtree) \
|
||||
M_C(name, _out_str)(FILE *file, tree_t const rbtree) \
|
||||
{ \
|
||||
RBTREEI_CONTRACT(rbtree); \
|
||||
assert (file != NULL); \
|
||||
@@ -934,13 +925,43 @@ typedef enum {
|
||||
if (commaToPrint) \
|
||||
fputc (M_GET_SEPARATOR oplist, file); \
|
||||
commaToPrint = true; \
|
||||
const type *item = M_C(name, _cref)(it); \
|
||||
M_GET_OUT_STR oplist (file, *item); \
|
||||
type const *item = M_C(name, _cref)(it); \
|
||||
M_CALL_OUT_STR(oplist, file, *item); \
|
||||
} \
|
||||
fputc (']', file); \
|
||||
} \
|
||||
, /* no out_str */ ) \
|
||||
\
|
||||
M_IF_METHOD(PARSE_STR, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _parse_str)(tree_t rbtree, const char str[], const char **endp) \
|
||||
{ \
|
||||
RBTREEI_CONTRACT(rbtree); \
|
||||
assert (str != NULL); \
|
||||
M_C(name,_clean)(rbtree); \
|
||||
bool success = false; \
|
||||
int c = *str++; \
|
||||
if (M_UNLIKELY (c != '[')) goto exit; \
|
||||
c = *str++; \
|
||||
if (M_UNLIKELY (c == ']')) { success = true; goto exit; } \
|
||||
if (M_UNLIKELY (c == 0)) goto exit; \
|
||||
str--; \
|
||||
type item; \
|
||||
M_CALL_INIT(oplist, item); \
|
||||
do { \
|
||||
bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \
|
||||
do { c = *str++; } while (isspace(c)); \
|
||||
if (b == false || c == 0) goto exit; \
|
||||
M_C(name, _push)(rbtree, item); \
|
||||
} while (c == M_GET_SEPARATOR oplist); \
|
||||
M_CALL_CLEAR(oplist, item); \
|
||||
success = (c == ']'); \
|
||||
exit: \
|
||||
if (endp) *endp = str; \
|
||||
return success; \
|
||||
} \
|
||||
, /* no parse_str */ ) \
|
||||
\
|
||||
M_IF_METHOD(IN_STR, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _in_str)(tree_t rbtree, FILE *file) \
|
||||
@@ -955,14 +976,14 @@ typedef enum {
|
||||
if (M_UNLIKELY (c == EOF)) return false; \
|
||||
ungetc(c, file); \
|
||||
type item; \
|
||||
M_GET_INIT oplist (item); \
|
||||
M_CALL_INIT(oplist, item); \
|
||||
do { \
|
||||
bool b = M_GET_IN_STR oplist (item, file); \
|
||||
c = fgetc(file); \
|
||||
bool b = M_CALL_IN_STR(oplist, item, file); \
|
||||
do { c = fgetc(file); } while (isspace(c)); \
|
||||
if (b == false || c == EOF) break; \
|
||||
M_C(name, _push)(rbtree, item); \
|
||||
} while (c == M_GET_SEPARATOR oplist); \
|
||||
M_GET_CLEAR oplist (item); \
|
||||
M_CALL_CLEAR(oplist, item); \
|
||||
return c == ']'; \
|
||||
} \
|
||||
, /* no in_str */ ) \
|
||||
|
||||
+211
-47
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - SHARED PTR Module
|
||||
* M*LIB - SHARED Module
|
||||
*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -22,25 +22,43 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __M_SHARED_PTR__H
|
||||
#define __M_SHARED_PTR__H
|
||||
#ifndef MSTARLIB_SHARED_PTR_H
|
||||
#define MSTARLIB_SHARED_PTR_H
|
||||
|
||||
#include "m-core.h"
|
||||
#include "m-atomic.h"
|
||||
#include "m-genint.h"
|
||||
|
||||
/* Define the oplist of a shared pointer.
|
||||
USAGE: SHARED_OPLIST(name [, oplist_of_the_type]) */
|
||||
#define SHARED_PTR_OPLIST(...) \
|
||||
SHAREDI_PTR_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__) ), \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST ), \
|
||||
_OPLIST(__VA_ARGS__ )))
|
||||
|
||||
/* Define shared pointer and its function.
|
||||
USAGE: SHARED_PTR_DEF(name[, oplist]) */
|
||||
USAGE: SHARED_PTR_DEF(name, type, [, oplist]) */
|
||||
#define SHARED_PTR_DEF(name, ...) \
|
||||
SHAREDI_PTR_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__) ), \
|
||||
(name, __VA_ARGS__ )))
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), SHAREDI_ATOMIC_OPLIST ), \
|
||||
(name, __VA_ARGS__ , SHAREDI_ATOMIC_OPLIST)))
|
||||
|
||||
/* Define relaxed shared pointer and its function (thread unsafe).
|
||||
USAGE: SHARED_PTR_RELAXED_DEF(name, type, [, oplist]) */
|
||||
#define SHARED_PTR_RELAXED_DEF(name, ...) \
|
||||
SHAREDI_PTR_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), SHAREDI_INTEGER_OPLIST ), \
|
||||
(name, __VA_ARGS__ , SHAREDI_INTEGER_OPLIST)))
|
||||
|
||||
/* Define shared resource and its function.
|
||||
This is a bounded pool of resource shared by multiple owners.
|
||||
USAGE: SHARED_RESOURCE_DEF(name, type, [, oplist]) */
|
||||
#define SHARED_RESOURCE_DEF(name, ...) \
|
||||
SHAREDI_RESOURCE_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__) ), \
|
||||
(name, __VA_ARGS__)))
|
||||
|
||||
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
|
||||
@@ -64,13 +82,38 @@
|
||||
// deferred
|
||||
#define SHAREDI_PTR_DEF(arg) SHAREDI_PTR_DEF2 arg
|
||||
|
||||
#define SHAREDI_PTR_DEF2(name, type, oplist) \
|
||||
// OPLIST to handle a counter of atomic type
|
||||
#define SHAREDI_ATOMIC_OPLIST (TYPE(atomic_int), \
|
||||
INIT_SET(atomic_init), \
|
||||
ADD(atomic_fetch_add), \
|
||||
SUB(atomic_fetch_sub), \
|
||||
IT_CREF(atomic_load))
|
||||
|
||||
// OPLIST to handle a counter of non-atomic type with its helper functions.
|
||||
#define SHAREDI_INTEGER_OPLIST (TYPE(int), \
|
||||
INIT_SET(sharedi_integer_init_set), \
|
||||
ADD(sharedi_integer_add), \
|
||||
SUB(sharedi_integer_sub), \
|
||||
IT_CREF(sharedi_integer_cref))
|
||||
static inline void sharedi_integer_init_set(int *p, int val) { *p = val; }
|
||||
static inline int sharedi_integer_add(int *p, int val) { int r = *p; *p += val; return r; }
|
||||
static inline int sharedi_integer_sub(int *p, int val) { int r = *p; *p -= val; return r; }
|
||||
static inline int sharedi_integer_cref(int *p) { return *p; }
|
||||
|
||||
#define SHAREDI_CONTRACT(shared, cpt_oplist) do { \
|
||||
assert(shared != NULL); \
|
||||
assert(*shared == NULL || M_CALL_IT_CREF(cpt_oplist, &(*shared)->cpt) >= 1); \
|
||||
} while (0)
|
||||
|
||||
#define SHAREDI_PTR_DEF2(name, type, oplist, cpt_oplist) \
|
||||
\
|
||||
typedef struct M_C(name, _s){ \
|
||||
type *data; /* Pointer to the data */ \
|
||||
atomic_int cpt; /* Counter of how many points to the data */ \
|
||||
type *data; /* Pointer to the data */ \
|
||||
M_GET_TYPE cpt_oplist cpt; /* Counter of how many points to the data */ \
|
||||
bool combineAlloc; /* Does the data and the ptr share the slot? */ \
|
||||
} *M_C(name, _t)[1]; \
|
||||
typedef struct M_C(name, _s) *M_C(name, _ptr); \
|
||||
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
|
||||
\
|
||||
typedef struct M_C(name, combine_s) { \
|
||||
type data; \
|
||||
@@ -93,16 +136,16 @@
|
||||
*shared = NULL; \
|
||||
return; \
|
||||
} \
|
||||
ptr = M_GET_NEW oplist (struct M_C(name, _s)); \
|
||||
ptr = M_CALL_NEW(oplist, struct M_C(name, _s)); \
|
||||
if (M_UNLIKELY (ptr == NULL)) { \
|
||||
M_MEMORY_FULL(sizeof(struct M_C(name, _s))); \
|
||||
return; \
|
||||
} \
|
||||
ptr->data = data; \
|
||||
atomic_init (&ptr->cpt, 1); \
|
||||
M_CALL_INIT_SET(cpt_oplist, &ptr->cpt, 1); \
|
||||
ptr->combineAlloc = false; \
|
||||
*shared = ptr; \
|
||||
SHAREDI_CONTRACT(shared); \
|
||||
SHAREDI_CONTRACT(shared, cpt_oplist); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
@@ -110,25 +153,25 @@
|
||||
{ \
|
||||
/* NOTE: Alloc 1 struct with both structures. */ \
|
||||
struct M_C(name, combine_s) *p = \
|
||||
M_GET_NEW oplist (struct M_C(name, combine_s)); \
|
||||
M_CALL_NEW(oplist, struct M_C(name, combine_s)); \
|
||||
if (M_UNLIKELY (p == NULL)) { \
|
||||
M_MEMORY_FULL(sizeof(struct M_C(name, combine_s))); \
|
||||
return; \
|
||||
} \
|
||||
struct M_C(name, _s) *ptr = &p->ptr; \
|
||||
type *data = &p->data; \
|
||||
M_GET_INIT oplist(*data); \
|
||||
M_CALL_INIT( oplist, *data); \
|
||||
ptr->data = data; \
|
||||
atomic_init (&ptr->cpt, 1); \
|
||||
M_CALL_INIT_SET(cpt_oplist, &ptr->cpt, 1); \
|
||||
ptr->combineAlloc = true; \
|
||||
*shared = ptr; \
|
||||
SHAREDI_CONTRACT(shared); \
|
||||
SHAREDI_CONTRACT(shared, cpt_oplist); \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _NULL_p)(const M_C(name, _t) shared) \
|
||||
{ \
|
||||
SHAREDI_CONTRACT(shared); \
|
||||
SHAREDI_CONTRACT(shared, cpt_oplist); \
|
||||
return *shared == NULL; \
|
||||
} \
|
||||
\
|
||||
@@ -136,35 +179,35 @@
|
||||
M_C(name, _init_set)(M_C(name, _t) dest, \
|
||||
const M_C(name, _t) shared) \
|
||||
{ \
|
||||
SHAREDI_CONTRACT(shared); \
|
||||
SHAREDI_CONTRACT(shared, cpt_oplist); \
|
||||
assert (dest != shared); \
|
||||
*dest = *shared; \
|
||||
if (*dest != NULL) { \
|
||||
int n = atomic_fetch_add(&((*dest)->cpt), 1); \
|
||||
int n = M_CALL_ADD(cpt_oplist, &((*dest)->cpt), 1); \
|
||||
(void) n; /* unused return value */ \
|
||||
} \
|
||||
SHAREDI_CONTRACT(dest); \
|
||||
SHAREDI_CONTRACT(dest, cpt_oplist); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _clear)(M_C(name, _t) dest) \
|
||||
{ \
|
||||
SHAREDI_CONTRACT(dest); \
|
||||
SHAREDI_CONTRACT(dest, cpt_oplist); \
|
||||
if (*dest != NULL) { \
|
||||
if (atomic_fetch_sub(&((*dest)->cpt), 1) == 1) { \
|
||||
if (M_CALL_SUB(cpt_oplist, &((*dest)->cpt), 1) == 1) { \
|
||||
bool combineAlloc = (*dest)->combineAlloc; \
|
||||
/* Note: if combineAlloc is true, the address of the slot \
|
||||
combining both data & ptr is the same as the address of the \
|
||||
first element, aka data itself. Static analyzer tools don't \
|
||||
seem to detect this and report error. */ \
|
||||
M_GET_CLEAR oplist (*(*dest)->data); \
|
||||
M_GET_DEL oplist ((*dest)->data); \
|
||||
M_CALL_CLEAR(oplist, *(*dest)->data); \
|
||||
M_CALL_DEL(oplist, (*dest)->data); \
|
||||
if (combineAlloc == false) \
|
||||
M_GET_DEL oplist (*dest); \
|
||||
M_CALL_DEL(oplist, *dest); \
|
||||
} \
|
||||
*dest = NULL; \
|
||||
} \
|
||||
SHAREDI_CONTRACT(dest); \
|
||||
SHAREDI_CONTRACT(dest, cpt_oplist); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
@@ -178,8 +221,8 @@
|
||||
M_C(name, _set)(M_C(name, _t) dest, \
|
||||
const M_C(name, _t) shared) \
|
||||
{ \
|
||||
SHAREDI_CONTRACT(dest); \
|
||||
SHAREDI_CONTRACT(shared); \
|
||||
SHAREDI_CONTRACT(dest, cpt_oplist); \
|
||||
SHAREDI_CONTRACT(shared, cpt_oplist); \
|
||||
M_C(name, _clear)(dest); \
|
||||
M_C(name, _init_set)(dest, shared); \
|
||||
} \
|
||||
@@ -188,19 +231,19 @@
|
||||
M_C(name, _init_move)(M_C(name, _t) dest, \
|
||||
M_C(name, _t) shared) \
|
||||
{ \
|
||||
SHAREDI_CONTRACT(shared); \
|
||||
SHAREDI_CONTRACT(shared, cpt_oplist); \
|
||||
assert (dest != NULL && dest != shared); \
|
||||
*dest = *shared; \
|
||||
*shared = NULL; \
|
||||
SHAREDI_CONTRACT(dest); \
|
||||
SHAREDI_CONTRACT(dest, cpt_oplist); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _move)(M_C(name, _t) dest, \
|
||||
M_C(name, _t) shared) \
|
||||
{ \
|
||||
SHAREDI_CONTRACT(dest); \
|
||||
SHAREDI_CONTRACT(shared); \
|
||||
SHAREDI_CONTRACT(dest, cpt_oplist); \
|
||||
SHAREDI_CONTRACT(shared, cpt_oplist); \
|
||||
assert (dest != shared); \
|
||||
M_C(name, _clear)(dest); \
|
||||
M_C(name, _init_move)(dest, shared); \
|
||||
@@ -210,26 +253,26 @@
|
||||
M_C(name, _swap)(M_C(name, _t) p1, \
|
||||
M_C(name, _t) p2) \
|
||||
{ \
|
||||
SHAREDI_CONTRACT(p1); \
|
||||
SHAREDI_CONTRACT(p2); \
|
||||
SHAREDI_CONTRACT(p1, cpt_oplist); \
|
||||
SHAREDI_CONTRACT(p2, cpt_oplist); \
|
||||
M_SWAP (struct M_C(name, _s)*, *p1, *p2); \
|
||||
SHAREDI_CONTRACT(p1); \
|
||||
SHAREDI_CONTRACT(p2); \
|
||||
SHAREDI_CONTRACT(p1, cpt_oplist); \
|
||||
SHAREDI_CONTRACT(p2, cpt_oplist); \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _equal_p)(const M_C(name, _t) p1, \
|
||||
const M_C(name, _t) p2) \
|
||||
{ \
|
||||
SHAREDI_CONTRACT(p1); \
|
||||
SHAREDI_CONTRACT(p2); \
|
||||
SHAREDI_CONTRACT(p1, cpt_oplist); \
|
||||
SHAREDI_CONTRACT(p2, cpt_oplist); \
|
||||
return *p1 == *p2; \
|
||||
} \
|
||||
\
|
||||
static inline const type * \
|
||||
static inline type const * \
|
||||
M_C(name, _cref)(const M_C(name, _t) shared) \
|
||||
{ \
|
||||
SHAREDI_CONTRACT(shared); \
|
||||
SHAREDI_CONTRACT(shared, cpt_oplist); \
|
||||
assert(*shared != NULL); \
|
||||
type *data = (*shared)->data; \
|
||||
assert (data != NULL); \
|
||||
@@ -239,18 +282,139 @@
|
||||
static inline type * \
|
||||
M_C(name, _ref)(M_C(name, _t) shared) \
|
||||
{ \
|
||||
SHAREDI_CONTRACT(shared); \
|
||||
SHAREDI_CONTRACT(shared, cpt_oplist); \
|
||||
assert(*shared != NULL); \
|
||||
type *data = (*shared)->data; \
|
||||
assert (data != NULL); \
|
||||
return data; \
|
||||
} \
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
/********************************** SHARED RESOURCE ************************************/
|
||||
|
||||
#define SHAREDI_CONTRACT(shared) do { \
|
||||
assert(shared != NULL); \
|
||||
assert(*shared == NULL || atomic_load (&(*shared)->cpt) >= 1); \
|
||||
#define SHAREDI_RESOURCE_CONTRACT(s) do { \
|
||||
assert (s != NULL); \
|
||||
assert (s->buffer != NULL); \
|
||||
} while (0)
|
||||
|
||||
// deferred
|
||||
#define SHAREDI_RESOURCE_DEF(arg) SHAREDI_RESOURCE_DEF2 arg
|
||||
|
||||
#define SHAREDI_RESOURCE_DEF2(name, type, oplist) \
|
||||
\
|
||||
/* Create an aligned type to avoid false sharing between threads */ \
|
||||
typedef struct M_C(name, _atype_s) { \
|
||||
atomic_uint cpt; \
|
||||
type x; \
|
||||
char align[M_ALIGN_FOR_CACHELINE_EXCLUSION > sizeof(type) +sizeof(atomic_uint) ? M_ALIGN_FOR_CACHELINE_EXCLUSION - sizeof(type) -sizeof(atomic_uint) : 1]; \
|
||||
} M_C(name, _atype_t); \
|
||||
\
|
||||
typedef struct M_C(name, _s) { \
|
||||
genint_t core; \
|
||||
M_C(name, _atype_t) *buffer; \
|
||||
} M_C(name, _t)[1]; \
|
||||
\
|
||||
typedef struct M_C(name, _it_s) { \
|
||||
unsigned int idx; \
|
||||
struct M_C(name, _s) *ref; \
|
||||
} M_C(name, _it_t)[1]; \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _init)(M_C(name, _t) s, size_t n) \
|
||||
{ \
|
||||
assert(s != NULL); \
|
||||
assert (n > 0); \
|
||||
s->buffer = M_CALL_REALLOC(oplist, M_C(name, _atype_t), NULL, n); \
|
||||
if (M_UNLIKELY (s->buffer == NULL)) { \
|
||||
M_MEMORY_FULL(sizeof(M_C(name, _atype_t)) * n); \
|
||||
return; \
|
||||
} \
|
||||
for(size_t i = 0; i < n; i++) { \
|
||||
M_CALL_INIT(oplist, s->buffer[i].x); \
|
||||
atomic_init (&s->buffer[i].cpt, 0U); \
|
||||
} \
|
||||
genint_init(s->core, n); \
|
||||
SHAREDI_RESOURCE_CONTRACT(s); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _clear)(M_C(name, _t) s) \
|
||||
{ \
|
||||
SHAREDI_RESOURCE_CONTRACT(s); \
|
||||
size_t n = genint_size(s->core); \
|
||||
for(size_t i = 0; i < n; i++) { \
|
||||
M_CALL_CLEAR(oplist, s->buffer[i].x); \
|
||||
} \
|
||||
M_CALL_FREE(oplist, s->buffer); \
|
||||
s->buffer = NULL; \
|
||||
genint_clear(s->core); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it)(M_C(name, _it_t) it, M_C(name, _t) s) \
|
||||
{ \
|
||||
SHAREDI_RESOURCE_CONTRACT(s); \
|
||||
assert (it != NULL); \
|
||||
unsigned int idx = genint_pop(s->core); \
|
||||
it->idx = idx; \
|
||||
it->ref = s; \
|
||||
if (M_LIKELY (idx != GENINT_ERROR)) { \
|
||||
assert(atomic_load(&s->buffer[idx].cpt) == 0); \
|
||||
atomic_store(&s->buffer[idx].cpt, 1U); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _end_p)(M_C(name, _it_t) it) \
|
||||
{ \
|
||||
assert (it != NULL); \
|
||||
return it->idx == GENINT_ERROR; \
|
||||
} \
|
||||
\
|
||||
static inline type * \
|
||||
M_C(name, _ref)(M_C(name, _it_t) it) \
|
||||
{ \
|
||||
assert (it != NULL && it->ref != NULL && it->idx != GENINT_ERROR); \
|
||||
SHAREDI_RESOURCE_CONTRACT(it->ref); \
|
||||
return &it->ref->buffer[it->idx].x; \
|
||||
} \
|
||||
\
|
||||
static inline type const * \
|
||||
M_C(name, _cref)(M_C(name, _it_t) it) \
|
||||
{ \
|
||||
assert (it != NULL && it->ref != NULL && it->idx != GENINT_ERROR); \
|
||||
SHAREDI_RESOURCE_CONTRACT(it->ref); \
|
||||
return M_CONST_CAST (type, &it->ref->buffer[it->idx].x); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _end)(M_C(name, _it_t) it, M_C(name, _t) s) \
|
||||
{ \
|
||||
SHAREDI_RESOURCE_CONTRACT(s); \
|
||||
assert (it != NULL); \
|
||||
assert (it->ref == s); \
|
||||
unsigned int idx = it->idx; \
|
||||
if (M_LIKELY (idx != GENINT_ERROR)) { \
|
||||
unsigned int c = atomic_fetch_sub (&it->ref->buffer[idx].cpt, 1U); \
|
||||
if (c == 1) { \
|
||||
genint_push(it->ref->core, idx); \
|
||||
} \
|
||||
it->idx = GENINT_ERROR; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _it_set)(M_C(name, _it_t) itd, M_C(name, _it_t) its) \
|
||||
{ \
|
||||
assert (itd != NULL && its != NULL); \
|
||||
SHAREDI_RESOURCE_CONTRACT(its->ref); \
|
||||
itd->ref = its->ref; \
|
||||
unsigned int idx = its->idx; \
|
||||
itd->idx = idx; \
|
||||
if (M_LIKELY (idx != GENINT_ERROR)) { \
|
||||
unsigned int c = atomic_fetch_add(&itd->ref->buffer[idx].cpt, 1U); \
|
||||
assert (c >= 1); \
|
||||
} \
|
||||
} \
|
||||
|
||||
|
||||
#endif
|
||||
+477
-96
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - SNAPSHOT Module
|
||||
*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -22,225 +22,606 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __M_SNAPSHOT
|
||||
#define __M_SNAPSHOT
|
||||
#ifndef MSTARLIB_SNAPSHOT
|
||||
#define MSTARLIB_SNAPSHOT
|
||||
|
||||
#include "m-atomic.h"
|
||||
#include "m-core.h"
|
||||
#include "m-genint.h"
|
||||
|
||||
/* Define a snapshot and it function
|
||||
USAGE: SNAPSHOT_DEF(name, type[, oplist]) */
|
||||
#define SNAPSHOT_DEF(name, ...) \
|
||||
SNAPSHOTI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__) ), \
|
||||
(name, __VA_ARGS__ )))
|
||||
/* Define a Single Producer Single Consummer snapshot and its functions
|
||||
USAGE: SNAPSHOT_SPSC_DEF(name, type[, oplist]) */
|
||||
#define SNAPSHOT_SPSC_DEF(name, ...) \
|
||||
SNAPSHOTI_SPSC_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
|
||||
(name, __VA_ARGS__ )))
|
||||
|
||||
/* Define the oplist of a snapshot.
|
||||
/* Define a Single Producer Multiple Consummer snapshot and its functions
|
||||
USAGE: SNAPSHOT_SPMC_DEF(name, type[, oplist]) */
|
||||
#define SNAPSHOT_SPMC_DEF(name, ...) \
|
||||
SNAPSHOTI_SPMC_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
|
||||
(name, __VA_ARGS__ )))
|
||||
|
||||
/* Define a Multiple Producer Multiple Consummer snapshot and its functions
|
||||
USAGE: SNAPSHOT_MPMC_DEF(name, type[, oplist]) */
|
||||
#define SNAPSHOT_MPMC_DEF(name, ...) \
|
||||
SNAPSHOTI_MPMC_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
|
||||
(name, __VA_ARGS__ )))
|
||||
|
||||
/* Define the oplist of a snapshot (SPSC, SPMC or MPMC).
|
||||
USAGE: SNAPSHOT_OPLIST(name[, oplist]) */
|
||||
#define SNAPSHOT_OPLIST(...) \
|
||||
SNAPSHOTI_OPLIST(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__) ), \
|
||||
((__VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)() ), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
|
||||
// deferred
|
||||
// deferred evaluation of the input
|
||||
#define SNAPSHOTI_OPLIST(arg) SNAPSHOTI_OPLIST2 arg
|
||||
|
||||
/* Define the oplist of a snapshot */
|
||||
#define SNAPSHOTI_OPLIST2(name, oplist) \
|
||||
(INIT(M_C(name, _init)), \
|
||||
INIT_SET(M_C(name, _init_set)), \
|
||||
SET(M_C(name, _set)), \
|
||||
CLEAR(M_C(name, _clear)), \
|
||||
TYPE(M_C(name, _t)), \
|
||||
SUBTYPE(M_C(name_, type_t)) \
|
||||
(INIT(M_C(name, _init)) \
|
||||
,INIT_SET(M_C(name, _init_set)) \
|
||||
,SET(M_C(name, _set)) \
|
||||
,CLEAR(M_C(name, _clear)) \
|
||||
,TYPE(M_C(name, _t)) \
|
||||
,SUBTYPE(M_C(name_, type_t)) \
|
||||
,OPLIST(oplist) \
|
||||
,M_IF_METHOD(INIT_MOVE, oplist)(INIT_MOVE(M_C(name, _init_move)),) \
|
||||
,M_IF_METHOD(MOVE, oplist)(MOVE(M_C(name, _move)),) \
|
||||
)
|
||||
|
||||
|
||||
/******************************** INTERNAL SPSC **********************************/
|
||||
|
||||
/* Flag defining the atomic state of a snapshot:
|
||||
* - r: Index of the read buffer Range [0..2]
|
||||
* - w: Index of the write buffer Range [0..2]
|
||||
* - f: Next index of the write buffer when a shot is taken Range [0..2]
|
||||
* - b: Boolean indicating that the read buffer shall be updated
|
||||
*/
|
||||
#define SNAPSHOTI_FLAG(r, w, f, b) \
|
||||
#define SNAPSHOTI_SPSC_FLAG(r, w, f, b) \
|
||||
((unsigned char)( ( (r) << 4) | ((w) << 2) | ((f)) | ((b) << 6)))
|
||||
#define SNAPSHOTI_R(flags) \
|
||||
#define SNAPSHOTI_SPSC_R(flags) \
|
||||
(((flags) >> 4) & 0x03u)
|
||||
#define SNAPSHOTI_W(flags) \
|
||||
#define SNAPSHOTI_SPSC_W(flags) \
|
||||
(((flags) >> 2) & 0x03u)
|
||||
#define SNAPSHOTI_F(flags) \
|
||||
#define SNAPSHOTI_SPSC_F(flags) \
|
||||
(((flags) >> 0) & 0x03u)
|
||||
#define SNAPSHOTI_B(flags) \
|
||||
#define SNAPSHOTI_SPSC_B(flags) \
|
||||
(((flags) >> 6) & 0x01u)
|
||||
|
||||
/* NOTE: Due to atomic_load only accepting non-const pointer,
|
||||
we can't have any const in the interface. */
|
||||
#define SNAPSHOTI_FLAGS_CONTRACT(flags) \
|
||||
assert(SNAPSHOTI_R(flags) != SNAPSHOTI_W(flags) \
|
||||
&& SNAPSHOTI_R(flags) != SNAPSHOTI_F(flags) \
|
||||
&& SNAPSHOTI_W(flags) != SNAPSHOTI_F(flags))
|
||||
#define SNAPSHOTI_CONTRACT(snap) do { \
|
||||
#define SNAPSHOTI_SPSC_FLAGS_CONTRACT(flags) \
|
||||
assert(SNAPSHOTI_SPSC_R(flags) != SNAPSHOTI_SPSC_W(flags) \
|
||||
&& SNAPSHOTI_SPSC_R(flags) != SNAPSHOTI_SPSC_F(flags) \
|
||||
&& SNAPSHOTI_SPSC_W(flags) != SNAPSHOTI_SPSC_F(flags))
|
||||
#define SNAPSHOTI_SPSC_CONTRACT(snap) do { \
|
||||
assert((snap) != NULL); \
|
||||
unsigned char f = atomic_load (&(snap)->flags); \
|
||||
SNAPSHOTI_FLAGS_CONTRACT(f); \
|
||||
SNAPSHOTI_SPSC_FLAGS_CONTRACT(f); \
|
||||
} while (0)
|
||||
|
||||
//defered
|
||||
#define SNAPSHOTI_DEF(arg) SNAPSHOTI_DEF2 arg
|
||||
// Defered evaluation (TBC if it really helps).
|
||||
#define SNAPSHOTI_SPSC_DEF(arg) SNAPSHOTI_SPSC_DEF2 arg
|
||||
|
||||
// This is basically an atomic triple buffer between a produced thread
|
||||
// and a consummer thread.
|
||||
// TODO: Analyse generalisation to N produced threads and M consummer threads
|
||||
#define SNAPSHOTI_MAX_BUFFER 3
|
||||
// This is basically an atomic triple buffer (Lock Free)
|
||||
// between a produced thread and a consummer thread.
|
||||
#define SNAPSHOTI_SPSC_MAX_BUFFER 3
|
||||
|
||||
#define SNAPSHOTI_DEF2(name, type, oplist) \
|
||||
\
|
||||
#define SNAPSHOTI_SPSC_DEF2(name, type, oplist) \
|
||||
\
|
||||
/* Create an aligned type to avoid false sharing between threads */ \
|
||||
typedef struct M_C(name, _aligned_type_s) { \
|
||||
type x; \
|
||||
char align[M_ALIGN_FOR_CACHELINE_EXCLUSION > sizeof(type) ? M_ALIGN_FOR_CACHELINE_EXCLUSION - sizeof(type) : 1]; \
|
||||
} M_C(name, _aligned_type_t); \
|
||||
\
|
||||
typedef struct M_C(name, _s) { \
|
||||
type data[SNAPSHOTI_MAX_BUFFER]; \
|
||||
M_C(name, _aligned_type_t) data[SNAPSHOTI_SPSC_MAX_BUFFER]; \
|
||||
atomic_uchar flags; \
|
||||
} M_C(name, _t)[1]; \
|
||||
typedef struct M_C(name, _s) *M_C(name, _ptr); \
|
||||
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
|
||||
\
|
||||
typedef type M_C(name, _type_t); \
|
||||
\
|
||||
static inline void M_C(name, _init)(M_C(name, _t) snap) \
|
||||
{ \
|
||||
assert(snap != NULL); \
|
||||
for(int i = 0; i < SNAPSHOTI_MAX_BUFFER; i++) { \
|
||||
M_GET_INIT oplist(snap->data[i]); \
|
||||
for(int i = 0; i < SNAPSHOTI_SPSC_MAX_BUFFER; i++) { \
|
||||
M_CALL_INIT(oplist, snap->data[i].x); \
|
||||
} \
|
||||
atomic_init (&snap->flags, SNAPSHOTI_FLAG(0, 1, 2, 0)); \
|
||||
SNAPSHOTI_CONTRACT(snap); \
|
||||
atomic_init (&snap->flags, SNAPSHOTI_SPSC_FLAG(0, 1, 2, 0)); \
|
||||
SNAPSHOTI_SPSC_CONTRACT(snap); \
|
||||
} \
|
||||
\
|
||||
\
|
||||
static inline void M_C(name, _clear)(M_C(name, _t) snap) \
|
||||
{ \
|
||||
SNAPSHOTI_CONTRACT(snap); \
|
||||
for(int i = 0; i < SNAPSHOTI_MAX_BUFFER; i++) { \
|
||||
M_GET_CLEAR oplist(snap->data[i]); \
|
||||
SNAPSHOTI_SPSC_CONTRACT(snap); \
|
||||
for(int i = 0; i < SNAPSHOTI_SPSC_MAX_BUFFER; i++) { \
|
||||
M_CALL_CLEAR(oplist, snap->data[i].x); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
\
|
||||
static inline void M_C(name, _init_set)(M_C(name, _t) snap, \
|
||||
M_C(name, _t) org) \
|
||||
{ \
|
||||
SNAPSHOTI_CONTRACT(org); \
|
||||
SNAPSHOTI_SPSC_CONTRACT(org); \
|
||||
assert(snap != NULL && snap != org); \
|
||||
for(int i = 0; i < SNAPSHOTI_MAX_BUFFER; i++) { \
|
||||
M_GET_INIT_SET oplist(snap->data[i], org->data[i]); \
|
||||
for(int i = 0; i < SNAPSHOTI_SPSC_MAX_BUFFER; i++) { \
|
||||
M_CALL_INIT_SET(oplist, snap->data[i].x, org->data[i].x); \
|
||||
} \
|
||||
atomic_init (&snap->flags, atomic_load(&org->flags)); \
|
||||
SNAPSHOTI_CONTRACT(snap); \
|
||||
SNAPSHOTI_SPSC_CONTRACT(snap); \
|
||||
} \
|
||||
\
|
||||
\
|
||||
static inline void M_C(name, _set)(M_C(name, _t) snap, \
|
||||
M_C(name, _t) org) \
|
||||
{ \
|
||||
SNAPSHOTI_CONTRACT(snap); \
|
||||
SNAPSHOTI_CONTRACT(org); \
|
||||
for(int i = 0; i < SNAPSHOTI_MAX_BUFFER; i++) { \
|
||||
M_GET_SET oplist(snap->data[i], org->data[i]); \
|
||||
SNAPSHOTI_SPSC_CONTRACT(snap); \
|
||||
SNAPSHOTI_SPSC_CONTRACT(org); \
|
||||
for(int i = 0; i < SNAPSHOTI_SPSC_MAX_BUFFER; i++) { \
|
||||
M_CALL_SET(oplist, snap->data[i].x, org->data[i].x); \
|
||||
} \
|
||||
atomic_init (&snap->flags, atomic_load(&org->flags)); \
|
||||
SNAPSHOTI_CONTRACT(snap); \
|
||||
SNAPSHOTI_SPSC_CONTRACT(snap); \
|
||||
} \
|
||||
\
|
||||
\
|
||||
M_IF_METHOD(INIT_MOVE, oplist)( \
|
||||
static inline void M_C(name, _init_move)(M_C(name, _t) snap, \
|
||||
M_C(name, _t) org) \
|
||||
{ \
|
||||
SNAPSHOTI_CONTRACT(org); \
|
||||
SNAPSHOTI_SPSC_CONTRACT(org); \
|
||||
assert(snap != NULL && snap != org); \
|
||||
for(int i = 0; i < SNAPSHOTI_MAX_BUFFER; i++) { \
|
||||
M_GET_INIT_MOVE oplist(snap->data[i], org->data[i]); \
|
||||
for(int i = 0; i < SNAPSHOTI_SPSC_MAX_BUFFER; i++) { \
|
||||
M_CALL_INIT_MOVE(oplist, snap->data[i].x, org->data[i].x); \
|
||||
} \
|
||||
atomic_store (&snap->flags, atomic_load(&org->flags)); \
|
||||
atomic_store (&org->flags, SNAPSHOTI_FLAG(0,0,0,0) ); \
|
||||
SNAPSHOTI_CONTRACT(snap); \
|
||||
atomic_store (&org->flags, SNAPSHOTI_SPSC_FLAG(0,0,0,0) ); \
|
||||
SNAPSHOTI_SPSC_CONTRACT(snap); \
|
||||
} \
|
||||
,) /* IF_METHOD (INIT_MOVE) */ \
|
||||
\
|
||||
\
|
||||
M_IF_METHOD(MOVE, oplist)( \
|
||||
static inline void M_C(name, _move)(M_C(name, _t) snap, \
|
||||
M_C(name, _t) org) \
|
||||
{ \
|
||||
SNAPSHOTI_CONTRACT(snap); \
|
||||
SNAPSHOTI_CONTRACT(org); \
|
||||
SNAPSHOTI_SPSC_CONTRACT(snap); \
|
||||
SNAPSHOTI_SPSC_CONTRACT(org); \
|
||||
assert(snap != org); \
|
||||
for(int i = 0; i < SNAPSHOTI_MAX_BUFFER; i++) { \
|
||||
M_GET_MOVE oplist(snap->data[i], org->data[i]); \
|
||||
for(int i = 0; i < SNAPSHOTI_SPSC_MAX_BUFFER; i++) { \
|
||||
M_CALL_MOVE(oplist, snap->data[i].x, org->data[i].x); \
|
||||
} \
|
||||
atomic_store (&snap->flags, atomic_load(&org->flags)); \
|
||||
atomic_store (&org->flags, SNAPSHOTI_FLAG(0,0,0,0) ); \
|
||||
SNAPSHOTI_CONTRACT(snap); \
|
||||
atomic_store (&org->flags, SNAPSHOTI_SPSC_FLAG(0,0,0,0) ); \
|
||||
SNAPSHOTI_SPSC_CONTRACT(snap); \
|
||||
} \
|
||||
,) /* IF_METHOD (MOVE) */ \
|
||||
\
|
||||
\
|
||||
\
|
||||
static inline type *M_C(name, _write)(M_C(name, _t) snap) \
|
||||
{ \
|
||||
SNAPSHOTI_CONTRACT(snap); \
|
||||
SNAPSHOTI_SPSC_CONTRACT(snap); \
|
||||
unsigned char nextFlags, origFlags = atomic_load (&snap->flags); \
|
||||
/* Atomic CAS operation */ \
|
||||
do { \
|
||||
/* Swap F and W buffer, setting exchange flag */ \
|
||||
nextFlags = SNAPSHOTI_FLAG(SNAPSHOTI_R(origFlags), \
|
||||
SNAPSHOTI_F(origFlags), \
|
||||
SNAPSHOTI_W(origFlags), 1); \
|
||||
/* FIXME: Is exponential backoff really needed ? */ \
|
||||
nextFlags = SNAPSHOTI_SPSC_FLAG(SNAPSHOTI_SPSC_R(origFlags), \
|
||||
SNAPSHOTI_SPSC_F(origFlags), \
|
||||
SNAPSHOTI_SPSC_W(origFlags), 1); \
|
||||
/* exponential backoff is not needed as there can't be more \
|
||||
than 2 threads which try to update the data. */ \
|
||||
} while (!atomic_compare_exchange_weak (&snap->flags, &origFlags, \
|
||||
nextFlags)); \
|
||||
/* Return new write buffer for new updating */ \
|
||||
return &snap->data[SNAPSHOTI_W(nextFlags)]; \
|
||||
return &snap->data[SNAPSHOTI_SPSC_W(nextFlags)].x; \
|
||||
} \
|
||||
\
|
||||
static inline const type *M_C(name, _read)(M_C(name, _t) snap) \
|
||||
\
|
||||
static inline type const *M_C(name, _read)(M_C(name, _t) snap) \
|
||||
{ \
|
||||
SNAPSHOTI_CONTRACT(snap); \
|
||||
SNAPSHOTI_SPSC_CONTRACT(snap); \
|
||||
unsigned char nextFlags, origFlags = atomic_load (&snap->flags); \
|
||||
/* Atomic CAS operation */ \
|
||||
do { \
|
||||
/* If no exchange registered, do nothing and keep the same */ \
|
||||
if (!SNAPSHOTI_B(origFlags)) { \
|
||||
if (!SNAPSHOTI_SPSC_B(origFlags)) { \
|
||||
nextFlags = origFlags; \
|
||||
break; \
|
||||
} \
|
||||
/* Swap R and F buffer, clearing exchange flag */ \
|
||||
nextFlags = SNAPSHOTI_FLAG(SNAPSHOTI_F(origFlags), \
|
||||
SNAPSHOTI_W(origFlags), \
|
||||
SNAPSHOTI_R(origFlags), 0); \
|
||||
/* FIXME: Is exponential backoff really needed ? */ \
|
||||
nextFlags = SNAPSHOTI_SPSC_FLAG(SNAPSHOTI_SPSC_F(origFlags), \
|
||||
SNAPSHOTI_SPSC_W(origFlags), \
|
||||
SNAPSHOTI_SPSC_R(origFlags), 0); \
|
||||
/* exponential backoff is not needed as there can't be more \
|
||||
than 2 threads which try to update the data. */ \
|
||||
} while (!atomic_compare_exchange_weak (&snap->flags, &origFlags, \
|
||||
nextFlags)); \
|
||||
/* Return current read buffer */ \
|
||||
return M_CONST_CAST(type, &snap->data[SNAPSHOTI_R(nextFlags)]); \
|
||||
return M_CONST_CAST(type, &snap->data[SNAPSHOTI_SPSC_R(nextFlags)].x); \
|
||||
} \
|
||||
\
|
||||
\
|
||||
static inline bool M_C(name, _updated_p)(M_C(name, _t) snap) \
|
||||
{ \
|
||||
SNAPSHOTI_CONTRACT(snap); \
|
||||
SNAPSHOTI_SPSC_CONTRACT(snap); \
|
||||
unsigned char flags = atomic_load (&snap->flags); \
|
||||
return SNAPSHOTI_B(flags); \
|
||||
return SNAPSHOTI_SPSC_B(flags); \
|
||||
} \
|
||||
\
|
||||
static inline type *M_C(name, _get_write_buffer)(M_C(name, _t) snap) \
|
||||
{ \
|
||||
SNAPSHOTI_CONTRACT(snap); \
|
||||
SNAPSHOTI_SPSC_CONTRACT(snap); \
|
||||
unsigned char flags = atomic_load(&snap->flags); \
|
||||
return &snap->data[SNAPSHOTI_W(flags)]; \
|
||||
return &snap->data[SNAPSHOTI_SPSC_W(flags)].x; \
|
||||
} \
|
||||
\
|
||||
static inline const type *M_C(name, _get_read_buffer)(M_C(name, _t) snap) \
|
||||
\
|
||||
static inline type const *M_C(name, _get_read_buffer)(M_C(name, _t) snap) \
|
||||
{ \
|
||||
SNAPSHOTI_CONTRACT(snap); \
|
||||
SNAPSHOTI_SPSC_CONTRACT(snap); \
|
||||
unsigned char flags = atomic_load(&snap->flags); \
|
||||
return M_CONST_CAST(type, &snap->data[SNAPSHOTI_R(flags)]); \
|
||||
return M_CONST_CAST(type, &snap->data[SNAPSHOTI_SPSC_R(flags)].x); \
|
||||
} \
|
||||
|
||||
// FIXME: Method SWAP ?
|
||||
|
||||
/******************************** INTERNAL SPMC **********************************/
|
||||
|
||||
#define SNAPSHOTI_SPMC_INT_FLAG(w, n) ( ((w) << 1) | (n) )
|
||||
#define SNAPSHOTI_SPMC_INT_FLAG_W(f) ((f) >> 1)
|
||||
#define SNAPSHOTI_SPMC_INT_FLAG_N(f) ((f) & 1)
|
||||
|
||||
// 2 more buffer than the number of readers are needed
|
||||
#define SNAPSHOTI_SPMC_EXTRA_BUFFER 2
|
||||
|
||||
#define SNAPSHOTI_SPMC_MAX_READER (GENINT_MAX_ALLOC-SNAPSHOTI_SPMC_EXTRA_BUFFER)
|
||||
|
||||
// structure to handle SPMC snapshot but return a unique index in a buffer array.
|
||||
typedef struct snapshot_mrsw_int_s {
|
||||
atomic_uint lastNext;
|
||||
unsigned int currentWrite;
|
||||
size_t n;
|
||||
atomic_uint *cptTab;
|
||||
genint_t freeList;
|
||||
} snapshot_mrsw_int_t[1];
|
||||
|
||||
// can't check currentWrite due to potential data race on it
|
||||
#define SNAPSHOTI_SPMC_INT_CONTRACT(s) do { \
|
||||
assert (s != NULL); \
|
||||
assert (s->n > 0 && s->n <= SNAPSHOTI_SPMC_MAX_READER); \
|
||||
assert ((size_t)SNAPSHOTI_SPMC_INT_FLAG_W(atomic_load(&s->lastNext)) \
|
||||
<= s->n + SNAPSHOTI_SPMC_EXTRA_BUFFER); \
|
||||
assert (s->cptTab != NULL); \
|
||||
} while (0)
|
||||
|
||||
static inline void snapshot_mrsw_int_init(snapshot_mrsw_int_t s, size_t n)
|
||||
{
|
||||
assert (s != NULL);
|
||||
assert (n >= 1 && n <= SNAPSHOTI_SPMC_MAX_READER);
|
||||
s->n = n;
|
||||
n += SNAPSHOTI_SPMC_EXTRA_BUFFER;
|
||||
atomic_uint *ptr = M_MEMORY_REALLOC (atomic_uint, NULL, n);
|
||||
if (M_UNLIKELY (ptr == NULL)) {
|
||||
M_MEMORY_FULL(sizeof (atomic_uint) * n);
|
||||
return;
|
||||
}
|
||||
s->cptTab = ptr;
|
||||
for(size_t i = 0; i < n; i++)
|
||||
atomic_init(&s->cptTab[i], 0U);
|
||||
genint_init (s->freeList, n);
|
||||
// Get a free buffer and set it as available for readers
|
||||
unsigned int w = genint_pop(s->freeList);
|
||||
assert (w != GENINT_ERROR);
|
||||
atomic_store(&s->cptTab[w], 1U);
|
||||
atomic_init(&s->lastNext, SNAPSHOTI_SPMC_INT_FLAG(w, true));
|
||||
// Get working buffer
|
||||
s->currentWrite = genint_pop(s->freeList);
|
||||
assert (s->currentWrite != GENINT_ERROR);
|
||||
atomic_store(&s->cptTab[s->currentWrite], 1U);
|
||||
SNAPSHOTI_SPMC_INT_CONTRACT(s);
|
||||
}
|
||||
|
||||
static inline void snapshot_mrsw_int_clear(snapshot_mrsw_int_t s)
|
||||
{
|
||||
SNAPSHOTI_SPMC_INT_CONTRACT(s);
|
||||
M_MEMORY_FREE (s->cptTab);
|
||||
genint_clear(s->freeList);
|
||||
s->cptTab = NULL;
|
||||
s->n = 0;
|
||||
}
|
||||
|
||||
static inline unsigned int snapshot_mrsw_int_get_write_idx(snapshot_mrsw_int_t s)
|
||||
{
|
||||
SNAPSHOTI_SPMC_INT_CONTRACT(s);
|
||||
return s->currentWrite;
|
||||
}
|
||||
|
||||
static inline unsigned int snapshot_mrsw_int_size(snapshot_mrsw_int_t s)
|
||||
{
|
||||
SNAPSHOTI_SPMC_INT_CONTRACT(s);
|
||||
return s->n - SNAPSHOTI_SPMC_EXTRA_BUFFER;
|
||||
}
|
||||
|
||||
static inline unsigned int snapshot_mrsw_int_write_idx(snapshot_mrsw_int_t s, unsigned int idx)
|
||||
{
|
||||
SNAPSHOTI_SPMC_INT_CONTRACT(s);
|
||||
unsigned int newNext, previous = atomic_load(&s->lastNext);
|
||||
do {
|
||||
newNext = SNAPSHOTI_SPMC_INT_FLAG(idx, true);
|
||||
} while (!atomic_compare_exchange_weak(&s->lastNext, &previous, newNext));
|
||||
if (SNAPSHOTI_SPMC_INT_FLAG_N(previous)) {
|
||||
// Reuse previous buffer as it was not used by any reader
|
||||
idx = SNAPSHOTI_SPMC_INT_FLAG_W(previous);
|
||||
// Some other read threads may already have try to reserve this index
|
||||
// So atomic_load(&s->cptTab[idx]) can be greater than 1.
|
||||
// However they will fail to ack it in lastNext, and remove their reservation later
|
||||
} else {
|
||||
// Free the write index
|
||||
idx = SNAPSHOTI_SPMC_INT_FLAG_W(previous);
|
||||
unsigned int c = atomic_fetch_sub(&s->cptTab[idx], 1U);
|
||||
assert (c != 0 && c <= s->n + 1);
|
||||
// Get a new buffer.
|
||||
if (c != 1) {
|
||||
// If someone else keeps a ref on the buffer, we can't reuse it
|
||||
// get another free one.
|
||||
idx = genint_pop(s->freeList);
|
||||
assert(idx != GENINT_ERROR);
|
||||
}
|
||||
assert (idx < s->n + SNAPSHOTI_SPMC_EXTRA_BUFFER);
|
||||
assert (atomic_load(&s->cptTab[idx]) == 0);
|
||||
atomic_store(&s->cptTab[idx], 1U);
|
||||
}
|
||||
SNAPSHOTI_SPMC_INT_CONTRACT(s);
|
||||
return idx;
|
||||
}
|
||||
|
||||
static inline unsigned int snapshot_mrsw_int_write(snapshot_mrsw_int_t s)
|
||||
{
|
||||
s->currentWrite = snapshot_mrsw_int_write_idx(s, s->currentWrite);
|
||||
SNAPSHOTI_SPMC_INT_CONTRACT(s);
|
||||
return s->currentWrite;
|
||||
}
|
||||
|
||||
static inline unsigned int snapshot_mrsw_int_write_start(snapshot_mrsw_int_t s)
|
||||
{
|
||||
SNAPSHOTI_SPMC_INT_CONTRACT(s);
|
||||
// Get a new buffer.
|
||||
unsigned int idx = genint_pop(s->freeList);
|
||||
assert (idx != GENINT_ERROR);
|
||||
assert (idx < s->n + SNAPSHOTI_SPMC_EXTRA_BUFFER);
|
||||
assert (atomic_load(&s->cptTab[idx]) == 0);
|
||||
atomic_store(&s->cptTab[idx], 1U);
|
||||
SNAPSHOTI_SPMC_INT_CONTRACT(s);
|
||||
return idx;
|
||||
}
|
||||
|
||||
static inline void snapshot_mrsw_int_write_end(snapshot_mrsw_int_t s, unsigned int idx)
|
||||
{
|
||||
SNAPSHOTI_SPMC_INT_CONTRACT(s);
|
||||
unsigned int newNext, previous = atomic_load(&s->lastNext);
|
||||
do {
|
||||
newNext = SNAPSHOTI_SPMC_INT_FLAG(idx, true);
|
||||
} while (!atomic_compare_exchange_weak(&s->lastNext, &previous, newNext));
|
||||
|
||||
// Free the previous write buffer
|
||||
idx = SNAPSHOTI_SPMC_INT_FLAG_W(previous);
|
||||
unsigned int c = atomic_fetch_sub(&s->cptTab[idx], 1U);
|
||||
assert (c != 0 && c <= s->n + 1);
|
||||
if (c == 1) {
|
||||
genint_push(s->freeList, idx);
|
||||
}
|
||||
SNAPSHOTI_SPMC_INT_CONTRACT(s);
|
||||
}
|
||||
|
||||
static inline unsigned int snapshot_mrsw_int_read_start(snapshot_mrsw_int_t s)
|
||||
{
|
||||
SNAPSHOTI_SPMC_INT_CONTRACT(s);
|
||||
unsigned int idx, previous;
|
||||
reload:
|
||||
// Load the last published index + Next flag
|
||||
previous = atomic_load(&s->lastNext);
|
||||
while (true) {
|
||||
// Get the last published index
|
||||
idx = SNAPSHOTI_SPMC_INT_FLAG_W(previous);
|
||||
// Load the number of threads using this index
|
||||
unsigned int c = atomic_load(&s->cptTab[idx]);
|
||||
assert (c <= s->n + 1);
|
||||
// Reserve the index if it still being reserved by someone else
|
||||
if (M_UNLIKELY (c == 0
|
||||
|| !atomic_compare_exchange_strong(&s->cptTab[idx], &c, c+1)))
|
||||
goto reload;
|
||||
// Try to ack it
|
||||
unsigned int newNext = SNAPSHOTI_SPMC_INT_FLAG(idx, false);
|
||||
reforce:
|
||||
if (M_LIKELY (atomic_compare_exchange_strong(&s->lastNext, &previous, newNext)))
|
||||
break;
|
||||
// We have been preempted by another thread
|
||||
if (idx == SNAPSHOTI_SPMC_INT_FLAG_W(previous)) {
|
||||
// This is still ok if the index has not changed
|
||||
// We can get previous to true again if the writer has recycled the index,
|
||||
// while we reserved it, and the reader get prempted until its CAS.
|
||||
if (M_UNLIKELY (SNAPSHOTI_SPMC_INT_FLAG_N(previous) == true)) goto reforce;
|
||||
break;
|
||||
}
|
||||
// Free the reserved index as we failed it to ack it
|
||||
c = atomic_fetch_sub(&s->cptTab[idx], 1U);
|
||||
assert (c != 0 && c <= s->n+1);
|
||||
if (c == 1) {
|
||||
genint_push(s->freeList, idx);
|
||||
}
|
||||
}
|
||||
SNAPSHOTI_SPMC_INT_CONTRACT(s);
|
||||
return idx;
|
||||
}
|
||||
|
||||
static inline void snapshot_mrsw_int_read_end(snapshot_mrsw_int_t s, unsigned int idx)
|
||||
{
|
||||
SNAPSHOTI_SPMC_INT_CONTRACT(s);
|
||||
assert (idx < s->n + SNAPSHOTI_SPMC_EXTRA_BUFFER);
|
||||
// Decrement reference counter of the buffer
|
||||
unsigned int c = atomic_fetch_sub(&s->cptTab[idx], 1U);
|
||||
assert (c != 0 && c <= s->n+1);
|
||||
if (c == 1) {
|
||||
// Buffer no longer used by any reader thread.
|
||||
// Push back index in free list
|
||||
genint_push(s->freeList, idx);
|
||||
}
|
||||
SNAPSHOTI_SPMC_INT_CONTRACT(s);
|
||||
}
|
||||
|
||||
#define SNAPSHOTI_SPMC_CONTRACT(snap) do { \
|
||||
assert (snap != NULL); \
|
||||
assert (snap->data != NULL); \
|
||||
} while (0)
|
||||
|
||||
|
||||
// Defered evaluation (TBC if it really helps).
|
||||
#define SNAPSHOTI_SPMC_DEF(arg) SNAPSHOTI_SPMC_DEF2 arg
|
||||
|
||||
#define SNAPSHOTI_SPMC_DEF2(name, type, oplist) \
|
||||
\
|
||||
/* Create an aligned type to avoid false sharing between threads */ \
|
||||
typedef struct M_C(name, _aligned_type_s) { \
|
||||
type x; \
|
||||
char align[M_ALIGN_FOR_CACHELINE_EXCLUSION > sizeof(type) ? M_ALIGN_FOR_CACHELINE_EXCLUSION - sizeof(type) : 1]; \
|
||||
} M_C(name, _aligned_type_t); \
|
||||
\
|
||||
typedef struct M_C(name, _s) { \
|
||||
M_C(name, _aligned_type_t) *data; \
|
||||
snapshot_mrsw_int_t core; \
|
||||
} M_C(name, _t)[1]; \
|
||||
\
|
||||
typedef type M_C(name, _type_t); \
|
||||
\
|
||||
static inline void M_C(name, _init)(M_C(name, _t) snap, size_t nReader) \
|
||||
{ \
|
||||
assert (snap != NULL); \
|
||||
assert (nReader > 0 && nReader <= SNAPSHOTI_SPMC_MAX_READER); \
|
||||
snap->data = M_CALL_REALLOC(oplist, M_C(name, _aligned_type_t), \
|
||||
NULL, nReader+SNAPSHOTI_SPMC_EXTRA_BUFFER); \
|
||||
if (M_UNLIKELY (snap->data == NULL)) { \
|
||||
M_MEMORY_FULL(sizeof(M_C(name, _aligned_type_t)) * \
|
||||
(nReader+SNAPSHOTI_SPMC_EXTRA_BUFFER)); \
|
||||
return; \
|
||||
} \
|
||||
for(size_t i = 0; i < nReader + SNAPSHOTI_SPMC_EXTRA_BUFFER; i++) { \
|
||||
M_CALL_INIT(oplist, snap->data[i].x); \
|
||||
} \
|
||||
snapshot_mrsw_int_init(snap->core, nReader); \
|
||||
SNAPSHOTI_SPMC_CONTRACT(snap); \
|
||||
} \
|
||||
\
|
||||
static inline void M_C(name, _clear)(M_C(name, _t) snap) \
|
||||
{ \
|
||||
SNAPSHOTI_SPMC_CONTRACT(snap); \
|
||||
size_t nReader = snapshot_mrsw_int_size(snap->core); \
|
||||
for(size_t i = 0; i < nReader + SNAPSHOTI_SPMC_EXTRA_BUFFER; i++) { \
|
||||
M_CALL_CLEAR(oplist, snap->data[i].x); \
|
||||
} \
|
||||
M_CALL_FREE(oplist, snap->data); \
|
||||
snapshot_mrsw_int_clear(snap->core); \
|
||||
} \
|
||||
\
|
||||
static inline type *M_C(name, _write)(M_C(name, _t) snap) \
|
||||
{ \
|
||||
SNAPSHOTI_SPMC_CONTRACT(snap); \
|
||||
const unsigned int idx = snapshot_mrsw_int_write(snap->core); \
|
||||
return &snap->data[idx].x; \
|
||||
} \
|
||||
\
|
||||
static inline type const *M_C(name, _read_start)(M_C(name, _t) snap) \
|
||||
{ \
|
||||
SNAPSHOTI_SPMC_CONTRACT(snap); \
|
||||
const unsigned int idx = snapshot_mrsw_int_read_start(snap->core); \
|
||||
return M_CONST_CAST(type, &snap->data[idx].x); \
|
||||
} \
|
||||
\
|
||||
static inline void M_C(name, _read_end)(M_C(name, _t) snap, type const *old) \
|
||||
{ \
|
||||
SNAPSHOTI_SPMC_CONTRACT(snap); \
|
||||
assert (old != NULL); \
|
||||
const M_C(name, _aligned_type_t) *oldx; \
|
||||
oldx = M_CTYPE_FROM_FIELD(M_C(name, _aligned_type_t), old, type, x); \
|
||||
assert (oldx >= snap->data); \
|
||||
assert (oldx < snap->data + snap->core->n+SNAPSHOTI_SPMC_EXTRA_BUFFER); \
|
||||
const unsigned int idx = oldx - snap->data; \
|
||||
snapshot_mrsw_int_read_end(snap->core, idx); \
|
||||
} \
|
||||
\
|
||||
static inline type *M_C(name, _get_write_buffer)(M_C(name, _t) snap) \
|
||||
{ \
|
||||
SNAPSHOTI_SPMC_CONTRACT(snap); \
|
||||
const unsigned int idx = snapshot_mrsw_int_get_write_idx(snap->core); \
|
||||
return &snap->data[idx].x; \
|
||||
} \
|
||||
\
|
||||
|
||||
//FIXME: Evaluate the needs for the methods _set_, _init_set.
|
||||
|
||||
|
||||
/******************************** INTERNAL MPMC **********************************/
|
||||
|
||||
// MPMC is built upon SPMC
|
||||
|
||||
// Defered evaluation (TBC if it really helps).
|
||||
#define SNAPSHOTI_MPMC_DEF(arg) SNAPSHOTI_MPMC_DEF2 arg
|
||||
|
||||
#define SNAPSHOTI_MPMC_DEF2(name, type, oplist) \
|
||||
\
|
||||
SNAPSHOTI_SPMC_DEF((M_C(name, _mrsw), type, oplist)) \
|
||||
\
|
||||
typedef struct M_C(name, _s) { \
|
||||
M_C(name, _mrsw_t) core; \
|
||||
} M_C(name, _t)[1]; \
|
||||
\
|
||||
typedef type M_C(name, _type_t); \
|
||||
\
|
||||
static inline void M_C(name, _init)(M_C(name, _t) snap, size_t nReader, size_t nWriter) \
|
||||
{ \
|
||||
M_C(name, _mrsw_init)(snap->core, nReader + nWriter -1 ); \
|
||||
unsigned int idx = snap->core->core->currentWrite; \
|
||||
snap->core->core->currentWrite = GENINT_ERROR; \
|
||||
snapshot_mrsw_int_write_end(snap->core->core, idx); \
|
||||
} \
|
||||
\
|
||||
static inline void M_C(name, _clear)(M_C(name, _t) snap) \
|
||||
{ \
|
||||
M_C(name, _mrsw_clear)(snap->core); \
|
||||
} \
|
||||
\
|
||||
static inline type *M_C(name, _write_start)(M_C(name, _t) snap) \
|
||||
{ \
|
||||
SNAPSHOTI_SPMC_CONTRACT(snap->core); \
|
||||
const unsigned int idx = snapshot_mrsw_int_write_start(snap->core->core); \
|
||||
return &snap->core->data[idx].x; \
|
||||
} \
|
||||
\
|
||||
static inline void M_C(name, _write_end)(M_C(name, _t) snap, type *old) \
|
||||
{ \
|
||||
SNAPSHOTI_SPMC_CONTRACT(snap->core); \
|
||||
const M_C(name, _mrsw_aligned_type_t) *oldx; \
|
||||
oldx = M_CTYPE_FROM_FIELD(M_C(name, _mrsw_aligned_type_t), old, type, x); \
|
||||
assert (oldx >= snap->core->data); \
|
||||
assert (oldx < snap->core->data + snap->core->core->n + SNAPSHOTI_SPMC_EXTRA_BUFFER); \
|
||||
const unsigned int idx = oldx - snap->core->data; \
|
||||
snapshot_mrsw_int_write_end(snap->core->core, idx); \
|
||||
} \
|
||||
\
|
||||
static inline type const *M_C(name, _read_start)(M_C(name, _t) snap) \
|
||||
{ \
|
||||
return M_C(name, _mrsw_read_start)(snap->core); \
|
||||
} \
|
||||
\
|
||||
static inline void M_C(name, _read_end)(M_C(name, _t) snap, type const *old) \
|
||||
{ \
|
||||
M_C(name, _mrsw_read_end)(snap->core, old); \
|
||||
} \
|
||||
\
|
||||
|
||||
//TODO: _set_, _init_set
|
||||
|
||||
#endif
|
||||
+588
-165
File diff suppressed because it is too large.
Load diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -20,8 +20,8 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __M_TUPLE_H
|
||||
#define __M_TUPLE_H
|
||||
#ifndef MSTARLIB_TUPLE_H
|
||||
#define MSTARLIB_TUPLE_H
|
||||
|
||||
#include "m-core.h"
|
||||
|
||||
@@ -29,47 +29,105 @@
|
||||
|
||||
/* Define the tuple type and functions.
|
||||
USAGE:
|
||||
TUPLE_DEF2(name, [(field1, type1, oplist1), (field2, type2, oplist2), ...] ) */
|
||||
#define TUPLE_DEF2(name, ...) \
|
||||
TUPLE_DEFINE_TYPE(name, __VA_ARGS__) \
|
||||
TUPLE_DEFINE_INIT(name, __VA_ARGS__) \
|
||||
TUPLE_DEFINE_INIT_SET(name, __VA_ARGS__) \
|
||||
TUPLE_DEFINE_INIT_SET2(name, __VA_ARGS__) \
|
||||
TUPLE_DEFINE_SET(name, __VA_ARGS__) \
|
||||
TUPLE_DEFINE_CLEAR(name, __VA_ARGS__) \
|
||||
TUPLE_DEFINE_GETTER(name, __VA_ARGS__) \
|
||||
TUPLE_DEFINE_SETTER(name, __VA_ARGS__) \
|
||||
M_IF(TUPLE_ALL_CMP(__VA_ARGS__)) \
|
||||
(TUPLE_DEFINE_CMP(name, __VA_ARGS__),) \
|
||||
M_IF(TUPLE_ALL_HASH(__VA_ARGS__)) \
|
||||
(TUPLE_DEFINE_HASH(name, __VA_ARGS__),) \
|
||||
M_IF(TUPLE_ALL_EQUAL(__VA_ARGS__)) \
|
||||
(TUPLE_DEFINE_EQUAL(name, __VA_ARGS__),) \
|
||||
M_IF(TUPLE_ALL_GET_STR(__VA_ARGS__)) \
|
||||
(TUPLE_DEFINE_GET_STR(name, __VA_ARGS__),) \
|
||||
M_IF(TUPLE_ALL_OUT_STR(__VA_ARGS__)) \
|
||||
(TUPLE_DEFINE_OUT_STR(name, __VA_ARGS__),) \
|
||||
M_IF(TUPLE_ALL_IN_STR(__VA_ARGS__)) \
|
||||
(TUPLE_DEFINE_IN_STR(name, __VA_ARGS__),) \
|
||||
M_IF(TUPLE_ALL_INIT_MOVE(__VA_ARGS__)) \
|
||||
(TUPLE_DEFINE_INIT_MOVE(name, __VA_ARGS__),) \
|
||||
M_IF(TUPLE_ALL_MOVE(__VA_ARGS__)) \
|
||||
(TUPLE_DEFINE_MOVE(name, __VA_ARGS__),) \
|
||||
M_IF(TUPLE_ALL_SWAP(__VA_ARGS__)) \
|
||||
(TUPLE_DEFINE_SWAP(name, __VA_ARGS__),)
|
||||
TUPLE_DEF2(name, [(field1, type1[, oplist1]), (field2, type2[, oplist2]), ...] ) */
|
||||
#define TUPLE_DEF2(name, ...) \
|
||||
TUPLEI_DEF2_A( (name, TUPLEI_INJECT_GLOBAL(__VA_ARGS__)) )
|
||||
|
||||
/* Define the oplist of a tuple.
|
||||
USAGE: TUPLE_OPLIST(name[, oplist of the first type, ...]) */
|
||||
#define TUPLE_OPLIST(...) \
|
||||
M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
(TUPLEI_OPLIST(__VA_ARGS__, () ), \
|
||||
(TUPLEI_OPLIST(__VA_ARGS__, M_DEFAULT_OPLIST ), \
|
||||
TUPLEI_OPLIST(__VA_ARGS__ ))
|
||||
|
||||
/* Return an array suitable for the _cmp_order function.
|
||||
As compound literals are not supported in C++,
|
||||
provide a separate definition for C++ using initializer_list
|
||||
(shall be constexpr, but only supported in C++14).
|
||||
*/
|
||||
#ifndef __cplusplus
|
||||
#define TUPLE_ORDER(name, ...) \
|
||||
( (const int[]) {M_MAP2_C(TUPLE_ORDER_CONVERT, name, __VA_ARGS__), 0})
|
||||
#else
|
||||
#include <initializer_list>
|
||||
namespace m_tuple {
|
||||
template <int N>
|
||||
struct m_int_array {
|
||||
int data[N];
|
||||
/*constexpr*/ inline m_int_array(std::initializer_list<int> init){
|
||||
int j = 0;
|
||||
for(auto i:init) {
|
||||
data[j++] = i;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
#define TUPLE_ORDER(name, ...) \
|
||||
(m_tuple::m_int_array<M_NARGS(__VA_ARGS__,0)>({M_MAP2_C(TUPLE_ORDER_CONVERT, name, __VA_ARGS__), 0}).data)
|
||||
#endif
|
||||
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
|
||||
#define TUPLE_GET_FIELD(f,t,o) f
|
||||
#define TUPLE_GET_TYPE(f,t,o) t
|
||||
#define TUPLEI_INJECT_GLOBAL(...) \
|
||||
M_MAP_C(TUPLEI_INJECT_OPLIST_A, __VA_ARGS__)
|
||||
|
||||
#define TUPLEI_INJECT_OPLIST_A( duo_or_trio ) \
|
||||
TUPLEI_INJECT_OPLIST_B duo_or_trio
|
||||
|
||||
#define TUPLEI_INJECT_OPLIST_B( f, ... ) \
|
||||
M_IF_NARGS_EQ1(__VA_ARGS__)( (f, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)()), (f, __VA_ARGS__) )
|
||||
|
||||
#define TUPLEI_DEF2_A(...) \
|
||||
TUPLEI_DEF2_B __VA_ARGS__
|
||||
|
||||
#define TUPLEI_DEF2_B(name, ...) \
|
||||
TUPLE_DEFINE_TYPE(name, __VA_ARGS__) \
|
||||
TUPLE_DEFINE_ENUM(name, __VA_ARGS__) \
|
||||
TUPLEI_IF_ALL(INIT, __VA_ARGS__) \
|
||||
(TUPLE_DEFINE_INIT(name, __VA_ARGS__),) \
|
||||
TUPLE_DEFINE_INIT_SET(name, __VA_ARGS__) \
|
||||
TUPLE_DEFINE_INIT_SET2(name, __VA_ARGS__) \
|
||||
TUPLE_DEFINE_SET(name, __VA_ARGS__) \
|
||||
TUPLE_DEFINE_SET2(name, __VA_ARGS__) \
|
||||
TUPLE_DEFINE_CLEAR(name, __VA_ARGS__) \
|
||||
TUPLE_DEFINE_GETTER_FIELD(name, __VA_ARGS__) \
|
||||
TUPLE_DEFINE_SETTER_FIELD(name, __VA_ARGS__) \
|
||||
TUPLEI_IF_ALL(CMP, __VA_ARGS__) \
|
||||
(TUPLE_DEFINE_CMP(name, __VA_ARGS__),) \
|
||||
TUPLEI_IF_ALL(CMP, __VA_ARGS__) \
|
||||
(TUPLE_DEFINE_CMP_ORDER(name, __VA_ARGS__),) \
|
||||
TUPLE_DEFINE_CMP_FIELD(name, __VA_ARGS__) \
|
||||
TUPLEI_IF_ALL(HASH, __VA_ARGS__) \
|
||||
(TUPLE_DEFINE_HASH(name, __VA_ARGS__),) \
|
||||
TUPLEI_IF_ALL(EQUAL, __VA_ARGS__) \
|
||||
(TUPLE_DEFINE_EQUAL(name, __VA_ARGS__),) \
|
||||
TUPLEI_IF_ALL(GET_STR, __VA_ARGS__) \
|
||||
(TUPLE_DEFINE_GET_STR(name, __VA_ARGS__),) \
|
||||
TUPLEI_IF_ALL(OUT_STR, __VA_ARGS__) \
|
||||
(TUPLE_DEFINE_OUT_STR(name, __VA_ARGS__),) \
|
||||
TUPLEI_IF_ALL(IN_STR, __VA_ARGS__) \
|
||||
(TUPLE_DEFINE_IN_STR(name, __VA_ARGS__),) \
|
||||
TUPLEI_IF_ALL(PARSE_STR, __VA_ARGS__) \
|
||||
(TUPLE_DEFINE_PARSE_STR(name, __VA_ARGS__),) \
|
||||
TUPLEI_IF_ALL(INIT_MOVE, __VA_ARGS__) \
|
||||
(TUPLE_DEFINE_INIT_MOVE(name, __VA_ARGS__),) \
|
||||
TUPLEI_IF_ALL(MOVE, __VA_ARGS__) \
|
||||
(TUPLE_DEFINE_MOVE(name, __VA_ARGS__),) \
|
||||
TUPLEI_IF_ALL(SWAP, __VA_ARGS__) \
|
||||
(TUPLE_DEFINE_SWAP(name, __VA_ARGS__),) \
|
||||
TUPLEI_IF_ALL(CLEAN, __VA_ARGS__) \
|
||||
(TUPLE_DEFINE_CLEAN(name, __VA_ARGS__),)
|
||||
|
||||
#define TUPLE_ORDER_CONVERT(name, x) M_C(name, M_C(TUPLE_ORDER_CONVERT_, x))
|
||||
#define TUPLE_ORDER_CONVERT_ASC(x) M_C3(_,x,_value)
|
||||
#define TUPLE_ORDER_CONVERT_DSC(x) M_C3(_,x,_value)*-1
|
||||
|
||||
/* Get the field name, the type, the oplist or the methods
|
||||
based on the tuple (field, type, oplist) */
|
||||
#define TUPLE_GET_FIELD(f,t,o) f
|
||||
#define TUPLE_GET_TYPE(f,t,o) t
|
||||
#define TUPLE_GET_OPLIST(f,t,o) o
|
||||
#define TUPLE_GET_INIT(f,t,o) M_GET_INIT o
|
||||
#define TUPLE_GET_INIT_SET(f,t,o) M_GET_INIT_SET o
|
||||
#define TUPLE_GET_INIT_MOVE(f,t,o) M_GET_INIT_MOVE o
|
||||
@@ -82,8 +140,25 @@
|
||||
#define TUPLE_GET_STR(f,t,o) M_GET_GET_STR o
|
||||
#define TUPLE_GET_OUT_STR(f,t,o) M_GET_OUT_STR o
|
||||
#define TUPLE_GET_IN_STR(f,t,o) M_GET_IN_STR o
|
||||
#define TUPLE_GET_PARSE_STR(f,t,o) M_GET_PARSE_STR o
|
||||
#define TUPLE_GET_SWAP(f,t,o) M_GET_SWAP o
|
||||
#define TUPLE_GET_CLEAN(f,t,o) M_GET_CLEAN o
|
||||
|
||||
#define TUPLE_CALL_INIT(t, ...) M_APPLY_API(TUPLE_GET_INIT t, TUPLE_GET_OPLIST t, __VA_ARGS__)
|
||||
#define TUPLE_CALL_INIT_SET(t, ...) M_APPLY_API(TUPLE_GET_INIT_SET t, TUPLE_GET_OPLIST t, __VA_ARGS__)
|
||||
#define TUPLE_CALL_INIT_MOVE(t, ...) M_APPLY_API(TUPLE_GET_INIT_MOVE t, TUPLE_GET_OPLIST t, __VA_ARGS__)
|
||||
#define TUPLE_CALL_MOVE(t, ...) M_APPLY_API(TUPLE_GET_MOVE t, TUPLE_GET_OPLIST t, __VA_ARGS__)
|
||||
#define TUPLE_CALL_SET(t, ...) M_APPLY_API(TUPLE_GET_SET t, TUPLE_GET_OPLIST t, __VA_ARGS__)
|
||||
#define TUPLE_CALL_CLEAR(t, ...) M_APPLY_API(TUPLE_GET_CLEAR t, TUPLE_GET_OPLIST t, __VA_ARGS__)
|
||||
#define TUPLE_CALL_CMP(t, ...) M_APPLY_API(TUPLE_GET_CMP t, TUPLE_GET_OPLIST t, __VA_ARGS__)
|
||||
#define TUPLE_CALL_HASH(t, ...) M_APPLY_API(TUPLE_GET_HASH t, TUPLE_GET_OPLIST t, __VA_ARGS__)
|
||||
#define TUPLE_CALL_EQUAL(t, ...) M_APPLY_API(TUPLE_GET_EQUAL t, TUPLE_GET_OPLIST t, __VA_ARGS__)
|
||||
#define TUPLE_CALL_GET_STR(t, ...) M_APPLY_API(TUPLE_GET_STR t, TUPLE_GET_OPLIST t, __VA_ARGS__)
|
||||
#define TUPLE_CALL_OUT_STR(t, ...) M_APPLY_API(TUPLE_GET_OUT_STR t, TUPLE_GET_OPLIST t, __VA_ARGS__)
|
||||
#define TUPLE_CALL_IN_STR(t, ...) M_APPLY_API(TUPLE_GET_IN_STR t, TUPLE_GET_OPLIST t, __VA_ARGS__)
|
||||
#define TUPLE_CALL_PARSE_STR(t, ...) M_APPLY_API(TUPLE_GET_PARSE_STR t, TUPLE_GET_OPLIST t, __VA_ARGS__)
|
||||
#define TUPLE_CALL_SWAP(t, ...) M_APPLY_API(TUPLE_GET_SWAP t, TUPLE_GET_OPLIST t, __VA_ARGS__)
|
||||
#define TUPLE_CALL_CLEAN(t, ...) M_APPLY_API(TUPLE_GET_CLEAN t, TUPLE_GET_OPLIST t, __VA_ARGS__)
|
||||
|
||||
#define TUPLE_DEFINE_TYPE(name, ...) \
|
||||
typedef struct M_C(name, _s) { \
|
||||
@@ -95,12 +170,21 @@
|
||||
TUPLE_GET_TYPE a TUPLE_GET_FIELD a ;
|
||||
|
||||
|
||||
#define TUPLE_DEFINE_ENUM(name, ...) \
|
||||
typedef enum { \
|
||||
M_C(name, _first_one_val), \
|
||||
M_MAP2_C(TUPLE_DEFINE_ENUM_ELE , name, __VA_ARGS__) \
|
||||
} M_C(name,_field_e);
|
||||
#define TUPLE_DEFINE_ENUM_ELE(name, a) \
|
||||
M_C4(name, _, TUPLE_GET_FIELD a, _value)
|
||||
|
||||
|
||||
#define TUPLE_DEFINE_INIT(name, ...) \
|
||||
static inline void M_C(name, _init)(M_C(name,_t) my) { \
|
||||
M_MAP(TUPLE_DEFINE_INIT_FUNC , __VA_ARGS__) \
|
||||
}
|
||||
#define TUPLE_DEFINE_INIT_FUNC(a) \
|
||||
TUPLE_GET_INIT a ( my -> TUPLE_GET_FIELD a );
|
||||
TUPLE_CALL_INIT(a, my -> TUPLE_GET_FIELD a );
|
||||
|
||||
|
||||
#define TUPLE_DEFINE_INIT_SET(name, ...) \
|
||||
@@ -108,7 +192,7 @@
|
||||
M_MAP(TUPLE_DEFINE_INIT_SET_FUNC , __VA_ARGS__) \
|
||||
}
|
||||
#define TUPLE_DEFINE_INIT_SET_FUNC(a) \
|
||||
TUPLE_GET_INIT_SET a ( my -> TUPLE_GET_FIELD a , org -> TUPLE_GET_FIELD a );
|
||||
TUPLE_CALL_INIT_SET(a, my -> TUPLE_GET_FIELD a , org -> TUPLE_GET_FIELD a );
|
||||
|
||||
|
||||
#define TUPLE_DEFINE_INIT_SET2(name, ...) \
|
||||
@@ -120,7 +204,7 @@
|
||||
#define TUPLE_DEFINE_INIT_SET2_PROTO(a) \
|
||||
, TUPLE_GET_TYPE a const TUPLE_GET_FIELD a
|
||||
#define TUPLE_DEFINE_INIT_SET2_FUNC(a) \
|
||||
TUPLE_GET_INIT_SET a ( my -> TUPLE_GET_FIELD a , TUPLE_GET_FIELD a );
|
||||
TUPLE_CALL_INIT_SET(a, my -> TUPLE_GET_FIELD a , TUPLE_GET_FIELD a );
|
||||
|
||||
|
||||
#define TUPLE_DEFINE_SET(name, ...) \
|
||||
@@ -129,7 +213,19 @@
|
||||
M_MAP(TUPLE_DEFINE_SET_FUNC , __VA_ARGS__) \
|
||||
}
|
||||
#define TUPLE_DEFINE_SET_FUNC(a) \
|
||||
TUPLE_GET_SET a ( my -> TUPLE_GET_FIELD a , org -> TUPLE_GET_FIELD a );
|
||||
TUPLE_CALL_SET(a, my -> TUPLE_GET_FIELD a , org -> TUPLE_GET_FIELD a );
|
||||
|
||||
|
||||
#define TUPLE_DEFINE_SET2(name, ...) \
|
||||
static inline void M_C(name, _set2)(M_C(name,_t) my \
|
||||
M_MAP(TUPLE_DEFINE_SET2_PROTO, __VA_ARGS__) \
|
||||
) { \
|
||||
M_MAP(TUPLE_DEFINE_SET2_FUNC , __VA_ARGS__) \
|
||||
}
|
||||
#define TUPLE_DEFINE_SET2_PROTO(a) \
|
||||
, TUPLE_GET_TYPE a const TUPLE_GET_FIELD a
|
||||
#define TUPLE_DEFINE_SET2_FUNC(a) \
|
||||
TUPLE_CALL_SET(a, my -> TUPLE_GET_FIELD a , TUPLE_GET_FIELD a );
|
||||
|
||||
|
||||
#define TUPLE_DEFINE_CLEAR(name, ...) \
|
||||
@@ -137,23 +233,26 @@
|
||||
M_MAP(TUPLE_DEFINE_CLEAR_FUNC , __VA_ARGS__) \
|
||||
}
|
||||
#define TUPLE_DEFINE_CLEAR_FUNC(a) \
|
||||
TUPLE_GET_CLEAR a ( my -> TUPLE_GET_FIELD a );
|
||||
TUPLE_CALL_CLEAR(a, my -> TUPLE_GET_FIELD a );
|
||||
|
||||
|
||||
#define TUPLE_DEFINE_GETTER(name, ...) \
|
||||
M_MAP2(TUPLE_DEFINE_GETTER_PROTO, name, __VA_ARGS__)
|
||||
#define TUPLE_DEFINE_GETTER_PROTO(name, a) \
|
||||
static inline TUPLE_GET_TYPE a const * M_C3(name, _get_, TUPLE_GET_FIELD a) \
|
||||
(M_C(name,_t) const my) { \
|
||||
return (TUPLE_GET_TYPE a const * ) &(my->TUPLE_GET_FIELD a);}
|
||||
#define TUPLE_DEFINE_GETTER_FIELD(name, ...) \
|
||||
M_MAP2(TUPLE_DEFINE_GETTER_FIELD_PROTO, name, __VA_ARGS__)
|
||||
#define TUPLE_DEFINE_GETTER_FIELD_PROTO(name, a) \
|
||||
static inline TUPLE_GET_TYPE a * M_C3(name, _get_at_, TUPLE_GET_FIELD a) \
|
||||
(M_C(name,_t) my) { \
|
||||
return &(my->TUPLE_GET_FIELD a);} \
|
||||
static inline TUPLE_GET_TYPE a const * M_C3(name, _cget_at_, TUPLE_GET_FIELD a) \
|
||||
(M_C(name,_t) const my) { \
|
||||
return &(my->TUPLE_GET_FIELD a);}
|
||||
|
||||
|
||||
#define TUPLE_DEFINE_SETTER(name, ...) \
|
||||
M_MAP2(TUPLE_DEFINE_SETTER_PROTO, name, __VA_ARGS__)
|
||||
#define TUPLE_DEFINE_SETTER_PROTO(name, a) \
|
||||
#define TUPLE_DEFINE_SETTER_FIELD(name, ...) \
|
||||
M_MAP2(TUPLE_DEFINE_SETTER_FIELD_PROTO, name, __VA_ARGS__)
|
||||
#define TUPLE_DEFINE_SETTER_FIELD_PROTO(name, a) \
|
||||
static inline void M_C3(name, _set_, TUPLE_GET_FIELD a) \
|
||||
(M_C(name,_t) my, TUPLE_GET_TYPE a TUPLE_GET_FIELD a) { \
|
||||
TUPLE_GET_SET a (my ->TUPLE_GET_FIELD a, TUPLE_GET_FIELD a);}
|
||||
(M_C(name,_t) my, TUPLE_GET_TYPE a const TUPLE_GET_FIELD a) { \
|
||||
TUPLE_CALL_SET(a, my ->TUPLE_GET_FIELD a, TUPLE_GET_FIELD a);}
|
||||
|
||||
|
||||
#define TUPLE_DEFINE_CMP(name, ...) \
|
||||
@@ -164,10 +263,44 @@
|
||||
return 0; \
|
||||
}
|
||||
#define TUPLE_DEFINE_CMP_FUNC(a) \
|
||||
i = TUPLE_GET_CMP a ( e1 -> TUPLE_GET_FIELD a , e2 -> TUPLE_GET_FIELD a );\
|
||||
i = TUPLE_CALL_CMP(a, e1 -> TUPLE_GET_FIELD a , e2 -> TUPLE_GET_FIELD a ); \
|
||||
if (i != 0) return i;
|
||||
|
||||
|
||||
#define TUPLE_DEFINE_CMP_ORDER(name, ...) \
|
||||
static inline int M_C(name, _cmp_order)(M_C(name,_t) const e1 , \
|
||||
M_C(name,_t) const e2, \
|
||||
const int order[]) { \
|
||||
int i, r; \
|
||||
while ((i=*order++) != 0) { \
|
||||
switch (i) { \
|
||||
M_MAP2(TUPLE_DEFINE_CMP_ORDER_FUNC , name, __VA_ARGS__) \
|
||||
default: assert(0); \
|
||||
} \
|
||||
} \
|
||||
return 0; \
|
||||
}
|
||||
#define TUPLE_DEFINE_CMP_ORDER_FUNC(name, a) \
|
||||
case M_C4(name, _, TUPLE_GET_FIELD a, _value): \
|
||||
case -M_C4(name, _, TUPLE_GET_FIELD a, _value): \
|
||||
r = TUPLE_CALL_CMP(a, e1 -> TUPLE_GET_FIELD a , e2 -> TUPLE_GET_FIELD a ); \
|
||||
if (r != 0) return i < 0 ? -r : r; \
|
||||
break;
|
||||
|
||||
|
||||
#define TUPLE_DEFINE_CMP_FIELD(name, ...) \
|
||||
M_MAP2(TUPLE_MAP_CMP_FIELD, name, __VA_ARGS__)
|
||||
#define TUPLE_MAP_CMP_FIELD(name, a) \
|
||||
M_IF_METHOD(CMP, TUPLE_GET_OPLIST a)( \
|
||||
TUPLE_DEFINE_CMP_FIELD_FUNC(name, TUPLE_GET_FIELD a, TUPLE_GET_CMP a), \
|
||||
)
|
||||
#define TUPLE_DEFINE_CMP_FIELD_FUNC(name, field, func_cmp) \
|
||||
static inline int M_C3(name, _cmp_, field)(M_C(name,_t) const e1 , \
|
||||
M_C(name,_t) const e2) { \
|
||||
return func_cmp ( e1 -> field , e2 -> field ); \
|
||||
}
|
||||
|
||||
|
||||
#define TUPLE_DEFINE_EQUAL(name, ...) \
|
||||
static inline bool M_C(name, _equal_p)(M_C(name,_t) const e1 , \
|
||||
M_C(name,_t) const e2) { \
|
||||
@@ -176,7 +309,7 @@
|
||||
return true; \
|
||||
}
|
||||
#define TUPLE_DEFINE_EQUAL_FUNC(a) \
|
||||
b = TUPLE_GET_EQUAL a ( e1 -> TUPLE_GET_FIELD a , e2 -> TUPLE_GET_FIELD a ); \
|
||||
b = TUPLE_CALL_EQUAL(a, e1 -> TUPLE_GET_FIELD a , e2 -> TUPLE_GET_FIELD a ); \
|
||||
if (!b) return false;
|
||||
|
||||
|
||||
@@ -187,7 +320,7 @@
|
||||
return M_HASH_FINAL (hash); \
|
||||
}
|
||||
#define TUPLE_DEFINE_HASH_FUNC(a) \
|
||||
M_HASH_UP(hash, TUPLE_GET_HASH a ( e1 -> TUPLE_GET_FIELD a) );
|
||||
M_HASH_UP(hash, TUPLE_CALL_HASH(a, e1 -> TUPLE_GET_FIELD a) );
|
||||
|
||||
|
||||
#define TUPLE_DEFINE_GET_STR(name, ...) \
|
||||
@@ -203,7 +336,7 @@
|
||||
#define TUPLE_DEFINE_GET_STR_FUNC(a) \
|
||||
if (comma) string_push_back (str, ','); \
|
||||
comma = true; \
|
||||
TUPLE_GET_STR a (str, el -> TUPLE_GET_FIELD a, true); \
|
||||
TUPLE_CALL_GET_STR(a, str, el -> TUPLE_GET_FIELD a, true); \
|
||||
|
||||
|
||||
#define TUPLE_DEFINE_OUT_STR(name, ...) \
|
||||
@@ -218,7 +351,7 @@
|
||||
#define TUPLE_DEFINE_OUT_STR_FUNC(a) \
|
||||
if (comma) fputc (',', f); \
|
||||
comma = true; \
|
||||
TUPLE_GET_OUT_STR a (f, el -> TUPLE_GET_FIELD a); \
|
||||
TUPLE_CALL_OUT_STR(a, f, el -> TUPLE_GET_FIELD a); \
|
||||
|
||||
|
||||
#define TUPLE_DEFINE_IN_STR(name, ...) \
|
||||
@@ -237,16 +370,42 @@
|
||||
if (c != ',' || c == EOF) return false; \
|
||||
} \
|
||||
comma = true; \
|
||||
if (TUPLE_GET_IN_STR a (el -> TUPLE_GET_FIELD a, f) == false) \
|
||||
if (TUPLE_CALL_IN_STR(a, el -> TUPLE_GET_FIELD a, f) == false) \
|
||||
return false ; \
|
||||
|
||||
|
||||
#define TUPLE_DEFINE_PARSE_STR(name, ...) \
|
||||
static inline bool M_C(name, _parse_str)(M_C(name,_t) el, \
|
||||
const char str[], \
|
||||
const char **endptr) { \
|
||||
assert (str != NULL && el != NULL); \
|
||||
bool success = false; \
|
||||
bool comma = false; \
|
||||
int c = *str++; \
|
||||
if (c != '(') goto exit; \
|
||||
M_MAP(TUPLE_DEFINE_PARSE_STR_FUNC , __VA_ARGS__) \
|
||||
c = *str++; \
|
||||
success = (c == ')'); \
|
||||
exit: \
|
||||
if (endptr) *endptr = str; \
|
||||
return success; \
|
||||
}
|
||||
#define TUPLE_DEFINE_PARSE_STR_FUNC(a) \
|
||||
if (comma) { \
|
||||
c = *str++; \
|
||||
if (c != ',' || c == 0) goto exit; \
|
||||
} \
|
||||
comma = true; \
|
||||
if (TUPLE_CALL_PARSE_STR(a, el -> TUPLE_GET_FIELD a, str, &str) == false) \
|
||||
goto exit ; \
|
||||
|
||||
|
||||
#define TUPLE_DEFINE_INIT_MOVE(name, ...) \
|
||||
static inline void M_C(name, _init_move)(M_C(name,_t) el, M_C(name,_t) org) { \
|
||||
M_MAP(TUPLE_DEFINE_INIT_MOVE_FUNC , __VA_ARGS__) \
|
||||
}
|
||||
#define TUPLE_DEFINE_INIT_MOVE_FUNC(a) \
|
||||
TUPLE_GET_INIT_MOVE a (el -> TUPLE_GET_FIELD a, org -> TUPLE_GET_FIELD a);
|
||||
TUPLE_CALL_INIT_MOVE(a, el -> TUPLE_GET_FIELD a, org -> TUPLE_GET_FIELD a);
|
||||
|
||||
|
||||
#define TUPLE_DEFINE_MOVE(name, ...) \
|
||||
@@ -254,7 +413,7 @@
|
||||
M_MAP(TUPLE_DEFINE_MOVE_FUNC , __VA_ARGS__) \
|
||||
}
|
||||
#define TUPLE_DEFINE_MOVE_FUNC(a) \
|
||||
TUPLE_GET_MOVE a (el -> TUPLE_GET_FIELD a, org -> TUPLE_GET_FIELD a);
|
||||
TUPLE_CALL_MOVE(a, el -> TUPLE_GET_FIELD a, org -> TUPLE_GET_FIELD a);
|
||||
|
||||
|
||||
#define TUPLE_DEFINE_SWAP(name, ...) \
|
||||
@@ -262,7 +421,16 @@
|
||||
M_MAP(TUPLE_DEFINE_SWAP_FUNC , __VA_ARGS__) \
|
||||
}
|
||||
#define TUPLE_DEFINE_SWAP_FUNC(a) \
|
||||
TUPLE_GET_SWAP a (el1 -> TUPLE_GET_FIELD a, el2 -> TUPLE_GET_FIELD a);
|
||||
TUPLE_CALL_SWAP(a, el1 -> TUPLE_GET_FIELD a, el2 -> TUPLE_GET_FIELD a);
|
||||
|
||||
|
||||
#define TUPLE_DEFINE_CLEAN(name, ...) \
|
||||
static inline void M_C(name, _clean)(M_C(name,_t) el1) { \
|
||||
M_MAP(TUPLE_DEFINE_CLEAN_FUNC , __VA_ARGS__) \
|
||||
}
|
||||
#define TUPLE_DEFINE_CLEAN_FUNC(a) \
|
||||
TUPLE_CALL_CLEAN(a, el1 -> TUPLE_GET_FIELD a);
|
||||
|
||||
|
||||
/* Macros for testing for method presence */
|
||||
#define TUPLE_TEST_METHOD2_P(method, f, t, op) \
|
||||
@@ -270,28 +438,13 @@
|
||||
#define TUPLE_TEST_METHOD_P(method, trio) \
|
||||
M_APPLY(TUPLE_TEST_METHOD2_P, method, M_OPFLAT trio)
|
||||
|
||||
#define TUPLE_ALL_CMP(...) \
|
||||
M_REDUCE2(TUPLE_TEST_METHOD_P, M_AND, CMP, __VA_ARGS__)
|
||||
#define TUPLE_ALL_EQUAL(...) \
|
||||
M_REDUCE2(TUPLE_TEST_METHOD_P, M_AND, EQUAL, __VA_ARGS__)
|
||||
#define TUPLE_ALL_HASH(...) \
|
||||
M_REDUCE2(TUPLE_TEST_METHOD_P, M_AND, HASH, __VA_ARGS__)
|
||||
#define TUPLE_ALL_GET_STR(...) \
|
||||
M_REDUCE2(TUPLE_TEST_METHOD_P, M_AND, GET_STR, __VA_ARGS__)
|
||||
#define TUPLE_ALL_OUT_STR(...) \
|
||||
M_REDUCE2(TUPLE_TEST_METHOD_P, M_AND, OUT_STR, __VA_ARGS__)
|
||||
#define TUPLE_ALL_IN_STR(...) \
|
||||
M_REDUCE2(TUPLE_TEST_METHOD_P, M_AND, IN_STR, __VA_ARGS__)
|
||||
#define TUPLE_ALL_INIT_MOVE(...) \
|
||||
M_REDUCE2(TUPLE_TEST_METHOD_P, M_AND, INIT_MOVE, __VA_ARGS__)
|
||||
#define TUPLE_ALL_MOVE(...) \
|
||||
M_REDUCE2(TUPLE_TEST_METHOD_P, M_AND, MOVE, __VA_ARGS__)
|
||||
#define TUPLE_ALL_SWAP(...) \
|
||||
M_REDUCE2(TUPLE_TEST_METHOD_P, M_AND, SWAP, __VA_ARGS__)
|
||||
#define TUPLEI_IF_ALL(method, ...) \
|
||||
M_IF(M_REDUCE2(TUPLE_TEST_METHOD_P, M_AND, method, __VA_ARGS__))
|
||||
|
||||
#define TUPLEI_OPLIST(name, ...) \
|
||||
(INIT(M_C(name,_init)), \
|
||||
(M_IF_METHOD_ALL(INIT, __VA_ARGS__)(INIT(M_C(name,_init)),), \
|
||||
INIT_SET(M_C(name, _init_set)), \
|
||||
INIT_WITH(M_C(name, _init_set2)), \
|
||||
SET(M_C(name,_set)), \
|
||||
CLEAR(M_C(name, _clear)), \
|
||||
TYPE(M_C(name,_t)), \
|
||||
@@ -299,11 +452,13 @@
|
||||
M_IF_METHOD_ALL(HASH, __VA_ARGS__)(HASH(M_C(name, _hash)),), \
|
||||
M_IF_METHOD_ALL(EQUAL, __VA_ARGS__)(EQUAL(M_C(name, _equal_p)),), \
|
||||
M_IF_METHOD_ALL(GET_STR, __VA_ARGS__)(GET_STR(M_C(name, _get_str)),), \
|
||||
M_IF_METHOD_ALL(PARSE_STR, __VA_ARGS__)(PARSE_STR(M_C(name, _parse_str)),), \
|
||||
M_IF_METHOD_ALL(IN_STR, __VA_ARGS__)(IN_STR(M_C(name, _in_str)),), \
|
||||
M_IF_METHOD_ALL(OUT_STR, __VA_ARGS__)(OUT_STR(M_C(name, _out_str)),), \
|
||||
M_IF_METHOD_ALL(INIT_MOVE, __VA_ARGS__)(INIT_MOVE(M_C(name, _init_move)),), \
|
||||
M_IF_METHOD_ALL(MOVE, __VA_ARGS__)(MOVE(M_C(name, _move)),), \
|
||||
M_IF_METHOD_ALL(SWAP, __VA_ARGS__)(MOVE(M_C(name, _swap)),), \
|
||||
M_IF_METHOD_ALL(MOVE, __VA_ARGS__)(MOVE(M_C(name, _move)),), \
|
||||
M_IF_METHOD_ALL(SWAP, __VA_ARGS__)(SWAP(M_C(name, _swap)),), \
|
||||
M_IF_METHOD_ALL(CLEAN, __VA_ARGS__)(CLEAN(M_C(name, _clean)),), \
|
||||
M_IF_METHOD(NEW, M_RET_ARG1(__VA_ARGS__,))(NEW(M_DELAY2(M_GET_NEW) M_RET_ARG1(__VA_ARGS__,)),), \
|
||||
M_IF_METHOD(REALLOC, M_RET_ARG1(__VA_ARGS__,))(REALLOC(M_DELAY2(M_GET_REALLOC) M_RET_ARG1(__VA_ARGS__,)),), \
|
||||
M_IF_METHOD(DEL, M_RET_ARG1(__VA_ARGS__,))(DEL(M_DELAY2(M_GET_DEL) M_RET_ARG1(__VA_ARGS__,)),), \
|
||||
|
||||
+211
-105
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - VARIANT module
|
||||
*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -22,8 +22,8 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __M_VARIANT_H
|
||||
#define __M_VARIANT_H
|
||||
#ifndef MSTARLIB_VARIANT_H
|
||||
#define MSTARLIB_VARIANT_H
|
||||
|
||||
#include "m-core.h"
|
||||
|
||||
@@ -33,60 +33,99 @@
|
||||
/* Define the variant type and functions.
|
||||
USAGE:
|
||||
VARIANT_DEF2(name, [(field1, type1, oplist1), (field2, type2, oplist2), ...] ) */
|
||||
#define VARIANT_DEF2(name, ...) \
|
||||
#define VARIANT_DEF2(name, ...) \
|
||||
VARIANTI_DEF2_A( (name, VARIANTI_INJECT_GLOBAL(__VA_ARGS__)) )
|
||||
|
||||
/* Define the oplist of a variant.
|
||||
USAGE: VARIANT_OPLIST(name[, oplist of the first type, ...]) */
|
||||
#define VARIANT_OPLIST(...) \
|
||||
M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
(VARIANTI_OPLIST(__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
VARIANTI_OPLIST(__VA_ARGS__ ))
|
||||
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
|
||||
#define VARIANTI_INJECT_GLOBAL(...) \
|
||||
M_MAP_C(VARIANTI_INJECT_OPLIST_A, __VA_ARGS__)
|
||||
|
||||
#define VARIANTI_INJECT_OPLIST_A( duo_or_trio ) \
|
||||
VARIANTI_INJECT_OPLIST_B duo_or_trio
|
||||
|
||||
#define VARIANTI_INJECT_OPLIST_B( f, ... ) \
|
||||
M_IF_NARGS_EQ1(__VA_ARGS__)( (f, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)()), (f, __VA_ARGS__) )
|
||||
|
||||
#define VARIANTI_DEF2_A(...) \
|
||||
VARIANTI_DEF2_B __VA_ARGS__
|
||||
|
||||
#define VARIANTI_DEF2_B(name, ...) \
|
||||
VARIANTI_DEFINE_TYPE(name, __VA_ARGS__) \
|
||||
VARIANTI_DEFINE_INIT(name, __VA_ARGS__) \
|
||||
VARIANTI_DEFINE_CLEAR(name, __VA_ARGS__) \
|
||||
VARIANTI_DEFINE_INIT_SET(name, __VA_ARGS__) \
|
||||
VARIANTI_DEFINE_SET(name, __VA_ARGS__) \
|
||||
VARIANTI_DEFINE_TEST_P(name, __VA_ARGS__) \
|
||||
VARIANTI_DEFINE_SETTER(name, __VA_ARGS__) \
|
||||
VARIANTI_DEFINE_GETTER(name, __VA_ARGS__) \
|
||||
VARIANTI_IF_ALL(INIT, __VA_ARGS__) \
|
||||
(VARIANTI_DEFINE_INIT_FIELD(name, __VA_ARGS__),) \
|
||||
VARIANTI_DEFINE_INIT_SETTER_FIELD(name, __VA_ARGS__) \
|
||||
VARIANTI_DEFINE_SETTER_FIELD(name, __VA_ARGS__) \
|
||||
VARIANTI_DEFINE_GETTER_FIELD(name, __VA_ARGS__) \
|
||||
VARIANTI_DEFINE_CLEAN_FUNC(name, __VA_ARGS__) \
|
||||
M_IF(VARIANTI_ALL_HASH(__VA_ARGS__)) \
|
||||
VARIANTI_IF_ALL(HASH, __VA_ARGS__) \
|
||||
(VARIANTI_DEFINE_HASH(name, __VA_ARGS__),) \
|
||||
M_IF(VARIANTI_ALL_EQUAL(__VA_ARGS__)) \
|
||||
VARIANTI_IF_ALL(EQUAL, __VA_ARGS__) \
|
||||
(VARIANTI_DEFINE_EQUAL(name, __VA_ARGS__),) \
|
||||
M_IF(VARIANTI_ALL_GET_STR(__VA_ARGS__)) \
|
||||
VARIANTI_IF_ALL(GET_STR, __VA_ARGS__) \
|
||||
(VARIANTI_DEFINE_GET_STR(name, __VA_ARGS__),) \
|
||||
M_IF(VARIANTI_ALL_OUT_STR(__VA_ARGS__)) \
|
||||
VARIANTI_IF_ALL2(PARSE_STR, INIT, __VA_ARGS__) \
|
||||
(VARIANTI_DEFINE_PARSE_STR(name, __VA_ARGS__),) \
|
||||
VARIANTI_IF_ALL(OUT_STR, __VA_ARGS__) \
|
||||
(VARIANTI_DEFINE_OUT_STR(name, __VA_ARGS__),) \
|
||||
M_IF(VARIANTI_ALL_IN_STR(__VA_ARGS__)) \
|
||||
VARIANTI_IF_ALL2(IN_STR, INIT, __VA_ARGS__) \
|
||||
(VARIANTI_DEFINE_IN_STR(name, __VA_ARGS__),) \
|
||||
M_IF(VARIANTI_ALL_INIT_MOVE(__VA_ARGS__)) \
|
||||
VARIANTI_IF_ALL(INIT_MOVE, __VA_ARGS__) \
|
||||
(VARIANTI_DEFINE_INIT_MOVE(name, __VA_ARGS__),) \
|
||||
M_IF(VARIANTI_ALL_INIT_MOVE(__VA_ARGS__)) \
|
||||
VARIANTI_IF_ALL(INIT_MOVE, __VA_ARGS__) \
|
||||
(VARIANTI_DEFINE_MOVE(name, __VA_ARGS__),) \
|
||||
M_IF(VARIANTI_ALL_INIT_MOVE(__VA_ARGS__)) \
|
||||
VARIANTI_IF_ALL(INIT_MOVE, __VA_ARGS__) \
|
||||
(VARIANTI_DEFINE_MOVER(name, __VA_ARGS__),) \
|
||||
M_IF(VARIANTI_ALL_SWAP(__VA_ARGS__)) \
|
||||
VARIANTI_IF_ALL(SWAP, __VA_ARGS__) \
|
||||
(VARIANTI_DEFINE_SWAP(name, __VA_ARGS__),)
|
||||
|
||||
/* Define the oplist of a tuple.
|
||||
USAGE: VARIANT_OPLIST(name[, oplist of the first type, ...]) */
|
||||
#define VARIANT_OPLIST(...) \
|
||||
M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
(VARIANTI_OPLIST(__VA_ARGS__, () ), \
|
||||
VARIANTI_OPLIST(__VA_ARGS__ ))
|
||||
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
|
||||
#define VARIANTI_GET_FIELD(f,t,o) f
|
||||
#define VARIANTI_GET_TYPE(f,t,o) t
|
||||
#define VARIANTI_GET_INIT(f,t,o) M_GET_INIT o
|
||||
#define VARIANTI_GET_INIT_SET(f,t,o) M_GET_INIT_SET o
|
||||
/* Get the field name, the type, the oplist or the methods
|
||||
based on the variant (field, type, oplist) */
|
||||
#define VARIANTI_GET_FIELD(f,t,o) f
|
||||
#define VARIANTI_GET_TYPE(f,t,o) t
|
||||
#define VARIANTI_GET_OPLIST(f,t,o) o
|
||||
#define VARIANTI_GET_INIT(f,t,o) M_GET_INIT o
|
||||
#define VARIANTI_GET_INIT_SET(f,t,o) M_GET_INIT_SET o
|
||||
#define VARIANTI_GET_INIT_MOVE(f,t,o) M_GET_INIT_MOVE o
|
||||
#define VARIANTI_GET_MOVE(f,t,o) M_GET_MOVE o
|
||||
#define VARIANTI_GET_SET(f,t,o) M_GET_SET o
|
||||
#define VARIANTI_GET_CLEAR(f,t,o) M_GET_CLEAR o
|
||||
#define VARIANTI_GET_CMP(f,t,o) M_GET_CMP o
|
||||
#define VARIANTI_GET_HASH(f,t,o) M_GET_HASH o
|
||||
#define VARIANTI_GET_EQUAL(f,t,o) M_GET_EQUAL o
|
||||
#define VARIANTI_GET_STR(f,t,o) M_GET_GET_STR o
|
||||
#define VARIANTI_GET_OUT_STR(f,t,o) M_GET_OUT_STR o
|
||||
#define VARIANTI_GET_IN_STR(f,t,o) M_GET_IN_STR o
|
||||
#define VARIANTI_GET_SWAP(f,t,o) M_GET_SWAP o
|
||||
#define VARIANTI_GET_MOVE(f,t,o) M_GET_MOVE o
|
||||
#define VARIANTI_GET_SET(f,t,o) M_GET_SET o
|
||||
#define VARIANTI_GET_CLEAR(f,t,o) M_GET_CLEAR o
|
||||
#define VARIANTI_GET_CMP(f,t,o) M_GET_CMP o
|
||||
#define VARIANTI_GET_HASH(f,t,o) M_GET_HASH o
|
||||
#define VARIANTI_GET_EQUAL(f,t,o) M_GET_EQUAL o
|
||||
#define VARIANTI_GET_STR(f,t,o) M_GET_GET_STR o
|
||||
#define VARIANTI_GET_OUT_STR(f,t,o) M_GET_OUT_STR o
|
||||
#define VARIANTI_GET_IN_STR(f,t,o) M_GET_IN_STR o
|
||||
#define VARIANTI_GET_PARSE_STR(f,t,o) M_GET_PARSE_STR o
|
||||
#define VARIANTI_GET_SWAP(f,t,o) M_GET_SWAP o
|
||||
|
||||
#define VARIANTI_CALL_INIT(t, ...) M_APPLY_API(VARIANTI_GET_INIT t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_INIT_SET(t, ...) M_APPLY_API(VARIANTI_GET_INIT_SET t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_INIT_MOVE(t, ...) M_APPLY_API(VARIANTI_GET_INIT_MOVE t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_MOVE(t, ...) M_APPLY_API(VARIANTI_GET_MOVE t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_SET(t, ...) M_APPLY_API(VARIANTI_GET_SET t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_CLEAR(t, ...) M_APPLY_API(VARIANTI_GET_CLEAR t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_CMP(t, ...) M_APPLY_API(VARIANTI_GET_CMP t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_HASH(t, ...) M_APPLY_API(VARIANTI_GET_HASH t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_EQUAL(t, ...) M_APPLY_API(VARIANTI_GET_EQUAL t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_GET_STR(t, ...) M_APPLY_API(VARIANTI_GET_STR t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_OUT_STR(t, ...) M_APPLY_API(VARIANTI_GET_OUT_STR t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_IN_STR(t, ...) M_APPLY_API(VARIANTI_GET_IN_STR t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_PARSE_STR(t, ...) M_APPLY_API(VARIANTI_GET_PARSE_STR t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
#define VARIANTI_CALL_SWAP(t, ...) M_APPLY_API(VARIANTI_GET_SWAP t, VARIANTI_GET_OPLIST t, __VA_ARGS__)
|
||||
|
||||
#define VARIANTI_DEFINE_TYPE(name, ...) \
|
||||
enum M_C(name, _enum) { M_C(name, _EMPTY) \
|
||||
@@ -97,9 +136,12 @@
|
||||
union { \
|
||||
M_MAP(VARIANTI_DEFINE_TYPE_ELE , __VA_ARGS__) \
|
||||
} value; \
|
||||
} M_C(name,_t)[1];
|
||||
} M_C(name,_t)[1]; \
|
||||
typedef struct M_C(name, _s) *M_C(name, _ptr); \
|
||||
typedef const struct M_C(name, _s) *M_C(name, _srcptr);
|
||||
|
||||
#define VARIANTI_DEFINE_UNION_ELE(name, a) \
|
||||
, M_C3(name, _, VARIANTI_GET_FIELD a)
|
||||
, M_C4(name, _, VARIANTI_GET_FIELD a, _value)
|
||||
#define VARIANTI_DEFINE_TYPE_ELE(a) \
|
||||
VARIANTI_GET_TYPE a VARIANTI_GET_FIELD a ;
|
||||
|
||||
@@ -119,9 +161,9 @@
|
||||
} \
|
||||
}
|
||||
#define VARIANTI_DEFINE_INIT_SET_FUNC(name, a) \
|
||||
case M_C3(name, _, VARIANTI_GET_FIELD a): \
|
||||
VARIANTI_GET_INIT_SET a (my -> value. VARIANTI_GET_FIELD a , \
|
||||
org -> value.VARIANTI_GET_FIELD a ); \
|
||||
case M_C4(name, _, VARIANTI_GET_FIELD a, _value): \
|
||||
VARIANTI_CALL_INIT_SET(a, my -> value. VARIANTI_GET_FIELD a , \
|
||||
org -> value.VARIANTI_GET_FIELD a ); \
|
||||
break;
|
||||
|
||||
|
||||
@@ -141,9 +183,9 @@
|
||||
} \
|
||||
}
|
||||
#define VARIANTI_DEFINE_SET_FUNC(name, a) \
|
||||
case M_C3(name, _, VARIANTI_GET_FIELD a): \
|
||||
VARIANTI_GET_SET a (my -> value. VARIANTI_GET_FIELD a , \
|
||||
org -> value.VARIANTI_GET_FIELD a ); \
|
||||
case M_C4(name, _, VARIANTI_GET_FIELD a, _value): \
|
||||
VARIANTI_CALL_SET(a, my -> value. VARIANTI_GET_FIELD a , \
|
||||
org -> value.VARIANTI_GET_FIELD a ); \
|
||||
break;
|
||||
|
||||
|
||||
@@ -157,8 +199,8 @@
|
||||
my->type = M_C(name, _EMPTY); \
|
||||
}
|
||||
#define VARIANTI_DEFINE_CLEAR_FUNC(name, a) \
|
||||
case M_C3(name, _, VARIANTI_GET_FIELD a): \
|
||||
VARIANTI_GET_CLEAR a (my -> value. VARIANTI_GET_FIELD a); \
|
||||
case M_C4(name, _, VARIANTI_GET_FIELD a, _value): \
|
||||
VARIANTI_CALL_CLEAR(a, my -> value. VARIANTI_GET_FIELD a); \
|
||||
break;
|
||||
|
||||
|
||||
@@ -173,34 +215,61 @@
|
||||
M_MAP2(VARIANTI_DEFINE_TEST_FUNC, name, __VA_ARGS__)
|
||||
#define VARIANTI_DEFINE_TEST_FUNC(name, a) \
|
||||
static inline bool M_C4(name, _, VARIANTI_GET_FIELD a, _p)(M_C(name,_t) my) { \
|
||||
return my->type == M_C3(name, _, VARIANTI_GET_FIELD a); \
|
||||
return my->type == M_C4(name, _, VARIANTI_GET_FIELD a, _value); \
|
||||
}
|
||||
|
||||
|
||||
#define VARIANTI_DEFINE_SETTER(name, ...) \
|
||||
M_MAP2(VARIANTI_DEFINE_SETTER_FUNC, name, __VA_ARGS__)
|
||||
#define VARIANTI_DEFINE_SETTER_FUNC(name, a) \
|
||||
#define VARIANTI_DEFINE_INIT_FIELD(name, ...) \
|
||||
M_MAP2(VARIANTI_DEFINE_INIT_FIELD_FUNC, name, __VA_ARGS__)
|
||||
#define VARIANTI_DEFINE_INIT_FIELD_FUNC(name, a) \
|
||||
static inline void M_C3(name, _init_, VARIANTI_GET_FIELD a)(M_C(name,_t) my) { \
|
||||
/* Reinit variable with the given value */ \
|
||||
my->type = M_C4(name, _, VARIANTI_GET_FIELD a, _value); \
|
||||
VARIANTI_CALL_INIT(a, my -> value. VARIANTI_GET_FIELD a); \
|
||||
}
|
||||
|
||||
|
||||
#define VARIANTI_DEFINE_INIT_SETTER_FIELD(name, ...) \
|
||||
M_MAP2(VARIANTI_DEFINE_INIT_SETTER_FIELD_FUNC, name, __VA_ARGS__)
|
||||
#define VARIANTI_DEFINE_INIT_SETTER_FIELD_FUNC(name, a) \
|
||||
static inline void M_C3(name, _init_set_, VARIANTI_GET_FIELD a)(M_C(name,_t) my, \
|
||||
VARIANTI_GET_TYPE a VARIANTI_GET_FIELD a ) { \
|
||||
my->type = M_C4(name, _, VARIANTI_GET_FIELD a, _value); \
|
||||
VARIANTI_CALL_INIT_SET(a, my -> value. VARIANTI_GET_FIELD a, \
|
||||
VARIANTI_GET_FIELD a); \
|
||||
}
|
||||
|
||||
|
||||
#define VARIANTI_DEFINE_SETTER_FIELD(name, ...) \
|
||||
M_MAP2(VARIANTI_DEFINE_SETTER_FIELD_FUNC, name, __VA_ARGS__)
|
||||
#define VARIANTI_DEFINE_SETTER_FIELD_FUNC(name, a) \
|
||||
static inline void M_C3(name, _set_, VARIANTI_GET_FIELD a)(M_C(name,_t) my, \
|
||||
VARIANTI_GET_TYPE a VARIANTI_GET_FIELD a ) { \
|
||||
if (my->type == M_C3(name, _, VARIANTI_GET_FIELD a) ) { \
|
||||
VARIANTI_GET_SET a (my -> value. VARIANTI_GET_FIELD a, \
|
||||
VARIANTI_GET_FIELD a); \
|
||||
if (my->type == M_C4(name, _, VARIANTI_GET_FIELD a, _value) ) { \
|
||||
VARIANTI_CALL_SET(a, my -> value. VARIANTI_GET_FIELD a, \
|
||||
VARIANTI_GET_FIELD a); \
|
||||
} else { \
|
||||
M_C(name, _clear)(my); \
|
||||
/* Reinit variable with the given value */ \
|
||||
my->type = M_C3(name, _, VARIANTI_GET_FIELD a); \
|
||||
VARIANTI_GET_INIT_SET a(my -> value. VARIANTI_GET_FIELD a, \
|
||||
VARIANTI_GET_FIELD a); \
|
||||
my->type = M_C4(name, _, VARIANTI_GET_FIELD a, _value); \
|
||||
VARIANTI_CALL_INIT_SET(a, my -> value. VARIANTI_GET_FIELD a, \
|
||||
VARIANTI_GET_FIELD a); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
#define VARIANTI_DEFINE_GETTER(name, ...) \
|
||||
M_MAP2(VARIANTI_DEFINE_GETTER_FUNC, name, __VA_ARGS__)
|
||||
#define VARIANTI_DEFINE_GETTER_FUNC(name, a) \
|
||||
#define VARIANTI_DEFINE_GETTER_FIELD(name, ...) \
|
||||
M_MAP2(VARIANTI_DEFINE_GETTER_FIELD_FUNC, name, __VA_ARGS__)
|
||||
#define VARIANTI_DEFINE_GETTER_FIELD_FUNC(name, a) \
|
||||
static inline VARIANTI_GET_TYPE a * \
|
||||
M_C3(name, _get_, VARIANTI_GET_FIELD a)(M_C(name,_t) my) { \
|
||||
if (my->type != M_C3(name, _, VARIANTI_GET_FIELD a) ) { \
|
||||
if (my->type != M_C4(name, _, VARIANTI_GET_FIELD a, _value) ) { \
|
||||
return NULL; \
|
||||
} \
|
||||
return &my -> value . VARIANTI_GET_FIELD a; \
|
||||
} \
|
||||
static inline VARIANTI_GET_TYPE a const * \
|
||||
M_C3(name, _cget_, VARIANTI_GET_FIELD a)(M_C(name,_t) const my) { \
|
||||
if (my->type != M_C4(name, _, VARIANTI_GET_FIELD a, _value) ) { \
|
||||
return NULL; \
|
||||
} \
|
||||
return &my -> value . VARIANTI_GET_FIELD a; \
|
||||
@@ -220,9 +289,9 @@
|
||||
return true; \
|
||||
}
|
||||
#define VARIANTI_DEFINE_EQUAL_FUNC(name, a) \
|
||||
case M_C3(name, _, VARIANTI_GET_FIELD a): \
|
||||
b = VARIANTI_GET_EQUAL a ( e1 -> value . VARIANTI_GET_FIELD a , \
|
||||
e2 -> value . VARIANTI_GET_FIELD a ); \
|
||||
case M_C4(name, _, VARIANTI_GET_FIELD a, _value): \
|
||||
b = VARIANTI_CALL_EQUAL(a, e1 -> value . VARIANTI_GET_FIELD a , \
|
||||
e2 -> value . VARIANTI_GET_FIELD a ); \
|
||||
if (!b) return false; \
|
||||
break;
|
||||
|
||||
@@ -239,8 +308,8 @@
|
||||
return M_HASH_FINAL (hash); \
|
||||
}
|
||||
#define VARIANTI_DEFINE_HASH_FUNC(name, a) \
|
||||
case M_C3(name, _, VARIANTI_GET_FIELD a): \
|
||||
M_HASH_UP(hash, VARIANTI_GET_HASH a ( e1 -> value . VARIANTI_GET_FIELD a) ); \
|
||||
case M_C4(name, _, VARIANTI_GET_FIELD a, _value): \
|
||||
M_HASH_UP(hash, VARIANTI_CALL_HASH(a, e1 -> value . VARIANTI_GET_FIELD a) ); \
|
||||
break;
|
||||
|
||||
|
||||
@@ -255,9 +324,9 @@
|
||||
org -> type = M_C(name, _EMPTY); \
|
||||
}
|
||||
#define VARIANTI_DEFINE_INIT_MOVE_FUNC(name, a) \
|
||||
case M_C3(name, _, VARIANTI_GET_FIELD a): \
|
||||
VARIANTI_GET_INIT_MOVE a (el -> value . VARIANTI_GET_FIELD a, \
|
||||
org -> value . VARIANTI_GET_FIELD a); \
|
||||
case M_C4(name, _, VARIANTI_GET_FIELD a, _value): \
|
||||
VARIANTI_CALL_INIT_MOVE(a, el -> value . VARIANTI_GET_FIELD a, \
|
||||
org -> value . VARIANTI_GET_FIELD a); \
|
||||
break;
|
||||
|
||||
|
||||
@@ -275,9 +344,9 @@
|
||||
VARIANTI_GET_TYPE a VARIANTI_GET_FIELD a ) { \
|
||||
M_C(name, _clear)(my); \
|
||||
/* Reinit variable with the given value */ \
|
||||
my->type = M_C3(name, _, VARIANTI_GET_FIELD a); \
|
||||
VARIANTI_GET_INIT_MOVE a(my -> value. VARIANTI_GET_FIELD a, \
|
||||
VARIANTI_GET_FIELD a); \
|
||||
my->type = M_C4(name, _, VARIANTI_GET_FIELD a, _value); \
|
||||
VARIANTI_CALL_INIT_MOVE(a, my -> value. VARIANTI_GET_FIELD a, \
|
||||
VARIANTI_GET_FIELD a); \
|
||||
}
|
||||
|
||||
|
||||
@@ -291,7 +360,7 @@
|
||||
} \
|
||||
} else { \
|
||||
M_C(name,_t) tmp; \
|
||||
M_IF(VARIANTI_ALL_INIT_MOVE(__VA_ARGS__)) \
|
||||
VARIANTI_IF_ALL(INIT_MOVE, __VA_ARGS__) \
|
||||
( /* NOTE: Slow implementation */ \
|
||||
M_C(name, _init_move)(tmp, el1); \
|
||||
M_C(name, _init_move)(el1, el2); \
|
||||
@@ -306,9 +375,9 @@
|
||||
} \
|
||||
}
|
||||
#define VARIANTI_DEFINE_INIT_SWAP_FUNC(name, a) \
|
||||
case M_C3(name, _, VARIANTI_GET_FIELD a): \
|
||||
VARIANTI_GET_SWAP a (el1 -> value . VARIANTI_GET_FIELD a, \
|
||||
el2 -> value . VARIANTI_GET_FIELD a); \
|
||||
case M_C4(name, _, VARIANTI_GET_FIELD a, _value): \
|
||||
VARIANTI_CALL_SWAP(a, el1 -> value . VARIANTI_GET_FIELD a, \
|
||||
el2 -> value . VARIANTI_GET_FIELD a); \
|
||||
break;
|
||||
|
||||
|
||||
@@ -327,9 +396,9 @@
|
||||
string_push_back (str, '@'); \
|
||||
}
|
||||
#define VARIANTI_DEFINE_GET_STR_FUNC(name, a) \
|
||||
case M_C3(name, _, VARIANTI_GET_FIELD a): \
|
||||
case M_C4(name, _, VARIANTI_GET_FIELD a, _value): \
|
||||
func(str, "@" M_APPLY (M_AS_STR, VARIANTI_GET_FIELD a) "@"); \
|
||||
VARIANTI_GET_STR a (str, el -> value . VARIANTI_GET_FIELD a, true); \
|
||||
VARIANTI_CALL_GET_STR(a, str, el -> value . VARIANTI_GET_FIELD a, true); \
|
||||
break;
|
||||
|
||||
|
||||
@@ -345,9 +414,9 @@
|
||||
fputc ('@', f); \
|
||||
}
|
||||
#define VARIANTI_DEFINE_OUT_STR_FUNC(name, a) \
|
||||
case M_C3(name, _, VARIANTI_GET_FIELD a): \
|
||||
fprintf(f, "@" M_APPLY(M_AS_STR, VARIANTI_GET_FIELD a) "@"); \
|
||||
VARIANTI_GET_OUT_STR a (f, el -> value . VARIANTI_GET_FIELD a); \
|
||||
case M_C4(name, _, VARIANTI_GET_FIELD a, _value): \
|
||||
fprintf(f, "@" M_APPLY(M_AS_STR, VARIANTI_GET_FIELD a) "@"); \
|
||||
VARIANTI_CALL_OUT_STR(a, f, el -> value . VARIANTI_GET_FIELD a); \
|
||||
break;
|
||||
|
||||
|
||||
@@ -379,13 +448,54 @@
|
||||
}
|
||||
#define VARIANTI_DEFINE_IN_STR_FUNC(name, a) \
|
||||
else if (strcmp (variantTypeBuf, M_APPLY (M_AS_STR, VARIANTI_GET_FIELD a)) == 0) { \
|
||||
el->type = M_C3(name, _, VARIANTI_GET_FIELD a); \
|
||||
VARIANTI_GET_INIT a (el ->value . VARIANTI_GET_FIELD a ); \
|
||||
bool b = VARIANTI_GET_IN_STR a (el -> value . VARIANTI_GET_FIELD a, f); \
|
||||
el->type = M_C4(name, _, VARIANTI_GET_FIELD a, _value); \
|
||||
VARIANTI_CALL_INIT(a, el ->value . VARIANTI_GET_FIELD a ); \
|
||||
bool b = VARIANTI_CALL_IN_STR(a, el -> value . VARIANTI_GET_FIELD a, f); \
|
||||
if (!b) return false; \
|
||||
}
|
||||
|
||||
|
||||
#define VARIANTI_DEFINE_PARSE_STR(name, ...) \
|
||||
static inline bool M_C(name, _parse_str)(M_C(name,_t) el, \
|
||||
const char str[], \
|
||||
const char **endp) { \
|
||||
assert (str != NULL && el != NULL); \
|
||||
bool success = false; \
|
||||
/* A buffer of 400 bytes should be more than enough for all variant names... */ \
|
||||
char variantTypeBuf[400]; \
|
||||
int c = *str++; \
|
||||
unsigned int i = 0; \
|
||||
if (c != '@') goto exit; \
|
||||
/* First read the name of the type */ \
|
||||
c = *str++; \
|
||||
while (c != '@' && c != 0 && i < sizeof(variantTypeBuf) - 1) { \
|
||||
variantTypeBuf[i++] = c; \
|
||||
c = *str++; \
|
||||
} \
|
||||
if (c != '@') goto exit; \
|
||||
variantTypeBuf[i++] = 0; \
|
||||
assert(i < sizeof(variantTypeBuf)); \
|
||||
M_C(name, _clear)(el); \
|
||||
/* In function of the type */ \
|
||||
if (strcmp(variantTypeBuf, "EMPTY") == 0) { \
|
||||
el->type = M_C(name, _EMPTY); \
|
||||
} \
|
||||
M_MAP2(VARIANTI_DEFINE_PARSE_STR_FUNC , name, __VA_ARGS__) \
|
||||
else goto exit; \
|
||||
success = (*str++ == '@'); \
|
||||
exit: \
|
||||
if (endp) *endp = str; \
|
||||
return success; \
|
||||
}
|
||||
#define VARIANTI_DEFINE_PARSE_STR_FUNC(name, a) \
|
||||
else if (strcmp (variantTypeBuf, M_APPLY (M_AS_STR, VARIANTI_GET_FIELD a)) == 0) { \
|
||||
el->type = M_C4(name, _, VARIANTI_GET_FIELD a, _value); \
|
||||
VARIANTI_CALL_INIT(a, el ->value . VARIANTI_GET_FIELD a ); \
|
||||
bool b = VARIANTI_CALL_PARSE_STR(a, el -> value . VARIANTI_GET_FIELD a, str, &str); \
|
||||
if (!b) goto exit; \
|
||||
}
|
||||
|
||||
|
||||
#define VARIANTI_DEFINE_CLEAN_FUNC(name, ...) \
|
||||
static inline void M_C(name, _clean)(M_C(name,_t) my) \
|
||||
{ \
|
||||
@@ -399,11 +509,14 @@
|
||||
INIT_SET(M_C(name, _init_set)), \
|
||||
SET(M_C(name,_set)), \
|
||||
CLEAR(M_C(name, _clear)), \
|
||||
CLEAN(M_C(name, _clean)), \
|
||||
TYPE(M_C(name,_t)), \
|
||||
TEST_EMPTY(M_C(name,_empty_p)), \
|
||||
M_IF_METHOD_ALL(HASH, __VA_ARGS__)(HASH(M_C(name, _hash)),), \
|
||||
M_IF_METHOD_ALL(EQUAL, __VA_ARGS__)(EQUAL(M_C(name, _equal_p)),), \
|
||||
M_IF_METHOD_ALL(GET_STR, __VA_ARGS__)(GET_STR(M_C(name, _get_str)),), \
|
||||
M_IF_METHOD_ALL(IN_STR, __VA_ARGS__)(IN_STR(M_C(name, _in_str)),), \
|
||||
M_IF_METHOD2_ALL(PARSE_STR, INIT, __VA_ARGS__)(PARSE_STR(M_C(name, _parse_str)),), \
|
||||
M_IF_METHOD2_ALL(IN_STR, INIT, __VA_ARGS__)(IN_STR(M_C(name, _in_str)),), \
|
||||
M_IF_METHOD_ALL(OUT_STR, __VA_ARGS__)(OUT_STR(M_C(name, _out_str)),), \
|
||||
M_IF_METHOD_ALL(INIT_MOVE, __VA_ARGS__)(INIT_MOVE(M_C(name, _init_move)),), \
|
||||
M_IF_METHOD_ALL(INIT_MOVE, __VA_ARGS__)(MOVE(M_C(name, _move)),), \
|
||||
@@ -414,25 +527,18 @@
|
||||
)
|
||||
|
||||
/* Macros for testing for method presence */
|
||||
#define VARIANTI_TEST_METHOD2_P(method, f, t, op) \
|
||||
#define VARIANTI_TEST_METHOD_P2(method, f, t, op) \
|
||||
M_TEST_METHOD_P(method, op)
|
||||
#define VARIANTI_TEST_METHOD_P(method, trio) \
|
||||
M_APPLY(VARIANTI_TEST_METHOD2_P, method, M_OPFLAT trio)
|
||||
|
||||
#define VARIANTI_ALL_EQUAL(...) \
|
||||
M_REDUCE2(VARIANTI_TEST_METHOD_P, M_AND, EQUAL, __VA_ARGS__)
|
||||
#define VARIANTI_ALL_HASH(...) \
|
||||
M_REDUCE2(VARIANTI_TEST_METHOD_P, M_AND, HASH, __VA_ARGS__)
|
||||
#define VARIANTI_ALL_GET_STR(...) \
|
||||
M_REDUCE2(VARIANTI_TEST_METHOD_P, M_AND, GET_STR, __VA_ARGS__)
|
||||
#define VARIANTI_ALL_OUT_STR(...) \
|
||||
M_REDUCE2(VARIANTI_TEST_METHOD_P, M_AND, OUT_STR, __VA_ARGS__)
|
||||
#define VARIANTI_ALL_IN_STR(...) \
|
||||
M_REDUCE2(VARIANTI_TEST_METHOD_P, M_AND, IN_STR, __VA_ARGS__)
|
||||
#define VARIANTI_ALL_INIT_MOVE(...) \
|
||||
M_REDUCE2(VARIANTI_TEST_METHOD_P, M_AND, INIT_MOVE, __VA_ARGS__)
|
||||
#define VARIANTI_ALL_SWAP(...) \
|
||||
M_REDUCE2(VARIANTI_TEST_METHOD_P, M_AND, SWAP, __VA_ARGS__)
|
||||
M_APPLY(VARIANTI_TEST_METHOD_P2, method, M_OPFLAT trio)
|
||||
#define VARIANTI_IF_ALL(method, ...) \
|
||||
M_IF(M_REDUCE2(VARIANTI_TEST_METHOD_P, M_AND, method, __VA_ARGS__))
|
||||
|
||||
#define VARIANTI_TEST_METHOD2_P2(method1, method2, f, t, op) \
|
||||
M_AND(M_TEST_METHOD_P(method1, op), M_TEST_METHOD_P(method2, op))
|
||||
#define VARIANTI_TEST_METHOD2_P(method, trio) \
|
||||
M_APPLY(VARIANTI_TEST_METHOD2_P2, M_PAIR_1 method, M_PAIR_2 method, M_OPFLAT trio)
|
||||
#define VARIANTI_IF_ALL2(method1, method2, ...) \
|
||||
M_IF(M_REDUCE2(VARIANTI_TEST_METHOD2_P, M_AND, (method1, method2), __VA_ARGS__))
|
||||
|
||||
#endif
|
||||
+126
-61
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB / WORKER - Extra worker interface
|
||||
*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -22,8 +22,8 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __M_WORKER_H
|
||||
#define __M_WORKER_H
|
||||
#ifndef MSTARLIB_WORKER_H
|
||||
#define MSTARLIB_WORKER_H
|
||||
|
||||
#ifndef WORKER_DISABLE
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
# define WORKER_USE_CPP_FUNCTION 0
|
||||
#endif
|
||||
|
||||
/* Control that not both options are selected at the same times.
|
||||
/* Control that not both options are selected at the same time.
|
||||
Note: there are not really incompatible, but if we use C++ we shall go to
|
||||
lambda directly (there is no need to support blocks)! */
|
||||
#if WORKER_USE_CLANG_BLOCK && WORKER_USE_CPP_FUNCTION
|
||||
@@ -108,12 +108,6 @@ typedef struct worker_thread_s {
|
||||
m_thread_t id;
|
||||
} worker_thread_t;
|
||||
|
||||
/* This type defines a synchronization point for workers */
|
||||
typedef struct worker_sync_s {
|
||||
atomic_int num_spawn;
|
||||
atomic_int num_terminated_spawn;
|
||||
} worker_sync_t[1];
|
||||
|
||||
/* Let's define the queue which will manage all work orders */
|
||||
BUFFER_DEF(worker_queue, worker_order_t, 0, BUFFER_QUEUE|BUFFER_UNBLOCKING_PUSH|BUFFER_BLOCKING_POP|BUFFER_THREAD_SAFE|BUFFER_DEFERRED_POP, M_POD_OPLIST)
|
||||
|
||||
@@ -128,8 +122,22 @@ typedef struct worker_s {
|
||||
|
||||
/* The global reset function */
|
||||
void (*resetFunc_g)(void);
|
||||
|
||||
/* The global clear function */
|
||||
void (*clearFunc_g)(void);
|
||||
|
||||
m_mutex_t lock;
|
||||
m_cond_t a_thread_ends;
|
||||
|
||||
} worker_t[1];
|
||||
|
||||
/* This type defines a synchronization point for workers */
|
||||
typedef struct worker_sync_s {
|
||||
atomic_int num_spawn;
|
||||
atomic_int num_terminated_spawn;
|
||||
struct worker_s *worker;
|
||||
} worker_sync_t[1];
|
||||
|
||||
/* Return the number of CPU of the system */
|
||||
static inline int
|
||||
workeri_get_cpu_count(void)
|
||||
@@ -145,7 +153,7 @@ workeri_get_cpu_count(void)
|
||||
nm[0] = CTL_HW;
|
||||
nm[1] = HW_NCPU;
|
||||
sysctl(nm, 2, &count, &len, NULL, 0);
|
||||
return MAX(1, count);
|
||||
return M_MAX(1, count);
|
||||
#elif defined (_SC_NPROCESSORS_ONLN)
|
||||
return sysconf(_SC_NPROCESSORS_ONLN);
|
||||
#else
|
||||
@@ -153,6 +161,30 @@ workeri_get_cpu_count(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Execute the registered work order synchronously */
|
||||
static inline void
|
||||
workeri_exec(worker_order_t *w)
|
||||
{
|
||||
assert (w!= NULL && w->block != NULL);
|
||||
//printf ("Starting thread with data %p\n", w->data);
|
||||
#if WORKER_USE_CLANG_BLOCK
|
||||
//printf ("Running %s f=%p b=%p\n", (w->func == NULL) ? "Blocks" : "Function", w->func, w->blockFunc);
|
||||
if (w->func == NULL)
|
||||
w->blockFunc(w->data);
|
||||
else
|
||||
#endif
|
||||
#if WORKER_USE_CPP_FUNCTION
|
||||
//printf ("Running %s f=%p b=%p\n", (w->function == NULL) ? "Lambda" : "Function", w->func, w->blockFunc);
|
||||
if (w->function)
|
||||
w->function(w->data);
|
||||
else
|
||||
#endif
|
||||
w->func(w->data);
|
||||
/* Increment the number of terminated work order for the synchronous point */
|
||||
atomic_fetch_add (&w->block->num_terminated_spawn, 1);
|
||||
}
|
||||
|
||||
|
||||
/* The worker thread */
|
||||
static inline void
|
||||
workeri_thread(void *arg)
|
||||
@@ -164,56 +196,62 @@ workeri_thread(void *arg)
|
||||
g->resetFunc_g();
|
||||
//printf ("Waiting for data (queue: %lu / %lu)\n", worker_queue_size(g->queue_g), worker_queue_capacity(g->queue_g));
|
||||
worker_queue_pop(&w, g->queue_g);
|
||||
if (w.block == NULL) return;
|
||||
//printf ("Starting thread with data %p\n", w.data);
|
||||
#if WORKER_USE_CLANG_BLOCK
|
||||
//printf ("Running %s f=%p b=%p\n", (w.func == NULL) ? "Blocks" : "Function", w.func, w.blockFunc);
|
||||
if (w.func == NULL)
|
||||
w.blockFunc(w.data);
|
||||
else
|
||||
#endif
|
||||
#if WORKER_USE_CPP_FUNCTION
|
||||
//printf ("Running %s f=%p b=%p\n", (w.function == NULL) ? "Lambda" : "Function", w.func, w.blockFunc);
|
||||
if (w.function)
|
||||
w.function(w.data);
|
||||
else
|
||||
#endif
|
||||
w.func(w.data);
|
||||
if (w.block == NULL) break;
|
||||
workeri_exec(&w);
|
||||
worker_queue_pop_release(g->queue_g);
|
||||
atomic_fetch_add (&w.block->num_terminated_spawn, 1);
|
||||
m_mutex_lock(g->lock);
|
||||
m_cond_broadcast(g->a_thread_ends);
|
||||
m_mutex_unlock(g->lock);
|
||||
}
|
||||
if (g->clearFunc_g != NULL)
|
||||
g->clearFunc_g();
|
||||
}
|
||||
|
||||
/* Initialization of the worker module
|
||||
Input:
|
||||
@numWorker: number of worker to create
|
||||
@numWorker: number of worker to create (0=autodetect, -1=2*autodetect)
|
||||
@extraQueue: number of extra work order we can get if all workers are full
|
||||
@resetFunc: function to reset the state of a worker between work orders (or NULL if none)
|
||||
@clearFunc: function to clear the state of a worker before terminaning (or NULL if none)
|
||||
*/
|
||||
static inline void
|
||||
worker_init(worker_t g, unsigned int numWorker, unsigned int extraQueue, void (*resetFunc)(void))
|
||||
worker_init(worker_t g, int numWorker, unsigned int extraQueue, void (*resetFunc)(void), void (*clearFunc)(void))
|
||||
{
|
||||
if (numWorker == 0)
|
||||
numWorker = workeri_get_cpu_count()-1;
|
||||
assert (numWorker >= -1);
|
||||
if (numWorker <= 0)
|
||||
numWorker = (1 + (numWorker == -1))*workeri_get_cpu_count()-1;
|
||||
//printf ("Starting queue with: %d\n", numWorker + extraQueue);
|
||||
worker_queue_init(g->queue_g, numWorker + extraQueue);
|
||||
size_t numWorker_st = numWorker;
|
||||
g->worker = M_MEMORY_REALLOC(worker_thread_t, NULL, numWorker_st);
|
||||
M_ASSERT_INIT (g->worker != NULL);
|
||||
g->numWorker_g = numWorker;
|
||||
if (g->worker == NULL) {
|
||||
M_MEMORY_FULL(sizeof (worker_thread_t) * numWorker_st);
|
||||
return;
|
||||
}
|
||||
g->numWorker_g = numWorker_st;
|
||||
g->resetFunc_g = resetFunc;
|
||||
for(size_t i = 0; i < numWorker; i++) {
|
||||
g->clearFunc_g = clearFunc;
|
||||
m_mutex_init(g->lock);
|
||||
m_cond_init(g->a_thread_ends);
|
||||
|
||||
for(size_t i = 0; i < numWorker_st; i++) {
|
||||
m_thread_create(g->worker[i].id, workeri_thread, M_ASSIGN_CAST(void*, g));
|
||||
}
|
||||
}
|
||||
// Define function with default values.
|
||||
#define worker_init(...) worker_init(M_DEFAULT_ARGS(5, (0, 0, NULL, NULL), __VA_ARGS__))
|
||||
|
||||
/* Clear of the worker module */
|
||||
static inline void
|
||||
worker_clear(worker_t g)
|
||||
{
|
||||
assert (worker_queue_empty_p (g->queue_g));
|
||||
for(unsigned int i = 0; i < g->numWorker_g; i++) {
|
||||
worker_order_t w = WORKER_EMPTY_ORDER;
|
||||
worker_queue_push (g->queue_g, w);
|
||||
// Normaly all worker threads shall be waiting at this
|
||||
// stage, so all push won't block as the queue is empty.
|
||||
// But for robustness, let's wait.
|
||||
worker_queue_push_blocking (g->queue_g, w, true);
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < g->numWorker_g; i++) {
|
||||
@@ -221,26 +259,29 @@ worker_clear(worker_t g)
|
||||
}
|
||||
M_MEMORY_FREE(g->worker);
|
||||
|
||||
m_mutex_clear(g->lock);
|
||||
m_cond_clear(g->a_thread_ends);
|
||||
worker_queue_clear(g->queue_g);
|
||||
}
|
||||
|
||||
/* Start a new collaboration between workers
|
||||
and define the synchronization point 'block' */
|
||||
static inline void
|
||||
worker_start(worker_sync_t block)
|
||||
worker_start(worker_sync_t block, worker_t g)
|
||||
{
|
||||
atomic_init (&block->num_spawn, 0);
|
||||
atomic_init (&block->num_terminated_spawn, 0);
|
||||
block->worker = g;
|
||||
}
|
||||
|
||||
/* Spawn or not the given work order to workers,
|
||||
or do it ourself if no worker is available */
|
||||
static inline void
|
||||
worker_spawn(worker_t g, worker_sync_t block, void (*func)(void *data), void *data)
|
||||
worker_spawn(worker_sync_t block, void (*func)(void *data), void *data)
|
||||
{
|
||||
const worker_order_t w = { block, data, func WORKER_EXTRA_ORDER };
|
||||
if (M_UNLIKELY (!worker_queue_full_p(g->queue_g))
|
||||
&& worker_queue_push (g->queue_g, w) == true) {
|
||||
if (M_UNLIKELY (!worker_queue_full_p(block->worker->queue_g))
|
||||
&& worker_queue_push (block->worker->queue_g, w) == true) {
|
||||
//printf ("Sending data to thread: %p (block: %d / %d)\n", data, block->num_spawn, block->num_terminated_spawn);
|
||||
atomic_fetch_add (&block->num_spawn, 1);
|
||||
return;
|
||||
@@ -254,11 +295,11 @@ worker_spawn(worker_t g, worker_sync_t block, void (*func)(void *data), void *da
|
||||
/* Spawn or not the given work order to workers,
|
||||
or do it ourself if no worker is available */
|
||||
static inline void
|
||||
worker_spawn_block(worker_t g, worker_sync_t block, void (^func)(void *data), void *data)
|
||||
worker_spawn_block(worker_sync_t block, void (^func)(void *data), void *data)
|
||||
{
|
||||
const worker_order_t w = { block, data, NULL, func };
|
||||
if (M_UNLIKELY (!worker_queue_full_p(g->queue_g))
|
||||
&& worker_queue_push (g->queue_g, w) == true) {
|
||||
if (M_UNLIKELY (!worker_queue_full_p(block->worker->queue_g))
|
||||
&& worker_queue_push (block->worker->queue_g, w) == true) {
|
||||
//printf ("Sending data to thread as block: %p (block: %d / %d)\n", data, block->num_spawn, block->num_terminated_spawn);
|
||||
atomic_fetch_add (&block->num_spawn, 1);
|
||||
return;
|
||||
@@ -273,11 +314,11 @@ worker_spawn_block(worker_t g, worker_sync_t block, void (^func)(void *data), vo
|
||||
/* Spawn or not the given work order to workers,
|
||||
or do it ourself if no worker is available */
|
||||
static inline void
|
||||
worker_spawn_function(worker_t g, worker_sync_t block, std::function<void(void *data)> func, void *data)
|
||||
worker_spawn_function(worker_sync_t block, std::function<void(void *data)> func, void *data)
|
||||
{
|
||||
const worker_order_t w = { block, data, NULL, func };
|
||||
if (M_UNLIKELY (!worker_queue_full_p(g->queue_g))
|
||||
&& worker_queue_push (g->queue_g, w) == true) {
|
||||
if (M_UNLIKELY (!worker_queue_full_p(block->worker->queue_g))
|
||||
&& worker_queue_push (block->worker->queue_g, w) == true) {
|
||||
//printf ("Sending data to thread as block: %p (block: %d / %d)\n", data, block->num_spawn, block->num_terminated_spawn);
|
||||
atomic_fetch_add (&block->num_spawn, 1);
|
||||
return;
|
||||
@@ -288,17 +329,41 @@ worker_spawn_function(worker_t g, worker_sync_t block, std::function<void(void *
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Test if all work orders are finished */
|
||||
static inline bool
|
||||
worker_sync_p(worker_sync_t block)
|
||||
{
|
||||
/* If the number of spawns is greated than the number
|
||||
of terminated spawns, some spawns are still working.
|
||||
So wait for terminaison */
|
||||
return (atomic_load(&block->num_spawn) == atomic_load (&block->num_terminated_spawn));
|
||||
}
|
||||
|
||||
/* Wait for all work orders to be finished */
|
||||
static inline void
|
||||
worker_sync(worker_sync_t block)
|
||||
{
|
||||
//printf ("Waiting for thread terminasion.\n");
|
||||
/* If the number of spawns is greated than the number
|
||||
of terminated spawns, some spawns are still working.
|
||||
So wait for terminaison */
|
||||
while (atomic_load(&block->num_spawn) != atomic_load (&block->num_terminated_spawn));
|
||||
if (worker_sync_p(block)) return;
|
||||
m_mutex_lock(block->worker->lock);
|
||||
while (!worker_sync_p(block)) {
|
||||
m_cond_wait(block->worker->a_thread_ends, block->worker->lock);
|
||||
}
|
||||
m_mutex_unlock(block->worker->lock);
|
||||
}
|
||||
|
||||
/* Flush any work order in the queue if some remains.*/
|
||||
static inline void
|
||||
worker_flush(worker_t g)
|
||||
{
|
||||
worker_order_t w;
|
||||
while (worker_queue_pop_blocking (&w, g->queue_g, false) == true) {
|
||||
workeri_exec(&w);
|
||||
worker_queue_pop_release(g->queue_g);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Return the number of workers */
|
||||
static inline size_t
|
||||
worker_count(worker_t g)
|
||||
@@ -313,19 +378,19 @@ worker_count(worker_t g)
|
||||
'output' is the list of output variables of the 'core' block within "( )"
|
||||
Output variables are only available after a synchronisation block. */
|
||||
#if WORKER_USE_CLANG_BLOCK
|
||||
#define WORKER_SPAWN(_worker, _block, _input, _core, _output) \
|
||||
#define WORKER_SPAWN(_block, _input, _core, _output) \
|
||||
WORKER_DEF_DATA(_input, _output) \
|
||||
WORKER_DEF_SUBBLOCK(_input, _output, _core) \
|
||||
worker_spawn_block ((_worker), (_block), WORKER_SPAWN_SUBFUNC_NAME, &WORKER_SPAWN_DATA_NAME)
|
||||
worker_spawn_block ((_block), WORKER_SPAWN_SUBFUNC_NAME, &WORKER_SPAWN_DATA_NAME)
|
||||
#elif WORKER_USE_CPP_FUNCTION
|
||||
// TODO: Explicit pass all arguments by reference.
|
||||
#define WORKER_SPAWN(_worker, _block, _input, _core, _output) \
|
||||
worker_spawn_function ((_worker), (_block), [&](void *param) {(void)param ; _core } , NULL)
|
||||
#define WORKER_SPAWN(_block, _input, _core, _output) \
|
||||
worker_spawn_function ((_block), [&](void *param) {(void)param ; _core } , NULL)
|
||||
#else
|
||||
#define WORKER_SPAWN(_worker, _block, _input, _core, _output) \
|
||||
#define WORKER_SPAWN(_block, _input, _core, _output) \
|
||||
WORKER_DEF_DATA(_input, _output) \
|
||||
WORKER_DEF_SUBFUNC(_input, _output, _core) \
|
||||
worker_spawn ((_worker), (_block), WORKER_SPAWN_SUBFUNC_NAME, &WORKER_SPAWN_DATA_NAME)
|
||||
worker_spawn ((_block), WORKER_SPAWN_SUBFUNC_NAME, &WORKER_SPAWN_DATA_NAME)
|
||||
#endif
|
||||
|
||||
#define WORKER_SPAWN_STRUCT_NAME M_C(worker_data_s_, __LINE__)
|
||||
@@ -343,7 +408,7 @@ worker_count(worker_t g)
|
||||
#define WORKER_DEF_DATA_INPUT(...) \
|
||||
M_MAP(WORKER_DEF_SINGLE_INPUT, __VA_ARGS__)
|
||||
#define WORKER_DEF_SINGLE_OUTPUT(var) \
|
||||
__typeof__(var) *M_C(var, __ptr);
|
||||
__typeof__(var) *M_C(var, _ptr);
|
||||
#define WORKER_DEF_DATA_OUTPUT(...) \
|
||||
M_MAP(WORKER_DEF_SINGLE_OUTPUT, __VA_ARGS__)
|
||||
#define WORKER_INIT_SINGLE_INPUT(var) \
|
||||
@@ -351,7 +416,7 @@ worker_count(worker_t g)
|
||||
#define WORKER_INIT_DATA_INPUT(...) \
|
||||
M_MAP(WORKER_INIT_SINGLE_INPUT, __VA_ARGS__)
|
||||
#define WORKER_INIT_SINGLE_OUTPUT(var) \
|
||||
.M_C(var, __ptr) = &var,
|
||||
.M_C(var, _ptr) = &var,
|
||||
#define WORKER_INIT_DATA_OUTPUT(...) \
|
||||
M_MAP(WORKER_INIT_SINGLE_OUTPUT, __VA_ARGS__)
|
||||
#define WORKER_DEF_SUBFUNC(_input, _output, _core) \
|
||||
@@ -382,7 +447,7 @@ worker_count(worker_t g)
|
||||
#define WORKER_INIT_LOCAL_OUTPUT(...) \
|
||||
M_MAP(WORKER_INIT_SINGLE_LOCAL_OUTPUT, __VA_ARGS__)
|
||||
#define WORKER_PROPAGATE_SINGLE_OUTPUT(var) \
|
||||
*(_s_data->M_C(var, __ptr)) = var;
|
||||
*(_s_data->M_C(var, _ptr)) = var;
|
||||
#define WORKER_PROPAGATE_LOCAL_OUTPUT(...) \
|
||||
M_MAP(WORKER_PROPAGATE_SINGLE_OUTPUT, __VA_ARGS__)
|
||||
|
||||
@@ -404,11 +469,11 @@ typedef struct worker_s {
|
||||
|
||||
#define worker_init(g, numWorker, extraQueue, resetFunc) do { (void) g; } while (0)
|
||||
#define worker_clear(g) do { (void) g; } while (0)
|
||||
#define worker_start(b) do { (void) b; } while (0)
|
||||
#define worker_spawn(w, b, f, d) do { f(d); } while (0)
|
||||
#define worker_start(b, w) do { (void) b; } while (0)
|
||||
#define worker_spawn(b, f, d) do { f(d); } while (0)
|
||||
#define worker_sync(b) do { (void) b; } while (0)
|
||||
#define worker_count(w) 1
|
||||
#define WORKER_SPAWN(w, b, i, c, o) do { c } while (0)
|
||||
#define WORKER_SPAWN(b, i, c, o) do { c } while (0)
|
||||
|
||||
#endif /* WORKER_DISABLE */
|
||||
|
||||
|
||||
+41
-10
@@ -1,11 +1,11 @@
|
||||
#NOTE: Neither GMP not pthread is not a real dependency. It is only used for the test suite.
|
||||
CC=cc -std=c99
|
||||
WCFLAGS=-Wall -W -pedantic -Wshadow -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wswitch-default -Wswitch-enum -Wcast-align -Wpointer-arith -Wbad-function-cast -Wstrict-overflow=5 -Wstrict-prototypes -Winline -Wundef -Wnested-externs -Wcast-qual -Wshadow -Wunreachable-code -Wlogical-op -Wstrict-aliasing=2 -Wredundant-decls -Wold-style-definition -Wno-unused-function -ftrapv
|
||||
XCFLAGS=
|
||||
CFLAGS=$(WCFLAGS) $(XCFLAGS)
|
||||
CPPFLAGS=-I..
|
||||
LDFLAGS=-pthread -lgmp
|
||||
LDFLAGS=-pthread
|
||||
RM=rm -f
|
||||
LOG_COMPILER=
|
||||
|
||||
all:
|
||||
@echo "Nothing to be done."
|
||||
@@ -23,7 +23,7 @@ all:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -MM -MT `echo $< |sed -e 's/\.c/\.exe/g'` $< > $@
|
||||
|
||||
.exe.log:
|
||||
./$<
|
||||
$(LOG_COMPILER) ./$<
|
||||
|
||||
.exe.valgrind:
|
||||
valgrind ./$<
|
||||
@@ -31,10 +31,10 @@ all:
|
||||
.c.gcov:
|
||||
awk '/START_COVERAGE/ { printit=1} (printit == 0) { print }' $< > $<.first.c
|
||||
awk ' printit { print } /END_COVERAGE/ { printit=1}' $< > $<.end.c
|
||||
$(CC) $(CPPFLAGS) -E -DNDEBUG -DWITHIN_COVERAGE $< |grep -v '^#' |awk ' /END_COVERAGE/ { printit=0} printit { print } /START_COVERAGE/ { printit=1 }' |perl -pi -e "s/;(?![\"'])/;\n/g ; s/}(?![\"'])/}\n/g ; s/{(?![\"'])/{\n/g" > $<.middle.c
|
||||
$(CC) $(CPPFLAGS) -E -DNDEBUG -DWITHIN_COVERAGE $< |grep -v '^#' |awk ' /END_COVERAGE/ { printit=0} printit { print } /START_COVERAGE/ { printit=1 }' |perl -p -e "s/;(?![\"'])/;\n/g ; s/}(?![\"'])/}\n/g ; s/{(?![\"'])/{\n/g" > $<.middle.c
|
||||
cat $<.first.c $<.middle.c $<.end.c > $<.c
|
||||
$(CC) $(CPPFLAGS) $(LDFLAGS) -ftest-coverage -fprofile-arcs -g $<.c -o $<.exe
|
||||
./$<.exe
|
||||
$(LOG_COMPILER) ./$<.exe
|
||||
gcov $<.gcno > `basename $< .c`.synt
|
||||
cat `basename $< .c`.synt
|
||||
mv $<.c.gcov `basename $< .c`.gcov
|
||||
@@ -69,17 +69,25 @@ distclean: clean
|
||||
#####################################################################################################
|
||||
|
||||
gene-list:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -O3 -DNDEBUG -S tgen-mlist.c && less tgen-mlist.s
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -O3 -march=native -DNDEBUG -S tgen-mlist.c && less tgen-mlist.s
|
||||
|
||||
gene-array:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -O3 -DNDEBUG -S tgen-marray.c && less tgen-marray.s
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -O3 -march=native -DNDEBUG -S tgen-marray.c && less tgen-marray.s
|
||||
|
||||
gene-shared:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -O3 -DNDEBUG -S tgen-shared.c && less tgen-shared.s
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -O3 -march=native -DNDEBUG -S tgen-shared.c && less tgen-shared.s
|
||||
|
||||
gene-bitset:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -O3 -DNDEBUG -S tgen-bitset.c && less tgen-bitset.s
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -O3 -march=native -DNDEBUG -S tgen-bitset.c && less tgen-bitset.s
|
||||
|
||||
gene-dict:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -O3 -march=native -DNDEBUG -S tgen-mdict.c && less tgen-mdict.s
|
||||
|
||||
gene-queue:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -O3 -march=native -DNDEBUG -S tgen-queue.c && less tgen-queue.s
|
||||
|
||||
gene-string:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -O3 -march=native -DNDEBUG -S tgen-mstring.c && less tgen-mstring.s
|
||||
|
||||
#####################################################################################################
|
||||
# Coverage
|
||||
@@ -92,6 +100,7 @@ coverage:
|
||||
SYNTHESIS_DATA= M-ALGO test-malgo.c.c test-malgo.synt \
|
||||
M-ARRAY test-marray.c.c test-marray.synt \
|
||||
M-BITSET ../m-bitset.h test-mbitset.synt \
|
||||
M-BBPTREE test-mbptree.c test-mbptree.synt \
|
||||
M-BUFFER test-mbuffer.c.c test-mbuffer.synt \
|
||||
M-CORE test-mcore.c.c test-mcore.synt \
|
||||
M-DEQUE test-mdeque.c.c test-mdeque.synt \
|
||||
@@ -128,10 +137,32 @@ synthesis:
|
||||
valgrind:
|
||||
$(MAKE) `ls test-*.c|sed -e 's/\.c/\.valgrind/g'`
|
||||
|
||||
scan-build:
|
||||
scan-build: clean
|
||||
$(RM) -rf ./mlib-report
|
||||
scan-build -o ./mlib-report $(MAKE) check XCFLAGS="-std=c99"
|
||||
|
||||
sanitize:
|
||||
$(MAKE) clean check XCFLAGS="-fsanitize=address,undefined"
|
||||
$(MAKE) clean check XCFLAGS="-fsanitize=thread -pie -fPIE"
|
||||
|
||||
|
||||
#####################################################################################################
|
||||
# Test All
|
||||
#####################################################################################################
|
||||
|
||||
WCFLAGS_CLANG=-Wall -W -pedantic -Wshadow -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wswitch-default -Wswitch-enum -Wcast-align -Wpointer-arith -Wbad-function-cast -Wstrict-overflow=5 -Wstrict-prototypes -Winline -Wundef -Wnested-externs -Wcast-qual -Wshadow -Wunreachable-code -Wstrict-aliasing=2 -Wredundant-decls -Wold-style-definition -Wno-unused-function -ftrapv
|
||||
WCFLAGS_CPP=-Wall -W -pedantic -Wshadow -Wpointer-arith -Wcast-qual -Wswitch-default -Wswitch-enum -Wcast-align -Wpointer-arith -Wstrict-overflow=5 -Winline -Wundef -Wcast-qual -Wshadow -Wunreachable-code -Wlogical-op -Wstrict-aliasing=2 -Wredundant-decls -Wno-unused-function -ftrapv
|
||||
|
||||
checkall:
|
||||
@$(MAKE) clean ; $(MAKE) check CC="gcc -std=c99"
|
||||
@$(MAKE) clean ; $(MAKE) check CC="gcc -std=c99 -m32"
|
||||
@$(MAKE) clean ; $(MAKE) check CC="gcc -std=c11"
|
||||
@$(MAKE) clean ; $(MAKE) check CC="gcc -std=c11 -m32"
|
||||
@if which clang ; then $(MAKE) clean ; $(MAKE) check CC="clang -std=c99" WCFLAGS="$(WCFLAGS_CLANG)" ; else echo "SYSTEM CLANG not found: SKIP TEST" ; fi
|
||||
@if which g++ ; then $(MAKE) clean ; $(MAKE) check CC="g++ -std=c++11" WCFLAGS="$(WCFLAGS_CPP)" ; else echo "SYSTEM G++ not found: SKIP TEST" ; fi
|
||||
@if which clang++ ; then $(MAKE) clean ; $(MAKE) check CC="clang++ -std=c++11" WCFLAGS="$(WCFLAGS_CLANG)" ; else echo "SYSTEM CLANG++ not found: SKIP TEST" ; fi
|
||||
@if which tcc ; then $(MAKE) clean ; $(MAKE) check CC="tcc -std=c99" ; else echo "SYSTEM TCC not found: SKIP TEST" ; fi
|
||||
@if which musl-gcc ; then $(MAKE) clean ; $(MAKE) check CC="musl-gcc -std=c11" ; else echo "SYSTEM MUSL-GCC not found: SKIP TEST" ; fi
|
||||
@for f in /opt/gcc* ; do if test -x "$$f/bin/gcc" ; then $(MAKE) clean ; $(MAKE) check CC="$$f/bin/gcc -std=c99" ; fi ; done
|
||||
@for f in /opt/clang* ; do if test -x "$$f/bin/clang" ; then $(MAKE) clean ; $(MAKE) check CC="$$f/bin/clang -std=c99" WCFLAGS="$(WCFLAGS_CLANG)" ; fi ; done
|
||||
@for f in /opt/tcc* ; do if test -x "$$f/bin/tcc" ; then $(MAKE) clean ; $(MAKE) check CC="$$f/bin/tcc -std=c99" ; fi ; done
|
||||
+15
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -49,17 +49,30 @@
|
||||
#undef M_UNLIKELY
|
||||
#undef M_MEMORY_FULL
|
||||
#undef M_SWAP
|
||||
#undef STRING_CONTRACT
|
||||
#undef STRINGI_CONTRACT
|
||||
#undef ARRAYI_CONTRACT
|
||||
#undef LIST_CONTRACT
|
||||
#undef DICTI_CONTRACT
|
||||
#undef DICTI_OA_CONTRACT
|
||||
#undef SHAREDI_CONTRACT
|
||||
#undef SHAREDI_RESSOURCE_CONTRACT
|
||||
#undef BUFFERI_CONTRACT
|
||||
#undef BUFFERI_PROTECTED_CONTRACT
|
||||
#undef ILISTI_CONTRACT
|
||||
#undef DEQUEI_CONTRACT
|
||||
#undef QUEUEI_MPMC_CONTRACT
|
||||
#undef RBTREEI_CONTRACT_NODE
|
||||
#undef RBTREEI_CONTRACT
|
||||
#undef BPTREEI_NODE_CONTRACT
|
||||
#undef BPTREEI_CONTRACT
|
||||
#undef LISTI_CONTRACT
|
||||
#undef LISTI_DUAL_PUSH_CONTRACT
|
||||
#undef NDEBUG
|
||||
#undef atomic_init
|
||||
#undef atomic_load
|
||||
#undef atomic_store
|
||||
#undef M_CONST_CAST
|
||||
#undef M_ASSIGN_CAST
|
||||
#else
|
||||
/* For normal builds, empty macros which does nothing */
|
||||
#define START_COVERAGE
|
||||
|
||||
+181
-11
@@ -1,19 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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.
|
||||
*/
|
||||
#include "m-list.h"
|
||||
#include "m-i-list.h"
|
||||
#include "m-array.h"
|
||||
#include "m-string.h"
|
||||
#include "m-deque.h"
|
||||
#include "test-obj.h"
|
||||
#include "m-algo.h"
|
||||
|
||||
typedef struct over_s {
|
||||
unsigned long data;
|
||||
ILIST_INTERFACE(ilist_over, over_s);
|
||||
} over_t;
|
||||
|
||||
// TODO: Test Intrusive List
|
||||
// HASHMAP, RBTREE, B+TREE: Is this useful?
|
||||
ARRAY_DEF(array_int, int)
|
||||
#define M_OPL_array_int_t() ARRAY_OPLIST(array_int)
|
||||
LIST_DEF(list_int, int)
|
||||
ILIST_DEF(ilist_over, over_t, M_POD_OPLIST)
|
||||
LIST_DEF(list_string, string_t, STRING_OPLIST)
|
||||
DEQUE_DEF(deque_obj, testobj_t, TESTOBJ_CMP_OPLIST)
|
||||
#define M_OPL_deque_obj_t() DEQUE_OPLIST(deque_obj, TESTOBJ_CMP_OPLIST)
|
||||
|
||||
#include "coverage.h"
|
||||
START_COVERAGE
|
||||
ALGO_DEF(algo_array, ARRAY_OPLIST(array_int))
|
||||
|
||||
ALGO_DEF(algo_array, array_int_t)
|
||||
ALGO_DEF(algo_list, LIST_OPLIST(list_int))
|
||||
|
||||
ALGO_DEF(algo_over, ILIST_OPLIST(ilist_over, M_POD_OPLIST))
|
||||
ALGO_DEF(algo_string, LIST_OPLIST(list_string, STRING_OPLIST))
|
||||
ALGO_DEF(algo_deque, deque_obj_t)
|
||||
END_COVERAGE
|
||||
|
||||
int g_min, g_max, g_count;
|
||||
@@ -35,24 +71,57 @@ static void func_reduce(int *d, int n)
|
||||
*d += n;
|
||||
}
|
||||
|
||||
static bool func_test_42(int d)
|
||||
{
|
||||
return d == 42;
|
||||
}
|
||||
|
||||
static bool func_test_101(int d)
|
||||
{
|
||||
return d == 101;
|
||||
}
|
||||
|
||||
static bool func_test_pos(int d)
|
||||
{
|
||||
return d >= 0;
|
||||
}
|
||||
|
||||
static bool func_test_both_even_or_odd(int a, int b)
|
||||
{
|
||||
return (a&1) == (b&1);
|
||||
}
|
||||
|
||||
static void test_list(void)
|
||||
{
|
||||
list_int_t l;
|
||||
list_int_init(l);
|
||||
for(int i = 0; i < 100; i++)
|
||||
list_int_push_back (l, i);
|
||||
assert( algo_list_contains(l, 62) == true);
|
||||
assert( algo_list_contains(l, -1) == false);
|
||||
assert( algo_list_contain_p(l, 62) == true);
|
||||
assert( algo_list_contain_p(l, -1) == false);
|
||||
|
||||
assert( algo_list_count(l, 1) == 1);
|
||||
list_int_push_back (l, 17);
|
||||
assert( algo_list_count(l, 17) == 2);
|
||||
assert( algo_list_count(l, -1) == 0);
|
||||
|
||||
assert (algo_list_all_of_p(l, func_test_42) == false);
|
||||
assert (algo_list_any_of_p(l, func_test_42) == true);
|
||||
assert (algo_list_none_of_p(l, func_test_42) == false);
|
||||
assert (algo_list_all_of_p(l, func_test_101) == false);
|
||||
assert (algo_list_any_of_p(l, func_test_101) == false);
|
||||
assert (algo_list_none_of_p(l, func_test_101) == true);
|
||||
assert (algo_list_all_of_p(l, func_test_pos) == true);
|
||||
assert (algo_list_any_of_p(l, func_test_pos) == true);
|
||||
assert (algo_list_none_of_p(l, func_test_pos) == false);
|
||||
|
||||
#define f(x) assert((x) >= 0 && (x) < 100);
|
||||
ALGO_MAP(l, LIST_OPLIST(list_int), f);
|
||||
ALGO_FOR_EACH(l, LIST_OPLIST(list_int), f);
|
||||
#define g(y, x) assert((x) >= 0 && (x) < y);
|
||||
ALGO_MAP(l, LIST_OPLIST(list_int), g, 100);
|
||||
ALGO_FOR_EACH(l, LIST_OPLIST(list_int), g, 100);
|
||||
|
||||
assert( algo_list_count_if(l, func_test_42) == 1);
|
||||
assert( algo_list_count_if(l, func_test_101) == 0);
|
||||
|
||||
int *p = algo_list_min(l);
|
||||
assert(p != NULL && *p == 0);
|
||||
@@ -70,6 +139,16 @@ static void test_list(void)
|
||||
algo_list_find_last (it2, l, 3);
|
||||
assert (!list_int_end_p (it2));
|
||||
assert (!list_int_it_equal_p(it1, it2));
|
||||
|
||||
algo_list_find_if(it1, l, func_test_101);
|
||||
assert (list_int_end_p (it1));
|
||||
algo_list_find_if(it1, l, func_test_42);
|
||||
assert (!list_int_end_p (it1));
|
||||
assert (*list_int_cref(it1) == 42);
|
||||
|
||||
algo_list_mismatch(it1, it2, l, l);
|
||||
assert (list_int_end_p (it1));
|
||||
assert (list_int_end_p (it2));
|
||||
|
||||
for(int i = -100; i < 100; i+=2)
|
||||
list_int_push_back (l, i);
|
||||
@@ -91,16 +170,29 @@ static void test_array(void)
|
||||
array_int_init(l);
|
||||
for(int i = 0; i < 100; i++)
|
||||
array_int_push_back (l, i);
|
||||
assert( algo_array_contains(l, 62) == true);
|
||||
assert( algo_array_contains(l, -1) == false);
|
||||
assert( algo_array_contain_p(l, 62) == true);
|
||||
assert( algo_array_contain_p(l, -1) == false);
|
||||
assert( algo_array_sort_p(l) == true);
|
||||
|
||||
assert (algo_array_all_of_p(l, func_test_42) == false);
|
||||
assert (algo_array_any_of_p(l, func_test_42) == true);
|
||||
assert (algo_array_none_of_p(l, func_test_42) == false);
|
||||
assert (algo_array_all_of_p(l, func_test_101) == false);
|
||||
assert (algo_array_any_of_p(l, func_test_101) == false);
|
||||
assert (algo_array_none_of_p(l, func_test_101) == true);
|
||||
assert (algo_array_all_of_p(l, func_test_pos) == true);
|
||||
assert (algo_array_any_of_p(l, func_test_pos) == true);
|
||||
assert (algo_array_none_of_p(l, func_test_pos) == false);
|
||||
|
||||
assert( algo_array_count(l, 1) == 1);
|
||||
array_int_push_back (l, 17);
|
||||
assert( algo_array_count(l, 17) == 2);
|
||||
assert( algo_array_count(l, -1) == 0);
|
||||
assert( algo_array_sort_p(l) == false);
|
||||
|
||||
assert( algo_array_count_if(l, func_test_42) == 1);
|
||||
assert( algo_array_count_if(l, func_test_101) == 0);
|
||||
|
||||
array_int_it_t it;
|
||||
algo_array_find_last(it, l, 17);
|
||||
assert (!array_int_end_p (it));
|
||||
@@ -111,12 +203,13 @@ static void test_array(void)
|
||||
assert (array_int_end_p (it));
|
||||
|
||||
#define f(x) assert((x) >= 0 && (x) < 100);
|
||||
ALGO_MAP(l, ARRAY_OPLIST(array_int), f);
|
||||
ALGO_FOR_EACH(l, ARRAY_OPLIST(array_int), f);
|
||||
ALGO_FOR_EACH(l, array_int_t, f);
|
||||
|
||||
g_min = 0;
|
||||
g_max = 99;
|
||||
g_count = 0;
|
||||
algo_array_map(l, g_f);
|
||||
algo_array_for_each(l, g_f);
|
||||
assert(g_count == 101);
|
||||
|
||||
int n;
|
||||
@@ -152,6 +245,31 @@ static void test_array(void)
|
||||
assert (array_int_size(l) == 102);
|
||||
assert( algo_array_sort_p(l) == true);
|
||||
|
||||
M_LET(l2, array_int_t) {
|
||||
array_int_set(l2, l);
|
||||
array_int_it_t it1, it2;
|
||||
algo_array_mismatch(it1, it2, l, l2);
|
||||
assert (array_int_end_p (it1));
|
||||
assert (array_int_end_p (it2));
|
||||
array_int_pop_back(NULL, l2);
|
||||
array_int_push_back(l2, 159);
|
||||
algo_array_mismatch(it1, it2, l, l2);
|
||||
assert (!array_int_end_p (it1));
|
||||
assert (!array_int_end_p (it2));
|
||||
assert (*array_int_cref(it1) == 1742);
|
||||
assert (*array_int_cref(it2) == 159);
|
||||
algo_array_mismatch_if(it1, it2, l, l2, func_test_both_even_or_odd);
|
||||
assert (!array_int_end_p (it1));
|
||||
assert (!array_int_end_p (it2));
|
||||
assert (*array_int_cref(it1) == 1742);
|
||||
assert (*array_int_cref(it2) == 159);
|
||||
array_int_pop_back(NULL, l2);
|
||||
array_int_push_back(l2, 152);
|
||||
algo_array_mismatch_if(it1, it2, l, l2, func_test_both_even_or_odd);
|
||||
assert (array_int_end_p (it1));
|
||||
assert (array_int_end_p (it2));
|
||||
}
|
||||
|
||||
array_int_clean(l);
|
||||
assert (algo_array_min(l) == NULL);
|
||||
assert (algo_array_max(l) == NULL);
|
||||
@@ -163,11 +281,31 @@ static void test_array(void)
|
||||
assert (array_int_size(l) == 0);
|
||||
assert( algo_array_sort_p(l) == true);
|
||||
|
||||
for(int i = -14025; i < 324035; i+=17)
|
||||
array_int_push_back(l, i);
|
||||
for(int i = -14025; i < 324035; i+=7)
|
||||
array_int_push_back(l, i);
|
||||
assert( algo_array_sort_p(l) == false);
|
||||
array_int_special_stable_sort(l);
|
||||
assert( algo_array_sort_p(l) == true);
|
||||
|
||||
array_int_clear(l);
|
||||
|
||||
ALGO_INIT_VA(l, ARRAY_OPLIST(array_int), 1, 2, 3, 4, 5);
|
||||
assert (array_int_size(l) == 5);
|
||||
assert (algo_array_sort_p(l) == true);
|
||||
assert (algo_array_sort_dsc_p(l) == false);
|
||||
algo_array_sort_dsc(l);
|
||||
assert (algo_array_sort_p(l) == false);
|
||||
assert (algo_array_sort_dsc_p(l) == true);
|
||||
|
||||
array_int_clear(l);
|
||||
|
||||
ALGO_LET_INIT_VA(arr, array_int_t, 1, 5, 34) {
|
||||
assert (array_int_size(arr) == 3);
|
||||
assert (algo_array_sort_p(arr) == true);
|
||||
assert (algo_array_sort_dsc_p(arr) == false);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_string(void)
|
||||
@@ -185,6 +323,16 @@ static void test_string(void)
|
||||
assert (string_equal_str_p(s, " John"));
|
||||
assert (list_string_empty_p(l));
|
||||
|
||||
string_set_str(s, "Hello,,John");
|
||||
algo_string_split(l, s, ',');
|
||||
list_string_pop_back (&s, l);
|
||||
assert (string_equal_str_p(s, "Hello"));
|
||||
list_string_pop_back (&s, l);
|
||||
assert (string_equal_str_p(s, ""));
|
||||
list_string_pop_back (&s, l);
|
||||
assert (string_equal_str_p(s, "John"));
|
||||
assert (list_string_empty_p(l));
|
||||
|
||||
list_string_push_back (l, STRING_CTE("John"));
|
||||
list_string_push_back (l, STRING_CTE("Who"));
|
||||
list_string_push_back (l, STRING_CTE("Is"));
|
||||
@@ -216,6 +364,8 @@ static void test_extract(void)
|
||||
#define inc(d, c) (d) += (c)
|
||||
ALGO_REDUCE(dst, a, ARRAY_OPLIST(array_int), inc);
|
||||
assert (dst == 100*99/2-10*11/2);
|
||||
ALGO_REDUCE(dst, a, array_int_t, inc);
|
||||
assert (dst == 100*99/2-10*11/2);
|
||||
#define sqr(d, c) (d) = (c)*(c)
|
||||
ALGO_REDUCE(dst, a, ARRAY_OPLIST(array_int), inc, sqr);
|
||||
assert (dst == 327965);
|
||||
@@ -236,11 +386,31 @@ static void test_extract(void)
|
||||
list_int_clear(l);
|
||||
}
|
||||
|
||||
ARRAY_DEF(aint, int)
|
||||
LIST_DEF(lint, int)
|
||||
#define M_OPL_aint_t() ARRAY_OPLIST(aint)
|
||||
#define M_OPL_lint_t() LIST_OPLIST(lint)
|
||||
|
||||
static void test_insert(void)
|
||||
{
|
||||
ALGO_LET_INIT_VA(a, lint_t, 1, 2, 3, 4)
|
||||
ALGO_LET_INIT_VA(b, aint_t, -1, -2, -3) {
|
||||
lint_it_t i;
|
||||
lint_it(i, a);
|
||||
ALGO_INSERT_AT(a, lint_t, i, b, aint_t);
|
||||
// TBC: order ok for the list (the reverse is confusing...)?
|
||||
ALGO_LET_INIT_VA(c, lint_t, 1, 2, 3, -3, -2, -1, 4) {
|
||||
assert (lint_equal_p (c, a));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_list();
|
||||
test_array();
|
||||
test_string();
|
||||
test_extract();
|
||||
test_insert();
|
||||
exit(0);
|
||||
}
|
||||
+79
-40
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -21,44 +21,19 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <gmp.h>
|
||||
#include "test-obj.h"
|
||||
#include "m-array.h"
|
||||
|
||||
static void my_mpz_out_str(FILE *f, const mpz_t z)
|
||||
{
|
||||
mpz_out_str (f, 10, z);
|
||||
}
|
||||
|
||||
static bool my_mpz_in_str(mpz_t z, FILE *f)
|
||||
{
|
||||
return mpz_inp_str (z, f, 10) > 0;
|
||||
}
|
||||
|
||||
static bool my_mpz_equal_p(const mpz_t z1, const mpz_t z2)
|
||||
{
|
||||
return mpz_cmp (z1, z2) == 0;
|
||||
}
|
||||
|
||||
#include "m-string.h"
|
||||
static void my_mpz_str(string_t str, const mpz_t z, bool append)
|
||||
{
|
||||
char *s = mpz_get_str(NULL, 10, z);
|
||||
assert (s != NULL);
|
||||
if (append) string_cat_str (str, s);
|
||||
else string_set_str (str, s);
|
||||
free(s);
|
||||
}
|
||||
|
||||
#include "coverage.h"
|
||||
|
||||
START_COVERAGE
|
||||
ARRAY_DEF(array_uint, unsigned int)
|
||||
ARRAY_DEF(array_mpz, mpz_t,
|
||||
(INIT(mpz_init), INIT_SET(mpz_init_set), SET(mpz_set), CLEAR(mpz_clear), \
|
||||
OUT_STR(my_mpz_out_str), IN_STR(my_mpz_in_str), EQUAL(my_mpz_equal_p), \
|
||||
GET_STR(my_mpz_str) ))
|
||||
ARRAY_DEF(array_mpz, testobj_t, TESTOBJ_OPLIST)
|
||||
END_COVERAGE
|
||||
#define ARRAY_UINT_OPLIST ARRAY_OPLIST(array_uint)
|
||||
|
||||
// Array with the minimum number of methods.
|
||||
ARRAY_DEF(array_min_z, testobj_t, (INIT_SET(testobj_init_set), SET(testobj_set), CLEAR(testobj_clear)))
|
||||
|
||||
static void test_uint(void)
|
||||
{
|
||||
array_uint_t v;
|
||||
@@ -86,6 +61,10 @@ static void test_uint(void)
|
||||
}
|
||||
assert( s == 100 * 99 /2);
|
||||
|
||||
array_uint_pop_at(&s, v, 49);
|
||||
assert (s == 49);
|
||||
array_uint_push_at(v, 49, 49);
|
||||
|
||||
array_uint_pop_at (NULL, v, 0);
|
||||
assert(array_uint_size(v) == 99);
|
||||
s= 0;
|
||||
@@ -125,9 +104,17 @@ static void test_uint(void)
|
||||
assert(array_uint_size(v) == 1000);
|
||||
assert(*array_uint_cget(v, 999) == 0);
|
||||
|
||||
array_uint_t v2;
|
||||
array_uint_t v2, v3;
|
||||
array_uint_init_set(v2,v);
|
||||
array_uint_init(v3);
|
||||
array_uint_push_back(v3, 459320);
|
||||
array_uint_set(v3, v);
|
||||
assert (array_uint_equal_p(v3, v));
|
||||
array_uint_push_back(v3, 459320);
|
||||
array_uint_set(v3, v);
|
||||
assert (array_uint_equal_p(v3, v));
|
||||
array_uint_clear(v);
|
||||
array_uint_clear(v3);
|
||||
assert(array_uint_size(v2) == 1000);
|
||||
assert(*array_uint_cget(v2, 999) == 0);
|
||||
array_uint_push_at (v2, 0, 1742);
|
||||
@@ -162,6 +149,39 @@ static void test_uint(void)
|
||||
assert (array_uint_capacity(v) > 0);
|
||||
array_uint_reserve(v, 0);
|
||||
assert (array_uint_capacity(v) == 0);
|
||||
|
||||
array_uint_clean(v2);
|
||||
array_uint_splice(v, v2);
|
||||
assert (array_uint_size(v) == 0);
|
||||
assert (array_uint_size(v2) == 0);
|
||||
|
||||
for(unsigned i = 0; i < 100; i++) {
|
||||
array_uint_push_back(v, i);
|
||||
array_uint_push_back(v2, 100+i);
|
||||
}
|
||||
array_uint_splice(v, v2);
|
||||
assert (array_uint_size(v) == 200);
|
||||
assert (array_uint_size(v2) == 0);
|
||||
for(unsigned i = 0; i < 200; i++) {
|
||||
assert (i == *array_uint_get(v, i));
|
||||
}
|
||||
|
||||
x = 1478963;
|
||||
s = *array_uint_back(v);
|
||||
array_uint_push_move(v, &x);
|
||||
assert (*array_uint_back(v) == 1478963);
|
||||
x = 0;
|
||||
array_uint_pop_move(&x, v);
|
||||
assert (x == 1478963);
|
||||
assert (s == *array_uint_back(v));
|
||||
|
||||
s = array_uint_size (v);
|
||||
bool b = array_uint_erase (v, 12459);
|
||||
assert (b == false);
|
||||
assert (s == array_uint_size (v));
|
||||
b = array_uint_erase (v, 0);
|
||||
assert (b == true);
|
||||
assert (s == array_uint_size (v)+1);
|
||||
|
||||
array_uint_clear(v);
|
||||
array_uint_clear(v2);
|
||||
@@ -170,11 +190,11 @@ static void test_uint(void)
|
||||
static void test_mpz(void)
|
||||
{
|
||||
array_mpz_t array1, array2;
|
||||
mpz_t z;
|
||||
testobj_t z;
|
||||
|
||||
array_mpz_init (array1);
|
||||
array_mpz_init (array2);
|
||||
mpz_init (z);
|
||||
testobj_init (z);
|
||||
|
||||
// Test empty
|
||||
FILE *f = fopen ("a-marray.dat", "wt");
|
||||
@@ -191,7 +211,7 @@ static void test_mpz(void)
|
||||
|
||||
// Test non empty
|
||||
for(int n = 0; n < 1000; n++) {
|
||||
mpz_set_ui (z, n);
|
||||
testobj_set_ui (z, n);
|
||||
array_mpz_push_back (array1, z);
|
||||
}
|
||||
|
||||
@@ -212,18 +232,37 @@ static void test_mpz(void)
|
||||
M_LET(str, STRING_OPLIST) {
|
||||
array_mpz_get_str(str, array1, false);
|
||||
assert (string_cmp_str (str, "[]") == 0);
|
||||
mpz_set_ui (z, 17);
|
||||
const char *sp;
|
||||
b = array_mpz_parse_str(array2, string_get_cstr(str), &sp);
|
||||
assert(b);
|
||||
assert(*sp == 0);
|
||||
assert(array_mpz_equal_p(array1, array2));
|
||||
|
||||
testobj_set_ui (z, 17);
|
||||
array_mpz_push_back (array1, z);
|
||||
array_mpz_get_str(str, array1, false);
|
||||
assert (string_cmp_str (str, "[17]") == 0);
|
||||
mpz_set_ui (z, 42);
|
||||
b = array_mpz_parse_str(array2, string_get_cstr(str), &sp);
|
||||
assert(b);
|
||||
assert(*sp == 0);
|
||||
assert(array_mpz_equal_p(array1, array2));
|
||||
|
||||
testobj_set_ui (z, 42);
|
||||
array_mpz_push_back (array1, z);
|
||||
array_mpz_get_str(str, array1, true);
|
||||
assert (string_cmp_str (str, "[17][17,42]") == 0);
|
||||
b = array_mpz_parse_str(array2, string_get_cstr(str), &sp);
|
||||
assert(b);
|
||||
assert(strcmp(sp, "[17,42]") == 0);
|
||||
assert(!array_mpz_equal_p(array1, array2));
|
||||
b = array_mpz_parse_str(array2, sp, &sp);
|
||||
assert(b);
|
||||
assert(strcmp(sp, "") == 0);
|
||||
assert(array_mpz_equal_p(array1, array2));
|
||||
// NOTE: Not the same order than list...
|
||||
}
|
||||
|
||||
mpz_clear (z);
|
||||
testobj_clear (z);
|
||||
array_mpz_clear(array2);
|
||||
array_mpz_clear(array1);
|
||||
}
|
||||
@@ -253,7 +292,7 @@ static void test_d(void)
|
||||
for(int i = 9; i < 19; i++)
|
||||
assert (*array_uint_get (a1, i) == 18U-i);
|
||||
|
||||
array_uint_set_at2(a1, 100, 100);
|
||||
*array_uint_get_at(a1, 100) = 100;
|
||||
assert (*array_uint_back(a1) == 100);
|
||||
assert (array_uint_size(a1) == 101);
|
||||
array_uint_pop_back (NULL, a1);
|
||||
|
||||
+83
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -30,6 +30,7 @@ static void test1(void)
|
||||
bitset_t set;
|
||||
|
||||
bitset_init(set);
|
||||
assert(bitset_empty_p(set) == true);
|
||||
for(int i = 0; i < 100; i ++) {
|
||||
bitset_push_back(set, i%2);
|
||||
assert (bitset_back(set) == (i%2));
|
||||
@@ -39,7 +40,9 @@ static void test1(void)
|
||||
}
|
||||
assert(bitset_size(set) == 100);
|
||||
assert(bitset_empty_p(set) == false);
|
||||
|
||||
assert(bitset_front(set) == false);
|
||||
assert(bitset_back(set) == true);
|
||||
|
||||
bitset_t set2;
|
||||
bitset_init_set(set2, set);
|
||||
|
||||
@@ -105,6 +108,19 @@ static void test1(void)
|
||||
assert (b == false);
|
||||
assert (bitset_size(set) == 196);
|
||||
|
||||
bitset_parse_str(set, "[1010101010101010101000000000000000000000000000000000000000000000000000001111]", NULL);
|
||||
bitset_push_at(set, 23, true);
|
||||
bitset_t set3;
|
||||
bitset_init(set3);
|
||||
bitset_parse_str(set3, "[10101010101010101010000100000000000000000000000000000000000000000000000001111]", NULL);
|
||||
assert(bitset_equal_p(set, set3));
|
||||
bitset_push_at(set, 25, true);
|
||||
const char *sp;
|
||||
bitset_parse_str(set3, "[101010101010101010100001010000000000000000000000000000000000000000000000001111]", &sp);
|
||||
assert(bitset_equal_p(set, set3));
|
||||
assert (*sp == 0);
|
||||
bitset_clear(set3);
|
||||
|
||||
bitset_clean(set);
|
||||
for(int i = 0; i < 200; i++)
|
||||
bitset_push_back(set, (i*17547854547UL)&1) ;
|
||||
@@ -144,6 +160,15 @@ static void test1(void)
|
||||
assert (bitset_get(set2, 2) == false);
|
||||
assert (bitset_get(set2, 75) == true);
|
||||
|
||||
bitset_set_at (set2, 3, false);
|
||||
bitset_set_at (set2, 76, true);
|
||||
bitset_flip_at (set2, 2);
|
||||
bitset_flip_at (set2, 75);
|
||||
assert (bitset_get(set2, 2) == true);
|
||||
assert (bitset_get(set2, 3) == false);
|
||||
assert (bitset_get(set2, 75) == false);
|
||||
assert (bitset_get(set2, 76) == true);
|
||||
|
||||
bitset_set(set, set2);
|
||||
bitset_set_at(set, 199, !bitset_get(set, 199));
|
||||
assert(!bitset_equal_p(set, set2));
|
||||
@@ -182,6 +207,20 @@ static void test1(void)
|
||||
bitset_clear(set2);
|
||||
}
|
||||
|
||||
static void test2(void)
|
||||
{
|
||||
M_LET(set1, bitset_t) {
|
||||
bitset_t set2;
|
||||
bitset_push_back(set1, true);
|
||||
bitset_init_move(set2, set1);
|
||||
bool b;
|
||||
bitset_pop_back(&b, set2);
|
||||
assert(b);
|
||||
bitset_init(set1);
|
||||
bitset_move(set1, set2);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_str(void)
|
||||
{
|
||||
bitset_t set1, set2;
|
||||
@@ -253,15 +292,15 @@ static void test_logic(void)
|
||||
bitset_set_str(s2, "[11100000000000000000010000000000000000000000000000000000000000000]");
|
||||
assert(bitset_equal_p(s1, s2));
|
||||
|
||||
bitset_set_str(s1, "[1010000000000000000001111111111110]");
|
||||
bitset_set_str(s2, "[1100000000000000000111111111111111]");
|
||||
bitset_set_str(s1, "[10100000000000000000011111111111100000000000000000000000000000000]");
|
||||
bitset_set_str(s2, "[110000000000000000011111111111111100000000000000000000000000000000]");
|
||||
bitset_xor (s1, s2);
|
||||
bitset_set_str(s2, "[0110000000000000000110000000000001]");
|
||||
bitset_set_str(s2, "[01100000000000000001100000000000010000000000000000000000000000000]");
|
||||
assert(bitset_equal_p(s1, s2));
|
||||
|
||||
bitset_set_str(s1, "[1010000000000000000000000000000000000]");
|
||||
bitset_set_str(s1, "[1010000000000000000000000000000000000000000000000000000000000000001]");
|
||||
bitset_not (s1);
|
||||
bitset_set_str(s2, "[0101111111111111111111111111111111111]");
|
||||
bitset_set_str(s2, "[0101111111111111111111111111111111111111111111111111111111111111110]");
|
||||
assert(bitset_equal_p(s1, s2));
|
||||
|
||||
}
|
||||
@@ -274,11 +313,48 @@ static void test_let(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void test_clz(void)
|
||||
{
|
||||
M_LET(s1, bitset_t) {
|
||||
bitset_push_back(s1, false);
|
||||
assert (bitset_clz(s1) == 1);
|
||||
bitset_push_back(s1, false);
|
||||
assert (bitset_clz(s1) == 2);
|
||||
bitset_set_at(s1, 0, true);
|
||||
assert (bitset_clz(s1) == 1);
|
||||
bitset_set_at(s1, 1, true);
|
||||
assert (bitset_clz(s1) == 0);
|
||||
for(int i = 0; i < 62; i++)
|
||||
bitset_push_back(s1, false);
|
||||
assert (bitset_clz(s1) == 62);
|
||||
bitset_set_at(s1, 63, true);
|
||||
assert (bitset_clz(s1) == 0);
|
||||
bitset_push_back(s1, false);
|
||||
assert (bitset_clz(s1) == 1);
|
||||
bitset_set_at(s1, 64, true);
|
||||
assert (bitset_clz(s1) == 0);
|
||||
for(size_t i = 0; i < 620; i++) {
|
||||
bitset_push_back(s1, false);
|
||||
assert (bitset_clz(s1) == i+1);
|
||||
}
|
||||
bitset_set_at(s1, 64+620, true);
|
||||
assert (bitset_clz(s1) == 0);
|
||||
for(size_t i = 1; i < 64+1+620; i++) {
|
||||
bitset_set_at(s1, i, false);
|
||||
}
|
||||
assert (bitset_clz(s1) == 64+620);
|
||||
bitset_set_at(s1, 0, false);
|
||||
assert (bitset_clz(s1) == 64+620+1);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test1();
|
||||
test2();
|
||||
test_str();
|
||||
test_logic();
|
||||
test_let();
|
||||
test_clz();
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,427 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "m-string.h"
|
||||
#include "m-bptree.h"
|
||||
#include "test-obj.h"
|
||||
#include "coverage.h"
|
||||
|
||||
START_COVERAGE
|
||||
BPTREE_DEF2(btree, 3, int, M_DEFAULT_OPLIST, int, M_DEFAULT_OPLIST)
|
||||
END_COVERAGE
|
||||
BPTREE_DEF2(btree_my, 3, testobj_t, TESTOBJ_CMP_OPLIST, testobj_t, TESTOBJ_CMP_OPLIST)
|
||||
|
||||
BPTREE_DEF2(btree_int, 17, int, int)
|
||||
BPTREE_DEF(btree_intset, 13, int)
|
||||
BPTREE_DEF(btree_myset, 15, testobj_t, TESTOBJ_CMP_OPLIST)
|
||||
|
||||
static void test1(void)
|
||||
{
|
||||
btree_t b;
|
||||
btree_init(b);
|
||||
assert (btree_empty_p(b));
|
||||
btree_set_at(b, 12, 34);
|
||||
assert (!btree_empty_p(b));
|
||||
assert (btree_size(b) == 1);
|
||||
assert (btree_get(b, 12) != NULL);
|
||||
assert (*btree_get(b, 12) == 34);
|
||||
btree_set_at(b, 11, 35);
|
||||
assert (!btree_empty_p(b));
|
||||
assert (btree_size(b) == 2);
|
||||
assert (btree_get(b, 11) != NULL);
|
||||
assert (*btree_get(b, 11) == 35);
|
||||
btree_set_at(b, 1, 36);
|
||||
assert (!btree_empty_p(b));
|
||||
assert (btree_size(b) == 3);
|
||||
assert (btree_get(b, 1) != NULL);
|
||||
assert (*btree_get(b, 1) == 36);
|
||||
btree_set_at(b, 25, 37);
|
||||
assert (!btree_empty_p(b));
|
||||
assert (btree_size(b) == 4);
|
||||
assert (btree_get(b, 25) != NULL);
|
||||
assert (*btree_get(b, 25) == 37);
|
||||
btree_set_at(b, -25, 38);
|
||||
assert (btree_size(b) == 5);
|
||||
assert (btree_get(b, -25) != NULL);
|
||||
assert (*btree_get(b, -25) == 38);
|
||||
btree_set_at(b, 5, 39);
|
||||
assert (btree_size(b) == 6);
|
||||
assert (*btree_get(b, -25) == 38);
|
||||
btree_set_at(b, 5, 40);
|
||||
assert (btree_size(b) == 6);
|
||||
assert (*btree_get(b, 5) == 40);
|
||||
btree_set_at(b, -5, 41);
|
||||
assert (*btree_get(b, -5) == 41);
|
||||
for(int i = -1000; i < 1000; i+=3) {
|
||||
btree_set_at(b, i, 2*i);
|
||||
assert (btree_get(b, i) != NULL);
|
||||
assert (*btree_get(b, i) == 2*i);
|
||||
}
|
||||
for(int i = -1000+1; i < 1000; i+=3) {
|
||||
btree_set_at(b, i, 2*i);
|
||||
assert (btree_get(b, i) != NULL);
|
||||
assert (*btree_get(b, i) == 2*i);
|
||||
}
|
||||
for(int i = -1000+2; i < 1000; i+=3) {
|
||||
btree_set_at(b, i, 2*i);
|
||||
assert (btree_get(b, i) != NULL);
|
||||
assert (*btree_get(b, i) == 2*i);
|
||||
}
|
||||
for(int i = -1000; i < 1000; i++) {
|
||||
assert (btree_get(b, i) != NULL);
|
||||
assert (*btree_get(b, i) == 2*i);
|
||||
}
|
||||
assert (btree_size(b) == 2000);
|
||||
bool r = btree_erase(b, 0);
|
||||
assert (r == true);
|
||||
assert (btree_get(b, 0) == NULL);
|
||||
assert (btree_size(b) == 1999);
|
||||
r = btree_erase(b, 1);
|
||||
assert (r == true);
|
||||
assert (btree_get(b, 1) == NULL);
|
||||
assert (btree_size(b) == 1998);
|
||||
r = btree_erase(b, 2);
|
||||
assert (r == true);
|
||||
assert (btree_get(b, 2) == NULL);
|
||||
assert (btree_size(b) == 1997);
|
||||
r = btree_erase(b, 0);
|
||||
assert (r == false);
|
||||
assert (btree_get(b, 0) == NULL);
|
||||
assert (btree_size(b) == 1997);
|
||||
btree_erase(b, -1);
|
||||
assert (btree_get(b, -1) == NULL);
|
||||
btree_erase(b, -2);
|
||||
assert (btree_get(b, -2) == NULL);
|
||||
btree_erase(b, 3);
|
||||
assert (btree_get(b, 3) == NULL);
|
||||
btree_erase(b, 4);
|
||||
assert (btree_get(b, 4) == NULL);
|
||||
btree_erase(b, -3);
|
||||
assert (btree_get(b, -3) == NULL);
|
||||
btree_erase(b, 5);
|
||||
assert (btree_get(b, 5) == NULL);
|
||||
btree_erase(b, -5);
|
||||
assert (btree_get(b, -5) == NULL);
|
||||
btree_erase(b, -4);
|
||||
assert (btree_get(b, -4) == NULL);
|
||||
btree_erase(b, 6);
|
||||
assert (btree_get(b, 6) == NULL);
|
||||
btree_erase(b, 7);
|
||||
assert (btree_get(b, 7) == NULL);
|
||||
for(int i = -1000; i < 0; i++) {
|
||||
btree_erase(b, i);
|
||||
assert (btree_get(b, i) == NULL);
|
||||
}
|
||||
for(int i = 0; i < 1000; i++) {
|
||||
btree_erase(b, i);
|
||||
assert (btree_get(b, i) == NULL);
|
||||
}
|
||||
assert (btree_size(b) == 0);
|
||||
assert (btree_empty_p(b));
|
||||
btree_clear(b);
|
||||
}
|
||||
|
||||
static void test2(void)
|
||||
{
|
||||
btree_t b;
|
||||
btree_init(b);
|
||||
for(int i = 0; i < 100; i++)
|
||||
btree_set_at(b, i, 100*i);
|
||||
for(int i = 0; i < 100; i++)
|
||||
assert (*btree_cget(b, i) == 100*i);
|
||||
assert (btree_size(b) == 100);
|
||||
int k;
|
||||
bool r = btree_pop_at(NULL, b, 0);
|
||||
assert (r == true);
|
||||
assert (btree_size(b) == 99);
|
||||
r = btree_pop_at(&k, b, 1);
|
||||
assert (r == true);
|
||||
assert (btree_size(b) == 98);
|
||||
assert (k == 100);
|
||||
k = -2;
|
||||
r = btree_pop_at(&k, b, 1);
|
||||
assert (r == false);
|
||||
assert (k == -2);
|
||||
r = btree_pop_at(NULL, b, 0);
|
||||
assert (r == false);
|
||||
btree_clean(b);
|
||||
assert (btree_size(b) == 0);
|
||||
assert (btree_empty_p(b));
|
||||
btree_clear(b);
|
||||
}
|
||||
|
||||
static void test3(void)
|
||||
{
|
||||
btree_t b;
|
||||
btree_init(b);
|
||||
|
||||
for(int i = 0; i < 1000; i++)
|
||||
btree_set_at(b, i, 1000*i);
|
||||
assert(btree_size(b) == 1000);
|
||||
for(int i = 0; i < 1000; i++) {
|
||||
bool r = btree_erase(b, i);
|
||||
assert (r == true);
|
||||
}
|
||||
assert(btree_size(b) == 0);
|
||||
|
||||
for(int i = 1000-1; i >= 0; i--)
|
||||
btree_set_at(b, i, 1000*i);
|
||||
assert(btree_size(b) == 1000);
|
||||
for(int i = 1000-1; i >= 0; i--) {
|
||||
bool r = btree_erase(b, i);
|
||||
assert (r == true);
|
||||
}
|
||||
assert(btree_size(b) == 0);
|
||||
|
||||
btree_clear(b);
|
||||
}
|
||||
|
||||
static void test4(void)
|
||||
{
|
||||
btree_t b1, b2;
|
||||
btree_init(b1);
|
||||
|
||||
for(int i = 0; i < 1000; i++)
|
||||
btree_set_at(b1, i, 1000*i);
|
||||
assert(btree_size(b1) == 1000);
|
||||
btree_init_set(b2, b1);
|
||||
assert (btree_size(b2) == 1000);
|
||||
assert (btree_equal_p(b1, b2));
|
||||
for(int i = 0; i < 1000; i++) {
|
||||
bool r = btree_erase(b2, i);
|
||||
assert (r == true);
|
||||
assert (!btree_equal_p(b1, b2));
|
||||
}
|
||||
assert(btree_size(b2) == 0);
|
||||
|
||||
for(int i = 5000; i < 10000; i++)
|
||||
btree_set_at(b2, i, 1000*i);
|
||||
assert(btree_size(b2) == 5000);
|
||||
btree_set(b2, b1);
|
||||
assert(btree_size(b2) == 1000);
|
||||
assert (btree_equal_p(b1, b2));
|
||||
for(int i = 0; i < 1000; i++) {
|
||||
bool r = btree_erase(b2, i);
|
||||
assert (r == true);
|
||||
}
|
||||
assert(btree_size(b2) == 0);
|
||||
|
||||
btree_set(b1, b2);
|
||||
assert(btree_size(b1) == 0);
|
||||
assert (btree_equal_p(b1, b2));
|
||||
|
||||
btree_clear(b1);
|
||||
btree_clear(b2);
|
||||
|
||||
btree_init(b1);
|
||||
btree_init(b2);
|
||||
for(int i = -50; i < 50; i++)
|
||||
btree_set_at(b1, i, 1000*i);
|
||||
assert (btree_size(b1) == 100);
|
||||
for(int i = -10; i < 10; i++)
|
||||
btree_set_at(b2, i, 1000*i);
|
||||
assert (btree_size(b2) == 20);
|
||||
btree_swap(b1, b2);
|
||||
assert (btree_size(b1) == 20);
|
||||
assert (btree_size(b2) == 100);
|
||||
btree_move(b1, b2);
|
||||
btree_clear(b1);
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void test5(void)
|
||||
{
|
||||
btree_t b;
|
||||
btree_init(b);
|
||||
|
||||
for(int i = 0; i < 1000; i+=3)
|
||||
btree_set_at(b, i, 1000*i);
|
||||
for(int i = 1; i < 1000; i+=3)
|
||||
btree_set_at(b, i, 1000*i);
|
||||
for(int i = 2; i < 1000; i+=3)
|
||||
btree_set_at(b, i, 1000*i);
|
||||
assert(btree_size(b) == 1000);
|
||||
|
||||
assert (*btree_cmin(b) == 0*1000);
|
||||
assert (*btree_cmax(b) == 999*1000);
|
||||
|
||||
btree_it_t it, it2;
|
||||
int i = 0;
|
||||
for(btree_it(it, b), btree_it_set(it2, it); !btree_end_p(it); btree_next(it)) {
|
||||
const btree_type_t *item = btree_cref(it);
|
||||
assert (*item->key_ptr == i);
|
||||
assert (*item->value_ptr == 1000*i);
|
||||
i++;
|
||||
}
|
||||
|
||||
assert (i == 1000);
|
||||
assert (!btree_it_equal_p(it, it2));
|
||||
btree_it_end(it2, b);
|
||||
assert (btree_it_equal_p(it, it2));
|
||||
|
||||
i = 500;
|
||||
for(btree_it_from(it, b, 500); !btree_it_to_p(it, 600); btree_next(it)) {
|
||||
const btree_type_t *item = btree_cref(it);
|
||||
assert (*item->key_ptr == i);
|
||||
assert (*item->value_ptr == 1000*i);
|
||||
i++;
|
||||
}
|
||||
assert (i == 600);
|
||||
|
||||
btree_erase (b, 500);
|
||||
i = 501;
|
||||
for(btree_it_from(it, b, 500); !btree_it_to_p(it, 600); btree_next(it)) {
|
||||
const btree_type_t *item = btree_cref(it);
|
||||
assert (*item->key_ptr == i);
|
||||
assert (*item->value_ptr == 1000*i);
|
||||
i++;
|
||||
}
|
||||
assert (i == 600);
|
||||
|
||||
btree_it_from(it, b, 1000);
|
||||
assert (btree_end_p(it));
|
||||
assert (btree_it_to_p(it, 1001));
|
||||
|
||||
btree_clear(b);
|
||||
}
|
||||
|
||||
static void test_io(void)
|
||||
{
|
||||
M_LET(mpz1, mpz2, TESTOBJ_CMP_OPLIST)
|
||||
M_LET(str, STRING_OPLIST)
|
||||
M_LET(tree1, tree2, BPTREE_OPLIST2(btree_my, TESTOBJ_CMP_OPLIST, TESTOBJ_CMP_OPLIST)) {
|
||||
btree_my_get_str(str, tree1, false);
|
||||
assert(string_equal_str_p(str, "[]"));
|
||||
const char *endp;
|
||||
bool b = btree_my_parse_str(tree2, string_get_cstr(str), &endp);
|
||||
assert(b);
|
||||
assert(*endp == 0);
|
||||
assert (btree_my_equal_p (tree1, tree2));
|
||||
|
||||
FILE *f = fopen ("a-mbptree.dat", "wt");
|
||||
if (!f) abort();
|
||||
btree_my_out_str(f, tree1);
|
||||
fclose (f);
|
||||
|
||||
f = fopen ("a-mbptree.dat", "rt");
|
||||
if (!f) abort();
|
||||
b = btree_my_in_str (tree2, f);
|
||||
assert (b == true);
|
||||
assert (btree_my_equal_p (tree1, tree2));
|
||||
fclose(f);
|
||||
|
||||
testobj_set_ui(mpz1, 67);
|
||||
testobj_set_ui(mpz2, 670);
|
||||
btree_my_set_at(tree1, mpz1, mpz2);
|
||||
testobj_set_ui(mpz1, 17);
|
||||
testobj_set_ui(mpz2, 170);
|
||||
btree_my_set_at(tree1, mpz1, mpz2);
|
||||
|
||||
btree_my_get_str(str, tree1, false);
|
||||
assert(string_equal_str_p(str, "[17:170,67:670]"));
|
||||
b = btree_my_parse_str(tree2, string_get_cstr(str), &endp);
|
||||
assert(b);
|
||||
assert(*endp == 0);
|
||||
assert (btree_my_equal_p (tree1, tree2));
|
||||
|
||||
f = fopen ("a-mbptree.dat", "wt");
|
||||
if (!f) abort();
|
||||
btree_my_out_str(f, tree1);
|
||||
fclose (f);
|
||||
|
||||
f = fopen ("a-mbptree.dat", "rt");
|
||||
if (!f) abort();
|
||||
b = btree_my_in_str (tree2, f);
|
||||
assert (b == true);
|
||||
assert (btree_my_equal_p (tree1, tree2));
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_io_set(void)
|
||||
{
|
||||
M_LET(mpz1, TESTOBJ_CMP_OPLIST)
|
||||
M_LET(str, STRING_OPLIST)
|
||||
M_LET(tree1, tree2, BPTREE_OPLIST(btree_myset, TESTOBJ_CMP_OPLIST)) {
|
||||
btree_myset_get_str(str, tree1, false);
|
||||
assert(string_equal_str_p(str, "[]"));
|
||||
const char *endp;
|
||||
bool b = btree_myset_parse_str(tree2, string_get_cstr(str), &endp);
|
||||
assert(b);
|
||||
assert(*endp == 0);
|
||||
assert (btree_myset_equal_p (tree1, tree2));
|
||||
|
||||
FILE *f = fopen ("a-mbptree.dat", "wt");
|
||||
if (!f) abort();
|
||||
btree_myset_out_str(f, tree1);
|
||||
fclose (f);
|
||||
|
||||
f = fopen ("a-mbptree.dat", "rt");
|
||||
if (!f) abort();
|
||||
b = btree_myset_in_str (tree2, f);
|
||||
assert (b == true);
|
||||
assert (btree_myset_equal_p (tree1, tree2));
|
||||
fclose(f);
|
||||
|
||||
testobj_set_ui(mpz1, 67);
|
||||
btree_myset_push(tree1, mpz1);
|
||||
testobj_set_ui(mpz1, 670);
|
||||
btree_myset_push(tree1, mpz1);
|
||||
testobj_set_ui(mpz1, 17);
|
||||
btree_myset_push(tree1, mpz1);
|
||||
testobj_set_ui(mpz1, 170);
|
||||
btree_myset_push(tree1, mpz1);
|
||||
btree_myset_get_str(str, tree1, false);
|
||||
assert(string_equal_str_p(str, "[17,67,170,670]"));
|
||||
b = btree_myset_parse_str(tree2, string_get_cstr(str), &endp);
|
||||
assert(b);
|
||||
assert(*endp == 0);
|
||||
assert (btree_myset_equal_p (tree1, tree2));
|
||||
|
||||
f = fopen ("a-mbptree.dat", "wt");
|
||||
if (!f) abort();
|
||||
btree_myset_out_str(f, tree1);
|
||||
fclose (f);
|
||||
|
||||
f = fopen ("a-mbptree.dat", "rt");
|
||||
if (!f) abort();
|
||||
b = btree_myset_in_str (tree2, f);
|
||||
assert (b == true);
|
||||
assert (btree_myset_equal_p (tree1, tree2));
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
test4();
|
||||
test5();
|
||||
test_io();
|
||||
test_io_set();
|
||||
exit(0);
|
||||
}
|
||||
+63
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -22,11 +22,13 @@
|
||||
*/
|
||||
#include "m-buffer.h"
|
||||
|
||||
#include "test-obj.h"
|
||||
#include "coverage.h"
|
||||
START_COVERAGE
|
||||
// Define a fixed queue of unsigned int
|
||||
BUFFER_DEF(buffer_uint, unsigned int, 10, BUFFER_QUEUE|BUFFER_BLOCKING)
|
||||
QUEUE_MPMC_DEF(queue_uint, unsigned int, BUFFER_QUEUE)
|
||||
QUEUE_SPSC_DEF(squeue_uint, unsigned int, BUFFER_QUEUE)
|
||||
END_COVERAGE
|
||||
|
||||
// Define a variable stack of float
|
||||
@@ -38,10 +40,17 @@ BUFFER_DEF(buffer_char, char, 10, BUFFER_STACK|BUFFER_UNBLOCKING)
|
||||
// Define a fixed queue of long long
|
||||
BUFFER_DEF(buffer_llong, long long, 16, BUFFER_QUEUE|BUFFER_THREAD_UNSAFE|BUFFER_UNBLOCKING)
|
||||
|
||||
#include <gmp.h>
|
||||
BUFFER_DEF(buffer_mpz, mpz_t, 32, BUFFER_QUEUE, (INIT(mpz_init), INIT_SET(mpz_init_set), SET(mpz_set), CLEAR(mpz_clear)))
|
||||
// Define a buffer of complex structure.
|
||||
BUFFER_DEF(buffer_mpz, testobj_t, 32, BUFFER_QUEUE, TESTOBJ_OPLIST)
|
||||
QUEUE_MPMC_DEF(queue_z, testobj_t, BUFFER_QUEUE, TESTOBJ_OPLIST)
|
||||
QUEUE_SPSC_DEF(squeue_a, testobj_t, BUFFER_QUEUE, TESTOBJ_OPLIST)
|
||||
|
||||
buffer_uint_t g_buff;
|
||||
buffer_uint_t g_buffB;
|
||||
|
||||
// Number of thread created by the test (twice this amount).
|
||||
#define MAX_TEST_THREAD 100
|
||||
#define MAX_TEST_THREAD2 (MAX_TEST_THREAD*2/3)
|
||||
|
||||
static void conso(void *arg)
|
||||
{
|
||||
@@ -62,16 +71,17 @@ static void prod(void *arg)
|
||||
|
||||
static void test_global(void)
|
||||
{
|
||||
m_thread_t idx_p[100];
|
||||
m_thread_t idx_c[100];
|
||||
m_thread_t idx_p[MAX_TEST_THREAD];
|
||||
m_thread_t idx_c[MAX_TEST_THREAD];
|
||||
|
||||
buffer_uint_init(g_buff, 10);
|
||||
assert (buffer_uint_capacity(g_buff) == 10);
|
||||
|
||||
for(int i = 0; i < 100; i++) {
|
||||
for(int i = 0; i < MAX_TEST_THREAD; i++) {
|
||||
m_thread_create (idx_p[i], conso, NULL);
|
||||
m_thread_create (idx_c[i], prod, NULL);
|
||||
}
|
||||
for(int i = 0; i < 100;i++) {
|
||||
for(int i = 0; i < MAX_TEST_THREAD;i++) {
|
||||
m_thread_join(idx_p[i]);
|
||||
m_thread_join(idx_c[i]);
|
||||
}
|
||||
@@ -97,14 +107,47 @@ static void test_global(void)
|
||||
assert (!buffer_uint_empty_p(g_buff));
|
||||
assert (buffer_uint_full_p(g_buff));
|
||||
assert (buffer_uint_size(g_buff) == 10);
|
||||
|
||||
assert (buffer_uint_push_blocking(g_buff, 15, false) == false);
|
||||
|
||||
buffer_uint_clean(g_buff);
|
||||
assert (buffer_uint_empty_p(g_buff));
|
||||
assert (!buffer_uint_full_p(g_buff));
|
||||
assert (buffer_uint_size(g_buff) == 0);
|
||||
unsigned int s;
|
||||
assert (buffer_uint_pop_blocking(&s, g_buff, false) == false);
|
||||
|
||||
assert (buffer_uint_overwrite(g_buff) == 0);
|
||||
|
||||
buffer_uint_init_set(g_buffB, g_buff);
|
||||
assert (buffer_uint_empty_p(g_buffB));
|
||||
buffer_uint_clear(g_buffB);
|
||||
for(unsigned int i = 0; i < 5;i++)
|
||||
buffer_uint_push(g_buff, i);
|
||||
buffer_uint_init_set(g_buffB, g_buff);
|
||||
assert (!buffer_uint_empty_p(g_buffB));
|
||||
assert (!buffer_uint_full_p(g_buffB));
|
||||
assert (buffer_uint_size(g_buffB) == 5);
|
||||
for(unsigned int i = 0; i < 5;i++) {
|
||||
unsigned int j;
|
||||
bool b= buffer_uint_pop(&j, g_buffB);
|
||||
assert(b);
|
||||
assert(j == i);
|
||||
}
|
||||
assert (buffer_uint_empty_p(g_buffB));
|
||||
|
||||
buffer_uint_set(g_buffB, g_buff);
|
||||
assert (!buffer_uint_empty_p(g_buffB));
|
||||
assert (!buffer_uint_full_p(g_buffB));
|
||||
assert (buffer_uint_size(g_buffB) == 5);
|
||||
for(unsigned int i = 0; i < 5;i++) {
|
||||
unsigned int j;
|
||||
bool b= buffer_uint_pop(&j, g_buffB);
|
||||
assert(b);
|
||||
assert(j == i);
|
||||
}
|
||||
assert (buffer_uint_empty_p(g_buffB));
|
||||
buffer_uint_clear(g_buffB);
|
||||
|
||||
buffer_uint_clear(g_buff);
|
||||
}
|
||||
|
||||
@@ -112,6 +155,7 @@ static void test_stack(void)
|
||||
{
|
||||
buffer_floats_t buff;
|
||||
buffer_floats_init(buff, 100);
|
||||
assert (buffer_floats_capacity(buff) == 100);
|
||||
for(unsigned int i = 0; i < 100; i++) {
|
||||
float f = i;
|
||||
buffer_floats_push(buff, f);
|
||||
@@ -186,6 +230,8 @@ static void test_no_thread(void)
|
||||
buffer_llong_clear(buff);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
/* Test intrusive shared pointer + buffer */
|
||||
#include "m-i-shared.h"
|
||||
// Tiny test structure
|
||||
@@ -254,19 +300,19 @@ static void test_prod(void *arg)
|
||||
|
||||
static void test_global_ishared(void)
|
||||
{
|
||||
m_thread_t idx_p[100];
|
||||
m_thread_t idx_c1[100];
|
||||
m_thread_t idx_c2[100];
|
||||
m_thread_t idx_p[MAX_TEST_THREAD2];
|
||||
m_thread_t idx_c1[MAX_TEST_THREAD2];
|
||||
m_thread_t idx_c2[MAX_TEST_THREAD2];
|
||||
|
||||
buffer_itest_init(comm1, 16);
|
||||
buffer_itest_init(comm2, 16);
|
||||
|
||||
for(int i = 0; i < 100; i++) {
|
||||
for(int i = 0; i < MAX_TEST_THREAD2; i++) {
|
||||
m_thread_create (idx_c1[i], test_conso1, NULL);
|
||||
m_thread_create (idx_c2[i], test_conso2, NULL);
|
||||
m_thread_create (idx_p[i], test_prod, NULL);
|
||||
}
|
||||
for(int i = 0; i < 100;i++) {
|
||||
for(int i = 0; i < MAX_TEST_THREAD2;i++) {
|
||||
m_thread_join(idx_p[i]);
|
||||
m_thread_join(idx_c1[i]);
|
||||
m_thread_join(idx_c2[i]);
|
||||
@@ -329,6 +375,8 @@ static void test_queue(size_t n, int cpu_count, unsigned long long ref)
|
||||
// Init
|
||||
queue_uint_init(g_buff2, 64*2);
|
||||
queue_ull_init (g_final2, 64*2);
|
||||
assert (queue_uint_capacity(g_buff2) == 64*2);
|
||||
assert (queue_ull_capacity(g_final2) == 64*2);
|
||||
|
||||
// Create thread
|
||||
m_thread_t idx_p[64];
|
||||
@@ -359,6 +407,8 @@ static void test_queue(size_t n, int cpu_count, unsigned long long ref)
|
||||
queue_uint_clear(g_buff2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
unsigned int x, y;
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "test-obj.h"
|
||||
#include "coverage.h"
|
||||
|
||||
#include "m-tuple.h"
|
||||
#include "m-array.h"
|
||||
#include "m-dict.h"
|
||||
#include "m-list.h"
|
||||
#include "m-deque.h"
|
||||
#include "m-bptree.h"
|
||||
#include "m-rbtree.h"
|
||||
#include "m-concurrent.h"
|
||||
|
||||
TUPLE_DEF2(point, (x, int), (y, int))
|
||||
#define M_OPL_point_t() TUPLE_OPLIST(point, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
|
||||
CONCURRENT_DEF(ppoint, point_t)
|
||||
|
||||
ARRAY_DEF(array1, int)
|
||||
CONCURRENT_DEF(parray1, array1_t, ARRAY_OPLIST(array1))
|
||||
|
||||
DICT_DEF2(dict1, int, int)
|
||||
CONCURRENT_DEF(pdict1, dict1_t, DICT_OPLIST(dict1))
|
||||
|
||||
DICT_SET_DEF(dict2, int)
|
||||
CONCURRENT_DEF(pdict2, dict2_t, DICT_SET_OPLIST(dict2))
|
||||
|
||||
LIST_DEF(list1, int)
|
||||
CONCURRENT_DEF(plist1, list1_t, LIST_OPLIST(list1))
|
||||
|
||||
LIST_DUAL_PUSH_DEF(list2, int)
|
||||
CONCURRENT_DEF(plist2, list2_t, LIST_OPLIST(list2))
|
||||
|
||||
DEQUE_DEF(deque1, int)
|
||||
CONCURRENT_DEF(pdeque1, deque1_t, DEQUE_OPLIST(deque1))
|
||||
|
||||
BPTREE_DEF2(bptree1, 10, int, int)
|
||||
CONCURRENT_DEF(pbtree1, bptree1_t, BPTREE_OPLIST2(bptree1, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST))
|
||||
|
||||
BPTREE_DEF(bptree2, 10, int)
|
||||
CONCURRENT_DEF(pbtree2, bptree2_t, BPTREE_OPLIST(bptree2))
|
||||
|
||||
RBTREE_DEF(rbtree1, int)
|
||||
CONCURRENT_DEF(prbtree1, rbtree1_t, RBTREE_OPLIST(rbtree1))
|
||||
|
||||
DICT_DEF2(string_pool, string_t, string_t)
|
||||
#define STRING_POOL_OPLIST \
|
||||
DICT_OPLIST(string_pool, STRING_OPLIST, STRING_OPLIST)
|
||||
#define M_OPL_string_pool_t() STRING_POOL_OPLIST
|
||||
|
||||
CONCURRENT_DEF(string_pool_ts, string_pool_t, STRING_POOL_OPLIST)
|
||||
#define STRING_POOL_TS_OPLIST \
|
||||
CONCURRENT_OPLIST(string_pool_ts, STRING_POOL_OPLIST)
|
||||
#define M_OPL_string_pool_ts_t() STRING_POOL_TS_OPLIST
|
||||
|
||||
TUPLE_DEF2(my_tuple, (pool, string_pool_ts_t))
|
||||
#define MY_TUPLE_OPLIST TUPLE_OPLIST(my_tuple, STRING_POOL_TS_OPLIST)
|
||||
#define M_OPL_my_tuple_t() MY_TUPLE_OPLIST
|
||||
|
||||
/********************************/
|
||||
parray1_t arr;
|
||||
|
||||
static void conso(void *p)
|
||||
{
|
||||
assert (p == NULL);
|
||||
for(int i = 0; i < 1000; i++) {
|
||||
int j;
|
||||
bool b = parray1_pop_blocking(&j, arr, true);
|
||||
assert (b);
|
||||
assert (j == i);
|
||||
}
|
||||
}
|
||||
|
||||
static void test(void)
|
||||
{
|
||||
m_thread_t idx;
|
||||
parray1_init(arr);
|
||||
m_thread_create (idx, conso, NULL);
|
||||
m_thread_sleep(1000);
|
||||
for(int i = 0; i < 1000; i++) {
|
||||
parray1_push (arr, i);
|
||||
while (!parray1_empty_p(arr));
|
||||
}
|
||||
m_thread_join(idx);
|
||||
parray1_clear(arr);
|
||||
}
|
||||
|
||||
static void test_basic(void)
|
||||
{
|
||||
pdict1_t dict;
|
||||
pdict1_init(dict);
|
||||
int z;
|
||||
pdict1_set_at(dict, 2, 3);
|
||||
pdict1_get_copy(&z, dict, 2);
|
||||
assert (z == 3);
|
||||
z = 1;
|
||||
bool b = pdict1_get_blocking(&z, dict, 2, false);
|
||||
assert (b);
|
||||
assert (z == 3);
|
||||
pdict1_clear(dict);
|
||||
|
||||
M_LET(str, string_t)
|
||||
M_LET(pool, string_pool_ts_t) {
|
||||
string_pool_ts_set_at (pool, STRING_CTE("A"), STRING_CTE("B"));
|
||||
b = string_pool_ts_get_copy (&str, pool, STRING_CTE("A"));
|
||||
assert (b);
|
||||
assert (string_equal_str_p (str, "B"));
|
||||
}
|
||||
M_LET(tuple, my_tuple_t) {
|
||||
assert (string_pool_ts_empty_p(tuple->pool));
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_basic();
|
||||
test();
|
||||
}
|
||||
+100
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <gmp.h>
|
||||
#include "test-obj.h"
|
||||
|
||||
#include "m-core.h"
|
||||
|
||||
@@ -120,6 +120,9 @@ static void test_return(void)
|
||||
assert (M_RET_ARG2(0, 1, 0));
|
||||
assert (M_RET_ARG3(0, 0, 1, 0));
|
||||
assert (M_RET_ARG27(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0));
|
||||
assert (M_GET_AT((0,1,0),1));
|
||||
assert (M_GET_AT((1,0,0),0));
|
||||
assert (M_GET_AT((0,0,0,1),3));
|
||||
}
|
||||
|
||||
static void test_invert(void)
|
||||
@@ -145,22 +148,50 @@ static void test_empty(void)
|
||||
assert (M_EMPTY_P(,) == false);
|
||||
assert (M_EMPTY_P(()) == false);
|
||||
assert (M_EMPTY_P("123") == false);
|
||||
assert (M_EMPTY_P(,1,2,3) == false);
|
||||
}
|
||||
|
||||
#define f(n) (n)*(n) +
|
||||
#define f2(d,n) (d)*(n) +
|
||||
#define f3(d,i,n) (d)*(i)*(n) +
|
||||
|
||||
static void test_map(void)
|
||||
{
|
||||
assert (M_MAP (f, 1) 0 == 1);
|
||||
assert (M_MAP (f, 1, 2) 0 == 5);
|
||||
assert (M_MAP (f, 1, 2, 3) 0 == 14);
|
||||
|
||||
assert (M_MAP2 (f2, 2, 1) 0 == 2);
|
||||
assert (M_MAP2 (f2, 3, 1, 2) 0 == 9);
|
||||
assert (M_MAP2 (f2, 4, 1, 2, 3) 0 == 24);
|
||||
|
||||
assert (M_MAP3 (f3, 2, 2) 0 == 4);
|
||||
assert (M_MAP3 (f3, 3, 2, 4) 0 == 30);
|
||||
assert (M_MAP3 (f3, 4, 2, 4, 6) 0 == 8+32+12*6);
|
||||
}
|
||||
|
||||
static void test_let(void)
|
||||
{
|
||||
M_LET(z, M_CLASSIC_OPLIST(mpz)) {
|
||||
mpz_add(z,z,z);
|
||||
bool b = false;
|
||||
M_LET(z, M_CLASSIC_OPLIST(testobj)) {
|
||||
testobj_set_ui(z, 12);
|
||||
M_LET((zz,z), M_CLASSIC_OPLIST(testobj)) {
|
||||
assert( testobj_cmp_ui(zz, 12) == 0);
|
||||
}
|
||||
M_LET((zz,42), M_OPEXTEND(M_CLASSIC_OPLIST(testobj), INIT_WITH(testobj_init_set_ui))) {
|
||||
assert( testobj_cmp_ui(zz, 42) == 0);
|
||||
}
|
||||
M_LET((zz,z), z2, M_CLASSIC_OPLIST(testobj)) {
|
||||
assert( testobj_cmp_ui(zz, 12) == 0);
|
||||
}
|
||||
M_LET(z3, (zz,42), M_OPEXTEND(M_CLASSIC_OPLIST(testobj), INIT_WITH(testobj_init_set_ui))) {
|
||||
assert( testobj_cmp_ui(zz, 42) == 0);
|
||||
}
|
||||
b = true;
|
||||
break;
|
||||
assert(0);
|
||||
}
|
||||
assert(b);
|
||||
}
|
||||
|
||||
static void test_va(void)
|
||||
@@ -251,6 +282,16 @@ static void test_oplist(void)
|
||||
#define M_OPL_op() (INIT(1), CLEAR(0))
|
||||
assert(M_GET_INIT M_GLOBAL_OPLIST(op));
|
||||
assert(!M_GET_INIT M_GLOBAL_OPLIST(a));
|
||||
|
||||
#define MAKE_OPLIST(op) (OPLIST(M_GLOBAL_OPLIST_OR_DEF(op)()))
|
||||
#define M_OPL_uint() (INIT(0), CLEAR(1))
|
||||
#define M_OPL_array_uint() (OPLIST(M_GLOBAL_OPLIST_OR_DEF(uint)()))
|
||||
#define M_OPL_list_array_uint() MAKE_OPLIST(array_uint)
|
||||
#define A1 A2(M_OPL_list_array_uint())
|
||||
#define A2(op) A3(M_GET_OPLIST op)
|
||||
#define A3(op) A4(M_GET_OPLIST op)
|
||||
#define A4(op) M_GET_CLEAR op
|
||||
assert (A1);
|
||||
}
|
||||
|
||||
static void test_cast(void)
|
||||
@@ -276,6 +317,60 @@ static void test_reduce(void)
|
||||
== M_REDUCE(M_ID, add, M_SEQ(1, 10)));
|
||||
}
|
||||
|
||||
|
||||
static void test_parse_standard_c_type(void)
|
||||
{
|
||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
||||
char c = 0;
|
||||
M_PARSE_DEFAULT_TYPE(&c, "C", NULL);
|
||||
assert (c == 'C');
|
||||
|
||||
short s = 0;
|
||||
M_PARSE_DEFAULT_TYPE(&s, "-2", NULL);
|
||||
assert (s == -2);
|
||||
|
||||
int i = 0;
|
||||
M_PARSE_DEFAULT_TYPE(&i, "2", NULL);
|
||||
assert (i == 2);
|
||||
|
||||
long l = 0;
|
||||
M_PARSE_DEFAULT_TYPE(&l, "1742", NULL);
|
||||
assert (l == 1742);
|
||||
|
||||
long long ll = 0;
|
||||
M_PARSE_DEFAULT_TYPE(&ll, "-1742548676843540", NULL);
|
||||
assert (ll == -1742548676843540LL);
|
||||
|
||||
unsigned short us = 0;
|
||||
M_PARSE_DEFAULT_TYPE(&us, "3", NULL);
|
||||
assert (us == 3);
|
||||
|
||||
unsigned int ui = 0;
|
||||
M_PARSE_DEFAULT_TYPE(&ui, "2", NULL);
|
||||
assert (ui == 2);
|
||||
|
||||
unsigned long ul = 0;
|
||||
M_PARSE_DEFAULT_TYPE(&ul, "1742", NULL);
|
||||
assert (ul == 1742);
|
||||
|
||||
unsigned long long ull = 0;
|
||||
M_PARSE_DEFAULT_TYPE(&ull, "1742548676843540", NULL);
|
||||
assert (ull == 1742548676843540ULL);
|
||||
|
||||
float f;
|
||||
M_PARSE_DEFAULT_TYPE(&f, "-0.5", NULL);
|
||||
assert (f == -0.5);
|
||||
|
||||
double d;
|
||||
M_PARSE_DEFAULT_TYPE(&d, "2.5", NULL);
|
||||
assert (d == 2.5);
|
||||
|
||||
long double ld;
|
||||
M_PARSE_DEFAULT_TYPE(&ld, "2.5", NULL);
|
||||
assert (ld == 2.5);
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_cat();
|
||||
@@ -293,5 +388,6 @@ int main(void)
|
||||
test_oplist();
|
||||
test_cast();
|
||||
test_reduce();
|
||||
test_parse_standard_c_type();
|
||||
exit(0);
|
||||
}
|
||||
+151
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -21,7 +21,7 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <gmp.h> // For testing purpose only.
|
||||
#include "test-obj.h"
|
||||
|
||||
#include "m-string.h"
|
||||
#include "m-deque.h"
|
||||
@@ -31,6 +31,10 @@
|
||||
START_COVERAGE
|
||||
DEQUE_DEF(deque, int)
|
||||
END_COVERAGE
|
||||
DEQUE_DEF(deque_mpz, testobj_t, TESTOBJ_OPLIST)
|
||||
|
||||
// Deque with the minimum number of methods.
|
||||
DEQUE_DEF(deque_min_z, testobj_t, (INIT_SET(testobj_init_set), SET(testobj_set), CLEAR(testobj_clear)))
|
||||
|
||||
#define OPL DEQUE_OPLIST(deque)
|
||||
|
||||
@@ -39,22 +43,25 @@ static void test_ti1(int n)
|
||||
deque_t d;
|
||||
|
||||
deque_init(d);
|
||||
|
||||
assert(deque_capacity(d) == DEQUEUI_DEFAULT_SIZE);
|
||||
|
||||
for(int i =0; i < n ;i++) {
|
||||
deque_push_back(d, i);
|
||||
assert(*deque_back(d) == i);
|
||||
deque_push_front(d, i);
|
||||
assert(*deque_front(d) == i);
|
||||
}
|
||||
assert (*deque_back(d) == n-1);
|
||||
assert (*deque_front(d) == n-1);
|
||||
assert (!deque_empty_p(d));
|
||||
assert (deque_size(d) == (size_t)2*n);
|
||||
int s = 0;
|
||||
for(int i =0; i < 2*n ;i++) {
|
||||
int z;
|
||||
if ((i % 3) == 0)
|
||||
if ((i % 3) == 0) {
|
||||
deque_pop_front(&z, d);
|
||||
else
|
||||
assert (z > *deque_front(d) );
|
||||
} else {
|
||||
deque_pop_back(&z, d);
|
||||
}
|
||||
s += z;
|
||||
}
|
||||
assert (s == n*(n-1) );
|
||||
@@ -67,6 +74,7 @@ static void test_ti1(int n)
|
||||
static void test1(void)
|
||||
{
|
||||
deque_t d;
|
||||
int j;
|
||||
|
||||
deque_init(d);
|
||||
assert (deque_empty_p(d));
|
||||
@@ -95,12 +103,24 @@ static void test1(void)
|
||||
assert (deque_size(d) == 99*2);
|
||||
for(int i = 0; i < 99*2; i++)
|
||||
assert (*deque_get(d, i) == (i < 99) ? i - 99 : i - 98);
|
||||
for(int i = 0; i < 50; i++) {
|
||||
for(int i = 0; i < 98; i++) {
|
||||
deque_pop_back(NULL, d);
|
||||
assert (*deque_back(d) == i - 98);
|
||||
deque_pop_front(NULL, d);
|
||||
assert (*deque_front(d) == 98 - i);
|
||||
}
|
||||
deque_pop_back(&j, d);
|
||||
assert (j == -1);
|
||||
deque_pop_front(&j, d);
|
||||
assert (j == 1);
|
||||
|
||||
assert (deque_empty_p(d));
|
||||
assert (deque_size(d) == 0);
|
||||
deque_push_back(d, 1914);
|
||||
deque_push_front(d, 1918);
|
||||
assert (*deque_get(d, 0) == 1918);
|
||||
assert (*deque_get(d, 1) == 1914);
|
||||
|
||||
deque_clear(d);
|
||||
}
|
||||
|
||||
@@ -138,6 +158,8 @@ static void test_it(void)
|
||||
deque_previous(it);
|
||||
assert (deque_end_p(it) || !deque_last_p(it));
|
||||
}
|
||||
deque_previous(it);
|
||||
assert (deque_end_p(it));
|
||||
deque_it_end(it, d);
|
||||
assert (deque_end_p(it));
|
||||
assert (deque_last_p(it));
|
||||
@@ -194,6 +216,16 @@ static void test_set(void)
|
||||
assert (*deque_back(e) == 3000);
|
||||
deque_clear(d);
|
||||
deque_init_move (d, e);
|
||||
assert (*deque_back(d) == 3000);
|
||||
assert (*deque_front(d) == 2000);
|
||||
assert (deque_size (d) == 1997);
|
||||
deque_push_back(d, 10000);
|
||||
deque_push_front(d, -10000);
|
||||
assert (deque_size (d) == 1999);
|
||||
for(size_t i = 0; i < 1999; i ++)
|
||||
deque_pop_back(NULL, d);
|
||||
for(size_t i = 0; i < 3001; i ++)
|
||||
deque_push_back(d, i);
|
||||
deque_init (e);
|
||||
deque_move (e, d);
|
||||
deque_init (d);
|
||||
@@ -201,6 +233,113 @@ static void test_set(void)
|
||||
}
|
||||
}
|
||||
|
||||
// This test showed a very bad behavior in the memory allocator
|
||||
// deque will continously allocate a new space whereas at most only
|
||||
// one item is in the container!
|
||||
// This is still quite slow.
|
||||
static void test_advance(void)
|
||||
{
|
||||
deque_t d;
|
||||
int j;
|
||||
deque_init(d);
|
||||
for(int i = 0; i < 100000000; i ++) {
|
||||
deque_push_back(d, i);
|
||||
deque_pop_front(&j, d);
|
||||
assert (i == j);
|
||||
}
|
||||
deque_clear(d);
|
||||
}
|
||||
|
||||
static void test_z(void)
|
||||
{
|
||||
M_LET(d, DEQUE_OPLIST(deque_mpz, TESTOBJ_OPLIST))
|
||||
M_LET(z, TESTOBJ_OPLIST) {
|
||||
testobj_set_ui(z, 1742);
|
||||
deque_mpz_push_back_move(d, &z);
|
||||
assert (testobj_cmp_ui (*deque_mpz_back(d), 1742) == 0);
|
||||
testobj_init(z);
|
||||
testobj_set_ui(z, 4217);
|
||||
deque_mpz_push_front_move(d, &z);
|
||||
assert (testobj_cmp_ui (*deque_mpz_front(d), 4217) == 0);
|
||||
deque_mpz_pop_back_move(&z, d);
|
||||
assert (testobj_cmp_ui (z, 1742) == 0);
|
||||
testobj_clear(z);
|
||||
deque_mpz_pop_front_move(&z, d);
|
||||
assert (testobj_cmp_ui (z, 4217) == 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void test_io(void)
|
||||
{
|
||||
M_LET(d1, d2, DEQUE_OPLIST(deque_mpz, TESTOBJ_OPLIST))
|
||||
M_LET(z, TESTOBJ_OPLIST)
|
||||
M_LET(str, STRING_OPLIST) {
|
||||
// Test empty
|
||||
FILE *f = fopen ("a-marray.dat", "wt");
|
||||
if (!f) abort();
|
||||
deque_mpz_out_str(f, d1);
|
||||
fclose (f);
|
||||
|
||||
f = fopen ("a-marray.dat", "rt");
|
||||
if (!f) abort();
|
||||
bool b = deque_mpz_in_str (d2, f);
|
||||
assert (b == true);
|
||||
assert (deque_mpz_equal_p (d1, d2));
|
||||
fclose(f);
|
||||
|
||||
// Test non empty
|
||||
for(int n = 0; n < 1000; n++) {
|
||||
testobj_set_ui (z, n);
|
||||
deque_mpz_push_back (d1, z);
|
||||
}
|
||||
|
||||
f = fopen ("a-marray.dat", "wt");
|
||||
if (!f) abort();
|
||||
deque_mpz_out_str(f, d1);
|
||||
fclose (f);
|
||||
|
||||
f = fopen ("a-marray.dat", "rt");
|
||||
if (!f) abort();
|
||||
b = deque_mpz_in_str (d2, f);
|
||||
assert (b == true);
|
||||
assert (deque_mpz_equal_p (d1, d2));
|
||||
fclose(f);
|
||||
|
||||
deque_mpz_clean(d1);
|
||||
|
||||
deque_mpz_get_str(str, d1, false);
|
||||
assert (string_cmp_str (str, "[]") == 0);
|
||||
const char *sp;
|
||||
b = deque_mpz_parse_str(d2, string_get_cstr(str), &sp);
|
||||
assert(b);
|
||||
assert(*sp == 0);
|
||||
assert(deque_mpz_equal_p(d1, d2));
|
||||
|
||||
testobj_set_ui (z, 17);
|
||||
deque_mpz_push_back (d1, z);
|
||||
deque_mpz_get_str(str, d1, false);
|
||||
assert (string_cmp_str (str, "[17]") == 0);
|
||||
b = deque_mpz_parse_str(d2, string_get_cstr(str), &sp);
|
||||
assert(b);
|
||||
assert(*sp == 0);
|
||||
assert(deque_mpz_equal_p(d1, d2));
|
||||
|
||||
testobj_set_ui (z, 42);
|
||||
deque_mpz_push_back (d1, z);
|
||||
deque_mpz_get_str(str, d1, true);
|
||||
assert (string_cmp_str (str, "[17][17,42]") == 0);
|
||||
b = deque_mpz_parse_str(d2, string_get_cstr(str), &sp);
|
||||
assert(b);
|
||||
assert(strcmp(sp, "[17,42]") == 0);
|
||||
assert(!deque_mpz_equal_p(d1, d2));
|
||||
b = deque_mpz_parse_str(d2, sp, &sp);
|
||||
assert(b);
|
||||
assert(strcmp(sp, "") == 0);
|
||||
assert(deque_mpz_equal_p(d1, d2));
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test1();
|
||||
@@ -210,4 +349,8 @@ int main(void)
|
||||
test_ti1(10000);
|
||||
test_it();
|
||||
test_set();
|
||||
test_io();
|
||||
test_z();
|
||||
test_advance();
|
||||
exit(0);
|
||||
}
|
||||
+93
-33
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -20,29 +20,26 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <limits.h>
|
||||
#include <time.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include "m-dict.h"
|
||||
#include "m-array.h"
|
||||
#include "m-string.h"
|
||||
#include "test-obj.h"
|
||||
|
||||
static inline bool oor_equal_p(int k, unsigned char n)
|
||||
{
|
||||
return k == (int)-n-1;
|
||||
}
|
||||
static inline void oor_set(int *k, unsigned char n)
|
||||
{
|
||||
*k = (int)-n-1;
|
||||
}
|
||||
|
||||
static inline bool oor_equal_p(int k, unsigned char n) { return k == (int)-n-1; }
|
||||
static inline void oor_set(int *k, unsigned char n) { *k = (int)-n-1; }
|
||||
static inline void update_value (int *p, const int val) { *p += val; }
|
||||
#include "coverage.h"
|
||||
START_COVERAGE
|
||||
DICT_DEF2(dict_str, string_t, STRING_OPLIST, string_t, STRING_OPLIST)
|
||||
DICT_OA_DEF2(dict_oa_int, int, M_OPEXTEND(M_DEFAULT_OPLIST, OOR_EQUAL(oor_equal_p), OOR_SET(oor_set M_IPTR)), int, M_DEFAULT_OPLIST)
|
||||
DICT_OA_DEF2(dict_oa_int, int, M_OPEXTEND(M_DEFAULT_OPLIST, OOR_EQUAL(oor_equal_p), OOR_SET(oor_set M_IPTR)), int, M_OPEXTEND(M_DEFAULT_OPLIST, UPDATE(update_value M_IPTR)))
|
||||
END_COVERAGE
|
||||
|
||||
DICT_SET_DEF2(dict_setstr, string_t, STRING_OPLIST)
|
||||
DICT_STOREHASH_DEF2(dict_str2, string_t, STRING_OPLIST, string_t, STRING_OPLIST)
|
||||
DICT_SET_DEF(dict_setstr, string_t, STRING_OPLIST)
|
||||
DICT_DEF2(dict_int, int, M_DEFAULT_OPLIST, int, M_DEFAULT_OPLIST)
|
||||
DICT_DEF2(dict_mpz, string_t, STRING_OPLIST, testobj_t, TESTOBJ_OPLIST)
|
||||
|
||||
/* Helper structure */
|
||||
ARRAY_DEF(array_string, string_t, STRING_OPLIST)
|
||||
@@ -72,10 +69,12 @@ static void test_data(void)
|
||||
{
|
||||
dict_str_t dict;
|
||||
int data_size = (int) array_string_size(v_str);
|
||||
|
||||
|
||||
dict_str_init (dict);
|
||||
assert(dict_str_empty_p(dict));
|
||||
for (int i = 0; i < data_size; ++i) {
|
||||
dict_str_set_at(dict, *array_string_cget(v_str, i), *array_string_cget(v_str, (i+1) % data_size));
|
||||
assert(!dict_str_empty_p(dict));
|
||||
}
|
||||
assert(dict_str_size(dict) == 1227176);
|
||||
|
||||
@@ -92,9 +91,15 @@ static void test_data(void)
|
||||
static void check_io(void)
|
||||
{
|
||||
M_LET(str, STRING_OPLIST)
|
||||
M_LET(dict, DICT_OPLIST(dict_str)) {
|
||||
M_LET(dict, dict2, DICT_OPLIST(dict_str)) {
|
||||
dict_str_get_str(str, dict, false);
|
||||
assert (string_cmp_str (str, "{}") == 0);
|
||||
const char *sp;
|
||||
bool b = dict_str_parse_str(dict2, string_get_cstr(str), &sp);
|
||||
assert (b);
|
||||
assert (*sp == 0);
|
||||
assert (dict_str_equal_p(dict, dict2));
|
||||
|
||||
dict_str_set_at (dict, STRING_CTE("LICENCE"), STRING_CTE("BSD3"));
|
||||
dict_str_get_str(str, dict, false);
|
||||
assert (string_cmp_str (str, "{\"LICENCE\":\"BSD3\"}") == 0);
|
||||
@@ -102,17 +107,20 @@ static void check_io(void)
|
||||
dict_str_get_str(str, dict, false);
|
||||
//NOTE: order is dependent on the hash system.
|
||||
assert (string_cmp_str (str, "{\"LICENCE\":\"BSD3\",\"AUTHOR\":\"PP\"}") == 0 || string_cmp_str (str, "{\"AUTHOR\":\"PP\",\"LICENCE\":\"BSD3\"}") == 0);
|
||||
b = dict_str_parse_str(dict2, string_get_cstr(str), &sp);
|
||||
assert (b);
|
||||
assert (*sp == 0);
|
||||
assert (dict_str_equal_p(dict, dict2));
|
||||
|
||||
FILE *f = fopen ("a-mdict.dat", "wt");
|
||||
if (!f) abort();
|
||||
dict_str_out_str(f, dict);
|
||||
fclose (f);
|
||||
f = fopen ("a-mdict.dat", "rt");
|
||||
if (!f) abort();
|
||||
M_LET(dict2, DICT_OPLIST(dict_str)) {
|
||||
bool b = dict_str_in_str(dict2, f);
|
||||
assert (b == true);
|
||||
assert (dict_str_equal_p (dict, dict2));
|
||||
}
|
||||
b = dict_str_in_str(dict2, f);
|
||||
assert (b == true);
|
||||
assert (dict_str_equal_p (dict, dict2));
|
||||
fclose (f);
|
||||
}
|
||||
}
|
||||
@@ -123,13 +131,19 @@ static void test_set(void)
|
||||
M_LET(set, DICT_SET_OPLIST(dict_setstr, STRING_OPLIST)) {
|
||||
for(int i = 0; i < 100; i++) {
|
||||
string_printf(str, "%d", i);
|
||||
dict_setstr_set_at(set, str);
|
||||
dict_setstr_push(set, str);
|
||||
}
|
||||
assert(dict_setstr_size(set) == 100);
|
||||
for(int i = 0; i < 100; i++) {
|
||||
string_printf(str, "%d", i);
|
||||
string_t *p = dict_setstr_get(set, str);
|
||||
assert (p != NULL);
|
||||
assert(string_equal_p (*p, str));
|
||||
p = dict_setstr_get_at(set, str);
|
||||
assert (p != NULL);
|
||||
assert(string_equal_p (*p, str));
|
||||
}
|
||||
assert(dict_setstr_size(set) == 100);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +157,18 @@ static void test_init(void)
|
||||
dict_str_set_at (d1, str1, str2);
|
||||
}
|
||||
assert (dict_str_size (d1) == 100);
|
||||
|
||||
for(size_t i = 0; i < 100; i++) {
|
||||
string_printf(str1, "%d", 2*i);
|
||||
string_printf(str2, "%d", 2*i+1);
|
||||
string_t *p = dict_str_get (d1, str1);
|
||||
assert (p != NULL);
|
||||
assert (string_equal_p(*p, str2));
|
||||
p = dict_str_get_at (d1, str1);
|
||||
assert (p != NULL);
|
||||
assert (string_equal_p(*p, str2));
|
||||
}
|
||||
|
||||
dict_str_t d3;
|
||||
dict_str_init_set (d3, d1);
|
||||
assert (dict_str_equal_p (d3, d1));
|
||||
@@ -154,14 +180,14 @@ static void test_init(void)
|
||||
dict_str_set_at (d1, STRING_CTE("x"), STRING_CTE("y"));
|
||||
assert (dict_str_size (d1) == 101);
|
||||
assert (!dict_str_equal_p (d2, d1));
|
||||
bool b = dict_str_remove (d1, STRING_CTE("0"));
|
||||
bool b = dict_str_erase (d1, STRING_CTE("0"));
|
||||
assert (dict_str_size (d1) == 100);
|
||||
assert (b);
|
||||
assert (!dict_str_equal_p (d2, d1));
|
||||
|
||||
for(size_t i = 1; i < 100; i++) {
|
||||
string_printf(str1, "%d", 2*i);
|
||||
b = dict_str_remove (d1, str1);
|
||||
b = dict_str_erase (d1, str1);
|
||||
assert (b);
|
||||
}
|
||||
assert (dict_str_size (d1) == 1);
|
||||
@@ -185,6 +211,23 @@ static void test_init(void)
|
||||
dict_str_clean (d2);
|
||||
assert (dict_str_size (d2) == 0);
|
||||
|
||||
assert (dict_str_size (d1) == 100);
|
||||
for(size_t i = 100; i < 200; i++) {
|
||||
string_printf(str1, "%d", 2*i);
|
||||
string_printf(*dict_str_get_at (d1, str1), "%d", 2*i+1);
|
||||
}
|
||||
assert (dict_str_size (d1) == 200);
|
||||
for(size_t i = 100; i < 200; i++) {
|
||||
string_printf(str1, "%d", 2*i);
|
||||
string_printf(str2, "%d", 2*i+1);
|
||||
string_t *p = dict_str_get (d1, str1);
|
||||
assert (p != NULL);
|
||||
assert (string_equal_p(*p, str2));
|
||||
p = dict_str_get_at (d1, str1);
|
||||
assert (p != NULL);
|
||||
assert (string_equal_p(*p, str2));
|
||||
}
|
||||
|
||||
//assert (dict_str_hash (d2) != 0);
|
||||
}
|
||||
}
|
||||
@@ -203,7 +246,7 @@ static void test_equal(void)
|
||||
string_printf(str1, "%d", i);
|
||||
dict_str_set_at(d2, str1, str1);
|
||||
string_printf(str1, "%d", n-1-i);
|
||||
dict_str_remove(d1, str1);
|
||||
dict_str_erase(d1, str1);
|
||||
assert(dict_str_equal_p(d1, d2) == ((i+1) == (n+1)/2));
|
||||
assert(dict_str_equal_p(d2, d1) == ((i+1) == (n+1)/2));
|
||||
}
|
||||
@@ -223,6 +266,8 @@ static void test1(void)
|
||||
FILE *f = fopen("dict.txt", "rt");
|
||||
if (!f) abort();
|
||||
dict_str_set_at(dict, STRING_CTE("LICENCE"), STRING_CTE("BSD3"));
|
||||
assert(string_equal_p(*dict_str_cget(dict, STRING_CTE("LICENCE")), STRING_CTE("BSD3")));
|
||||
|
||||
while (!feof(f) && !ferror(f)) {
|
||||
string_fgets(key, f, STRING_READ_PURE_LINE);
|
||||
size_t idx = string_search_char(key, ':');
|
||||
@@ -293,6 +338,21 @@ static void test_oa(void)
|
||||
assert (p == NULL);
|
||||
}
|
||||
}
|
||||
assert(dict_oa_int_size(d) == 50);
|
||||
|
||||
for(size_t i = 1 ; i < 150; i+= 3)
|
||||
*dict_oa_int_get_at(d, i) = i*i;
|
||||
assert(dict_oa_int_size(d) == 100);
|
||||
for(size_t i = 0 ; i < 150; i++) {
|
||||
int *p = dict_oa_int_get(d, i);
|
||||
if ((i % 3) != 2) {
|
||||
assert (p != NULL);
|
||||
assert (*p == M_ASSIGN_CAST(int, i*i));
|
||||
} else {
|
||||
assert (p == NULL);
|
||||
}
|
||||
}
|
||||
assert(dict_oa_int_size(d) == 100);
|
||||
|
||||
dict_oa_int_clear(d);
|
||||
}
|
||||
@@ -305,16 +365,16 @@ static void test_init_oa(void)
|
||||
}
|
||||
assert (dict_oa_int_size (d1) == 100);
|
||||
dict_oa_int_set_at (d1, 17, 42);
|
||||
dict_oa_int_remove (d1, 17);
|
||||
dict_oa_int_erase (d1, 17);
|
||||
dict_oa_int_t d3;
|
||||
dict_oa_int_init_set (d3, d1);
|
||||
//assert (dict_oa_int_equal_p (d3, d1));
|
||||
assert (dict_oa_int_equal_p (d3, d1));
|
||||
dict_oa_int_set (d2, d1);
|
||||
|
||||
assert (dict_oa_int_get (d2, -10) == NULL);
|
||||
assert (*dict_oa_int_get (d2, 10) == 11);
|
||||
//assert (dict_oa_int_equal_p (d2, d1));
|
||||
//assert (dict_oa_int_equal_p (d2, d3));
|
||||
assert (dict_oa_int_equal_p (d2, d1));
|
||||
assert (dict_oa_int_equal_p (d2, d3));
|
||||
dict_oa_int_clear (d3);
|
||||
|
||||
dict_oa_int_set_at (d1, -10, -20);
|
||||
@@ -324,14 +384,14 @@ static void test_init_oa(void)
|
||||
assert (dict_oa_int_size (d1) == 101);
|
||||
assert (*dict_oa_int_get (d1, -10) == -22);
|
||||
|
||||
//assert (!dict_oa_int_equal_p (d2, d1));
|
||||
bool b = dict_oa_int_remove (d1, 0);
|
||||
assert (!dict_oa_int_equal_p (d2, d1));
|
||||
bool b = dict_oa_int_erase (d1, 0);
|
||||
assert (dict_oa_int_size (d1) == 100);
|
||||
assert (b);
|
||||
//assert (!dict_oa_int_equal_p (d2, d1));
|
||||
assert (!dict_oa_int_equal_p (d2, d1));
|
||||
|
||||
for(size_t i = 1; i < 100; i++) {
|
||||
b = dict_oa_int_remove (d1, 2*i);
|
||||
b = dict_oa_int_erase (d1, 2*i);
|
||||
assert (b);
|
||||
}
|
||||
assert (dict_oa_int_size (d1) == 1);
|
||||
|
||||
+93
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -21,8 +21,13 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "m-mutex.h"
|
||||
#include "m-atomic.h"
|
||||
|
||||
#include "m-genint.h"
|
||||
|
||||
#define MAX_N 256
|
||||
|
||||
static void test(size_t n)
|
||||
{
|
||||
genint_t s;
|
||||
@@ -35,8 +40,8 @@ static void test(size_t n)
|
||||
assert (i == j);
|
||||
}
|
||||
i = genint_pop(s);
|
||||
assert (i == -1UL);
|
||||
assert (genint_pop(s) == -1UL);
|
||||
assert (i == GENINT_ERROR);
|
||||
assert (genint_pop(s) == GENINT_ERROR);
|
||||
|
||||
for(size_t j = 0; j< n ; j++) {
|
||||
genint_push(s, (j+k) % n );
|
||||
@@ -46,9 +51,91 @@ static void test(size_t n)
|
||||
genint_clear(s);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
/*******************************************************/
|
||||
|
||||
genint_t global;
|
||||
atomic_bool tab_g[MAX_N];
|
||||
|
||||
static void conso(void *p)
|
||||
{
|
||||
for(size_t n = 1; n < 256; n++) {
|
||||
test(n);
|
||||
size_t n = *(size_t*)p;
|
||||
for(int i = 0; i < 100000; i++) {
|
||||
unsigned int tab[4];
|
||||
for(int j= 0; j < 4; j++) {
|
||||
tab[j] = genint_pop(global);
|
||||
if (tab[j] != GENINT_ERROR) {
|
||||
assert (tab[j] < n);
|
||||
assert (atomic_load(&tab_g[tab[j]]) == false);
|
||||
atomic_store(&tab_g[tab[j]], true);
|
||||
}
|
||||
}
|
||||
for(int j = 0; j < 4; j++) {
|
||||
if (tab[j] != GENINT_ERROR) {
|
||||
assert (tab[j] < n);
|
||||
assert (atomic_load(&tab_g[tab[j]]) == true);
|
||||
atomic_store(&tab_g[tab[j]], false);
|
||||
genint_push(global, tab[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test2(size_t n)
|
||||
{
|
||||
m_thread_t idx[4];
|
||||
|
||||
genint_init(global, n);
|
||||
for(int i = 0; i < MAX_N; i++)
|
||||
atomic_init(&tab_g[i], false);
|
||||
|
||||
for(int i = 0; i < 4; i++) {
|
||||
m_thread_create (idx[i], conso, (void*)&n);
|
||||
}
|
||||
for(int i = 0; i < 4;i++) {
|
||||
m_thread_join(idx[i]);
|
||||
}
|
||||
|
||||
genint_clear(global);
|
||||
}
|
||||
|
||||
// In this test, the dimension of the global and the number of threads
|
||||
// match: we cannot have a failure.
|
||||
static void conso2(void *p)
|
||||
{
|
||||
size_t n = *(size_t*)p;
|
||||
for(int i = 0; i < 1000000; i++) {
|
||||
unsigned int j = genint_pop(global);
|
||||
assert (j != -1U);
|
||||
assert (j < n);
|
||||
genint_push(global, j);
|
||||
}
|
||||
}
|
||||
|
||||
static void test3(size_t n)
|
||||
{
|
||||
m_thread_t idx[MAX_N];
|
||||
assert (n <= MAX_N);
|
||||
|
||||
genint_init(global, n);
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
m_thread_create (idx[i], conso2, (void*)&n);
|
||||
}
|
||||
for(size_t i = 0; i < n;i++) {
|
||||
m_thread_join(idx[i]);
|
||||
}
|
||||
genint_clear(global);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
for(size_t n = 1; n < MAX_N; n++) {
|
||||
test(n);
|
||||
}
|
||||
for(size_t n = 1; n < MAX_N; n+=17) {
|
||||
test2(n);
|
||||
}
|
||||
for(size_t n = 2; n < 6; n++) {
|
||||
test3(n);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
+118
-4
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* MLIB - TEST module
|
||||
*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -32,9 +32,10 @@ typedef struct test_s {
|
||||
ILIST_INTERFACE (ilist_free, struct test_s);
|
||||
} test_t;
|
||||
|
||||
|
||||
#include "coverage.h"
|
||||
START_COVERAGE
|
||||
ILIST_DEF(ilist_tname, test_t)
|
||||
ILIST_DEF(ilist_tname, test_t, M_POD_OPLIST)
|
||||
END_COVERAGE
|
||||
|
||||
ILIST_DEF(ilist_free, test_t, (DEL(free)))
|
||||
@@ -47,7 +48,10 @@ static void test(void)
|
||||
x1.n = 1;
|
||||
x2.n = 2;
|
||||
x3.n = 3;
|
||||
|
||||
ilist_tname_init_field(&x1);
|
||||
ilist_tname_init_field(&x2);
|
||||
ilist_tname_init_field(&x3);
|
||||
|
||||
ilist_tname_init(list);
|
||||
|
||||
assert (ilist_tname_size (list) == 0);
|
||||
@@ -59,7 +63,9 @@ static void test(void)
|
||||
assert (ilist_tname_size (list) == 3);
|
||||
assert (ilist_tname_front (list)->n == 1);
|
||||
assert (ilist_tname_back (list)->n == 3);
|
||||
|
||||
assert (ilist_tname_next_obj(list, &x1) == &x2);
|
||||
assert (ilist_tname_previous_obj(list, &x2) == &x1);
|
||||
|
||||
int n = 1;
|
||||
for M_EACH(item, list, ILIST_OPLIST(ilist_tname)) {
|
||||
assert (n == item->n);
|
||||
@@ -83,6 +89,13 @@ static void test(void)
|
||||
ilist_tname_it_set(it2, it1);
|
||||
assert (ilist_tname_it_equal_p(it1, it2));
|
||||
|
||||
ilist_tname_t list2;
|
||||
ilist_tname_init(list2);
|
||||
ilist_tname_move(list2, list);
|
||||
assert (ilist_tname_size (list2) == 3);
|
||||
ilist_tname_init_move(list, list2);
|
||||
assert (ilist_tname_size (list) == 3);
|
||||
|
||||
for M_EACH(item, list, ILIST_OPLIST(ilist_tname)) {
|
||||
if (item->n == 2)
|
||||
ilist_tname_unlink(item);
|
||||
@@ -99,9 +112,109 @@ static void test(void)
|
||||
assert(false);
|
||||
}
|
||||
|
||||
ilist_tname_push_back (list, &x3);
|
||||
assert (ilist_tname_size (list) == 1);
|
||||
ilist_tname_push_front (list, &x1);
|
||||
assert (ilist_tname_size (list) == 2);
|
||||
ilist_tname_push_after (&x1, &x2);
|
||||
assert (ilist_tname_size (list) == 3);
|
||||
assert (ilist_tname_front (list)->n == 1);
|
||||
assert (ilist_tname_back (list)->n == 3);
|
||||
|
||||
ilist_tname_it(it1, list);
|
||||
ilist_tname_next(it1);
|
||||
ilist_tname_remove(list, it1);
|
||||
assert (ilist_tname_size (list) == 2);
|
||||
assert (ilist_tname_cref(it1) == &x3);
|
||||
assert (ilist_tname_previous_obj(list, &x3) == &x1);
|
||||
|
||||
ilist_tname_clear(list);
|
||||
}
|
||||
|
||||
#define NUM 10
|
||||
static void test2(void)
|
||||
{
|
||||
test_t x[NUM];
|
||||
M_LET(list1, list2, ILIST_OPLIST(ilist_tname)) {
|
||||
for(int i = 0; i < NUM; i++)
|
||||
x[i].n = i;
|
||||
for(int i = 0; i < NUM; i++) {
|
||||
ilist_tname_push_back(list1, &x[i]);
|
||||
}
|
||||
assert (ilist_tname_size(list1) == NUM);
|
||||
assert (ilist_tname_size(list2) == 0);
|
||||
assert (ilist_tname_equal_p(list1, list2) == false);
|
||||
ilist_tname_it_t it;
|
||||
ilist_tname_it (it, list1);
|
||||
for(int i = 0; i < NUM; i++) {
|
||||
assert (ilist_tname_cref(it)->n == i);
|
||||
ilist_tname_splice_back (list2, list1, it);
|
||||
}
|
||||
assert (ilist_tname_size(list1) == 0);
|
||||
assert (ilist_tname_size(list2) == NUM);
|
||||
assert (ilist_tname_equal_p(list1, list2) == false);
|
||||
int n = 0;
|
||||
for M_EACH(item, list2, ILIST_OPLIST(ilist_tname)) {
|
||||
assert (n == item->n);
|
||||
n++;
|
||||
}
|
||||
assert (n == NUM);
|
||||
|
||||
ilist_tname_it_t it2;
|
||||
ilist_tname_it_end (it, list1);
|
||||
ilist_tname_it (it2, list2);
|
||||
for(int i = 0; i < NUM; i++) {
|
||||
assert (ilist_tname_cref(it2)->n == i);
|
||||
ilist_tname_splice_at (list1, it, list2, it2);
|
||||
assert (ilist_tname_cref(it)->n == i);
|
||||
}
|
||||
assert (ilist_tname_size(list1) == NUM);
|
||||
assert (ilist_tname_size(list2) == 0);
|
||||
n = 0;
|
||||
for M_EACH(item, list1, ILIST_OPLIST(ilist_tname)) {
|
||||
assert (n == item->n);
|
||||
n++;
|
||||
}
|
||||
assert (n == NUM);
|
||||
ilist_tname_reverse(list1);
|
||||
for M_EACH(item, list1, ILIST_OPLIST(ilist_tname)) {
|
||||
n--;
|
||||
assert (n == item->n);
|
||||
}
|
||||
assert (n == 0);
|
||||
|
||||
}
|
||||
M_LET(list1, list2, ILIST_OPLIST(ilist_tname)) {
|
||||
for(int i = 0; i < NUM; i++)
|
||||
x[i].n = i;
|
||||
for(int i = 0; i < NUM/2; i++) {
|
||||
ilist_tname_push_back(list1, &x[i]);
|
||||
}
|
||||
for(int i = NUM/2; i < NUM; i++) {
|
||||
ilist_tname_push_back(list2, &x[i]);
|
||||
}
|
||||
ilist_tname_splice(list1, list2);
|
||||
assert (ilist_tname_size(list1) == NUM);
|
||||
assert (ilist_tname_size(list2) == 0);
|
||||
int n = 0;
|
||||
for M_EACH(item, list1, ILIST_OPLIST(ilist_tname)) {
|
||||
assert (n == item->n);
|
||||
n++;
|
||||
}
|
||||
assert (n == NUM);
|
||||
|
||||
ilist_tname_swap(list1, list2);
|
||||
assert (ilist_tname_size(list2) == NUM);
|
||||
assert (ilist_tname_size(list1) == 0);
|
||||
n = 0;
|
||||
for M_EACH(item, list2, ILIST_OPLIST(ilist_tname)) {
|
||||
assert (n == item->n);
|
||||
n++;
|
||||
}
|
||||
assert (n == NUM);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_free(void)
|
||||
{
|
||||
ilist_free_t list;
|
||||
@@ -129,6 +242,7 @@ static void test_free(void)
|
||||
int main(void)
|
||||
{
|
||||
test();
|
||||
test2();
|
||||
test_free();
|
||||
exit(0);
|
||||
}
|
||||
+407
-45
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -21,41 +21,24 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <gmp.h> // For testing purpose only.
|
||||
#include "test-obj.h"
|
||||
#include "m-string.h"
|
||||
#include "m-list.h"
|
||||
|
||||
static void my_mpz_out_str(FILE *f, const mpz_t z)
|
||||
{
|
||||
mpz_out_str (f, 10, z);
|
||||
}
|
||||
|
||||
static bool my_mpz_in_str(mpz_t z, FILE *f)
|
||||
{
|
||||
return mpz_inp_str (z, f, 10) > 0;
|
||||
}
|
||||
|
||||
static bool my_mpz_equal_p(const mpz_t z1, const mpz_t z2)
|
||||
{
|
||||
return mpz_cmp (z1, z2) == 0;
|
||||
}
|
||||
|
||||
static void my_mpz_str(string_t str, const mpz_t z, bool append)
|
||||
{
|
||||
char *s = mpz_get_str(NULL, 10, z);
|
||||
if (append) string_cat_str (str, s);
|
||||
else string_set_str (str, s);
|
||||
free(s);
|
||||
}
|
||||
|
||||
#include "coverage.h"
|
||||
START_COVERAGE
|
||||
LIST_DEF(list_uint, unsigned int)
|
||||
LIST_DEF(list_mpz, mpz_t, (INIT(mpz_init), INIT_SET(mpz_init_set), SET(mpz_set), CLEAR(mpz_clear), \
|
||||
OUT_STR(my_mpz_out_str), IN_STR(my_mpz_in_str), EQUAL(my_mpz_equal_p), \
|
||||
GET_STR(my_mpz_str)))
|
||||
LIST_DEF(list_mpz, testobj_t, TESTOBJ_OPLIST)
|
||||
LIST_DUAL_PUSH_DEF(list2_double, double)
|
||||
END_COVERAGE
|
||||
LIST_DUAL_PUSH_DEF(list2_mpz, testobj_t, TESTOBJ_OPLIST)
|
||||
#define LIST_UINT_OPLIST LIST_OPLIST(list_uint)
|
||||
#define LIST2_DOUBLE_OPLIST LIST_OPLIST(list2_double)
|
||||
|
||||
// Array with the minimum number of methods.
|
||||
LIST_DEF(list_min_z, testobj_t, (INIT_SET(testobj_init_set), SET(testobj_set), CLEAR(testobj_clear)))
|
||||
LIST_DUAL_PUSH_DEF(list2_min_z, testobj_t, (INIT_SET(testobj_init_set), SET(testobj_set), CLEAR(testobj_clear)))
|
||||
|
||||
|
||||
static void test_uint(void)
|
||||
{
|
||||
@@ -203,7 +186,33 @@ static void test_uint(void)
|
||||
assert (!list_uint_equal_p(v,v2));
|
||||
|
||||
assert (list_uint_hash(v) != 0);
|
||||
|
||||
|
||||
x = 1478963;
|
||||
s = *list_uint_back(v);
|
||||
list_uint_push_move(v, &x);
|
||||
assert (*list_uint_back(v) == 1478963);
|
||||
x = 0;
|
||||
list_uint_pop_move(&x, v);
|
||||
assert (x == 1478963);
|
||||
assert (s == *list_uint_back(v));
|
||||
|
||||
list_uint_clean(v);
|
||||
list_uint_clean(v2);
|
||||
for(unsigned int i = 0; i < 10; i++)
|
||||
list_uint_push_back (v, i);
|
||||
list_uint_it (u, v);
|
||||
list_uint_it_end (u2, v2);
|
||||
for(unsigned int i = 0; i < 10; i++) {
|
||||
list_uint_splice_at (v2, u2, v, u);
|
||||
}
|
||||
assert (list_uint_empty_p(v));
|
||||
assert (list_uint_size (v2) == 10);
|
||||
s = 9;
|
||||
for M_EACH(item, v2, LIST_UINT_OPLIST) {
|
||||
assert (*item == s);
|
||||
s --;
|
||||
}
|
||||
|
||||
list_uint_clear(v);
|
||||
list_uint_clear(v2);
|
||||
}
|
||||
@@ -211,36 +220,36 @@ static void test_uint(void)
|
||||
static void test_mpz(void)
|
||||
{
|
||||
list_mpz_t v;
|
||||
mpz_t z, x, s;
|
||||
testobj_t z, x, s;
|
||||
|
||||
mpz_init (z);
|
||||
mpz_init (x);
|
||||
mpz_init (s);
|
||||
testobj_init (z);
|
||||
testobj_init (x);
|
||||
testobj_init (s);
|
||||
|
||||
list_mpz_init(v);
|
||||
assert (list_mpz_empty_p(v) == true);
|
||||
|
||||
mpz_set_ui (z, 2);
|
||||
testobj_set_ui (z, 2);
|
||||
list_mpz_push_back(v, z);
|
||||
assert (list_mpz_empty_p(v) == false);
|
||||
|
||||
list_mpz_pop_back(&x, v);
|
||||
assert( mpz_cmp (x, z) == 0);
|
||||
assert( testobj_equal_p (x, z));
|
||||
assert (list_mpz_empty_p(v) == true);
|
||||
assert (list_mpz_size(v) == 0);
|
||||
|
||||
for(unsigned int i = 0; i < 1000; i ++) {
|
||||
mpz_set_ui (z, i);
|
||||
testobj_set_ui (z, i);
|
||||
list_mpz_push_back(v, z);
|
||||
}
|
||||
assert (list_mpz_size(v) == 1000);
|
||||
|
||||
list_mpz_it_t u;
|
||||
mpz_set_ui (s, 0);
|
||||
testobj_set_ui (s, 0);
|
||||
for(list_mpz_it(u, v); !list_mpz_end_p(u); list_mpz_next(u)) {
|
||||
mpz_add (s, s, *list_mpz_cref(u));
|
||||
testobj_add (s, s, *list_mpz_cref(u));
|
||||
}
|
||||
assert (mpz_get_ui(s) == 1000*999/2);
|
||||
assert (testobj_get_ui(s) == 1000*999/2);
|
||||
|
||||
FILE *f = fopen ("a-mlist.dat", "wt");
|
||||
if (!f) abort();
|
||||
@@ -257,29 +266,382 @@ static void test_mpz(void)
|
||||
list_mpz_clear(list2);
|
||||
|
||||
list_mpz_clean(v);
|
||||
list_mpz_init(list2);
|
||||
M_LET(str, STRING_OPLIST) {
|
||||
list_mpz_get_str(str, v, false);
|
||||
assert (string_cmp_str (str, "[]") == 0);
|
||||
mpz_set_ui (z, 17);
|
||||
const char *sp;
|
||||
b = list_mpz_parse_str(list2, string_get_cstr(str), &sp);
|
||||
assert(b);
|
||||
assert(*sp == 0);
|
||||
assert(list_mpz_equal_p(v, list2));
|
||||
|
||||
testobj_set_ui (z, 17);
|
||||
list_mpz_push_back (v, z);
|
||||
list_mpz_get_str(str, v, false);
|
||||
assert (string_cmp_str (str, "[17]") == 0);
|
||||
mpz_set_ui (z, 42);
|
||||
b = list_mpz_parse_str(list2, string_get_cstr(str), &sp);
|
||||
assert(b);
|
||||
assert(*sp == 0);
|
||||
assert(list_mpz_equal_p(v, list2));
|
||||
|
||||
testobj_set_ui (z, 42);
|
||||
list_mpz_push_back (v, z);
|
||||
list_mpz_get_str(str, v, true);
|
||||
assert (string_cmp_str (str, "[17][42,17]") == 0);
|
||||
b = list_mpz_parse_str(list2, string_get_cstr(str), &sp);
|
||||
assert(b);
|
||||
assert(strcmp(sp, "[42,17]") == 0);
|
||||
assert(!list_mpz_equal_p(v, list2));
|
||||
b = list_mpz_parse_str(list2, sp, &sp);
|
||||
assert(b);
|
||||
assert(strcmp(sp, "") == 0);
|
||||
assert(list_mpz_equal_p(v, list2));
|
||||
}
|
||||
|
||||
list_mpz_clear(v);
|
||||
|
||||
mpz_clear (z);
|
||||
mpz_clear (x);
|
||||
mpz_clear (s);
|
||||
list_mpz_clear(list2);
|
||||
|
||||
testobj_clear (z);
|
||||
testobj_clear (x);
|
||||
testobj_clear (s);
|
||||
}
|
||||
|
||||
static void test_dual_push1(void)
|
||||
{
|
||||
list2_double_t list;
|
||||
double d;
|
||||
|
||||
list2_double_init(list);
|
||||
assert(list2_double_empty_p(list));
|
||||
assert(list2_double_size(list) == 0);
|
||||
|
||||
list2_double_push_back(list, 2.0);
|
||||
assert(!list2_double_empty_p(list));
|
||||
assert (*list2_double_back(list) == 2.0);
|
||||
assert (*list2_double_front(list) == 2.0);
|
||||
list2_double_pop_back(&d, list);
|
||||
assert (d == 2.0);
|
||||
assert(list2_double_empty_p(list));
|
||||
assert(list2_double_size(list) == 0);
|
||||
|
||||
list2_double_push_front(list, 3.0);
|
||||
assert (*list2_double_back(list) == 3.0);
|
||||
assert (*list2_double_front(list) == 3.0);
|
||||
assert(!list2_double_empty_p(list));
|
||||
list2_double_pop_back(&d, list);
|
||||
assert (d == 3.0);
|
||||
assert(list2_double_empty_p(list));
|
||||
assert(list2_double_size(list) == 0);
|
||||
|
||||
list2_double_push_back(list, 2.0);
|
||||
list2_double_push_back(list, 3.0);
|
||||
list2_double_push_front(list, 1.0);
|
||||
list2_double_push_front(list, 0.0);
|
||||
assert (*list2_double_back(list) == 3.0);
|
||||
assert (*list2_double_front(list) == 0.0);
|
||||
list2_double_pop_back(&d, list);
|
||||
assert (d == 3.0);
|
||||
list2_double_pop_back(&d, list);
|
||||
assert (d == 2.0);
|
||||
list2_double_pop_back(&d, list);
|
||||
assert (d == 1.0);
|
||||
list2_double_pop_back(&d, list);
|
||||
assert (d == 0.0);
|
||||
assert(list2_double_empty_p(list));
|
||||
|
||||
list2_double_push_back_new(list);
|
||||
list2_double_pop_back(&d, list);
|
||||
assert (d == 0.0);
|
||||
list2_double_push_front_new(list);
|
||||
list2_double_pop_back(&d, list);
|
||||
assert (d == 0.0);
|
||||
|
||||
list2_double_push_back_new(list);
|
||||
list2_double_pop_back(NULL, list);
|
||||
assert(list2_double_empty_p(list));
|
||||
list2_double_push_front_new(list);
|
||||
list2_double_pop_back(NULL, list);
|
||||
assert(list2_double_empty_p(list));
|
||||
|
||||
for(double e = 0.0; e < 1024.0; e += 1.0) {
|
||||
if (e < 1000.0)
|
||||
list2_double_push_front(list, e);
|
||||
if (e >= 24.0 && e < 1000.0) {
|
||||
assert (list2_double_size (list) == 25);
|
||||
}
|
||||
if (e >= 24.0) {
|
||||
list2_double_pop_back(&d, list);
|
||||
assert (d + 24.0 == e);
|
||||
}
|
||||
}
|
||||
|
||||
list2_double_push_back(list, 15);
|
||||
list2_double_t list2;
|
||||
list2_double_init(list2);
|
||||
assert(list2_double_empty_p(list2));
|
||||
assert(!list2_double_empty_p(list));
|
||||
list2_double_swap(list, list2);
|
||||
assert(!list2_double_empty_p(list2));
|
||||
assert(list2_double_empty_p(list));
|
||||
list2_double_clear(list2);
|
||||
|
||||
for(double e = 0.0; e < 1024.0; e += 1.0) {
|
||||
list2_double_push_back(list, e);
|
||||
}
|
||||
list2_double_init_set (list2, list);
|
||||
assert(list2_double_equal_p(list, list2));
|
||||
|
||||
list2_double_reverse(list2);
|
||||
for(double e = 0.0; e < 1024.0 ; e += 1.0) {
|
||||
list2_double_pop_back(&d, list2);
|
||||
assert (d == e);
|
||||
}
|
||||
assert(list2_double_empty_p(list2));
|
||||
|
||||
list2_double_splice(list2, list);
|
||||
assert(list2_double_size(list2) == 1024);
|
||||
assert(list2_double_size(list) == 0);
|
||||
list2_double_set(list, list2);
|
||||
assert(list2_double_size(list) == 1024);
|
||||
list2_double_splice(list2, list);
|
||||
assert(list2_double_size(list2) == 2048);
|
||||
assert(list2_double_size(list) == 0);
|
||||
|
||||
list2_double_move(list, list2);
|
||||
list2_double_clear(list);
|
||||
}
|
||||
|
||||
static void test_dual_it1(void)
|
||||
{
|
||||
list2_double_t list;
|
||||
list2_double_it_t it, it2;
|
||||
double d;
|
||||
|
||||
list2_double_init(list);
|
||||
|
||||
list2_double_it(it, list);
|
||||
assert(list2_double_end_p(it));
|
||||
list2_double_it_end(it, list);
|
||||
assert(list2_double_end_p(it));
|
||||
|
||||
for(double e = 0.0; e < 1024.0 ; e += 1.0)
|
||||
list2_double_push_front(list, e);
|
||||
|
||||
list2_double_it(it, list);
|
||||
assert(!list2_double_end_p(it));
|
||||
list2_double_it_set (it2, it);
|
||||
assert(list2_double_it_equal_p(it2, it));
|
||||
list2_double_it_end(it, list);
|
||||
assert(!list2_double_it_equal_p(it2, it));
|
||||
assert(list2_double_end_p(it));
|
||||
assert(!list2_double_end_p(it2));
|
||||
assert(*list2_double_ref(it2) == 0.0);
|
||||
|
||||
d = 0.0;
|
||||
for(list2_double_it(it, list) ;
|
||||
!list2_double_end_p(it) ;
|
||||
list2_double_next(it) ) {
|
||||
double e = *list2_double_cref(it);
|
||||
assert(e == d);
|
||||
if (list2_double_last_p(it))
|
||||
assert (d == 1023.0);
|
||||
d += 1.0;
|
||||
}
|
||||
assert (d == 1024.0);
|
||||
|
||||
// Insert in last
|
||||
list2_double_it_end(it, list);
|
||||
list2_double_insert(list, it, -1.0);
|
||||
d = -1.0;
|
||||
for(list2_double_it(it, list) ;
|
||||
!list2_double_end_p(it) ;
|
||||
list2_double_next(it) ) {
|
||||
double e = *list2_double_cref(it);
|
||||
assert(e == d);
|
||||
if (list2_double_last_p(it))
|
||||
list2_double_it_set(it2, it);
|
||||
d += 1.0;
|
||||
}
|
||||
assert (d == 1024.0);
|
||||
assert(*list2_double_back(list) == -1.0);
|
||||
assert(*list2_double_front(list) == 1023.0);
|
||||
|
||||
// Insert in front
|
||||
list2_double_insert(list, it2, 1024.0);
|
||||
d = -1.0;
|
||||
for(list2_double_it(it, list) ;
|
||||
!list2_double_end_p(it) ;
|
||||
list2_double_next(it) ) {
|
||||
double e = *list2_double_cref(it);
|
||||
assert(e == d);
|
||||
d += 1.0;
|
||||
}
|
||||
assert (d == 1025.0);
|
||||
assert(*list2_double_back(list) == -1.0);
|
||||
assert(*list2_double_front(list) == 1024.0);
|
||||
|
||||
// Insert in between
|
||||
list2_double_it(it, list) ;
|
||||
list2_double_insert(list, it, -0.5);
|
||||
d = -1.0;
|
||||
for(list2_double_it(it, list) ;
|
||||
!list2_double_end_p(it) ;
|
||||
list2_double_next(it) ) {
|
||||
double e = *list2_double_cref(it);
|
||||
assert(e == d);
|
||||
d += (d < 0.0) ? 0.5 : 1.0;
|
||||
}
|
||||
assert (d == 1025.0);
|
||||
assert(*list2_double_back(list) == -1.0);
|
||||
assert(*list2_double_front(list) == 1024.0);
|
||||
|
||||
// Remove in between
|
||||
list2_double_it(it, list) ;
|
||||
list2_double_next(it);
|
||||
assert (*list2_double_cref(it) == -0.5);
|
||||
list2_double_remove(list, it);
|
||||
d = -1.0;
|
||||
for(list2_double_it(it, list) ;
|
||||
!list2_double_end_p(it) ;
|
||||
list2_double_next(it) ) {
|
||||
double e = *list2_double_cref(it);
|
||||
assert(e == d);
|
||||
if (list2_double_last_p(it))
|
||||
list2_double_it_set(it2, it);
|
||||
d += 1.0;
|
||||
}
|
||||
assert (d == 1025.0);
|
||||
assert(*list2_double_back(list) == -1.0);
|
||||
assert(*list2_double_front(list) == 1024.0);
|
||||
|
||||
// Remove front
|
||||
list2_double_remove(list, it2);
|
||||
d = -1.0;
|
||||
for(list2_double_it(it, list) ;
|
||||
!list2_double_end_p(it) ;
|
||||
list2_double_next(it) ) {
|
||||
double e = *list2_double_cref(it);
|
||||
assert(e == d);
|
||||
d += 1.0;
|
||||
}
|
||||
assert (d == 1024.0);
|
||||
assert(*list2_double_back(list) == -1.0);
|
||||
assert(*list2_double_front(list) == 1023.0);
|
||||
|
||||
// Remove back
|
||||
list2_double_it(it, list);
|
||||
list2_double_remove(list, it);
|
||||
d = 0.0;
|
||||
for(list2_double_it(it, list) ;
|
||||
!list2_double_end_p(it) ;
|
||||
list2_double_next(it) ) {
|
||||
double e = *list2_double_cref(it);
|
||||
assert(e == d);
|
||||
d += 1.0;
|
||||
}
|
||||
assert (d == 1024.0);
|
||||
assert(*list2_double_back(list) == 0.0);
|
||||
assert(*list2_double_front(list) == 1023.0);
|
||||
|
||||
list2_double_t list2;
|
||||
list2_double_init_set(list2, list);
|
||||
|
||||
// Splice back the back of list
|
||||
list2_double_it(it, list);
|
||||
list2_double_splice_back(list2, list, it);
|
||||
assert(*list2_double_back(list) == 1.0);
|
||||
assert(*list2_double_cref(it) == 1.0);
|
||||
list2_double_it(it2, list2);
|
||||
assert(*list2_double_cref(it2) == 0.0);
|
||||
list2_double_next(it2);
|
||||
assert(*list2_double_cref(it2) == 0.0);
|
||||
list2_double_next(it2);
|
||||
assert(*list2_double_cref(it2) == 1.0);
|
||||
|
||||
// Splice back an element of list
|
||||
list2_double_next(it);
|
||||
list2_double_splice_back(list2, list, it);
|
||||
assert(*list2_double_back(list) == 1.0);
|
||||
assert(*list2_double_cref(it) == 3.0);
|
||||
list2_double_it(it2, list2);
|
||||
assert(*list2_double_cref(it2) == 2.0);
|
||||
list2_double_next(it2);
|
||||
assert(*list2_double_cref(it2) == 0.0);
|
||||
list2_double_next(it2);
|
||||
assert(*list2_double_cref(it2) == 0.0);
|
||||
list2_double_next(it2);
|
||||
assert(*list2_double_cref(it2) == 1.0);
|
||||
|
||||
list2_double_clean(list);
|
||||
list2_double_clean(list2);
|
||||
for(double i = 0; i < 10; i++)
|
||||
list2_double_push_back (list, i);
|
||||
list2_double_it (it, list);
|
||||
list2_double_it_end (it2, list2);
|
||||
for(double i = 0; i < 10; i++) {
|
||||
list2_double_splice_at (list2, it2, list, it);
|
||||
}
|
||||
assert (list2_double_empty_p(list));
|
||||
assert (list2_double_size (list2) == 10);
|
||||
double s = 9.0;
|
||||
for M_EACH(item, list2, LIST2_DOUBLE_OPLIST) {
|
||||
assert (*item == s);
|
||||
s -= 1.0;
|
||||
}
|
||||
|
||||
list2_double_clear(list2);
|
||||
list2_double_clear(list);
|
||||
}
|
||||
|
||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
||||
|
||||
LIST_DEF(list_int, uint32_t)
|
||||
#define M_OPL_list_int_t() LIST_OPLIST(list_int)
|
||||
|
||||
static void test_out_default_oplist(void)
|
||||
{
|
||||
M_LET(str, string_t)
|
||||
M_LET(list, l2, list_int_t) {
|
||||
FILE *f = fopen ("a-mlist.dat", "wt");
|
||||
if (!f) abort();
|
||||
list_int_push_back(list, 2);
|
||||
list_int_push_back(list, 3);
|
||||
list_int_out_str(f, list);
|
||||
fclose(f);
|
||||
f = fopen("a-mlist.dat", "rt");
|
||||
if (!f) abort();
|
||||
char buffer[1024];
|
||||
assert (fgets(buffer, 1024, f) == buffer);
|
||||
assert (strcmp(buffer, "[3,2]") == 0);
|
||||
fclose(f);
|
||||
list_int_get_str(str, list, false);
|
||||
assert (string_equal_str_p(str, "[3,2]"));
|
||||
f = fopen("a-mlist.dat", "rt");
|
||||
if (!f) abort();
|
||||
assert (list_int_in_str(l2, f) == true);
|
||||
assert (list_int_equal_p(l2, list));
|
||||
fclose(f);
|
||||
}
|
||||
M_LET(str, string_t)
|
||||
M_LET( (list, 1, 2, 3, 4, 5), list_int_t) {
|
||||
list_int_get_str(str, list, false);
|
||||
assert (string_equal_str_p(str, "[5,4,3,2,1]"));
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void test_out_default_oplist(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_uint();
|
||||
test_mpz();
|
||||
test_dual_push1();
|
||||
test_dual_it1();
|
||||
test_out_default_oplist();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -22,12 +22,14 @@
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include "test-obj.h"
|
||||
#include "m-prioqueue.h"
|
||||
|
||||
#include "coverage.h"
|
||||
START_COVERAGE
|
||||
PRIOQUEUE_DEF(int_pqueue, int)
|
||||
END_COVERAGE
|
||||
PRIOQUEUE_DEF(mpz_pqueue, testobj_t, TESTOBJ_CMP_OPLIST)
|
||||
|
||||
static void test1(void)
|
||||
{
|
||||
@@ -66,7 +68,11 @@ static void test1(void)
|
||||
int_pqueue_push(p, 5);
|
||||
int_pqueue_pop(&x, p);
|
||||
assert (x == 5);
|
||||
|
||||
int_pqueue_push(p, 17);
|
||||
bool b = int_pqueue_erase(p, 17);
|
||||
assert(b);
|
||||
b = int_pqueue_erase(p, 1742);
|
||||
assert(!b);
|
||||
int_pqueue_clear(p);
|
||||
}
|
||||
|
||||
@@ -99,4 +105,5 @@ int main(void)
|
||||
{
|
||||
test1();
|
||||
test2();
|
||||
exit(0);
|
||||
}
|
||||
+102
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -21,7 +21,7 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <gmp.h> // For testing purpose only.
|
||||
#include "test-obj.h"
|
||||
#include "m-string.h"
|
||||
#include "m-rbtree.h"
|
||||
|
||||
@@ -31,10 +31,19 @@ static bool uint_in_str(unsigned int *u, FILE *f)
|
||||
return n == 1;
|
||||
}
|
||||
|
||||
static bool uint_parse_str(unsigned int *u, const char str[], const char **endptr)
|
||||
{
|
||||
char *end;
|
||||
*u = strtol(str, &end, 0);
|
||||
if (endptr) { *endptr = (const char*) end; }
|
||||
return (uintptr_t) end != (uintptr_t) str;
|
||||
}
|
||||
|
||||
static void uint_out_str(FILE *f, unsigned int u)
|
||||
{
|
||||
fprintf(f, "%u", u);
|
||||
}
|
||||
|
||||
static void uint_get_str(string_t str, unsigned int u, bool append)
|
||||
{
|
||||
(append ? string_cat_printf : string_printf) (str, "%u", u);
|
||||
@@ -42,22 +51,59 @@ static void uint_get_str(string_t str, unsigned int u, bool append)
|
||||
|
||||
#include "coverage.h"
|
||||
START_COVERAGE
|
||||
RBTREE_DEF(rbtree_uint, unsigned int, M_OPEXTEND(M_DEFAULT_OPLIST, IN_STR(uint_in_str M_IPTR), OUT_STR(uint_out_str), GET_STR(uint_get_str)) )
|
||||
RBTREE_DEF(rbtree_float, float)
|
||||
RBTREE_DEF(rbtree_uint, unsigned int, M_OPEXTEND(M_DEFAULT_OPLIST, IN_STR(uint_in_str M_IPTR), OUT_STR(uint_out_str), GET_STR(uint_get_str), PARSE_STR(uint_parse_str M_IPTR)) )
|
||||
END_COVERAGE
|
||||
|
||||
RBTREE_DEF(rbtree_float, float)
|
||||
RBTREE_DEF(rbtree_mpz, testobj_t, TESTOBJ_OPLIST)
|
||||
|
||||
#define UINT_OPLIST RBTREE_OPLIST(rbtree_uint)
|
||||
#define FLOAT_OP RBTREE_OPLIST(rbtree_float)
|
||||
|
||||
static void test_uint(void)
|
||||
{
|
||||
rbtree_uint_t tree, tree2;
|
||||
rbtree_uint_t tree, tree2, tree3;
|
||||
rbtree_uint_init(tree);
|
||||
assert (rbtree_uint_empty_p(tree));
|
||||
rbtree_uint_init_set(tree2, tree);
|
||||
assert (rbtree_uint_empty_p(tree2));
|
||||
rbtree_uint_clear(tree);
|
||||
|
||||
rbtree_uint_push(tree, 10);
|
||||
rbtree_uint_push(tree, 5);
|
||||
rbtree_uint_push(tree, 17);
|
||||
rbtree_uint_push(tree, 3);
|
||||
rbtree_uint_push(tree, 11);
|
||||
assert(rbtree_uint_size(tree) == 5);
|
||||
assert(rbtree_uint_empty_p(tree) == false);
|
||||
const unsigned int tab[] = {3, 5, 10, 11, 17};
|
||||
int k = 0;
|
||||
for M_EACH(item, tree, UINT_OPLIST) {
|
||||
assert (tab[k] == *item);
|
||||
k ++;
|
||||
}
|
||||
assert (k == 5);
|
||||
rbtree_uint_it_t it, it2;
|
||||
for (rbtree_uint_it_last(it, tree) ; !rbtree_uint_end_p(it) ; rbtree_uint_previous(it)) {
|
||||
k--;
|
||||
rbtree_uint_it_set(it2, it);
|
||||
assert(rbtree_uint_it_equal_p(it2, it));
|
||||
assert (tab[k] == *rbtree_uint_cref(it2) );
|
||||
}
|
||||
rbtree_uint_it_end(it, tree);
|
||||
assert (rbtree_uint_end_p(it));
|
||||
assert(!rbtree_uint_it_equal_p(it2, it));
|
||||
assert( k == 0);
|
||||
assert (rbtree_uint_hash(tree) != 0);
|
||||
|
||||
rbtree_uint_init(tree3);
|
||||
rbtree_uint_push(tree3, 5);
|
||||
rbtree_uint_move(tree3, tree);
|
||||
rbtree_uint_clear(tree3);
|
||||
|
||||
rbtree_uint_init_move(tree, tree2);
|
||||
rbtree_uint_init_move(tree2, tree);
|
||||
assert (rbtree_uint_empty_p(tree2));
|
||||
|
||||
const unsigned int max = 1001;
|
||||
for(unsigned int num = 1; num < max; num++) {
|
||||
rbtree_uint_init(tree);
|
||||
@@ -65,6 +111,11 @@ static void test_uint(void)
|
||||
rbtree_uint_push(tree, i);
|
||||
assert(rbtree_uint_size(tree) == num);
|
||||
assert(rbtree_uint_empty_p(tree) == false);
|
||||
if (num > 10) {
|
||||
rbtree_uint_push(tree, 10);
|
||||
assert(rbtree_uint_size(tree) == num);
|
||||
assert(rbtree_uint_empty_p(tree) == false);
|
||||
}
|
||||
unsigned int count = 0;
|
||||
for M_EACH(item, tree, UINT_OPLIST) {
|
||||
assert (count == *item);
|
||||
@@ -72,27 +123,32 @@ static void test_uint(void)
|
||||
}
|
||||
assert (*rbtree_uint_min(tree) == 0);
|
||||
assert (*rbtree_uint_max(tree) == num-1);
|
||||
assert (*rbtree_uint_cmin(tree) == 0);
|
||||
assert (*rbtree_uint_cmax(tree) == num-1);
|
||||
assert (count == num);
|
||||
rbtree_uint_set(tree2, tree);
|
||||
unsigned int *ptr = rbtree_uint_get(tree2, num/2);
|
||||
assert (ptr != NULL);
|
||||
assert (*ptr == num/2);
|
||||
const unsigned int *cptr = rbtree_uint_cget(tree2, num/2);
|
||||
assert (cptr != NULL);
|
||||
assert (*cptr == num/2);
|
||||
rbtree_uint_clear(tree);
|
||||
|
||||
bool b = rbtree_uint_pop(NULL, tree2, num+1);
|
||||
bool b = rbtree_uint_pop_at(NULL, tree2, num+1);
|
||||
assert (b == false);
|
||||
for(unsigned int i = 0; i < num; i+=3) {
|
||||
b = rbtree_uint_pop(NULL, tree2, i);
|
||||
b = rbtree_uint_pop_at(NULL, tree2, i);
|
||||
assert(b);
|
||||
ptr = rbtree_uint_get(tree2, i);
|
||||
assert (ptr == NULL);
|
||||
}
|
||||
for(unsigned int i = 1; i < num; i+=3) {
|
||||
b = rbtree_uint_pop(NULL, tree2, i);
|
||||
b = rbtree_uint_pop_at(NULL, tree2, i);
|
||||
assert(b);
|
||||
ptr = rbtree_uint_get(tree2, i);
|
||||
assert (ptr == NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rbtree_uint_init (tree);
|
||||
@@ -110,6 +166,32 @@ static void test_uint(void)
|
||||
rbtree_uint_clear(tree2);
|
||||
}
|
||||
|
||||
static void test_uint_permut(void)
|
||||
{
|
||||
const unsigned int max = 1001;
|
||||
unsigned int tab[1001];
|
||||
for(unsigned int num = 1; num < max; num++) {
|
||||
rbtree_uint_t tree;
|
||||
rbtree_uint_init(tree);
|
||||
for(unsigned int i = 0; i < num; i++)
|
||||
tab[i] = i;
|
||||
for(unsigned int i = 0; i < 2 * num; i++) {
|
||||
int j = rand() % num;
|
||||
int k = rand() % num;
|
||||
M_SWAP(unsigned int, tab[j], tab[k]);
|
||||
}
|
||||
for(unsigned int i = 0; i < num; i++)
|
||||
rbtree_uint_push(tree, tab[i]);
|
||||
unsigned int k = 0;
|
||||
for M_EACH(item, tree, UINT_OPLIST) {
|
||||
assert (k == *item);
|
||||
k++;
|
||||
}
|
||||
rbtree_uint_clear(tree);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void test_float(void)
|
||||
{
|
||||
M_LET(tree, FLOAT_OP) {
|
||||
@@ -146,6 +228,11 @@ static void test_io(void)
|
||||
|
||||
rbtree_uint_get_str(str, tree1, false);
|
||||
assert(string_equal_str_p(str, "[]"));
|
||||
const char *sp;
|
||||
b = rbtree_uint_parse_str(tree2, string_get_cstr(str), &sp);
|
||||
assert(b);
|
||||
assert(*sp == 0);
|
||||
assert(rbtree_uint_equal_p(tree1, tree2));
|
||||
|
||||
// Fill in data
|
||||
for(unsigned int i = 0 ; i < 10; i++)
|
||||
@@ -165,6 +252,10 @@ static void test_io(void)
|
||||
|
||||
rbtree_uint_get_str(str, tree1, false);
|
||||
assert(string_equal_str_p(str, "[0,1,2,3,4,5,6,7,8,9]"));
|
||||
b = rbtree_uint_parse_str(tree2, string_get_cstr(str), &sp);
|
||||
assert(b);
|
||||
assert(*sp == 0);
|
||||
assert(rbtree_uint_equal_p(tree1, tree2));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -172,6 +263,7 @@ static void test_io(void)
|
||||
int main(void)
|
||||
{
|
||||
test_uint();
|
||||
test_uint_permut();
|
||||
test_float();
|
||||
test_io();
|
||||
exit(0);
|
||||
|
||||
+55
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -20,16 +20,18 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <gmp.h>
|
||||
#include <assert.h>
|
||||
#include "test-obj.h"
|
||||
|
||||
#include "m-shared.h"
|
||||
|
||||
#include "coverage.h"
|
||||
START_COVERAGE
|
||||
SHARED_PTR_DEF(shared_int, int)
|
||||
SHARED_RESOURCE_DEF(shared_ressource, testobj_t, TESTOBJ_OPLIST)
|
||||
END_COVERAGE
|
||||
SHARED_PTR_DEF(shared_mpz, mpz_t, M_CLASSIC_OPLIST(mpz))
|
||||
SHARED_PTR_DEF(shared_mpz, testobj_t, TESTOBJ_OPLIST)
|
||||
SHARED_PTR_RELAXED_DEF(shared_relaxed_int, int)
|
||||
|
||||
static int f(const shared_int_t p)
|
||||
{
|
||||
@@ -128,9 +130,59 @@ static void test2(void)
|
||||
shared_int_clear(p1);
|
||||
}
|
||||
|
||||
#define MAX_RESSOURCE 10
|
||||
|
||||
static void test_ressource(int n)
|
||||
{
|
||||
shared_ressource_t ressource;
|
||||
shared_ressource_it_t it[MAX_RESSOURCE];
|
||||
shared_ressource_it_t copy[MAX_RESSOURCE];
|
||||
shared_ressource_it_t onemore;
|
||||
|
||||
shared_ressource_init (ressource, n);
|
||||
for(int i = 0; i < n ; i++) {
|
||||
shared_ressource_it (it[i], ressource);
|
||||
assert (!shared_ressource_end_p(it[i]));
|
||||
testobj_t *z = shared_ressource_ref(it[i]);
|
||||
assert (z != NULL);
|
||||
testobj_set_ui(*z, i);
|
||||
}
|
||||
shared_ressource_it (onemore, ressource);
|
||||
assert (shared_ressource_end_p(onemore));
|
||||
for(int i = 0; i < n ; i++) {
|
||||
shared_ressource_it_set (copy[i], it[i]);
|
||||
assert (!shared_ressource_end_p(copy[i]));
|
||||
const testobj_t *z = shared_ressource_cref(copy[i]);
|
||||
assert (z != NULL);
|
||||
assert (testobj_cmp_ui(*z, i) == 0);
|
||||
}
|
||||
shared_ressource_it (onemore, ressource);
|
||||
assert (shared_ressource_end_p(onemore));
|
||||
for(int i = 0; i < n ; i++) {
|
||||
shared_ressource_end (it[i], ressource);
|
||||
}
|
||||
shared_ressource_it (onemore, ressource);
|
||||
assert (shared_ressource_end_p(onemore));
|
||||
for(int i = n-1; i >= 0 ; i--) {
|
||||
const testobj_t *z = shared_ressource_cref(copy[i]);
|
||||
assert (z != NULL);
|
||||
shared_ressource_end (copy[i], ressource);
|
||||
assert (shared_ressource_end_p(copy[i]));
|
||||
shared_ressource_it (onemore, ressource);
|
||||
assert (!shared_ressource_end_p(onemore));
|
||||
shared_ressource_end (onemore, ressource);
|
||||
shared_ressource_end (copy[i], ressource);
|
||||
}
|
||||
|
||||
shared_ressource_clear(ressource);
|
||||
}
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test1();
|
||||
test2();
|
||||
for(int i = 1; i < MAX_RESSOURCE; i++)
|
||||
test_ressource(i);
|
||||
exit(0);
|
||||
}
|
||||
+213
-22
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -20,17 +20,39 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <gmp.h> // For testing purpose only.
|
||||
#include "test-obj.h"
|
||||
|
||||
#include "m-snapshot.h"
|
||||
#include "m-mutex.h"
|
||||
|
||||
#include "coverage.h"
|
||||
|
||||
// Long Structure with a simple CRC to check for proper passing
|
||||
typedef struct {
|
||||
unsigned int n;
|
||||
unsigned long long spare1;
|
||||
unsigned int c;
|
||||
unsigned long long spare2;
|
||||
unsigned int p;
|
||||
} data_t;
|
||||
|
||||
static void data_crc(data_t *p) { p->p = -p->n; p->c = ~p->n; }
|
||||
static void data_init(data_t *p) { p->n = 0; p->spare1 = 0; p->spare2 = 0; data_crc(p); }
|
||||
static void data_valid_p(const data_t *p) { assert (p->n == -p->p && p->n == ~p->c); }
|
||||
static void data_clear(data_t *p) { (void) p;}
|
||||
static void data_set(data_t *p, data_t o) { p->n = o.n; data_crc(p); }
|
||||
|
||||
#define DATA_OPLIST \
|
||||
(INIT(data_init M_IPTR), INIT_SET(data_set M_IPTR), SET(data_set M_IPTR), CLEAR(data_clear M_IPTR))
|
||||
|
||||
START_COVERAGE
|
||||
SNAPSHOT_DEF(snapshot_uint, unsigned int)
|
||||
SNAPSHOT_DEF(snapshot_mpz, mpz_t,
|
||||
(INIT(mpz_init), INIT_SET(mpz_init_set), SET(mpz_set), CLEAR(mpz_clear)))
|
||||
SNAPSHOT_SPSC_DEF(snapshot_uint, unsigned int)
|
||||
SNAPSHOT_SPSC_DEF(snapshot_mpz, testobj_t, TESTOBJ_OPLIST)
|
||||
SNAPSHOT_SPMC_DEF(snapshot_mrsw_data, data_t, DATA_OPLIST)
|
||||
SNAPSHOT_MPMC_DEF(snapshot_mrmw_data, data_t, DATA_OPLIST)
|
||||
END_COVERAGE
|
||||
SNAPSHOT_SPSC_DEF(snapshot_data, data_t, DATA_OPLIST )
|
||||
|
||||
|
||||
static void test_uint(void)
|
||||
{
|
||||
@@ -75,21 +97,6 @@ static void test_uint(void)
|
||||
assert (*snapshot_uint_read(t) == 3);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
unsigned int n;
|
||||
unsigned int c;
|
||||
unsigned int p;
|
||||
} data_t;
|
||||
|
||||
static void data_crc(data_t *p) { p->p = -p->n; p->c = ~p->n; }
|
||||
static void data_init(data_t *p) { p->n = 0; data_crc(p); }
|
||||
static void data_clear(data_t *p) { (void) p;}
|
||||
static void data_set(data_t *p, data_t o) { p->p = o.p; data_crc(p); }
|
||||
|
||||
SNAPSHOT_DEF(snapshot_data, data_t,
|
||||
(INIT(data_init M_IPTR), INIT_SET(data_set M_IPTR), SET(data_set M_IPTR), CLEAR(data_clear M_IPTR))
|
||||
)
|
||||
|
||||
snapshot_data_t g_buff;
|
||||
|
||||
static void conso(void *arg)
|
||||
@@ -97,8 +104,7 @@ static void conso(void *arg)
|
||||
assert (arg == NULL);
|
||||
while (true) {
|
||||
const data_t *p = snapshot_data_read(g_buff);
|
||||
assert (p->n == -p->p);
|
||||
assert (p->n == ~p->c);
|
||||
data_valid_p(p);
|
||||
if (p->n == 0)
|
||||
return;
|
||||
}
|
||||
@@ -140,10 +146,195 @@ static void test_global(void)
|
||||
snapshot_data_clear(g_buff);
|
||||
}
|
||||
|
||||
static void test_mrsw_int1(void)
|
||||
{
|
||||
snapshot_mrsw_int_t idx;
|
||||
snapshot_mrsw_int_init(idx, 1);
|
||||
|
||||
assert (snapshot_mrsw_int_get_write_idx(idx) == 1);
|
||||
|
||||
assert (snapshot_mrsw_int_write(idx) == 0);
|
||||
assert (snapshot_mrsw_int_write(idx) == 1);
|
||||
assert (snapshot_mrsw_int_write(idx) == 0);
|
||||
assert (snapshot_mrsw_int_write(idx) == 1);
|
||||
|
||||
assert (snapshot_mrsw_int_read_start(idx) == 0);
|
||||
snapshot_mrsw_int_read_end(idx, 0);
|
||||
assert (snapshot_mrsw_int_read_start(idx) == 0);
|
||||
snapshot_mrsw_int_read_end(idx, 0);
|
||||
|
||||
assert (snapshot_mrsw_int_write(idx) == 0);
|
||||
|
||||
assert (snapshot_mrsw_int_read_start(idx) == 1);
|
||||
assert (snapshot_mrsw_int_write(idx) == 2);
|
||||
assert (snapshot_mrsw_int_write(idx) == 0);
|
||||
assert (snapshot_mrsw_int_write(idx) == 2);
|
||||
snapshot_mrsw_int_read_end(idx, 1);
|
||||
assert (snapshot_mrsw_int_read_start(idx) == 0);
|
||||
assert (snapshot_mrsw_int_write(idx) == 1);
|
||||
|
||||
snapshot_mrsw_int_clear(idx);
|
||||
}
|
||||
|
||||
static void test_mrsw_int2(void)
|
||||
{
|
||||
snapshot_mrsw_int_t idx;
|
||||
snapshot_mrsw_int_init(idx, 2);
|
||||
|
||||
assert (snapshot_mrsw_int_get_write_idx(idx) == 1);
|
||||
|
||||
assert (snapshot_mrsw_int_write(idx) == 0);
|
||||
assert (snapshot_mrsw_int_write(idx) == 1);
|
||||
assert (snapshot_mrsw_int_write(idx) == 0);
|
||||
assert (snapshot_mrsw_int_write(idx) == 1);
|
||||
|
||||
assert (snapshot_mrsw_int_read_start(idx) == 0);
|
||||
snapshot_mrsw_int_read_end(idx, 0);
|
||||
assert (snapshot_mrsw_int_read_start(idx) == 0);
|
||||
snapshot_mrsw_int_read_end(idx, 0);
|
||||
|
||||
assert (snapshot_mrsw_int_write(idx) == 0);
|
||||
|
||||
assert (snapshot_mrsw_int_read_start(idx) == 1);
|
||||
assert (snapshot_mrsw_int_write(idx) == 2);
|
||||
assert (snapshot_mrsw_int_write(idx) == 0);
|
||||
assert (snapshot_mrsw_int_write(idx) == 2);
|
||||
assert (snapshot_mrsw_int_read_start(idx) == 0);
|
||||
assert (snapshot_mrsw_int_write(idx) == 3);
|
||||
snapshot_mrsw_int_read_end(idx, 1);
|
||||
assert (snapshot_mrsw_int_read_start(idx) == 2);
|
||||
assert (snapshot_mrsw_int_write(idx) == 1);
|
||||
snapshot_mrsw_int_read_end(idx, 0);
|
||||
|
||||
snapshot_mrsw_int_clear(idx);
|
||||
}
|
||||
|
||||
snapshot_mrsw_data_t global_mrsw;
|
||||
|
||||
static void conso2(void *arg)
|
||||
{
|
||||
assert (arg == NULL);
|
||||
while (true) {
|
||||
const data_t *p = snapshot_mrsw_data_read_start(global_mrsw);
|
||||
assert (p != NULL);
|
||||
data_valid_p(p);
|
||||
if (p->n == 0)
|
||||
return;
|
||||
snapshot_mrsw_data_read_end(global_mrsw, p);
|
||||
}
|
||||
}
|
||||
|
||||
static void prod2(void *arg)
|
||||
{
|
||||
assert (arg == NULL);
|
||||
data_t *p = snapshot_mrsw_data_get_write_buffer(global_mrsw);
|
||||
for(unsigned int i = 1; i < 200000;i++) {
|
||||
assert (p != NULL);
|
||||
p->n = i * i;
|
||||
data_crc(p);
|
||||
p = snapshot_mrsw_data_write(global_mrsw);
|
||||
}
|
||||
p->n = 0;
|
||||
data_crc(p);
|
||||
p = snapshot_mrsw_data_write(global_mrsw);
|
||||
assert (p != NULL);
|
||||
}
|
||||
|
||||
#define MAX_WRITER 8
|
||||
#define MAX_READER 8
|
||||
|
||||
static void test_mrsw_global(int reader)
|
||||
{
|
||||
m_thread_t idx_w;
|
||||
m_thread_t idx[MAX_READER];
|
||||
|
||||
snapshot_mrsw_data_init(global_mrsw, reader);
|
||||
data_t *p = snapshot_mrsw_data_write(global_mrsw);
|
||||
assert (p != NULL);
|
||||
p->n = 42;
|
||||
data_crc(p);
|
||||
p = snapshot_mrsw_data_write(global_mrsw);
|
||||
assert (p != NULL);
|
||||
|
||||
for(int i = 0 ; i < reader; i++)
|
||||
m_thread_create (idx[i], conso2, NULL);
|
||||
m_thread_create (idx_w, prod2, NULL);
|
||||
|
||||
m_thread_join(idx_w);
|
||||
for(int i = 0 ; i < reader; i++)
|
||||
m_thread_join(idx[i]);
|
||||
|
||||
snapshot_mrsw_data_clear(global_mrsw);
|
||||
}
|
||||
|
||||
snapshot_mrmw_data_t global_mrmw;
|
||||
|
||||
static void conso3(void *arg)
|
||||
{
|
||||
assert (arg == NULL);
|
||||
while (true) {
|
||||
const data_t *p = snapshot_mrmw_data_read_start(global_mrmw);
|
||||
assert (p != NULL);
|
||||
data_valid_p(p);
|
||||
if (p->n == 0)
|
||||
return;
|
||||
snapshot_mrmw_data_read_end(global_mrmw, p);
|
||||
}
|
||||
}
|
||||
|
||||
static void prod3(void *arg)
|
||||
{
|
||||
assert (arg == NULL);
|
||||
for(unsigned int i = 1; i < 20000000;i++) {
|
||||
data_t *p = snapshot_mrmw_data_write_start(global_mrmw);
|
||||
assert (p != NULL);
|
||||
p->n = i * i;
|
||||
data_crc(p);
|
||||
snapshot_mrmw_data_write_end(global_mrmw, p);
|
||||
}
|
||||
data_t *p = snapshot_mrmw_data_write_start(global_mrmw);
|
||||
p->n = 0;
|
||||
data_crc(p);
|
||||
snapshot_mrmw_data_write_end(global_mrmw, p);
|
||||
}
|
||||
|
||||
static void test_mrmw_global(int reader, int writer)
|
||||
{
|
||||
m_thread_t idx_w[MAX_WRITER];
|
||||
m_thread_t idx[MAX_READER];
|
||||
|
||||
snapshot_mrmw_data_init(global_mrmw, reader, writer);
|
||||
data_t *p = snapshot_mrmw_data_write_start(global_mrmw);
|
||||
assert (p != NULL);
|
||||
p->n = 42;
|
||||
data_crc(p);
|
||||
snapshot_mrmw_data_write_end(global_mrmw, p);
|
||||
|
||||
for(int i = 0 ; i < reader; i++)
|
||||
m_thread_create (idx[i], conso3, NULL);
|
||||
for(int i = 0 ; i < writer; i++)
|
||||
m_thread_create (idx_w[i], prod3, NULL);
|
||||
|
||||
for(int i = 0 ; i < writer; i++)
|
||||
m_thread_join(idx_w[i]);
|
||||
for(int i = 0 ; i < reader; i++)
|
||||
m_thread_join(idx[i]);
|
||||
|
||||
snapshot_mrmw_data_clear(global_mrmw);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_uint();
|
||||
test_global();
|
||||
test_mrsw_int1();
|
||||
test_mrsw_int2();
|
||||
test_mrsw_global(1);
|
||||
test_mrsw_global(2);
|
||||
test_mrsw_global(4);
|
||||
test_mrsw_global(MAX_READER);
|
||||
test_mrmw_global(1, 1);
|
||||
test_mrmw_global(MAX_READER/4, MAX_WRITER/4);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
+107
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -24,11 +24,20 @@
|
||||
#define STRING_WITHIN_TEST
|
||||
#include "m-string.h"
|
||||
|
||||
BOUNDED_STRING_DEF(string16, 16)
|
||||
|
||||
static void test1(void)
|
||||
{
|
||||
M_LET(s1, s2, string_t) {
|
||||
string_set_str(s1, "Success!");
|
||||
string_set(s2, s1);
|
||||
assert(string_equal_p(s1, s2));
|
||||
}
|
||||
M_LET((s1,"OK"),string_t) {
|
||||
assert(string_equal_str_p(s1, "OK"));
|
||||
}
|
||||
M_LET((s1,"OK S=%d", 16),string_t) {
|
||||
assert(string_equal_str_p(s1, "OK S=16"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +120,7 @@ static void test_utf8_it(void)
|
||||
string_clear(s);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
static void test0(void)
|
||||
{
|
||||
string_t s1;
|
||||
string_t s2;
|
||||
@@ -147,6 +156,9 @@ int main(void)
|
||||
assert (strcmp(string_get_cstr(s1), "Hello beautiful world!") == 0);
|
||||
|
||||
string_clean(s1);
|
||||
string_set_n (s2, s1, 0, 5);
|
||||
assert(string_empty_p(s2));
|
||||
|
||||
string_set_str(s1, "Hello");
|
||||
string_cat_str(s1, ", world!");
|
||||
assert (string_size(s1) == 13);
|
||||
@@ -218,6 +230,7 @@ int main(void)
|
||||
|
||||
string_clear (s1);
|
||||
string_init (s1);
|
||||
string_strim(s1);
|
||||
|
||||
assert (string_cmp_str (s1, "") == 0);
|
||||
assert (string_cmpi (s1, s1) == 0);
|
||||
@@ -271,6 +284,10 @@ int main(void)
|
||||
string_strim(s1);
|
||||
assert (string_cmp_str (s1, "") == 0);
|
||||
|
||||
string_set_str(s1, " \r\n\t++\t YES ++ \n\r\t");
|
||||
string_strim(s1, "\r\n\t +");
|
||||
assert (string_cmp_str (s1, "YES") == 0);
|
||||
|
||||
string_set_str(s1, "Hello world");
|
||||
string_get_str(s2, s1, false);
|
||||
assert (string_cmp_str (s2, "\"Hello world\"") == 0);
|
||||
@@ -278,6 +295,16 @@ int main(void)
|
||||
string_get_str(s2, s1, true);
|
||||
assert (string_cmp_str (s2, "\"Hello world\"\"Hello \\\"world\\\"\"") == 0);
|
||||
|
||||
const char *sp = string_get_cstr(s2);
|
||||
b = string_parse_str(s1, sp, &sp);
|
||||
assert (b == true);
|
||||
assert (strcmp(sp, "\"Hello \\\"world\\\"\"") == 0);
|
||||
assert (string_cmp_str(s1, "Hello world") == 0);
|
||||
b = string_parse_str(s1, sp, &sp);
|
||||
assert (b == true);
|
||||
assert (strcmp(sp, "") == 0);
|
||||
assert (string_cmp_str(s1, "Hello \"world\"") == 0);
|
||||
|
||||
string_set_str(s1, "Hello \"world\"");
|
||||
FILE *f = fopen ("a-mstring.dat", "wt");
|
||||
assert (f != NULL);
|
||||
@@ -290,6 +317,21 @@ int main(void)
|
||||
fclose(f);
|
||||
assert (string_equal_p(s1, s2));
|
||||
|
||||
string_set_str(s1, "\tHell\\o\n\"World\"\r\001");
|
||||
string_get_str(s2, s1, false);
|
||||
assert (string_cmp_str(s2, "\"\\tHell\\\\o\\n\\\"World\\\"\\r\\001\"") == 0);
|
||||
|
||||
f = fopen ("a-mstring.dat", "wt");
|
||||
assert (f != NULL);
|
||||
string_out_str (f, s1);
|
||||
fclose (f);
|
||||
f = fopen("a-mstring.dat", "rt");
|
||||
assert (f != NULL);
|
||||
b = string_in_str(s2, f);
|
||||
assert(b);
|
||||
fclose(f);
|
||||
assert (string_equal_p(s1, s2));
|
||||
|
||||
string_set_str(s1, "AZERTY");
|
||||
string_set_str(s2, "QWERTY");
|
||||
string_swap (s1, s2);
|
||||
@@ -374,7 +416,7 @@ int main(void)
|
||||
string_init(s1);
|
||||
f = fopen("a-mstring.dat", "wt");
|
||||
assert(f != NULL);
|
||||
fprintf(f, "hello world.\n\tHowwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww do you do?");
|
||||
fprintf(f, "hello world...\n\tHowwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww do you do?");
|
||||
fclose(f);
|
||||
f = fopen("a-mstring.dat", "rt");
|
||||
b = string_fget_word(s1, " \t.\n?", f);
|
||||
@@ -416,10 +458,72 @@ int main(void)
|
||||
h = string_search_pbrk(s1, "oO");
|
||||
assert(h==4);
|
||||
|
||||
string_clean(s1);
|
||||
string_reserve(s1, 0);
|
||||
string_cat_printf(s1, "%d little %s.", 42, "suns");
|
||||
string_set_str(s2, "42 little suns.");
|
||||
assert(string_equal_p(s1, s2) == true);
|
||||
|
||||
string_clear (s1);
|
||||
string_clear (s2);
|
||||
}
|
||||
|
||||
static void test_bounded1(void)
|
||||
{
|
||||
string16_t s, d;
|
||||
string16_init(s);
|
||||
assert (string16_empty_p(s));
|
||||
assert (string16_size(s) == 0);
|
||||
assert (string16_capacity(s) == 17);
|
||||
string16_set_str(s, "Hello");
|
||||
assert (!string16_empty_p(s));
|
||||
assert (string16_size(s) == 5);
|
||||
assert (string16_equal_str_p(s, "Hello"));
|
||||
assert (string16_cmp_str(s, "Hello") == 0);
|
||||
assert (string16_get_char(s, 1) == 'e');
|
||||
assert (strcmp(string16_get_cstr(s), "Hello") == 0);
|
||||
string16_clean(s);
|
||||
assert (string16_empty_p(s));
|
||||
assert (string16_size(s) == 0);
|
||||
string16_set_str(s, "Hello, world! How do you do?");
|
||||
assert (!string16_empty_p(s));
|
||||
assert (string16_size(s) == 16);
|
||||
assert (string16_equal_str_p(s, "Hello, world! Ho"));
|
||||
string16_set_strn(s, "Hello, world! How do you do?", 17);
|
||||
assert (!string16_empty_p(s));
|
||||
assert (string16_size(s) == 16);
|
||||
assert (string16_equal_str_p(s, "Hello, world! Ho"));
|
||||
string16_set_strn(s, "Hello, world! How do you do?", 15);
|
||||
assert (!string16_empty_p(s));
|
||||
assert (string16_size(s) == 15);
|
||||
assert (string16_equal_str_p(s, "Hello, world! H"));
|
||||
string16_cat_str(s, "ow do you do?");
|
||||
assert (string16_size(s) == 16);
|
||||
assert (!string16_equal_str_p(s, "Hello, world! H"));
|
||||
assert (string16_equal_str_p(s, "Hello, world! Ho"));
|
||||
string16_printf(s, "HeH:%d", 16);
|
||||
assert (string16_size(s) == 6);
|
||||
assert (string16_equal_str_p(s, "HeH:16"));
|
||||
string16_cat_printf(s, " GeG:%d/%d FRE:%d", 17, 42, 13);
|
||||
assert (string16_size(s) == 16);
|
||||
assert (string16_equal_str_p(s, "HeH:16 GeG:17/42"));
|
||||
assert (string16_hash(s) != 0);
|
||||
|
||||
string16_set_strn(s, "Hello, world! How do you do?", 15);
|
||||
string16_init_set(d, s);
|
||||
string16_clean(s);
|
||||
assert (string16_equal_str_p(d, "Hello, world! H"));
|
||||
|
||||
string16_clear(d);
|
||||
string16_clear(s);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test0();
|
||||
test1();
|
||||
test_utf8_basic();
|
||||
test_utf8_it();
|
||||
test_bounded1();
|
||||
exit(0);
|
||||
}
|
||||
+84
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Patrick Pelissier
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -21,35 +21,57 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <gmp.h>
|
||||
|
||||
#include "test-obj.h"
|
||||
#include "m-string.h"
|
||||
|
||||
#include "m-tuple.h"
|
||||
|
||||
#include "coverage.h"
|
||||
START_COVERAGE
|
||||
TUPLE_DEF2(pair,
|
||||
(key, string_t, (INIT(string_init), INIT_SET(string_init_set), SET(string_set), CLEAR(string_clear))),
|
||||
(value, mpz_t, (INIT(mpz_init), INIT_SET(mpz_init_set), SET(mpz_set), CLEAR(mpz_clear) )))
|
||||
(value, testobj_t, TESTOBJ_OPLIST) )
|
||||
END_COVERAGE
|
||||
|
||||
// Minimum definition
|
||||
TUPLE_DEF2(pair_min,
|
||||
(key1, string_t, (INIT_SET(string_init_set), SET(string_set), CLEAR(string_clear))),
|
||||
(key2, string_t, (INIT_SET(string_init_set), SET(string_set), CLEAR(string_clear))))
|
||||
|
||||
// No cmp defined
|
||||
TUPLE_DEF2(triple,
|
||||
(key, string_t, (INIT(string_init), INIT_SET(string_init_set), SET(string_set), CLEAR(string_clear), CMP(string_cmp))),
|
||||
(value, mpz_t, (INIT(mpz_init), INIT_SET(mpz_init_set), SET(mpz_set), CLEAR(mpz_clear) )),
|
||||
(value, testobj_t, TESTOBJ_OPLIST ),
|
||||
(data, const char *, ()))
|
||||
|
||||
// cmp shall be defined
|
||||
TUPLE_DEF2(pair3,
|
||||
(key, string_t, (INIT(string_init), INIT_SET(string_init_set), SET(string_set), CLEAR(string_clear), CMP(string_cmp), HASH(string_hash), EQUAL(string_equal_p))),
|
||||
(value, mpz_t, (INIT(mpz_init), INIT_SET(mpz_init_set), SET(mpz_set), CLEAR(mpz_clear), CMP(mpz_cmp) )))
|
||||
(value, testobj_t, M_OPEXTEND( TESTOBJ_OPLIST, CMP(testobj_cmp)) ) )
|
||||
|
||||
TUPLE_DEF2(pair_str,
|
||||
(vala, string_t, STRING_OPLIST),
|
||||
(valb, string_t, STRING_OPLIST))
|
||||
#define M_OPL_pair_str_t() TUPLE_OPLIST(pair_str, STRING_OPLIST, STRING_OPLIST)
|
||||
|
||||
TUPLE_DEF2(single_str, (vala, string_t, STRING_OPLIST))
|
||||
|
||||
TUPLE_DEF2(pair2_str,
|
||||
(str, string_t),
|
||||
(value, unsigned long))
|
||||
|
||||
// Recursive tuple definition.
|
||||
TUPLE_DEF2(rtuple,
|
||||
(ituple, pair_str_t),
|
||||
(name, string_t))
|
||||
#define M_OPL_rtuple_t() TUPLE_OPLIST(rtuple, M_OPL_pair_str_t(), STRING_OPLIST)
|
||||
|
||||
TUPLE_DEF2(rtuple2, (name, string_t), (value, int))
|
||||
#define M_OPL_rtuple2_t() TUPLE_OPLIST(rtuple2, STRING_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
TUPLE_DEF2(rtuple3, (name, string_t), (ituple2, rtuple2_t), (ituple, rtuple_t) )
|
||||
|
||||
|
||||
static void check_swap(void)
|
||||
{
|
||||
pair_str_t p1, p2;
|
||||
@@ -68,6 +90,24 @@ static void check_swap(void)
|
||||
pair_str_clear(p2);
|
||||
}
|
||||
|
||||
static void check_clean(void)
|
||||
{
|
||||
single_str_t p1;
|
||||
single_str_init (p1);
|
||||
|
||||
string_set_str(p1->vala, "Hello");
|
||||
single_str_clean(p1);
|
||||
assert(string_equal_str_p (p1->vala, ""));
|
||||
|
||||
single_str_clear(p1);
|
||||
|
||||
M_LET(r, rtuple_t) {
|
||||
string_set_str(r->name, "Hello");
|
||||
rtuple_clean(r);
|
||||
assert(string_equal_str_p (r->name, ""));
|
||||
}
|
||||
}
|
||||
|
||||
static void check_io(void)
|
||||
{
|
||||
M_LET(s, STRING_OPLIST)
|
||||
@@ -77,6 +117,11 @@ static void check_io(void)
|
||||
string_set_str(pair->valb, "World");
|
||||
pair_str_get_str(s, pair, false);
|
||||
assert(string_cmp_str(s, "(\"Hello\",\"World\")") == 0);
|
||||
const char *end;
|
||||
bool b = pair_str_parse_str(pair2, string_get_cstr(s), &end);
|
||||
assert (b);
|
||||
assert (pair_str_equal_p(pair, pair2) == true);
|
||||
assert(*end == 0);
|
||||
|
||||
FILE *f = fopen("a-mtuple.dat", "wt");
|
||||
if (!f) abort();
|
||||
@@ -85,56 +130,78 @@ static void check_io(void)
|
||||
|
||||
f = fopen("a-mtuple.dat", "rt");
|
||||
if (!f) abort();
|
||||
bool b = pair_str_in_str(pair2, f);
|
||||
b = pair_str_in_str(pair2, f);
|
||||
assert (b);
|
||||
fclose(f);
|
||||
assert (pair_str_equal_p(pair, pair2) == true);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
static void test1(void)
|
||||
{
|
||||
pair_t p1, p2;
|
||||
|
||||
pair_init (p1);
|
||||
string_set_str(p1->key, "HELLO");
|
||||
mpz_set_str(p1->value, "1742", 10);
|
||||
testobj_set_ui(p1->value, 1742);
|
||||
pair_init_set (p2, p1);
|
||||
assert(mpz_cmp_ui(p2->value, 1742) == 0);
|
||||
assert(testobj_cmp_ui(p2->value, 1742) == 0);
|
||||
assert(string_equal_str_p(p2->key, "HELLO"));
|
||||
string_set_str(p2->key, "HELLO WORLD");
|
||||
mpz_set_str(p2->value, "174217", 10);
|
||||
testobj_set_ui(p2->value, 174217);
|
||||
pair_set(p1, p1);
|
||||
pair_set(p1, p2);
|
||||
assert(mpz_cmp_ui(p1->value, 174217) == 0);
|
||||
assert(testobj_cmp_ui(p1->value, 174217) == 0);
|
||||
assert(string_equal_str_p(p1->key, "HELLO WORLD"));
|
||||
pair_clear(p1);
|
||||
pair_clear(p2);
|
||||
|
||||
string_t s;
|
||||
mpz_t z;
|
||||
testobj_t z;
|
||||
pair3_t p3, p4;
|
||||
string_init_set_str(s, "HELLO");
|
||||
mpz_init_set_ui (z, 1442);
|
||||
testobj_init_set_ui (z, 1442);
|
||||
pair3_init_set2(p3, s, z);
|
||||
pair3_init_set2(p4, s, z);
|
||||
int i = pair3_cmp (p3, p4);
|
||||
assert (i == 0);
|
||||
mpz_set_ui (z, 1443);
|
||||
pair3_set2(p4, s, z);
|
||||
i = pair3_cmp (p3, p4);
|
||||
assert (i == 0);
|
||||
testobj_set_ui (z, 1443);
|
||||
pair3_set_value(p4, z);
|
||||
i = pair3_cmp (p3, p4);
|
||||
assert (i < 0);
|
||||
mpz_set_ui (z, 1442);
|
||||
testobj_set_ui (z, 1442);
|
||||
string_set_str(s, "HELLN");
|
||||
pair3_set_key(p4, s);
|
||||
i = pair3_cmp (p3, p4);
|
||||
assert (i > 0);
|
||||
|
||||
// p3 = HELLO, 1442
|
||||
// p4 = HELLN, 1443
|
||||
i = pair3_cmp_order(p3, p3, TUPLE_ORDER(pair3, ASC(value), DSC(key)));
|
||||
assert (i == 0);
|
||||
i = pair3_cmp_order(p3, p4, TUPLE_ORDER(pair3, ASC(key), ASC(value)));
|
||||
assert (i > 0);
|
||||
i = pair3_cmp_order(p3, p4, TUPLE_ORDER(pair3, ASC(value), ASC(key)));
|
||||
assert (i < 0);
|
||||
i = pair3_cmp_order(p3, p4, TUPLE_ORDER(pair3, DSC(key), ASC(value)));
|
||||
assert (i < 0);
|
||||
i = pair3_cmp_order(p3, p4, TUPLE_ORDER(pair3, DSC(value), ASC(key)));
|
||||
assert (i > 0);
|
||||
|
||||
pair3_clear(p3);
|
||||
pair3_clear(p4);
|
||||
string_clear(s);
|
||||
mpz_clear(z);
|
||||
testobj_clear(z);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test1();
|
||||
check_io();
|
||||
check_swap();
|
||||
check_clean();
|
||||
exit(0);
|
||||
}
|
||||
Loaded 100 of 109 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user