mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-04 21:02:27 +08:00
Compare commits
1022 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d4fc981b6 | ||
|
|
4c21fe700c | ||
|
|
b7370e8ef8 | ||
|
|
38920e3be6 | ||
|
|
a630a7cda9 | ||
|
|
66dfd9c4fd | ||
|
|
be62aaac9b | ||
|
|
e0c9ccbce3 | ||
|
|
d2d1e5a055 | ||
|
|
bbc58c7e46 | ||
|
|
ac2ecceaec | ||
|
|
a06bb8ee6a | ||
|
|
27b5702fd3 | ||
|
|
b2c3fa0ab8 | ||
|
|
4afc838937 | ||
|
|
3482f80d1c | ||
|
|
565f2893b9 | ||
|
|
1b1b10d0ee | ||
|
|
c5c3888ffc | ||
|
|
11214986cc | ||
|
|
0bc7b83e59 | ||
|
|
6225418074 | ||
|
|
ddd265de78 | ||
|
|
80e48f06ca | ||
|
|
0b976e9a4c | ||
|
|
96320069f4 | ||
|
|
c5297ec329 | ||
|
|
aa4b61ff85 | ||
|
|
7dbd7002a3 | ||
|
|
a995db445a | ||
|
|
6e5b67bee7 | ||
|
|
52b8ea8166 | ||
|
|
6fe8a9efe7 | ||
|
|
dcef06e9da | ||
|
|
8f410bf9cb | ||
|
|
9cd6eb4ddf | ||
|
|
bf581162ee | ||
|
|
bd984fa378 | ||
|
|
d7f23e2dee | ||
|
|
3802c715c3 | ||
|
|
4b12173f24 | ||
|
|
31c618c187 | ||
|
|
6497b321b0 | ||
|
|
bda78201f5 | ||
|
|
0753ea2801 | ||
|
|
e25b53712b | ||
|
|
56c9327a86 | ||
|
|
11eb559998 | ||
|
|
61a612a06a | ||
|
|
2351ab4714 | ||
|
|
88eec858b4 | ||
|
|
14c37b5a5f | ||
|
|
ad8b1d424c | ||
|
|
a9f01d8a69 | ||
|
|
781b89d51a | ||
|
|
073c77e751 | ||
|
|
91a0d3077d | ||
|
|
17b34f8f19 | ||
|
|
8ff37f0eff | ||
|
|
f445ecbaf8 | ||
|
|
b698fec50f | ||
|
|
b38bb66b34 | ||
|
|
bdfdeaf496 | ||
|
|
172c44c07a | ||
|
|
534263eb08 | ||
|
|
a25b1ac7e3 | ||
|
|
d84f9777ea | ||
|
|
f9caaf8063 | ||
|
|
f198711d1c | ||
|
|
19378ca4d1 | ||
|
|
71c7733eb0 | ||
|
|
20786c360b | ||
|
|
51fafa110e | ||
|
|
07fc453b74 | ||
|
|
b309044981 | ||
|
|
541e6d4ea3 | ||
|
|
4037b96cc4 | ||
|
|
8e484c4a6f | ||
|
|
dd339563bc | ||
|
|
9567dcc57f | ||
|
|
de104ebeb6 | ||
|
|
eebd95395f | ||
|
|
56265647ff | ||
|
|
756da3719d | ||
|
|
f3008ac7c4 | ||
|
|
c6583d3b83 | ||
|
|
a3d7bac72e | ||
|
|
74b4d53514 | ||
|
|
a08afe0d77 | ||
|
|
ee680aa84c | ||
|
|
caa74064f0 | ||
|
|
abc94c0b26 | ||
|
|
86bcdce07b | ||
|
|
789f5d933d | ||
|
|
156fd2ef5d | ||
|
|
ea036b3a81 | ||
|
|
3daca8b331 | ||
|
|
21a952d2be | ||
|
|
03d331dfb6 | ||
|
|
ef79e1305e | ||
|
|
7c2670d019 | ||
|
|
31f64c0a98 | ||
|
|
28b8986cbe | ||
|
|
b4ff33ce80 | ||
|
|
25fa117712 | ||
|
|
0c9f539e3d | ||
|
|
05bd1afaa6 | ||
|
|
51cb665110 | ||
|
|
47c368e8f2 | ||
|
|
3127360b28 | ||
|
|
5b6def5ff0 | ||
|
|
33d99ad7d4 | ||
|
|
8bd31d39c3 | ||
|
|
a418549525 | ||
|
|
1f849efe1c | ||
|
|
8ad374bb21 | ||
|
|
a69d886565 | ||
|
|
cf58542b4a | ||
|
|
51f166d1d9 | ||
|
|
fbeb031b40 | ||
|
|
095cfc0aab | ||
|
|
e66ab154bc | ||
|
|
91bc1a512c | ||
|
|
8753255026 | ||
|
|
9cf1f13fec | ||
|
|
5d78bae4bb | ||
|
|
27777eecc0 | ||
|
|
1a7e0e8792 | ||
|
|
02b7aa8f33 | ||
|
|
f188d3d08f | ||
|
|
0c924b859e | ||
|
|
c5a5a07462 | ||
|
|
1ff5dd6df0 | ||
|
|
b5f86a488c | ||
|
|
1390b7a964 | ||
|
|
c3e28a9fc0 | ||
|
|
e924429d6e | ||
|
|
7079b1f704 | ||
|
|
40cad365c0 | ||
|
|
6386ab908d | ||
|
|
bb563724c7 | ||
|
|
72924d4486 | ||
|
|
231f8b04b4 | ||
|
|
daa932fee3 | ||
|
|
1b0691f1d5 | ||
|
|
0b6a00bd99 | ||
|
|
4ab98664bb | ||
|
|
31eb5f4d1f | ||
|
|
0ae5d17a06 | ||
|
|
4558d7a3c2 | ||
|
|
4715301240 | ||
|
|
1c58ee23f1 | ||
|
|
2a303d5e4b | ||
|
|
326e7881a6 | ||
|
|
31e8b12674 | ||
|
|
a0431d9435 | ||
|
|
0456b65cc7 | ||
|
|
989b4dd791 | ||
|
|
a76b02fbba | ||
|
|
2d7747738a | ||
|
|
fe0cb04137 | ||
|
|
fe0264f628 | ||
|
|
65396cee32 | ||
|
|
544702b460 | ||
|
|
d8936cdcb5 | ||
|
|
d4b97d6b20 | ||
|
|
5caa14c838 | ||
|
|
0d0848ac67 | ||
|
|
d5334f892f | ||
|
|
36ef5b3bd3 | ||
|
|
6f48b00c88 | ||
|
|
7b744c299e | ||
|
|
154ec56780 | ||
|
|
584aabdf62 | ||
|
|
1b754a6264 | ||
|
|
06a558d797 | ||
|
|
423779d3ff | ||
|
|
78c17a9e7b | ||
|
|
529b9317d0 | ||
|
|
dc838f645b | ||
|
|
7f559c4940 | ||
|
|
acb08dfd90 | ||
|
|
ffe46fd06d | ||
|
|
b419e46b9b | ||
|
|
8fbcdfd515 | ||
|
|
f03f48210c | ||
|
|
742944e2f0 | ||
|
|
c0b491ad78 | ||
|
|
e9abae2a92 | ||
|
|
8229de2f10 | ||
|
|
ab364744b6 | ||
|
|
17ff84fa1f | ||
|
|
bf50baa07d | ||
|
|
7d56da8108 | ||
|
|
16c2df711b | ||
|
|
015f8c3f5c | ||
|
|
ba25701d89 | ||
|
|
a0cb4bb266 | ||
|
|
d6ba7497f9 | ||
|
|
7da931e0a0 | ||
|
|
8f49078eb3 | ||
|
|
3050d93703 | ||
|
|
efcfbfb6a1 | ||
|
|
844b7a2c3b | ||
|
|
c8a536eafc | ||
|
|
0fd268face | ||
|
|
8ced7e887f | ||
|
|
957568ed5c | ||
|
|
fffabd0ffa | ||
|
|
e839af3ef9 | ||
|
|
1650e70d04 | ||
|
|
23382b2b76 | ||
|
|
daa3aa3da2 | ||
|
|
be355a23f3 | ||
|
|
95af83b0cb | ||
|
|
1efdbc0973 | ||
|
|
9b829aa695 | ||
|
|
bfa9091c09 | ||
|
|
c11d63c2e2 | ||
|
|
83832daeb1 | ||
|
|
7311f84772 | ||
|
|
a63d78111e | ||
|
|
e2fc2f1bc9 | ||
|
|
23a0135947 | ||
|
|
013b6457bb | ||
|
|
e08a62b0ba | ||
|
|
850800a233 | ||
|
|
a6a8fd88bc | ||
|
|
2f6ee84443 | ||
|
|
b1c6614549 | ||
|
|
a8761eefb0 | ||
|
|
cbf8cfdffa | ||
|
|
286a187942 | ||
|
|
b787e99528 | ||
|
|
e54c9b2850 | ||
|
|
8944109c4c | ||
|
|
10e3732f32 | ||
|
|
a415597c6b | ||
|
|
69b32fd043 | ||
|
|
75ed359084 | ||
|
|
2c71b6375c | ||
|
|
2894bec80c | ||
|
|
09ec5b97a6 | ||
|
|
46ecb117a5 | ||
|
|
388171e739 | ||
|
|
6fbaf2b005 | ||
|
|
53a91cad71 | ||
|
|
a51a182fb2 | ||
|
|
64982f0c89 | ||
|
|
f38f69ce17 | ||
|
|
a33ea3d013 | ||
|
|
e35462fb14 | ||
|
|
af106a4a8e | ||
|
|
ec7232ec40 | ||
|
|
67c1b54b5a | ||
|
|
e149ae2f72 | ||
|
|
b1fcfce188 | ||
|
|
259dbce85e | ||
|
|
78aa679670 | ||
|
|
3beb769f09 | ||
|
|
8af02ff95b | ||
|
|
ba4485d8c0 | ||
|
|
8a1dd40738 | ||
|
|
85e2806531 | ||
|
|
1616d3d1be | ||
|
|
aa8e0d5c12 | ||
|
|
f05477d9cf | ||
|
|
cd91e16b26 | ||
|
|
9fcf046fb3 | ||
|
|
c3f1bc39d7 | ||
|
|
f5784b0f46 | ||
|
|
c86a8a479d | ||
|
|
e2aeb8ec07 | ||
|
|
654ba15aaf | ||
|
|
945c59896b | ||
|
|
219e31d929 | ||
|
|
82cb86b35c | ||
|
|
f93c561f5d | ||
|
|
4888909208 | ||
|
|
33c8875d14 | ||
|
|
99cb1b13a3 | ||
|
|
42ec189995 | ||
|
|
424c291813 | ||
|
|
4311b9ac66 | ||
|
|
581e338889 | ||
|
|
f399425c2a | ||
|
|
dcdb29334d | ||
|
|
4859f3ca23 | ||
|
|
9f2528842e | ||
|
|
6066d6669f | ||
|
|
e3804e9534 | ||
|
|
2f51397d2c | ||
|
|
11217a11c7 | ||
|
|
fa81ee143e | ||
|
|
62891f20f8 | ||
|
|
5e79eaa9dd | ||
|
|
aaf45012e4 | ||
|
|
48c17ea1ce | ||
|
|
e90283c3f9 | ||
|
|
f82c4a305d | ||
|
|
05997603a9 | ||
|
|
6d5ec807f7 | ||
|
|
f73c7e7e86 | ||
|
|
c137428b9e | ||
|
|
9f68620b37 | ||
|
|
2cdbba56a4 | ||
|
|
01a3b139c0 | ||
|
|
fcb3b97b45 | ||
|
|
52ea64bc33 | ||
|
|
14bc08c6d6 | ||
|
|
47bdd6718a | ||
|
|
18d27604e6 | ||
|
|
66bd339e3a | ||
|
|
04abb7a3ea | ||
|
|
975c303a31 | ||
|
|
3b8dd94a5c | ||
|
|
815791c0b6 | ||
|
|
a12a691ee6 | ||
|
|
247cf89947 | ||
|
|
c3fad62d8c | ||
|
|
a8a96be21b | ||
|
|
8e297769b2 | ||
|
|
98cdf1c040 | ||
|
|
d7976e31a4 | ||
|
|
5b11a8b457 | ||
|
|
d4a16534f2 | ||
|
|
ecf0688788 | ||
|
|
90f9cad1ea | ||
|
|
4a298876e9 | ||
|
|
74474cd9ef | ||
|
|
f23f18457e | ||
|
|
18f01ffd75 | ||
|
|
c53d541a6b | ||
|
|
5b9b4c4344 | ||
|
|
6e7300bbbf | ||
|
|
3685aee02b | ||
|
|
046f3e0bf9 | ||
|
|
c01c9d14b4 | ||
|
|
f198191063 | ||
|
|
8bdd46bda4 | ||
|
|
e29b56c3c3 | ||
|
|
c357fc68c8 | ||
|
|
bc25e7a037 | ||
|
|
217350042b | ||
|
|
e56a8a1ef5 | ||
|
|
6453f755a6 | ||
|
|
027abd6ad5 | ||
|
|
91503b1656 | ||
|
|
e2522cd29b | ||
|
|
da69b77892 | ||
|
|
05772d8d7d | ||
|
|
3b497532f3 | ||
|
|
1cd3be508c | ||
|
|
a41d461910 | ||
|
|
cde5946bf0 | ||
|
|
c28803b25e | ||
|
|
f09e521783 | ||
|
|
0aa41f337d | ||
|
|
48814a720a | ||
|
|
04b79b3dfe | ||
|
|
302007ebdf | ||
|
|
965e5fbcda | ||
|
|
70d0adde42 | ||
|
|
0b80074bb7 | ||
|
|
90945a0399 | ||
|
|
47dccd63af | ||
|
|
4ae7e59829 | ||
|
|
8f0c60cade | ||
|
|
3f6aef1432 | ||
|
|
a714e04470 | ||
|
|
7456621153 | ||
|
|
73ac9825e9 | ||
|
|
930bb9c839 | ||
|
|
3d8f1be212 | ||
|
|
13a4ed59fa | ||
|
|
c799d10ce9 | ||
|
|
5ab322ade2 | ||
|
|
d0ffc61842 | ||
|
|
5e66bc6227 | ||
|
|
7261b281ad | ||
|
|
f79693804b | ||
|
|
534c7a0abc | ||
|
|
4eaff47d38 | ||
|
|
3e019522c7 | ||
|
|
0734f220b3 | ||
|
|
2d2c277090 | ||
|
|
ef1e548dfc | ||
|
|
924589d2da | ||
|
|
77f32f4cc6 | ||
|
|
1755dd249b | ||
|
|
7a25688ec1 | ||
|
|
51a6912eb3 | ||
|
|
28d0428b50 | ||
|
|
6a9eb645bb | ||
|
|
3857b342f6 | ||
|
|
71aa91a58d | ||
|
|
081908bce3 | ||
|
|
66efe61834 | ||
|
|
94f7f3e0e5 | ||
|
|
9d2c9806d1 | ||
|
|
e523d4af6e | ||
|
|
f3801bcd8f | ||
|
|
5767aad303 | ||
|
|
f7aaa1ed2a | ||
|
|
ef19a414bc | ||
|
|
65704dea06 | ||
|
|
5eac358bb0 | ||
|
|
8e36ef5cef | ||
|
|
21b0d2ec0e | ||
|
|
32ca975204 | ||
|
|
ec740e442c | ||
|
|
ed98ad53ec | ||
|
|
af7b9d2710 | ||
|
|
f08b368001 | ||
|
|
be8a0558f8 | ||
|
|
46de539e22 | ||
|
|
36fb3abe9e | ||
|
|
fee8d325b7 | ||
|
|
8784be1583 | ||
|
|
84cd68de7f | ||
|
|
a774c060ce | ||
|
|
730f061b95 | ||
|
|
2eb08f3404 | ||
|
|
4c1496b648 | ||
|
|
c2ae784f27 | ||
|
|
e71cecefea | ||
|
|
572e53aa14 | ||
|
|
ca9ecb9c8a | ||
|
|
8bf0786abe | ||
|
|
61cb8395e3 | ||
|
|
dde8a41daf | ||
|
|
d7518e01af | ||
|
|
be9fa7acaa | ||
|
|
2629a731cc | ||
|
|
54c7f90b7f | ||
|
|
770bc88b88 | ||
|
|
4cc1722f81 | ||
|
|
d0260b2841 | ||
|
|
57bd64cae7 | ||
|
|
c383719496 | ||
|
|
4b196a72b1 | ||
|
|
888381a06c | ||
|
|
a554eb7ef4 | ||
|
|
26ff90122b | ||
|
|
75b27c6540 | ||
|
|
a7e77fa98d | ||
|
|
abf392117a | ||
|
|
d231d9f572 | ||
|
|
97447d058e | ||
|
|
040e112aa6 | ||
|
|
afb021b4b5 | ||
|
|
5075774000 | ||
|
|
6bba44dc50 | ||
|
|
422022c74d | ||
|
|
87fcf97e2d | ||
|
|
05d1f348d4 | ||
|
|
6f2f1f3004 | ||
|
|
9cd9d1aeb5 | ||
|
|
71b27c0aa9 | ||
|
|
09a379ec6d | ||
|
|
b4b9b03835 | ||
|
|
48c7794b01 | ||
|
|
8e3911833d | ||
|
|
ebe494051b | ||
|
|
c35bda6a65 | ||
|
|
1fe4cdc429 | ||
|
|
6a79e322e3 | ||
|
|
17e8d2bb6d | ||
|
|
325be0d6a1 | ||
|
|
ea0f96a8c0 | ||
|
|
82cbb54787 | ||
|
|
585d33cafa | ||
|
|
bc4cf35e15 | ||
|
|
a3bc20af1d | ||
|
|
61338b6b46 | ||
|
|
bc3c080ac3 | ||
|
|
d3fab15af3 | ||
|
|
6e3e411d46 | ||
|
|
f976941e36 | ||
|
|
4c5524354c | ||
|
|
2c6e9a3fb9 | ||
|
|
5f2c3edff4 | ||
|
|
b422d98702 | ||
|
|
6f27e0bfbf | ||
|
|
ce3b6b461e | ||
|
|
0d6ad4f0d2 | ||
|
|
c875a7f8b8 | ||
|
|
0c62d117a1 | ||
|
|
c260ce493d | ||
|
|
9ffe96d3ef | ||
|
|
d4bba76dc8 | ||
|
|
adaa3ebc43 | ||
|
|
61d38ae3b8 | ||
|
|
c85d910044 | ||
|
|
d95a7c6101 | ||
|
|
bce3641ec6 | ||
|
|
ceb134b2fd | ||
|
|
4b9b1d32c5 | ||
|
|
395e0883c7 | ||
|
|
a930511054 | ||
|
|
7380721ccc | ||
|
|
936011dc3b | ||
|
|
bc0ee08f38 | ||
|
|
f1afd753d4 | ||
|
|
b05a0a91c3 | ||
|
|
da2eb66657 | ||
|
|
ad20159de2 | ||
|
|
278733d3d1 | ||
|
|
2f184907ff | ||
|
|
50fcc718ee | ||
|
|
cc68feb52d | ||
|
|
ca2a51b37e | ||
|
|
be444f521d | ||
|
|
e21dd07d46 | ||
|
|
4044deac70 | ||
|
|
d9c6294775 | ||
|
|
d8505d1a5f | ||
|
|
6498c7d68a | ||
|
|
5f0211f0c4 | ||
|
|
1de2e2cedd | ||
|
|
c8f8b1b7d9 | ||
|
|
3062eb7789 | ||
|
|
64d5486cc6 | ||
|
|
6d57891f66 | ||
|
|
927245e47f | ||
|
|
31fdbee0b5 | ||
|
|
3712819994 | ||
|
|
6d7dec1cea | ||
|
|
9b6a004dbc | ||
|
|
3ad6f4bd9e | ||
|
|
b8c6746f31 | ||
|
|
0bf8bbf4cb | ||
|
|
a6ba1028c5 | ||
|
|
cc54dd7ec9 | ||
|
|
54834dba4c | ||
|
|
e996d4c945 | ||
|
|
ae92ae7666 | ||
|
|
526e568d0e | ||
|
|
1dc5e8ac23 | ||
|
|
b5f7b0e670 | ||
|
|
39c576248c | ||
|
|
eb164d1536 | ||
|
|
68e170080c | ||
|
|
652b09135c | ||
|
|
bff24c89bc | ||
|
|
49a460eef8 | ||
|
|
a58e52e53c | ||
|
|
b07356423f | ||
|
|
005dd9d2ab | ||
|
|
65315dafb1 | ||
|
|
b06fb6736d | ||
|
|
b9f0854950 | ||
|
|
6a2dd328ad | ||
|
|
dd1147f6d0 | ||
|
|
6da7ce64af | ||
|
|
b7e5d946f1 | ||
|
|
687db4ce79 | ||
|
|
a9a4bb8841 | ||
|
|
bc6cb5f61b | ||
|
|
2c57266f8e | ||
|
|
57e49c9520 | ||
|
|
985c9a5d9a | ||
|
|
5cfb11f036 | ||
|
|
5b3d48a1e7 | ||
|
|
d0576e028f | ||
|
|
76bdec2b54 | ||
|
|
fa20aba3a7 | ||
|
|
7a4a429e23 | ||
|
|
a70ec6ad1e | ||
|
|
e435fa271b | ||
|
|
1533d00891 | ||
|
|
8b99641de0 | ||
|
|
251f899f18 | ||
|
|
00407e5182 | ||
|
|
4e457ad672 | ||
|
|
7d8d9c3543 | ||
|
|
1197e8d1b6 | ||
|
|
13bbe19ab2 | ||
|
|
2725575d2f | ||
|
|
037d2729ce | ||
|
|
09d98745b0 | ||
|
|
af5cfe6da1 | ||
|
|
d59259bbe0 | ||
|
|
3189628d54 | ||
|
|
62c5e251a5 | ||
|
|
6e6444c8c0 | ||
|
|
dd613e98b2 | ||
|
|
2d905ab03e | ||
|
|
205fedb197 | ||
|
|
4c864da62d | ||
|
|
263ab7e316 | ||
|
|
809b7a53df | ||
|
|
61c43daabb | ||
|
|
18914ee2cd | ||
|
|
0a8058956f | ||
|
|
a044da7d2f | ||
|
|
f8b785c4cb | ||
|
|
82c8a04c35 | ||
|
|
280ecb5cef | ||
|
|
ec27ad4c40 | ||
|
|
d66f92cd68 | ||
|
|
d8ed692651 | ||
|
|
a16de97d1d | ||
|
|
6f458e4367 | ||
|
|
37c7508ad0 | ||
|
|
acb5844b15 | ||
|
|
76f4eeea16 | ||
|
|
5466a23019 | ||
|
|
5692982dd1 | ||
|
|
c39c8914fb | ||
|
|
29bdca1bd2 | ||
|
|
eb66d038ac | ||
|
|
a99ada5ee1 | ||
|
|
a87faf5453 | ||
|
|
ab6fec2f69 | ||
|
|
7b290989f5 | ||
|
|
5722c724e6 | ||
|
|
f84584ca04 | ||
|
|
80cbbdc787 | ||
|
|
be148e07ba | ||
|
|
d36ab5cc3a | ||
|
|
2b17329094 | ||
|
|
f869a0a670 | ||
|
|
be000a4bd6 | ||
|
|
81efa800ea | ||
|
|
a783de57a8 | ||
|
|
a622959a78 | ||
|
|
f709dd53ce | ||
|
|
ee9e9625e2 | ||
|
|
84da7d4f27 | ||
|
|
089fd4e13c | ||
|
|
6c40e02324 | ||
|
|
a270b1b634 | ||
|
|
260fb795d3 | ||
|
|
8c036f830c | ||
|
|
bf332b9f1c | ||
|
|
6b6cd66f9f | ||
|
|
5399c2290e | ||
|
|
6248293c49 | ||
|
|
eced25b76d | ||
|
|
96a4327aa7 | ||
|
|
fcfbdea597 | ||
|
|
87896f917a | ||
|
|
b8563ed646 | ||
|
|
1ccf0af2b3 | ||
|
|
6314889c6a | ||
|
|
294bd5a5ed | ||
|
|
ca44815fd5 | ||
|
|
bcd1cabf80 | ||
|
|
4edefcca67 | ||
|
|
fab24c8d12 | ||
|
|
604acd9b07 | ||
|
|
531cb19fd1 | ||
|
|
4f0161ca53 | ||
|
|
73362b7f69 | ||
|
|
b289f2975b | ||
|
|
90ce2705ca | ||
|
|
35e1d09ce3 | ||
|
|
d67d8fad3a | ||
|
|
c5e6d01a31 | ||
|
|
6d6c3f692f | ||
|
|
c695837b16 | ||
|
|
ef28b52963 | ||
|
|
18b2b6ff7c | ||
|
|
6a05a123f4 | ||
|
|
f7c33f258d | ||
|
|
04058dd7da | ||
|
|
72e1d92fa1 | ||
|
|
063df0f0d1 | ||
|
|
fc10689b25 | ||
|
|
9239bcfdc3 | ||
|
|
c984815dea | ||
|
|
301cb5db87 | ||
|
|
cc1bacff74 | ||
|
|
f49f75b371 | ||
|
|
b30f4a7bab | ||
|
|
982cb8932b | ||
|
|
9107eb4b32 | ||
|
|
1acf977b24 | ||
|
|
5c878d0873 | ||
|
|
ab0716a472 | ||
|
|
3c2e0ca5b3 | ||
|
|
551e66ba29 | ||
|
|
4eeeabb227 | ||
|
|
b3437fdddf | ||
|
|
312dcab369 | ||
|
|
0cb89f4f46 | ||
|
|
a2dec87995 | ||
|
|
9094cb29bf | ||
|
|
ddb992ad2f | ||
|
|
24f18aaaec | ||
|
|
36169874e5 | ||
|
|
23aeb6ac99 | ||
|
|
a1984f0a59 | ||
|
|
c95db23d2c | ||
|
|
fbf251d805 | ||
|
|
337f08a04b | ||
|
|
c6a7371049 | ||
|
|
b697858038 | ||
|
|
5446f7e33c | ||
|
|
553f63e76b | ||
|
|
fc3e94df58 | ||
|
|
5c66f38a5b | ||
|
|
70e213b3f7 | ||
|
|
c1b7500bcb | ||
|
|
3d7600a9e4 | ||
|
|
0299c454ab | ||
|
|
741af66404 | ||
|
|
ef5d0379a1 | ||
|
|
105ab49763 | ||
|
|
b5ba9ba573 | ||
|
|
ac3baac5c6 | ||
|
|
a82b5dd206 | ||
|
|
ac0fb5ef25 | ||
|
|
9bd1c205fe | ||
|
|
6ccf9fd3cf | ||
|
|
c02ef2c62d | ||
|
|
6414031754 | ||
|
|
5336130570 | ||
|
|
bfa46d46a2 | ||
|
|
aab28b914c | ||
|
|
336e454ce7 | ||
|
|
35a50bd792 | ||
|
|
2fd23f02f6 | ||
|
|
aad5b447c9 | ||
|
|
cece13e929 | ||
|
|
ecf325a06c | ||
|
|
44370aef5e | ||
|
|
1782b11ec4 | ||
|
|
fe6495123c | ||
|
|
d442f564ce | ||
|
|
f35446cc13 | ||
|
|
506a8b4776 | ||
|
|
47ecfbfd5f | ||
|
|
82f7401368 | ||
|
|
068c878d1e | ||
|
|
47b2402345 | ||
|
|
a245716f99 | ||
|
|
71c85bb8f0 | ||
|
|
5edafec393 | ||
|
|
957878dd98 | ||
|
|
12e979cf3c | ||
|
|
ded42f8ff5 | ||
|
|
d06cde3fcf | ||
|
|
9cd8bfb8e0 | ||
|
|
51c9877224 | ||
|
|
f7f6427919 | ||
|
|
56b6844a2d | ||
|
|
cce56f0479 | ||
|
|
3dbd7d8980 | ||
|
|
3625921912 | ||
|
|
1faaf54986 | ||
|
|
efe1fba267 | ||
|
|
0c43cb3f68 | ||
|
|
c25111e349 | ||
|
|
a61ab5716e | ||
|
|
628404dc7f | ||
|
|
bab0a27e40 | ||
|
|
fc0e104591 | ||
|
|
e83e9a1651 | ||
|
|
3d2e6295c4 | ||
|
|
221fe44e63 | ||
|
|
7930f517ae | ||
|
|
0f61321d5b | ||
|
|
7ddeeb51e5 | ||
|
|
484d2845b3 | ||
|
|
bbbc6b6941 | ||
|
|
acf028cdcd | ||
|
|
31e43ec356 | ||
|
|
9f45e68fef | ||
|
|
d2df99a6f0 | ||
|
|
713c341831 | ||
|
|
0b05a6dd6f | ||
|
|
d21c101caf | ||
|
|
bf49db60d9 | ||
|
|
d57fa3b603 | ||
|
|
b3a29ce82d | ||
|
|
1d8a37d6e8 | ||
|
|
ef2d8e14b0 | ||
|
|
0cbb3dd97e | ||
|
|
38a7f9423f | ||
|
|
2c0ab9e922 | ||
|
|
9aa3b742ff | ||
|
|
6bfaba750d | ||
|
|
69f8bee935 | ||
|
|
07d04a9cd0 | ||
|
|
6f17ba9116 | ||
|
|
56d1e5e95c | ||
|
|
11d86d0270 | ||
|
|
d1c20a1da8 | ||
|
|
f87ab89207 | ||
|
|
9ebb0c7920 | ||
|
|
31a5ed11a3 | ||
|
|
a706d488e6 | ||
|
|
61251fb0f1 | ||
|
|
747dc30b02 | ||
|
|
d54f27d9a9 | ||
|
|
dbca2f6be4 | ||
|
|
2ef9b56d22 | ||
|
|
52ecde7e24 | ||
|
|
a7dce5e4d3 | ||
|
|
4718da44f4 | ||
|
|
1aa1f95524 | ||
|
|
d922b2e778 | ||
|
|
5a7b3c4a37 | ||
|
|
1c2b1a2f02 | ||
|
|
c68440ecf8 | ||
|
|
6a48b3f99e | ||
|
|
bab9ae32d4 | ||
|
|
f0e6e94c5e | ||
|
|
a6fe155781 | ||
|
|
d5be9009fc | ||
|
|
ca47be41f6 | ||
|
|
ff5db30208 | ||
|
|
a429d46072 | ||
|
|
7b84fbfd94 | ||
|
|
fc6f618885 | ||
|
|
7f03c3b0a2 | ||
|
|
4b4386bd47 | ||
|
|
10a1706613 | ||
|
|
d097f356fd | ||
|
|
bc9bacaa55 | ||
|
|
c949059fb1 | ||
|
|
9c6d489db7 | ||
|
|
45436798af | ||
|
|
bd0eb0682c | ||
|
|
bdbc06b095 | ||
|
|
2f504ce851 | ||
|
|
02a24b461c | ||
|
|
3f7a81c005 | ||
|
|
cc6e10ee5a | ||
|
|
eff2f22440 | ||
|
|
47b6747bb0 | ||
|
|
298219aee7 | ||
|
|
d5607b0a5a | ||
|
|
f47873fbc7 | ||
|
|
f24ad26692 | ||
|
|
88de9bfac2 | ||
|
|
e4777a0986 | ||
|
|
84a69d1fa5 | ||
|
|
5eb056277d | ||
|
|
4e6d586251 | ||
|
|
61c7012e17 | ||
|
|
0d48778886 | ||
|
|
e98c46c903 | ||
|
|
155f287ab0 | ||
|
|
652916b7d7 | ||
|
|
955de2bdbf | ||
|
|
a4c1d40faa | ||
|
|
f155e0caa6 | ||
|
|
fb0332449c | ||
|
|
68f0fd1d4c | ||
|
|
70995c5098 | ||
|
|
55e62ed8ca | ||
|
|
e614274f07 | ||
|
|
c524eb04a1 | ||
|
|
985b3cddd8 | ||
|
|
abadeec007 | ||
|
|
3ab05154aa | ||
|
|
9f1c89bf0e | ||
|
|
9444582e44 | ||
|
|
c27ccad2b9 | ||
|
|
d1c6c57700 | ||
|
|
922999037f | ||
|
|
046e90486d | ||
|
|
fc6dee9e77 | ||
|
|
980ff2c363 | ||
|
|
763aa8e10d | ||
|
|
83c0d1d6e6 | ||
|
|
dfc6b868fb | ||
|
|
f66c0938e5 | ||
|
|
a4900fecb4 | ||
|
|
dc25bdab2f | ||
|
|
bb23c9eef8 | ||
|
|
b4cd0750e4 | ||
|
|
c960841491 | ||
|
|
5d6f9443fd | ||
|
|
5483380066 | ||
|
|
20b9e5353e | ||
|
|
1ec3a5af87 | ||
|
|
f31dde97b1 | ||
|
|
1718fd1cf1 | ||
|
|
513c0f829c | ||
|
|
c51a806aff | ||
|
|
3c16d50c4b | ||
|
|
21dd6ab8aa | ||
|
|
f5bf5183cc | ||
|
|
f28b5b2f92 | ||
|
|
79867e8a63 | ||
|
|
19939c2b03 | ||
|
|
bf7ffbfa8d | ||
|
|
cbb46f9cb4 | ||
|
|
3ad142d5d7 | ||
|
|
1a436aeb41 | ||
|
|
1af8fe8daf | ||
|
|
ae54c8db6f | ||
|
|
be942ec33e | ||
|
|
fafb59dad6 | ||
|
|
e36c01cac9 | ||
|
|
aa0afa8d94 | ||
|
|
18e0031e0e | ||
|
|
a5018c110c | ||
|
|
142deb83b2 | ||
|
|
ccc0188352 | ||
|
|
98dba83107 | ||
|
|
9c6aff9030 | ||
|
|
7fd9a94922 | ||
|
|
dd3b1f3aed | ||
|
|
f9dce592e8 | ||
|
|
c4b0967623 | ||
|
|
41613061d5 | ||
|
|
50ef63e27d | ||
|
|
3ae4a35d04 | ||
|
|
89ea0ee15a | ||
|
|
85399e23ed | ||
|
|
19a6f218e6 | ||
|
|
ccaf290b63 | ||
|
|
54745d512c | ||
|
|
39234d4722 | ||
|
|
4b4310265b | ||
|
|
d8a982bf07 | ||
|
|
0334a6f02b | ||
|
|
ae0c613b8e | ||
|
|
40b2560752 | ||
|
|
50c6e51393 | ||
|
|
1434e00712 | ||
|
|
76c941cff0 | ||
|
|
bb03f31a8b | ||
|
|
c939b26cb8 | ||
|
|
af480efa8c | ||
|
|
bc913d70b1 | ||
|
|
2bc87d8a6b | ||
|
|
2185c48eab | ||
|
|
59b5046a15 | ||
|
|
569902b528 | ||
|
|
78519088ab | ||
|
|
0c06cdb29f | ||
|
|
e8b8ff8927 | ||
|
|
06bd407a0c | ||
|
|
aa28479d11 | ||
|
|
bf7db0ded2 | ||
|
|
dd1cbf2ee3 | ||
|
|
9d5db895c9 | ||
|
|
6b09da6e6e | ||
|
|
b2b6710a1b | ||
|
|
e7ee2ed7cf | ||
|
|
1dfd1ec1d3 | ||
|
|
2e8834a2c1 | ||
|
|
df098392c2 | ||
|
|
5e318a78d2 | ||
|
|
d872d64fe0 | ||
|
|
c2257493a8 | ||
|
|
48c1f8ffad | ||
|
|
8a4c8218d2 | ||
|
|
92e1321d43 | ||
|
|
43fb907a81 | ||
|
|
f551c56921 | ||
|
|
85e1f711f5 | ||
|
|
ade567a620 | ||
|
|
a26e519a3f | ||
|
|
797e47363f | ||
|
|
20e1836eb7 | ||
|
|
5ae746a1f0 | ||
|
|
17d271190b | ||
|
|
310f8bd1e1 | ||
|
|
bf7a4e729f | ||
|
|
8d0ff28304 | ||
|
|
cee9bb538f | ||
|
|
eb59d02a08 | ||
|
|
10ed71a3a2 | ||
|
|
0e86244559 | ||
|
|
5ab150cad3 | ||
|
|
3546afe86c | ||
|
|
70077a6010 | ||
|
|
efdf36a817 | ||
|
|
e233788f69 | ||
|
|
957e6356f6 | ||
|
|
887eaa528a | ||
|
|
33126570bd | ||
|
|
5937183af0 | ||
|
|
4eaa0a39d5 | ||
|
|
18ec73839b | ||
|
|
89aef977a2 | ||
|
|
2612569500 | ||
|
|
acc8b59387 | ||
|
|
2878d389c8 | ||
|
|
3f8effb7a3 | ||
|
|
bd30855ae6 | ||
|
|
0efe2f57c3 | ||
|
|
8868fcabed | ||
|
|
dfbb9e30e0 | ||
|
|
b22be7cade | ||
|
|
87da6c6816 | ||
|
|
912f7052a3 | ||
|
|
e6f9b0954c | ||
|
|
98e861cf3b | ||
|
|
43e0ca7edf | ||
|
|
d491bea263 | ||
|
|
6f1feb96d6 | ||
|
|
d46d12f949 | ||
|
|
c6fc92a94c | ||
|
|
f4fa790b72 | ||
|
|
22798fd750 | ||
|
|
907df56f86 | ||
|
|
3dadfa234b | ||
|
|
f1d7154179 | ||
|
|
dc47b9ce98 | ||
|
|
6e626851cf | ||
|
|
c906d8aea7 | ||
|
|
e5e4e09308 | ||
|
|
7ed173849a | ||
|
|
24c0d95112 | ||
|
|
e9fed34729 | ||
|
|
9caefdb67d | ||
|
|
681d5b6bdf | ||
|
|
b0e9758e0d | ||
|
|
7b9a8a55e7 | ||
|
|
44ac82e8b8 | ||
|
|
c4b4cb1173 | ||
|
|
30d798366b | ||
|
|
3e9a2b5c59 | ||
|
|
1343683b08 |
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
liberapay: Duke_Du
|
||||
patreon: DukeDu
|
||||
2
.github/workflows/codecov.yml
vendored
2
.github/workflows/codecov.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
fetch-depth: 2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "1.16"
|
||||
go-version: "1.18"
|
||||
- name: Run coverage
|
||||
run: go test -v ./... -coverprofile=coverage.txt -covermode=atomic
|
||||
- name: Upload coverage to Codecov
|
||||
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -6,5 +6,10 @@ fileutil/*.txt
|
||||
fileutil/*.zip
|
||||
fileutil/*.link
|
||||
fileutil/unzip/*
|
||||
fileutil/tempdir/*
|
||||
slice/testdata/*
|
||||
cryptor/*.pem
|
||||
cryptor/*.pem
|
||||
test
|
||||
docs/node_modules
|
||||
docs/.vitepress/cache
|
||||
docs/.vitepress/dist
|
||||
|
||||
37
CONTRIBUTING.en-US.md
Normal file
37
CONTRIBUTING.en-US.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Lancet Contributing Guide
|
||||
|
||||
Hi! Thank you for choosing Lancet.
|
||||
|
||||
Lancet is a powerful, efficient, and reusable util function library of go. It makes Go dev easier by taking the hassle out of working with concurrency, net, math, slice, string, etc.
|
||||
|
||||
We are excited that you are interested in contributing to lancet. Before submitting your contribution though, please make sure to take a moment and read through the following guidelines.
|
||||
|
||||
## Issue Guidelines
|
||||
|
||||
- Issues are exclusively for bug reports, feature requests and design-related topics. Other questions may be closed directly.
|
||||
|
||||
- Before submitting an issue, please check if similar problems have already been issued.
|
||||
|
||||
- Please specify which version of Lancet and Go you are using, and provide OS information. [Go Playground](https://go.dev/play/) is recommended to build a live demo so that your issue can be reproduced clearly.
|
||||
|
||||
## Pull Request Guidelines
|
||||
|
||||
- Fork this repository to your own account. Do not create branches here.
|
||||
|
||||
- Commit info should be formatted as `type(scope): info about commit`. eg. `fix(package): [scrollbar] fix xxx bug`.
|
||||
|
||||
1. type: type must be one of [chore, docs, feat, fix, refactor, release, test].
|
||||
|
||||
2. scope: scope must be one of [package, file, internal].
|
||||
|
||||
3. header: header must not be longer than 72 characters.
|
||||
|
||||
- Rebase before creating a PR to keep commit history clear.
|
||||
|
||||
- Before submitting a PR, please execute the unit test command: `go test -v ./...` to ensure that all unit test tasks should pass.
|
||||
|
||||
- Make sure PRs are created to `v2` branch instead of `master` branch.
|
||||
|
||||
- If your PR fixes a bug, please provide a description about the related bug.
|
||||
|
||||
- If the PR is for a new feature, make sure to complete the relevant documentation (/lancet/docs/en/api/packages).
|
||||
37
CONTRIBUTING.zh-CN.md
Normal file
37
CONTRIBUTING.zh-CN.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Lancet 贡献指南
|
||||
|
||||
Hi! 首先感谢你使用 Lancet。
|
||||
|
||||
lancet(柳叶刀)是一个功能强大、全面、高效、可复用的go语言工具函数库。它消除了处理并发、网络、数学、切片、字符串等的麻烦,使 Go 开发变得更容易。
|
||||
|
||||
Lancet 的成长离不开大家的支持,如果你愿意为 Lancet 贡献代码或提供建议,请阅读以下内容。
|
||||
|
||||
## Issue 规范
|
||||
|
||||
- issue 仅用于提交 Bug 或 Feature 以及设计相关的内容,其它内容可能会被直接关闭。
|
||||
|
||||
- 在提交 issue 之前,请搜索相关内容是否已被提出。
|
||||
|
||||
- 请说明 Lancet 和 Go 的版本号,并提供操作系统信息。推荐使用 [Go Playground](https://go.dev/play/) 生成在线 demo,这能够更直观地重现问题。
|
||||
|
||||
## Pull Request 规范
|
||||
|
||||
- 请先 fork 一份到自己的项目下,不要直接在仓库下建分支。
|
||||
|
||||
- commit 信息要以 `type(scope): 描述信息` 的形式填写,例如 `fix(package): [scrollbar] fix xxx bug`。
|
||||
|
||||
1. type: 必须是 chore, docs, feat, fix, refactor, release, test 其中的一个。
|
||||
|
||||
2. scope: 必须是 package, file, internal 其中的一个。
|
||||
|
||||
3. header: 描述信息不要超过 72 个字符。
|
||||
|
||||
- 提交 PR 前请 rebase,确保 commit 记录的整洁。
|
||||
|
||||
- 提交 PR 前请执行单元测试命令:go test -v ./...,确保所有单元测试任务通过。
|
||||
|
||||
- 确保 PR 是提交到 `v2` 分支,而不是 `main` 分支。
|
||||
|
||||
- 如果是修复 bug,请在 PR 中给出描述信息。
|
||||
|
||||
- 如果PR是新功能,确保完成相关文档(/lancet/docs/api/packages)。
|
||||
2093
README_zh-CN.md
2093
README_zh-CN.md
File diff suppressed because it is too large
Load Diff
15
SECURITY.md
Normal file
15
SECURITY.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
Here is the lancet version and compatibility with go language version.
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------|
|
||||
| 2.x.x | +go v1.18 |
|
||||
| 1.x.x | +go v1.12 |
|
||||
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
For now, there is no public website to report a vulnerability, If you find security issue in lancet, you can send it to me via my email `lanliddd.2007@163.com`.
|
||||
we can discuss it. I am appreciate if someone can create a public page for reporting vulnerability.
|
||||
121
algorithm/lrucache.go
Normal file
121
algorithm/lrucache.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package algorithm
|
||||
|
||||
type lruNode[K comparable, V any] struct {
|
||||
key K
|
||||
value V
|
||||
pre *lruNode[K, V]
|
||||
next *lruNode[K, V]
|
||||
}
|
||||
|
||||
// newLruNode return a lruNode pointer
|
||||
func newLruNode[K comparable, V any](key K, value V) *lruNode[K, V] {
|
||||
return &lruNode[K, V]{
|
||||
key: key,
|
||||
value: value,
|
||||
pre: nil,
|
||||
next: nil,
|
||||
}
|
||||
}
|
||||
|
||||
// LRUCache lru cache (thread unsafe)
|
||||
type LRUCache[K comparable, V any] struct {
|
||||
cache map[K]*lruNode[K, V]
|
||||
head *lruNode[K, V]
|
||||
tail *lruNode[K, V]
|
||||
capacity int
|
||||
length int
|
||||
}
|
||||
|
||||
// NewLRUCache creates a LRUCache pointer instance.
|
||||
func NewLRUCache[K comparable, V any](capacity int) *LRUCache[K, V] {
|
||||
return &LRUCache[K, V]{
|
||||
cache: make(map[K]*lruNode[K, V], capacity),
|
||||
head: nil,
|
||||
tail: nil,
|
||||
capacity: capacity,
|
||||
length: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Get value of key from lru cache.
|
||||
// Play: https://go.dev/play/p/iUynEfOP8G0
|
||||
func (l *LRUCache[K, V]) Get(key K) (V, bool) {
|
||||
var value V
|
||||
|
||||
node, ok := l.cache[key]
|
||||
if ok {
|
||||
l.moveToHead(node)
|
||||
return node.value, true
|
||||
}
|
||||
|
||||
return value, false
|
||||
}
|
||||
|
||||
// Put value of key into lru cache.
|
||||
// Play: https://go.dev/play/p/iUynEfOP8G0
|
||||
func (l *LRUCache[K, V]) Put(key K, value V) {
|
||||
node, ok := l.cache[key]
|
||||
if !ok {
|
||||
newNode := newLruNode(key, value)
|
||||
l.cache[key] = newNode
|
||||
l.addNode(newNode)
|
||||
|
||||
if len(l.cache) > l.capacity {
|
||||
oldKey := l.deleteNode(l.head)
|
||||
delete(l.cache, oldKey)
|
||||
}
|
||||
} else {
|
||||
node.value = value
|
||||
l.moveToHead(node)
|
||||
}
|
||||
l.length = len(l.cache)
|
||||
}
|
||||
|
||||
// Delete item from lru cache.
|
||||
func (l *LRUCache[K, V]) Delete(key K) bool {
|
||||
node, ok := l.cache[key]
|
||||
if ok {
|
||||
key := l.deleteNode(node)
|
||||
delete(l.cache, key)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Len returns the number of items in the cache.
|
||||
func (l *LRUCache[K, V]) Len() int {
|
||||
return l.length
|
||||
}
|
||||
|
||||
func (l *LRUCache[K, V]) addNode(node *lruNode[K, V]) {
|
||||
if l.tail != nil {
|
||||
l.tail.next = node
|
||||
node.pre = l.tail
|
||||
node.next = nil
|
||||
}
|
||||
l.tail = node
|
||||
if l.head == nil {
|
||||
l.head = node
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LRUCache[K, V]) deleteNode(node *lruNode[K, V]) K {
|
||||
if node == l.tail {
|
||||
l.tail = l.tail.pre
|
||||
} else if node == l.head {
|
||||
l.head = l.head.next
|
||||
} else {
|
||||
node.pre.next = node.next
|
||||
node.next.pre = node.pre
|
||||
}
|
||||
return node.key
|
||||
}
|
||||
|
||||
func (l *LRUCache[K, V]) moveToHead(node *lruNode[K, V]) {
|
||||
if l.tail == node {
|
||||
return
|
||||
}
|
||||
l.deleteNode(node)
|
||||
l.addNode(node)
|
||||
}
|
||||
79
algorithm/lrucache_example_test.go
Normal file
79
algorithm/lrucache_example_test.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package algorithm
|
||||
|
||||
import "fmt"
|
||||
|
||||
func ExampleLRUCache_Put() {
|
||||
cache := NewLRUCache[int, int](2)
|
||||
|
||||
cache.Put(1, 1)
|
||||
cache.Put(2, 2)
|
||||
|
||||
result1, ok1 := cache.Get(1)
|
||||
result2, ok2 := cache.Get(2)
|
||||
result3, ok3 := cache.Get(3)
|
||||
|
||||
fmt.Println(result1, ok1)
|
||||
fmt.Println(result2, ok2)
|
||||
fmt.Println(result3, ok3)
|
||||
|
||||
// Output:
|
||||
// 1 true
|
||||
// 2 true
|
||||
// 0 false
|
||||
}
|
||||
|
||||
func ExampleLRUCache_Get() {
|
||||
cache := NewLRUCache[int, int](2)
|
||||
|
||||
cache.Put(1, 1)
|
||||
cache.Put(2, 2)
|
||||
|
||||
result1, ok1 := cache.Get(1)
|
||||
result2, ok2 := cache.Get(2)
|
||||
result3, ok3 := cache.Get(3)
|
||||
|
||||
fmt.Println(result1, ok1)
|
||||
fmt.Println(result2, ok2)
|
||||
fmt.Println(result3, ok3)
|
||||
|
||||
// Output:
|
||||
// 1 true
|
||||
// 2 true
|
||||
// 0 false
|
||||
}
|
||||
|
||||
func ExampleLRUCache_Delete() {
|
||||
cache := NewLRUCache[int, int](2)
|
||||
|
||||
cache.Put(1, 1)
|
||||
cache.Put(2, 2)
|
||||
|
||||
result1, ok1 := cache.Get(1)
|
||||
|
||||
ok2 := cache.Delete(2)
|
||||
|
||||
_, ok3 := cache.Get(2)
|
||||
|
||||
fmt.Println(result1, ok1)
|
||||
fmt.Println(ok2)
|
||||
fmt.Println(ok3)
|
||||
|
||||
// Output:
|
||||
// 1 true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleLRUCache_Len() {
|
||||
cache := NewLRUCache[int, int](2)
|
||||
|
||||
cache.Put(1, 1)
|
||||
cache.Put(2, 2)
|
||||
|
||||
result := cache.Len()
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
}
|
||||
34
algorithm/lrucache_test.go
Normal file
34
algorithm/lrucache_test.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package algorithm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestLRUCache(t *testing.T) {
|
||||
t.Parallel()
|
||||
asssert := internal.NewAssert(t, "TestLRUCache")
|
||||
|
||||
cache := NewLRUCache[int, int](3)
|
||||
|
||||
cache.Put(1, 1)
|
||||
cache.Put(2, 2)
|
||||
cache.Put(3, 3)
|
||||
|
||||
asssert.Equal(3, cache.Len())
|
||||
|
||||
v, ok := cache.Get(1)
|
||||
asssert.Equal(true, ok)
|
||||
asssert.Equal(1, v)
|
||||
|
||||
v, ok = cache.Get(2)
|
||||
asssert.Equal(true, ok)
|
||||
asssert.Equal(2, v)
|
||||
|
||||
ok = cache.Delete(2)
|
||||
asssert.Equal(true, ok)
|
||||
|
||||
_, ok = cache.Get(2)
|
||||
asssert.Equal(false, ok)
|
||||
}
|
||||
66
algorithm/search.go
Normal file
66
algorithm/search.go
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package algorithm contain some basic algorithm functions. eg. sort, search, list, linklist, stack, queue, tree, graph.
|
||||
package algorithm
|
||||
|
||||
import "github.com/duke-git/lancet/v2/constraints"
|
||||
|
||||
// Search algorithms see https://github.com/TheAlgorithms/Go/tree/master/search
|
||||
|
||||
// LinearSearch return the index of target in slice base on equal function.
|
||||
// If not found return -1
|
||||
// Play: https://go.dev/play/p/IsS7rgn5s3x
|
||||
func LinearSearch[T any](slice []T, target T, equal func(a, b T) bool) int {
|
||||
for i, v := range slice {
|
||||
if equal(v, target) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// BinarySearch return the index of target within a sorted slice, use binary search (recursive call itself).
|
||||
// If not found return -1.
|
||||
// Play: https://go.dev/play/p/t6MeGiUSN47
|
||||
func BinarySearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator constraints.Comparator) int {
|
||||
if highIndex < lowIndex || len(sortedSlice) == 0 {
|
||||
return -1
|
||||
}
|
||||
|
||||
midIndex := int(lowIndex + (highIndex-lowIndex)/2)
|
||||
isMidValGreatTarget := comparator.Compare(sortedSlice[midIndex], target) == 1
|
||||
isMidValLessTarget := comparator.Compare(sortedSlice[midIndex], target) == -1
|
||||
|
||||
if isMidValGreatTarget {
|
||||
return BinarySearch(sortedSlice, target, lowIndex, midIndex-1, comparator)
|
||||
} else if isMidValLessTarget {
|
||||
return BinarySearch(sortedSlice, target, midIndex+1, highIndex, comparator)
|
||||
}
|
||||
|
||||
return midIndex
|
||||
}
|
||||
|
||||
// BinaryIterativeSearch return the index of target within a sorted slice, use binary search (no recursive).
|
||||
// If not found return -1.
|
||||
// Play: https://go.dev/play/p/Anozfr8ZLH3
|
||||
func BinaryIterativeSearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator constraints.Comparator) int {
|
||||
startIndex := lowIndex
|
||||
endIndex := highIndex
|
||||
|
||||
var midIndex int
|
||||
for startIndex <= endIndex {
|
||||
midIndex = int(startIndex + (endIndex-startIndex)/2)
|
||||
isMidValGreatTarget := comparator.Compare(sortedSlice[midIndex], target) == 1
|
||||
isMidValLessTarget := comparator.Compare(sortedSlice[midIndex], target) == -1
|
||||
|
||||
if isMidValGreatTarget {
|
||||
endIndex = midIndex - 1
|
||||
} else if isMidValLessTarget {
|
||||
startIndex = midIndex + 1
|
||||
} else {
|
||||
return midIndex
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
51
algorithm/search_example_test.go
Normal file
51
algorithm/search_example_test.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package algorithm
|
||||
|
||||
import "fmt"
|
||||
|
||||
func ExampleLinearSearch() {
|
||||
numbers := []int{3, 4, 5, 3, 2, 1}
|
||||
|
||||
equalFunc := func(a, b int) bool {
|
||||
return a == b
|
||||
}
|
||||
|
||||
result1 := LinearSearch(numbers, 3, equalFunc)
|
||||
result2 := LinearSearch(numbers, 6, equalFunc)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
// -1
|
||||
}
|
||||
|
||||
func ExampleBinarySearch() {
|
||||
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8}
|
||||
comparator := &intComparator{}
|
||||
|
||||
result1 := BinarySearch(numbers, 5, 0, len(numbers)-1, comparator)
|
||||
result2 := BinarySearch(numbers, 9, 0, len(numbers)-1, comparator)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
|
||||
// Output:
|
||||
// 4
|
||||
// -1
|
||||
}
|
||||
|
||||
func ExampleBinaryIterativeSearch() {
|
||||
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8}
|
||||
comparator := &intComparator{}
|
||||
|
||||
result1 := BinaryIterativeSearch(numbers, 5, 0, len(numbers)-1, comparator)
|
||||
result2 := BinaryIterativeSearch(numbers, 9, 0, len(numbers)-1, comparator)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
|
||||
// Output:
|
||||
// 4
|
||||
// -1
|
||||
}
|
||||
41
algorithm/search_test.go
Normal file
41
algorithm/search_test.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package algorithm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestLinearSearch(t *testing.T) {
|
||||
t.Parallel()
|
||||
asssert := internal.NewAssert(t, "TestLinearSearch")
|
||||
|
||||
numbers := []int{3, 4, 5, 3, 2, 1}
|
||||
equalFunc := func(a, b int) bool {
|
||||
return a == b
|
||||
}
|
||||
|
||||
asssert.Equal(0, LinearSearch(numbers, 3, equalFunc))
|
||||
asssert.Equal(-1, LinearSearch(numbers, 6, equalFunc))
|
||||
}
|
||||
|
||||
func TestBinarySearch(t *testing.T) {
|
||||
t.Parallel()
|
||||
asssert := internal.NewAssert(t, "TestBinarySearch")
|
||||
|
||||
sortedNumbers := []int{1, 2, 3, 4, 5, 6, 7, 8}
|
||||
comparator := &intComparator{}
|
||||
|
||||
asssert.Equal(4, BinarySearch(sortedNumbers, 5, 0, len(sortedNumbers)-1, comparator))
|
||||
asssert.Equal(-1, BinarySearch(sortedNumbers, 9, 0, len(sortedNumbers)-1, comparator))
|
||||
}
|
||||
|
||||
func TestBinaryIterativeSearch(t *testing.T) {
|
||||
asssert := internal.NewAssert(t, "TestBinaryIterativeSearch")
|
||||
|
||||
sortedNumbers := []int{1, 2, 3, 4, 5, 6, 7, 8}
|
||||
comparator := &intComparator{}
|
||||
|
||||
asssert.Equal(4, BinaryIterativeSearch(sortedNumbers, 5, 0, len(sortedNumbers)-1, comparator))
|
||||
asssert.Equal(-1, BinaryIterativeSearch(sortedNumbers, 9, 0, len(sortedNumbers)-1, comparator))
|
||||
}
|
||||
204
algorithm/sort.go
Normal file
204
algorithm/sort.go
Normal file
@@ -0,0 +1,204 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
package algorithm
|
||||
|
||||
import "github.com/duke-git/lancet/v2/constraints"
|
||||
|
||||
// BubbleSort applys the bubble sort algorithm to sort the collection, will change the original collection data.
|
||||
// Play: https://go.dev/play/p/GNdv7Jg2Taj
|
||||
func BubbleSort[T any](slice []T, comparator constraints.Comparator) {
|
||||
for i := 0; i < len(slice); i++ {
|
||||
breakTag := false
|
||||
for j := 0; j < len(slice)-1-i; j++ {
|
||||
isCurrGreatThanNext := comparator.Compare(slice[j], slice[j+1]) == 1
|
||||
if isCurrGreatThanNext {
|
||||
swap(slice, j, j+1)
|
||||
breakTag = true
|
||||
}
|
||||
}
|
||||
if !breakTag {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// InsertionSort applys the insertion sort algorithm to sort the collection, will change the original collection data.
|
||||
// Play: https://go.dev/play/p/G5LJiWgJJW6
|
||||
func InsertionSort[T any](slice []T, comparator constraints.Comparator) {
|
||||
for i := 0; i < len(slice); i++ {
|
||||
for j := i; j > 0; j-- {
|
||||
isPreLessThanCurrent := comparator.Compare(slice[j], slice[j-1]) == -1
|
||||
if isPreLessThanCurrent {
|
||||
swap(slice, j, j-1)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SelectionSort applys the selection sort algorithm to sort the collection, will change the original collection data.
|
||||
// Play: https://go.dev/play/p/oXovbkekayS
|
||||
func SelectionSort[T any](slice []T, comparator constraints.Comparator) {
|
||||
for i := 0; i < len(slice); i++ {
|
||||
min := i
|
||||
for j := i + 1; j < len(slice); j++ {
|
||||
if comparator.Compare(slice[j], slice[min]) == -1 {
|
||||
min = j
|
||||
}
|
||||
}
|
||||
swap(slice, i, min)
|
||||
}
|
||||
}
|
||||
|
||||
// ShellSort applys the shell sort algorithm to sort the collection, will change the original collection data.
|
||||
// Play: https://go.dev/play/p/3ibkszpJEu3
|
||||
func ShellSort[T any](slice []T, comparator constraints.Comparator) {
|
||||
size := len(slice)
|
||||
|
||||
gap := 1
|
||||
for gap < size/3 {
|
||||
gap = 3*gap + 1
|
||||
}
|
||||
|
||||
for gap >= 1 {
|
||||
for i := gap; i < size; i++ {
|
||||
for j := i; j >= gap && comparator.Compare(slice[j], slice[j-gap]) == -1; j -= gap {
|
||||
swap(slice, j, j-gap)
|
||||
}
|
||||
}
|
||||
gap = gap / 3
|
||||
}
|
||||
}
|
||||
|
||||
// QuickSort quick sorting for slice, lowIndex is 0 and highIndex is len(slice)-1.
|
||||
// Play: https://go.dev/play/p/7Y7c1Elk3ax
|
||||
func QuickSort[T any](slice []T, comparator constraints.Comparator) {
|
||||
quickSort(slice, 0, len(slice)-1, comparator)
|
||||
}
|
||||
|
||||
func quickSort[T any](slice []T, lowIndex, highIndex int, comparator constraints.Comparator) {
|
||||
if lowIndex < highIndex {
|
||||
p := partition(slice, lowIndex, highIndex, comparator)
|
||||
quickSort(slice, lowIndex, p-1, comparator)
|
||||
quickSort(slice, p+1, highIndex, comparator)
|
||||
}
|
||||
}
|
||||
|
||||
// partition split slice into two parts
|
||||
func partition[T any](slice []T, lowIndex, highIndex int, comparator constraints.Comparator) int {
|
||||
p := slice[highIndex]
|
||||
i := lowIndex
|
||||
for j := lowIndex; j < highIndex; j++ {
|
||||
if comparator.Compare(slice[j], p) == -1 { //slice[j] < p
|
||||
swap(slice, i, j)
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
swap(slice, i, highIndex)
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
// HeapSort applys the heap sort algorithm to sort the collection, will change the original collection data.
|
||||
// Play: https://go.dev/play/p/u6Iwa1VZS_f
|
||||
func HeapSort[T any](slice []T, comparator constraints.Comparator) {
|
||||
size := len(slice)
|
||||
|
||||
for i := size/2 - 1; i >= 0; i-- {
|
||||
sift(slice, i, size-1, comparator)
|
||||
}
|
||||
for j := size - 1; j > 0; j-- {
|
||||
swap(slice, 0, j)
|
||||
sift(slice, 0, j-1, comparator)
|
||||
}
|
||||
}
|
||||
|
||||
func sift[T any](slice []T, lowIndex, highIndex int, comparator constraints.Comparator) {
|
||||
i := lowIndex
|
||||
j := 2*i + 1
|
||||
|
||||
temp := slice[i]
|
||||
for j <= highIndex {
|
||||
if j < highIndex && comparator.Compare(slice[j], slice[j+1]) == -1 { //slice[j] < slice[j+1]
|
||||
j++
|
||||
}
|
||||
if comparator.Compare(temp, slice[j]) == -1 { //tmp < slice[j]
|
||||
slice[i] = slice[j]
|
||||
i = j
|
||||
j = 2*i + 1
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
slice[i] = temp
|
||||
}
|
||||
|
||||
// MergeSort applys the merge sort algorithm to sort the collection, will change the original collection data.
|
||||
// Play: https://go.dev/play/p/ydinn9YzUJn
|
||||
func MergeSort[T any](slice []T, comparator constraints.Comparator) {
|
||||
mergeSort(slice, 0, len(slice)-1, comparator)
|
||||
}
|
||||
|
||||
func mergeSort[T any](slice []T, lowIndex, highIndex int, comparator constraints.Comparator) {
|
||||
if lowIndex < highIndex {
|
||||
mid := (lowIndex + highIndex) / 2
|
||||
mergeSort(slice, lowIndex, mid, comparator)
|
||||
mergeSort(slice, mid+1, highIndex, comparator)
|
||||
merge(slice, lowIndex, mid, highIndex, comparator)
|
||||
}
|
||||
}
|
||||
|
||||
func merge[T any](slice []T, lowIndex, midIndex, highIndex int, comparator constraints.Comparator) {
|
||||
i := lowIndex
|
||||
j := midIndex + 1
|
||||
temp := []T{}
|
||||
|
||||
for i <= midIndex && j <= highIndex {
|
||||
//slice[i] < slice[j]
|
||||
if comparator.Compare(slice[i], slice[j]) == -1 {
|
||||
temp = append(temp, slice[i])
|
||||
i++
|
||||
} else {
|
||||
temp = append(temp, slice[j])
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
if i <= midIndex {
|
||||
temp = append(temp, slice[i:midIndex+1]...)
|
||||
} else {
|
||||
temp = append(temp, slice[j:highIndex+1]...)
|
||||
}
|
||||
|
||||
for k := 0; k < len(temp); k++ {
|
||||
slice[lowIndex+k] = temp[k]
|
||||
}
|
||||
}
|
||||
|
||||
// CountSort applys the count sort algorithm to sort the collection, don't change the original collection data.
|
||||
// Play: https://go.dev/play/p/tB-Umgm0DrP
|
||||
func CountSort[T any](slice []T, comparator constraints.Comparator) []T {
|
||||
size := len(slice)
|
||||
out := make([]T, size)
|
||||
|
||||
for i := 0; i < size; i++ {
|
||||
count := 0
|
||||
for j := 0; j < size; j++ {
|
||||
//slice[i] > slice[j]
|
||||
if comparator.Compare(slice[i], slice[j]) == 1 {
|
||||
count++
|
||||
}
|
||||
}
|
||||
out[count] = slice[i]
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// swap two slice value at index i and j
|
||||
func swap[T any](slice []T, i, j int) {
|
||||
slice[i], slice[j] = slice[j], slice[i]
|
||||
}
|
||||
93
algorithm/sort_example_test.go
Normal file
93
algorithm/sort_example_test.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package algorithm
|
||||
|
||||
import "fmt"
|
||||
|
||||
func ExampleBubbleSort() {
|
||||
numbers := []int{2, 1, 5, 3, 6, 4}
|
||||
comparator := &intComparator{}
|
||||
|
||||
BubbleSort(numbers, comparator)
|
||||
|
||||
fmt.Println(numbers)
|
||||
// Output:
|
||||
// [1 2 3 4 5 6]
|
||||
}
|
||||
|
||||
func ExampleCountSort() {
|
||||
numbers := []int{2, 1, 5, 3, 6, 4}
|
||||
comparator := &intComparator{}
|
||||
|
||||
sortedNumber := CountSort(numbers, comparator)
|
||||
|
||||
fmt.Println(numbers)
|
||||
fmt.Println(sortedNumber)
|
||||
// Output:
|
||||
// [2 1 5 3 6 4]
|
||||
// [1 2 3 4 5 6]
|
||||
}
|
||||
|
||||
func ExampleHeapSort() {
|
||||
numbers := []int{2, 1, 5, 3, 6, 4}
|
||||
comparator := &intComparator{}
|
||||
|
||||
HeapSort(numbers, comparator)
|
||||
|
||||
fmt.Println(numbers)
|
||||
// Output:
|
||||
// [1 2 3 4 5 6]
|
||||
}
|
||||
|
||||
func ExampleMergeSort() {
|
||||
numbers := []int{2, 1, 5, 3, 6, 4}
|
||||
comparator := &intComparator{}
|
||||
|
||||
MergeSort(numbers, comparator)
|
||||
|
||||
fmt.Println(numbers)
|
||||
// Output:
|
||||
// [1 2 3 4 5 6]
|
||||
}
|
||||
|
||||
func ExampleInsertionSort() {
|
||||
numbers := []int{2, 1, 5, 3, 6, 4}
|
||||
comparator := &intComparator{}
|
||||
|
||||
InsertionSort(numbers, comparator)
|
||||
|
||||
fmt.Println(numbers)
|
||||
// Output:
|
||||
// [1 2 3 4 5 6]
|
||||
}
|
||||
|
||||
func ExampleSelectionSort() {
|
||||
numbers := []int{2, 1, 5, 3, 6, 4}
|
||||
comparator := &intComparator{}
|
||||
|
||||
SelectionSort(numbers, comparator)
|
||||
|
||||
fmt.Println(numbers)
|
||||
// Output:
|
||||
// [1 2 3 4 5 6]
|
||||
}
|
||||
|
||||
func ExampleShellSort() {
|
||||
numbers := []int{2, 1, 5, 3, 6, 4}
|
||||
comparator := &intComparator{}
|
||||
|
||||
ShellSort(numbers, comparator)
|
||||
|
||||
fmt.Println(numbers)
|
||||
// Output:
|
||||
// [1 2 3 4 5 6]
|
||||
}
|
||||
|
||||
func ExampleQuickSort() {
|
||||
numbers := []int{2, 1, 5, 3, 6, 4}
|
||||
comparator := &intComparator{}
|
||||
|
||||
QuickSort(numbers, comparator)
|
||||
|
||||
fmt.Println(numbers)
|
||||
// Output:
|
||||
// [1 2 3 4 5 6]
|
||||
}
|
||||
217
algorithm/sort_test.go
Normal file
217
algorithm/sort_test.go
Normal file
@@ -0,0 +1,217 @@
|
||||
package algorithm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
// People test mock data
|
||||
type people struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
// PeopleAageComparator sort people slice by age field
|
||||
type peopleAgeComparator struct{}
|
||||
|
||||
// Compare implements github.com/duke-git/lancet/v2/constraints/constraints.go/Comparator
|
||||
func (pc *peopleAgeComparator) Compare(v1 any, v2 any) int {
|
||||
p1, _ := v1.(people)
|
||||
p2, _ := v2.(people)
|
||||
|
||||
//ascending order
|
||||
if p1.Age < p2.Age {
|
||||
return -1
|
||||
} else if p1.Age > p2.Age {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1 any, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
//ascending order
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func TestBubbleSortForStructSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
asssert := internal.NewAssert(t, "TestBubbleSortForStructSlice")
|
||||
|
||||
peoples := []people{
|
||||
{Name: "a", Age: 20},
|
||||
{Name: "b", Age: 10},
|
||||
{Name: "c", Age: 17},
|
||||
{Name: "d", Age: 8},
|
||||
{Name: "e", Age: 28},
|
||||
}
|
||||
comparator := &peopleAgeComparator{}
|
||||
BubbleSort(peoples, comparator)
|
||||
|
||||
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
|
||||
actual := fmt.Sprintf("%v", peoples)
|
||||
|
||||
asssert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
func TestBubbleSortForIntSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
asssert := internal.NewAssert(t, "TestBubbleSortForIntSlice")
|
||||
|
||||
numbers := []int{2, 1, 5, 3, 6, 4}
|
||||
comparator := &intComparator{}
|
||||
BubbleSort(numbers, comparator)
|
||||
|
||||
asssert.Equal([]int{1, 2, 3, 4, 5, 6}, numbers)
|
||||
}
|
||||
|
||||
func TestInsertionSort(t *testing.T) {
|
||||
t.Parallel()
|
||||
asssert := internal.NewAssert(t, "TestInsertionSort")
|
||||
|
||||
peoples := []people{
|
||||
{Name: "a", Age: 20},
|
||||
{Name: "b", Age: 10},
|
||||
{Name: "c", Age: 17},
|
||||
{Name: "d", Age: 8},
|
||||
{Name: "e", Age: 28},
|
||||
}
|
||||
comparator := &peopleAgeComparator{}
|
||||
InsertionSort(peoples, comparator)
|
||||
|
||||
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
|
||||
actual := fmt.Sprintf("%v", peoples)
|
||||
|
||||
asssert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
func TestSelectionSort(t *testing.T) {
|
||||
t.Parallel()
|
||||
asssert := internal.NewAssert(t, "TestSelectionSort")
|
||||
|
||||
peoples := []people{
|
||||
{Name: "a", Age: 20},
|
||||
{Name: "b", Age: 10},
|
||||
{Name: "c", Age: 17},
|
||||
{Name: "d", Age: 8},
|
||||
{Name: "e", Age: 28},
|
||||
}
|
||||
comparator := &peopleAgeComparator{}
|
||||
SelectionSort(peoples, comparator)
|
||||
|
||||
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
|
||||
actual := fmt.Sprintf("%v", peoples)
|
||||
|
||||
asssert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
func TestShellSort(t *testing.T) {
|
||||
t.Parallel()
|
||||
asssert := internal.NewAssert(t, "TestShellSort")
|
||||
|
||||
peoples := []people{
|
||||
{Name: "a", Age: 20},
|
||||
{Name: "b", Age: 10},
|
||||
{Name: "c", Age: 17},
|
||||
{Name: "d", Age: 8},
|
||||
{Name: "e", Age: 28},
|
||||
}
|
||||
comparator := &peopleAgeComparator{}
|
||||
ShellSort(peoples, comparator)
|
||||
|
||||
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
|
||||
actual := fmt.Sprintf("%v", peoples)
|
||||
|
||||
asssert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
func TestQuickSort(t *testing.T) {
|
||||
t.Parallel()
|
||||
asssert := internal.NewAssert(t, "TestQuickSort")
|
||||
|
||||
peoples := []people{
|
||||
{Name: "a", Age: 20},
|
||||
{Name: "b", Age: 10},
|
||||
{Name: "c", Age: 17},
|
||||
{Name: "d", Age: 8},
|
||||
{Name: "e", Age: 28},
|
||||
}
|
||||
comparator := &peopleAgeComparator{}
|
||||
QuickSort(peoples, comparator)
|
||||
|
||||
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
|
||||
actual := fmt.Sprintf("%v", peoples)
|
||||
|
||||
asssert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
func TestHeapSort(t *testing.T) {
|
||||
t.Parallel()
|
||||
asssert := internal.NewAssert(t, "TestHeapSort")
|
||||
|
||||
peoples := []people{
|
||||
{Name: "a", Age: 20},
|
||||
{Name: "b", Age: 10},
|
||||
{Name: "c", Age: 17},
|
||||
{Name: "d", Age: 8},
|
||||
{Name: "e", Age: 28},
|
||||
}
|
||||
comparator := &peopleAgeComparator{}
|
||||
HeapSort(peoples, comparator)
|
||||
|
||||
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
|
||||
actual := fmt.Sprintf("%v", peoples)
|
||||
|
||||
asssert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
func TestMergeSort(t *testing.T) {
|
||||
t.Parallel()
|
||||
asssert := internal.NewAssert(t, "TestMergeSort")
|
||||
|
||||
peoples := []people{
|
||||
{Name: "a", Age: 20},
|
||||
{Name: "b", Age: 10},
|
||||
{Name: "c", Age: 17},
|
||||
{Name: "d", Age: 8},
|
||||
{Name: "e", Age: 28},
|
||||
}
|
||||
comparator := &peopleAgeComparator{}
|
||||
MergeSort(peoples, comparator)
|
||||
|
||||
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
|
||||
actual := fmt.Sprintf("%v", peoples)
|
||||
|
||||
asssert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
func TestCountSort(t *testing.T) {
|
||||
t.Parallel()
|
||||
asssert := internal.NewAssert(t, "TestCountSort")
|
||||
|
||||
peoples := []people{
|
||||
{Name: "a", Age: 20},
|
||||
{Name: "b", Age: 10},
|
||||
{Name: "c", Age: 17},
|
||||
{Name: "d", Age: 8},
|
||||
{Name: "e", Age: 28},
|
||||
}
|
||||
comparator := &peopleAgeComparator{}
|
||||
sortedPeopleByAge := CountSort(peoples, comparator)
|
||||
|
||||
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
|
||||
actual := fmt.Sprintf("%v", sortedPeopleByAge)
|
||||
|
||||
asssert.Equal(expected, actual)
|
||||
}
|
||||
72
compare/compare.go
Normal file
72
compare/compare.go
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright 2023 dudaodong@gmail.com. All rights resulterved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package compare provides a lightweight comparison function on any type.
|
||||
// reference: https://github.com/stretchr/testify
|
||||
package compare
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
// operator type
|
||||
const (
|
||||
equal = "eq"
|
||||
lessThan = "lt"
|
||||
greaterThan = "gt"
|
||||
lessOrEqual = "le"
|
||||
greaterOrEqual = "ge"
|
||||
)
|
||||
|
||||
var (
|
||||
timeType = reflect.TypeOf(time.Time{})
|
||||
bytesType = reflect.TypeOf([]byte{})
|
||||
)
|
||||
|
||||
// Equal checks if two values are equal or not. (check both type and value)
|
||||
// Play: https://go.dev/play/p/wmVxR-to4lz
|
||||
func Equal(left, right any) bool {
|
||||
return compareValue(equal, left, right)
|
||||
}
|
||||
|
||||
// EqualValue checks if two values are equal or not. (check value only)
|
||||
// Play: https://go.dev/play/p/fxnna_LLD9u
|
||||
func EqualValue(left, right any) bool {
|
||||
ls, rs := convertor.ToString(left), convertor.ToString(right)
|
||||
return ls == rs
|
||||
}
|
||||
|
||||
// LessThan checks if value `left` less than value `right`.
|
||||
// Play: https://go.dev/play/p/cYh7FQQj0ne
|
||||
func LessThan(left, right any) bool {
|
||||
return compareValue(lessThan, left, right)
|
||||
}
|
||||
|
||||
// GreaterThan checks if value `left` greater than value `right`.
|
||||
// Play: https://go.dev/play/p/9-NYDFZmIMp
|
||||
func GreaterThan(left, right any) bool {
|
||||
return compareValue(greaterThan, left, right)
|
||||
}
|
||||
|
||||
// LessOrEqual checks if value `left` less than or equal to value `right`.
|
||||
// Play: https://go.dev/play/p/e4T_scwoQzp
|
||||
func LessOrEqual(left, right any) bool {
|
||||
return compareValue(lessOrEqual, left, right)
|
||||
}
|
||||
|
||||
// GreaterOrEqual checks if value `left` greater than or equal to value `right`.
|
||||
// Play: https://go.dev/play/p/vx8mP0U8DFk
|
||||
func GreaterOrEqual(left, right any) bool {
|
||||
return compareValue(greaterOrEqual, left, right)
|
||||
}
|
||||
|
||||
// InDelta checks if two values are equal or not within a delta.
|
||||
// Play: https://go.dev/play/p/TuDdcNtMkjo
|
||||
func InDelta[T constraints.Integer | constraints.Float](left, right T, delta float64) bool {
|
||||
return float64(mathutil.Abs(left-right)) <= delta
|
||||
}
|
||||
196
compare/compare_example_test.go
Normal file
196
compare/compare_example_test.go
Normal file
@@ -0,0 +1,196 @@
|
||||
package compare
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ExampleEqual() {
|
||||
result1 := Equal(1, 1)
|
||||
result2 := Equal("1", "1")
|
||||
result3 := Equal([]int{1, 2, 3}, []int{1, 2, 3})
|
||||
result4 := Equal(map[int]string{1: "a", 2: "b"}, map[int]string{1: "a", 2: "b"})
|
||||
|
||||
result5 := Equal(1, "1")
|
||||
result6 := Equal(1, int64(1))
|
||||
result7 := Equal([]int{1, 2}, []int{1, 2, 3})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
fmt.Println(result7)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleEqualValue() {
|
||||
result1 := EqualValue(1, 1)
|
||||
result2 := EqualValue(int(1), int64(1))
|
||||
result3 := EqualValue(1, "1")
|
||||
result4 := EqualValue(1, "2")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleLessThan() {
|
||||
result1 := LessThan(1, 2)
|
||||
result2 := LessThan(1.1, 2.2)
|
||||
result3 := LessThan("a", "b")
|
||||
|
||||
time1 := time.Now()
|
||||
time2 := time1.Add(time.Second)
|
||||
result4 := LessThan(time1, time2)
|
||||
|
||||
result5 := LessThan(2, 1)
|
||||
result6 := LessThan(1, int64(2))
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleGreaterThan() {
|
||||
result1 := GreaterThan(2, 1)
|
||||
result2 := GreaterThan(2.2, 1.1)
|
||||
result3 := GreaterThan("b", "a")
|
||||
|
||||
time1 := time.Now()
|
||||
time2 := time1.Add(time.Second)
|
||||
result4 := GreaterThan(time2, time1)
|
||||
|
||||
result5 := GreaterThan(1, 2)
|
||||
result6 := GreaterThan(int64(2), 1)
|
||||
result7 := GreaterThan("b", "c")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
fmt.Println(result7)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleLessOrEqual() {
|
||||
result1 := LessOrEqual(1, 1)
|
||||
result2 := LessOrEqual(1.1, 2.2)
|
||||
result3 := LessOrEqual("a", "b")
|
||||
|
||||
time1 := time.Now()
|
||||
time2 := time1.Add(time.Second)
|
||||
result4 := LessOrEqual(time1, time2)
|
||||
|
||||
result5 := LessOrEqual(2, 1)
|
||||
result6 := LessOrEqual(1, int64(2))
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleGreaterOrEqual() {
|
||||
result1 := GreaterOrEqual(1, 1)
|
||||
result2 := GreaterOrEqual(2.2, 1.1)
|
||||
result3 := GreaterOrEqual("b", "b")
|
||||
|
||||
time1 := time.Now()
|
||||
time2 := time1.Add(time.Second)
|
||||
result4 := GreaterOrEqual(time2, time1)
|
||||
|
||||
result5 := GreaterOrEqual(1, 2)
|
||||
result6 := GreaterOrEqual(int64(2), 1)
|
||||
result7 := GreaterOrEqual("b", "c")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
fmt.Println(result7)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleInDelta() {
|
||||
result1 := InDelta(1, 1, 0)
|
||||
result2 := InDelta(1, 2, 0)
|
||||
|
||||
result3 := InDelta(2.0/3.0, 0.66667, 0.001)
|
||||
result4 := InDelta(2.0/3.0, 0.0, 0.001)
|
||||
|
||||
result5 := InDelta(float64(74.96)-float64(20.48), 54.48, 0)
|
||||
result6 := InDelta(float64(74.96)-float64(20.48), 54.48, 1e-14)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
// true
|
||||
}
|
||||
323
compare/compare_internal.go
Normal file
323
compare/compare_internal.go
Normal file
@@ -0,0 +1,323 @@
|
||||
package compare
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func compareValue(operator string, left, right any) bool {
|
||||
leftType, rightType := reflect.TypeOf(left), reflect.TypeOf(right)
|
||||
|
||||
if leftType.Kind() != rightType.Kind() {
|
||||
return false
|
||||
}
|
||||
|
||||
switch leftType.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||
reflect.Float32, reflect.Float64, reflect.Bool, reflect.String:
|
||||
return compareBasicValue(operator, left, right)
|
||||
|
||||
case reflect.Struct, reflect.Slice, reflect.Map:
|
||||
return compareRefValue(operator, left, right, leftType.Kind())
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func compareRefValue(operator string, leftObj, rightObj any, kind reflect.Kind) bool {
|
||||
leftVal, rightVal := reflect.ValueOf(leftObj), reflect.ValueOf(rightObj)
|
||||
|
||||
switch kind {
|
||||
case reflect.Struct:
|
||||
|
||||
// compare time
|
||||
if leftVal.CanConvert(timeType) {
|
||||
timeObj1, ok := leftObj.(time.Time)
|
||||
if !ok {
|
||||
timeObj1 = leftVal.Convert(timeType).Interface().(time.Time)
|
||||
}
|
||||
|
||||
timeObj2, ok := rightObj.(time.Time)
|
||||
if !ok {
|
||||
timeObj2 = rightVal.Convert(timeType).Interface().(time.Time)
|
||||
}
|
||||
|
||||
return compareBasicValue(operator, timeObj1.UnixNano(), timeObj2.UnixNano())
|
||||
}
|
||||
|
||||
// for other struct type, only process equal operator
|
||||
switch operator {
|
||||
case equal:
|
||||
return objectsAreEqualValues(leftObj, rightObj)
|
||||
}
|
||||
|
||||
case reflect.Slice:
|
||||
// compare []byte
|
||||
if leftVal.CanConvert(bytesType) {
|
||||
bytesObj1, ok := leftObj.([]byte)
|
||||
if !ok {
|
||||
bytesObj1 = leftVal.Convert(bytesType).Interface().([]byte)
|
||||
}
|
||||
bytesObj2, ok := rightObj.([]byte)
|
||||
if !ok {
|
||||
bytesObj2 = rightVal.Convert(bytesType).Interface().([]byte)
|
||||
}
|
||||
|
||||
switch operator {
|
||||
case equal:
|
||||
if bytes.Equal(bytesObj1, bytesObj2) {
|
||||
return true
|
||||
}
|
||||
case lessThan:
|
||||
if bytes.Compare(bytesObj1, bytesObj2) == -1 {
|
||||
return true
|
||||
}
|
||||
case greaterThan:
|
||||
if bytes.Compare(bytesObj1, bytesObj2) == 1 {
|
||||
return true
|
||||
}
|
||||
case lessOrEqual:
|
||||
if bytes.Compare(bytesObj1, bytesObj2) <= 0 {
|
||||
return true
|
||||
}
|
||||
case greaterOrEqual:
|
||||
if bytes.Compare(bytesObj1, bytesObj2) >= 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// for other type slice, only process equal operator
|
||||
switch operator {
|
||||
case equal:
|
||||
return reflect.DeepEqual(leftObj, rightObj)
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
// only process equal operator
|
||||
switch operator {
|
||||
case equal:
|
||||
return reflect.DeepEqual(leftObj, rightObj)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func objectsAreEqualValues(expected, actual interface{}) bool {
|
||||
if objectsAreEqual(expected, actual) {
|
||||
return true
|
||||
}
|
||||
|
||||
actualType := reflect.TypeOf(actual)
|
||||
if actualType == nil {
|
||||
return false
|
||||
}
|
||||
expectedValue := reflect.ValueOf(expected)
|
||||
if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) {
|
||||
// Attempt comparison after type conversion
|
||||
return reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), actual)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func objectsAreEqual(expected, actual interface{}) bool {
|
||||
if expected == nil || actual == nil {
|
||||
return expected == actual
|
||||
}
|
||||
|
||||
exp, ok := expected.([]byte)
|
||||
if !ok {
|
||||
return reflect.DeepEqual(expected, actual)
|
||||
}
|
||||
|
||||
act, ok := actual.([]byte)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if exp == nil || act == nil {
|
||||
return exp == nil && act == nil
|
||||
}
|
||||
return bytes.Equal(exp, act)
|
||||
}
|
||||
|
||||
// compareBasic compare basic value: integer, float, string, bool
|
||||
func compareBasicValue(operator string, leftValue, rightValue any) bool {
|
||||
if leftValue == nil && rightValue == nil && operator == equal {
|
||||
return true
|
||||
}
|
||||
|
||||
switch leftVal := leftValue.(type) {
|
||||
case json.Number:
|
||||
if left, err := leftVal.Float64(); err == nil {
|
||||
switch rightVal := rightValue.(type) {
|
||||
case json.Number:
|
||||
if right, err := rightVal.Float64(); err == nil {
|
||||
switch operator {
|
||||
case equal:
|
||||
if left == right {
|
||||
return true
|
||||
}
|
||||
case lessThan:
|
||||
if left < right {
|
||||
return true
|
||||
}
|
||||
case greaterThan:
|
||||
if left > right {
|
||||
return true
|
||||
}
|
||||
case lessOrEqual:
|
||||
if left <= right {
|
||||
return true
|
||||
}
|
||||
case greaterOrEqual:
|
||||
if left >= right {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case float32, float64, int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64:
|
||||
right, err := convertor.ToFloat(rightValue)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
switch operator {
|
||||
case equal:
|
||||
if left == right {
|
||||
return true
|
||||
}
|
||||
case lessThan:
|
||||
if left < right {
|
||||
return true
|
||||
}
|
||||
case greaterThan:
|
||||
if left > right {
|
||||
return true
|
||||
}
|
||||
case lessOrEqual:
|
||||
if left <= right {
|
||||
return true
|
||||
}
|
||||
case greaterOrEqual:
|
||||
if left >= right {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case float32, float64, int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64:
|
||||
left, err := convertor.ToFloat(leftValue)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
switch rightVal := rightValue.(type) {
|
||||
case json.Number:
|
||||
if right, err := rightVal.Float64(); err == nil {
|
||||
switch operator {
|
||||
case equal:
|
||||
if left == right {
|
||||
return true
|
||||
}
|
||||
case lessThan:
|
||||
if left < right {
|
||||
return true
|
||||
}
|
||||
case greaterThan:
|
||||
if left > right {
|
||||
return true
|
||||
}
|
||||
case lessOrEqual:
|
||||
if left <= right {
|
||||
return true
|
||||
}
|
||||
case greaterOrEqual:
|
||||
if left >= right {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
case float32, float64, int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64:
|
||||
right, err := convertor.ToFloat(rightValue)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch operator {
|
||||
case equal:
|
||||
if left == right {
|
||||
return true
|
||||
}
|
||||
case lessThan:
|
||||
if left < right {
|
||||
return true
|
||||
}
|
||||
case greaterThan:
|
||||
if left > right {
|
||||
return true
|
||||
}
|
||||
case lessOrEqual:
|
||||
if left <= right {
|
||||
return true
|
||||
}
|
||||
case greaterOrEqual:
|
||||
if left >= right {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case string:
|
||||
left := leftVal
|
||||
switch right := rightValue.(type) {
|
||||
case string:
|
||||
switch operator {
|
||||
case equal:
|
||||
if left == right {
|
||||
return true
|
||||
}
|
||||
case lessThan:
|
||||
if left < right {
|
||||
return true
|
||||
}
|
||||
case greaterThan:
|
||||
if left > right {
|
||||
return true
|
||||
}
|
||||
case lessOrEqual:
|
||||
if left <= right {
|
||||
return true
|
||||
}
|
||||
case greaterOrEqual:
|
||||
if left >= right {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case bool:
|
||||
left := leftVal
|
||||
switch right := rightValue.(type) {
|
||||
case bool:
|
||||
switch operator {
|
||||
case equal:
|
||||
if left == right {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
156
compare/compare_test.go
Normal file
156
compare/compare_test.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package compare
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestEqual(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestEqual")
|
||||
|
||||
assert.Equal(true, Equal(1, 1))
|
||||
assert.Equal(true, Equal(int64(1), int64(1)))
|
||||
assert.Equal(true, Equal("a", "a"))
|
||||
assert.Equal(true, Equal(true, true))
|
||||
assert.Equal(true, Equal([]int{1, 2, 3}, []int{1, 2, 3}))
|
||||
assert.Equal(true, Equal(map[int]string{1: "a", 2: "b"}, map[int]string{1: "a", 2: "b"}))
|
||||
|
||||
assert.Equal(false, Equal(1, 2))
|
||||
assert.Equal(false, Equal(1, int64(1)))
|
||||
assert.Equal(false, Equal("a", "b"))
|
||||
assert.Equal(false, Equal(true, false))
|
||||
assert.Equal(false, Equal([]int{1, 2}, []int{1, 2, 3}))
|
||||
assert.Equal(false, Equal(map[int]string{1: "a", 2: "b"}, map[int]string{1: "a"}))
|
||||
|
||||
time1 := time.Now()
|
||||
time2 := time1.Add(time.Second)
|
||||
time3 := time1.Add(time.Second)
|
||||
|
||||
assert.Equal(false, Equal(time1, time2))
|
||||
assert.Equal(true, Equal(time2, time3))
|
||||
|
||||
st1 := struct {
|
||||
A string
|
||||
B string
|
||||
}{
|
||||
A: "a",
|
||||
B: "b",
|
||||
}
|
||||
|
||||
st2 := struct {
|
||||
A string
|
||||
B string
|
||||
}{
|
||||
A: "a",
|
||||
B: "b",
|
||||
}
|
||||
|
||||
st3 := struct {
|
||||
A string
|
||||
B string
|
||||
}{
|
||||
A: "a1",
|
||||
B: "b",
|
||||
}
|
||||
|
||||
assert.Equal(true, Equal(st1, st2))
|
||||
assert.Equal(false, Equal(st1, st3))
|
||||
}
|
||||
|
||||
func TestEqualValue(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestEqualValue")
|
||||
|
||||
assert.Equal(true, EqualValue(1, 1))
|
||||
assert.Equal(true, EqualValue(int(1), int64(1)))
|
||||
assert.Equal(true, EqualValue(1, "1"))
|
||||
|
||||
assert.Equal(false, EqualValue(1, "2"))
|
||||
}
|
||||
|
||||
func TestLessThan(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestLessThan")
|
||||
|
||||
assert.Equal(true, LessThan(1, 2))
|
||||
assert.Equal(true, LessThan(1.1, 2.2))
|
||||
assert.Equal(true, LessThan("a", "b"))
|
||||
|
||||
time1 := time.Now()
|
||||
time2 := time1.Add(time.Second)
|
||||
assert.Equal(true, LessThan(time1, time2))
|
||||
|
||||
assert.Equal(false, LessThan(1, 1))
|
||||
assert.Equal(false, LessThan(1, int64(1)))
|
||||
}
|
||||
|
||||
func TestGreaterThan(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestGreaterThan")
|
||||
|
||||
assert.Equal(true, GreaterThan(2, 1))
|
||||
assert.Equal(true, GreaterThan(2.2, 1.1))
|
||||
assert.Equal(true, GreaterThan("b", "a"))
|
||||
|
||||
time1 := time.Now()
|
||||
time2 := time1.Add(time.Second)
|
||||
assert.Equal(true, GreaterThan(time2, time1))
|
||||
|
||||
assert.Equal(false, GreaterThan(1, 2))
|
||||
assert.Equal(false, GreaterThan(int64(2), 1))
|
||||
assert.Equal(false, GreaterThan("b", "c"))
|
||||
}
|
||||
|
||||
func TestLessOrEqual(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestLessOrEqual")
|
||||
|
||||
assert.Equal(true, LessOrEqual(1, 2))
|
||||
assert.Equal(true, LessOrEqual(1, 1))
|
||||
assert.Equal(true, LessOrEqual(1.1, 2.2))
|
||||
assert.Equal(true, LessOrEqual("a", "b"))
|
||||
|
||||
time1 := time.Now()
|
||||
time2 := time1.Add(time.Second)
|
||||
assert.Equal(true, LessOrEqual(time1, time2))
|
||||
|
||||
assert.Equal(false, LessOrEqual(2, 1))
|
||||
assert.Equal(false, LessOrEqual(1, int64(2)))
|
||||
}
|
||||
|
||||
func TestGreaterOrEqual(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestGreaterThan")
|
||||
|
||||
assert.Equal(true, GreaterOrEqual(2, 1))
|
||||
assert.Equal(true, GreaterOrEqual(1, 1))
|
||||
assert.Equal(true, GreaterOrEqual(2.2, 1.1))
|
||||
assert.Equal(true, GreaterOrEqual("b", "b"))
|
||||
|
||||
time1 := time.Now()
|
||||
time2 := time1.Add(time.Second)
|
||||
assert.Equal(true, GreaterOrEqual(time2, time1))
|
||||
|
||||
assert.Equal(false, GreaterOrEqual(1, 2))
|
||||
assert.Equal(false, GreaterOrEqual(int64(2), 1))
|
||||
assert.Equal(false, GreaterOrEqual("b", "c"))
|
||||
}
|
||||
|
||||
func TestInDelta(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestInDelta")
|
||||
|
||||
assert.Equal(true, InDelta(1, 1, 0))
|
||||
assert.Equal(false, InDelta(1, 2, 0))
|
||||
|
||||
assert.Equal(true, InDelta(2.0/3.0, 0.66667, 0.001))
|
||||
assert.Equal(false, InDelta(2.0/3.0, 0.0, 0.001))
|
||||
|
||||
assert.Equal(false, InDelta(float64(74.96)-float64(20.48), 54.48, 0))
|
||||
assert.Equal(true, InDelta(float64(74.96)-float64(20.48), 54.48, 1e-14))
|
||||
assert.Equal(false, InDelta(float64(float32(80.45)), float64(80.45), 0))
|
||||
assert.Equal(true, InDelta(float64(float32(80.45)), float64(80.45), 1e-5))
|
||||
}
|
||||
247
concurrency/channel.go
Normal file
247
concurrency/channel.go
Normal file
@@ -0,0 +1,247 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package concurrency contain some functions to support concurrent programming. eg, goroutine, channel.
|
||||
package concurrency
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Channel is a logic object which can generate or manipulate go channel
|
||||
// all methods of Channel are in the book tilted《Concurrency in Go》
|
||||
type Channel[T any] struct {
|
||||
}
|
||||
|
||||
// NewChannel return a Channel instance
|
||||
func NewChannel[T any]() *Channel[T] {
|
||||
return &Channel[T]{}
|
||||
}
|
||||
|
||||
// Generate creates channel, then put values into the channel.
|
||||
// Play: https://go.dev/play/p/7aB4KyMMp9A
|
||||
func (c *Channel[T]) Generate(ctx context.Context, values ...T) <-chan T {
|
||||
dataStream := make(chan T)
|
||||
|
||||
go func() {
|
||||
defer close(dataStream)
|
||||
|
||||
for _, v := range values {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case dataStream <- v:
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return dataStream
|
||||
}
|
||||
|
||||
// Repeat create channel, put values into the channel repeatly until cancel the context.
|
||||
// Play: https://go.dev/play/p/k5N_ALVmYjE
|
||||
func (c *Channel[T]) Repeat(ctx context.Context, values ...T) <-chan T {
|
||||
dataStream := make(chan T)
|
||||
|
||||
go func() {
|
||||
defer close(dataStream)
|
||||
for {
|
||||
for _, v := range values {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case dataStream <- v:
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return dataStream
|
||||
}
|
||||
|
||||
// RepeatFn create a channel, excutes fn repeatly, and put the result into the channel
|
||||
// until close context.
|
||||
// Play: https://go.dev/play/p/4J1zAWttP85
|
||||
func (c *Channel[T]) RepeatFn(ctx context.Context, fn func() T) <-chan T {
|
||||
dataStream := make(chan T)
|
||||
|
||||
go func() {
|
||||
defer close(dataStream)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case dataStream <- fn():
|
||||
}
|
||||
}
|
||||
}()
|
||||
return dataStream
|
||||
}
|
||||
|
||||
// Take create a channel whose values are taken from another channel with limit number.
|
||||
// Play: https://go.dev/play/p/9Utt-1pDr2J
|
||||
func (c *Channel[T]) Take(ctx context.Context, valueStream <-chan T, number int) <-chan T {
|
||||
takeStream := make(chan T)
|
||||
|
||||
go func() {
|
||||
defer close(takeStream)
|
||||
|
||||
for i := 0; i < number; i++ {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case takeStream <- <-valueStream:
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return takeStream
|
||||
}
|
||||
|
||||
// FanIn merge multiple channels into one channel.
|
||||
// Play: https://go.dev/play/p/2VYFMexEvTm
|
||||
func (c *Channel[T]) FanIn(ctx context.Context, channels ...<-chan T) <-chan T {
|
||||
out := make(chan T)
|
||||
|
||||
go func() {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(channels))
|
||||
|
||||
for _, c := range channels {
|
||||
go func(c <-chan T) {
|
||||
defer wg.Done()
|
||||
for v := range c {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case out <- v:
|
||||
}
|
||||
}
|
||||
}(c)
|
||||
}
|
||||
wg.Wait()
|
||||
close(out)
|
||||
}()
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// Tee split one chanel into two channels, until cancel the context.
|
||||
// Play: https://go.dev/play/p/3TQPKnCirrP
|
||||
func (c *Channel[T]) Tee(ctx context.Context, in <-chan T) (<-chan T, <-chan T) {
|
||||
out1 := make(chan T)
|
||||
out2 := make(chan T)
|
||||
|
||||
go func() {
|
||||
defer close(out1)
|
||||
defer close(out2)
|
||||
|
||||
for val := range c.OrDone(ctx, in) {
|
||||
var out1, out2 = out1, out2
|
||||
for i := 0; i < 2; i++ {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case out1 <- val:
|
||||
out1 = nil
|
||||
case out2 <- val:
|
||||
out2 = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return out1, out2
|
||||
}
|
||||
|
||||
// Bridge link multiply channels into one channel.
|
||||
// Play: https://go.dev/play/p/qmWSy1NVF-Y
|
||||
func (c *Channel[T]) Bridge(ctx context.Context, chanStream <-chan <-chan T) <-chan T {
|
||||
valStream := make(chan T)
|
||||
|
||||
go func() {
|
||||
defer close(valStream)
|
||||
|
||||
for {
|
||||
var stream <-chan T
|
||||
select {
|
||||
case maybeStream, ok := <-chanStream:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
stream = maybeStream
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
|
||||
for val := range c.OrDone(ctx, stream) {
|
||||
select {
|
||||
case valStream <- val:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return valStream
|
||||
}
|
||||
|
||||
// Or read one or more channels into one channel, will close when any readin channel is closed.
|
||||
// Play: https://go.dev/play/p/Wqz9rwioPww
|
||||
func (c *Channel[T]) Or(channels ...<-chan T) <-chan T {
|
||||
switch len(channels) {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
return channels[0]
|
||||
}
|
||||
|
||||
orDone := make(chan T)
|
||||
|
||||
go func() {
|
||||
defer close(orDone)
|
||||
|
||||
switch len(channels) {
|
||||
case 2:
|
||||
select {
|
||||
case <-channels[0]:
|
||||
case <-channels[1]:
|
||||
}
|
||||
default:
|
||||
select {
|
||||
case <-channels[0]:
|
||||
case <-channels[1]:
|
||||
case <-channels[2]:
|
||||
case <-c.Or(append(channels[3:], orDone)...):
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return orDone
|
||||
}
|
||||
|
||||
// OrDone read a channel into another channel, will close until cancel context.
|
||||
// Play: https://go.dev/play/p/lm_GoS6aDjo
|
||||
func (c *Channel[T]) OrDone(ctx context.Context, channel <-chan T) <-chan T {
|
||||
valStream := make(chan T)
|
||||
|
||||
go func() {
|
||||
defer close(valStream)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case v, ok := <-channel:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
select {
|
||||
case valStream <- v:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
return valStream
|
||||
}
|
||||
196
concurrency/channel_example_test.go
Normal file
196
concurrency/channel_example_test.go
Normal file
@@ -0,0 +1,196 @@
|
||||
package concurrency
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ExampleChannel_Generate() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
c := NewChannel[int]()
|
||||
intStream := c.Generate(ctx, 1, 2, 3)
|
||||
|
||||
fmt.Println(<-intStream)
|
||||
fmt.Println(<-intStream)
|
||||
fmt.Println(<-intStream)
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 2
|
||||
// 3
|
||||
}
|
||||
|
||||
func ExampleChannel_Repeat() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
c := NewChannel[int]()
|
||||
intStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 4)
|
||||
|
||||
for v := range intStream {
|
||||
fmt.Println(v)
|
||||
}
|
||||
// Output:
|
||||
// 1
|
||||
// 2
|
||||
// 1
|
||||
// 2
|
||||
}
|
||||
|
||||
func ExampleChannel_RepeatFn() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
fn := func() string {
|
||||
return "hello"
|
||||
}
|
||||
|
||||
c := NewChannel[string]()
|
||||
intStream := c.Take(ctx, c.RepeatFn(ctx, fn), 3)
|
||||
|
||||
for v := range intStream {
|
||||
fmt.Println(v)
|
||||
}
|
||||
// Output:
|
||||
// hello
|
||||
// hello
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleChannel_Take() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
numbers := make(chan int, 5)
|
||||
numbers <- 1
|
||||
numbers <- 2
|
||||
numbers <- 3
|
||||
numbers <- 4
|
||||
numbers <- 5
|
||||
defer close(numbers)
|
||||
|
||||
c := NewChannel[int]()
|
||||
intStream := c.Take(ctx, numbers, 3)
|
||||
|
||||
for v := range intStream {
|
||||
fmt.Println(v)
|
||||
}
|
||||
// Output:
|
||||
// 1
|
||||
// 2
|
||||
// 3
|
||||
}
|
||||
|
||||
func ExampleChannel_FanIn() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
c := NewChannel[int]()
|
||||
channels := make([]<-chan int, 2)
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
channels[i] = c.Take(ctx, c.Repeat(ctx, i), 2)
|
||||
}
|
||||
|
||||
chs := c.FanIn(ctx, channels...)
|
||||
|
||||
for v := range chs {
|
||||
fmt.Println(v) //1 1 0 0 or 0 0 1 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func ExampleChannel_Or() {
|
||||
sig := func(after time.Duration) <-chan any {
|
||||
c := make(chan any)
|
||||
go func() {
|
||||
defer close(c)
|
||||
time.Sleep(after)
|
||||
}()
|
||||
return c
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
|
||||
c := NewChannel[any]()
|
||||
<-c.Or(
|
||||
sig(1*time.Second),
|
||||
sig(2*time.Second),
|
||||
sig(3*time.Second),
|
||||
)
|
||||
|
||||
if time.Since(start).Seconds() < 2 {
|
||||
fmt.Println("ok")
|
||||
}
|
||||
// Output:
|
||||
// ok
|
||||
}
|
||||
|
||||
func ExampleChannel_OrDone() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
c := NewChannel[int]()
|
||||
intStream := c.Take(ctx, c.Repeat(ctx, 1), 3)
|
||||
|
||||
for v := range c.OrDone(ctx, intStream) {
|
||||
fmt.Println(v)
|
||||
}
|
||||
// Output:
|
||||
// 1
|
||||
// 1
|
||||
// 1
|
||||
}
|
||||
|
||||
func ExampleChannel_Tee() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
c := NewChannel[int]()
|
||||
intStream := c.Take(ctx, c.Repeat(ctx, 1), 2)
|
||||
|
||||
ch1, ch2 := c.Tee(ctx, intStream)
|
||||
|
||||
for v := range ch1 {
|
||||
fmt.Println(v)
|
||||
fmt.Println(<-ch2)
|
||||
}
|
||||
// Output:
|
||||
// 1
|
||||
// 1
|
||||
// 1
|
||||
// 1
|
||||
}
|
||||
|
||||
func ExampleChannel_Bridge() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
c := NewChannel[int]()
|
||||
genVals := func() <-chan <-chan int {
|
||||
out := make(chan (<-chan int))
|
||||
go func() {
|
||||
defer close(out)
|
||||
for i := 1; i <= 5; i++ {
|
||||
stream := make(chan int, 1)
|
||||
stream <- i
|
||||
close(stream)
|
||||
out <- stream
|
||||
}
|
||||
}()
|
||||
return out
|
||||
}
|
||||
|
||||
for v := range c.Bridge(ctx, genVals()) {
|
||||
fmt.Println(v)
|
||||
}
|
||||
// Output:
|
||||
// 1
|
||||
// 2
|
||||
// 3
|
||||
// 4
|
||||
// 5
|
||||
}
|
||||
196
concurrency/channel_test.go
Normal file
196
concurrency/channel_test.go
Normal file
@@ -0,0 +1,196 @@
|
||||
package concurrency
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestGenerate(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestGenerate")
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
c := NewChannel[int]()
|
||||
intStream := c.Generate(ctx, 1, 2, 3)
|
||||
|
||||
assert.Equal(1, <-intStream)
|
||||
assert.Equal(2, <-intStream)
|
||||
assert.Equal(3, <-intStream)
|
||||
}
|
||||
|
||||
func TestRepeat(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestRepeat")
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
c := NewChannel[int]()
|
||||
intStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 5)
|
||||
|
||||
assert.Equal(1, <-intStream)
|
||||
assert.Equal(2, <-intStream)
|
||||
assert.Equal(1, <-intStream)
|
||||
assert.Equal(2, <-intStream)
|
||||
assert.Equal(1, <-intStream)
|
||||
}
|
||||
|
||||
func TestRepeatFn(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestRepeatFn")
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
fn := func() string {
|
||||
s := "a"
|
||||
return s
|
||||
}
|
||||
c := NewChannel[string]()
|
||||
dataStream := c.Take(ctx, c.RepeatFn(ctx, fn), 3)
|
||||
|
||||
assert.Equal("a", <-dataStream)
|
||||
assert.Equal("a", <-dataStream)
|
||||
assert.Equal("a", <-dataStream)
|
||||
}
|
||||
|
||||
func TestTake(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestTake")
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
numbers := make(chan int, 5)
|
||||
numbers <- 1
|
||||
numbers <- 2
|
||||
numbers <- 3
|
||||
numbers <- 4
|
||||
numbers <- 5
|
||||
defer close(numbers)
|
||||
|
||||
c := NewChannel[int]()
|
||||
intStream := c.Take(ctx, numbers, 3)
|
||||
|
||||
assert.Equal(1, <-intStream)
|
||||
assert.Equal(2, <-intStream)
|
||||
assert.Equal(3, <-intStream)
|
||||
}
|
||||
|
||||
func TestFanIn(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestFanIn")
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
c := NewChannel[int]()
|
||||
channels := make([]<-chan int, 3)
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
channels[i] = c.Take(ctx, c.Repeat(ctx, i), 3)
|
||||
}
|
||||
|
||||
mergedChannel := c.FanIn(ctx, channels...)
|
||||
|
||||
for val := range mergedChannel {
|
||||
t.Logf("\t%d\n", val)
|
||||
}
|
||||
|
||||
assert.Equal(1, 1)
|
||||
}
|
||||
|
||||
func TestOr(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestOr")
|
||||
|
||||
sig := func(after time.Duration) <-chan any {
|
||||
c := make(chan interface{})
|
||||
go func() {
|
||||
defer close(c)
|
||||
time.Sleep(after)
|
||||
}()
|
||||
return c
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
|
||||
c := NewChannel[any]()
|
||||
<-c.Or(
|
||||
sig(1*time.Second),
|
||||
sig(2*time.Second),
|
||||
sig(3*time.Second),
|
||||
sig(4*time.Second),
|
||||
sig(5*time.Second),
|
||||
)
|
||||
|
||||
assert.Equal(true, time.Since(start).Seconds() < 2)
|
||||
}
|
||||
|
||||
func TestOrDone(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestOrDone")
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
c := NewChannel[int]()
|
||||
intStream := c.Take(ctx, c.Repeat(ctx, 1), 3)
|
||||
|
||||
for val := range c.OrDone(ctx, intStream) {
|
||||
assert.Equal(1, val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTee(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestTee")
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
c := NewChannel[int]()
|
||||
inStream := c.Take(ctx, c.Repeat(ctx, 1), 4)
|
||||
|
||||
out1, out2 := c.Tee(ctx, inStream)
|
||||
for val := range out1 {
|
||||
val1 := val
|
||||
val2 := <-out2
|
||||
assert.Equal(1, val1)
|
||||
assert.Equal(1, val2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBridge(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestBridge")
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
c := NewChannel[int]()
|
||||
genVals := func() <-chan <-chan int {
|
||||
chanStream := make(chan (<-chan int))
|
||||
go func() {
|
||||
defer close(chanStream)
|
||||
for i := 0; i < 10; i++ {
|
||||
stream := make(chan int, 1)
|
||||
stream <- i
|
||||
close(stream)
|
||||
chanStream <- stream
|
||||
}
|
||||
}()
|
||||
return chanStream
|
||||
}
|
||||
|
||||
index := 0
|
||||
for val := range c.Bridge(ctx, genVals()) {
|
||||
assert.Equal(index, val)
|
||||
index++
|
||||
}
|
||||
}
|
||||
85
condition/condition.go
Normal file
85
condition/condition.go
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package condition contains some functions for conditional judgment. eg. And, Or, TernaryOperator ...
|
||||
// The implementation of this package refers to the implementation of carlmjohnson's truthy package, you may find more
|
||||
// useful information in truthy(https://github.com/carlmjohnson/truthy), thanks carlmjohnson.
|
||||
package condition
|
||||
|
||||
import "reflect"
|
||||
|
||||
// Bool returns the truthy value of anything.
|
||||
// If the value's type has a Bool() bool method, the method is called and returned.
|
||||
// If the type has an IsZero() bool method, the opposite value is returned.
|
||||
// Slices and maps are truthy if they have a length greater than zero.
|
||||
// All other types are truthy if they are not their zero value.
|
||||
// Play: https://go.dev/play/p/ETzeDJRSvhm
|
||||
func Bool[T any](value T) bool {
|
||||
switch m := any(value).(type) {
|
||||
case interface{ Bool() bool }:
|
||||
return m.Bool()
|
||||
case interface{ IsZero() bool }:
|
||||
return !m.IsZero()
|
||||
}
|
||||
return reflectValue(&value)
|
||||
}
|
||||
|
||||
func reflectValue(vp any) bool {
|
||||
switch rv := reflect.ValueOf(vp).Elem(); rv.Kind() {
|
||||
case reflect.Map, reflect.Slice:
|
||||
return rv.Len() != 0
|
||||
default:
|
||||
is := rv.IsZero()
|
||||
return !is
|
||||
}
|
||||
}
|
||||
|
||||
// And returns true if both a and b are truthy.
|
||||
// Play: https://go.dev/play/p/W1SSUmt6pvr
|
||||
func And[T, U any](a T, b U) bool {
|
||||
return Bool(a) && Bool(b)
|
||||
}
|
||||
|
||||
// Or returns false if neither a nor b is truthy.
|
||||
// Play: https://go.dev/play/p/UlQTxHaeEkq
|
||||
func Or[T, U any](a T, b U) bool {
|
||||
return Bool(a) || Bool(b)
|
||||
}
|
||||
|
||||
// Xor returns true if a or b but not both is truthy.
|
||||
// Play: https://go.dev/play/p/gObZrW7ZbG8
|
||||
func Xor[T, U any](a T, b U) bool {
|
||||
valA := Bool(a)
|
||||
valB := Bool(b)
|
||||
return (valA || valB) && valA != valB
|
||||
}
|
||||
|
||||
// Nor returns true if neither a nor b is truthy.
|
||||
// Play: https://go.dev/play/p/g2j08F_zZky
|
||||
func Nor[T, U any](a T, b U) bool {
|
||||
return !(Bool(a) || Bool(b))
|
||||
}
|
||||
|
||||
// Xnor returns true if both a and b or neither a nor b are truthy.
|
||||
// Play: https://go.dev/play/p/OuDB9g51643
|
||||
func Xnor[T, U any](a T, b U) bool {
|
||||
valA := Bool(a)
|
||||
valB := Bool(b)
|
||||
return (valA && valB) || (!valA && !valB)
|
||||
}
|
||||
|
||||
// Nand returns false if both a and b are truthy.
|
||||
// Play: https://go.dev/play/p/vSRMLxLIbq8
|
||||
func Nand[T, U any](a T, b U) bool {
|
||||
return !Bool(a) || !Bool(b)
|
||||
}
|
||||
|
||||
// TernaryOperator checks the value of param `isTrue`, if true return ifValue else return elseValue.
|
||||
// Play: https://go.dev/play/p/ElllPZY0guT
|
||||
func TernaryOperator[T, U any](isTrue T, ifValue U, elseValue U) U {
|
||||
if Bool(isTrue) {
|
||||
return ifValue
|
||||
} else {
|
||||
return elseValue
|
||||
}
|
||||
}
|
||||
163
condition/condition_example_test.go
Normal file
163
condition/condition_example_test.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package condition
|
||||
|
||||
import "fmt"
|
||||
|
||||
func ExampleBool() {
|
||||
// bool
|
||||
result1 := Bool(false)
|
||||
result2 := Bool(true)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
|
||||
// integer
|
||||
result3 := Bool(0)
|
||||
result4 := Bool(1)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// string
|
||||
result5 := Bool("")
|
||||
result6 := Bool(" ")
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
|
||||
// slice
|
||||
var nums = []int{}
|
||||
result7 := Bool(nums)
|
||||
nums = append(nums, 1, 2)
|
||||
result8 := Bool(nums)
|
||||
fmt.Println(result7)
|
||||
fmt.Println(result8)
|
||||
|
||||
// Output:
|
||||
// false
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleAnd() {
|
||||
result1 := And(0, 1)
|
||||
result2 := And(0, "")
|
||||
result3 := And(0, "0")
|
||||
result4 := And(1, "0")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// false
|
||||
// false
|
||||
// false
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleOr() {
|
||||
result1 := Or(0, "")
|
||||
result2 := Or(0, 1)
|
||||
result3 := Or(0, "0")
|
||||
result4 := Or(1, "0")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// false
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleXor() {
|
||||
result1 := Xor(0, 0)
|
||||
result2 := Xor(1, 1)
|
||||
result3 := Xor(0, 1)
|
||||
result4 := Xor(1, 0)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// false
|
||||
// false
|
||||
// true
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleNor() {
|
||||
result1 := Nor(0, 0)
|
||||
result2 := Nor(1, 1)
|
||||
result3 := Nor(0, 1)
|
||||
result4 := Nor(1, 0)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleXnor() {
|
||||
result1 := Xnor(0, 0)
|
||||
result2 := Xnor(1, 1)
|
||||
result3 := Xnor(0, 1)
|
||||
result4 := Xnor(1, 0)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleNand() {
|
||||
result1 := Nand(0, 0)
|
||||
result2 := Nand(1, 0)
|
||||
result3 := Nand(0, 1)
|
||||
result4 := Nand(1, 1)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleTernaryOperator() {
|
||||
conditionTrue := 2 > 1
|
||||
result1 := TernaryOperator(conditionTrue, 0, 1)
|
||||
fmt.Println(result1)
|
||||
|
||||
conditionFalse := 2 > 3
|
||||
result2 := TernaryOperator(conditionFalse, 0, 1)
|
||||
fmt.Println(result2)
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
// 1
|
||||
}
|
||||
136
condition/condition_test.go
Normal file
136
condition/condition_test.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package condition
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
type TestStruct struct{}
|
||||
|
||||
func TestBool(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestBool")
|
||||
|
||||
// bool
|
||||
assert.Equal(false, Bool(false))
|
||||
assert.Equal(true, Bool(true))
|
||||
|
||||
// integer
|
||||
assert.Equal(false, Bool(0))
|
||||
assert.Equal(true, Bool(1))
|
||||
|
||||
// float
|
||||
assert.Equal(false, Bool(0.0))
|
||||
assert.Equal(true, Bool(0.1))
|
||||
|
||||
// string
|
||||
assert.Equal(false, Bool(""))
|
||||
assert.Equal(true, Bool(" "))
|
||||
assert.Equal(true, Bool("0"))
|
||||
|
||||
// slice
|
||||
var nums [2]int
|
||||
assert.Equal(false, Bool(nums))
|
||||
nums = [2]int{0, 1}
|
||||
assert.Equal(true, Bool(nums))
|
||||
|
||||
// map
|
||||
assert.Equal(false, Bool(map[string]string{}))
|
||||
assert.Equal(true, Bool(map[string]string{"a": "a"}))
|
||||
|
||||
// channel
|
||||
var ch chan int
|
||||
assert.Equal(false, Bool(ch))
|
||||
ch = make(chan int)
|
||||
assert.Equal(true, Bool(ch))
|
||||
|
||||
// interface
|
||||
var err error
|
||||
assert.Equal(false, Bool(err))
|
||||
err = errors.New("error message")
|
||||
assert.Equal(true, Bool(err))
|
||||
|
||||
// struct
|
||||
assert.Equal(false, Bool(struct{}{}))
|
||||
assert.Equal(true, Bool(time.Now()))
|
||||
|
||||
// struct pointer
|
||||
ts := TestStruct{}
|
||||
assert.Equal(false, Bool(ts))
|
||||
assert.Equal(true, Bool(&ts))
|
||||
}
|
||||
|
||||
func TestAnd(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestAnd")
|
||||
assert.Equal(false, And(0, 1))
|
||||
assert.Equal(false, And(0, ""))
|
||||
assert.Equal(false, And(0, "0"))
|
||||
assert.Equal(true, And(1, "0"))
|
||||
}
|
||||
|
||||
func TestOr(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestOr")
|
||||
assert.Equal(false, Or(0, ""))
|
||||
assert.Equal(true, Or(0, 1))
|
||||
assert.Equal(true, Or(0, "0"))
|
||||
assert.Equal(true, Or(1, "0"))
|
||||
}
|
||||
|
||||
func TestXor(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestOr")
|
||||
assert.Equal(false, Xor(0, 0))
|
||||
assert.Equal(true, Xor(0, 1))
|
||||
assert.Equal(true, Xor(1, 0))
|
||||
assert.Equal(false, Xor(1, 1))
|
||||
}
|
||||
|
||||
func TestNor(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestNor")
|
||||
assert.Equal(true, Nor(0, 0))
|
||||
assert.Equal(false, Nor(0, 1))
|
||||
assert.Equal(false, Nor(1, 0))
|
||||
assert.Equal(false, Nor(1, 1))
|
||||
}
|
||||
|
||||
func TestXnor(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestXnor")
|
||||
assert.Equal(true, Xnor(0, 0))
|
||||
assert.Equal(false, Xnor(0, 1))
|
||||
assert.Equal(false, Xnor(1, 0))
|
||||
assert.Equal(true, Xnor(1, 1))
|
||||
}
|
||||
|
||||
func TestNand(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestNand")
|
||||
assert.Equal(true, Nand(0, 0))
|
||||
assert.Equal(true, Nand(0, 1))
|
||||
assert.Equal(true, Nand(1, 0))
|
||||
assert.Equal(false, Nand(1, 1))
|
||||
}
|
||||
|
||||
func TestTernaryOperator(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TernaryOperator")
|
||||
|
||||
trueValue := "1"
|
||||
falseValue := "0"
|
||||
|
||||
assert.Equal(trueValue, TernaryOperator(true, trueValue, falseValue))
|
||||
}
|
||||
13
constraints/constraints.go
Normal file
13
constraints/constraints.go
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package constraints contain some custom interface.
|
||||
package constraints
|
||||
|
||||
// Comparator is for comparing two values
|
||||
type Comparator interface {
|
||||
// Compare v1 and v2
|
||||
// Ascending order: should return 1 -> v1 > v2, 0 -> v1 = v2, -1 -> v1 < v2
|
||||
// Descending order: should return 1 -> v1 < v2, 0 -> v1 = v2, -1 -> v1 > v2
|
||||
Compare(v1, v2 any) int
|
||||
}
|
||||
@@ -9,21 +9,28 @@ import (
|
||||
"encoding/binary"
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// ToBool convert string to a boolean
|
||||
// ToBool convert string to boolean.
|
||||
// Play: https://go.dev/play/p/ARht2WnGdIN
|
||||
func ToBool(s string) (bool, error) {
|
||||
return strconv.ParseBool(s)
|
||||
}
|
||||
|
||||
// ToBytes convert interface to bytes
|
||||
func ToBytes(value interface{}) ([]byte, error) {
|
||||
// ToBytes convert value to byte slice.
|
||||
// Play: https://go.dev/play/p/fAMXYFDvOvr
|
||||
func ToBytes(value any) ([]byte, error) {
|
||||
v := reflect.ValueOf(value)
|
||||
|
||||
switch value.(type) {
|
||||
@@ -63,7 +70,8 @@ func ToBytes(value interface{}) ([]byte, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// ToChar convert string to char slice
|
||||
// ToChar convert string to char slice.
|
||||
// Play: https://go.dev/play/p/JJ1SvbFkVdM
|
||||
func ToChar(s string) []string {
|
||||
c := make([]string, 0)
|
||||
if len(s) == 0 {
|
||||
@@ -75,133 +83,179 @@ func ToChar(s string) []string {
|
||||
return c
|
||||
}
|
||||
|
||||
// ToChannel convert a slice of elements to a read-only channel.
|
||||
// Play: https://go.dev/play/p/hOx_oYZbAnL
|
||||
func ToChannel[T any](array []T) <-chan T {
|
||||
ch := make(chan T)
|
||||
|
||||
go func() {
|
||||
for _, item := range array {
|
||||
ch <- item
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
// ToString convert value to string
|
||||
func ToString(value interface{}) string {
|
||||
res := ""
|
||||
// for number, string, []byte, will convert to string
|
||||
// for other type (slice, map, array, struct) will call json.Marshal.
|
||||
// Play: https://go.dev/play/p/nF1zOOslpQq
|
||||
func ToString(value any) string {
|
||||
if value == nil {
|
||||
return res
|
||||
return ""
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(value)
|
||||
|
||||
switch value.(type) {
|
||||
case float32, float64:
|
||||
res = strconv.FormatFloat(v.Float(), 'f', -1, 64)
|
||||
return res
|
||||
case int, int8, int16, int32, int64:
|
||||
res = strconv.FormatInt(v.Int(), 10)
|
||||
return res
|
||||
case uint, uint8, uint16, uint32, uint64:
|
||||
res = strconv.FormatUint(v.Uint(), 10)
|
||||
return res
|
||||
switch val := value.(type) {
|
||||
case float32:
|
||||
return strconv.FormatFloat(float64(val), 'f', -1, 32)
|
||||
case float64:
|
||||
return strconv.FormatFloat(val, 'f', -1, 64)
|
||||
case int:
|
||||
return strconv.FormatInt(int64(val), 10)
|
||||
case int8:
|
||||
return strconv.FormatInt(int64(val), 10)
|
||||
case int16:
|
||||
return strconv.FormatInt(int64(val), 10)
|
||||
case int32:
|
||||
return strconv.FormatInt(int64(val), 10)
|
||||
case int64:
|
||||
return strconv.FormatInt(val, 10)
|
||||
case uint:
|
||||
return strconv.FormatUint(uint64(val), 10)
|
||||
case uint8:
|
||||
return strconv.FormatUint(uint64(val), 10)
|
||||
case uint16:
|
||||
return strconv.FormatUint(uint64(val), 10)
|
||||
case uint32:
|
||||
return strconv.FormatUint(uint64(val), 10)
|
||||
case uint64:
|
||||
return strconv.FormatUint(val, 10)
|
||||
case string:
|
||||
res = v.String()
|
||||
return res
|
||||
return val
|
||||
case []byte:
|
||||
res = string(v.Bytes())
|
||||
return res
|
||||
return string(val)
|
||||
default:
|
||||
newValue, _ := json.Marshal(value)
|
||||
res = string(newValue)
|
||||
return res
|
||||
b, err := json.Marshal(val)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(b)
|
||||
|
||||
// todo: maybe we should't supprt other type conversion
|
||||
// v := reflect.ValueOf(value)
|
||||
// log.Panicf("Unsupported data type: %s ", v.String())
|
||||
// return ""
|
||||
}
|
||||
}
|
||||
|
||||
// ToJson convert value to a valid json string
|
||||
func ToJson(value interface{}) (string, error) {
|
||||
res, err := json.Marshal(value)
|
||||
// ToJson convert value to a json string.
|
||||
// Play: https://go.dev/play/p/2rLIkMmXWvR
|
||||
func ToJson(value any) (string, error) {
|
||||
result, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(res), nil
|
||||
return string(result), nil
|
||||
}
|
||||
|
||||
// ToFloat convert value to a float64, if input is not a float return 0.0 and error
|
||||
func ToFloat(value interface{}) (float64, error) {
|
||||
// ToFloat convert value to float64, if input is not a float return 0.0 and error.
|
||||
// Play: https://go.dev/play/p/4YTmPCibqHJ
|
||||
func ToFloat(value any) (float64, error) {
|
||||
v := reflect.ValueOf(value)
|
||||
|
||||
res := 0.0
|
||||
result := 0.0
|
||||
err := fmt.Errorf("ToInt: unvalid interface type %T", value)
|
||||
switch value.(type) {
|
||||
case int, int8, int16, int32, int64:
|
||||
res = float64(v.Int())
|
||||
return res, nil
|
||||
result = float64(v.Int())
|
||||
return result, nil
|
||||
case uint, uint8, uint16, uint32, uint64:
|
||||
res = float64(v.Uint())
|
||||
return res, nil
|
||||
result = float64(v.Uint())
|
||||
return result, nil
|
||||
case float32, float64:
|
||||
res = v.Float()
|
||||
return res, nil
|
||||
result = v.Float()
|
||||
return result, nil
|
||||
case string:
|
||||
res, err = strconv.ParseFloat(v.String(), 64)
|
||||
result, err = strconv.ParseFloat(v.String(), 64)
|
||||
if err != nil {
|
||||
res = 0.0
|
||||
result = 0.0
|
||||
}
|
||||
return res, err
|
||||
return result, err
|
||||
default:
|
||||
return res, err
|
||||
return result, err
|
||||
}
|
||||
}
|
||||
|
||||
// ToInt convert value to a int64, if input is not a numeric format return 0 and error
|
||||
func ToInt(value interface{}) (int64, error) {
|
||||
// ToInt convert value to int64 value, if input is not numerical, return 0 and error.
|
||||
// Play: https://go.dev/play/p/9_h9vIt-QZ_b
|
||||
func ToInt(value any) (int64, error) {
|
||||
v := reflect.ValueOf(value)
|
||||
|
||||
var res int64
|
||||
err := fmt.Errorf("ToInt: invalid interface type %T", value)
|
||||
var result int64
|
||||
err := fmt.Errorf("ToInt: invalid value type %T", value)
|
||||
switch value.(type) {
|
||||
case int, int8, int16, int32, int64:
|
||||
res = v.Int()
|
||||
return res, nil
|
||||
result = v.Int()
|
||||
return result, nil
|
||||
case uint, uint8, uint16, uint32, uint64:
|
||||
res = int64(v.Uint())
|
||||
return res, nil
|
||||
result = int64(v.Uint())
|
||||
return result, nil
|
||||
case float32, float64:
|
||||
res = int64(v.Float())
|
||||
return res, nil
|
||||
result = int64(v.Float())
|
||||
return result, nil
|
||||
case string:
|
||||
res, err = strconv.ParseInt(v.String(), 0, 64)
|
||||
result, err = strconv.ParseInt(v.String(), 0, 64)
|
||||
if err != nil {
|
||||
res = 0
|
||||
result = 0
|
||||
}
|
||||
return res, err
|
||||
return result, err
|
||||
default:
|
||||
return res, err
|
||||
return result, err
|
||||
}
|
||||
}
|
||||
|
||||
// ToPointer returns a pointer to passed value.
|
||||
// Play: https://go.dev/play/p/ASf_etHNlw1
|
||||
func ToPointer[T any](value T) *T {
|
||||
return &value
|
||||
}
|
||||
|
||||
// ToMap convert a slice of structs to a map based on iteratee function.
|
||||
// Play: https://go.dev/play/p/tVFy7E-t24l
|
||||
func ToMap[T any, K comparable, V any](array []T, iteratee func(T) (K, V)) map[K]V {
|
||||
result := make(map[K]V, len(array))
|
||||
for _, item := range array {
|
||||
k, v := iteratee(item)
|
||||
result[k] = v
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// StructToMap convert struct to map, only convert exported struct field
|
||||
// map key is specified same as struct field tag `json` value
|
||||
func StructToMap(value interface{}) (map[string]interface{}, error) {
|
||||
v := reflect.ValueOf(value)
|
||||
t := reflect.TypeOf(value)
|
||||
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
if t.Kind() != reflect.Struct {
|
||||
return nil, fmt.Errorf("data type %T not support, shuld be struct or pointer to struct", value)
|
||||
}
|
||||
|
||||
res := make(map[string]interface{})
|
||||
|
||||
fieldNum := t.NumField()
|
||||
pattern := `^[A-Z]`
|
||||
regex := regexp.MustCompile(pattern)
|
||||
for i := 0; i < fieldNum; i++ {
|
||||
name := t.Field(i).Name
|
||||
tag := t.Field(i).Tag.Get("json")
|
||||
if regex.MatchString(name) && tag != "" {
|
||||
//res[name] = v.Field(i).Interface()
|
||||
res[tag] = v.Field(i).Interface()
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
// map key is specified same as struct field tag `json` value.
|
||||
// Play: https://go.dev/play/p/KYGYJqNUBOI
|
||||
func StructToMap(value any) (map[string]any, error) {
|
||||
return structs.ToMap(value)
|
||||
}
|
||||
|
||||
// ColorHexToRGB convert hex color to rgb color
|
||||
// MapToSlice convert map to slice based on iteratee function.
|
||||
// Play: https://go.dev/play/p/dmX4Ix5V6Wl
|
||||
func MapToSlice[T any, K comparable, V any](aMap map[K]V, iteratee func(K, V) T) []T {
|
||||
result := make([]T, 0, len(aMap))
|
||||
|
||||
for k, v := range aMap {
|
||||
result = append(result, iteratee(k, v))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// ColorHexToRGB convert hex color to rgb color.
|
||||
// Play: https://go.dev/play/p/o7_ft-JCJBV
|
||||
func ColorHexToRGB(colorHex string) (red, green, blue int) {
|
||||
colorHex = strings.TrimPrefix(colorHex, "#")
|
||||
color64, err := strconv.ParseInt(colorHex, 16, 32)
|
||||
@@ -212,7 +266,8 @@ func ColorHexToRGB(colorHex string) (red, green, blue int) {
|
||||
return color >> 16, (color & 0x00FF00) >> 8, color & 0x0000FF
|
||||
}
|
||||
|
||||
// ColorRGBToHex convert rgb color to hex color
|
||||
// ColorRGBToHex convert rgb color to hex color.
|
||||
// Play: https://go.dev/play/p/nzKS2Ro87J1
|
||||
func ColorRGBToHex(red, green, blue int) string {
|
||||
r := strconv.FormatInt(int64(red), 16)
|
||||
g := strconv.FormatInt(int64(green), 16)
|
||||
@@ -231,22 +286,9 @@ func ColorRGBToHex(red, green, blue int) string {
|
||||
return "#" + r + g + b
|
||||
}
|
||||
|
||||
// ToChannel convert a array of elements to a read-only channels
|
||||
func ToChannel(array []interface{}) <-chan interface{} {
|
||||
ch := make(chan interface{})
|
||||
|
||||
go func() {
|
||||
for _, item := range array {
|
||||
ch <- item
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
// EncodeByte encode data to byte
|
||||
func EncodeByte(data interface{}) ([]byte, error) {
|
||||
// EncodeByte encode data to byte slice.
|
||||
// Play: https://go.dev/play/p/DVmM1G5JfuP
|
||||
func EncodeByte(data any) ([]byte, error) {
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
encoder := gob.NewEncoder(buffer)
|
||||
err := encoder.Encode(data)
|
||||
@@ -256,9 +298,99 @@ func EncodeByte(data interface{}) ([]byte, error) {
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// DecodeByte decode byte data to target object
|
||||
func DecodeByte(data []byte, target interface{}) error {
|
||||
// DecodeByte decode byte slice data to target object.
|
||||
// Play: https://go.dev/play/p/zI6xsmuQRbn
|
||||
func DecodeByte(data []byte, target any) error {
|
||||
buffer := bytes.NewBuffer(data)
|
||||
decoder := gob.NewDecoder(buffer)
|
||||
return decoder.Decode(target)
|
||||
}
|
||||
|
||||
// DeepClone creates a deep copy of passed item.
|
||||
// can't clone unexported field of struct
|
||||
// Play: https://go.dev/play/p/j4DP5dquxnk
|
||||
func DeepClone[T any](src T) T {
|
||||
c := cloner{
|
||||
ptrs: map[reflect.Type]map[uintptr]reflect.Value{},
|
||||
}
|
||||
result := c.clone(reflect.ValueOf(src))
|
||||
if result.Kind() == reflect.Invalid {
|
||||
var zeroValue T
|
||||
return zeroValue
|
||||
}
|
||||
|
||||
return result.Interface().(T)
|
||||
}
|
||||
|
||||
// CopyProperties copies each field from the source into the destination. It recursively copies struct pointers and interfaces that contain struct pointers.
|
||||
// use json.Marshal/Unmarshal, so json tag should be set for fields of dst and src struct.
|
||||
// Play: https://go.dev/play/p/oZujoB5Sgg5
|
||||
func CopyProperties[T, U any](dst T, src U) error {
|
||||
dstType, srcType := reflect.TypeOf(dst), reflect.TypeOf(src)
|
||||
|
||||
if dstType.Kind() != reflect.Ptr || dstType.Elem().Kind() != reflect.Struct {
|
||||
return errors.New("CopyProperties: parameter dst should be struct pointer")
|
||||
}
|
||||
|
||||
if srcType.Kind() == reflect.Ptr {
|
||||
srcType = srcType.Elem()
|
||||
}
|
||||
if srcType.Kind() != reflect.Struct {
|
||||
return errors.New("CopyProperties: parameter src should be a struct or struct pointer")
|
||||
}
|
||||
|
||||
bytes, err := json.Marshal(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CopyProperties: unable to marshal src: %s", err)
|
||||
}
|
||||
err = json.Unmarshal(bytes, dst)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CopyProperties: unable to unmarshal into dst: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToInterface converts reflect value to its interface type.
|
||||
// Play: https://go.dev/play/p/syqw0-WG7Xd
|
||||
func ToInterface(v reflect.Value) (value interface{}, ok bool) {
|
||||
if v.IsValid() && v.CanInterface() {
|
||||
return v.Interface(), true
|
||||
}
|
||||
switch v.Kind() {
|
||||
case reflect.Bool:
|
||||
return v.Bool(), true
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int(), true
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint(), true
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float(), true
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
return v.Complex(), true
|
||||
case reflect.String:
|
||||
return v.String(), true
|
||||
case reflect.Ptr:
|
||||
return ToInterface(v.Elem())
|
||||
case reflect.Interface:
|
||||
return ToInterface(v.Elem())
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
// Utf8ToGbk convert utf8 encoding data to GBK encoding data.
|
||||
// Play: https://go.dev/play/p/9FlIaFLArIL
|
||||
func Utf8ToGbk(bs []byte) ([]byte, error) {
|
||||
r := transform.NewReader(bytes.NewReader(bs), simplifiedchinese.GBK.NewEncoder())
|
||||
b, err := io.ReadAll(r)
|
||||
return b, err
|
||||
}
|
||||
|
||||
// GbkToUtf8 convert GBK encoding data to utf8 encoding data.
|
||||
// Play: https://go.dev/play/p/OphmHCN_9u8
|
||||
func GbkToUtf8(bs []byte) ([]byte, error) {
|
||||
r := transform.NewReader(bytes.NewReader(bs), simplifiedchinese.GBK.NewDecoder())
|
||||
b, err := io.ReadAll(r)
|
||||
return b, err
|
||||
}
|
||||
|
||||
393
convertor/convertor_example_test.go
Normal file
393
convertor/convertor_example_test.go
Normal file
@@ -0,0 +1,393 @@
|
||||
package convertor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/duke-git/lancet/v2/validator"
|
||||
)
|
||||
|
||||
func ExampleToBool() {
|
||||
cases := []string{"1", "true", "True", "false", "False", "0", "123", "0.0", "abc"}
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
result, _ := ToBool(cases[i])
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
// false
|
||||
// false
|
||||
// false
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleToBytes() {
|
||||
result1, _ := ToBytes(1)
|
||||
result2, _ := ToBytes("abc")
|
||||
result3, _ := ToBytes(true)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// [0 0 0 0 0 0 0 1]
|
||||
// [97 98 99]
|
||||
// [116 114 117 101]
|
||||
}
|
||||
|
||||
func ExampleToChar() {
|
||||
result1 := ToChar("")
|
||||
result2 := ToChar("abc")
|
||||
result3 := ToChar("1 2#3")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// []
|
||||
// [a b c]
|
||||
// [1 2 # 3]
|
||||
}
|
||||
|
||||
func ExampleToChannel() {
|
||||
ch := ToChannel([]int{1, 2, 3})
|
||||
result1 := <-ch
|
||||
result2 := <-ch
|
||||
result3 := <-ch
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 2
|
||||
// 3
|
||||
}
|
||||
|
||||
func ExampleToString() {
|
||||
result1 := ToString("")
|
||||
result2 := ToString(nil)
|
||||
result3 := ToString(0)
|
||||
result4 := ToString(1.23)
|
||||
result5 := ToString(true)
|
||||
result6 := ToString(false)
|
||||
result7 := ToString([]int{1, 2, 3})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
fmt.Println(result7)
|
||||
|
||||
// Output:
|
||||
//
|
||||
//
|
||||
// 0
|
||||
// 1.23
|
||||
// true
|
||||
// false
|
||||
// [1,2,3]
|
||||
}
|
||||
|
||||
func ExampleToJson() {
|
||||
|
||||
aMap := map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
result1, err := ToJson(aMap)
|
||||
if err != nil {
|
||||
fmt.Printf("%v", err)
|
||||
}
|
||||
fmt.Println(result1)
|
||||
|
||||
// Output:
|
||||
// {"a":1,"b":2,"c":3}
|
||||
}
|
||||
|
||||
func ExampleToFloat() {
|
||||
result1, _ := ToFloat("")
|
||||
result2, _ := ToFloat("abc")
|
||||
result3, _ := ToFloat("-1")
|
||||
result4, _ := ToFloat("-.11")
|
||||
result5, _ := ToFloat("1.23e3")
|
||||
result6, _ := ToFloat(true)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
// 0
|
||||
// -1
|
||||
// -0.11
|
||||
// 1230
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleToInt() {
|
||||
result1, _ := ToInt("123")
|
||||
result2, _ := ToInt("-123")
|
||||
result3, _ := ToInt(float64(12.3))
|
||||
result4, _ := ToInt("abc")
|
||||
result5, _ := ToInt(true)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
|
||||
// Output:
|
||||
// 123
|
||||
// -123
|
||||
// 12
|
||||
// 0
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleToPointer() {
|
||||
result := ToPointer(123)
|
||||
fmt.Println(*result)
|
||||
|
||||
// Output:
|
||||
// 123
|
||||
}
|
||||
|
||||
func ExampleToMap() {
|
||||
type Message struct {
|
||||
name string
|
||||
code int
|
||||
}
|
||||
messages := []Message{
|
||||
{name: "Hello", code: 100},
|
||||
{name: "Hi", code: 101},
|
||||
}
|
||||
result := ToMap(messages, func(msg Message) (int, string) {
|
||||
return msg.code, msg.name
|
||||
})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[100:Hello 101:Hi]
|
||||
}
|
||||
|
||||
func ExampleStructToMap() {
|
||||
type People struct {
|
||||
Name string `json:"name"`
|
||||
age int
|
||||
}
|
||||
p := People{
|
||||
"test",
|
||||
100,
|
||||
}
|
||||
pm, _ := StructToMap(p)
|
||||
|
||||
fmt.Println(pm)
|
||||
|
||||
// Output:
|
||||
// map[name:test]
|
||||
}
|
||||
|
||||
func ExampleMapToSlice() {
|
||||
aMap := map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
result := MapToSlice(aMap, func(key string, value int) string {
|
||||
return key + ":" + strconv.Itoa(value)
|
||||
})
|
||||
|
||||
fmt.Println(result) //[]string{"a:1", "c:3", "b:2"} (random order)
|
||||
}
|
||||
|
||||
func ExampleColorHexToRGB() {
|
||||
colorHex := "#003366"
|
||||
r, g, b := ColorHexToRGB(colorHex)
|
||||
|
||||
fmt.Println(r, g, b)
|
||||
|
||||
// Output:
|
||||
// 0 51 102
|
||||
}
|
||||
|
||||
func ExampleColorRGBToHex() {
|
||||
r := 0
|
||||
g := 51
|
||||
b := 102
|
||||
colorHex := ColorRGBToHex(r, g, b)
|
||||
|
||||
fmt.Println(colorHex)
|
||||
|
||||
// Output:
|
||||
// #003366
|
||||
}
|
||||
|
||||
func ExampleEncodeByte() {
|
||||
byteData, _ := EncodeByte("abc")
|
||||
fmt.Println(byteData)
|
||||
|
||||
// Output:
|
||||
// [6 12 0 3 97 98 99]
|
||||
}
|
||||
|
||||
func ExampleDecodeByte() {
|
||||
var obj string
|
||||
byteData := []byte{6, 12, 0, 3, 97, 98, 99}
|
||||
err := DecodeByte(byteData, &obj)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(obj)
|
||||
|
||||
// Output:
|
||||
// abc
|
||||
}
|
||||
|
||||
func ExampleDeepClone() {
|
||||
type Struct struct {
|
||||
Str string
|
||||
Int int
|
||||
Float float64
|
||||
Bool bool
|
||||
Nil interface{}
|
||||
// unexported string
|
||||
}
|
||||
|
||||
cases := []interface{}{
|
||||
true,
|
||||
1,
|
||||
0.1,
|
||||
map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
},
|
||||
&Struct{
|
||||
Str: "test",
|
||||
Int: 1,
|
||||
Float: 0.1,
|
||||
Bool: true,
|
||||
Nil: nil,
|
||||
// unexported: "can't be cloned",
|
||||
},
|
||||
}
|
||||
|
||||
for _, item := range cases {
|
||||
cloned := DeepClone(item)
|
||||
|
||||
isPointerEqual := &cloned == &item
|
||||
fmt.Println(cloned, isPointerEqual)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true false
|
||||
// 1 false
|
||||
// 0.1 false
|
||||
// map[a:1 b:2] false
|
||||
// &{test 1 0.1 true <nil>} false
|
||||
}
|
||||
|
||||
func ExampleCopyProperties() {
|
||||
type Disk struct {
|
||||
Name string `json:"name"`
|
||||
Total string `json:"total"`
|
||||
Used string `json:"used"`
|
||||
Percent float64 `json:"percent"`
|
||||
}
|
||||
|
||||
type DiskVO struct {
|
||||
Name string `json:"name"`
|
||||
Total string `json:"total"`
|
||||
Used string `json:"used"`
|
||||
Percent float64 `json:"percent"`
|
||||
}
|
||||
|
||||
type Indicator struct {
|
||||
Id string `json:"id"`
|
||||
Ip string `json:"ip"`
|
||||
UpTime string `json:"upTime"`
|
||||
LoadAvg string `json:"loadAvg"`
|
||||
Cpu int `json:"cpu"`
|
||||
Disk []Disk `json:"disk"`
|
||||
Stop chan bool `json:"-"`
|
||||
}
|
||||
|
||||
type IndicatorVO struct {
|
||||
Id string `json:"id"`
|
||||
Ip string `json:"ip"`
|
||||
UpTime string `json:"upTime"`
|
||||
LoadAvg string `json:"loadAvg"`
|
||||
Cpu int64 `json:"cpu"`
|
||||
Disk []DiskVO `json:"disk"`
|
||||
}
|
||||
|
||||
indicator := &Indicator{Id: "001", Ip: "127.0.0.1", Cpu: 1, Disk: []Disk{
|
||||
{Name: "disk-001", Total: "100", Used: "1", Percent: 10},
|
||||
{Name: "disk-002", Total: "200", Used: "1", Percent: 20},
|
||||
{Name: "disk-003", Total: "300", Used: "1", Percent: 30},
|
||||
}}
|
||||
|
||||
indicatorVO := IndicatorVO{}
|
||||
|
||||
CopyProperties(&indicatorVO, indicator)
|
||||
|
||||
fmt.Println(indicatorVO.Id)
|
||||
fmt.Println(indicatorVO.Ip)
|
||||
fmt.Println(len(indicatorVO.Disk))
|
||||
|
||||
// Output:
|
||||
// 001
|
||||
// 127.0.0.1
|
||||
// 3
|
||||
}
|
||||
|
||||
func ExampleToInterface() {
|
||||
val := reflect.ValueOf("abc")
|
||||
iVal, ok := ToInterface(val)
|
||||
|
||||
fmt.Printf("%T\n", iVal)
|
||||
fmt.Printf("%v\n", iVal)
|
||||
fmt.Println(ok)
|
||||
|
||||
// Output:
|
||||
// string
|
||||
// abc
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleUtf8ToGbk() {
|
||||
utf8Data := []byte("hello")
|
||||
gbkData, _ := Utf8ToGbk(utf8Data)
|
||||
|
||||
fmt.Println(utf8.Valid(utf8Data))
|
||||
fmt.Println(validator.IsGBK(gbkData))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleGbkToUtf8() {
|
||||
gbkData, _ := Utf8ToGbk([]byte("hello"))
|
||||
utf8Data, _ := GbkToUtf8(gbkData)
|
||||
|
||||
fmt.Println(utf8.Valid(utf8Data))
|
||||
fmt.Println(string(utf8Data))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// hello
|
||||
}
|
||||
216
convertor/convertor_internal.go
Normal file
216
convertor/convertor_internal.go
Normal file
@@ -0,0 +1,216 @@
|
||||
// Copyright 2023 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package convertor implements some functions to convert data.
|
||||
package convertor
|
||||
|
||||
import "reflect"
|
||||
|
||||
type cloner struct {
|
||||
ptrs map[reflect.Type]map[uintptr]reflect.Value
|
||||
}
|
||||
|
||||
// clone return a duplicate of passed item.
|
||||
func (c *cloner) clone(v reflect.Value) reflect.Value {
|
||||
switch v.Kind() {
|
||||
case reflect.Invalid:
|
||||
return reflect.ValueOf(nil)
|
||||
|
||||
// bool
|
||||
case reflect.Bool:
|
||||
return reflect.ValueOf(v.Bool())
|
||||
|
||||
//int
|
||||
case reflect.Int:
|
||||
return reflect.ValueOf(int(v.Int()))
|
||||
case reflect.Int8:
|
||||
return reflect.ValueOf(int8(v.Int()))
|
||||
case reflect.Int16:
|
||||
return reflect.ValueOf(int16(v.Int()))
|
||||
case reflect.Int32:
|
||||
return reflect.ValueOf(int32(v.Int()))
|
||||
case reflect.Int64:
|
||||
return reflect.ValueOf(v.Int())
|
||||
|
||||
// uint
|
||||
case reflect.Uint:
|
||||
return reflect.ValueOf(uint(v.Uint()))
|
||||
case reflect.Uint8:
|
||||
return reflect.ValueOf(uint8(v.Uint()))
|
||||
case reflect.Uint16:
|
||||
return reflect.ValueOf(uint16(v.Uint()))
|
||||
case reflect.Uint32:
|
||||
return reflect.ValueOf(uint32(v.Uint()))
|
||||
case reflect.Uint64:
|
||||
return reflect.ValueOf(v.Uint())
|
||||
|
||||
// float
|
||||
case reflect.Float32:
|
||||
return reflect.ValueOf(float32(v.Float()))
|
||||
case reflect.Float64:
|
||||
return reflect.ValueOf(v.Float())
|
||||
|
||||
// complex
|
||||
case reflect.Complex64:
|
||||
return reflect.ValueOf(complex64(v.Complex()))
|
||||
case reflect.Complex128:
|
||||
return reflect.ValueOf(v.Complex())
|
||||
|
||||
// string
|
||||
case reflect.String:
|
||||
return reflect.ValueOf(v.String())
|
||||
|
||||
// array
|
||||
case reflect.Array, reflect.Slice:
|
||||
return c.cloneArray(v)
|
||||
|
||||
// map
|
||||
case reflect.Map:
|
||||
return c.cloneMap(v)
|
||||
|
||||
// Ptr
|
||||
case reflect.Ptr:
|
||||
return c.clonePtr(v)
|
||||
|
||||
// struct
|
||||
case reflect.Struct:
|
||||
return c.cloneStruct(v)
|
||||
|
||||
// func
|
||||
case reflect.Func:
|
||||
return v
|
||||
|
||||
// interface
|
||||
case reflect.Interface:
|
||||
return c.clone(v.Elem())
|
||||
|
||||
}
|
||||
|
||||
return reflect.Zero(v.Type())
|
||||
}
|
||||
|
||||
func (c *cloner) cloneArray(v reflect.Value) reflect.Value {
|
||||
if v.IsNil() {
|
||||
return reflect.Zero(v.Type())
|
||||
}
|
||||
|
||||
arr := reflect.MakeSlice(v.Type(), v.Len(), v.Len())
|
||||
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
val := c.clone(v.Index(i))
|
||||
|
||||
if !val.IsValid() {
|
||||
continue
|
||||
}
|
||||
|
||||
item := arr.Index(i)
|
||||
if !item.CanSet() {
|
||||
continue
|
||||
}
|
||||
|
||||
item.Set(val.Convert(item.Type()))
|
||||
}
|
||||
|
||||
return arr
|
||||
}
|
||||
|
||||
func (c *cloner) cloneMap(v reflect.Value) reflect.Value {
|
||||
if v.IsNil() {
|
||||
return reflect.Zero(v.Type())
|
||||
}
|
||||
|
||||
clonedMap := reflect.MakeMap(v.Type())
|
||||
|
||||
for _, key := range v.MapKeys() {
|
||||
value := v.MapIndex(key)
|
||||
clonedKey := c.clone(key)
|
||||
clonedValue := c.clone(value)
|
||||
|
||||
if !isNillable(clonedKey) || !clonedKey.IsNil() {
|
||||
clonedKey = clonedKey.Convert(key.Type())
|
||||
}
|
||||
|
||||
if (!isNillable(clonedValue) || !clonedValue.IsNil()) && clonedValue.IsValid() {
|
||||
clonedValue = clonedValue.Convert(value.Type())
|
||||
}
|
||||
|
||||
if !clonedValue.IsValid() {
|
||||
clonedValue = reflect.Zero(clonedMap.Type().Elem())
|
||||
}
|
||||
|
||||
clonedMap.SetMapIndex(clonedKey, clonedValue)
|
||||
}
|
||||
|
||||
return clonedMap
|
||||
}
|
||||
|
||||
func isNillable(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Chan, reflect.Interface, reflect.Ptr, reflect.Func:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *cloner) clonePtr(v reflect.Value) reflect.Value {
|
||||
if v.IsNil() {
|
||||
return reflect.Zero(v.Type())
|
||||
}
|
||||
|
||||
var newVal reflect.Value
|
||||
|
||||
if v.Elem().CanAddr() {
|
||||
ptrs, exists := c.ptrs[v.Type()]
|
||||
if exists {
|
||||
if newVal, exists := ptrs[v.Elem().UnsafeAddr()]; exists {
|
||||
return newVal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newVal = c.clone(v.Elem())
|
||||
|
||||
if v.Elem().CanAddr() {
|
||||
ptrs, exists := c.ptrs[v.Type()]
|
||||
if exists {
|
||||
if newVal, exists := ptrs[v.Elem().UnsafeAddr()]; exists {
|
||||
return newVal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clonedPtr := reflect.New(newVal.Type())
|
||||
clonedPtr.Elem().Set(newVal)
|
||||
|
||||
return clonedPtr
|
||||
}
|
||||
|
||||
func (c *cloner) cloneStruct(v reflect.Value) reflect.Value {
|
||||
clonedStructPtr := reflect.New(v.Type())
|
||||
clonedStruct := clonedStructPtr.Elem()
|
||||
|
||||
if v.CanAddr() {
|
||||
ptrs := c.ptrs[clonedStructPtr.Type()]
|
||||
if ptrs == nil {
|
||||
ptrs = make(map[uintptr]reflect.Value)
|
||||
c.ptrs[clonedStructPtr.Type()] = ptrs
|
||||
}
|
||||
ptrs[v.UnsafeAddr()] = clonedStructPtr
|
||||
}
|
||||
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
newStructValue := clonedStruct.Field(i)
|
||||
if !newStructValue.CanSet() {
|
||||
continue
|
||||
}
|
||||
|
||||
clonedVal := c.clone(v.Field(i))
|
||||
if !clonedVal.IsValid() {
|
||||
continue
|
||||
}
|
||||
|
||||
newStructValue.Set(clonedVal.Convert(newStructValue.Type()))
|
||||
}
|
||||
|
||||
return clonedStruct
|
||||
}
|
||||
@@ -2,12 +2,19 @@ package convertor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/duke-git/lancet/internal"
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
"github.com/duke-git/lancet/v2/validator"
|
||||
)
|
||||
|
||||
func TestToChar(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestToChar")
|
||||
|
||||
cases := []string{"", "abc", "1 2#3"}
|
||||
@@ -21,7 +28,23 @@ func TestToChar(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestToChannel(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestToChannel")
|
||||
|
||||
ch := ToChannel([]int{1, 2, 3})
|
||||
assert.Equal(1, <-ch)
|
||||
assert.Equal(2, <-ch)
|
||||
assert.Equal(3, <-ch)
|
||||
|
||||
_, ok := <-ch
|
||||
assert.Equal(false, ok)
|
||||
}
|
||||
|
||||
func TestToBool(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestToBool")
|
||||
|
||||
cases := []string{"1", "true", "True", "false", "False", "0", "123", "0.0", "abc"}
|
||||
@@ -34,9 +57,11 @@ func TestToBool(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestToBytes(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestToBytes")
|
||||
|
||||
cases := []interface{}{
|
||||
cases := []any{
|
||||
0,
|
||||
false,
|
||||
"1",
|
||||
@@ -60,9 +85,11 @@ func TestToBytes(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestToInt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestToInt")
|
||||
|
||||
cases := []interface{}{"123", "-123", 123,
|
||||
cases := []any{"123", "-123", 123,
|
||||
uint(123), uint8(123), uint16(123), uint32(123), uint64(123),
|
||||
float32(12.3), float64(12.3),
|
||||
"abc", false, "111111111111111111111111111111111111111"}
|
||||
@@ -76,9 +103,11 @@ func TestToInt(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestToFloat(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestToFloat")
|
||||
|
||||
cases := []interface{}{
|
||||
cases := []any{
|
||||
"", "-1", "-.11", "1.23e3", ".123e10", "abc",
|
||||
int(0), int8(1), int16(-1), int32(123), int64(123),
|
||||
uint(123), uint8(123), uint16(123), uint32(123), uint64(123),
|
||||
@@ -94,6 +123,8 @@ func TestToFloat(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestToString(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestToString")
|
||||
|
||||
aMap := make(map[string]int)
|
||||
@@ -106,7 +137,7 @@ func TestToString(t *testing.T) {
|
||||
}
|
||||
aStruct := TestStruct{Name: "TestStruct"}
|
||||
|
||||
cases := []interface{}{
|
||||
cases := []any{
|
||||
"", nil,
|
||||
int(0), int8(1), int16(-1), int32(123), int64(123),
|
||||
uint(123), uint8(123), uint16(123), uint32(123), uint64(123),
|
||||
@@ -118,9 +149,10 @@ func TestToString(t *testing.T) {
|
||||
"", "",
|
||||
"0", "1", "-1",
|
||||
"123", "123", "123", "123", "123", "123", "123",
|
||||
"12.3", "12.300000190734863",
|
||||
"12.3", "12.3",
|
||||
"true", "false",
|
||||
"[1,2,3]", "{\"a\":1,\"b\":2,\"c\":3}", "{\"Name\":\"TestStruct\"}", "hello"}
|
||||
"[1,2,3]", "{\"a\":1,\"b\":2,\"c\":3}", "{\"Name\":\"TestStruct\"}", "hello",
|
||||
}
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
actual := ToString(cases[i])
|
||||
@@ -128,6 +160,8 @@ func TestToString(t *testing.T) {
|
||||
}
|
||||
}
|
||||
func TestToJson(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestToJson")
|
||||
|
||||
var aMap = map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
@@ -142,23 +176,83 @@ func TestToJson(t *testing.T) {
|
||||
assert.Equal("{\"Name\":\"TestStruct\"}", structJsonStr)
|
||||
}
|
||||
|
||||
func TestToMap(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestToMap")
|
||||
|
||||
type Message struct {
|
||||
name string
|
||||
code int
|
||||
}
|
||||
messages := []Message{
|
||||
{name: "Hello", code: 100},
|
||||
{name: "Hi", code: 101},
|
||||
}
|
||||
result := ToMap(messages, func(msg Message) (int, string) {
|
||||
return msg.code, msg.name
|
||||
})
|
||||
expected := map[int]string{100: "Hello", 101: "Hi"}
|
||||
|
||||
assert.Equal(expected, result)
|
||||
}
|
||||
|
||||
func TestStructToMap(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestStructToMap")
|
||||
|
||||
type People struct {
|
||||
Name string `json:"name"`
|
||||
age int
|
||||
}
|
||||
p := People{
|
||||
"test",
|
||||
100,
|
||||
}
|
||||
pm, _ := StructToMap(p)
|
||||
var expected = map[string]interface{}{"name": "test"}
|
||||
assert.Equal(expected, pm)
|
||||
t.Run("StructToMap", func(_ *testing.T) {
|
||||
type People struct {
|
||||
Name string `json:"name"`
|
||||
age int
|
||||
}
|
||||
p := People{
|
||||
"test",
|
||||
100,
|
||||
}
|
||||
pm, _ := StructToMap(p)
|
||||
var expected = map[string]any{"name": "test"}
|
||||
assert.Equal(expected, pm)
|
||||
})
|
||||
|
||||
t.Run("StructToMapWithJsonAttr", func(_ *testing.T) {
|
||||
type People struct {
|
||||
Name string `json:"name,omitempty"` // json tag with attribute
|
||||
Phone string `json:"phone"` // json tag without attribute
|
||||
Sex string `json:"-"` // ignore
|
||||
age int // no tag
|
||||
}
|
||||
p := People{
|
||||
Phone: "1111",
|
||||
Sex: "male",
|
||||
age: 100,
|
||||
}
|
||||
pm, _ := StructToMap(p)
|
||||
var expected = map[string]any{"phone": "1111"}
|
||||
assert.Equal(expected, pm)
|
||||
})
|
||||
}
|
||||
|
||||
func TestMapToSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestMapToSlice")
|
||||
|
||||
aMap := map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
result := MapToSlice(aMap, func(key string, value int) string {
|
||||
return key + ":" + strconv.Itoa(value)
|
||||
})
|
||||
|
||||
assert.Equal(3, len(result))
|
||||
assert.Equal(true, slice.Contain(result, "a:1"))
|
||||
assert.Equal(true, slice.Contain(result, "b:2"))
|
||||
assert.Equal(true, slice.Contain(result, "c:3"))
|
||||
}
|
||||
|
||||
func TestColorHexToRGB(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
colorHex := "#003366"
|
||||
r, g, b := ColorHexToRGB(colorHex)
|
||||
colorRGB := fmt.Sprintf("%d,%d,%d", r, g, b)
|
||||
@@ -169,6 +263,8 @@ func TestColorHexToRGB(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestColorRGBToHex(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
r := 0
|
||||
g := 51
|
||||
b := 102
|
||||
@@ -179,24 +275,18 @@ func TestColorRGBToHex(t *testing.T) {
|
||||
assert.Equal(expected, colorHex)
|
||||
}
|
||||
|
||||
func TestToChannel(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestToChannel")
|
||||
func TestToPointer(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ch := ToChannel([]interface{}{1, 2, 3})
|
||||
val1, _ := <-ch
|
||||
assert.Equal(1, val1)
|
||||
assert := internal.NewAssert(t, "TestToPointer")
|
||||
result := ToPointer(123)
|
||||
|
||||
val2, _ := <-ch
|
||||
assert.Equal(2, val2)
|
||||
|
||||
val3, _ := <-ch
|
||||
assert.Equal(3, val3)
|
||||
|
||||
_, ok := <-ch
|
||||
assert.Equal(false, ok)
|
||||
assert.Equal(*result, 123)
|
||||
}
|
||||
|
||||
func TestEncodeByte(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestEncodeByte")
|
||||
|
||||
byteData, _ := EncodeByte("abc")
|
||||
@@ -206,10 +296,168 @@ func TestEncodeByte(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDecodeByte(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestDecodeByte")
|
||||
|
||||
var obj string
|
||||
byteData := []byte{6, 12, 0, 3, 97, 98, 99}
|
||||
DecodeByte(byteData, &obj)
|
||||
err := DecodeByte(byteData, &obj)
|
||||
assert.IsNil(err)
|
||||
assert.Equal("abc", obj)
|
||||
}
|
||||
|
||||
func TestDeepClone(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// assert := internal.NewAssert(t, "TestDeepClone")
|
||||
|
||||
type Struct struct {
|
||||
Str string
|
||||
Int int
|
||||
Float float64
|
||||
Bool bool
|
||||
Nil interface{}
|
||||
// unexported string
|
||||
}
|
||||
|
||||
cases := []interface{}{
|
||||
true,
|
||||
1,
|
||||
0.1,
|
||||
map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
},
|
||||
&Struct{
|
||||
Str: "test",
|
||||
Int: 1,
|
||||
Float: 0.1,
|
||||
Bool: true,
|
||||
Nil: nil,
|
||||
// unexported: "can't be cloned",
|
||||
},
|
||||
[]interface{}{1, &Struct{Str: "test"}, Struct{Str: "test2"}},
|
||||
}
|
||||
|
||||
for i, item := range cases {
|
||||
cloned := DeepClone(item)
|
||||
|
||||
if &cloned == &item {
|
||||
t.Fatalf("[TestDeepClone case #%d failed]: equal pointer", i)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(item, cloned) {
|
||||
t.Fatalf("[TestDeepClone case #%d failed] unequal objects", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyProperties(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestCopyProperties")
|
||||
|
||||
type Disk struct {
|
||||
Name string `json:"name"`
|
||||
Total string `json:"total"`
|
||||
Used string `json:"used"`
|
||||
Percent float64 `json:"percent"`
|
||||
}
|
||||
|
||||
type DiskVO struct {
|
||||
Name string `json:"name"`
|
||||
Total string `json:"total"`
|
||||
Used string `json:"used"`
|
||||
Percent float64 `json:"percent"`
|
||||
}
|
||||
|
||||
type Indicator struct {
|
||||
Id string `json:"id"`
|
||||
Ip string `json:"ip"`
|
||||
UpTime string `json:"upTime"`
|
||||
LoadAvg string `json:"loadAvg"`
|
||||
Cpu int `json:"cpu"`
|
||||
Disk []Disk `json:"disk"`
|
||||
Stop chan bool `json:"-"`
|
||||
}
|
||||
|
||||
type IndicatorVO struct {
|
||||
Id string `json:"id"`
|
||||
Ip string `json:"ip"`
|
||||
UpTime string `json:"upTime"`
|
||||
LoadAvg string `json:"loadAvg"`
|
||||
Cpu int64 `json:"cpu"`
|
||||
Disk []DiskVO `json:"disk"`
|
||||
}
|
||||
|
||||
indicator := &Indicator{Id: "001", Ip: "127.0.0.1", Cpu: 1, Disk: []Disk{
|
||||
{Name: "disk-001", Total: "100", Used: "1", Percent: 10},
|
||||
{Name: "disk-002", Total: "200", Used: "1", Percent: 20},
|
||||
{Name: "disk-003", Total: "300", Used: "1", Percent: 30},
|
||||
}}
|
||||
|
||||
indicatorVO := IndicatorVO{}
|
||||
|
||||
err := CopyProperties(&indicatorVO, indicator)
|
||||
|
||||
assert.IsNil(err)
|
||||
assert.Equal("001", indicatorVO.Id)
|
||||
assert.Equal("127.0.0.1", indicatorVO.Ip)
|
||||
assert.Equal(3, len(indicatorVO.Disk))
|
||||
}
|
||||
|
||||
func TestToInterface(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestToInterface")
|
||||
|
||||
cases := []reflect.Value{
|
||||
reflect.ValueOf("abc"),
|
||||
reflect.ValueOf(int(0)), reflect.ValueOf(int8(1)), reflect.ValueOf(int16(-1)), reflect.ValueOf(int32(123)), reflect.ValueOf(int64(123)),
|
||||
reflect.ValueOf(uint(123)), reflect.ValueOf(uint8(123)), reflect.ValueOf(uint16(123)), reflect.ValueOf(uint32(123)), reflect.ValueOf(uint64(123)),
|
||||
reflect.ValueOf(float64(12.3)), reflect.ValueOf(float32(12.3)),
|
||||
reflect.ValueOf(true), reflect.ValueOf(false),
|
||||
}
|
||||
|
||||
expected := []interface{}{
|
||||
"abc",
|
||||
0, int8(1), int16(-1), int32(123), int64(123),
|
||||
uint(123), uint8(123), uint16(123), uint32(123), uint64(123),
|
||||
float64(12.3), float32(12.3),
|
||||
true, false,
|
||||
}
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
actual, _ := ToInterface(cases[i])
|
||||
assert.Equal(expected[i], actual)
|
||||
}
|
||||
|
||||
nilVal, ok := ToInterface(reflect.ValueOf(nil))
|
||||
assert.EqualValues(nil, nilVal)
|
||||
assert.Equal(false, ok)
|
||||
}
|
||||
|
||||
func TestUtf8ToGbk(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestUtf8ToGbk")
|
||||
|
||||
utf8Data := []byte("hello")
|
||||
gbkData, err := Utf8ToGbk(utf8Data)
|
||||
|
||||
assert.Equal(true, utf8.Valid(utf8Data))
|
||||
assert.Equal(true, validator.IsGBK(gbkData))
|
||||
assert.IsNil(err)
|
||||
}
|
||||
|
||||
func TestGbkToUtf8(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestGbkToUtf8")
|
||||
|
||||
gbkData, err := Utf8ToGbk([]byte("hello"))
|
||||
utf8Data, err := GbkToUtf8(gbkData)
|
||||
|
||||
assert.IsNil(err)
|
||||
assert.Equal(true, utf8.Valid(utf8Data))
|
||||
assert.Equal("hello", string(utf8Data))
|
||||
}
|
||||
|
||||
174
cryptor/aes.go
174
cryptor/aes.go
@@ -1,174 +0,0 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package cryptor implements some util functions to encrypt and decrypt.
|
||||
// Note:
|
||||
// 1. for aes crypt function, the `key` param length should be 16, 24 or 32. if not, will panic.
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
)
|
||||
|
||||
// AesEcbEncrypt encrypt data with key use AES ECB algorithm
|
||||
// len(key) should be 16, 24 or 32
|
||||
func AesEcbEncrypt(data, key []byte) []byte {
|
||||
cipher, _ := aes.NewCipher(generateAesKey(key))
|
||||
length := (len(data) + aes.BlockSize) / aes.BlockSize
|
||||
plain := make([]byte, length*aes.BlockSize)
|
||||
copy(plain, data)
|
||||
pad := byte(len(plain) - len(data))
|
||||
for i := len(data); i < len(plain); i++ {
|
||||
plain[i] = pad
|
||||
}
|
||||
encrypted := make([]byte, len(plain))
|
||||
for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
||||
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
|
||||
}
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// AesEcbDecrypt decrypt data with key use AES ECB algorithm
|
||||
// len(key) should be 16, 24 or 32
|
||||
func AesEcbDecrypt(encrypted, key []byte) []byte {
|
||||
cipher, _ := aes.NewCipher(generateAesKey(key))
|
||||
decrypted := make([]byte, len(encrypted))
|
||||
//
|
||||
for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
||||
cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
|
||||
}
|
||||
|
||||
trim := 0
|
||||
if len(decrypted) > 0 {
|
||||
trim = len(decrypted) - int(decrypted[len(decrypted)-1])
|
||||
}
|
||||
|
||||
return decrypted[:trim]
|
||||
}
|
||||
|
||||
// AesCbcEncrypt encrypt data with key use AES CBC algorithm
|
||||
// len(key) should be 16, 24 or 32
|
||||
func AesCbcEncrypt(data, key []byte) []byte {
|
||||
block, _ := aes.NewCipher(key)
|
||||
data = pkcs7Padding(data, block.BlockSize())
|
||||
|
||||
encrypted := make([]byte, aes.BlockSize+len(data))
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
mode.CryptBlocks(encrypted[aes.BlockSize:], data)
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// AesCbcDecrypt decrypt data with key use AES CBC algorithm
|
||||
// len(key) should be 16, 24 or 32
|
||||
func AesCbcDecrypt(encrypted, key []byte) []byte {
|
||||
block, _ := aes.NewCipher(key)
|
||||
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
encrypted = encrypted[aes.BlockSize:]
|
||||
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(encrypted, encrypted)
|
||||
|
||||
decrypted := pkcs7UnPadding(encrypted)
|
||||
return decrypted
|
||||
}
|
||||
|
||||
// AesCtrCrypt encrypt data with key use AES CTR algorithm
|
||||
// len(key) should be 16, 24 or 32
|
||||
func AesCtrCrypt(data, key []byte) []byte {
|
||||
block, _ := aes.NewCipher(key)
|
||||
|
||||
iv := bytes.Repeat([]byte("1"), block.BlockSize())
|
||||
stream := cipher.NewCTR(block, iv)
|
||||
|
||||
dst := make([]byte, len(data))
|
||||
stream.XORKeyStream(dst, data)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// AesCfbEncrypt encrypt data with key use AES CFB algorithm
|
||||
// len(key) should be 16, 24 or 32
|
||||
func AesCfbEncrypt(data, key []byte) []byte {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
encrypted := make([]byte, aes.BlockSize+len(data))
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stream := cipher.NewCFBEncrypter(block, iv)
|
||||
stream.XORKeyStream(encrypted[aes.BlockSize:], data)
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// AesCfbDecrypt decrypt data with key use AES CFB algorithm
|
||||
// len(encrypted) should be great than 16, len(key) should be 16, 24 or 32
|
||||
func AesCfbDecrypt(encrypted, key []byte) []byte {
|
||||
block, _ := aes.NewCipher(key)
|
||||
if len(encrypted) < aes.BlockSize {
|
||||
panic("encrypted data is too short")
|
||||
}
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
encrypted = encrypted[aes.BlockSize:]
|
||||
|
||||
stream := cipher.NewCFBDecrypter(block, iv)
|
||||
stream.XORKeyStream(encrypted, encrypted)
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// AesOfbEncrypt encrypt data with key use AES OFB algorithm
|
||||
// len(key) should be 16, 24 or 32
|
||||
func AesOfbEncrypt(data, key []byte) []byte {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
data = pkcs7Padding(data, aes.BlockSize)
|
||||
encrypted := make([]byte, aes.BlockSize+len(data))
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stream := cipher.NewOFB(block, iv)
|
||||
stream.XORKeyStream(encrypted[aes.BlockSize:], data)
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// AesOfbDecrypt decrypt data with key use AES OFB algorithm
|
||||
// len(key) should be 16, 24 or 32
|
||||
func AesOfbDecrypt(data, key []byte) []byte {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
iv := data[:aes.BlockSize]
|
||||
data = data[aes.BlockSize:]
|
||||
if len(data)%aes.BlockSize != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
decrypted := make([]byte, len(data))
|
||||
mode := cipher.NewOFB(block, iv)
|
||||
mode.XORKeyStream(decrypted, data)
|
||||
|
||||
decrypted = pkcs7UnPadding(decrypted)
|
||||
return decrypted
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/internal"
|
||||
)
|
||||
|
||||
func TestAesEcbEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
aesEcbEncrypt := AesEcbEncrypt([]byte(data), []byte(key))
|
||||
aesEcbDecrypt := AesEcbDecrypt(aesEcbEncrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestAesEcbEncrypt")
|
||||
assert.Equal(data, string(aesEcbDecrypt))
|
||||
}
|
||||
|
||||
func TestAesCbcEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
aesCbcEncrypt := AesCbcEncrypt([]byte(data), []byte(key))
|
||||
aesCbcDecrypt := AesCbcDecrypt(aesCbcEncrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestAesCbcEncrypt")
|
||||
assert.Equal(data, string(aesCbcDecrypt))
|
||||
}
|
||||
|
||||
func TestAesCtrCrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
aesCtrCrypt := AesCtrCrypt([]byte(data), []byte(key))
|
||||
aesCtrDeCrypt := AesCtrCrypt(aesCtrCrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestAesCtrCrypt")
|
||||
assert.Equal(data, string(aesCtrDeCrypt))
|
||||
}
|
||||
|
||||
func TestAesCfbEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
aesCfbEncrypt := AesCfbEncrypt([]byte(data), []byte(key))
|
||||
aesCfbDecrypt := AesCfbDecrypt(aesCfbEncrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestAesCfbEncrypt")
|
||||
assert.Equal(data, string(aesCfbDecrypt))
|
||||
}
|
||||
|
||||
func TestAesOfbEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
aesOfbEncrypt := AesOfbEncrypt([]byte(data), []byte(key))
|
||||
aesOfbDecrypt := AesOfbDecrypt(aesOfbEncrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestAesOfbEncrypt")
|
||||
assert.Equal(data, string(aesOfbDecrypt))
|
||||
}
|
||||
140
cryptor/basic.go
140
cryptor/basic.go
@@ -19,25 +19,52 @@ import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// Base64StdEncode encode string with base64 encoding
|
||||
// Base64StdEncode encode string with base64 encoding.
|
||||
// Play: https://go.dev/play/p/VOaUyQUreoK
|
||||
func Base64StdEncode(s string) string {
|
||||
return base64.StdEncoding.EncodeToString([]byte(s))
|
||||
}
|
||||
|
||||
// Base64StdDecode decode a base64 encoded string
|
||||
// Base64StdDecode decode a base64 encoded string.
|
||||
// Play: https://go.dev/play/p/RWQylnJVgIe
|
||||
func Base64StdDecode(s string) string {
|
||||
b, _ := base64.StdEncoding.DecodeString(s)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// Md5String return the md5 value of string
|
||||
// Md5String return the md5 value of string.
|
||||
// Play: https://go.dev/play/p/1bLcVetbTOI
|
||||
func Md5String(s string) string {
|
||||
h := md5.New()
|
||||
h.Write([]byte(s))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// Md5File return the md5 value of file
|
||||
// Md5StringWithBase64 return the md5 value of string with base64.
|
||||
// Play: https://go.dev/play/p/Lx4gH7Vdr5_y
|
||||
func Md5StringWithBase64(s string) string {
|
||||
h := md5.New()
|
||||
h.Write([]byte(s))
|
||||
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// Md5Byte return the md5 string of byte slice.
|
||||
// Play: https://go.dev/play/p/suraalH8lyC
|
||||
func Md5Byte(data []byte) string {
|
||||
h := md5.New()
|
||||
h.Write(data)
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// Md5ByteWithBase64 return the md5 string of byte slice with base64.
|
||||
// Play: https://go.dev/play/p/Tcb-Z7LN2ax
|
||||
func Md5ByteWithBase64(data []byte) string {
|
||||
h := md5.New()
|
||||
h.Write(data)
|
||||
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// Md5File return the md5 value of file.
|
||||
func Md5File(filename string) (string, error) {
|
||||
if fileInfo, err := os.Stat(filename); err != nil {
|
||||
return "", err
|
||||
@@ -69,51 +96,114 @@ func Md5File(filename string) (string, error) {
|
||||
return checksum, nil
|
||||
}
|
||||
|
||||
// HmacMd5 return the hmac hash of string use md5
|
||||
func HmacMd5(data, key string) string {
|
||||
// HmacMd5 return the hmac hash of string use md5.
|
||||
// Play: https://go.dev/play/p/uef0q1fz53I
|
||||
func HmacMd5(str, key string) string {
|
||||
h := hmac.New(md5.New, []byte(key))
|
||||
h.Write([]byte(str))
|
||||
return hex.EncodeToString(h.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// HmacMd5WithBase64 return the hmac hash of string use md5 with base64.
|
||||
// https://go.dev/play/p/UY0ng2AefFC
|
||||
func HmacMd5WithBase64(data, key string) string {
|
||||
h := hmac.New(md5.New, []byte(key))
|
||||
h.Write([]byte(data))
|
||||
return hex.EncodeToString(h.Sum([]byte("")))
|
||||
return base64.StdEncoding.EncodeToString(h.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// HmacSha1 return the hmac hash of string use sha1
|
||||
func HmacSha1(data, key string) string {
|
||||
// HmacSha1 return the hmac hash of string use sha1.
|
||||
// Play: https://go.dev/play/p/1UI4oQ4WXKM
|
||||
func HmacSha1(str, key string) string {
|
||||
h := hmac.New(sha1.New, []byte(key))
|
||||
h.Write([]byte(data))
|
||||
h.Write([]byte(str))
|
||||
return hex.EncodeToString(h.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// HmacSha256 return the hmac hash of string use sha256
|
||||
func HmacSha256(data, key string) string {
|
||||
// HmacSha1WithBase64 return the hmac hash of string use sha1 with base64.
|
||||
// Play: https://go.dev/play/p/47JmmGrnF7B
|
||||
func HmacSha1WithBase64(str, key string) string {
|
||||
h := hmac.New(sha1.New, []byte(key))
|
||||
h.Write([]byte(str))
|
||||
return base64.StdEncoding.EncodeToString(h.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// HmacSha256 return the hmac hash of string use sha256.
|
||||
// Play: https://go.dev/play/p/HhpwXxFhhC0
|
||||
func HmacSha256(str, key string) string {
|
||||
h := hmac.New(sha256.New, []byte(key))
|
||||
h.Write([]byte(data))
|
||||
h.Write([]byte(str))
|
||||
return hex.EncodeToString(h.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// HmacSha512 return the hmac hash of string use sha512
|
||||
func HmacSha512(data, key string) string {
|
||||
// HmacSha256WithBase64 return the hmac hash of string use sha256 with base64.
|
||||
// Play: https://go.dev/play/p/EKbkUvPTLwO
|
||||
func HmacSha256WithBase64(str, key string) string {
|
||||
h := hmac.New(sha256.New, []byte(key))
|
||||
h.Write([]byte(str))
|
||||
return base64.StdEncoding.EncodeToString(h.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// HmacSha512 return the hmac hash of string use sha512.
|
||||
// Play: https://go.dev/play/p/59Od6m4A0Ud
|
||||
func HmacSha512(str, key string) string {
|
||||
h := hmac.New(sha512.New, []byte(key))
|
||||
h.Write([]byte(data))
|
||||
h.Write([]byte(str))
|
||||
return hex.EncodeToString(h.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// Sha1 return the sha1 value (SHA-1 hash algorithm) of string
|
||||
func Sha1(data string) string {
|
||||
// HmacSha512WithBase64 return the hmac hash of string use sha512 with base64.
|
||||
// Play: https://go.dev/play/p/c6dSe3E2ydU
|
||||
func HmacSha512WithBase64(str, key string) string {
|
||||
h := hmac.New(sha512.New, []byte(key))
|
||||
h.Write([]byte(str))
|
||||
return base64.StdEncoding.EncodeToString(h.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// Sha1 return the sha1 value (SHA-1 hash algorithm) of string.
|
||||
// Play: https://go.dev/play/p/_m_uoD1deMT
|
||||
func Sha1(str string) string {
|
||||
sha1 := sha1.New()
|
||||
sha1.Write([]byte(data))
|
||||
sha1.Write([]byte(str))
|
||||
return hex.EncodeToString(sha1.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// Sha256 return the sha256 value (SHA256 hash algorithm) of string
|
||||
func Sha256(data string) string {
|
||||
// Sha1WithBase64 return the sha1 value (SHA-1 hash algorithm) of base64 string.
|
||||
// Play: https://go.dev/play/p/fSyx-Gl2l2-
|
||||
func Sha1WithBase64(str string) string {
|
||||
sha1 := sha1.New()
|
||||
sha1.Write([]byte(str))
|
||||
return base64.StdEncoding.EncodeToString(sha1.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// Sha256 return the sha256 value (SHA256 hash algorithm) of string.
|
||||
// Play: https://go.dev/play/p/tU9tfBMIAr1
|
||||
func Sha256(str string) string {
|
||||
sha256 := sha256.New()
|
||||
sha256.Write([]byte(data))
|
||||
sha256.Write([]byte(str))
|
||||
return hex.EncodeToString(sha256.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// Sha512 return the sha512 value (SHA512 hash algorithm) of string
|
||||
func Sha512(data string) string {
|
||||
// Sha256WithBase64 return the sha256 value (SHA256 hash algorithm) of base64 string.
|
||||
// Play: https://go.dev/play/p/85IXJHIal1k
|
||||
func Sha256WithBase64(str string) string {
|
||||
sha256 := sha256.New()
|
||||
sha256.Write([]byte(str))
|
||||
return base64.StdEncoding.EncodeToString(sha256.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// Sha512 return the sha512 value (SHA512 hash algorithm) of string.
|
||||
// Play: https://go.dev/play/p/3WsvLYZxsHa
|
||||
func Sha512(str string) string {
|
||||
sha512 := sha512.New()
|
||||
sha512.Write([]byte(data))
|
||||
sha512.Write([]byte(str))
|
||||
return hex.EncodeToString(sha512.Sum([]byte("")))
|
||||
}
|
||||
|
||||
// Sha512WithBase64 return the sha512 value (SHA512 hash algorithm) of base64 string.
|
||||
// Play: https://go.dev/play/p/q_fY2rA-k5I
|
||||
func Sha512WithBase64(str string) string {
|
||||
sha512 := sha512.New()
|
||||
sha512.Write([]byte(str))
|
||||
return base64.StdEncoding.EncodeToString(sha512.Sum([]byte("")))
|
||||
}
|
||||
|
||||
@@ -3,25 +3,55 @@ package cryptor
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/internal"
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestBase64StdEncode(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestBase64StdEncode")
|
||||
assert.Equal("aGVsbG8gd29ybGQ=", Base64StdEncode("hello world"))
|
||||
}
|
||||
|
||||
func TestBase64StdDecode(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestBase64StdDecode")
|
||||
assert.Equal("hello world", Base64StdDecode("aGVsbG8gd29ybGQ="))
|
||||
}
|
||||
|
||||
func TestMd5String(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestMd5String")
|
||||
assert.Equal("5d41402abc4b2a76b9719d911017c592", Md5String("hello"))
|
||||
}
|
||||
|
||||
func TestMd5StringWithBase64(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestMd5StringWithBase64")
|
||||
assert.Equal("XUFAKrxLKna5cZ2REBfFkg==", Md5StringWithBase64("hello"))
|
||||
}
|
||||
|
||||
func TestMd5Byte(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestMd5Byte")
|
||||
data := []byte{'a'}
|
||||
assert.Equal("0cc175b9c0f1b6a831c399e269772661", Md5Byte(data))
|
||||
}
|
||||
|
||||
func TestMd5ByteWithBase64(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestMd5ByteWithBase64")
|
||||
assert.Equal("XUFAKrxLKna5cZ2REBfFkg==", Md5ByteWithBase64([]byte("hello")))
|
||||
}
|
||||
|
||||
func TestMd5File(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fileMd5, err := Md5File("./basic.go")
|
||||
assert := internal.NewAssert(t, "TestMd5File")
|
||||
assert.IsNotNil(fileMd5)
|
||||
@@ -29,11 +59,22 @@ func TestMd5File(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHmacMd5(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestHmacMd5")
|
||||
assert.Equal("5f4c9faaff0a1ad3007d9ddc06abe36d", HmacMd5("hello world", "12345"))
|
||||
}
|
||||
|
||||
func TestHmacMd5WithBase64(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestHmacMd5WithBase64")
|
||||
assert.Equal("6DQwbquJLYclJdSRinpjmg==", HmacMd5WithBase64("hello", "12345"))
|
||||
}
|
||||
|
||||
func TestHmacSha1(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := "hello world"
|
||||
key := "12345"
|
||||
hmacSha1 := HmacSha1(s, key)
|
||||
@@ -43,17 +84,45 @@ func TestHmacSha1(t *testing.T) {
|
||||
assert.Equal(expected, hmacSha1)
|
||||
}
|
||||
|
||||
func TestHmacSha256(t *testing.T) {
|
||||
s := "hello world"
|
||||
func TestHmacSha1WithBase64(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := "hello"
|
||||
key := "12345"
|
||||
hmacSha256 := HmacSha256(s, key)
|
||||
hmacSha1 := HmacSha1WithBase64(s, key)
|
||||
expected := "XGqdsMzLkuNu0DI/0Jt/k23prOA="
|
||||
|
||||
assert := internal.NewAssert(t, "TestHmacSha1")
|
||||
assert.Equal(expected, hmacSha1)
|
||||
}
|
||||
|
||||
func TestHmacSha256(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
str := "hello world"
|
||||
key := "12345"
|
||||
hmacSha256 := HmacSha256(str, key)
|
||||
expected := "9dce2609f2d67d41f74c7f9efc8ccd44370d41ad2de52982627588dfe7289ab8"
|
||||
|
||||
assert := internal.NewAssert(t, "TestHmacSha256")
|
||||
assert.Equal(expected, hmacSha256)
|
||||
}
|
||||
|
||||
func TestHmacSha256WithBase64(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
str := "hello"
|
||||
key := "12345"
|
||||
hms := HmacSha256WithBase64(str, key)
|
||||
expected := "MVu5PE6YmGK6Ccti4F1zpfN2yzbw14btqwwyDQWf3nU="
|
||||
|
||||
assert := internal.NewAssert(t, "TestHmacSha256WithBase64")
|
||||
assert.Equal(expected, hms)
|
||||
}
|
||||
|
||||
func TestHmacSha512(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := "hello world"
|
||||
key := "12345"
|
||||
hmacSha512 := HmacSha512(s, key)
|
||||
@@ -63,7 +132,21 @@ func TestHmacSha512(t *testing.T) {
|
||||
assert.Equal(expected, hmacSha512)
|
||||
}
|
||||
|
||||
func TestHmacSha512WithBase64(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
str := "hello"
|
||||
key := "12345"
|
||||
hms := HmacSha512WithBase64(str, key)
|
||||
expected := "3Y8SkKndI9NU4lJtmi6c6M///dN8syCADRxsE9Lvw2Mog3ahlsVFja9T+OGqa0Wm2FYwPVwKIGS/+XhYYdSM/A=="
|
||||
|
||||
assert := internal.NewAssert(t, "TestHmacSha512WithBase64")
|
||||
assert.Equal(expected, hms)
|
||||
}
|
||||
|
||||
func TestSha1(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := "hello world"
|
||||
sha1 := Sha1(s)
|
||||
expected := "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"
|
||||
@@ -72,7 +155,19 @@ func TestSha1(t *testing.T) {
|
||||
assert.Equal(expected, sha1)
|
||||
}
|
||||
|
||||
func TestSha1WithBase64(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
str := Sha1WithBase64("hello")
|
||||
expected := "qvTGHdzF6KLavt4PO0gs2a6pQ00="
|
||||
|
||||
assert := internal.NewAssert(t, "TestSha1WithBase64")
|
||||
assert.Equal(expected, str)
|
||||
}
|
||||
|
||||
func TestSha256(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := "hello world"
|
||||
sha256 := Sha256(s)
|
||||
expected := "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
|
||||
@@ -81,7 +176,19 @@ func TestSha256(t *testing.T) {
|
||||
assert.Equal(expected, sha256)
|
||||
}
|
||||
|
||||
func TestSha256WithBase64(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
str := Sha256WithBase64("hello")
|
||||
expected := "LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ="
|
||||
|
||||
assert := internal.NewAssert(t, "TestSha256WithBase64")
|
||||
assert.Equal(expected, str)
|
||||
}
|
||||
|
||||
func TestSha512(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := "hello world"
|
||||
sha512 := Sha512(s)
|
||||
expected := "309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f"
|
||||
@@ -89,3 +196,13 @@ func TestSha512(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSha512")
|
||||
assert.Equal(expected, sha512)
|
||||
}
|
||||
|
||||
func TestSha512WithBase64(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
str := Sha512WithBase64("hello")
|
||||
expected := "m3HSJL1i83hdltRq0+o9czGb+8KJDKra4t/3JRlnPKcjI8PZm6XBHXx6zG4UuMXaDEZjR1wuXDre9G9zvN7AQw=="
|
||||
|
||||
assert := internal.NewAssert(t, "TestSha512WithBase64")
|
||||
assert.Equal(expected, str)
|
||||
}
|
||||
|
||||
537
cryptor/crypto.go
Normal file
537
cryptor/crypto.go
Normal file
@@ -0,0 +1,537 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package cryptor implements some util functions to encrypt and decrypt.
|
||||
// Note:
|
||||
// 1. for aes crypt function, the `key` param length should be 16, 24 or 32. if not, will panic.
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// AesEcbEncrypt encrypt data with key use AES ECB algorithm
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/jT5irszHx-j
|
||||
func AesEcbEncrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 16 && size != 24 && size != 32 {
|
||||
panic("key length shoud be 16 or 24 or 32")
|
||||
}
|
||||
|
||||
length := (len(data) + aes.BlockSize) / aes.BlockSize
|
||||
plain := make([]byte, length*aes.BlockSize)
|
||||
|
||||
copy(plain, data)
|
||||
|
||||
pad := byte(len(plain) - len(data))
|
||||
for i := len(data); i < len(plain); i++ {
|
||||
plain[i] = pad
|
||||
}
|
||||
|
||||
encrypted := make([]byte, len(plain))
|
||||
cipher, _ := aes.NewCipher(generateAesKey(key, size))
|
||||
|
||||
for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
||||
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
|
||||
}
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// AesEcbDecrypt decrypt data with key use AES ECB algorithm
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/jT5irszHx-j
|
||||
func AesEcbDecrypt(encrypted, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 16 && size != 24 && size != 32 {
|
||||
panic("key length shoud be 16 or 24 or 32")
|
||||
}
|
||||
cipher, _ := aes.NewCipher(generateAesKey(key, size))
|
||||
decrypted := make([]byte, len(encrypted))
|
||||
|
||||
for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
||||
cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
|
||||
}
|
||||
|
||||
trim := 0
|
||||
if len(decrypted) > 0 {
|
||||
trim = len(decrypted) - int(decrypted[len(decrypted)-1])
|
||||
}
|
||||
|
||||
return decrypted[:trim]
|
||||
}
|
||||
|
||||
// AesCbcEncrypt encrypt data with key use AES CBC algorithm
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/IOq_g8_lKZD
|
||||
func AesCbcEncrypt(data, key []byte) []byte {
|
||||
block, _ := aes.NewCipher(key)
|
||||
data = pkcs7Padding(data, block.BlockSize())
|
||||
|
||||
encrypted := make([]byte, aes.BlockSize+len(data))
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
mode.CryptBlocks(encrypted[aes.BlockSize:], data)
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// AesCbcDecrypt decrypt data with key use AES CBC algorithm
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/IOq_g8_lKZD
|
||||
func AesCbcDecrypt(encrypted, key []byte) []byte {
|
||||
block, _ := aes.NewCipher(key)
|
||||
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
encrypted = encrypted[aes.BlockSize:]
|
||||
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(encrypted, encrypted)
|
||||
|
||||
decrypted := pkcs7UnPadding(encrypted)
|
||||
return decrypted
|
||||
}
|
||||
|
||||
// AesCtrCrypt encrypt data with key use AES CTR algorithm
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/SpaZO0-5Nsp
|
||||
func AesCtrCrypt(data, key []byte) []byte {
|
||||
block, _ := aes.NewCipher(key)
|
||||
|
||||
iv := bytes.Repeat([]byte("1"), block.BlockSize())
|
||||
stream := cipher.NewCTR(block, iv)
|
||||
|
||||
dst := make([]byte, len(data))
|
||||
stream.XORKeyStream(dst, data)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// AesCfbEncrypt encrypt data with key use AES CFB algorithm
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/tfkF10B13kH
|
||||
func AesCfbEncrypt(data, key []byte) []byte {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
encrypted := make([]byte, aes.BlockSize+len(data))
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stream := cipher.NewCFBEncrypter(block, iv)
|
||||
stream.XORKeyStream(encrypted[aes.BlockSize:], data)
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// AesCfbDecrypt decrypt data with key use AES CFB algorithm
|
||||
// len(encrypted) should be great than 16, len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/tfkF10B13kH
|
||||
func AesCfbDecrypt(encrypted, key []byte) []byte {
|
||||
if len(encrypted) < aes.BlockSize {
|
||||
panic("encrypted data is too short")
|
||||
}
|
||||
|
||||
block, _ := aes.NewCipher(key)
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
encrypted = encrypted[aes.BlockSize:]
|
||||
|
||||
stream := cipher.NewCFBDecrypter(block, iv)
|
||||
|
||||
stream.XORKeyStream(encrypted, encrypted)
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// AesOfbEncrypt encrypt data with key use AES OFB algorithm
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/VtHxtkUj-3F
|
||||
func AesOfbEncrypt(data, key []byte) []byte {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
data = pkcs7Padding(data, aes.BlockSize)
|
||||
encrypted := make([]byte, aes.BlockSize+len(data))
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stream := cipher.NewOFB(block, iv)
|
||||
stream.XORKeyStream(encrypted[aes.BlockSize:], data)
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// AesOfbDecrypt decrypt data with key use AES OFB algorithm
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/VtHxtkUj-3F
|
||||
func AesOfbDecrypt(data, key []byte) []byte {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
iv := data[:aes.BlockSize]
|
||||
data = data[aes.BlockSize:]
|
||||
if len(data)%aes.BlockSize != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
decrypted := make([]byte, len(data))
|
||||
mode := cipher.NewOFB(block, iv)
|
||||
mode.XORKeyStream(decrypted, data)
|
||||
|
||||
decrypted = pkcs7UnPadding(decrypted)
|
||||
|
||||
return decrypted
|
||||
}
|
||||
|
||||
// DesEcbEncrypt encrypt data with key use DES ECB algorithm
|
||||
// len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/8qivmPeZy4P
|
||||
func DesEcbEncrypt(data, key []byte) []byte {
|
||||
length := (len(data) + des.BlockSize) / des.BlockSize
|
||||
plain := make([]byte, length*des.BlockSize)
|
||||
copy(plain, data)
|
||||
|
||||
pad := byte(len(plain) - len(data))
|
||||
for i := len(data); i < len(plain); i++ {
|
||||
plain[i] = pad
|
||||
}
|
||||
|
||||
encrypted := make([]byte, len(plain))
|
||||
cipher, _ := des.NewCipher(generateDesKey(key))
|
||||
|
||||
for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
||||
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
|
||||
}
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// DesEcbDecrypt decrypt data with key use DES ECB algorithm
|
||||
// len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/8qivmPeZy4P
|
||||
func DesEcbDecrypt(encrypted, key []byte) []byte {
|
||||
cipher, _ := des.NewCipher(generateDesKey(key))
|
||||
decrypted := make([]byte, len(encrypted))
|
||||
|
||||
for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
||||
cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
|
||||
}
|
||||
|
||||
trim := 0
|
||||
if len(decrypted) > 0 {
|
||||
trim = len(decrypted) - int(decrypted[len(decrypted)-1])
|
||||
}
|
||||
|
||||
return decrypted[:trim]
|
||||
}
|
||||
|
||||
// DesCbcEncrypt encrypt data with key use DES CBC algorithm
|
||||
// len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/4cC4QvWfe3_1
|
||||
func DesCbcEncrypt(data, key []byte) []byte {
|
||||
block, _ := des.NewCipher(key)
|
||||
data = pkcs7Padding(data, block.BlockSize())
|
||||
|
||||
encrypted := make([]byte, des.BlockSize+len(data))
|
||||
iv := encrypted[:des.BlockSize]
|
||||
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
mode.CryptBlocks(encrypted[des.BlockSize:], data)
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// DesCbcDecrypt decrypt data with key use DES CBC algorithm
|
||||
// len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/4cC4QvWfe3_1
|
||||
func DesCbcDecrypt(encrypted, key []byte) []byte {
|
||||
block, _ := des.NewCipher(key)
|
||||
|
||||
iv := encrypted[:des.BlockSize]
|
||||
encrypted = encrypted[des.BlockSize:]
|
||||
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(encrypted, encrypted)
|
||||
|
||||
decrypted := pkcs7UnPadding(encrypted)
|
||||
return decrypted
|
||||
}
|
||||
|
||||
// DesCtrCrypt encrypt data with key use DES CTR algorithm
|
||||
// len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/9-T6OjKpcdw
|
||||
func DesCtrCrypt(data, key []byte) []byte {
|
||||
block, _ := des.NewCipher(key)
|
||||
|
||||
iv := bytes.Repeat([]byte("1"), block.BlockSize())
|
||||
stream := cipher.NewCTR(block, iv)
|
||||
|
||||
dst := make([]byte, len(data))
|
||||
stream.XORKeyStream(dst, data)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// DesCfbEncrypt encrypt data with key use DES CFB algorithm
|
||||
// len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/y-eNxcFBlxL
|
||||
func DesCfbEncrypt(data, key []byte) []byte {
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
encrypted := make([]byte, des.BlockSize+len(data))
|
||||
iv := encrypted[:des.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stream := cipher.NewCFBEncrypter(block, iv)
|
||||
stream.XORKeyStream(encrypted[des.BlockSize:], data)
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// DesCfbDecrypt decrypt data with key use DES CFB algorithm
|
||||
// len(encrypted) should be great than 16, len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/y-eNxcFBlxL
|
||||
func DesCfbDecrypt(encrypted, key []byte) []byte {
|
||||
block, _ := des.NewCipher(key)
|
||||
if len(encrypted) < des.BlockSize {
|
||||
panic("encrypted data is too short")
|
||||
}
|
||||
iv := encrypted[:des.BlockSize]
|
||||
encrypted = encrypted[des.BlockSize:]
|
||||
|
||||
stream := cipher.NewCFBDecrypter(block, iv)
|
||||
stream.XORKeyStream(encrypted, encrypted)
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// DesOfbEncrypt encrypt data with key use DES OFB algorithm
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/74KmNadjN1J
|
||||
func DesOfbEncrypt(data, key []byte) []byte {
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
data = pkcs7Padding(data, des.BlockSize)
|
||||
encrypted := make([]byte, des.BlockSize+len(data))
|
||||
iv := encrypted[:des.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stream := cipher.NewOFB(block, iv)
|
||||
stream.XORKeyStream(encrypted[des.BlockSize:], data)
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// DesOfbDecrypt decrypt data with key use DES OFB algorithm
|
||||
// len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/74KmNadjN1J
|
||||
func DesOfbDecrypt(data, key []byte) []byte {
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
iv := data[:des.BlockSize]
|
||||
data = data[des.BlockSize:]
|
||||
if len(data)%des.BlockSize != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
decrypted := make([]byte, len(data))
|
||||
mode := cipher.NewOFB(block, iv)
|
||||
mode.XORKeyStream(decrypted, data)
|
||||
|
||||
decrypted = pkcs7UnPadding(decrypted)
|
||||
|
||||
return decrypted
|
||||
}
|
||||
|
||||
// GenerateRsaKey create rsa private and public pemo file.
|
||||
// Play: https://go.dev/play/p/zutRHrDqs0X
|
||||
func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) error {
|
||||
// private key
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, keySize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
derText := x509.MarshalPKCS1PrivateKey(privateKey)
|
||||
|
||||
block := pem.Block{
|
||||
Type: "rsa private key",
|
||||
Bytes: derText,
|
||||
}
|
||||
|
||||
file, err := os.Create(priKeyFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = pem.Encode(file, &block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file.Close()
|
||||
|
||||
// public key
|
||||
publicKey := privateKey.PublicKey
|
||||
|
||||
derpText, err := x509.MarshalPKIXPublicKey(&publicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
block = pem.Block{
|
||||
Type: "rsa public key",
|
||||
Bytes: derpText,
|
||||
}
|
||||
|
||||
file, err = os.Create(pubKeyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = pem.Encode(file, &block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RsaEncrypt encrypt data with ras algorithm.
|
||||
// Play: https://go.dev/play/p/rDqTT01SPkZ
|
||||
func RsaEncrypt(data []byte, pubKeyFileName string) []byte {
|
||||
file, err := os.Open(pubKeyFileName)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fileInfo, err := file.Stat()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer file.Close()
|
||||
buf := make([]byte, fileInfo.Size())
|
||||
|
||||
_, err = file.Read(buf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(buf)
|
||||
|
||||
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pubKey := pubInterface.(*rsa.PublicKey)
|
||||
|
||||
cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return cipherText
|
||||
}
|
||||
|
||||
// RsaDecrypt decrypt data with ras algorithm.
|
||||
// Play: https://go.dev/play/p/rDqTT01SPkZ
|
||||
func RsaDecrypt(data []byte, privateKeyFileName string) []byte {
|
||||
file, err := os.Open(privateKeyFileName)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fileInfo, err := file.Stat()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
buf := make([]byte, fileInfo.Size())
|
||||
defer file.Close()
|
||||
|
||||
_, err = file.Read(buf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(buf)
|
||||
|
||||
priKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
plainText, err := rsa.DecryptPKCS1v15(rand.Reader, priKey, data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return plainText
|
||||
}
|
||||
|
||||
// GenerateRsaKeyPair create rsa private and public key.
|
||||
// Play: https://go.dev/play/p/sSVmkfENKMz
|
||||
func GenerateRsaKeyPair(keySize int) (*rsa.PrivateKey, *rsa.PublicKey) {
|
||||
privateKey, _ := rsa.GenerateKey(rand.Reader, keySize)
|
||||
return privateKey, &privateKey.PublicKey
|
||||
}
|
||||
|
||||
// RsaEncryptOAEP encrypts the given data with RSA-OAEP.
|
||||
// Play: https://go.dev/play/p/sSVmkfENKMz
|
||||
func RsaEncryptOAEP(data []byte, label []byte, key rsa.PublicKey) ([]byte, error) {
|
||||
encryptedBytes, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, &key, data, label)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return encryptedBytes, nil
|
||||
}
|
||||
|
||||
// RsaDecryptOAEP decrypts the data with RSA-OAEP.
|
||||
// Play: https://go.dev/play/p/sSVmkfENKMz
|
||||
func RsaDecryptOAEP(ciphertext []byte, label []byte, key rsa.PrivateKey) ([]byte, error) {
|
||||
decryptedBytes, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, &key, ciphertext, label)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return decryptedBytes, nil
|
||||
}
|
||||
510
cryptor/crypto_example_test.go
Normal file
510
cryptor/crypto_example_test.go
Normal file
@@ -0,0 +1,510 @@
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func ExampleAesEcbEncrypt() {
|
||||
data := "hello"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
encrypted := AesEcbEncrypt([]byte(data), []byte(key))
|
||||
|
||||
decrypted := AesEcbDecrypt(encrypted, []byte(key))
|
||||
|
||||
fmt.Println(string(decrypted))
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleAesEcbDecrypt() {
|
||||
data := "hello"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
encrypted := AesEcbEncrypt([]byte(data), []byte(key))
|
||||
|
||||
decrypted := AesEcbDecrypt(encrypted, []byte(key))
|
||||
|
||||
fmt.Println(string(decrypted))
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleAesCbcEncrypt() {
|
||||
data := "hello"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
encrypted := AesCbcEncrypt([]byte(data), []byte(key))
|
||||
|
||||
decrypted := AesCbcDecrypt(encrypted, []byte(key))
|
||||
|
||||
fmt.Println(string(decrypted))
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleAesCbcDecrypt() {
|
||||
data := "hello"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
encrypted := AesCbcEncrypt([]byte(data), []byte(key))
|
||||
|
||||
decrypted := AesCbcDecrypt(encrypted, []byte(key))
|
||||
|
||||
fmt.Println(string(decrypted))
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleAesCtrCrypt() {
|
||||
data := "hello"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
encrypted := AesCtrCrypt([]byte(data), []byte(key))
|
||||
decrypted := AesCtrCrypt(encrypted, []byte(key))
|
||||
|
||||
fmt.Println(string(decrypted))
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleAesCfbEncrypt() {
|
||||
data := "hello"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
encrypted := AesCfbEncrypt([]byte(data), []byte(key))
|
||||
|
||||
decrypted := AesCfbDecrypt(encrypted, []byte(key))
|
||||
|
||||
fmt.Println(string(decrypted))
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleAesCfbDecrypt() {
|
||||
data := "hello"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
encrypted := AesCfbEncrypt([]byte(data), []byte(key))
|
||||
|
||||
decrypted := AesCfbDecrypt(encrypted, []byte(key))
|
||||
|
||||
fmt.Println(string(decrypted))
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleAesOfbEncrypt() {
|
||||
data := "hello"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
encrypted := AesOfbEncrypt([]byte(data), []byte(key))
|
||||
|
||||
decrypted := AesOfbDecrypt(encrypted, []byte(key))
|
||||
|
||||
fmt.Println(string(decrypted))
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleAesOfbDecrypt() {
|
||||
data := "hello"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
encrypted := AesOfbEncrypt([]byte(data), []byte(key))
|
||||
|
||||
decrypted := AesOfbDecrypt(encrypted, []byte(key))
|
||||
|
||||
fmt.Println(string(decrypted))
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleDesEcbEncrypt() {
|
||||
data := "hello"
|
||||
key := "abcdefgh"
|
||||
|
||||
encrypted := DesEcbEncrypt([]byte(data), []byte(key))
|
||||
|
||||
decrypted := DesEcbDecrypt(encrypted, []byte(key))
|
||||
|
||||
fmt.Println(string(decrypted))
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleDesEcbDecrypt() {
|
||||
data := "hello"
|
||||
key := "abcdefgh"
|
||||
|
||||
encrypted := DesEcbEncrypt([]byte(data), []byte(key))
|
||||
|
||||
decrypted := DesEcbDecrypt(encrypted, []byte(key))
|
||||
|
||||
fmt.Println(string(decrypted))
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleDesCbcEncrypt() {
|
||||
data := "hello"
|
||||
key := "abcdefgh"
|
||||
|
||||
encrypted := DesCbcEncrypt([]byte(data), []byte(key))
|
||||
|
||||
decrypted := DesCbcDecrypt(encrypted, []byte(key))
|
||||
|
||||
fmt.Println(string(decrypted))
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleDesCbcDecrypt() {
|
||||
data := "hello"
|
||||
key := "abcdefgh"
|
||||
|
||||
encrypted := DesCbcEncrypt([]byte(data), []byte(key))
|
||||
|
||||
decrypted := DesCbcDecrypt(encrypted, []byte(key))
|
||||
|
||||
fmt.Println(string(decrypted))
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleDesCtrCrypt() {
|
||||
data := "hello"
|
||||
key := "abcdefgh"
|
||||
|
||||
encrypted := DesCtrCrypt([]byte(data), []byte(key))
|
||||
decrypted := DesCtrCrypt(encrypted, []byte(key))
|
||||
|
||||
fmt.Println(string(decrypted))
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleDesCfbEncrypt() {
|
||||
data := "hello"
|
||||
key := "abcdefgh"
|
||||
|
||||
encrypted := DesCfbEncrypt([]byte(data), []byte(key))
|
||||
|
||||
decrypted := DesCfbDecrypt(encrypted, []byte(key))
|
||||
|
||||
fmt.Println(string(decrypted))
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleDesCfbDecrypt() {
|
||||
data := "hello"
|
||||
key := "abcdefgh"
|
||||
|
||||
encrypted := DesCfbEncrypt([]byte(data), []byte(key))
|
||||
|
||||
decrypted := DesCfbDecrypt(encrypted, []byte(key))
|
||||
|
||||
fmt.Println(string(decrypted))
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleDesOfbEncrypt() {
|
||||
data := "hello"
|
||||
key := "abcdefgh"
|
||||
|
||||
encrypted := DesOfbEncrypt([]byte(data), []byte(key))
|
||||
|
||||
decrypted := DesOfbDecrypt(encrypted, []byte(key))
|
||||
|
||||
fmt.Println(string(decrypted))
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleDesOfbDecrypt() {
|
||||
data := "hello"
|
||||
key := "abcdefgh"
|
||||
|
||||
encrypted := DesOfbEncrypt([]byte(data), []byte(key))
|
||||
|
||||
decrypted := DesOfbDecrypt(encrypted, []byte(key))
|
||||
|
||||
fmt.Println(string(decrypted))
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleGenerateRsaKey() {
|
||||
// Create ras private and public pem file
|
||||
err := GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("foo")
|
||||
|
||||
// Output:
|
||||
// foo
|
||||
}
|
||||
|
||||
func ExampleRsaEncrypt() {
|
||||
// Create ras private and public pem file
|
||||
err := GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data := []byte("hello")
|
||||
encrypted := RsaEncrypt(data, "rsa_public.pem")
|
||||
decrypted := RsaDecrypt(encrypted, "rsa_private.pem")
|
||||
|
||||
fmt.Println(string(decrypted))
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleRsaDecrypt() {
|
||||
// Create ras private and public pem file
|
||||
err := GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data := []byte("hello")
|
||||
encrypted := RsaEncrypt(data, "rsa_public.pem")
|
||||
decrypted := RsaDecrypt(encrypted, "rsa_private.pem")
|
||||
|
||||
fmt.Println(string(decrypted))
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleBase64StdEncode() {
|
||||
base64Str := Base64StdEncode("hello")
|
||||
|
||||
fmt.Println(base64Str)
|
||||
|
||||
// Output:
|
||||
// aGVsbG8=
|
||||
}
|
||||
|
||||
func ExampleBase64StdDecode() {
|
||||
str := Base64StdDecode("aGVsbG8=")
|
||||
|
||||
fmt.Println(str)
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleHmacMd5() {
|
||||
str := "hello"
|
||||
key := "12345"
|
||||
|
||||
hms := HmacMd5(str, key)
|
||||
fmt.Println(hms)
|
||||
|
||||
// Output:
|
||||
// e834306eab892d872525d4918a7a639a
|
||||
}
|
||||
|
||||
func ExampleHmacMd5WithBase64() {
|
||||
str := "hello"
|
||||
key := "12345"
|
||||
|
||||
hms := HmacMd5WithBase64(str, key)
|
||||
fmt.Println(hms)
|
||||
|
||||
// Output:
|
||||
// 6DQwbquJLYclJdSRinpjmg==
|
||||
}
|
||||
|
||||
func ExampleHmacSha1() {
|
||||
str := "hello"
|
||||
key := "12345"
|
||||
|
||||
hms := HmacSha1(str, key)
|
||||
fmt.Println(hms)
|
||||
|
||||
// Output:
|
||||
// 5c6a9db0cccb92e36ed0323fd09b7f936de9ace0
|
||||
}
|
||||
|
||||
func ExampleHmacSha1WithBase64() {
|
||||
str := "hello"
|
||||
key := "12345"
|
||||
|
||||
hms := HmacSha1WithBase64(str, key)
|
||||
fmt.Println(hms)
|
||||
|
||||
// Output:
|
||||
// XGqdsMzLkuNu0DI/0Jt/k23prOA=
|
||||
}
|
||||
|
||||
func ExampleHmacSha256() {
|
||||
str := "hello"
|
||||
key := "12345"
|
||||
|
||||
hms := HmacSha256(str, key)
|
||||
fmt.Println(hms)
|
||||
|
||||
// Output:
|
||||
// 315bb93c4e989862ba09cb62e05d73a5f376cb36f0d786edab0c320d059fde75
|
||||
}
|
||||
|
||||
func ExampleHmacSha256WithBase64() {
|
||||
str := "hello"
|
||||
key := "12345"
|
||||
|
||||
hms := HmacSha256WithBase64(str, key)
|
||||
fmt.Println(hms)
|
||||
|
||||
// Output:
|
||||
// MVu5PE6YmGK6Ccti4F1zpfN2yzbw14btqwwyDQWf3nU=
|
||||
}
|
||||
|
||||
func ExampleHmacSha512() {
|
||||
str := "hello"
|
||||
key := "12345"
|
||||
|
||||
hms := HmacSha512(str, key)
|
||||
fmt.Println(hms)
|
||||
|
||||
// Output:
|
||||
// dd8f1290a9dd23d354e2526d9a2e9ce8cffffdd37cb320800d1c6c13d2efc363288376a196c5458daf53f8e1aa6b45a6d856303d5c0a2064bff9785861d48cfc
|
||||
}
|
||||
|
||||
func ExampleHmacSha512WithBase64() {
|
||||
str := "hello"
|
||||
key := "12345"
|
||||
|
||||
hms := HmacSha512WithBase64(str, key)
|
||||
fmt.Println(hms)
|
||||
|
||||
// Output:
|
||||
// 3Y8SkKndI9NU4lJtmi6c6M///dN8syCADRxsE9Lvw2Mog3ahlsVFja9T+OGqa0Wm2FYwPVwKIGS/+XhYYdSM/A==
|
||||
}
|
||||
|
||||
func ExampleMd5String() {
|
||||
md5Str := Md5String("hello")
|
||||
fmt.Println(md5Str)
|
||||
|
||||
// Output:
|
||||
// 5d41402abc4b2a76b9719d911017c592
|
||||
}
|
||||
|
||||
func ExampleMd5StringWithBase64() {
|
||||
md5Str := Md5StringWithBase64("hello")
|
||||
fmt.Println(md5Str)
|
||||
|
||||
// Output:
|
||||
// XUFAKrxLKna5cZ2REBfFkg==
|
||||
}
|
||||
|
||||
func ExampleMd5Byte() {
|
||||
md5Str := Md5Byte([]byte{'a'})
|
||||
fmt.Println(md5Str)
|
||||
|
||||
// Output:
|
||||
// 0cc175b9c0f1b6a831c399e269772661
|
||||
}
|
||||
|
||||
func ExampleMd5ByteWithBase64() {
|
||||
md5Str := Md5ByteWithBase64([]byte("hello"))
|
||||
fmt.Println(md5Str)
|
||||
|
||||
// Output:
|
||||
// XUFAKrxLKna5cZ2REBfFkg==
|
||||
}
|
||||
|
||||
func ExampleSha1() {
|
||||
result := Sha1("hello")
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
|
||||
}
|
||||
|
||||
func ExampleSha1WithBase64() {
|
||||
result := Sha1WithBase64("hello")
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// qvTGHdzF6KLavt4PO0gs2a6pQ00=
|
||||
}
|
||||
|
||||
func ExampleSha256() {
|
||||
result := Sha256("hello")
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
|
||||
}
|
||||
|
||||
func ExampleSha256WithBase64() {
|
||||
result := Sha256WithBase64("hello")
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=
|
||||
}
|
||||
|
||||
func ExampleSha512() {
|
||||
result := Sha512("hello")
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043
|
||||
}
|
||||
|
||||
func ExampleSha512WithBase64() {
|
||||
result := Sha512WithBase64("hello")
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// m3HSJL1i83hdltRq0+o9czGb+8KJDKra4t/3JRlnPKcjI8PZm6XBHXx6zG4UuMXaDEZjR1wuXDre9G9zvN7AQw==
|
||||
}
|
||||
|
||||
func ExampleRsaEncryptOAEP() {
|
||||
pri, pub := GenerateRsaKeyPair(1024)
|
||||
|
||||
data := []byte("hello world")
|
||||
label := []byte("123456")
|
||||
|
||||
encrypted, err := RsaEncryptOAEP(data, label, *pub)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
decrypted, err := RsaDecryptOAEP([]byte(encrypted), label, *pri)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(string(decrypted))
|
||||
|
||||
// Output:
|
||||
// hello world
|
||||
}
|
||||
@@ -2,11 +2,11 @@ package cryptor
|
||||
|
||||
import "bytes"
|
||||
|
||||
func generateAesKey(key []byte) []byte {
|
||||
genKey := make([]byte, 16)
|
||||
func generateAesKey(key []byte, size int) []byte {
|
||||
genKey := make([]byte, size)
|
||||
copy(genKey, key)
|
||||
for i := 16; i < len(key); {
|
||||
for j := 0; j < 16 && i < len(key); j, i = j+1, i+1 {
|
||||
for i := size; i < len(key); {
|
||||
for j := 0; j < size && i < len(key); j, i = j+1, i+1 {
|
||||
genKey[j] ^= key[i]
|
||||
}
|
||||
}
|
||||
|
||||
170
cryptor/crypto_test.go
Normal file
170
cryptor/crypto_test.go
Normal file
@@ -0,0 +1,170 @@
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestAesEcbEncrypt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := "hello world"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
aesEcbEncrypt := AesEcbEncrypt([]byte(data), []byte(key))
|
||||
aesEcbDecrypt := AesEcbDecrypt(aesEcbEncrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestAesEcbEncrypt")
|
||||
assert.Equal(data, string(aesEcbDecrypt))
|
||||
}
|
||||
|
||||
func TestAesCbcEncrypt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := "hello world"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
aesCbcEncrypt := AesCbcEncrypt([]byte(data), []byte(key))
|
||||
aesCbcDecrypt := AesCbcDecrypt(aesCbcEncrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestAesCbcEncrypt")
|
||||
assert.Equal(data, string(aesCbcDecrypt))
|
||||
}
|
||||
|
||||
func TestAesCtrCrypt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := "hello world"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
aesCtrCrypt := AesCtrCrypt([]byte(data), []byte(key))
|
||||
aesCtrDeCrypt := AesCtrCrypt(aesCtrCrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestAesCtrCrypt")
|
||||
assert.Equal(data, string(aesCtrDeCrypt))
|
||||
}
|
||||
|
||||
func TestAesCfbEncrypt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := "hello world"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
aesCfbEncrypt := AesCfbEncrypt([]byte(data), []byte(key))
|
||||
aesCfbDecrypt := AesCfbDecrypt(aesCfbEncrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestAesCfbEncrypt")
|
||||
assert.Equal(data, string(aesCfbDecrypt))
|
||||
}
|
||||
|
||||
func TestAesOfbEncrypt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := "hello world"
|
||||
key := "abcdefghijklmnop"
|
||||
|
||||
aesOfbEncrypt := AesOfbEncrypt([]byte(data), []byte(key))
|
||||
aesOfbDecrypt := AesOfbDecrypt(aesOfbEncrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestAesOfbEncrypt")
|
||||
assert.Equal(data, string(aesOfbDecrypt))
|
||||
}
|
||||
|
||||
func TestDesEcbEncrypt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := "hello world"
|
||||
key := "abcdefgh"
|
||||
|
||||
desEcbEncrypt := DesEcbEncrypt([]byte(data), []byte(key))
|
||||
desEcbDecrypt := DesEcbDecrypt(desEcbEncrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestDesEcbEncrypt")
|
||||
assert.Equal(data, string(desEcbDecrypt))
|
||||
}
|
||||
|
||||
func TestDesCbcEncrypt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := "hello world"
|
||||
key := "abcdefgh"
|
||||
|
||||
desCbcEncrypt := DesCbcEncrypt([]byte(data), []byte(key))
|
||||
desCbcDecrypt := DesCbcDecrypt(desCbcEncrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestDesCbcEncrypt")
|
||||
assert.Equal(data, string(desCbcDecrypt))
|
||||
}
|
||||
|
||||
func TestDesCtrCrypt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := "hello world"
|
||||
key := "abcdefgh"
|
||||
|
||||
desCtrCrypt := DesCtrCrypt([]byte(data), []byte(key))
|
||||
desCtrDeCrypt := DesCtrCrypt(desCtrCrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestDesCtrCrypt")
|
||||
assert.Equal(data, string(desCtrDeCrypt))
|
||||
}
|
||||
|
||||
func TestDesCfbEncrypt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := "hello world"
|
||||
key := "abcdefgh"
|
||||
|
||||
desCfbEncrypt := DesCfbEncrypt([]byte(data), []byte(key))
|
||||
desCfbDecrypt := DesCfbDecrypt(desCfbEncrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestDesCfbEncrypt")
|
||||
assert.Equal(data, string(desCfbDecrypt))
|
||||
}
|
||||
|
||||
func TestDesOfbEncrypt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := "hello world"
|
||||
key := "abcdefgh"
|
||||
|
||||
desOfbEncrypt := DesOfbEncrypt([]byte(data), []byte(key))
|
||||
desOfbDecrypt := DesOfbDecrypt(desOfbEncrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestDesOfbEncrypt")
|
||||
assert.Equal(data, string(desOfbDecrypt))
|
||||
}
|
||||
|
||||
func TestRsaEncrypt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
err := GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem")
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
data := []byte("hello world")
|
||||
encrypted := RsaEncrypt(data, "rsa_public.pem")
|
||||
decrypted := RsaDecrypt(encrypted, "rsa_private.pem")
|
||||
|
||||
assert := internal.NewAssert(t, "TestRsaEncrypt")
|
||||
assert.Equal(string(data), string(decrypted))
|
||||
}
|
||||
|
||||
func TestRsaEncryptOAEP(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestRsaEncrypt")
|
||||
t.Parallel()
|
||||
|
||||
pri, pub := GenerateRsaKeyPair(1024)
|
||||
|
||||
data := []byte("hello world")
|
||||
label := []byte("123456")
|
||||
|
||||
encrypted, err := RsaEncryptOAEP(data, label, *pub)
|
||||
assert.IsNil(err)
|
||||
|
||||
decrypted, err := RsaDecryptOAEP([]byte(encrypted), label, *pri)
|
||||
|
||||
assert.IsNil(err)
|
||||
assert.Equal("hello world", string(decrypted))
|
||||
}
|
||||
178
cryptor/des.go
178
cryptor/des.go
@@ -1,178 +0,0 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package cryptor implements some util functions to encrypt and decrypt.
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
)
|
||||
|
||||
// DesEcbEncrypt encrypt data with key use DES ECB algorithm
|
||||
// len(key) should be 8
|
||||
func DesEcbEncrypt(data, key []byte) []byte {
|
||||
cipher, _ := des.NewCipher(generateDesKey(key))
|
||||
length := (len(data) + des.BlockSize) / des.BlockSize
|
||||
plain := make([]byte, length*des.BlockSize)
|
||||
copy(plain, data)
|
||||
|
||||
pad := byte(len(plain) - len(data))
|
||||
for i := len(data); i < len(plain); i++ {
|
||||
plain[i] = pad
|
||||
}
|
||||
|
||||
encrypted := make([]byte, len(plain))
|
||||
for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
||||
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
|
||||
}
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// DesEcbDecrypt decrypt data with key use DES ECB algorithm
|
||||
// len(key) should be 8
|
||||
func DesEcbDecrypt(encrypted, key []byte) []byte {
|
||||
cipher, _ := des.NewCipher(generateDesKey(key))
|
||||
decrypted := make([]byte, len(encrypted))
|
||||
|
||||
for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
||||
cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
|
||||
}
|
||||
|
||||
trim := 0
|
||||
if len(decrypted) > 0 {
|
||||
trim = len(decrypted) - int(decrypted[len(decrypted)-1])
|
||||
}
|
||||
|
||||
return decrypted[:trim]
|
||||
}
|
||||
|
||||
// DesCbcEncrypt encrypt data with key use DES CBC algorithm
|
||||
// len(key) should be 8
|
||||
func DesCbcEncrypt(data, key []byte) []byte {
|
||||
block, _ := des.NewCipher(key)
|
||||
data = pkcs7Padding(data, block.BlockSize())
|
||||
|
||||
encrypted := make([]byte, des.BlockSize+len(data))
|
||||
iv := encrypted[:des.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
mode.CryptBlocks(encrypted[des.BlockSize:], data)
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// DesCbcDecrypt decrypt data with key use DES CBC algorithm
|
||||
// len(key) should be 8
|
||||
func DesCbcDecrypt(encrypted, key []byte) []byte {
|
||||
block, _ := des.NewCipher(key)
|
||||
|
||||
iv := encrypted[:des.BlockSize]
|
||||
encrypted = encrypted[des.BlockSize:]
|
||||
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(encrypted, encrypted)
|
||||
|
||||
decrypted := pkcs7UnPadding(encrypted)
|
||||
return decrypted
|
||||
}
|
||||
|
||||
// DesCtrCrypt encrypt data with key use DES CTR algorithm
|
||||
// len(key) should be 8
|
||||
func DesCtrCrypt(data, key []byte) []byte {
|
||||
block, _ := des.NewCipher(key)
|
||||
|
||||
iv := bytes.Repeat([]byte("1"), block.BlockSize())
|
||||
stream := cipher.NewCTR(block, iv)
|
||||
|
||||
dst := make([]byte, len(data))
|
||||
stream.XORKeyStream(dst, data)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// DesCfbEncrypt encrypt data with key use DES CFB algorithm
|
||||
// len(key) should be 8
|
||||
func DesCfbEncrypt(data, key []byte) []byte {
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
encrypted := make([]byte, des.BlockSize+len(data))
|
||||
iv := encrypted[:des.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stream := cipher.NewCFBEncrypter(block, iv)
|
||||
stream.XORKeyStream(encrypted[des.BlockSize:], data)
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// DesCfbDecrypt decrypt data with key use DES CFB algorithm
|
||||
// len(encrypted) should be great than 16, len(key) should be 8
|
||||
func DesCfbDecrypt(encrypted, key []byte) []byte {
|
||||
block, _ := des.NewCipher(key)
|
||||
if len(encrypted) < des.BlockSize {
|
||||
panic("encrypted data is too short")
|
||||
}
|
||||
iv := encrypted[:des.BlockSize]
|
||||
encrypted = encrypted[des.BlockSize:]
|
||||
|
||||
stream := cipher.NewCFBDecrypter(block, iv)
|
||||
stream.XORKeyStream(encrypted, encrypted)
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// DesOfbEncrypt encrypt data with key use DES OFB algorithm
|
||||
// len(key) should be 16, 24 or 32
|
||||
func DesOfbEncrypt(data, key []byte) []byte {
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
data = pkcs7Padding(data, des.BlockSize)
|
||||
encrypted := make([]byte, des.BlockSize+len(data))
|
||||
iv := encrypted[:des.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stream := cipher.NewOFB(block, iv)
|
||||
stream.XORKeyStream(encrypted[des.BlockSize:], data)
|
||||
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// DesOfbDecrypt decrypt data with key use DES OFB algorithm
|
||||
// len(key) should be 8
|
||||
func DesOfbDecrypt(data, key []byte) []byte {
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
iv := data[:des.BlockSize]
|
||||
data = data[des.BlockSize:]
|
||||
if len(data)%des.BlockSize != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
decrypted := make([]byte, len(data))
|
||||
mode := cipher.NewOFB(block, iv)
|
||||
mode.XORKeyStream(decrypted, data)
|
||||
|
||||
decrypted = pkcs7UnPadding(decrypted)
|
||||
|
||||
return decrypted
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/internal"
|
||||
)
|
||||
|
||||
func TestDesEcbEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefgh"
|
||||
|
||||
desEcbEncrypt := DesEcbEncrypt([]byte(data), []byte(key))
|
||||
desEcbDecrypt := DesEcbDecrypt(desEcbEncrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestDesEcbEncrypt")
|
||||
assert.Equal(data, string(desEcbDecrypt))
|
||||
}
|
||||
|
||||
func TestDesCbcEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefgh"
|
||||
|
||||
desCbcEncrypt := DesCbcEncrypt([]byte(data), []byte(key))
|
||||
desCbcDecrypt := DesCbcDecrypt(desCbcEncrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestDesCbcEncrypt")
|
||||
assert.Equal(data, string(desCbcDecrypt))
|
||||
}
|
||||
|
||||
func TestDesCtrCrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefgh"
|
||||
|
||||
desCtrCrypt := DesCtrCrypt([]byte(data), []byte(key))
|
||||
desCtrDeCrypt := DesCtrCrypt(desCtrCrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestDesCtrCrypt")
|
||||
assert.Equal(data, string(desCtrDeCrypt))
|
||||
}
|
||||
|
||||
func TestDesCfbEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefgh"
|
||||
|
||||
desCfbEncrypt := DesCfbEncrypt([]byte(data), []byte(key))
|
||||
desCfbDecrypt := DesCfbDecrypt(desCfbEncrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestDesCfbEncrypt")
|
||||
assert.Equal(data, string(desCfbDecrypt))
|
||||
}
|
||||
|
||||
func TestDesOfbEncrypt(t *testing.T) {
|
||||
data := "hello world"
|
||||
key := "abcdefgh"
|
||||
|
||||
desOfbEncrypt := DesOfbEncrypt([]byte(data), []byte(key))
|
||||
desOfbDecrypt := DesOfbDecrypt(desOfbEncrypt, []byte(key))
|
||||
|
||||
assert := internal.NewAssert(t, "TestDesOfbEncrypt")
|
||||
assert.Equal(data, string(desOfbDecrypt))
|
||||
}
|
||||
118
cryptor/rsa.go
118
cryptor/rsa.go
@@ -1,118 +0,0 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package cryptor implements some util functions to encrypt and decrypt.
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"os"
|
||||
)
|
||||
|
||||
// GenerateRsaKey make a rsa private key, and return key file name
|
||||
// Generated key file is `rsa_private.pem` and `rsa_public.pem` in current path
|
||||
func GenerateRsaKey(keySize int, priKeyFile, pubKeyFile string) error {
|
||||
// private key
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, keySize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
derText := x509.MarshalPKCS1PrivateKey(privateKey)
|
||||
|
||||
block := pem.Block{
|
||||
Type: "rsa private key",
|
||||
Bytes: derText,
|
||||
}
|
||||
|
||||
//file,err := os.Create("rsa_private.pem")
|
||||
file, err := os.Create(priKeyFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pem.Encode(file, &block)
|
||||
file.Close()
|
||||
|
||||
// public key
|
||||
publicKey := privateKey.PublicKey
|
||||
|
||||
derpText, err := x509.MarshalPKIXPublicKey(&publicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
block = pem.Block{
|
||||
Type: "rsa public key",
|
||||
Bytes: derpText,
|
||||
}
|
||||
|
||||
//file,err = os.Create("rsa_public.pem")
|
||||
file, err = os.Create(pubKeyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pem.Encode(file, &block)
|
||||
file.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RsaEncrypt encrypt data with ras algorithm
|
||||
func RsaEncrypt(data []byte, pubKeyFileName string) []byte {
|
||||
file, err := os.Open(pubKeyFileName)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fileInfo, err := file.Stat()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer file.Close()
|
||||
buf := make([]byte, fileInfo.Size())
|
||||
file.Read(buf)
|
||||
|
||||
block, _ := pem.Decode(buf)
|
||||
|
||||
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pubKey := pubInterface.(*rsa.PublicKey)
|
||||
|
||||
cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return cipherText
|
||||
}
|
||||
|
||||
// RsaDecrypt decrypt data with ras algorithm
|
||||
func RsaDecrypt(data []byte, privateKeyFileName string) []byte {
|
||||
file, err := os.Open(privateKeyFileName)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fileInfo, err := file.Stat()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
buf := make([]byte, fileInfo.Size())
|
||||
defer file.Close()
|
||||
file.Read(buf)
|
||||
|
||||
block, _ := pem.Decode(buf)
|
||||
|
||||
priKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
plainText, err := rsa.DecryptPKCS1v15(rand.Reader, priKey, data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return plainText
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/internal"
|
||||
)
|
||||
|
||||
func TestRsaEncrypt(t *testing.T) {
|
||||
err := GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem")
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
data := []byte("hello world")
|
||||
encrypted := RsaEncrypt(data, "rsa_public.pem")
|
||||
decrypted := RsaDecrypt(encrypted, "rsa_private.pem")
|
||||
|
||||
assert := internal.NewAssert(t, "TestRsaEncrypt")
|
||||
assert.Equal(string(data), string(decrypted))
|
||||
}
|
||||
193
datastructure/hashmap/hashmap.go
Normal file
193
datastructure/hashmap/hashmap.go
Normal file
@@ -0,0 +1,193 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure implements some data structure. hashmap structure.
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var defaultMapCapacity uint64 = 1 << 10
|
||||
|
||||
type mapNode struct {
|
||||
key any
|
||||
value any
|
||||
next *mapNode
|
||||
}
|
||||
|
||||
// HashMap implements a hash map
|
||||
type HashMap struct {
|
||||
capacity uint64
|
||||
size uint64
|
||||
table []*mapNode
|
||||
}
|
||||
|
||||
// NewHashMap return a HashMap instance
|
||||
func NewHashMap() *HashMap {
|
||||
return &HashMap{
|
||||
capacity: defaultMapCapacity,
|
||||
table: make([]*mapNode, defaultMapCapacity),
|
||||
}
|
||||
}
|
||||
|
||||
// NewHashMapWithCapacity return a HashMap instance with given size and capacity
|
||||
func NewHashMapWithCapacity(size, capacity uint64) *HashMap {
|
||||
return &HashMap{
|
||||
size: size,
|
||||
capacity: capacity,
|
||||
table: make([]*mapNode, capacity),
|
||||
}
|
||||
}
|
||||
|
||||
// Get return the value of given key in hashmap
|
||||
func (hm *HashMap) Get(key any) any {
|
||||
hashValue := hm.hash(key)
|
||||
node := hm.table[hashValue]
|
||||
for node != nil {
|
||||
if reflect.DeepEqual(node.key, key) {
|
||||
return node.value
|
||||
}
|
||||
node = node.next
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetOrDefault return the value of given key in hashmap, if not found return default value
|
||||
func (hm *HashMap) GetOrDefault(key any, defaultValue any) any {
|
||||
value := hm.Get(key)
|
||||
if value == nil {
|
||||
return defaultValue
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// Put new key value in hashmap
|
||||
func (hm *HashMap) Put(key any, value any) {
|
||||
hm.putValue(hm.hash(key), key, value)
|
||||
}
|
||||
|
||||
func (hm *HashMap) putValue(hash uint64, key, value any) {
|
||||
if hm.capacity == 0 {
|
||||
hm.capacity = defaultMapCapacity
|
||||
hm.table = make([]*mapNode, defaultMapCapacity)
|
||||
}
|
||||
|
||||
node := hm.table[hash]
|
||||
if node == nil {
|
||||
hm.table[hash] = newMapNode(key, value)
|
||||
} else if node.key == key {
|
||||
hm.table[hash] = newMapNodeWithNext(key, value, node)
|
||||
} else {
|
||||
hm.resize()
|
||||
hm.putValue(hash, value, value)
|
||||
}
|
||||
hm.size++
|
||||
}
|
||||
|
||||
// Delete item by given key in hashmap
|
||||
func (hm *HashMap) Delete(key any) {
|
||||
hash := hm.hash(key)
|
||||
node := hm.table[hash]
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
|
||||
hm.table = append(hm.table[:hash], hm.table[hash+1:]...)
|
||||
hm.size--
|
||||
}
|
||||
|
||||
// Contains checks if given key is in hashmap or not
|
||||
func (hm *HashMap) Contains(key any) bool {
|
||||
node := hm.table[hm.hash(key)]
|
||||
for node != nil {
|
||||
if reflect.DeepEqual(node.key, key) {
|
||||
return true
|
||||
}
|
||||
node = node.next
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Iterate executes iteratee funcation for every key and value pair of hashmap (random order)
|
||||
func (hm *HashMap) Iterate(iteratee func(key, value any)) {
|
||||
if hm.size > 0 {
|
||||
for i := 0; i < len(hm.table); i++ {
|
||||
item := hm.table[i]
|
||||
if item != nil {
|
||||
iteratee(item.key, item.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Keys returns a slice of the hashmap's keys (random order)
|
||||
func (hm *HashMap) Keys() []any {
|
||||
keys := make([]any, int(hm.size))
|
||||
index := 0
|
||||
if hm.size > 0 {
|
||||
hm.Iterate(func(key, value any) {
|
||||
keys[index] = key
|
||||
index++
|
||||
})
|
||||
}
|
||||
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns a slice of the hashmap's keys (random order)
|
||||
func (hm *HashMap) Values() []any {
|
||||
values := make([]any, int(hm.size))
|
||||
index := 0
|
||||
if hm.size > 0 {
|
||||
hm.Iterate(func(key, value any) {
|
||||
values[index] = value
|
||||
index++
|
||||
})
|
||||
}
|
||||
|
||||
return values
|
||||
}
|
||||
|
||||
func (hm *HashMap) resize() {
|
||||
hm.capacity <<= 1
|
||||
|
||||
tempTable := hm.table
|
||||
|
||||
hm.table = make([]*mapNode, hm.capacity)
|
||||
|
||||
for i := 0; i < len(tempTable); i++ {
|
||||
node := tempTable[i]
|
||||
if node == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
hm.table[hm.hash(node.key)] = node
|
||||
}
|
||||
}
|
||||
|
||||
func (hm *HashMap) hash(key any) uint64 {
|
||||
h := fnv.New64a()
|
||||
_, _ = h.Write([]byte(fmt.Sprintf("%v", key)))
|
||||
|
||||
hashValue := h.Sum64()
|
||||
|
||||
return (hm.capacity - 1) & (hashValue ^ (hashValue >> 16))
|
||||
}
|
||||
|
||||
func newMapNode(key, value any) *mapNode {
|
||||
return &mapNode{
|
||||
key: key,
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func newMapNodeWithNext(key, value any, next *mapNode) *mapNode {
|
||||
return &mapNode{
|
||||
key: key,
|
||||
value: value,
|
||||
next: next,
|
||||
}
|
||||
}
|
||||
107
datastructure/hashmap/hashmap_test.go
Normal file
107
datastructure/hashmap/hashmap_test.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestHashMap_PutAndGet(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestHashMap_PutAndGet")
|
||||
|
||||
hm := NewHashMap()
|
||||
|
||||
hm.Put("abc", 3)
|
||||
assert.Equal(3, hm.Get("abc"))
|
||||
assert.IsNil(hm.Get("abcd"))
|
||||
|
||||
hm.Put("abc", 4)
|
||||
assert.Equal(4, hm.Get("abc"))
|
||||
}
|
||||
|
||||
func TestHashMap_Resize(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestHashMap_Resize")
|
||||
|
||||
hm := NewHashMapWithCapacity(3, 3)
|
||||
|
||||
for i := 0; i < 20; i++ {
|
||||
hm.Put(i, 10)
|
||||
}
|
||||
|
||||
assert.Equal(10, hm.Get(5))
|
||||
}
|
||||
|
||||
func TestHashMap_Delete(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestHashMap_Delete")
|
||||
|
||||
hm := NewHashMap()
|
||||
|
||||
hm.Put("abc", 3)
|
||||
assert.Equal(3, hm.Get("abc"))
|
||||
|
||||
hm.Delete("abc")
|
||||
assert.IsNil(hm.Get("abc"))
|
||||
}
|
||||
|
||||
func TestHashMap_Contains(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestHashMap_Contains")
|
||||
|
||||
hm := NewHashMap()
|
||||
assert.Equal(false, hm.Contains("abc"))
|
||||
|
||||
hm.Put("abc", 3)
|
||||
assert.Equal(true, hm.Contains("abc"))
|
||||
}
|
||||
|
||||
func TestHashMap_KeysValues(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestHashMap_KeysValues")
|
||||
|
||||
hm := NewHashMap()
|
||||
|
||||
hm.Put("a", 1)
|
||||
hm.Put("b", 2)
|
||||
hm.Put("c", 3)
|
||||
|
||||
keys := hm.Keys()
|
||||
values := hm.Values()
|
||||
|
||||
assert.Equal(3, len(values))
|
||||
assert.Equal(3, len(keys))
|
||||
}
|
||||
|
||||
func TestHashMap_Keys(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestHashMap_Keys")
|
||||
|
||||
hm := NewHashMap()
|
||||
|
||||
hm.Put("a", 1)
|
||||
hm.Put("b", 2)
|
||||
hm.Put("c", 3)
|
||||
|
||||
keys := hm.Keys()
|
||||
|
||||
assert.Equal(3, len(keys))
|
||||
}
|
||||
|
||||
func TestHashMap_GetOrDefault(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestHashMap_GetOrDefault")
|
||||
|
||||
hm := NewHashMap()
|
||||
|
||||
hm.Put("a", 1)
|
||||
hm.Put("b", 2)
|
||||
hm.Put("c", 3)
|
||||
|
||||
assert.Equal(1, hm.GetOrDefault("a", 5))
|
||||
assert.Equal(5, hm.GetOrDefault("d", 5))
|
||||
}
|
||||
203
datastructure/heap/maxheap.go
Normal file
203
datastructure/heap/maxheap.go
Normal file
@@ -0,0 +1,203 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure implements some data structure. MaxHeap is a binary max heap.
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/duke-git/lancet/v2/constraints"
|
||||
)
|
||||
|
||||
// MaxHeap implements a binary max heap
|
||||
// type T should implements Compare function in constraints.Comparator interface.
|
||||
type MaxHeap[T any] struct {
|
||||
data []T
|
||||
comparator constraints.Comparator
|
||||
}
|
||||
|
||||
// NewMaxHeap returns a MaxHeap instance with the given comparator.
|
||||
func NewMaxHeap[T any](comparator constraints.Comparator) *MaxHeap[T] {
|
||||
return &MaxHeap[T]{
|
||||
data: make([]T, 0),
|
||||
comparator: comparator,
|
||||
}
|
||||
}
|
||||
|
||||
// BuildMaxHeap builds a MaxHeap instance with data and given comparator.
|
||||
func BuildMaxHeap[T any](data []T, comparator constraints.Comparator) *MaxHeap[T] {
|
||||
heap := &MaxHeap[T]{
|
||||
data: make([]T, 0, len(data)),
|
||||
comparator: comparator,
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
heap.Push(v)
|
||||
}
|
||||
|
||||
return heap
|
||||
}
|
||||
|
||||
// Push value into the heap
|
||||
func (h *MaxHeap[T]) Push(value T) {
|
||||
h.data = append(h.data, value)
|
||||
h.heapifyUp(len(h.data) - 1)
|
||||
}
|
||||
|
||||
// heapifyUp heapify the data from bottom to top
|
||||
func (h *MaxHeap[T]) heapifyUp(i int) {
|
||||
for h.comparator.Compare(h.data[parentIndex(i)], h.data[i]) < 0 {
|
||||
h.swap(parentIndex(i), i)
|
||||
i = parentIndex(i)
|
||||
}
|
||||
}
|
||||
|
||||
// Pop return the largest value, and remove it from the heap
|
||||
// if heap is empty, return zero value and fasle
|
||||
func (h *MaxHeap[T]) Pop() (T, bool) {
|
||||
var val T
|
||||
if h.Size() == 0 {
|
||||
return val, false
|
||||
}
|
||||
|
||||
val = h.data[0]
|
||||
l := len(h.data) - 1
|
||||
|
||||
h.data[0] = h.data[l]
|
||||
h.data = h.data[:l]
|
||||
h.heapifyDown(0)
|
||||
|
||||
return val, true
|
||||
}
|
||||
|
||||
// heapifyDown heapify the data from top to bottom
|
||||
func (h *MaxHeap[T]) heapifyDown(i int) {
|
||||
lastIndex := len(h.data) - 1
|
||||
l, r := leftChildIndex(i), rightChildIndex(i)
|
||||
childToCompare := 0
|
||||
|
||||
for l <= lastIndex {
|
||||
if l == lastIndex {
|
||||
childToCompare = l
|
||||
} else if h.comparator.Compare(h.data[l], h.data[r]) > 0 {
|
||||
childToCompare = l
|
||||
} else {
|
||||
childToCompare = r
|
||||
}
|
||||
|
||||
if h.comparator.Compare(h.data[i], h.data[childToCompare]) < 0 {
|
||||
h.swap(i, childToCompare)
|
||||
i = childToCompare
|
||||
l, r = leftChildIndex(i), rightChildIndex(i)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Peek returns the largest element from the heap without removing it.
|
||||
// if heap is empty, it returns zero value and false.
|
||||
func (h *MaxHeap[T]) Peek() (T, bool) {
|
||||
if h.Size() == 0 {
|
||||
var val T
|
||||
return val, false
|
||||
}
|
||||
|
||||
return h.data[0], true
|
||||
}
|
||||
|
||||
// Size return the number of elements in the heap
|
||||
func (h *MaxHeap[T]) Size() int {
|
||||
return len(h.data)
|
||||
}
|
||||
|
||||
// Data return data of the heap
|
||||
func (h *MaxHeap[T]) Data() []T {
|
||||
return h.data
|
||||
}
|
||||
|
||||
// PrintStructure print the structure of the heap
|
||||
func (h *MaxHeap[T]) PrintStructure() {
|
||||
level := 1
|
||||
data := h.data
|
||||
length := len(h.data)
|
||||
index := 0
|
||||
|
||||
list := [][]string{}
|
||||
temp := []string{}
|
||||
for index < length {
|
||||
start := powerTwo(level-1) - 1
|
||||
end := start + powerTwo(level-1) - 1
|
||||
|
||||
temp = append(temp, fmt.Sprintf("%v", data[index]))
|
||||
index++
|
||||
|
||||
if index > end || index >= length {
|
||||
list = append(list, temp)
|
||||
temp = []string{}
|
||||
|
||||
if index < length {
|
||||
level++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastNum := powerTwo(level - 1)
|
||||
lastLen := lastNum + (lastNum - 1)
|
||||
|
||||
heapTree := make([][]string, level)
|
||||
for i := 0; i < level; i++ {
|
||||
heapTree[i] = make([]string, lastLen)
|
||||
for j := 0; j < lastLen; j++ {
|
||||
heapTree[i][j] = ""
|
||||
}
|
||||
}
|
||||
|
||||
for k := 0; k < len(list); k++ {
|
||||
vals := list[k]
|
||||
tempLevel := level - k
|
||||
st := powerTwo(tempLevel-1) - 1
|
||||
for _, v := range vals {
|
||||
heapTree[k][st] = v
|
||||
gap := powerTwo(tempLevel)
|
||||
st = st + gap
|
||||
}
|
||||
}
|
||||
|
||||
for m := 0; m < level; m++ {
|
||||
for n := 0; n < lastLen; n++ {
|
||||
val := heapTree[m][n]
|
||||
if val == "" {
|
||||
fmt.Print(" ")
|
||||
} else {
|
||||
fmt.Print(val)
|
||||
}
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
// parentIndex get parent index of the given index
|
||||
func parentIndex(i int) int {
|
||||
return (i - 1) / 2
|
||||
}
|
||||
|
||||
// leftChildIndex get left child index of the given index
|
||||
func leftChildIndex(i int) int {
|
||||
return 2*i + 1
|
||||
}
|
||||
|
||||
// rightChildIndex get right child index of the given index
|
||||
func rightChildIndex(i int) int {
|
||||
return 2*i + 2
|
||||
}
|
||||
|
||||
// swap two elements in the heap
|
||||
func (h *MaxHeap[T]) swap(i, j int) {
|
||||
h.data[i], h.data[j] = h.data[j], h.data[i]
|
||||
}
|
||||
|
||||
func powerTwo(n int) int {
|
||||
return 1 << n
|
||||
}
|
||||
98
datastructure/heap/maxheap_test.go
Normal file
98
datastructure/heap/maxheap_test.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func TestMaxHeap_BuildMaxHeap(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestMaxHeap_BuildMaxHeap")
|
||||
|
||||
values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11}
|
||||
heap := BuildMaxHeap(values, &intComparator{})
|
||||
|
||||
expected := []int{12, 9, 11, 4, 8, 10, 7, 1, 3, 5, 6, 2}
|
||||
assert.Equal(expected, heap.data)
|
||||
|
||||
assert.Equal(12, heap.Size())
|
||||
}
|
||||
|
||||
func TestMaxHeap_Push(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestMaxHeap_Push")
|
||||
|
||||
heap := NewMaxHeap[int](&intComparator{})
|
||||
values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11}
|
||||
|
||||
for _, v := range values {
|
||||
heap.Push(v)
|
||||
}
|
||||
|
||||
expected := []int{12, 9, 11, 4, 8, 10, 7, 1, 3, 5, 6, 2}
|
||||
assert.Equal(expected, heap.data)
|
||||
|
||||
assert.Equal(12, heap.Size())
|
||||
|
||||
heap.PrintStructure()
|
||||
}
|
||||
|
||||
func TestMaxHeap_Pop(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestMaxHeap_Pop")
|
||||
|
||||
heap := NewMaxHeap[int](&intComparator{})
|
||||
|
||||
_, ok := heap.Pop()
|
||||
assert.Equal(false, ok)
|
||||
|
||||
values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11}
|
||||
for _, v := range values {
|
||||
heap.Push(v)
|
||||
}
|
||||
|
||||
val, ok := heap.Pop()
|
||||
assert.Equal(12, val)
|
||||
assert.Equal(true, ok)
|
||||
assert.Equal(11, heap.Size())
|
||||
}
|
||||
|
||||
func TestMaxHeap_Peek(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestMaxHeap_Peek")
|
||||
|
||||
heap := NewMaxHeap[int](&intComparator{})
|
||||
|
||||
_, ok := heap.Peek()
|
||||
assert.Equal(false, ok)
|
||||
|
||||
values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11}
|
||||
for _, v := range values {
|
||||
heap.Push(v)
|
||||
}
|
||||
|
||||
val, ok := heap.Peek()
|
||||
assert.Equal(12, val)
|
||||
assert.Equal(true, ok)
|
||||
|
||||
assert.Equal(12, heap.Size())
|
||||
}
|
||||
242
datastructure/link/doublylink.go
Normal file
242
datastructure/link/doublylink.go
Normal file
@@ -0,0 +1,242 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure contains some data structure. Link structure contains SinglyLink and DoublyLink.
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/duke-git/lancet/v2/datastructure"
|
||||
)
|
||||
|
||||
// DoublyLink is a linked list. Whose node has a generic Value, Pre pointer points to a previous node of the dl, Next pointer points to a next node of the dl.
|
||||
type DoublyLink[T any] struct {
|
||||
Head *datastructure.LinkNode[T]
|
||||
length int
|
||||
}
|
||||
|
||||
// NewDoublyLink return *DoublyLink instance
|
||||
func NewDoublyLink[T any]() *DoublyLink[T] {
|
||||
return &DoublyLink[T]{Head: nil}
|
||||
}
|
||||
|
||||
// InsertAtHead insert value into doubly linklist at head index
|
||||
func (dl *DoublyLink[T]) InsertAtHead(value T) {
|
||||
newNode := datastructure.NewLinkNode(value)
|
||||
size := dl.Size()
|
||||
|
||||
if size == 0 {
|
||||
dl.Head = newNode
|
||||
dl.length++
|
||||
return
|
||||
}
|
||||
|
||||
newNode.Next = dl.Head
|
||||
newNode.Pre = nil
|
||||
|
||||
dl.Head.Pre = newNode
|
||||
dl.Head = newNode
|
||||
|
||||
dl.length++
|
||||
}
|
||||
|
||||
// InsertAtTail insert value into doubly linklist at tail index
|
||||
func (dl *DoublyLink[T]) InsertAtTail(value T) {
|
||||
current := dl.Head
|
||||
if current == nil {
|
||||
dl.InsertAtHead(value)
|
||||
return
|
||||
}
|
||||
|
||||
for current.Next != nil {
|
||||
current = current.Next
|
||||
}
|
||||
|
||||
newNode := datastructure.NewLinkNode(value)
|
||||
newNode.Next = nil
|
||||
newNode.Pre = current
|
||||
current.Next = newNode
|
||||
|
||||
dl.length++
|
||||
}
|
||||
|
||||
// InsertAt insert value into doubly linklist at index
|
||||
// param `index` should between [0, length], if index do not meet the conditions, do nothing
|
||||
func (dl *DoublyLink[T]) InsertAt(index int, value T) {
|
||||
size := dl.length
|
||||
if index < 0 || index > size {
|
||||
return
|
||||
}
|
||||
|
||||
if index == 0 {
|
||||
dl.InsertAtHead(value)
|
||||
return
|
||||
}
|
||||
|
||||
if index == size {
|
||||
dl.InsertAtTail(value)
|
||||
return
|
||||
}
|
||||
|
||||
i := 0
|
||||
current := dl.Head
|
||||
|
||||
for current != nil {
|
||||
if i == index-1 {
|
||||
newNode := datastructure.NewLinkNode(value)
|
||||
newNode.Next = current.Next
|
||||
newNode.Pre = current
|
||||
|
||||
current.Next = newNode
|
||||
dl.length++
|
||||
|
||||
return
|
||||
}
|
||||
i++
|
||||
current = current.Next
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteAtHead delete value in doubly linklist at head index
|
||||
func (dl *DoublyLink[T]) DeleteAtHead() {
|
||||
if dl.Head == nil {
|
||||
return
|
||||
}
|
||||
|
||||
current := dl.Head
|
||||
dl.Head = current.Next
|
||||
dl.Head.Pre = nil
|
||||
dl.length--
|
||||
}
|
||||
|
||||
// DeleteAtTail delete value in doubly linklist at tail
|
||||
func (dl *DoublyLink[T]) DeleteAtTail() {
|
||||
if dl.Head == nil {
|
||||
return
|
||||
}
|
||||
|
||||
current := dl.Head
|
||||
if current.Next == nil {
|
||||
dl.DeleteAtHead()
|
||||
}
|
||||
|
||||
for current.Next.Next != nil {
|
||||
current = current.Next
|
||||
}
|
||||
|
||||
current.Next = nil
|
||||
dl.length--
|
||||
}
|
||||
|
||||
// DeleteAt delete value in doubly linklist at index
|
||||
// param `index` should be [0, len(DoublyLink)-1]
|
||||
func (dl *DoublyLink[T]) DeleteAt(index int) {
|
||||
if dl.Head == nil {
|
||||
return
|
||||
}
|
||||
|
||||
current := dl.Head
|
||||
if current.Next == nil || index == 0 {
|
||||
dl.DeleteAtHead()
|
||||
}
|
||||
|
||||
if index == dl.length-1 {
|
||||
dl.DeleteAtTail()
|
||||
}
|
||||
|
||||
if index < 0 || index > dl.length-1 {
|
||||
return
|
||||
}
|
||||
|
||||
i := 0
|
||||
for current != nil {
|
||||
if i == index-1 {
|
||||
current.Next = current.Next.Next
|
||||
dl.length--
|
||||
return
|
||||
}
|
||||
i++
|
||||
current = current.Next
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse the linked list
|
||||
func (dl *DoublyLink[T]) Reverse() {
|
||||
current := dl.Head
|
||||
var temp *datastructure.LinkNode[T]
|
||||
|
||||
for current != nil {
|
||||
temp = current.Pre
|
||||
current.Pre = current.Next
|
||||
current.Next = temp
|
||||
current = current.Pre
|
||||
}
|
||||
|
||||
if temp != nil {
|
||||
dl.Head = temp.Pre
|
||||
}
|
||||
}
|
||||
|
||||
// GetMiddleNode return node at middle index of linked list
|
||||
func (dl *DoublyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] {
|
||||
if dl.Head == nil {
|
||||
return nil
|
||||
}
|
||||
if dl.Head.Next == nil {
|
||||
return dl.Head
|
||||
}
|
||||
fast := dl.Head
|
||||
slow := dl.Head
|
||||
|
||||
for fast != nil {
|
||||
fast = fast.Next
|
||||
|
||||
if fast != nil {
|
||||
fast = fast.Next
|
||||
slow = slow.Next
|
||||
} else {
|
||||
return slow
|
||||
}
|
||||
}
|
||||
return slow
|
||||
}
|
||||
|
||||
// Size return the count of doubly linked list
|
||||
func (dl *DoublyLink[T]) Size() int {
|
||||
return dl.length
|
||||
}
|
||||
|
||||
// Values return slice of all doubly linklist node value
|
||||
func (dl *DoublyLink[T]) Values() []T {
|
||||
result := []T{}
|
||||
current := dl.Head
|
||||
for current != nil {
|
||||
result = append(result, current.Value)
|
||||
current = current.Next
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Print all nodes info of a linked list
|
||||
func (dl *DoublyLink[T]) Print() {
|
||||
current := dl.Head
|
||||
info := "[ "
|
||||
for current != nil {
|
||||
info += fmt.Sprintf("%+v, ", current)
|
||||
current = current.Next
|
||||
}
|
||||
info += " ]"
|
||||
fmt.Println(info)
|
||||
}
|
||||
|
||||
// IsEmpty checks if dl is empty or not
|
||||
func (dl *DoublyLink[T]) IsEmpty() bool {
|
||||
return dl.length == 0
|
||||
}
|
||||
|
||||
// Clear all nodes in doubly linklist
|
||||
func (dl *DoublyLink[T]) Clear() {
|
||||
dl.Head = nil
|
||||
dl.length = 0
|
||||
}
|
||||
181
datastructure/link/doublylink_test.go
Normal file
181
datastructure/link/doublylink_test.go
Normal file
@@ -0,0 +1,181 @@
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestDoublyLink_InsertAtFirst(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestDoublyLink_InsertAtFirst")
|
||||
|
||||
link := NewDoublyLink[int]()
|
||||
link.InsertAtHead(1)
|
||||
link.InsertAtHead(2)
|
||||
link.InsertAtHead(3)
|
||||
link.Print()
|
||||
|
||||
expected := []int{3, 2, 1}
|
||||
values := link.Values()
|
||||
|
||||
assert.Equal(expected, values)
|
||||
}
|
||||
|
||||
func TestDoublyLink_InsertAtTail(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestDoublyLink_InsertAtTail")
|
||||
|
||||
link := NewDoublyLink[int]()
|
||||
link.InsertAtTail(1)
|
||||
link.InsertAtTail(2)
|
||||
link.InsertAtTail(3)
|
||||
link.Print()
|
||||
|
||||
expected := []int{1, 2, 3}
|
||||
values := link.Values()
|
||||
|
||||
assert.Equal(expected, values)
|
||||
}
|
||||
|
||||
func TestDoublyLink_InsertAt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestDoublyLink_InsertAt")
|
||||
|
||||
link := NewDoublyLink[int]()
|
||||
link.InsertAt(1, 1) //do nothing
|
||||
link.InsertAt(0, 1)
|
||||
link.InsertAt(1, 2)
|
||||
link.InsertAt(2, 4)
|
||||
link.InsertAt(2, 3)
|
||||
|
||||
expected := []int{1, 2, 3, 4}
|
||||
values := link.Values()
|
||||
|
||||
assert.Equal(expected, values)
|
||||
}
|
||||
|
||||
func TestDoublyLink_DeleteAtHead(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestDoublyLink_DeleteAtHead")
|
||||
|
||||
link := NewDoublyLink[int]()
|
||||
link.DeleteAtHead()
|
||||
|
||||
link.InsertAtTail(1)
|
||||
link.InsertAtTail(2)
|
||||
link.InsertAtTail(3)
|
||||
link.InsertAtTail(4)
|
||||
|
||||
link.DeleteAtHead()
|
||||
|
||||
expected := []int{2, 3, 4}
|
||||
values := link.Values()
|
||||
|
||||
assert.Equal(expected, values)
|
||||
}
|
||||
|
||||
func TestDoublyLink_DeleteAtTail(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestDoublyLink_DeleteAtTail")
|
||||
|
||||
link := NewDoublyLink[int]()
|
||||
link.DeleteAtTail()
|
||||
|
||||
link.InsertAtTail(1)
|
||||
link.InsertAtTail(2)
|
||||
link.InsertAtTail(3)
|
||||
link.InsertAtTail(4)
|
||||
|
||||
link.DeleteAtTail()
|
||||
|
||||
expected := []int{1, 2, 3}
|
||||
values := link.Values()
|
||||
|
||||
assert.Equal(expected, values)
|
||||
}
|
||||
|
||||
func TestDoublyLink_DeleteAt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestDoublyLink_DeleteAt")
|
||||
|
||||
link := NewDoublyLink[int]()
|
||||
link.DeleteAt(0)
|
||||
|
||||
link.InsertAtTail(1)
|
||||
link.InsertAtTail(2)
|
||||
link.InsertAtTail(3)
|
||||
link.InsertAtTail(4)
|
||||
link.InsertAtTail(5)
|
||||
|
||||
link.DeleteAt(0)
|
||||
assert.Equal([]int{2, 3, 4, 5}, link.Values())
|
||||
|
||||
link.DeleteAt(3)
|
||||
assert.Equal([]int{2, 3, 4}, link.Values())
|
||||
|
||||
link.DeleteAt(1)
|
||||
assert.Equal(2, link.Size())
|
||||
assert.Equal([]int{2, 4}, link.Values())
|
||||
}
|
||||
|
||||
func TestDoublyLink_Reverse(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestDoublyLink_Reverse")
|
||||
|
||||
link := NewDoublyLink[int]()
|
||||
link.InsertAtTail(1)
|
||||
link.InsertAtTail(2)
|
||||
link.InsertAtTail(3)
|
||||
link.InsertAtTail(4)
|
||||
|
||||
link.Reverse()
|
||||
link.Print()
|
||||
assert.Equal([]int{4, 3, 2, 1}, link.Values())
|
||||
}
|
||||
|
||||
func TestDoublyLink_GetMiddleNode(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestDoublyLink_GetMiddleNode")
|
||||
|
||||
link := NewDoublyLink[int]()
|
||||
link.InsertAtTail(1)
|
||||
link.InsertAtTail(2)
|
||||
link.InsertAtTail(3)
|
||||
link.InsertAtTail(4)
|
||||
|
||||
middle1 := link.GetMiddleNode()
|
||||
assert.Equal(3, middle1.Value)
|
||||
|
||||
link.InsertAtTail(5)
|
||||
link.InsertAtTail(6)
|
||||
link.InsertAtTail(7)
|
||||
middle2 := link.GetMiddleNode()
|
||||
assert.Equal(4, middle2.Value)
|
||||
}
|
||||
|
||||
func TestDoublyLink_Clear(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestDoublyLink_Clear")
|
||||
|
||||
link := NewDoublyLink[int]()
|
||||
assert.Equal(true, link.IsEmpty())
|
||||
assert.Equal(0, link.Size())
|
||||
|
||||
link.InsertAtTail(1)
|
||||
assert.Equal(false, link.IsEmpty())
|
||||
assert.Equal(1, link.Size())
|
||||
|
||||
link.Clear()
|
||||
assert.Equal(true, link.IsEmpty())
|
||||
assert.Equal(0, link.Size())
|
||||
}
|
||||
245
datastructure/link/singlylink.go
Normal file
245
datastructure/link/singlylink.go
Normal file
@@ -0,0 +1,245 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure contains some data structure. Link structure contains SinglyLink and DoublyLink.
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/duke-git/lancet/v2/datastructure"
|
||||
)
|
||||
|
||||
// SinglyLink is a linked list. Whose node has a Value generics and Next pointer points to a next node of the sl.
|
||||
type SinglyLink[T any] struct {
|
||||
Head *datastructure.LinkNode[T]
|
||||
length int
|
||||
}
|
||||
|
||||
// NewSinglyLink return *SinglyLink instance
|
||||
func NewSinglyLink[T any]() *SinglyLink[T] {
|
||||
return &SinglyLink[T]{Head: nil}
|
||||
}
|
||||
|
||||
// InsertAtHead insert value into singly linklist at head index
|
||||
func (sl *SinglyLink[T]) InsertAtHead(value T) {
|
||||
newNode := datastructure.NewLinkNode(value)
|
||||
newNode.Next = sl.Head
|
||||
sl.Head = newNode
|
||||
sl.length++
|
||||
}
|
||||
|
||||
// InsertAtTail insert value into singly linklist at tail index
|
||||
func (sl *SinglyLink[T]) InsertAtTail(value T) {
|
||||
current := sl.Head
|
||||
if current == nil {
|
||||
sl.InsertAtHead(value)
|
||||
return
|
||||
}
|
||||
|
||||
for current.Next != nil {
|
||||
current = current.Next
|
||||
}
|
||||
|
||||
newNode := datastructure.NewLinkNode(value)
|
||||
newNode.Next = nil
|
||||
current.Next = newNode
|
||||
|
||||
sl.length++
|
||||
}
|
||||
|
||||
// InsertAt insert value into singly linklist at index
|
||||
// param `index` should between [0, len(SinglyLink)], if index do not meet the conditions, do nothing
|
||||
func (sl *SinglyLink[T]) InsertAt(index int, value T) {
|
||||
size := sl.length
|
||||
if index < 0 || index > size {
|
||||
return
|
||||
}
|
||||
|
||||
if index == 0 {
|
||||
sl.InsertAtHead(value)
|
||||
return
|
||||
}
|
||||
|
||||
if index == size {
|
||||
sl.InsertAtTail(value)
|
||||
return
|
||||
}
|
||||
|
||||
i := 0
|
||||
current := sl.Head
|
||||
|
||||
for current != nil {
|
||||
if i == index-1 {
|
||||
newNode := datastructure.NewLinkNode(value)
|
||||
newNode.Next = current.Next
|
||||
current.Next = newNode
|
||||
sl.length++
|
||||
return
|
||||
}
|
||||
i++
|
||||
current = current.Next
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteAtHead delete value in singly linklist at head index
|
||||
func (sl *SinglyLink[T]) DeleteAtHead() {
|
||||
if sl.Head == nil {
|
||||
return
|
||||
}
|
||||
|
||||
current := sl.Head
|
||||
sl.Head = current.Next
|
||||
sl.length--
|
||||
}
|
||||
|
||||
// DeleteAtTail delete value in singly linklist at tail
|
||||
func (sl *SinglyLink[T]) DeleteAtTail() {
|
||||
if sl.Head == nil {
|
||||
return
|
||||
}
|
||||
|
||||
current := sl.Head
|
||||
if current.Next == nil {
|
||||
sl.DeleteAtHead()
|
||||
}
|
||||
|
||||
for current.Next.Next != nil {
|
||||
current = current.Next
|
||||
}
|
||||
|
||||
current.Next = nil
|
||||
sl.length--
|
||||
}
|
||||
|
||||
// DeleteAt delete value in singly linklist at index
|
||||
// param `index` should be [0, len(SinglyLink)-1]
|
||||
func (sl *SinglyLink[T]) DeleteAt(index int) {
|
||||
if sl.Head == nil {
|
||||
return
|
||||
}
|
||||
current := sl.Head
|
||||
if current.Next == nil || index == 0 {
|
||||
sl.DeleteAtHead()
|
||||
}
|
||||
|
||||
if index == sl.length-1 {
|
||||
sl.DeleteAtTail()
|
||||
}
|
||||
|
||||
if index < 0 || index > sl.length-1 {
|
||||
return
|
||||
}
|
||||
|
||||
i := 0
|
||||
for current != nil {
|
||||
if i == index-1 {
|
||||
current.Next = current.Next.Next
|
||||
sl.length--
|
||||
return
|
||||
}
|
||||
i++
|
||||
current = current.Next
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteValue delete value in singly linklist
|
||||
func (sl *SinglyLink[T]) DeleteValue(value T) {
|
||||
if sl.Head == nil {
|
||||
return
|
||||
}
|
||||
dummyHead := datastructure.NewLinkNode(value)
|
||||
dummyHead.Next = sl.Head
|
||||
current := dummyHead
|
||||
|
||||
for current.Next != nil {
|
||||
if reflect.DeepEqual(current.Next.Value, value) {
|
||||
current.Next = current.Next.Next
|
||||
sl.length--
|
||||
} else {
|
||||
current = current.Next
|
||||
}
|
||||
}
|
||||
|
||||
sl.Head = dummyHead.Next
|
||||
}
|
||||
|
||||
// Reverse the linked list
|
||||
func (sl *SinglyLink[T]) Reverse() {
|
||||
var pre, next *datastructure.LinkNode[T]
|
||||
|
||||
current := sl.Head
|
||||
|
||||
for current != nil {
|
||||
next = current.Next
|
||||
current.Next = pre
|
||||
pre = current
|
||||
current = next
|
||||
}
|
||||
|
||||
sl.Head = pre
|
||||
}
|
||||
|
||||
// GetMiddleNode return node at middle index of linked list
|
||||
func (sl *SinglyLink[T]) GetMiddleNode() *datastructure.LinkNode[T] {
|
||||
if sl.Head == nil {
|
||||
return nil
|
||||
}
|
||||
if sl.Head.Next == nil {
|
||||
return sl.Head
|
||||
}
|
||||
fast := sl.Head
|
||||
slow := sl.Head
|
||||
|
||||
for fast != nil {
|
||||
fast = fast.Next
|
||||
|
||||
if fast != nil {
|
||||
fast = fast.Next
|
||||
slow = slow.Next
|
||||
} else {
|
||||
return slow
|
||||
}
|
||||
}
|
||||
return slow
|
||||
}
|
||||
|
||||
// Size return the count of singly linked list
|
||||
func (sl *SinglyLink[T]) Size() int {
|
||||
return sl.length
|
||||
}
|
||||
|
||||
// Values return slice of all singly linklist node value
|
||||
func (sl *SinglyLink[T]) Values() []T {
|
||||
result := []T{}
|
||||
current := sl.Head
|
||||
for current != nil {
|
||||
result = append(result, current.Value)
|
||||
current = current.Next
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// IsEmpty checks if sl is empty or not
|
||||
func (sl *SinglyLink[T]) IsEmpty() bool {
|
||||
return sl.length == 0
|
||||
}
|
||||
|
||||
// Clear all the node in singly linklist
|
||||
func (sl *SinglyLink[T]) Clear() {
|
||||
sl.Head = nil
|
||||
sl.length = 0
|
||||
}
|
||||
|
||||
// Print all nodes info of a linked list
|
||||
func (sl *SinglyLink[T]) Print() {
|
||||
current := sl.Head
|
||||
info := "[ "
|
||||
for current != nil {
|
||||
info += fmt.Sprintf("%+v, ", current)
|
||||
current = current.Next
|
||||
}
|
||||
info += " ]"
|
||||
fmt.Println(info)
|
||||
}
|
||||
201
datastructure/link/singlylink_test.go
Normal file
201
datastructure/link/singlylink_test.go
Normal file
@@ -0,0 +1,201 @@
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestSinglyLink_InsertAtFirst(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSinglyLink_InsertAtFirst")
|
||||
|
||||
link := NewSinglyLink[int]()
|
||||
link.InsertAtHead(1)
|
||||
link.InsertAtHead(2)
|
||||
link.InsertAtHead(3)
|
||||
link.Print()
|
||||
|
||||
expected := []int{3, 2, 1}
|
||||
values := link.Values()
|
||||
|
||||
assert.Equal(expected, values)
|
||||
}
|
||||
|
||||
func TestSinglyLink_InsertAtTail(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSinglyLink_InsertAtTail")
|
||||
|
||||
link := NewSinglyLink[int]()
|
||||
link.InsertAtTail(1)
|
||||
link.InsertAtTail(2)
|
||||
link.InsertAtTail(3)
|
||||
link.Print()
|
||||
|
||||
expected := []int{1, 2, 3}
|
||||
values := link.Values()
|
||||
|
||||
assert.Equal(expected, values)
|
||||
}
|
||||
|
||||
func TestSinglyLink_InsertAt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSinglyLink_InsertAt")
|
||||
|
||||
link := NewSinglyLink[int]()
|
||||
|
||||
link.InsertAt(1, 1) //do nothing
|
||||
|
||||
link.InsertAt(0, 1)
|
||||
link.InsertAt(1, 2)
|
||||
link.InsertAt(2, 4)
|
||||
link.InsertAt(2, 3)
|
||||
|
||||
link.Print()
|
||||
|
||||
expected := []int{1, 2, 3, 4}
|
||||
values := link.Values()
|
||||
|
||||
assert.Equal(expected, values)
|
||||
}
|
||||
|
||||
func TestSinglyLink_DeleteAtHead(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSinglyLink_DeleteAtHead")
|
||||
|
||||
link := NewSinglyLink[int]()
|
||||
|
||||
link.DeleteAtHead()
|
||||
|
||||
link.InsertAtTail(1)
|
||||
link.InsertAtTail(2)
|
||||
link.InsertAtTail(3)
|
||||
link.InsertAtTail(4)
|
||||
|
||||
link.DeleteAtHead()
|
||||
link.Print()
|
||||
|
||||
expected := []int{2, 3, 4}
|
||||
values := link.Values()
|
||||
|
||||
assert.Equal(expected, values)
|
||||
}
|
||||
|
||||
func TestSinglyLink_DeleteAtTail(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSinglyLink_DeleteAtTail")
|
||||
|
||||
link := NewSinglyLink[int]()
|
||||
link.InsertAtTail(1)
|
||||
link.InsertAtTail(2)
|
||||
link.InsertAtTail(3)
|
||||
link.InsertAtTail(4)
|
||||
|
||||
link.DeleteAtTail()
|
||||
|
||||
expected := []int{1, 2, 3}
|
||||
values := link.Values()
|
||||
|
||||
assert.Equal(expected, values)
|
||||
}
|
||||
|
||||
func TestSinglyLink_DeleteValue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSinglyLink_DeleteValue")
|
||||
|
||||
link := NewSinglyLink[int]()
|
||||
link.InsertAtTail(1)
|
||||
link.InsertAtTail(2)
|
||||
link.InsertAtTail(2)
|
||||
link.InsertAtTail(3)
|
||||
link.InsertAtTail(4)
|
||||
|
||||
link.DeleteValue(2)
|
||||
assert.Equal([]int{1, 3, 4}, link.Values())
|
||||
|
||||
link.DeleteValue(1)
|
||||
assert.Equal([]int{3, 4}, link.Values())
|
||||
}
|
||||
|
||||
func TestSinglyLink_DeleteAt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSinglyLink_DeleteAt")
|
||||
|
||||
link := NewSinglyLink[int]()
|
||||
link.InsertAtTail(1)
|
||||
link.InsertAtTail(2)
|
||||
link.InsertAtTail(3)
|
||||
link.InsertAtTail(4)
|
||||
link.InsertAtTail(5)
|
||||
|
||||
link.DeleteAt(0)
|
||||
assert.Equal([]int{2, 3, 4, 5}, link.Values())
|
||||
|
||||
link.DeleteAt(3)
|
||||
assert.Equal([]int{2, 3, 4}, link.Values())
|
||||
|
||||
link.DeleteAt(1)
|
||||
assert.Equal(2, link.Size())
|
||||
assert.Equal([]int{2, 4}, link.Values())
|
||||
}
|
||||
|
||||
func TestSinglyLink_Reverse(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSinglyLink_Reverse")
|
||||
|
||||
link := NewSinglyLink[int]()
|
||||
link.InsertAtTail(1)
|
||||
link.InsertAtTail(2)
|
||||
link.InsertAtTail(3)
|
||||
link.InsertAtTail(4)
|
||||
|
||||
link.Reverse()
|
||||
assert.Equal([]int{4, 3, 2, 1}, link.Values())
|
||||
}
|
||||
|
||||
func TestSinglyLink_GetMiddleNode(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSinglyLink_GetMiddleNode")
|
||||
|
||||
link := NewSinglyLink[int]()
|
||||
link.InsertAtTail(1)
|
||||
link.InsertAtTail(2)
|
||||
link.InsertAtTail(3)
|
||||
link.InsertAtTail(4)
|
||||
|
||||
middle1 := link.GetMiddleNode()
|
||||
assert.Equal(3, middle1.Value)
|
||||
|
||||
link.InsertAtTail(5)
|
||||
link.InsertAtTail(6)
|
||||
link.InsertAtTail(7)
|
||||
|
||||
middle2 := link.GetMiddleNode()
|
||||
assert.Equal(4, middle2.Value)
|
||||
}
|
||||
|
||||
func TestSinglyLink_Clear(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSinglyLink_Clear")
|
||||
|
||||
link := NewSinglyLink[int]()
|
||||
|
||||
assert.Equal(true, link.IsEmpty())
|
||||
assert.Equal(0, link.Size())
|
||||
|
||||
link.InsertAtTail(1)
|
||||
assert.Equal(false, link.IsEmpty())
|
||||
assert.Equal(1, link.Size())
|
||||
|
||||
link.Clear()
|
||||
assert.Equal(true, link.IsEmpty())
|
||||
assert.Equal(0, link.Size())
|
||||
}
|
||||
350
datastructure/list/copyonwritelist.go
Normal file
350
datastructure/list/copyonwritelist.go
Normal file
@@ -0,0 +1,350 @@
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type CopyOnWriteList[T any] struct {
|
||||
data []T
|
||||
lock sync.Locker
|
||||
}
|
||||
|
||||
// NewCopyOnWriteList Creates an empty list.
|
||||
func NewCopyOnWriteList[T any](data []T) *CopyOnWriteList[T] {
|
||||
return &CopyOnWriteList[T]{data: data, lock: &sync.RWMutex{}}
|
||||
}
|
||||
|
||||
func (c *CopyOnWriteList[T]) getList() []T {
|
||||
return c.data
|
||||
}
|
||||
func (c *CopyOnWriteList[T]) setList(data []T) {
|
||||
c.data = data
|
||||
}
|
||||
|
||||
// Size returns the number of elements in this list.
|
||||
func (c *CopyOnWriteList[T]) Size() int {
|
||||
return len(c.getList())
|
||||
}
|
||||
|
||||
// IsEmpty returns true if this list contains no elements.
|
||||
func (c *CopyOnWriteList[T]) IsEmpty() bool {
|
||||
return c.Size() == 0
|
||||
}
|
||||
|
||||
// Contain returns true if this list contains the specified element.
|
||||
func (c *CopyOnWriteList[T]) Contain(e T) bool {
|
||||
list := c.getList()
|
||||
return indexOf(e, list, 0, c.Size()) >= 0
|
||||
}
|
||||
|
||||
// ValueOf returns the index of the first occurrence of the specified element in this list, or null if this list does not contain the element.
|
||||
func (c *CopyOnWriteList[T]) ValueOf(index int) (*T, bool) {
|
||||
list := c.getList()
|
||||
if index < 0 || index >= len(c.data) {
|
||||
return nil, false
|
||||
}
|
||||
return get(list, index), true
|
||||
}
|
||||
|
||||
// IndexOf returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element.
|
||||
func (c *CopyOnWriteList[T]) IndexOf(e T) int {
|
||||
list := c.getList()
|
||||
return indexOf(e, list, 0, c.Size())
|
||||
}
|
||||
|
||||
// indexOf returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element.
|
||||
// start the start position of the search (inclusive)
|
||||
// end the end position of the search (exclusive)
|
||||
func indexOf[T any](o T, e []T, start int, end int) int {
|
||||
if start >= end {
|
||||
return -1
|
||||
}
|
||||
for i := start; i < end; i++ {
|
||||
if reflect.DeepEqual(e[i], o) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// LastIndexOf returns the index of the last occurrence of the specified element in this list, or -1 if this list does not contain the element.
|
||||
func (c *CopyOnWriteList[T]) LastIndexOf(e T) int {
|
||||
list := c.getList()
|
||||
return lastIndexOf(e, list, 0, c.Size())
|
||||
}
|
||||
|
||||
// lastIndexOf returns the index of the last occurrence of the specified element in this list, or -1 if this list does not contain the element.
|
||||
// start the start position of the search (inclusive)
|
||||
// end the end position of the search (exclusive)
|
||||
func lastIndexOf[T any](o T, e []T, start int, end int) int {
|
||||
if start >= end {
|
||||
return -1
|
||||
}
|
||||
for i := end - 1; i >= start; i-- {
|
||||
if reflect.DeepEqual(e[i], o) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// get returns the element at the specified position in this list.
|
||||
func get[T any](o []T, index int) *T {
|
||||
return &o[index]
|
||||
}
|
||||
|
||||
// Get returns the element at the specified position in this list.
|
||||
func (c *CopyOnWriteList[T]) Get(index int) *T {
|
||||
list := c.getList()
|
||||
if index < 0 || index >= len(list) {
|
||||
return nil
|
||||
}
|
||||
return get(list, index)
|
||||
}
|
||||
|
||||
func (c *CopyOnWriteList[T]) set(index int, e T) (oldValue *T) {
|
||||
lock := c.lock
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
list := c.getList()
|
||||
oldValue = get(list, index)
|
||||
|
||||
if reflect.DeepEqual(oldValue, e) {
|
||||
c.setList(list)
|
||||
} else {
|
||||
newList := make([]T, len(list))
|
||||
copy(newList, list)
|
||||
newList[index] = e
|
||||
c.setList(newList)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Set replaces the element at the specified position in this list with the specified element.
|
||||
func (c *CopyOnWriteList[T]) Set(index int, e T) (oldValue *T, ok bool) {
|
||||
list := c.getList()
|
||||
if index < 0 || index >= len(list) {
|
||||
return oldValue, false
|
||||
}
|
||||
return c.set(index, e), true
|
||||
}
|
||||
|
||||
// Add appends the specified element to the end of this list.
|
||||
func (c *CopyOnWriteList[T]) Add(e T) bool {
|
||||
lock := c.lock
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
list := c.getList()
|
||||
newList := make([]T, len(list)+1)
|
||||
copy(newList, list)
|
||||
newList[len(list)] = e
|
||||
c.setList(newList)
|
||||
return true
|
||||
}
|
||||
|
||||
// AddAll appends all the elements in the specified collection to the end of this list
|
||||
func (c *CopyOnWriteList[T]) AddAll(e []T) bool {
|
||||
lock := c.lock
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
list := c.getList()
|
||||
newList := make([]T, len(list)+len(e))
|
||||
copy(newList, list)
|
||||
copy(newList[len(list):], e)
|
||||
c.setList(newList)
|
||||
return true
|
||||
}
|
||||
|
||||
// AddByIndex inserts the specified element at the specified position in this list.
|
||||
func (c *CopyOnWriteList[T]) AddByIndex(index int, e T) bool {
|
||||
lock := c.lock
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
list := c.getList()
|
||||
length := len(list)
|
||||
if index < 0 || index > length {
|
||||
return false
|
||||
}
|
||||
var newList []T
|
||||
var numMove = length - index
|
||||
if numMove == 0 {
|
||||
newList = make([]T, length+1)
|
||||
copy(newList, list)
|
||||
} else {
|
||||
newList = make([]T, length+1)
|
||||
copy(newList, list[:index])
|
||||
copy(newList[index+1:], list[index:])
|
||||
}
|
||||
newList[index] = e
|
||||
c.setList(newList)
|
||||
return true
|
||||
}
|
||||
|
||||
// delete removes the element at the specified position in this list.
|
||||
func (c *CopyOnWriteList[T]) delete(index int) *T {
|
||||
lock := c.lock
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
list := c.getList()
|
||||
length := len(list)
|
||||
|
||||
oldValue := get(list, index)
|
||||
numMove := length - index - 1
|
||||
var newList []T
|
||||
if numMove == 0 {
|
||||
newList = make([]T, length-1)
|
||||
copy(newList, list[:index])
|
||||
} else {
|
||||
newList = make([]T, length-1)
|
||||
copy(newList, list[:index])
|
||||
copy(newList[index:], list[index+1:])
|
||||
}
|
||||
|
||||
c.setList(newList)
|
||||
return oldValue
|
||||
}
|
||||
|
||||
// DeleteAt removes the element at the specified position in this list.
|
||||
func (c *CopyOnWriteList[T]) DeleteAt(index int) (*T, bool) {
|
||||
list := c.getList()
|
||||
if index < 0 || index >= len(list) {
|
||||
return nil, false
|
||||
}
|
||||
return c.delete(index), true
|
||||
}
|
||||
|
||||
// DeleteBy removes the first occurrence of the specified element from this list, if it is present.
|
||||
func (c *CopyOnWriteList[T]) DeleteBy(o T) (*T, bool) {
|
||||
list := c.getList()
|
||||
index := indexOf(o, list, 0, len(list))
|
||||
if index == -1 {
|
||||
return nil, false
|
||||
}
|
||||
return c.delete(index), true
|
||||
}
|
||||
|
||||
// DeleteRange removes from this list all the elements whose index is between fromIndex, inclusive, and toIndex, exclusive.
|
||||
// left close and right open
|
||||
func (c *CopyOnWriteList[T]) DeleteRange(start int, end int) {
|
||||
lock := c.lock
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
list := c.getList()
|
||||
length := len(list)
|
||||
if start < 0 || end > length || start > end {
|
||||
return
|
||||
}
|
||||
var newList []T
|
||||
numMove := length - end
|
||||
if numMove == 0 {
|
||||
newList = make([]T, length-(end-start))
|
||||
copy(newList, list[:start])
|
||||
} else {
|
||||
newList = make([]T, length-(end-start))
|
||||
copy(newList, list[:start])
|
||||
copy(newList[start:], list[end:])
|
||||
}
|
||||
c.setList(newList)
|
||||
}
|
||||
|
||||
// DeleteIf removes all the elements of this collection that satisfy the given predicate.
|
||||
func (c *CopyOnWriteList[T]) DeleteIf(f func(T) bool) {
|
||||
lock := c.lock
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
list := c.getList()
|
||||
length := len(list)
|
||||
var newList []T
|
||||
for i := 0; i < length; i++ {
|
||||
if !f(list[i]) {
|
||||
newList = append(newList, list[i])
|
||||
}
|
||||
}
|
||||
c.setList(newList)
|
||||
}
|
||||
|
||||
// Equal returns true if the specified object is equal to this list.
|
||||
func (c *CopyOnWriteList[T]) Equal(other *[]T) bool {
|
||||
if other == nil {
|
||||
return false
|
||||
}
|
||||
if c.Size() != len(*other) {
|
||||
return false
|
||||
}
|
||||
list := c.getList()
|
||||
otherList := NewCopyOnWriteList(*other).getList()
|
||||
for i := 0; i < len(list); i++ {
|
||||
if !reflect.DeepEqual(list[i], otherList[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Clear removes all the elements from this list.
|
||||
func (c *CopyOnWriteList[T]) Clear() {
|
||||
lock := c.lock
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
list := c.getList()
|
||||
list = make([]T, 0)
|
||||
c.setList(list)
|
||||
}
|
||||
|
||||
// Merge a tow list to one, change the list
|
||||
func (c *CopyOnWriteList[T]) Merge(other []T) {
|
||||
lock := c.lock
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
list := c.getList()
|
||||
list = append(list, other...)
|
||||
c.setList(list)
|
||||
}
|
||||
|
||||
// ForEach performs the given action for each element of the Iterable until all elements have been processed
|
||||
// or the action throws an exception.
|
||||
func (c *CopyOnWriteList[T]) ForEach(f func(T)) {
|
||||
list := c.getList()
|
||||
for i := 0; i < len(list); i++ {
|
||||
f(list[i])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Sort sorts this list according to the order induced by the specified Comparator.
|
||||
func (c *CopyOnWriteList[T]) Sort(compare func(o1 T, o2 T) bool) {
|
||||
lock := c.lock
|
||||
lock.Lock()
|
||||
list := c.getList()
|
||||
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
return compare(list[i], list[j])
|
||||
})
|
||||
|
||||
c.setList(list)
|
||||
}
|
||||
|
||||
func (c *CopyOnWriteList[T]) SubList(start int, end int) (newList []T) {
|
||||
lock := c.lock
|
||||
lock.Lock()
|
||||
list := c.getList()
|
||||
length := len(list)
|
||||
defer lock.Unlock()
|
||||
if start < 0 || end > length || start > end {
|
||||
return []T{}
|
||||
}
|
||||
newList = make([]T, end-start)
|
||||
copy(newList, list[start:end])
|
||||
c.setList(newList)
|
||||
return
|
||||
}
|
||||
235
datastructure/list/copyonwritelist_test.go
Normal file
235
datastructure/list/copyonwritelist_test.go
Normal file
@@ -0,0 +1,235 @@
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCopyOnWriteList_ValueOf(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
|
||||
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_IndexOf")
|
||||
of, ok := list.ValueOf(3)
|
||||
assert.Equal(4, *of)
|
||||
assert.Equal(true, ok)
|
||||
|
||||
_, ok = list.ValueOf(6)
|
||||
assert.Equal(false, ok)
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_Contain(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_Contains")
|
||||
assert.Equal(true, list.Contain(3))
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_IsEmpty(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{})
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_IsEmpty")
|
||||
assert.Equal(true, list.IsEmpty())
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_Size(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_size")
|
||||
assert.Equal(5, list.Size())
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_GetList(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_GetList")
|
||||
assert.Equal([]int{1, 2, 3, 4, 5}, list.getList())
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_Get(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_Get")
|
||||
i := list.Get(2)
|
||||
assert.Equal(3, *i)
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_Set(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_Set")
|
||||
list.Set(2, 6)
|
||||
assert.Equal(6, list.getList()[2])
|
||||
|
||||
list = NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
|
||||
list.Set(0, 6)
|
||||
assert.Equal(6, list.getList()[0])
|
||||
|
||||
list = NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
|
||||
list.Set(0, 1)
|
||||
assert.Equal(1, list.getList()[0])
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_Add(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_Add")
|
||||
list.Add(6)
|
||||
assert.Equal([]int{1, 2, 3, 4, 5, 6}, list.getList())
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_AddAll(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_AddAll")
|
||||
list.AddAll([]int{6, 7, 8})
|
||||
assert.Equal([]int{1, 2, 3, 4, 5, 6, 7, 8}, list.getList())
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_AddByIndex(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_AddByIndex")
|
||||
list.AddByIndex(2, 6)
|
||||
assert.Equal([]int{1, 2, 6, 3, 4, 5}, list.getList())
|
||||
|
||||
list = NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
|
||||
list.AddByIndex(0, 6)
|
||||
assert.Equal([]int{6, 1, 2, 3, 4, 5}, list.getList())
|
||||
|
||||
list = NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
|
||||
list.AddByIndex(5, 6)
|
||||
assert.Equal([]int{1, 2, 3, 4, 5, 6}, list.getList())
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_DeleteAt2(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_RemoveByIndex")
|
||||
list.DeleteAt(2)
|
||||
assert.Equal([]int{1, 2, 4, 5}, list.getList())
|
||||
|
||||
list = NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
|
||||
list.DeleteAt(4)
|
||||
assert.Equal([]int{1, 2, 3, 4}, list.getList())
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_RemoveByValue(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_RemoveByValue")
|
||||
list.DeleteBy(3)
|
||||
assert.Equal([]int{1, 2, 4, 5}, list.getList())
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_DeleteRange(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_RemoveRange")
|
||||
list.DeleteRange(1, 3)
|
||||
assert.Equal([]int{1, 4, 5}, list.getList())
|
||||
|
||||
list = NewCopyOnWriteList([]int{1, 2, 3, 4, 5})
|
||||
list.DeleteRange(0, 5)
|
||||
assert.Equal([]int{}, list.getList())
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_LastIndexOf(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5, 3})
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_LastIndexOf")
|
||||
assert.Equal(5, list.LastIndexOf(3))
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_DeleteAt(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5, 3})
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_DeleteAt")
|
||||
list.DeleteAt(2)
|
||||
assert.Equal([]int{1, 2, 4, 5, 3}, list.getList())
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_DeleteBy(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5, 3})
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_DeleteBy")
|
||||
list.DeleteBy(3)
|
||||
assert.Equal([]int{1, 2, 4, 5, 3}, list.getList())
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_DeleteIf(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5, 3, 6})
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_DeleteIf")
|
||||
|
||||
list.DeleteIf(func(i int) bool {
|
||||
return i%2 == 0
|
||||
})
|
||||
|
||||
assert.Equal([]int{1, 3, 5, 3}, list.getList())
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_Equal(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5, 3, 6})
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_Equal")
|
||||
|
||||
assert.Equal(true, list.Equal(&[]int{1, 2, 3, 4, 5, 3, 6}))
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_ForEach(t *testing.T) {
|
||||
testList := make([]int, 0)
|
||||
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5, 3, 6})
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_ForEach")
|
||||
|
||||
list.ForEach(func(i int) {
|
||||
testList = append(testList, i)
|
||||
})
|
||||
assert.Equal([]int{1, 2, 3, 4, 5, 3, 6}, testList)
|
||||
|
||||
list.ForEach(func(i int) {
|
||||
list.Add(i)
|
||||
})
|
||||
assert.Equal([]int{1, 2, 3, 4, 5, 3, 6, 1, 2, 3, 4, 5, 3, 6}, list.getList())
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_Clear(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{1, 2, 3, 4, 5, 3, 6})
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_Clear")
|
||||
|
||||
list.Clear()
|
||||
assert.Equal([]int{}, list.getList())
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_Merge(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{1, 3, 5, 7, 9})
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_Merge")
|
||||
|
||||
list.Merge([]int{2, 4, 6, 8, 10})
|
||||
assert.Equal([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10}, list.getList())
|
||||
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_Sort(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10})
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_Sort")
|
||||
|
||||
list.Sort(func(i, j int) bool {
|
||||
return i < j
|
||||
})
|
||||
assert.Equal([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, list.getList())
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_IndexOf(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10})
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_IndexOf")
|
||||
|
||||
assert.Equal(0, list.IndexOf(1))
|
||||
assert.Equal(9, list.IndexOf(10))
|
||||
assert.Equal(-1, list.IndexOf(11))
|
||||
}
|
||||
|
||||
func TestCopyOnWriteList_SubList(t *testing.T) {
|
||||
list := NewCopyOnWriteList([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10})
|
||||
|
||||
assert := internal.NewAssert(t, "CopyOnWriteList_SubList")
|
||||
|
||||
list = NewCopyOnWriteList([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10})
|
||||
subList := list.SubList(1, 3)
|
||||
assert.Equal([]int{3, 5}, subList)
|
||||
|
||||
list = NewCopyOnWriteList([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10})
|
||||
subList = list.SubList(1, 1)
|
||||
assert.Equal([]int{}, subList)
|
||||
|
||||
list = NewCopyOnWriteList([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10})
|
||||
assert.Equal(10, list.Size())
|
||||
subList = list.SubList(1, 10)
|
||||
assert.Equal([]int{3, 5, 7, 9, 2, 4, 6, 8, 10}, subList)
|
||||
|
||||
list = NewCopyOnWriteList([]int{1, 3, 5, 7, 9, 2, 4, 6, 8, 10})
|
||||
subList = list.SubList(11, 1)
|
||||
assert.Equal([]int{}, subList)
|
||||
}
|
||||
418
datastructure/list/list.go
Normal file
418
datastructure/list/list.go
Normal file
@@ -0,0 +1,418 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure contains some data structure. list is a linear table, implemented with slice.
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/duke-git/lancet/v2/iterator"
|
||||
)
|
||||
|
||||
// List is a linear table, implemented with slice.
|
||||
type List[T any] struct {
|
||||
data []T
|
||||
}
|
||||
|
||||
// NewList return a pointer of List.
|
||||
func NewList[T any](data []T) *List[T] {
|
||||
return &List[T]{data: data}
|
||||
}
|
||||
|
||||
// Data return list data.
|
||||
func (l *List[T]) Data() []T {
|
||||
return l.data
|
||||
}
|
||||
|
||||
// ValueOf return the value pointer at index of list data.
|
||||
func (l *List[T]) ValueOf(index int) (*T, bool) {
|
||||
if index < 0 || index >= len(l.data) {
|
||||
return nil, false
|
||||
}
|
||||
return &l.data[index], true
|
||||
}
|
||||
|
||||
// IndexOf returns the index of value. if not found return -1.
|
||||
func (l *List[T]) IndexOf(value T) int {
|
||||
index := -1
|
||||
data := l.data
|
||||
for i, v := range data {
|
||||
if reflect.DeepEqual(v, value) {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
// LastIndexOf returns the index of the last occurrence of the value in this list.
|
||||
// if not found return -1.
|
||||
func (l *List[T]) LastIndexOf(value T) int {
|
||||
index := -1
|
||||
data := l.data
|
||||
for i := len(data) - 1; i >= 0; i-- {
|
||||
if reflect.DeepEqual(data[i], value) {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
// IndexOfFunc returns the first index satisfying f(v)
|
||||
// if not found return -1.
|
||||
func (l *List[T]) IndexOfFunc(f func(T) bool) int {
|
||||
index := -1
|
||||
data := l.data
|
||||
for i, v := range data {
|
||||
if f(v) {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
// LastIndexOfFunc returns the index of the last occurrence of the value in this list satisfying f(data[i])
|
||||
// if not found return -1.
|
||||
func (l *List[T]) LastIndexOfFunc(f func(T) bool) int {
|
||||
index := -1
|
||||
data := l.data
|
||||
for i := len(data) - 1; i >= 0; i-- {
|
||||
if f(data[i]) {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
// Contain checks if the value in the list or not.
|
||||
func (l *List[T]) Contain(value T) bool {
|
||||
data := l.data
|
||||
for _, v := range data {
|
||||
if reflect.DeepEqual(v, value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Push append value to the list data.
|
||||
func (l *List[T]) Push(value T) {
|
||||
l.data = append(l.data, value)
|
||||
}
|
||||
|
||||
// InsertAtFirst insert value into list at first index.
|
||||
func (l *List[T]) InsertAtFirst(value T) {
|
||||
l.InsertAt(0, value)
|
||||
}
|
||||
|
||||
// InsertAtLast insert value into list at last index.
|
||||
func (l *List[T]) InsertAtLast(value T) {
|
||||
l.InsertAt(len(l.data), value)
|
||||
}
|
||||
|
||||
// InsertAt insert value into list at index.
|
||||
func (l *List[T]) InsertAt(index int, value T) {
|
||||
data := l.data
|
||||
size := len(data)
|
||||
|
||||
if index < 0 || index > size {
|
||||
return
|
||||
}
|
||||
l.data = append(data[:index], append([]T{value}, data[index:]...)...)
|
||||
}
|
||||
|
||||
// PopFirst delete the first value of list and return it.
|
||||
func (l *List[T]) PopFirst() (*T, bool) {
|
||||
if len(l.data) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
v := l.data[0]
|
||||
l.DeleteAt(0)
|
||||
|
||||
return &v, true
|
||||
}
|
||||
|
||||
// PopLast delete the last value of list and return it.
|
||||
func (l *List[T]) PopLast() (*T, bool) {
|
||||
size := len(l.data)
|
||||
if size == 0 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
v := l.data[size-1]
|
||||
l.DeleteAt(size - 1)
|
||||
|
||||
return &v, true
|
||||
}
|
||||
|
||||
// DeleteAt delete the value of list at index.
|
||||
func (l *List[T]) DeleteAt(index int) {
|
||||
data := l.data
|
||||
size := len(data)
|
||||
if index < 0 || index > size-1 {
|
||||
return
|
||||
}
|
||||
if index == size-1 {
|
||||
data = data[:index]
|
||||
} else {
|
||||
data = append(data[:index], data[index+1:]...)
|
||||
}
|
||||
l.data = data
|
||||
}
|
||||
|
||||
// DeleteIf delete all satisfying f(data[i]), returns count of removed elements
|
||||
func (l *List[T]) DeleteIf(f func(T) bool) int {
|
||||
data := l.data
|
||||
size := len(data)
|
||||
|
||||
var c int
|
||||
for index := 0; index < len(data); index++ {
|
||||
if !f(data[index]) {
|
||||
continue
|
||||
}
|
||||
if index == size-1 {
|
||||
data = data[:index]
|
||||
} else {
|
||||
data = append(data[:index], data[index+1:]...)
|
||||
index--
|
||||
}
|
||||
c++
|
||||
}
|
||||
|
||||
if c > 0 {
|
||||
l.data = data
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// UpdateAt update value of list at index, index shoud between 0 and list size -1
|
||||
func (l *List[T]) UpdateAt(index int, value T) {
|
||||
data := l.data
|
||||
size := len(data)
|
||||
|
||||
if index < 0 || index >= size {
|
||||
return
|
||||
}
|
||||
l.data = append(data[:index], append([]T{value}, data[index+1:]...)...)
|
||||
}
|
||||
|
||||
// Equal compare list to other list, use reflect.DeepEqual.
|
||||
func (l *List[T]) Equal(other *List[T]) bool {
|
||||
if len(l.data) != len(other.data) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(l.data); i++ {
|
||||
if !reflect.DeepEqual(l.data[i], other.data[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// IsEmpty check if the list is empty or not.
|
||||
func (l *List[T]) IsEmpty() bool {
|
||||
return len(l.data) == 0
|
||||
}
|
||||
|
||||
// Clear the data of list.
|
||||
func (l *List[T]) Clear() {
|
||||
l.data = make([]T, 0)
|
||||
}
|
||||
|
||||
// Clone return a copy of list.
|
||||
func (l *List[T]) Clone() *List[T] {
|
||||
cl := NewList(make([]T, len(l.data)))
|
||||
copy(cl.data, l.data)
|
||||
|
||||
return cl
|
||||
}
|
||||
|
||||
// Merge two list, return new list, don't change original list.
|
||||
func (l *List[T]) Merge(other *List[T]) *List[T] {
|
||||
l1, l2 := len(l.data), len(other.data)
|
||||
ml := NewList(make([]T, l1+l2))
|
||||
|
||||
data := append([]T{}, append(l.data, other.data...)...)
|
||||
ml.data = data
|
||||
|
||||
return ml
|
||||
}
|
||||
|
||||
// Size return number of list data items.
|
||||
func (l *List[T]) Size() int {
|
||||
return len(l.data)
|
||||
}
|
||||
|
||||
// Cap return cap of the inner data.
|
||||
func (l *List[T]) Cap() int {
|
||||
return cap(l.data)
|
||||
}
|
||||
|
||||
// Swap the value of index i and j in list.
|
||||
func (l *List[T]) Swap(i, j int) {
|
||||
size := len(l.data)
|
||||
if i < 0 || i >= size || j < 0 || j >= size {
|
||||
return
|
||||
}
|
||||
l.data[i], l.data[j] = l.data[j], l.data[i]
|
||||
}
|
||||
|
||||
// Reverse the item order of list.
|
||||
func (l *List[T]) Reverse() {
|
||||
for i, j := 0, len(l.data)-1; i < j; i, j = i+1, j-1 {
|
||||
l.data[i], l.data[j] = l.data[j], l.data[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Unique delete duplicate items in list.
|
||||
func (l *List[T]) Unique() {
|
||||
data := l.data
|
||||
size := len(data)
|
||||
|
||||
uniqueData := make([]T, 0)
|
||||
for i := 0; i < size; i++ {
|
||||
value := data[i]
|
||||
skip := true
|
||||
for _, v := range uniqueData {
|
||||
if reflect.DeepEqual(value, v) {
|
||||
skip = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if skip {
|
||||
uniqueData = append(uniqueData, value)
|
||||
}
|
||||
}
|
||||
|
||||
l.data = uniqueData
|
||||
}
|
||||
|
||||
// Union creates a new list contain all element in list l and other, delete duplicate element.
|
||||
func (l *List[T]) Union(other *List[T]) *List[T] {
|
||||
result := NewList([]T{})
|
||||
|
||||
result.data = append(result.data, l.data...)
|
||||
result.data = append(result.data, other.data...)
|
||||
result.Unique()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Intersection creates a new list whose element both be contained in list l and other.
|
||||
func (l *List[T]) Intersection(other *List[T]) *List[T] {
|
||||
result := NewList(make([]T, 0))
|
||||
|
||||
for _, v := range l.data {
|
||||
if other.Contain(v) {
|
||||
result.data = append(result.data, v)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Difference returns the difference between two collections.
|
||||
// return a list whose element in the original list, not in the given list.
|
||||
func (l *List[T]) Difference(other *List[T]) *List[T] {
|
||||
result := NewList(make([]T, 0))
|
||||
|
||||
intersectList := l.Intersection(other)
|
||||
|
||||
for _, v := range l.data {
|
||||
if !intersectList.Contain(v) {
|
||||
result.data = append(result.data, v)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// SymmetricDifference oppoiste operation of intersection function.
|
||||
func (l *List[T]) SymmetricDifference(other *List[T]) *List[T] {
|
||||
result := NewList(make([]T, 0))
|
||||
|
||||
intersectList := l.Intersection(other)
|
||||
|
||||
for _, v := range l.data {
|
||||
if !intersectList.Contain(v) {
|
||||
result.data = append(result.data, v)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range other.data {
|
||||
if !intersectList.Contain(v) {
|
||||
result.data = append(result.data, v)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// SubList returns a sub list of the original list between the specified fromIndex, inclusive, and toIndex, exclusive.
|
||||
func (l *List[T]) SubList(fromIndex, toIndex int) *List[T] {
|
||||
data := l.data[fromIndex:toIndex]
|
||||
subList := make([]T, len(data))
|
||||
copy(subList, data)
|
||||
return NewList(subList)
|
||||
}
|
||||
|
||||
// ForEach performs the given action for each element of the list.
|
||||
func (l *List[T]) ForEach(consumer func(T)) {
|
||||
for _, it := range l.data {
|
||||
consumer(it)
|
||||
}
|
||||
}
|
||||
|
||||
// RetainAll retains only the elements in this list that are contained in the given list.
|
||||
func (l *List[T]) RetainAll(list *List[T]) bool {
|
||||
return l.batchRemove(list, true)
|
||||
}
|
||||
|
||||
// DeleteAll removes from this list all of its elements that are contained in the given list.
|
||||
func (l *List[T]) DeleteAll(list *List[T]) bool {
|
||||
return l.batchRemove(list, false)
|
||||
}
|
||||
|
||||
func (l *List[T]) batchRemove(list *List[T], complement bool) bool {
|
||||
var (
|
||||
w = 0
|
||||
data = l.data
|
||||
size = len(data)
|
||||
)
|
||||
|
||||
for i := 0; i < size; i++ {
|
||||
if list.Contain(data[i]) == complement {
|
||||
data[w] = data[i]
|
||||
w++
|
||||
}
|
||||
}
|
||||
|
||||
if w != size {
|
||||
l.data = data[:w]
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Iterator returns an iterator over the elements in this list in proper sequence.
|
||||
func (l *List[T]) Iterator() iterator.Iterator[T] {
|
||||
return iterator.FromSlice(l.data)
|
||||
}
|
||||
|
||||
// ListToMap convert a list to a map based on iteratee function.
|
||||
func ListToMap[T any, K comparable, V any](list *List[T], iteratee func(T) (K, V)) map[K]V {
|
||||
result := make(map[K]V, list.Size())
|
||||
for _, item := range list.data {
|
||||
k, v := iteratee(item)
|
||||
result[k] = v
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
531
datastructure/list/list_test.go
Normal file
531
datastructure/list/list_test.go
Normal file
@@ -0,0 +1,531 @@
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestListData(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestListData")
|
||||
|
||||
list := NewList([]int{1, 2, 3})
|
||||
assert.Equal([]int{1, 2, 3}, list.Data())
|
||||
}
|
||||
|
||||
func TestValueOf(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestValueOf")
|
||||
|
||||
list := NewList([]int{1, 2, 3})
|
||||
v, ok := list.ValueOf(0)
|
||||
assert.Equal(1, *v)
|
||||
assert.Equal(true, ok)
|
||||
|
||||
_, ok = list.ValueOf(3)
|
||||
assert.Equal(false, ok)
|
||||
}
|
||||
|
||||
func TestIndexOf(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestIndexOf")
|
||||
|
||||
list := NewList([]int{1, 2, 3})
|
||||
i := list.IndexOf(1)
|
||||
assert.Equal(0, i)
|
||||
|
||||
i = list.IndexOf(4)
|
||||
assert.Equal(-1, i)
|
||||
}
|
||||
|
||||
func TestIndexOfFunc(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestIndexOf")
|
||||
|
||||
list := NewList([]int{1, 2, 3})
|
||||
i := list.IndexOfFunc(func(a int) bool { return a == 1 })
|
||||
assert.Equal(0, i)
|
||||
|
||||
i = list.IndexOfFunc(func(a int) bool { return a == 4 })
|
||||
assert.Equal(-1, i)
|
||||
}
|
||||
|
||||
func TestLastIndexOf(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestIndexOf")
|
||||
|
||||
list := NewList([]int{1, 2, 3, 3, 3, 3, 4, 5, 6, 9})
|
||||
i := list.LastIndexOf(3)
|
||||
assert.Equal(5, i)
|
||||
|
||||
i = list.LastIndexOf(10)
|
||||
assert.Equal(-1, i)
|
||||
|
||||
i = list.LastIndexOf(4)
|
||||
assert.Equal(6, i)
|
||||
|
||||
i = list.LastIndexOf(1)
|
||||
assert.Equal(0, i)
|
||||
}
|
||||
|
||||
func TestLastIndexOfFunc(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestIndexOf")
|
||||
|
||||
list := NewList([]int{1, 2, 3, 3, 3, 3, 4, 5, 6, 9})
|
||||
i := list.LastIndexOfFunc(func(a int) bool { return a == 3 })
|
||||
assert.Equal(5, i)
|
||||
|
||||
i = list.LastIndexOfFunc(func(a int) bool { return a == 10 })
|
||||
assert.Equal(-1, i)
|
||||
|
||||
i = list.LastIndexOfFunc(func(a int) bool { return a == 4 })
|
||||
assert.Equal(6, i)
|
||||
|
||||
i = list.LastIndexOfFunc(func(a int) bool { return a == 1 })
|
||||
assert.Equal(0, i)
|
||||
}
|
||||
|
||||
func TestContain(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestContain")
|
||||
|
||||
list := NewList([]int{1, 2, 3})
|
||||
assert.Equal(true, list.Contain(1))
|
||||
assert.Equal(false, list.Contain(0))
|
||||
}
|
||||
|
||||
func TestPush(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestPush")
|
||||
|
||||
list := NewList([]int{1, 2, 3})
|
||||
list.Push(4)
|
||||
|
||||
assert.Equal([]int{1, 2, 3, 4}, list.Data())
|
||||
}
|
||||
|
||||
func TestInsertAtFirst(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestInsertAtFirst")
|
||||
|
||||
list := NewList([]int{1, 2, 3})
|
||||
list.InsertAtFirst(0)
|
||||
|
||||
assert.Equal([]int{0, 1, 2, 3}, list.Data())
|
||||
}
|
||||
|
||||
func TestInsertAtLast(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestInsertAtLast")
|
||||
|
||||
list := NewList([]int{1, 2, 3})
|
||||
list.InsertAtLast(4)
|
||||
|
||||
assert.Equal([]int{1, 2, 3, 4}, list.Data())
|
||||
}
|
||||
|
||||
func TestInsertAt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestInsertAt")
|
||||
|
||||
list := NewList([]int{1, 2, 3})
|
||||
|
||||
list.InsertAt(-1, 0)
|
||||
assert.Equal([]int{1, 2, 3}, list.Data())
|
||||
|
||||
list.InsertAt(4, 0)
|
||||
assert.Equal([]int{1, 2, 3}, list.Data())
|
||||
|
||||
list.InsertAt(0, 0)
|
||||
assert.Equal([]int{0, 1, 2, 3}, list.Data())
|
||||
|
||||
list.InsertAt(4, 4)
|
||||
assert.Equal([]int{0, 1, 2, 3, 4}, list.Data())
|
||||
}
|
||||
|
||||
func TestPopFirst(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestPopFirst")
|
||||
|
||||
list := NewList([]int{1, 2, 3})
|
||||
v, ok := list.PopFirst()
|
||||
assert.Equal(1, *v)
|
||||
assert.Equal(true, ok)
|
||||
assert.Equal([]int{2, 3}, list.Data())
|
||||
|
||||
list2 := NewList([]int{})
|
||||
_, ok = list2.PopFirst()
|
||||
assert.Equal(false, ok)
|
||||
assert.Equal([]int{}, list2.Data())
|
||||
}
|
||||
|
||||
func TestPopLast(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestPopLast")
|
||||
|
||||
list := NewList([]int{1, 2, 3})
|
||||
v, ok := list.PopLast()
|
||||
assert.Equal(3, *v)
|
||||
assert.Equal(true, ok)
|
||||
assert.Equal([]int{1, 2}, list.Data())
|
||||
|
||||
list2 := NewList([]int{})
|
||||
_, ok = list2.PopLast()
|
||||
assert.Equal(false, ok)
|
||||
assert.Equal([]int{}, list2.Data())
|
||||
}
|
||||
|
||||
func TestDeleteAt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestDeleteAt")
|
||||
|
||||
list := NewList([]int{1, 2, 3, 4})
|
||||
|
||||
list.DeleteAt(-1)
|
||||
assert.Equal([]int{1, 2, 3, 4}, list.Data())
|
||||
|
||||
list.DeleteAt(4)
|
||||
assert.Equal([]int{1, 2, 3, 4}, list.Data())
|
||||
|
||||
list.DeleteAt(0)
|
||||
assert.Equal([]int{2, 3, 4}, list.Data())
|
||||
|
||||
list.DeleteAt(2)
|
||||
assert.Equal([]int{2, 3}, list.Data())
|
||||
}
|
||||
|
||||
func TestUpdateAt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestUpdateAt")
|
||||
|
||||
list := NewList([]int{1, 2, 3, 4})
|
||||
|
||||
list.UpdateAt(-1, 0)
|
||||
assert.Equal([]int{1, 2, 3, 4}, list.Data())
|
||||
|
||||
list.UpdateAt(4, 0)
|
||||
assert.Equal([]int{1, 2, 3, 4}, list.Data())
|
||||
|
||||
list.UpdateAt(0, 5)
|
||||
assert.Equal([]int{5, 2, 3, 4}, list.Data())
|
||||
|
||||
list.UpdateAt(3, 1)
|
||||
assert.Equal([]int{5, 2, 3, 1}, list.Data())
|
||||
}
|
||||
|
||||
func TestEqual(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestEqual")
|
||||
|
||||
list1 := NewList([]int{1, 2, 3, 4})
|
||||
list2 := NewList([]int{1, 2, 3, 4})
|
||||
list3 := NewList([]int{1, 2, 3})
|
||||
|
||||
assert.Equal(true, list1.Equal(list2))
|
||||
assert.Equal(false, list1.Equal(list3))
|
||||
}
|
||||
|
||||
func TestIsEmpty(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsEmpty")
|
||||
|
||||
list1 := NewList([]int{1, 2, 3, 4})
|
||||
list2 := NewList([]int{})
|
||||
|
||||
assert.Equal(false, list1.IsEmpty())
|
||||
assert.Equal(true, list2.IsEmpty())
|
||||
}
|
||||
|
||||
func TestIsClear(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsClear")
|
||||
|
||||
list1 := NewList([]int{1, 2, 3, 4})
|
||||
list1.Clear()
|
||||
empty := NewList([]int{})
|
||||
|
||||
assert.Equal(empty, list1)
|
||||
}
|
||||
|
||||
func TestClone(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestClone")
|
||||
|
||||
list1 := NewList([]int{1, 2, 3, 4})
|
||||
list2 := list1.Clone()
|
||||
|
||||
assert.Equal(true, list1.Equal(list2))
|
||||
}
|
||||
|
||||
func TestMerge(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestMerge")
|
||||
|
||||
list1 := NewList([]int{1, 2, 3, 4})
|
||||
list2 := NewList([]int{4, 5, 6})
|
||||
expected := NewList([]int{1, 2, 3, 4, 4, 5, 6})
|
||||
|
||||
list3 := list1.Merge(list2)
|
||||
assert.Equal(true, expected.Equal(list3))
|
||||
}
|
||||
|
||||
func TestSize(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSize")
|
||||
|
||||
list := NewList([]int{1, 2, 3, 4})
|
||||
empty := NewList([]int{})
|
||||
|
||||
assert.Equal(4, list.Size())
|
||||
assert.Equal(0, empty.Size())
|
||||
}
|
||||
|
||||
func TestCap(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestCap")
|
||||
|
||||
data := make([]int, 0, 100)
|
||||
list := NewList(data)
|
||||
assert.Equal(100, list.Cap())
|
||||
|
||||
data = make([]int, 0)
|
||||
list = NewList(data)
|
||||
assert.Equal(0, list.Cap())
|
||||
}
|
||||
|
||||
func TestSwap(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSwap")
|
||||
|
||||
list := NewList([]int{1, 2, 3, 4})
|
||||
expected := NewList([]int{4, 2, 3, 1})
|
||||
|
||||
list.Swap(0, 3)
|
||||
|
||||
assert.Equal(true, expected.Equal(list))
|
||||
}
|
||||
|
||||
func TestReverse(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestReverse")
|
||||
|
||||
list := NewList([]int{1, 2, 3, 4})
|
||||
expected := NewList([]int{4, 3, 2, 1})
|
||||
|
||||
list.Reverse()
|
||||
|
||||
assert.Equal(true, expected.Equal(list))
|
||||
}
|
||||
|
||||
func TestUnique(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestUnique")
|
||||
|
||||
list := NewList([]int{1, 2, 2, 3, 4})
|
||||
expected := NewList([]int{1, 2, 3, 4})
|
||||
|
||||
list.Unique()
|
||||
|
||||
assert.Equal(true, expected.Equal(list))
|
||||
}
|
||||
|
||||
func TestUnion(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestUnion")
|
||||
|
||||
list1 := NewList([]int{1, 2, 3, 4})
|
||||
list2 := NewList([]int{4, 5, 6})
|
||||
expected := NewList([]int{1, 2, 3, 4, 5, 6})
|
||||
|
||||
list3 := list1.Union(list2)
|
||||
assert.Equal(true, expected.Equal(list3))
|
||||
}
|
||||
|
||||
func TestIntersection(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestIntersection")
|
||||
|
||||
list1 := NewList([]int{1, 2, 3, 4})
|
||||
list2 := NewList([]int{4, 5, 6})
|
||||
expected := NewList([]int{4})
|
||||
|
||||
list3 := list1.Intersection(list2)
|
||||
assert.Equal(true, expected.Equal(list3))
|
||||
}
|
||||
|
||||
func TestDifference(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestDifference")
|
||||
|
||||
list1 := NewList([]int{1, 2, 3})
|
||||
list2 := NewList([]int{1, 2, 4})
|
||||
expected := NewList([]int{3})
|
||||
|
||||
list3 := list1.Difference(list2)
|
||||
assert.Equal(true, expected.Equal(list3))
|
||||
}
|
||||
|
||||
func TestSymmetricDifference(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSymmetricDifference")
|
||||
|
||||
list1 := NewList([]int{1, 2, 3})
|
||||
list2 := NewList([]int{1, 2, 4})
|
||||
expected := NewList([]int{3, 4})
|
||||
|
||||
list3 := list1.SymmetricDifference(list2)
|
||||
assert.Equal(true, expected.Equal(list3))
|
||||
}
|
||||
|
||||
func TestSubSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSubSlice")
|
||||
|
||||
list := NewList([]int{1, 2, 3, 4, 5, 8})
|
||||
subList := list.SubList(2, 5)
|
||||
|
||||
assert.Equal([]int{3, 4, 5}, subList.Data())
|
||||
}
|
||||
|
||||
func BenchmarkSubSlice(b *testing.B) {
|
||||
list := NewList([]int{1, 2, 3, 4, 5, 8})
|
||||
for n := 0; n < b.N; n++ {
|
||||
list.SubList(2, 5)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteIf(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestDeleteIf")
|
||||
|
||||
list := NewList([]int{1, 1, 1, 1, 2, 3, 1, 1, 4, 1, 1, 1, 1, 1, 1})
|
||||
|
||||
count := list.DeleteIf(func(a int) bool { return a == 1 })
|
||||
assert.Equal([]int{2, 3, 4}, list.Data())
|
||||
assert.Equal(12, count)
|
||||
|
||||
count = list.DeleteIf(func(a int) bool { return a == 5 })
|
||||
assert.Equal([]int{2, 3, 4}, list.Data())
|
||||
assert.Equal(0, count)
|
||||
}
|
||||
|
||||
func TestForEach(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestForEach")
|
||||
|
||||
list := NewList([]int{1, 2, 3, 4})
|
||||
rs := make([]int, 0)
|
||||
list.ForEach(func(i int) {
|
||||
rs = append(rs, i)
|
||||
})
|
||||
|
||||
assert.Equal([]int{1, 2, 3, 4}, rs)
|
||||
}
|
||||
|
||||
func TestRetainAll(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestRetainAll")
|
||||
|
||||
list := NewList([]int{1, 2, 3, 4})
|
||||
list1 := NewList([]int{1, 2, 3, 4})
|
||||
list2 := NewList([]int{1, 2, 3, 4})
|
||||
|
||||
retain := NewList([]int{1, 2})
|
||||
retain1 := NewList([]int{2, 3})
|
||||
retain2 := NewList([]int{1, 2, 5})
|
||||
|
||||
list.RetainAll(retain)
|
||||
list1.RetainAll(retain1)
|
||||
list2.RetainAll(retain2)
|
||||
|
||||
assert.Equal([]int{1, 2}, list.Data())
|
||||
assert.Equal([]int{2, 3}, list1.Data())
|
||||
assert.Equal([]int{1, 2}, list2.Data())
|
||||
}
|
||||
|
||||
func TestDeleteAll(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestDeleteAll")
|
||||
|
||||
list := NewList([]int{1, 2, 3, 4})
|
||||
list1 := NewList([]int{1, 2, 3, 4})
|
||||
list2 := NewList([]int{1, 2, 3, 4})
|
||||
|
||||
del := NewList([]int{1})
|
||||
del1 := NewList([]int{2, 3})
|
||||
del2 := NewList([]int{1, 2, 5})
|
||||
|
||||
list.DeleteAll(del)
|
||||
list1.DeleteAll(del1)
|
||||
list2.DeleteAll(del2)
|
||||
assert.Equal([]int{2, 3, 4}, list.Data())
|
||||
assert.Equal([]int{1, 4}, list1.Data())
|
||||
assert.Equal([]int{3, 4}, list2.Data())
|
||||
}
|
||||
|
||||
func TestIterator(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestIterator")
|
||||
|
||||
list := NewList([]int{1, 2, 3, 4})
|
||||
iterator := list.Iterator()
|
||||
|
||||
rs := make([]int, 0)
|
||||
for iterator.HasNext() {
|
||||
item, _ := iterator.Next()
|
||||
rs = append(rs, item)
|
||||
}
|
||||
|
||||
assert.Equal([]int{1, 2, 3, 4}, rs)
|
||||
}
|
||||
|
||||
func TestListToMap(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "ListToMap")
|
||||
|
||||
list := NewList([]int{1, 2, 3, 4})
|
||||
result := ListToMap(list, func(n int) (int, bool) {
|
||||
return n, n > 1
|
||||
})
|
||||
|
||||
expected := map[int]bool{1: false, 2: true, 3: true, 4: true}
|
||||
assert.Equal(expected, result)
|
||||
}
|
||||
51
datastructure/node.go
Normal file
51
datastructure/node.go
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure implements some data structure.
|
||||
package datastructure
|
||||
|
||||
// LinkNode is a linkedlist node, which have a Value and Pre points to previous node, Next points to a next node of the link.
|
||||
type LinkNode[T any] struct {
|
||||
Value T
|
||||
Pre *LinkNode[T]
|
||||
Next *LinkNode[T]
|
||||
}
|
||||
|
||||
// NewLinkNode return a LinkNode pointer
|
||||
func NewLinkNode[T any](value T) *LinkNode[T] {
|
||||
return &LinkNode[T]{value, nil, nil}
|
||||
}
|
||||
|
||||
// StackNode is a node in stack, which have a Value and Next pointer points to next node in the stack.
|
||||
type StackNode[T any] struct {
|
||||
Value T
|
||||
Next *StackNode[T]
|
||||
}
|
||||
|
||||
// NewStackNode return a StackNode pointer
|
||||
func NewStackNode[T any](value T) *StackNode[T] {
|
||||
return &StackNode[T]{value, nil}
|
||||
}
|
||||
|
||||
// QueueNode is a node in a queue, which have a Value and Next pointer points to next node in the queue.
|
||||
type QueueNode[T any] struct {
|
||||
Value T
|
||||
Next *QueueNode[T]
|
||||
}
|
||||
|
||||
// NewQueueNode return a QueueNode pointer
|
||||
func NewQueueNode[T any](value T) *QueueNode[T] {
|
||||
return &QueueNode[T]{value, nil}
|
||||
}
|
||||
|
||||
// TreeNode is node of tree
|
||||
type TreeNode[T any] struct {
|
||||
Value T
|
||||
Left *TreeNode[T]
|
||||
Right *TreeNode[T]
|
||||
}
|
||||
|
||||
// NewTreeNode return a TreeNode pointer
|
||||
func NewTreeNode[T any](val T) *TreeNode[T] {
|
||||
return &TreeNode[T]{val, nil, nil}
|
||||
}
|
||||
155
datastructure/queue/arrayqueue.go
Normal file
155
datastructure/queue/arrayqueue.go
Normal file
@@ -0,0 +1,155 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure contains some data structure.
|
||||
// Queue structure contains ArrayQueue, LinkedQueue, CircularQueue, and PriorityQueue.
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// ArrayQueue implements queue with slice
|
||||
type ArrayQueue[T any] struct {
|
||||
data []T
|
||||
head int
|
||||
tail int
|
||||
capacity int
|
||||
size int
|
||||
}
|
||||
|
||||
func NewArrayQueue[T any](capacity int) *ArrayQueue[T] {
|
||||
return &ArrayQueue[T]{
|
||||
data: make([]T, 0, capacity),
|
||||
head: 0,
|
||||
tail: 0,
|
||||
capacity: capacity,
|
||||
size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Data return slice of queue data
|
||||
func (q *ArrayQueue[T]) Data() []T {
|
||||
items := []T{}
|
||||
for i := q.head; i < q.tail; i++ {
|
||||
items = append(items, q.data[i])
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
// Size return number of elements in queue
|
||||
func (q *ArrayQueue[T]) Size() int {
|
||||
return q.size
|
||||
}
|
||||
|
||||
// IsEmpty checks if queue is empty or not
|
||||
func (q *ArrayQueue[T]) IsEmpty() bool {
|
||||
return q.size == 0
|
||||
}
|
||||
|
||||
// IsFull checks if queue is full or not
|
||||
func (q *ArrayQueue[T]) IsFull() bool {
|
||||
return q.size == q.capacity
|
||||
}
|
||||
|
||||
// Front return front value of queue
|
||||
func (q *ArrayQueue[T]) Front() T {
|
||||
return q.data[0]
|
||||
}
|
||||
|
||||
// Back return back value of queue
|
||||
func (q *ArrayQueue[T]) Back() T {
|
||||
return q.data[q.size-1]
|
||||
}
|
||||
|
||||
// EnQueue put element into queue
|
||||
func (q *ArrayQueue[T]) Enqueue(item T) bool {
|
||||
if q.tail < q.capacity {
|
||||
q.data = append(q.data, item)
|
||||
// q.tail++
|
||||
q.data[q.tail] = item
|
||||
} else {
|
||||
//upgrade
|
||||
if q.head > 0 {
|
||||
for i := 0; i < q.tail-q.head; i++ {
|
||||
q.data[i] = q.data[i+q.head]
|
||||
}
|
||||
q.tail -= q.head
|
||||
q.head = 0
|
||||
} else {
|
||||
if q.capacity < 65536 {
|
||||
if q.capacity == 0 {
|
||||
q.capacity = 1
|
||||
}
|
||||
q.capacity *= 2
|
||||
} else {
|
||||
q.capacity += 2 ^ 16
|
||||
}
|
||||
|
||||
tmp := make([]T, q.capacity, q.capacity)
|
||||
copy(tmp, q.data)
|
||||
q.data = tmp
|
||||
}
|
||||
|
||||
q.data[q.tail] = item
|
||||
}
|
||||
|
||||
q.tail++
|
||||
q.size++
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// DeQueue remove head element of queue and return it, if queue is empty, return nil and error
|
||||
func (q *ArrayQueue[T]) Dequeue() (T, bool) {
|
||||
var item T
|
||||
if q.size == 0 {
|
||||
return item, false
|
||||
}
|
||||
|
||||
item = q.data[q.head]
|
||||
q.head++
|
||||
|
||||
if q.head >= 1024 || q.head*2 > q.tail {
|
||||
q.capacity -= q.head
|
||||
q.tail -= q.head
|
||||
tmp := make([]T, q.capacity, q.capacity)
|
||||
copy(tmp, q.data[q.head:])
|
||||
q.data = tmp
|
||||
q.head = 0
|
||||
}
|
||||
|
||||
q.size--
|
||||
return item, true
|
||||
}
|
||||
|
||||
// Clear the queue data
|
||||
func (q *ArrayQueue[T]) Clear() {
|
||||
capacity := q.capacity
|
||||
q.data = make([]T, 0, capacity)
|
||||
q.head = 0
|
||||
q.tail = 0
|
||||
q.size = 0
|
||||
q.capacity = capacity
|
||||
}
|
||||
|
||||
// Contain checks if the value is in queue or not
|
||||
func (q *ArrayQueue[T]) Contain(value T) bool {
|
||||
for _, v := range q.data {
|
||||
if reflect.DeepEqual(v, value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Print queue data
|
||||
func (q *ArrayQueue[T]) Print() {
|
||||
info := "["
|
||||
for i := q.head; i < q.tail; i++ {
|
||||
info += fmt.Sprintf("%+v, ", q.data[i])
|
||||
}
|
||||
info += "]"
|
||||
fmt.Println(info)
|
||||
}
|
||||
119
datastructure/queue/arrayqueue_test.go
Normal file
119
datastructure/queue/arrayqueue_test.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestArrayQueue_Enqueue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestArrayQueue_Enqueue")
|
||||
|
||||
queue := NewArrayQueue[int](2)
|
||||
queue.Enqueue(1)
|
||||
queue.Enqueue(2)
|
||||
queue.Enqueue(3)
|
||||
|
||||
data := queue.Data()
|
||||
size := queue.Size()
|
||||
|
||||
assert.Equal([]int{1, 2, 3}, data)
|
||||
assert.Equal(3, size)
|
||||
}
|
||||
|
||||
func TestArrayQueue_Dequeue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestArrayQueue_Dequeue")
|
||||
|
||||
queue := NewArrayQueue[int](4)
|
||||
queue.Enqueue(1)
|
||||
queue.Enqueue(2)
|
||||
queue.Enqueue(3)
|
||||
|
||||
val, ok := queue.Dequeue()
|
||||
assert.Equal(true, ok)
|
||||
|
||||
assert.Equal(1, val)
|
||||
assert.Equal([]int{2, 3}, queue.Data())
|
||||
}
|
||||
|
||||
func TestArrayQueue_Front(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestArrayQueue_Front")
|
||||
|
||||
queue := NewArrayQueue[int](4)
|
||||
queue.Enqueue(1)
|
||||
queue.Enqueue(2)
|
||||
queue.Enqueue(3)
|
||||
|
||||
val := queue.Front()
|
||||
|
||||
assert.Equal(1, val)
|
||||
assert.Equal([]int{1, 2, 3}, queue.Data())
|
||||
}
|
||||
|
||||
func TestArrayQueue_Back(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestArrayQueue_Back")
|
||||
|
||||
queue := NewArrayQueue[int](4)
|
||||
queue.Enqueue(1)
|
||||
queue.Enqueue(2)
|
||||
queue.Enqueue(3)
|
||||
|
||||
val := queue.Back()
|
||||
|
||||
assert.Equal(3, val)
|
||||
assert.Equal([]int{1, 2, 3}, queue.Data())
|
||||
}
|
||||
|
||||
func TestArrayQueue_Contain(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestArrayQueue_Contain")
|
||||
|
||||
queue := NewArrayQueue[int](4)
|
||||
queue.Enqueue(1)
|
||||
queue.Enqueue(2)
|
||||
queue.Enqueue(3)
|
||||
|
||||
assert.Equal(true, queue.Contain(1))
|
||||
assert.Equal(false, queue.Contain(4))
|
||||
}
|
||||
|
||||
func TestArrayQueue_Clear(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestArrayQueue_Clear")
|
||||
|
||||
queue := NewArrayQueue[int](4)
|
||||
|
||||
assert.Equal(true, queue.IsEmpty())
|
||||
assert.Equal(0, queue.Size())
|
||||
|
||||
queue.Enqueue(1)
|
||||
assert.Equal(false, queue.IsEmpty())
|
||||
assert.Equal(1, queue.Size())
|
||||
|
||||
queue.Clear()
|
||||
assert.Equal(true, queue.IsEmpty())
|
||||
assert.Equal(0, queue.Size())
|
||||
}
|
||||
|
||||
func TestArrayQueue_IsFull(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestArrayQueue_IsFull")
|
||||
|
||||
queue := NewArrayQueue[int](3)
|
||||
queue.Enqueue(1)
|
||||
queue.Enqueue(2)
|
||||
queue.Enqueue(3)
|
||||
|
||||
assert.Equal(true, queue.IsFull())
|
||||
}
|
||||
123
datastructure/queue/circularqueue.go
Normal file
123
datastructure/queue/circularqueue.go
Normal file
@@ -0,0 +1,123 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure contains some data structure.
|
||||
// Queue structure contains ArrayQueue, LinkedQueue, CircularQueue, and PriorityQueue.
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// CircularQueue implements circular queue with slice,
|
||||
// last index of CircularQueue don't contain value, so acturl capacity is capacity - 1
|
||||
type CircularQueue[T any] struct {
|
||||
data []T
|
||||
front int
|
||||
rear int
|
||||
capacity int
|
||||
}
|
||||
|
||||
// NewCircularQueue return a empty CircularQueue pointer
|
||||
func NewCircularQueue[T any](capacity int) *CircularQueue[T] {
|
||||
data := make([]T, capacity)
|
||||
return &CircularQueue[T]{data: data, front: 0, rear: 0, capacity: capacity}
|
||||
}
|
||||
|
||||
// Data return slice of queue data
|
||||
func (q *CircularQueue[T]) Data() []T {
|
||||
data := []T{}
|
||||
|
||||
front := q.front
|
||||
rear := q.rear
|
||||
if front <= rear {
|
||||
return q.data[front:rear]
|
||||
}
|
||||
|
||||
data = append(data, q.data[front:]...)
|
||||
data = append(data, q.data[0:rear]...)
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
// Size return number of elements in circular queue
|
||||
func (q *CircularQueue[T]) Size() int {
|
||||
if q.capacity == 0 {
|
||||
return 0
|
||||
}
|
||||
return (q.rear - q.front + q.capacity) % q.capacity
|
||||
}
|
||||
|
||||
// IsEmpty checks if queue is empty or not
|
||||
func (q *CircularQueue[T]) IsEmpty() bool {
|
||||
return q.front == q.rear
|
||||
}
|
||||
|
||||
// IsFull checks if queue is full or not
|
||||
func (q *CircularQueue[T]) IsFull() bool {
|
||||
return (q.rear+1)%q.capacity == q.front
|
||||
}
|
||||
|
||||
// Front return front value of queue
|
||||
func (q *CircularQueue[T]) Front() T {
|
||||
return q.data[q.front]
|
||||
}
|
||||
|
||||
// Back return back value of queue
|
||||
func (q *CircularQueue[T]) Back() T {
|
||||
if q.rear-1 >= 0 {
|
||||
return q.data[q.rear-1]
|
||||
}
|
||||
return q.data[q.capacity-1]
|
||||
}
|
||||
|
||||
// Enqueue put element into queue
|
||||
func (q *CircularQueue[T]) Enqueue(value T) error {
|
||||
if q.IsFull() {
|
||||
return errors.New("queue is full!")
|
||||
}
|
||||
|
||||
q.data[q.rear] = value
|
||||
q.rear = (q.rear + 1) % q.capacity
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Dequeue remove head element of queue and return it, if queue is empty, return nil and error
|
||||
func (q *CircularQueue[T]) Dequeue() (*T, error) {
|
||||
if q.IsEmpty() {
|
||||
return nil, errors.New("queue is empty")
|
||||
}
|
||||
|
||||
headItem := q.data[q.front]
|
||||
var t T
|
||||
q.data[q.front] = t
|
||||
q.front = (q.front + 1) % q.capacity
|
||||
|
||||
return &headItem, nil
|
||||
}
|
||||
|
||||
// Clear the queue data
|
||||
func (q *CircularQueue[T]) Clear() {
|
||||
q.data = []T{}
|
||||
q.front = 0
|
||||
q.rear = 0
|
||||
q.capacity = 0
|
||||
}
|
||||
|
||||
// Contain checks if the value is in queue or not
|
||||
func (q *CircularQueue[T]) Contain(value T) bool {
|
||||
for _, v := range q.data {
|
||||
if reflect.DeepEqual(v, value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Print queue data
|
||||
func (q *CircularQueue[T]) Print() {
|
||||
fmt.Printf("%+v\n", q)
|
||||
}
|
||||
159
datastructure/queue/circularqueue_test.go
Normal file
159
datastructure/queue/circularqueue_test.go
Normal file
@@ -0,0 +1,159 @@
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestCircularQueue_Enqueue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestCircularQueue_Enqueue")
|
||||
|
||||
queue := NewCircularQueue[int](6)
|
||||
|
||||
err := queue.Enqueue(1)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = queue.Enqueue(2)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = queue.Enqueue(3)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = queue.Enqueue(4)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = queue.Enqueue(5)
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal([]int{1, 2, 3, 4, 5}, queue.Data())
|
||||
assert.Equal(5, queue.Size())
|
||||
|
||||
err = queue.Enqueue(6)
|
||||
assert.IsNotNil(err)
|
||||
}
|
||||
|
||||
func TestCircularQueue_Dequeue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestCircularQueue_DeQueue")
|
||||
|
||||
queue := NewCircularQueue[int](4)
|
||||
assert.Equal(true, queue.IsEmpty())
|
||||
|
||||
err := queue.Enqueue(1)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = queue.Enqueue(2)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = queue.Enqueue(3)
|
||||
assert.IsNil(err)
|
||||
|
||||
val, err := queue.Dequeue()
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal(1, *val)
|
||||
assert.Equal(false, queue.IsFull())
|
||||
|
||||
val, _ = queue.Dequeue()
|
||||
assert.Equal(2, *val)
|
||||
assert.Equal(false, queue.IsFull())
|
||||
}
|
||||
|
||||
func TestCircularQueue_Front(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestCircularQueue_Front")
|
||||
|
||||
queue := NewCircularQueue[int](6)
|
||||
|
||||
err := queue.Enqueue(1)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = queue.Enqueue(2)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = queue.Enqueue(3)
|
||||
assert.IsNil(err)
|
||||
|
||||
val := queue.Front()
|
||||
assert.IsNil(err)
|
||||
assert.Equal(1, val)
|
||||
assert.Equal(3, queue.Size())
|
||||
}
|
||||
|
||||
func TestCircularQueue_Back(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestCircularQueue_Back")
|
||||
|
||||
queue := NewCircularQueue[int](3)
|
||||
assert.Equal(true, queue.IsEmpty())
|
||||
|
||||
err := queue.Enqueue(1)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = queue.Enqueue(2)
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal(2, queue.Back())
|
||||
|
||||
val, _ := queue.Dequeue()
|
||||
assert.Equal(1, *val)
|
||||
|
||||
err = queue.Enqueue(3)
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal(3, queue.Back())
|
||||
}
|
||||
|
||||
func TestCircularQueue_Contain(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestCircularQueue_Contain")
|
||||
|
||||
queue := NewCircularQueue[int](2)
|
||||
err := queue.Enqueue(1)
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal(true, queue.Contain(1))
|
||||
assert.Equal(false, queue.Contain(2))
|
||||
}
|
||||
|
||||
func TestCircularQueue_Clear(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestCircularQueue_Clear")
|
||||
|
||||
queue := NewCircularQueue[int](3)
|
||||
assert.Equal(true, queue.IsEmpty())
|
||||
assert.Equal(0, queue.Size())
|
||||
|
||||
err := queue.Enqueue(1)
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal(false, queue.IsEmpty())
|
||||
assert.Equal(1, queue.Size())
|
||||
|
||||
queue.Clear()
|
||||
assert.Equal(true, queue.IsEmpty())
|
||||
assert.Equal(0, queue.Size())
|
||||
}
|
||||
|
||||
func TestCircularQueue_Data(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestCircularQueue_Data")
|
||||
|
||||
queue := NewCircularQueue[int](3)
|
||||
err := queue.Enqueue(1)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = queue.Enqueue(2)
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal([]int{1, 2}, queue.Data())
|
||||
}
|
||||
122
datastructure/queue/linkedqueue.go
Normal file
122
datastructure/queue/linkedqueue.go
Normal file
@@ -0,0 +1,122 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure contains some data structure.
|
||||
// Queue structure contains ArrayQueue, LinkedQueue, CircularQueue, and PriorityQueue.
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/duke-git/lancet/v2/datastructure"
|
||||
)
|
||||
|
||||
// LinkedQueue implements queue with link list
|
||||
type LinkedQueue[T any] struct {
|
||||
head *datastructure.QueueNode[T]
|
||||
tail *datastructure.QueueNode[T]
|
||||
length int
|
||||
}
|
||||
|
||||
// NewLinkedQueue return a empty LinkedQueue pointer
|
||||
func NewLinkedQueue[T any]() *LinkedQueue[T] {
|
||||
return &LinkedQueue[T]{head: nil, tail: nil, length: 0}
|
||||
}
|
||||
|
||||
// Data return slice of queue data
|
||||
func (q *LinkedQueue[T]) Data() []T {
|
||||
res := []T{}
|
||||
current := q.head
|
||||
|
||||
for current != nil {
|
||||
res = append(res, current.Value)
|
||||
current = current.Next
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Size return length of queue data
|
||||
func (q *LinkedQueue[T]) Size() int {
|
||||
return q.length
|
||||
}
|
||||
|
||||
// IsEmpty checks if queue is empty or not
|
||||
func (q *LinkedQueue[T]) IsEmpty() bool {
|
||||
return q.length == 0
|
||||
}
|
||||
|
||||
// Enqueue put element into queue
|
||||
func (q *LinkedQueue[T]) Enqueue(value T) {
|
||||
newNode := datastructure.NewQueueNode(value)
|
||||
|
||||
if q.IsEmpty() {
|
||||
q.head = newNode
|
||||
q.tail = newNode
|
||||
} else {
|
||||
q.tail.Next = newNode
|
||||
q.tail = newNode
|
||||
}
|
||||
q.length++
|
||||
}
|
||||
|
||||
// Dequeue delete head element of queue then return it, if queue is empty, return nil and error
|
||||
func (q *LinkedQueue[T]) Dequeue() (*T, error) {
|
||||
if q.IsEmpty() {
|
||||
return nil, errors.New("queue is empty")
|
||||
}
|
||||
|
||||
head := q.head
|
||||
q.head = q.head.Next
|
||||
q.length--
|
||||
|
||||
return &head.Value, nil
|
||||
}
|
||||
|
||||
// Front return front value of queue
|
||||
func (q *LinkedQueue[T]) Front() (*T, error) {
|
||||
if q.IsEmpty() {
|
||||
return nil, errors.New("queue is empty")
|
||||
}
|
||||
return &q.head.Value, nil
|
||||
}
|
||||
|
||||
// Back return back value of queue
|
||||
func (q *LinkedQueue[T]) Back() (*T, error) {
|
||||
if q.IsEmpty() {
|
||||
return nil, errors.New("queue is empty")
|
||||
}
|
||||
return &q.tail.Value, nil
|
||||
}
|
||||
|
||||
// Clear clear the queue data
|
||||
func (q *LinkedQueue[T]) Clear() {
|
||||
q.head = nil
|
||||
q.tail = nil
|
||||
q.length = 0
|
||||
}
|
||||
|
||||
// Print all nodes info of queue link
|
||||
func (q *LinkedQueue[T]) Print() {
|
||||
current := q.head
|
||||
info := "[ "
|
||||
for current != nil {
|
||||
info += fmt.Sprintf("%+v, ", current)
|
||||
current = current.Next
|
||||
}
|
||||
info += " ]"
|
||||
fmt.Println(info)
|
||||
}
|
||||
|
||||
// Contain checks if the value is in queue or not
|
||||
func (q *LinkedQueue[T]) Contain(value T) bool {
|
||||
current := q.head
|
||||
for current != nil {
|
||||
if reflect.DeepEqual(current.Value, value) {
|
||||
return true
|
||||
}
|
||||
current = current.Next
|
||||
}
|
||||
return false
|
||||
}
|
||||
106
datastructure/queue/linkedqueue_test.go
Normal file
106
datastructure/queue/linkedqueue_test.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestLinkedQueue_Enqueue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestLinkedQueue_Enqueue")
|
||||
|
||||
queue := NewLinkedQueue[int]()
|
||||
queue.Enqueue(1)
|
||||
queue.Enqueue(2)
|
||||
queue.Enqueue(3)
|
||||
|
||||
assert.Equal([]int{1, 2, 3}, queue.Data())
|
||||
assert.Equal(3, queue.Size())
|
||||
}
|
||||
|
||||
func TestLinkedQueue_Dequeue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestLinkedQueue_DeQueue")
|
||||
|
||||
queue := NewLinkedQueue[int]()
|
||||
queue.Enqueue(1)
|
||||
queue.Enqueue(2)
|
||||
queue.Enqueue(3)
|
||||
|
||||
val, _ := queue.Dequeue()
|
||||
|
||||
queue.Print()
|
||||
|
||||
assert.Equal([]int{2, 3}, queue.Data())
|
||||
assert.Equal(1, *val)
|
||||
}
|
||||
|
||||
func TestLinkedQueue_Front(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestLinkedQueue_Front")
|
||||
|
||||
queue := NewLinkedQueue[int]()
|
||||
_, err := queue.Front()
|
||||
assert.IsNotNil(err)
|
||||
|
||||
queue.Enqueue(1)
|
||||
queue.Enqueue(2)
|
||||
queue.Enqueue(3)
|
||||
|
||||
val, err := queue.Front()
|
||||
assert.Equal(1, *val)
|
||||
assert.IsNil(err)
|
||||
}
|
||||
|
||||
func TestLinkedQueue_Back(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestLinkedQueue_Back")
|
||||
|
||||
queue := NewLinkedQueue[int]()
|
||||
_, err := queue.Back()
|
||||
assert.IsNotNil(err)
|
||||
|
||||
queue.Enqueue(1)
|
||||
queue.Enqueue(2)
|
||||
queue.Enqueue(3)
|
||||
|
||||
val, err := queue.Back()
|
||||
assert.Equal(3, *val)
|
||||
assert.IsNil(err)
|
||||
}
|
||||
|
||||
func TestLinkedQueue_Clear(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestLinkedQueue_Back")
|
||||
|
||||
queue := NewLinkedQueue[int]()
|
||||
assert.Equal(true, queue.IsEmpty())
|
||||
|
||||
queue.Enqueue(1)
|
||||
queue.Enqueue(2)
|
||||
queue.Enqueue(3)
|
||||
assert.Equal(false, queue.IsEmpty())
|
||||
|
||||
queue.Clear()
|
||||
assert.Equal(true, queue.IsEmpty())
|
||||
}
|
||||
|
||||
func TestLinkedQueue_Contain(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestLinkedQueue_Contain")
|
||||
|
||||
queue := NewLinkedQueue[int]()
|
||||
queue.Enqueue(1)
|
||||
queue.Enqueue(2)
|
||||
queue.Enqueue(3)
|
||||
|
||||
assert.Equal(true, queue.Contain(1))
|
||||
assert.Equal(false, queue.Contain(4))
|
||||
}
|
||||
118
datastructure/queue/priorityqueue.go
Normal file
118
datastructure/queue/priorityqueue.go
Normal file
@@ -0,0 +1,118 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure contains some data structure.
|
||||
// Queue structure contains ArrayQueue, LinkedQueue, CircularQueue, and PriorityQueue.
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/duke-git/lancet/v2/constraints"
|
||||
)
|
||||
|
||||
// PriorityQueue is a priority queue implemented by binary heap tree
|
||||
// type T should implements Compare function in constraints.Comparator interface.
|
||||
type PriorityQueue[T any] struct {
|
||||
items []T
|
||||
size int
|
||||
comparator constraints.Comparator
|
||||
}
|
||||
|
||||
// NewPriorityQueue return a pointer of PriorityQueue
|
||||
// param `comparator` is used to compare values in the queue
|
||||
func NewPriorityQueue[T any](capacity int, comparator constraints.Comparator) *PriorityQueue[T] {
|
||||
return &PriorityQueue[T]{
|
||||
items: make([]T, capacity+1),
|
||||
size: 0,
|
||||
comparator: comparator,
|
||||
}
|
||||
}
|
||||
|
||||
// IsEmpty checks if the queue is empty or not
|
||||
func (q *PriorityQueue[T]) IsEmpty() bool {
|
||||
return q.size == 0
|
||||
}
|
||||
|
||||
// Size get number of items in the queue
|
||||
func (q *PriorityQueue[T]) Size() int {
|
||||
return q.size
|
||||
}
|
||||
|
||||
// IsFull checks if the queue capacity is full or not
|
||||
func (q *PriorityQueue[T]) IsFull() bool {
|
||||
return q.size == len(q.items)-1
|
||||
}
|
||||
|
||||
// Data return a slice of queue data
|
||||
func (q *PriorityQueue[T]) Data() []T {
|
||||
data := make([]T, q.size)
|
||||
for i := 1; i < q.size+1; i++ {
|
||||
data[i-1] = q.items[i]
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
// Enqueue insert value into queue
|
||||
func (q *PriorityQueue[T]) Enqueue(val T) error {
|
||||
if q.IsFull() {
|
||||
return errors.New("queue is already full.")
|
||||
}
|
||||
q.size++
|
||||
q.items[q.size] = val
|
||||
q.swim(q.size)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Dequeue delete and return max value in queue
|
||||
func (q *PriorityQueue[T]) Dequeue() (T, bool) {
|
||||
var val T
|
||||
if q.IsEmpty() {
|
||||
return val, false
|
||||
}
|
||||
|
||||
max := q.items[1]
|
||||
|
||||
q.swap(1, q.size)
|
||||
q.size--
|
||||
q.sink(1)
|
||||
|
||||
//set zero value for rest values of the queue
|
||||
q.items[q.size+1] = val
|
||||
|
||||
return max, true
|
||||
}
|
||||
|
||||
// swim when child's key is larger than parent's key, exchange them.
|
||||
func (q *PriorityQueue[T]) swim(index int) {
|
||||
for index > 1 && q.comparator.Compare(q.items[index/2], q.items[index]) < 0 {
|
||||
q.swap(index, index/2)
|
||||
index = index / 2
|
||||
}
|
||||
}
|
||||
|
||||
// sink when parent's key smaller than child's key, exchange parent's key with larger child's key.
|
||||
func (q *PriorityQueue[T]) sink(index int) {
|
||||
|
||||
for 2*index <= q.size {
|
||||
j := 2 * index
|
||||
|
||||
// get larger child node index
|
||||
if j < q.size && q.comparator.Compare(q.items[j], q.items[j+1]) < 0 {
|
||||
j++
|
||||
}
|
||||
// if parent larger than child, stop
|
||||
if !(q.comparator.Compare(q.items[index], q.items[j]) < 0) {
|
||||
break
|
||||
}
|
||||
|
||||
q.swap(index, j)
|
||||
index = j
|
||||
}
|
||||
}
|
||||
|
||||
// swap the two values at index i and j
|
||||
func (q *PriorityQueue[T]) swap(i, j int) {
|
||||
q.items[i], q.items[j] = q.items[j], q.items[i]
|
||||
}
|
||||
74
datastructure/queue/priorityqueue_test.go
Normal file
74
datastructure/queue/priorityqueue_test.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
func TestPriorityQueue_Enqueue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestPriorityQueue_Enqueue")
|
||||
|
||||
comparator := &intComparator{}
|
||||
pq := NewPriorityQueue[int](3, comparator)
|
||||
|
||||
assert.Equal(true, pq.IsEmpty())
|
||||
assert.Equal(false, pq.IsFull())
|
||||
|
||||
err := pq.Enqueue(1)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = pq.Enqueue(2)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = pq.Enqueue(3)
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal(true, pq.IsFull())
|
||||
|
||||
queueData := pq.Data()
|
||||
assert.Equal([]int{3, 1, 2}, queueData)
|
||||
|
||||
}
|
||||
|
||||
func TestPriorityQueue_Dequeue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestPriorityQueue_Dequeue")
|
||||
|
||||
comparator := &intComparator{}
|
||||
pq := NewPriorityQueue[int](3, comparator)
|
||||
|
||||
_, ok := pq.Dequeue()
|
||||
assert.Equal(false, ok)
|
||||
|
||||
err := pq.Enqueue(1)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = pq.Enqueue(2)
|
||||
assert.IsNil(err)
|
||||
|
||||
err = pq.Enqueue(3)
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal(3, pq.Size())
|
||||
|
||||
val, ok := pq.Dequeue()
|
||||
assert.Equal(true, ok)
|
||||
assert.Equal(3, val)
|
||||
}
|
||||
199
datastructure/set/set.go
Normal file
199
datastructure/set/set.go
Normal file
@@ -0,0 +1,199 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure contains some data structure. Set is a data container, like slice, but element of set is not duplicate.
|
||||
package datastructure
|
||||
|
||||
// Set is a data container, like slice, but element of set is not duplicate.
|
||||
type Set[T comparable] map[T]struct{}
|
||||
|
||||
// NewSet return a instance of set
|
||||
func NewSet[T comparable](items ...T) Set[T] {
|
||||
set := make(Set[T])
|
||||
set.Add(items...)
|
||||
return set
|
||||
}
|
||||
|
||||
// NewSetFromSlice create a set from slice
|
||||
func NewSetFromSlice[T comparable](items []T) Set[T] {
|
||||
set := make(Set[T])
|
||||
for _, item := range items {
|
||||
set.Add(item)
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// Add items to set
|
||||
func (s Set[T]) Add(items ...T) {
|
||||
for _, v := range items {
|
||||
s[v] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// AddIfNotExist checks if item exists in the set,
|
||||
// it adds the item to set and returns true if it does not exist in the set,
|
||||
// or else it does nothing and returns false.
|
||||
func (s Set[T]) AddIfNotExist(item T) bool {
|
||||
if !s.Contain(item) {
|
||||
if _, ok := s[item]; !ok {
|
||||
s[item] = struct{}{}
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AddIfNotExistBy checks if item exists in the set and pass the `checker` function
|
||||
// it adds the item to set and returns true if it does not exists in the set and
|
||||
// function `checker` returns true, or else it does nothing and returns false.
|
||||
func (s Set[T]) AddIfNotExistBy(item T, checker func(element T) bool) bool {
|
||||
if !s.Contain(item) {
|
||||
if checker(item) {
|
||||
if _, ok := s[item]; !ok {
|
||||
s[item] = struct{}{}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Contain checks if set contains item or not
|
||||
func (s Set[T]) Contain(item T) bool {
|
||||
_, ok := s[item]
|
||||
return ok
|
||||
}
|
||||
|
||||
// ContainAll checks if set contains other set
|
||||
func (s Set[T]) ContainAll(other Set[T]) bool {
|
||||
for k := range other {
|
||||
_, ok := s[k]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Clone return a copy of set
|
||||
func (s Set[T]) Clone() Set[T] {
|
||||
set := NewSet[T]()
|
||||
set.Add(s.Values()...)
|
||||
return set
|
||||
}
|
||||
|
||||
// Delete item of set
|
||||
func (s Set[T]) Delete(items ...T) {
|
||||
for _, v := range items {
|
||||
delete(s, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Equal checks if two set has same elements or not
|
||||
func (s Set[T]) Equal(other Set[T]) bool {
|
||||
if s.Size() != other.Size() {
|
||||
return false
|
||||
}
|
||||
|
||||
return s.ContainAll(other) && other.ContainAll(s)
|
||||
}
|
||||
|
||||
// Iterate call function by every element of set
|
||||
func (s Set[T]) Iterate(fn func(item T)) {
|
||||
for v := range s {
|
||||
fn(v)
|
||||
}
|
||||
}
|
||||
|
||||
// IsEmpty checks the set is empty or not
|
||||
func (s Set[T]) IsEmpty() bool {
|
||||
return len(s) == 0
|
||||
}
|
||||
|
||||
// Size get the number of elements in set
|
||||
func (s Set[T]) Size() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
// Values return all values of set
|
||||
func (s Set[T]) Values() []T {
|
||||
result := make([]T, 0, len(s))
|
||||
|
||||
s.Iterate(func(value T) {
|
||||
result = append(result, value)
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Union creates a new set contain all element of set s and other
|
||||
func (s Set[T]) Union(other Set[T]) Set[T] {
|
||||
set := s.Clone()
|
||||
set.Add(other.Values()...)
|
||||
return set
|
||||
}
|
||||
|
||||
// Intersection creates a new set whose element both be contained in set s and other
|
||||
func (s Set[T]) Intersection(other Set[T]) Set[T] {
|
||||
set := NewSet[T]()
|
||||
s.Iterate(func(value T) {
|
||||
if other.Contain(value) {
|
||||
set.Add(value)
|
||||
}
|
||||
})
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
// SymmetricDifference creates a new set whose element is in set1 or set2, but not in both sets
|
||||
func (s Set[T]) SymmetricDifference(other Set[T]) Set[T] {
|
||||
set := NewSet[T]()
|
||||
s.Iterate(func(value T) {
|
||||
if !other.Contain(value) {
|
||||
set.Add(value)
|
||||
}
|
||||
})
|
||||
|
||||
other.Iterate(func(value T) {
|
||||
if !s.Contain(value) {
|
||||
set.Add(value)
|
||||
}
|
||||
})
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
// Minus creates an set of whose element in origin set but not in compared set
|
||||
func (s Set[T]) Minus(comparedSet Set[T]) Set[T] {
|
||||
set := NewSet[T]()
|
||||
|
||||
s.Iterate(func(value T) {
|
||||
if !comparedSet.Contain(value) {
|
||||
set.Add(value)
|
||||
}
|
||||
})
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
// EachWithBreak iterates over elements of a set and invokes function for each element,
|
||||
// when iteratee return false, will break the for each loop.
|
||||
func (s Set[T]) EachWithBreak(iteratee func(item T) bool) {
|
||||
for _, v := range s.Values() {
|
||||
if !iteratee(v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pop delete the top element of set then return it, if set is empty, return nil-value of T and false.
|
||||
func (s Set[T]) Pop() (v T, ok bool) {
|
||||
if len(s) > 0 {
|
||||
items := s.Values()
|
||||
item := items[len(s)-1]
|
||||
delete(s, item)
|
||||
return item, true
|
||||
}
|
||||
|
||||
return v, false
|
||||
}
|
||||
262
datastructure/set/set_test.go
Normal file
262
datastructure/set/set_test.go
Normal file
@@ -0,0 +1,262 @@
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestSet_NewSetFromSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_NewSetFromSlice")
|
||||
|
||||
s1 := NewSetFromSlice([]int{1, 2, 2, 3})
|
||||
assert.Equal(3, s1.Size())
|
||||
assert.Equal(true, s1.Contain(1))
|
||||
assert.Equal(true, s1.Contain(2))
|
||||
assert.Equal(true, s1.Contain(3))
|
||||
|
||||
s2 := NewSetFromSlice([]int{})
|
||||
assert.Equal(0, s2.Size())
|
||||
}
|
||||
|
||||
func TestSet_Add(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_Add")
|
||||
|
||||
set := NewSet[int]()
|
||||
set.Add(1, 2, 3)
|
||||
|
||||
cmpSet := NewSet(1, 2, 3)
|
||||
|
||||
assert.Equal(true, set.Equal(cmpSet))
|
||||
}
|
||||
|
||||
func TestSet_AddIfNotExist(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_AddIfNotExist")
|
||||
|
||||
set := NewSet[int]()
|
||||
set.Add(1, 2, 3)
|
||||
|
||||
assert.Equal(false, set.AddIfNotExist(1))
|
||||
assert.Equal(true, set.AddIfNotExist(4))
|
||||
assert.Equal(NewSet(1, 2, 3, 4), set)
|
||||
}
|
||||
|
||||
func TestSet_AddIfNotExistBy(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_AddIfNotExistBy")
|
||||
|
||||
set := NewSet[int]()
|
||||
set.Add(1, 2)
|
||||
|
||||
ok := set.AddIfNotExistBy(3, func(val int) bool {
|
||||
return val%2 != 0
|
||||
})
|
||||
|
||||
notOk := set.AddIfNotExistBy(4, func(val int) bool {
|
||||
return val%2 != 0
|
||||
})
|
||||
|
||||
assert.Equal(true, ok)
|
||||
assert.Equal(false, notOk)
|
||||
|
||||
assert.Equal(true, set.Contain(3))
|
||||
assert.Equal(false, set.Contain(4))
|
||||
}
|
||||
|
||||
func TestSet_Contain(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_Contain")
|
||||
|
||||
set := NewSet[int]()
|
||||
set.Add(1, 2, 3)
|
||||
|
||||
assert.Equal(true, set.Contain(1))
|
||||
assert.Equal(false, set.Contain(4))
|
||||
}
|
||||
|
||||
func TestSet_ContainAll(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_ContainAll")
|
||||
|
||||
set1 := NewSet(1, 2, 3)
|
||||
set2 := NewSet(1, 2)
|
||||
set3 := NewSet(1, 2, 3, 4)
|
||||
|
||||
assert.Equal(true, set1.ContainAll(set2))
|
||||
assert.Equal(false, set1.ContainAll(set3))
|
||||
}
|
||||
|
||||
func TestSet_Clone(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_Clone")
|
||||
|
||||
set1 := NewSet(1, 2, 3)
|
||||
set2 := set1.Clone()
|
||||
|
||||
assert.Equal(true, set1.Size() == set2.Size())
|
||||
assert.Equal(true, set1.ContainAll(set2))
|
||||
}
|
||||
|
||||
func TestSet_Delete(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_Delete")
|
||||
|
||||
set := NewSet[int]()
|
||||
set.Add(1, 2, 3)
|
||||
set.Delete(3)
|
||||
|
||||
assert.Equal(true, set.Equal(NewSet(1, 2)))
|
||||
}
|
||||
|
||||
func TestSet_Equal(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_Equal")
|
||||
|
||||
set1 := NewSet(1, 2, 3)
|
||||
set2 := NewSet(1, 2, 3)
|
||||
set3 := NewSet(1, 2, 3, 4)
|
||||
|
||||
assert.Equal(true, set1.Equal(set2))
|
||||
assert.Equal(false, set1.Equal(set3))
|
||||
}
|
||||
|
||||
func TestSet_Iterate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_Iterate")
|
||||
|
||||
set := NewSet(1, 2, 3)
|
||||
arr := []int{}
|
||||
set.Iterate(func(value int) {
|
||||
arr = append(arr, value)
|
||||
})
|
||||
|
||||
assert.Equal(3, len(arr))
|
||||
}
|
||||
|
||||
func TestSet_IsEmpty(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_IsEmpty")
|
||||
|
||||
set := NewSet[int]()
|
||||
assert.Equal(true, set.IsEmpty())
|
||||
}
|
||||
|
||||
func TestSet_Size(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_Size")
|
||||
|
||||
set := NewSet(1, 2, 3)
|
||||
assert.Equal(3, set.Size())
|
||||
}
|
||||
|
||||
func TestSet_Values(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_Values")
|
||||
|
||||
set := NewSet(1, 2, 3)
|
||||
values := set.Values()
|
||||
|
||||
assert.Equal(3, len(values))
|
||||
}
|
||||
|
||||
func TestSet_Union(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_Union")
|
||||
|
||||
set1 := NewSet(1, 2, 3)
|
||||
set2 := NewSet(2, 3, 4, 5)
|
||||
|
||||
unionSet := set1.Union(set2)
|
||||
|
||||
assert.Equal(NewSet(1, 2, 3, 4, 5), unionSet)
|
||||
}
|
||||
|
||||
func TestSet_Intersection(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_Intersection")
|
||||
|
||||
set1 := NewSet(1, 2, 3)
|
||||
set2 := NewSet(2, 3, 4, 5)
|
||||
intersectionSet := set1.Intersection(set2)
|
||||
|
||||
assert.Equal(NewSet(2, 3), intersectionSet)
|
||||
}
|
||||
|
||||
func TestSet_SymmetricDifference(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_SymmetricDifference")
|
||||
|
||||
set1 := NewSet(1, 2, 3)
|
||||
set2 := NewSet(2, 3, 4, 5)
|
||||
|
||||
assert.Equal(NewSet(1, 4, 5), set1.SymmetricDifference(set2))
|
||||
}
|
||||
|
||||
func TestSet_Minus(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_Minus")
|
||||
|
||||
set1 := NewSet(1, 2, 3)
|
||||
set2 := NewSet(2, 3, 4, 5)
|
||||
set3 := NewSet(2, 3)
|
||||
|
||||
assert.Equal(NewSet(1), set1.Minus(set2))
|
||||
assert.Equal(NewSet(4, 5), set2.Minus(set3))
|
||||
}
|
||||
|
||||
func TestEachWithBreak(t *testing.T) {
|
||||
// s := NewSet(1, 2, 3, 4, 5)
|
||||
|
||||
// var sum int
|
||||
|
||||
// s.EachWithBreak(func(n int) bool {
|
||||
// if n > 3 {
|
||||
// return false
|
||||
// }
|
||||
// sum += n
|
||||
// return true
|
||||
// })
|
||||
|
||||
// assert := internal.NewAssert(t, "TestEachWithBreak")
|
||||
// assert.Equal(6, sum)
|
||||
}
|
||||
|
||||
// func TestPop(t *testing.T) {
|
||||
// assert := internal.NewAssert(t, "TestPop")
|
||||
|
||||
// s := NewSet[int]()
|
||||
|
||||
// val, ok := s.Pop()
|
||||
// assert.Equal(0, val)
|
||||
// assert.Equal(false, ok)
|
||||
|
||||
// s.Add(1)
|
||||
// s.Add(2)
|
||||
// s.Add(3)
|
||||
|
||||
// // s = NewSet(1, 2, 3, 4, 5)
|
||||
|
||||
// val, ok = s.Pop()
|
||||
// assert.Equal(3, val)
|
||||
// assert.Equal(true, ok)
|
||||
// }
|
||||
66
datastructure/stack/arraystack.go
Normal file
66
datastructure/stack/arraystack.go
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure contains some data structure. Stack structure contains ArrayStack and LinkedStack.
|
||||
package datastructure
|
||||
|
||||
import "errors"
|
||||
|
||||
// ArrayStack implements stack with slice
|
||||
type ArrayStack[T any] struct {
|
||||
data []T
|
||||
length int
|
||||
}
|
||||
|
||||
// NewArrayStack return a empty ArrayStack pointer
|
||||
func NewArrayStack[T any]() *ArrayStack[T] {
|
||||
return &ArrayStack[T]{data: []T{}, length: 0}
|
||||
}
|
||||
|
||||
// Data return stack data
|
||||
func (s *ArrayStack[T]) Data() []T {
|
||||
return s.data
|
||||
}
|
||||
|
||||
// Size return length of stack data
|
||||
func (s *ArrayStack[T]) Size() int {
|
||||
return s.length
|
||||
}
|
||||
|
||||
// IsEmpty checks if stack is empty or not
|
||||
func (s *ArrayStack[T]) IsEmpty() bool {
|
||||
return s.length == 0
|
||||
}
|
||||
|
||||
// Push element into stack
|
||||
func (s *ArrayStack[T]) Push(value T) {
|
||||
s.data = append([]T{value}, s.data...)
|
||||
s.length++
|
||||
}
|
||||
|
||||
// Pop delete the top element of stack then return it, if stack is empty, return nil and error
|
||||
func (s *ArrayStack[T]) Pop() (*T, error) {
|
||||
if s.IsEmpty() {
|
||||
return nil, errors.New("stack is empty")
|
||||
}
|
||||
|
||||
topItem := s.data[0]
|
||||
s.data = s.data[1:]
|
||||
s.length--
|
||||
|
||||
return &topItem, nil
|
||||
}
|
||||
|
||||
// Peak return the top element of stack
|
||||
func (s *ArrayStack[T]) Peak() (*T, error) {
|
||||
if s.IsEmpty() {
|
||||
return nil, errors.New("stack is empty")
|
||||
}
|
||||
return &s.data[0], nil
|
||||
}
|
||||
|
||||
// Clear the stack data
|
||||
func (s *ArrayStack[T]) Clear() {
|
||||
s.data = []T{}
|
||||
s.length = 0
|
||||
}
|
||||
82
datastructure/stack/arraystack_test.go
Normal file
82
datastructure/stack/arraystack_test.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestArrayStack_Push(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestArrayStack_Push")
|
||||
|
||||
stack := NewArrayStack[int]()
|
||||
stack.Push(1)
|
||||
stack.Push(2)
|
||||
stack.Push(3)
|
||||
|
||||
values := stack.Data()
|
||||
length := stack.Size()
|
||||
|
||||
assert.Equal([]int{3, 2, 1}, values)
|
||||
assert.Equal(3, length)
|
||||
}
|
||||
|
||||
func TestArrayStack_Pop(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestArrayStack_Pop")
|
||||
|
||||
stack := NewArrayStack[int]()
|
||||
_, err := stack.Pop()
|
||||
assert.IsNotNil(err)
|
||||
|
||||
stack.Push(1)
|
||||
stack.Push(2)
|
||||
stack.Push(3)
|
||||
|
||||
topItem, err := stack.Pop()
|
||||
assert.IsNil(err)
|
||||
assert.Equal(3, *topItem)
|
||||
|
||||
assert.Equal([]int{2, 1}, stack.Data())
|
||||
}
|
||||
|
||||
func TestArrayStack_Peak(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestArrayStack_Peak")
|
||||
|
||||
stack := NewArrayStack[int]()
|
||||
_, err := stack.Peak()
|
||||
assert.IsNotNil(err)
|
||||
|
||||
stack.Push(1)
|
||||
stack.Push(2)
|
||||
stack.Push(3)
|
||||
|
||||
topItem, err := stack.Peak()
|
||||
assert.IsNil(err)
|
||||
assert.Equal(3, *topItem)
|
||||
|
||||
assert.Equal([]int{3, 2, 1}, stack.Data())
|
||||
}
|
||||
|
||||
func TestArrayStack_Clear(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestArrayStack_Clear")
|
||||
|
||||
stack := NewArrayStack[int]()
|
||||
assert.Equal(true, stack.IsEmpty())
|
||||
assert.Equal(0, stack.Size())
|
||||
|
||||
stack.Push(1)
|
||||
assert.Equal(false, stack.IsEmpty())
|
||||
assert.Equal(1, stack.Size())
|
||||
|
||||
stack.Clear()
|
||||
assert.Equal(true, stack.IsEmpty())
|
||||
assert.Equal(0, stack.Size())
|
||||
}
|
||||
98
datastructure/stack/linkedstack.go
Normal file
98
datastructure/stack/linkedstack.go
Normal file
@@ -0,0 +1,98 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure contains some data structure. Stack structure contains ArrayStack and LinkedStack.
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/duke-git/lancet/v2/datastructure"
|
||||
)
|
||||
|
||||
// LinkedStack implements stack with link list
|
||||
type LinkedStack[T any] struct {
|
||||
top *datastructure.StackNode[T]
|
||||
length int
|
||||
}
|
||||
|
||||
// NewLinkedStack return a empty LinkedStack pointer
|
||||
func NewLinkedStack[T any]() *LinkedStack[T] {
|
||||
return &LinkedStack[T]{top: nil, length: 0}
|
||||
}
|
||||
|
||||
// Data return stack data
|
||||
func (s *LinkedStack[T]) Data() []T {
|
||||
res := []T{}
|
||||
current := s.top
|
||||
|
||||
for current != nil {
|
||||
res = append(res, current.Value)
|
||||
current = current.Next
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Size return length of stack data
|
||||
func (s *LinkedStack[T]) Size() int {
|
||||
return s.length
|
||||
}
|
||||
|
||||
// IsEmpty checks if stack is empty or not
|
||||
func (s *LinkedStack[T]) IsEmpty() bool {
|
||||
return s.length == 0
|
||||
}
|
||||
|
||||
// Push element into stack
|
||||
func (s *LinkedStack[T]) Push(value T) {
|
||||
newNode := datastructure.NewStackNode(value)
|
||||
top := s.top
|
||||
if top == nil {
|
||||
s.top = newNode
|
||||
} else {
|
||||
newNode.Next = top
|
||||
s.top = newNode
|
||||
}
|
||||
|
||||
s.length++
|
||||
}
|
||||
|
||||
// Pop delete the top element of stack then return it, if stack is empty, return nil and error
|
||||
func (s *LinkedStack[T]) Pop() (*T, error) {
|
||||
if s.IsEmpty() {
|
||||
return nil, errors.New("stack is empty")
|
||||
}
|
||||
|
||||
top := s.top
|
||||
s.top = s.top.Next
|
||||
s.length--
|
||||
|
||||
return &top.Value, nil
|
||||
}
|
||||
|
||||
// Peak return the top element of stack then return it
|
||||
func (s *LinkedStack[T]) Peak() (*T, error) {
|
||||
if s.IsEmpty() {
|
||||
return nil, errors.New("stack is empty")
|
||||
}
|
||||
return &s.top.Value, nil
|
||||
}
|
||||
|
||||
// Clear clear the stack data
|
||||
func (s *LinkedStack[T]) Clear() {
|
||||
s.top = nil
|
||||
s.length = 0
|
||||
}
|
||||
|
||||
// Print all nodes info of stack link
|
||||
func (s *LinkedStack[T]) Print() {
|
||||
current := s.top
|
||||
info := "[ "
|
||||
for current != nil {
|
||||
info += fmt.Sprintf("%+v, ", current)
|
||||
current = current.Next
|
||||
}
|
||||
info += " ]"
|
||||
fmt.Println(info)
|
||||
}
|
||||
83
datastructure/stack/linkedstack_test.go
Normal file
83
datastructure/stack/linkedstack_test.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestLinkedStack_Push(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestLinkedStack_Push")
|
||||
|
||||
stack := NewLinkedStack[int]()
|
||||
stack.Push(1)
|
||||
stack.Push(2)
|
||||
stack.Push(3)
|
||||
|
||||
values := stack.Data()
|
||||
size := stack.Size()
|
||||
|
||||
assert.Equal([]int{3, 2, 1}, values)
|
||||
assert.Equal(3, size)
|
||||
}
|
||||
|
||||
func TestLinkedStack_Pop(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestLinkedStack_Pop")
|
||||
|
||||
stack := NewLinkedStack[int]()
|
||||
_, err := stack.Pop()
|
||||
assert.IsNotNil(err)
|
||||
|
||||
stack.Push(1)
|
||||
stack.Push(2)
|
||||
stack.Push(3)
|
||||
|
||||
topItem, err := stack.Pop()
|
||||
assert.IsNil(err)
|
||||
assert.Equal(3, *topItem)
|
||||
|
||||
stack.Print()
|
||||
assert.Equal([]int{2, 1}, stack.Data())
|
||||
}
|
||||
|
||||
func TestLinkedStack_Peak(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestLinkedStack_Peak")
|
||||
|
||||
stack := NewLinkedStack[int]()
|
||||
_, err := stack.Peak()
|
||||
assert.IsNotNil(err)
|
||||
|
||||
stack.Push(1)
|
||||
stack.Push(2)
|
||||
stack.Push(3)
|
||||
|
||||
topItem, err := stack.Peak()
|
||||
assert.IsNil(err)
|
||||
assert.Equal(3, *topItem)
|
||||
|
||||
assert.Equal([]int{3, 2, 1}, stack.Data())
|
||||
}
|
||||
|
||||
func TestLinkedStack_Empty(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestLinkedStack_Empty")
|
||||
|
||||
stack := NewLinkedStack[int]()
|
||||
assert.Equal(true, stack.IsEmpty())
|
||||
assert.Equal(0, stack.Size())
|
||||
|
||||
stack.Push(1)
|
||||
assert.Equal(false, stack.IsEmpty())
|
||||
assert.Equal(1, stack.Size())
|
||||
|
||||
stack.Clear()
|
||||
assert.Equal(true, stack.IsEmpty())
|
||||
assert.Equal(0, stack.Size())
|
||||
}
|
||||
112
datastructure/tree/bstree.go
Normal file
112
datastructure/tree/bstree.go
Normal file
@@ -0,0 +1,112 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package datastructure contains some data structure. BSTree is binary search tree.
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/duke-git/lancet/v2/constraints"
|
||||
"github.com/duke-git/lancet/v2/datastructure"
|
||||
)
|
||||
|
||||
// BSTree is a binary search tree data structure in which each node has at most two children,
|
||||
// which are referred to as the left child and the right child.
|
||||
// In BSTree: leftNode < rootNode < rightNode
|
||||
// type T should implements Compare function in constraints.Comparator interface.
|
||||
type BSTree[T any] struct {
|
||||
root *datastructure.TreeNode[T]
|
||||
comparator constraints.Comparator
|
||||
}
|
||||
|
||||
// NewBSTree create a BSTree pointer
|
||||
// param `comparator` is used to compare values in the tree
|
||||
func NewBSTree[T any](rootData T, comparator constraints.Comparator) *BSTree[T] {
|
||||
root := datastructure.NewTreeNode(rootData)
|
||||
return &BSTree[T]{root, comparator}
|
||||
}
|
||||
|
||||
// InsertNode insert data into BSTree
|
||||
func (t *BSTree[T]) Insert(data T) {
|
||||
root := t.root
|
||||
newNode := datastructure.NewTreeNode(data)
|
||||
if root == nil {
|
||||
t.root = newNode
|
||||
} else {
|
||||
insertTreeNode(root, newNode, t.comparator)
|
||||
}
|
||||
}
|
||||
|
||||
// DeletetNode delete data into BSTree
|
||||
func (t *BSTree[T]) Delete(data T) {
|
||||
deleteTreeNode(t.root, data, t.comparator)
|
||||
}
|
||||
|
||||
// NodeLevel get node level in BSTree
|
||||
func (t *BSTree[T]) NodeLevel(node *datastructure.TreeNode[T]) int {
|
||||
if node == nil {
|
||||
return 0
|
||||
}
|
||||
left := float64(t.NodeLevel(node.Left))
|
||||
right := float64(t.NodeLevel(node.Right))
|
||||
|
||||
return int(math.Max(left, right)) + 1
|
||||
}
|
||||
|
||||
// PreOrderTraverse traverse tree node in pre order
|
||||
func (t *BSTree[T]) PreOrderTraverse() []T {
|
||||
return preOrderTraverse(t.root)
|
||||
}
|
||||
|
||||
// PostOrderTraverse traverse tree node in post order
|
||||
func (t *BSTree[T]) PostOrderTraverse() []T {
|
||||
return postOrderTraverse(t.root)
|
||||
}
|
||||
|
||||
// InOrderTraverse traverse tree node in mid order
|
||||
func (t *BSTree[T]) InOrderTraverse() []T {
|
||||
return inOrderTraverse(t.root)
|
||||
}
|
||||
|
||||
// LevelOrderTraverse traverse tree node in level order
|
||||
func (t *BSTree[T]) LevelOrderTraverse() []T {
|
||||
traversal := make([]T, 0)
|
||||
levelOrderTraverse(t.root, &traversal)
|
||||
return traversal
|
||||
}
|
||||
|
||||
// Depth returns the calculated depth of a binary saerch tree
|
||||
func (t *BSTree[T]) Depth() int {
|
||||
return calculateDepth(t.root, 0)
|
||||
}
|
||||
|
||||
// IsSubTree checks if the tree `t` has `subTree` or not
|
||||
func (t *BSTree[T]) HasSubTree(subTree *BSTree[T]) bool {
|
||||
return hasSubTree(t.root, subTree.root, t.comparator)
|
||||
}
|
||||
|
||||
func hasSubTree[T any](superTreeRoot, subTreeRoot *datastructure.TreeNode[T],
|
||||
comparator constraints.Comparator) bool {
|
||||
result := false
|
||||
|
||||
if superTreeRoot != nil && subTreeRoot != nil {
|
||||
if comparator.Compare(superTreeRoot.Value, subTreeRoot.Value) == 0 {
|
||||
result = isSubTree(superTreeRoot, subTreeRoot, comparator)
|
||||
}
|
||||
if !result {
|
||||
result = hasSubTree(superTreeRoot.Left, subTreeRoot, comparator)
|
||||
}
|
||||
if !result {
|
||||
result = hasSubTree(superTreeRoot.Right, subTreeRoot, comparator)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Print the bstree structure
|
||||
func (t *BSTree[T]) Print() {
|
||||
maxLevel := t.NodeLevel(t.root)
|
||||
nodes := []*datastructure.TreeNode[T]{t.root}
|
||||
printTreeNodes(nodes, 1, maxLevel)
|
||||
}
|
||||
146
datastructure/tree/bstree_test.go
Normal file
146
datastructure/tree/bstree_test.go
Normal file
@@ -0,0 +1,146 @@
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func TestBSTree_PreOrderTraverse(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestBSTree_PreOrderTraverse")
|
||||
|
||||
bstree := NewBSTree(6, &intComparator{})
|
||||
|
||||
bstree.Insert(7)
|
||||
bstree.Insert(5)
|
||||
bstree.Insert(2)
|
||||
bstree.Insert(4)
|
||||
|
||||
acturl := bstree.PreOrderTraverse()
|
||||
assert.Equal([]int{6, 5, 2, 4, 7}, acturl)
|
||||
}
|
||||
|
||||
func TestBSTree_PostOrderTraverse(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestBSTree_PostOrderTraverse")
|
||||
|
||||
bstree := NewBSTree(6, &intComparator{})
|
||||
|
||||
bstree.Insert(7)
|
||||
bstree.Insert(5)
|
||||
bstree.Insert(2)
|
||||
bstree.Insert(4)
|
||||
|
||||
acturl := bstree.PostOrderTraverse()
|
||||
assert.Equal([]int{5, 2, 4, 7, 6}, acturl)
|
||||
}
|
||||
|
||||
func TestBSTree_InOrderTraverse(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestBSTree_InOrderTraverse")
|
||||
|
||||
bstree := NewBSTree(6, &intComparator{})
|
||||
|
||||
bstree.Insert(7)
|
||||
bstree.Insert(5)
|
||||
bstree.Insert(2)
|
||||
bstree.Insert(4)
|
||||
|
||||
acturl := bstree.InOrderTraverse()
|
||||
assert.Equal([]int{2, 4, 5, 6, 7}, acturl)
|
||||
}
|
||||
|
||||
func TestBSTree_LevelOrderTraverse(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestBSTree_LevelOrderTraverse")
|
||||
|
||||
bstree := NewBSTree(6, &intComparator{})
|
||||
|
||||
bstree.Insert(7)
|
||||
bstree.Insert(5)
|
||||
bstree.Insert(2)
|
||||
bstree.Insert(4)
|
||||
|
||||
acturl := bstree.LevelOrderTraverse()
|
||||
assert.Equal([]int{6, 5, 7, 2, 4}, acturl)
|
||||
}
|
||||
|
||||
func TestBSTree_Delete(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestBSTree_Delete")
|
||||
|
||||
bstree := NewBSTree(6, &intComparator{})
|
||||
|
||||
bstree.Insert(7)
|
||||
bstree.Insert(5)
|
||||
bstree.Insert(2)
|
||||
bstree.Insert(4)
|
||||
|
||||
bstree.Delete(4)
|
||||
|
||||
acturl1 := bstree.InOrderTraverse()
|
||||
assert.Equal([]int{2, 5, 6, 7}, acturl1)
|
||||
|
||||
//todo
|
||||
// bstree.DeletetNode(6, comparator)
|
||||
// bstree.Print()
|
||||
// acturl2 := bstree.InOrderTraverse()
|
||||
// assert.Equal([]int{2, 5, 7}, acturl2)
|
||||
}
|
||||
|
||||
func TestBSTree_Depth(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestBSTree_Depth")
|
||||
|
||||
bstree := NewBSTree(6, &intComparator{})
|
||||
|
||||
bstree.Insert(7)
|
||||
bstree.Insert(5)
|
||||
bstree.Insert(2)
|
||||
bstree.Insert(4)
|
||||
|
||||
assert.Equal(bstree.Depth(), 4)
|
||||
}
|
||||
|
||||
func TestBSTree_IsSubTree(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestBSTree_IsSubTree")
|
||||
|
||||
superTree := NewBSTree(8, &intComparator{})
|
||||
superTree.Insert(4)
|
||||
superTree.Insert(5)
|
||||
superTree.Insert(6)
|
||||
superTree.Insert(9)
|
||||
superTree.Insert(4)
|
||||
|
||||
superTree.Print()
|
||||
|
||||
subTree := NewBSTree(5, &intComparator{})
|
||||
subTree.Insert(4)
|
||||
subTree.Insert(6)
|
||||
|
||||
assert.Equal(true, superTree.HasSubTree(subTree))
|
||||
assert.Equal(false, subTree.HasSubTree(superTree))
|
||||
}
|
||||
238
datastructure/tree/tree_internal.go
Normal file
238
datastructure/tree/tree_internal.go
Normal file
@@ -0,0 +1,238 @@
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/duke-git/lancet/v2/constraints"
|
||||
"github.com/duke-git/lancet/v2/datastructure"
|
||||
)
|
||||
|
||||
func preOrderTraverse[T any](node *datastructure.TreeNode[T]) []T {
|
||||
data := []T{}
|
||||
if node != nil {
|
||||
data = append(data, node.Value)
|
||||
data = append(data, preOrderTraverse(node.Left)...)
|
||||
data = append(data, preOrderTraverse(node.Right)...)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func postOrderTraverse[T any](node *datastructure.TreeNode[T]) []T {
|
||||
data := []T{}
|
||||
if node != nil {
|
||||
data = append(data, preOrderTraverse(node.Left)...)
|
||||
data = append(data, preOrderTraverse(node.Right)...)
|
||||
data = append(data, node.Value)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func inOrderTraverse[T any](node *datastructure.TreeNode[T]) []T {
|
||||
data := []T{}
|
||||
if node != nil {
|
||||
data = append(data, inOrderTraverse(node.Left)...)
|
||||
data = append(data, node.Value)
|
||||
data = append(data, inOrderTraverse(node.Right)...)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// func preOrderPrint[T any](node *datastructure.TreeNode[T]) {
|
||||
// if node == nil {
|
||||
// return
|
||||
// }
|
||||
|
||||
// fmt.Printf("%v, ", node.Value)
|
||||
// preOrderPrint(node.Left)
|
||||
// preOrderPrint(node.Right)
|
||||
// }
|
||||
|
||||
// func postOrderPrint[T any](node *datastructure.TreeNode[T]) {
|
||||
// if node == nil {
|
||||
// return
|
||||
// }
|
||||
|
||||
// postOrderPrint(node.Left)
|
||||
// postOrderPrint(node.Right)
|
||||
// fmt.Printf("%v, ", node.Value)
|
||||
// }
|
||||
|
||||
// func inOrderPrint[T any](node *datastructure.TreeNode[T]) {
|
||||
// if node == nil {
|
||||
// return
|
||||
// }
|
||||
|
||||
// inOrderPrint(node.Left)
|
||||
// fmt.Printf("%v, ", node.Value)
|
||||
// inOrderPrint(node.Right)
|
||||
// }
|
||||
|
||||
func levelOrderTraverse[T any](root *datastructure.TreeNode[T], traversal *[]T) {
|
||||
var q []*datastructure.TreeNode[T] // queue
|
||||
var n *datastructure.TreeNode[T] // temp node
|
||||
|
||||
q = append(q, root)
|
||||
|
||||
for len(q) != 0 {
|
||||
n, q = q[0], q[1:]
|
||||
*traversal = append(*traversal, n.Value)
|
||||
if n.Left != nil {
|
||||
q = append(q, n.Left)
|
||||
}
|
||||
if n.Right != nil {
|
||||
q = append(q, n.Right)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func insertTreeNode[T any](rootNode, newNode *datastructure.TreeNode[T], comparator constraints.Comparator) {
|
||||
if comparator.Compare(newNode.Value, rootNode.Value) == -1 {
|
||||
if rootNode.Left == nil {
|
||||
rootNode.Left = newNode
|
||||
} else {
|
||||
insertTreeNode(rootNode.Left, newNode, comparator)
|
||||
}
|
||||
} else {
|
||||
if rootNode.Right == nil {
|
||||
rootNode.Right = newNode
|
||||
} else {
|
||||
insertTreeNode(rootNode.Right, newNode, comparator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// todo, delete root node failed
|
||||
func deleteTreeNode[T any](node *datastructure.TreeNode[T], data T, comparator constraints.Comparator) *datastructure.TreeNode[T] {
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
if comparator.Compare(data, node.Value) == -1 {
|
||||
node.Left = deleteTreeNode(node.Left, data, comparator)
|
||||
} else if comparator.Compare(data, node.Value) == 1 {
|
||||
node.Right = deleteTreeNode(node.Right, data, comparator)
|
||||
} else {
|
||||
if node.Left == nil {
|
||||
node = node.Right
|
||||
} else if node.Right == nil {
|
||||
node = node.Left
|
||||
} else {
|
||||
l := node.Right
|
||||
d := inOrderSuccessor(l)
|
||||
d.Left = node.Left
|
||||
return node.Right
|
||||
}
|
||||
}
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
func inOrderSuccessor[T any](root *datastructure.TreeNode[T]) *datastructure.TreeNode[T] {
|
||||
cur := root
|
||||
for cur.Left != nil {
|
||||
cur = cur.Left
|
||||
}
|
||||
return cur
|
||||
}
|
||||
|
||||
func printTreeNodes[T any](nodes []*datastructure.TreeNode[T], level, maxLevel int) {
|
||||
if len(nodes) == 0 || isAllNil(nodes) {
|
||||
return
|
||||
}
|
||||
|
||||
floor := maxLevel - level
|
||||
endgeLines := int(math.Pow(float64(2), (math.Max(float64(floor)-1, 0))))
|
||||
firstSpaces := int(math.Pow(float64(2), float64(floor))) - 1
|
||||
betweenSpaces := int(math.Pow(float64(2), float64(floor)+1)) - 1
|
||||
|
||||
printSpaces(firstSpaces)
|
||||
|
||||
newNodes := []*datastructure.TreeNode[T]{}
|
||||
for _, node := range nodes {
|
||||
if node != nil {
|
||||
fmt.Printf("%v", node.Value)
|
||||
newNodes = append(newNodes, node.Left)
|
||||
newNodes = append(newNodes, node.Right)
|
||||
} else {
|
||||
newNodes = append(newNodes, nil)
|
||||
newNodes = append(newNodes, nil)
|
||||
printSpaces(1)
|
||||
}
|
||||
|
||||
printSpaces(betweenSpaces)
|
||||
}
|
||||
|
||||
fmt.Println("")
|
||||
|
||||
for i := 1; i <= endgeLines; i++ {
|
||||
for j := 0; j < len(nodes); j++ {
|
||||
printSpaces(firstSpaces - i)
|
||||
if nodes[j] == nil {
|
||||
printSpaces(endgeLines + endgeLines + i + 1)
|
||||
continue
|
||||
}
|
||||
|
||||
if nodes[j].Left != nil {
|
||||
fmt.Print("/")
|
||||
} else {
|
||||
printSpaces(1)
|
||||
}
|
||||
|
||||
printSpaces(i + i - 1)
|
||||
|
||||
if nodes[j].Right != nil {
|
||||
fmt.Print("\\")
|
||||
} else {
|
||||
printSpaces(1)
|
||||
}
|
||||
printSpaces(endgeLines + endgeLines - 1)
|
||||
}
|
||||
fmt.Println("")
|
||||
}
|
||||
|
||||
printTreeNodes(newNodes, level+1, maxLevel)
|
||||
}
|
||||
|
||||
// printSpaces
|
||||
func printSpaces(n int) {
|
||||
for i := 0; i < n; i++ {
|
||||
fmt.Print(" ")
|
||||
}
|
||||
}
|
||||
|
||||
func isAllNil[T any](nodes []*datastructure.TreeNode[T]) bool {
|
||||
for _, v := range nodes {
|
||||
if v != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func calculateDepth[T any](node *datastructure.TreeNode[T], depth int) int {
|
||||
if node == nil {
|
||||
return depth
|
||||
}
|
||||
return max(calculateDepth(node.Left, depth+1), calculateDepth(node.Right, depth+1))
|
||||
}
|
||||
|
||||
func isSubTree[T any](superTreeRoot, subTreeRoot *datastructure.TreeNode[T], comparator constraints.Comparator) bool {
|
||||
if subTreeRoot == nil {
|
||||
return true
|
||||
}
|
||||
if superTreeRoot == nil {
|
||||
return false
|
||||
}
|
||||
if comparator.Compare(superTreeRoot.Value, subTreeRoot.Value) != 0 {
|
||||
return false
|
||||
}
|
||||
result := isSubTree(superTreeRoot.Left, subTreeRoot.Left, comparator) && isSubTree(superTreeRoot.Right, subTreeRoot.Right, comparator)
|
||||
return result
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
@@ -9,17 +9,20 @@ type theTime struct {
|
||||
unix int64
|
||||
}
|
||||
|
||||
// NewUnixNow return unix timestamp of current time
|
||||
// NewUnixNow return unix timestamp of current time.
|
||||
// Play: https://go.dev/play/p/U4PPx-9D0oz
|
||||
func NewUnixNow() *theTime {
|
||||
return &theTime{unix: time.Now().Unix()}
|
||||
}
|
||||
|
||||
// NewUnix return unix timestamp of specified time
|
||||
// NewUnix return unix timestamp of specified time.
|
||||
// Play: https://go.dev/play/p/psoSuh_kLRt
|
||||
func NewUnix(unix int64) *theTime {
|
||||
return &theTime{unix: unix}
|
||||
}
|
||||
|
||||
// NewFormat return unix timestamp of specified time string, t should be "yyyy-mm-dd hh:mm:ss"
|
||||
// NewFormat return unix timestamp of specified time string, t should be "yyyy-mm-dd hh:mm:ss".
|
||||
// Play: https://go.dev/play/p/VkW08ZOaXPZ
|
||||
func NewFormat(t string) (*theTime, error) {
|
||||
timeLayout := "2006-01-02 15:04:05"
|
||||
loc := time.FixedZone("CST", 8*3600)
|
||||
@@ -30,7 +33,8 @@ func NewFormat(t string) (*theTime, error) {
|
||||
return &theTime{unix: tt.Unix()}, nil
|
||||
}
|
||||
|
||||
// NewISO8601 return unix timestamp of specified iso8601 time string
|
||||
// NewISO8601 return unix timestamp of specified iso8601 time string.
|
||||
// Play: https://go.dev/play/p/mkhOHQkdeA2
|
||||
func NewISO8601(iso8601 string) (*theTime, error) {
|
||||
t, err := time.ParseInLocation(time.RFC3339, iso8601, time.UTC)
|
||||
if err != nil {
|
||||
@@ -39,22 +43,26 @@ func NewISO8601(iso8601 string) (*theTime, error) {
|
||||
return &theTime{unix: t.Unix()}, nil
|
||||
}
|
||||
|
||||
// ToUnix return unix timestamp
|
||||
// ToUnix return unix timestamp.
|
||||
// Play: https://go.dev/play/p/_LUiwAdocjy
|
||||
func (t *theTime) ToUnix() int64 {
|
||||
return t.unix
|
||||
}
|
||||
|
||||
// ToFormat return the time string 'yyyy-mm-dd hh:mm:ss' of unix time
|
||||
// ToFormat return the time string 'yyyy-mm-dd hh:mm:ss' of unix time.
|
||||
// Play: https://go.dev/play/p/VkW08ZOaXPZ
|
||||
func (t *theTime) ToFormat() string {
|
||||
return time.Unix(t.unix, 0).Format("2006-01-02 15:04:05")
|
||||
}
|
||||
|
||||
// ToFormatForTpl return the time string which format is specified tpl
|
||||
// ToFormatForTpl return the time string which format is specified tpl.
|
||||
// Play: https://go.dev/play/p/nyXxXcQJ8L5
|
||||
func (t *theTime) ToFormatForTpl(tpl string) string {
|
||||
return time.Unix(t.unix, 0).Format(tpl)
|
||||
}
|
||||
|
||||
// ToFormatForTpl return iso8601 time string
|
||||
// ToFormatForTpl return iso8601 time string.
|
||||
// Play: https://go.dev/play/p/mkhOHQkdeA2
|
||||
func (t *theTime) ToIso8601() string {
|
||||
return time.Unix(t.unix, 0).Format(time.RFC3339)
|
||||
}
|
||||
|
||||
@@ -3,10 +3,12 @@ package datetime
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/internal"
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestToUnix(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestToUnix")
|
||||
|
||||
tm1 := NewUnixNow()
|
||||
@@ -17,38 +19,37 @@ func TestToUnix(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestToFormat(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestToFormat")
|
||||
|
||||
tm, err := NewFormat("2022/03/18 17:04:05")
|
||||
assert.IsNotNil(err)
|
||||
|
||||
tm, err = NewFormat("2022-03-18 17:04:05")
|
||||
tm, err := NewFormat("2022-03-18 17:04:05")
|
||||
t.Log("TestToFormat", tm.ToFormat())
|
||||
assert.IsNil(err)
|
||||
|
||||
t.Log("ToFormat -> ", tm.ToFormat())
|
||||
}
|
||||
|
||||
func TestToFormatForTpl(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestToFormatForTpl")
|
||||
|
||||
tm, err := NewFormat("2022/03/18 17:04:05")
|
||||
_, err := NewFormat("2022/03/18 17:04:05")
|
||||
assert.IsNotNil(err)
|
||||
|
||||
tm, err = NewFormat("2022-03-18 17:04:05")
|
||||
tm, err := NewFormat("2022-03-18 17:04:05")
|
||||
t.Log("TestToFormatForTpl", tm.ToFormatForTpl("2006/01/02 15:04:05"))
|
||||
assert.IsNil(err)
|
||||
|
||||
t.Log("ToFormatForTpl -> ", tm.ToFormatForTpl("2006/01/02 15:04:05"))
|
||||
|
||||
}
|
||||
|
||||
func TestToIso8601(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestToIso8601")
|
||||
|
||||
tm, err := NewISO8601("2022-03-18 17:04:05")
|
||||
_, err := NewISO8601("2022-03-18 17:04:05")
|
||||
assert.IsNotNil(err)
|
||||
|
||||
tm, err = NewISO8601("2006-01-02T15:04:05.999Z")
|
||||
tm, err := NewISO8601("2006-01-02T15:04:05.999Z")
|
||||
t.Log("TestToIso8601", tm.ToIso8601())
|
||||
assert.IsNil(err)
|
||||
|
||||
t.Log("ToIso8601 -> ", tm.ToIso8601())
|
||||
}
|
||||
|
||||
@@ -3,29 +3,34 @@
|
||||
|
||||
// Package datetime implements some functions to format date and time.
|
||||
// Note:
|
||||
// 1. `format` param in FormatTimeToStr function should be as flow:
|
||||
//"yyyy-mm-dd hh:mm:ss"
|
||||
//"yyyy-mm-dd hh:mm"
|
||||
//"yyyy-mm-dd hh"
|
||||
//"yyyy-mm-dd"
|
||||
//"yyyy-mm"
|
||||
//"mm-dd"
|
||||
//"dd-mm-yy hh:mm:ss"
|
||||
//"yyyy/mm/dd hh:mm:ss"
|
||||
//"yyyy/mm/dd hh:mm"
|
||||
//"yyyy/mm/dd hh"
|
||||
//"yyyy/mm/dd"
|
||||
//"yyyy/mm"
|
||||
//"mm/dd"
|
||||
//"dd/mm/yy hh:mm:ss"
|
||||
//"yyyy"
|
||||
//"mm"
|
||||
//"hh:mm:ss"
|
||||
//"mm:ss"
|
||||
// 1. `format` param in FormatTimeToStr function should be as flow (case no sensitive):
|
||||
// "yyyy-mm-dd hh:mm:ss"
|
||||
// "yyyy-mm-dd hh:mm"
|
||||
// "yyyy-mm-dd hh"
|
||||
// "yyyy-mm-dd"
|
||||
// "yyyy-mm"
|
||||
// "mm-dd"
|
||||
// "dd-mm-yy hh:mm:ss"
|
||||
// "yyyy/mm/dd hh:mm:ss"
|
||||
// "yyyy/mm/dd hh:mm"
|
||||
// "yyyy/mm/dd hh"
|
||||
// "yyyy/mm/dd"
|
||||
// "yyyy/mm"
|
||||
// "mm/dd"
|
||||
// "dd/mm/yy hh:mm:ss"
|
||||
// "yyyymmdd"
|
||||
// "mmddyy"
|
||||
// "yyyy"
|
||||
// "yy"
|
||||
// "mm"
|
||||
// "hh:mm:ss"
|
||||
// "hh:mm"
|
||||
// "mm:ss"
|
||||
package datetime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -35,7 +40,7 @@ func init() {
|
||||
timeFormat = map[string]string{
|
||||
"yyyy-mm-dd hh:mm:ss": "2006-01-02 15:04:05",
|
||||
"yyyy-mm-dd hh:mm": "2006-01-02 15:04",
|
||||
"yyyy-mm-dd hh": "2006-01-02 15:04",
|
||||
"yyyy-mm-dd hh": "2006-01-02 15",
|
||||
"yyyy-mm-dd": "2006-01-02",
|
||||
"yyyy-mm": "2006-01",
|
||||
"mm-dd": "01-02",
|
||||
@@ -47,136 +52,341 @@ func init() {
|
||||
"yyyy/mm": "2006/01",
|
||||
"mm/dd": "01/02",
|
||||
"dd/mm/yy hh:mm:ss": "02/01/06 15:04:05",
|
||||
"yyyymmdd": "20060102",
|
||||
"mmddyy": "010206",
|
||||
"yyyy": "2006",
|
||||
"yy": "06",
|
||||
"mm": "01",
|
||||
"hh:mm:ss": "15:04:05",
|
||||
"hh:mm": "15:04",
|
||||
"mm:ss": "04:05",
|
||||
}
|
||||
}
|
||||
|
||||
// AddMinute add or sub minute to the time
|
||||
// AddMinute add or sub minute to the time.
|
||||
// Play: https://go.dev/play/p/nT1heB1KUUK
|
||||
func AddMinute(t time.Time, minute int64) time.Time {
|
||||
return t.Add(time.Minute * time.Duration(minute))
|
||||
}
|
||||
|
||||
// AddHour add or sub hour to the time
|
||||
// AddHour add or sub hour to the time.
|
||||
// Play: https://go.dev/play/p/rcMjd7OCsi5
|
||||
func AddHour(t time.Time, hour int64) time.Time {
|
||||
return t.Add(time.Hour * time.Duration(hour))
|
||||
}
|
||||
|
||||
// AddDay add or sub day to the time
|
||||
// AddDay add or sub day to the time.
|
||||
// Play: https://go.dev/play/p/dIGbs_uTdFa
|
||||
func AddDay(t time.Time, day int64) time.Time {
|
||||
return t.Add(24 * time.Hour * time.Duration(day))
|
||||
}
|
||||
|
||||
// GetNowDate return format yyyy-mm-dd of current date
|
||||
// AddYear add or sub year to the time.
|
||||
// Play: https://go.dev/play/p/MqW2ujnBx10
|
||||
func AddYear(t time.Time, year int64) time.Time {
|
||||
return t.Add(365 * 24 * time.Hour * time.Duration(year))
|
||||
}
|
||||
|
||||
// GetNowDate return format yyyy-mm-dd of current date.
|
||||
// Play: https://go.dev/play/p/PvfkPpcpBBf
|
||||
func GetNowDate() string {
|
||||
return time.Now().Format("2006-01-02")
|
||||
}
|
||||
|
||||
// GetNowTime return format hh-mm-ss of current time
|
||||
// GetNowTime return format hh-mm-ss of current time.
|
||||
// Play: https://go.dev/play/p/l7BNxCkTmJS
|
||||
func GetNowTime() string {
|
||||
return time.Now().Format("15:04:05")
|
||||
}
|
||||
|
||||
// GetNowDateTime return format yyyy-mm-dd hh-mm-ss of current datetime
|
||||
// GetNowDateTime return format yyyy-mm-dd hh-mm-ss of current datetime.
|
||||
// Play: https://go.dev/play/p/pI4AqngD0al
|
||||
func GetNowDateTime() string {
|
||||
return time.Now().Format("2006-01-02 15:04:05")
|
||||
}
|
||||
|
||||
// GetZeroHourTimestamp return timestamp of zero hour (timestamp of 00:00)
|
||||
// GetTodayStartTime return the start time of today, format: yyyy-mm-dd 00:00:00.
|
||||
// Play: https://go.dev/play/p/84siyYF7t99
|
||||
func GetTodayStartTime() string {
|
||||
return time.Now().Format("2006-01-02") + " 00:00:00"
|
||||
}
|
||||
|
||||
// GetTodayEndTime return the end time of today, format: yyyy-mm-dd 23:59:59.
|
||||
// Play: https://go.dev/play/p/jjrLnfoqgn3
|
||||
func GetTodayEndTime() string {
|
||||
return time.Now().Format("2006-01-02") + " 23:59:59"
|
||||
}
|
||||
|
||||
// GetZeroHourTimestamp return timestamp of zero hour (timestamp of 00:00).
|
||||
// Play: https://go.dev/play/p/QmL2oIaGE3q
|
||||
func GetZeroHourTimestamp() int64 {
|
||||
ts := time.Now().Format("2006-01-02")
|
||||
t, _ := time.Parse("2006-01-02", ts)
|
||||
return t.UTC().Unix() - 8*3600
|
||||
}
|
||||
|
||||
// GetNightTimestamp return timestamp of zero hour (timestamp of 23:59)
|
||||
// GetNightTimestamp return timestamp of zero hour (timestamp of 23:59).
|
||||
// Play: https://go.dev/play/p/UolysR3MYP1
|
||||
func GetNightTimestamp() int64 {
|
||||
return GetZeroHourTimestamp() + 86400 - 1
|
||||
}
|
||||
|
||||
// FormatTimeToStr convert time to string
|
||||
func FormatTimeToStr(t time.Time, format string) string {
|
||||
return t.Format(timeFormat[format])
|
||||
}
|
||||
|
||||
// FormatStrToTime convert string to time
|
||||
func FormatStrToTime(str, format string) (time.Time, error) {
|
||||
v, ok := timeFormat[format]
|
||||
// FormatTimeToStr convert time to string.
|
||||
// Play: https://go.dev/play/p/_Ia7M8H_OvE
|
||||
func FormatTimeToStr(t time.Time, format string, timezone ...string) string {
|
||||
tf, ok := timeFormat[strings.ToLower(format)]
|
||||
if !ok {
|
||||
return time.Time{}, fmt.Errorf("format %s not found", format)
|
||||
return ""
|
||||
}
|
||||
|
||||
return time.Parse(v, str)
|
||||
if timezone != nil && timezone[0] != "" {
|
||||
loc, err := time.LoadLocation(timezone[0])
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return t.In(loc).Format(tf)
|
||||
}
|
||||
return t.Format(tf)
|
||||
}
|
||||
|
||||
// BeginOfMinute return beginning minute time of day
|
||||
// FormatStrToTime convert string to time.
|
||||
// Play: https://go.dev/play/p/1h9FwdU8ql4
|
||||
func FormatStrToTime(str, format string, timezone ...string) (time.Time, error) {
|
||||
tf, ok := timeFormat[strings.ToLower(format)]
|
||||
if !ok {
|
||||
return time.Time{}, fmt.Errorf("format %s not support", format)
|
||||
}
|
||||
|
||||
if timezone != nil && timezone[0] != "" {
|
||||
loc, err := time.LoadLocation(timezone[0])
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
|
||||
return time.ParseInLocation(tf, str, loc)
|
||||
}
|
||||
|
||||
return time.Parse(tf, str)
|
||||
}
|
||||
|
||||
// BeginOfMinute return beginning minute time of day.
|
||||
// Play: https://go.dev/play/p/ieOLVJ9CiFT
|
||||
func BeginOfMinute(t time.Time) time.Time {
|
||||
y, m, d := t.Date()
|
||||
return time.Date(y, m, d, t.Hour(), t.Minute(), 0, 0, t.Location())
|
||||
}
|
||||
|
||||
// EndOfMinute return end minute time of day
|
||||
// EndOfMinute return end minute time of day.
|
||||
// Play: https://go.dev/play/p/yrL5wGzPj4z
|
||||
func EndOfMinute(t time.Time) time.Time {
|
||||
y, m, d := t.Date()
|
||||
return time.Date(y, m, d, t.Hour(), t.Minute(), 59, int(time.Second-time.Nanosecond), t.Location())
|
||||
}
|
||||
|
||||
// BeginOfHour return beginning hour time of day
|
||||
// BeginOfHour return beginning hour time of day.
|
||||
// Play: https://go.dev/play/p/GhdGFnDWpYs
|
||||
func BeginOfHour(t time.Time) time.Time {
|
||||
y, m, d := t.Date()
|
||||
return time.Date(y, m, d, t.Hour(), 0, 0, 0, t.Location())
|
||||
}
|
||||
|
||||
// EndOfHour return end hour time of day
|
||||
// EndOfHour return end hour time of day.
|
||||
// Play: https://go.dev/play/p/6ce3j_6cVqN
|
||||
func EndOfHour(t time.Time) time.Time {
|
||||
y, m, d := t.Date()
|
||||
return time.Date(y, m, d, t.Hour(), 59, 59, int(time.Second-time.Nanosecond), t.Location())
|
||||
}
|
||||
|
||||
// BeginOfDay return beginning hour time of day
|
||||
// BeginOfDay return beginning hour time of day.
|
||||
// Play: https://go.dev/play/p/94m_UT6cWs9
|
||||
func BeginOfDay(t time.Time) time.Time {
|
||||
y, m, d := t.Date()
|
||||
return time.Date(y, m, d, 0, 0, 0, 0, t.Location())
|
||||
}
|
||||
|
||||
// EndOfDay return end time of day
|
||||
// EndOfDay return end time of day.
|
||||
// Play: https://go.dev/play/p/eMBOvmq5Ih1
|
||||
func EndOfDay(t time.Time) time.Time {
|
||||
y, m, d := t.Date()
|
||||
return time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), t.Location())
|
||||
}
|
||||
|
||||
// BeginOfWeek return beginning week, week begin from Sunday
|
||||
func BeginOfWeek(t time.Time) time.Time {
|
||||
y, m, d := t.AddDate(0, 0, 0-int(BeginOfDay(t).Weekday())).Date()
|
||||
return time.Date(y, m, d, 0, 0, 0, 0, t.Location())
|
||||
// BeginOfWeek return beginning week, default week begin from Sunday.
|
||||
// Play: https://go.dev/play/p/ynjoJPz7VNV
|
||||
func BeginOfWeek(t time.Time, beginFrom ...time.Weekday) time.Time {
|
||||
var beginFromWeekday = time.Sunday
|
||||
if len(beginFrom) > 0 {
|
||||
beginFromWeekday = beginFrom[0]
|
||||
}
|
||||
y, m, d := t.AddDate(0, 0, int(beginFromWeekday-t.Weekday())).Date()
|
||||
beginOfWeek := time.Date(y, m, d, 0, 0, 0, 0, t.Location())
|
||||
if beginOfWeek.After(t) {
|
||||
return beginOfWeek.AddDate(0, 0, -7)
|
||||
}
|
||||
return beginOfWeek
|
||||
}
|
||||
|
||||
// EndOfWeek return end week time, week end with Saturday
|
||||
func EndOfWeek(t time.Time) time.Time {
|
||||
y, m, d := BeginOfWeek(t).AddDate(0, 0, 7).Add(-time.Nanosecond).Date()
|
||||
return time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), t.Location())
|
||||
// EndOfWeek return end week time, default week end with Saturday.
|
||||
// Play: https://go.dev/play/p/i08qKXD9flf
|
||||
func EndOfWeek(t time.Time, endWith ...time.Weekday) time.Time {
|
||||
var endWithWeekday = time.Saturday
|
||||
if len(endWith) > 0 {
|
||||
endWithWeekday = endWith[0]
|
||||
}
|
||||
y, m, d := t.AddDate(0, 0, int(endWithWeekday-t.Weekday())).Date()
|
||||
var endWithWeek = time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), t.Location())
|
||||
if endWithWeek.Before(t) {
|
||||
endWithWeek = endWithWeek.AddDate(0, 0, 7)
|
||||
}
|
||||
return endWithWeek
|
||||
}
|
||||
|
||||
// BeginOfMonth return beginning of month
|
||||
// BeginOfMonth return beginning of month.
|
||||
// Play: https://go.dev/play/p/bWXVFsmmzwL
|
||||
func BeginOfMonth(t time.Time) time.Time {
|
||||
y, m, _ := t.Date()
|
||||
return time.Date(y, m, 1, 0, 0, 0, 0, t.Location())
|
||||
}
|
||||
|
||||
// EndOfMonth return end of month
|
||||
// EndOfMonth return end of month.
|
||||
// Play: https://go.dev/play/p/_GWh10B3Nqi
|
||||
func EndOfMonth(t time.Time) time.Time {
|
||||
return BeginOfMonth(t).AddDate(0, 1, 0).Add(-time.Nanosecond)
|
||||
}
|
||||
|
||||
// BeginOfYear return beginning of year
|
||||
// BeginOfYear return the date time at the begin of year.
|
||||
// Play: https://go.dev/play/p/i326DSwLnV8
|
||||
func BeginOfYear(t time.Time) time.Time {
|
||||
y, _, _ := t.Date()
|
||||
return time.Date(y, time.January, 1, 0, 0, 0, 0, t.Location())
|
||||
}
|
||||
|
||||
// EndOfYear return end of year
|
||||
// EndOfYear return the date time at the end of year.
|
||||
// Play: https://go.dev/play/p/G01cKlMCvNm
|
||||
func EndOfYear(t time.Time) time.Time {
|
||||
return BeginOfYear(t).AddDate(1, 0, 0).Add(-time.Nanosecond)
|
||||
}
|
||||
|
||||
// IsLeapYear check if param year is leap year or not.
|
||||
// Play: https://go.dev/play/p/xS1eS2ejGew
|
||||
func IsLeapYear(year int) bool {
|
||||
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
|
||||
}
|
||||
|
||||
// BetweenSeconds returns the number of seconds between two times.
|
||||
// Play: https://go.dev/play/p/n3YDRyfyXJu
|
||||
func BetweenSeconds(t1 time.Time, t2 time.Time) int64 {
|
||||
index := t2.Unix() - t1.Unix()
|
||||
return index
|
||||
}
|
||||
|
||||
// DayOfYear returns which day of the year the parameter date `t` is.
|
||||
// Play: https://go.dev/play/p/0hjqhTwFNlH
|
||||
func DayOfYear(t time.Time) int {
|
||||
y, m, d := t.Date()
|
||||
firstDay := time.Date(y, 1, 1, 0, 0, 0, 0, t.Location())
|
||||
nowDate := time.Date(y, m, d, 0, 0, 0, 0, t.Location())
|
||||
|
||||
return int(nowDate.Sub(firstDay).Hours() / 24)
|
||||
}
|
||||
|
||||
// IsWeekend checks if passed time is weekend or not.
|
||||
// Play: https://go.dev/play/p/cupRM5aZOIY
|
||||
// Deprecated Use '== Weekday' instead
|
||||
func IsWeekend(t time.Time) bool {
|
||||
return time.Saturday == t.Weekday() || time.Sunday == t.Weekday()
|
||||
}
|
||||
|
||||
// NowDateOrTime return current datetime with specific format and timezone.
|
||||
// Play: https://go.dev/play/p/EZ-begEjtT0
|
||||
func NowDateOrTime(format string, timezone ...string) string {
|
||||
tf, ok := timeFormat[strings.ToLower(format)]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
if timezone != nil && timezone[0] != "" {
|
||||
loc, err := time.LoadLocation(timezone[0])
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return time.Now().In(loc).Format(tf)
|
||||
}
|
||||
|
||||
return time.Now().Format(tf)
|
||||
}
|
||||
|
||||
// Timestamp return current second timestamp.
|
||||
// Play: https://go.dev/play/p/iU5b7Vvjx6x
|
||||
func Timestamp(timezone ...string) int64 {
|
||||
t := time.Now()
|
||||
|
||||
if timezone != nil && timezone[0] != "" {
|
||||
loc, err := time.LoadLocation(timezone[0])
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
t = t.In(loc)
|
||||
}
|
||||
|
||||
return t.Unix()
|
||||
}
|
||||
|
||||
// TimestampMilli return current mill second timestamp.
|
||||
// Play: https://go.dev/play/p/4gvEusOTu1T
|
||||
func TimestampMilli(timezone ...string) int64 {
|
||||
t := time.Now()
|
||||
|
||||
if timezone != nil && timezone[0] != "" {
|
||||
loc, err := time.LoadLocation(timezone[0])
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
t = t.In(loc)
|
||||
}
|
||||
|
||||
return int64(time.Nanosecond) * t.UnixNano() / int64(time.Millisecond)
|
||||
}
|
||||
|
||||
// TimestampMicro return current micro second timestamp.
|
||||
// Play: https://go.dev/play/p/2maANglKHQE
|
||||
func TimestampMicro(timezone ...string) int64 {
|
||||
t := time.Now()
|
||||
|
||||
if timezone != nil && timezone[0] != "" {
|
||||
loc, err := time.LoadLocation(timezone[0])
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
t = t.In(loc)
|
||||
}
|
||||
|
||||
return int64(time.Nanosecond) * t.UnixNano() / int64(time.Microsecond)
|
||||
}
|
||||
|
||||
// TimestampNano return current nano second timestamp.
|
||||
// Play: https://go.dev/play/p/A9Oq_COrcCF
|
||||
func TimestampNano(timezone ...string) int64 {
|
||||
t := time.Now()
|
||||
|
||||
if timezone != nil && timezone[0] != "" {
|
||||
loc, err := time.LoadLocation(timezone[0])
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
t = t.In(loc)
|
||||
}
|
||||
|
||||
return t.UnixNano()
|
||||
}
|
||||
|
||||
// TraceFuncTime: trace the func costed time,just call it at top of the func like `defer TraceFuncTime()()`
|
||||
func TraceFuncTime() func() {
|
||||
pre := time.Now()
|
||||
return func() {
|
||||
elapsed := time.Since(pre)
|
||||
fmt.Println("Costs Time:\t", elapsed)
|
||||
}
|
||||
}
|
||||
|
||||
410
datetime/datetime_example_test.go
Normal file
410
datetime/datetime_example_test.go
Normal file
@@ -0,0 +1,410 @@
|
||||
package datetime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ExampleAddDay() {
|
||||
now := time.Now()
|
||||
|
||||
tomorrow := AddDay(now, 1)
|
||||
diff1 := tomorrow.Sub(now)
|
||||
|
||||
yesterday := AddDay(now, -1)
|
||||
diff2 := yesterday.Sub(now)
|
||||
|
||||
fmt.Println(diff1)
|
||||
fmt.Println(diff2)
|
||||
|
||||
// Output:
|
||||
// 24h0m0s
|
||||
// -24h0m0s
|
||||
}
|
||||
|
||||
func ExampleAddHour() {
|
||||
now := time.Now()
|
||||
|
||||
after2Hours := AddHour(now, 2)
|
||||
diff1 := after2Hours.Sub(now)
|
||||
|
||||
before2Hours := AddHour(now, -2)
|
||||
diff2 := before2Hours.Sub(now)
|
||||
|
||||
fmt.Println(diff1)
|
||||
fmt.Println(diff2)
|
||||
|
||||
// Output:
|
||||
// 2h0m0s
|
||||
// -2h0m0s
|
||||
}
|
||||
|
||||
func ExampleAddMinute() {
|
||||
now := time.Now()
|
||||
|
||||
after2Minutes := AddMinute(now, 2)
|
||||
diff1 := after2Minutes.Sub(now)
|
||||
|
||||
before2Minutes := AddMinute(now, -2)
|
||||
diff2 := before2Minutes.Sub(now)
|
||||
|
||||
fmt.Println(diff1)
|
||||
fmt.Println(diff2)
|
||||
|
||||
// Output:
|
||||
// 2m0s
|
||||
// -2m0s
|
||||
}
|
||||
|
||||
func ExampleAddYear() {
|
||||
now := time.Now()
|
||||
|
||||
after1Year := AddYear(now, 1)
|
||||
diff1 := after1Year.Sub(now)
|
||||
|
||||
before1Year := AddYear(now, -1)
|
||||
diff2 := before1Year.Sub(now)
|
||||
|
||||
fmt.Println(diff1)
|
||||
fmt.Println(diff2)
|
||||
|
||||
// Output:
|
||||
// 8760h0m0s
|
||||
// -8760h0m0s
|
||||
}
|
||||
|
||||
func ExampleGetNowDate() {
|
||||
result := GetNowDate()
|
||||
|
||||
expected := time.Now().Format("2006-01-02")
|
||||
|
||||
fmt.Println(result == expected)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleGetNowTime() {
|
||||
result := GetNowTime()
|
||||
|
||||
expected := time.Now().Format("15:04:05")
|
||||
|
||||
fmt.Println(result == expected)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleGetNowDateTime() {
|
||||
result := GetNowDateTime()
|
||||
|
||||
expected := time.Now().Format("2006-01-02 15:04:05")
|
||||
|
||||
fmt.Println(result == expected)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
// func ExampleGetZeroHourTimestamp() {
|
||||
// ts := GetZeroHourTimestamp()
|
||||
|
||||
// fmt.Println(ts)
|
||||
|
||||
// // Output:
|
||||
// // 1673107200
|
||||
// }
|
||||
|
||||
// func ExampleGetNightTimestamp() {
|
||||
// ts := GetNightTimestamp()
|
||||
|
||||
// fmt.Println(ts)
|
||||
|
||||
// // Output:
|
||||
// // 1673193599
|
||||
// }
|
||||
|
||||
func ExampleFormatTimeToStr() {
|
||||
datetime, _ := time.Parse("2006-01-02 15:04:05", "2021-01-02 16:04:08")
|
||||
|
||||
result1 := FormatTimeToStr(datetime, "yyyy-mm-dd hh:mm:ss")
|
||||
result2 := FormatTimeToStr(datetime, "yyyy-mm-dd")
|
||||
result3 := FormatTimeToStr(datetime, "dd-mm-yy hh:mm:ss")
|
||||
result4 := FormatTimeToStr(datetime, "yyyy-mm-dd hh")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// 2021-01-02 16:04:08
|
||||
// 2021-01-02
|
||||
// 02-01-21 16:04:08
|
||||
// 2021-01-02 16
|
||||
}
|
||||
|
||||
func ExampleFormatStrToTime() {
|
||||
result1, _ := FormatStrToTime("2021-01-02 16:04:08", "yyyy-mm-dd hh:mm:ss")
|
||||
result2, _ := FormatStrToTime("2021-01-02", "yyyy-mm-dd")
|
||||
result3, _ := FormatStrToTime("02-01-21 16:04:08", "dd-mm-yy hh:mm:ss")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 2021-01-02 16:04:08 +0000 UTC
|
||||
// 2021-01-02 00:00:00 +0000 UTC
|
||||
// 2021-01-02 16:04:08 +0000 UTC
|
||||
}
|
||||
|
||||
func ExampleBeginOfMinute() {
|
||||
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
|
||||
|
||||
result := BeginOfMinute(input)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 2023-01-08 18:50:00 +0000 UTC
|
||||
}
|
||||
|
||||
func ExampleEndOfMinute() {
|
||||
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
|
||||
|
||||
result := EndOfMinute(input)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 2023-01-08 18:50:59.999999999 +0000 UTC
|
||||
}
|
||||
|
||||
func ExampleBeginOfHour() {
|
||||
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
|
||||
|
||||
result := BeginOfHour(input)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 2023-01-08 18:00:00 +0000 UTC
|
||||
}
|
||||
|
||||
func ExampleEndOfHour() {
|
||||
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
|
||||
|
||||
result := EndOfHour(input)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 2023-01-08 18:59:59.999999999 +0000 UTC
|
||||
}
|
||||
|
||||
func ExampleBeginOfDay() {
|
||||
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
|
||||
|
||||
result := BeginOfDay(input)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 2023-01-08 00:00:00 +0000 UTC
|
||||
}
|
||||
|
||||
func ExampleEndOfDay() {
|
||||
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
|
||||
|
||||
result := EndOfDay(input)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 2023-01-08 23:59:59.999999999 +0000 UTC
|
||||
}
|
||||
|
||||
func ExampleBeginOfWeek() {
|
||||
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
|
||||
|
||||
result := BeginOfWeek(input)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 2023-01-08 00:00:00 +0000 UTC
|
||||
}
|
||||
|
||||
func ExampleEndOfWeek() {
|
||||
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
|
||||
|
||||
result := EndOfWeek(input)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 2023-01-14 23:59:59.999999999 +0000 UTC
|
||||
}
|
||||
|
||||
func ExampleBeginOfMonth() {
|
||||
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
|
||||
|
||||
result := BeginOfMonth(input)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 2023-01-01 00:00:00 +0000 UTC
|
||||
}
|
||||
|
||||
func ExampleEndOfMonth() {
|
||||
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
|
||||
|
||||
result := EndOfMonth(input)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 2023-01-31 23:59:59.999999999 +0000 UTC
|
||||
}
|
||||
|
||||
func ExampleBeginOfYear() {
|
||||
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
|
||||
|
||||
result := BeginOfYear(input)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 2023-01-01 00:00:00 +0000 UTC
|
||||
}
|
||||
|
||||
func ExampleEndOfYear() {
|
||||
input := time.Date(2023, 1, 8, 18, 50, 10, 100, time.UTC)
|
||||
|
||||
result := EndOfYear(input)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 2023-12-31 23:59:59.999999999 +0000 UTC
|
||||
}
|
||||
|
||||
func ExampleNewUnix() {
|
||||
result := NewUnix(1647597438)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// &{1647597438}
|
||||
}
|
||||
|
||||
func ExampleNewUnixNow() {
|
||||
tm1 := NewUnixNow()
|
||||
|
||||
unixTimestamp := tm1.ToUnix()
|
||||
|
||||
tm2 := NewUnix(unixTimestamp)
|
||||
|
||||
fmt.Println(reflect.DeepEqual(tm1, tm2))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
// func ExampleNewFormat() {
|
||||
// tm, err := NewFormat("2022-03-18 17:04:05")
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
|
||||
// result := tm.ToFormat()
|
||||
|
||||
// fmt.Println(result)
|
||||
|
||||
// // Output:
|
||||
// // 2022-03-18 17:04:05
|
||||
// }
|
||||
|
||||
// func ExampleNewISO8601() {
|
||||
// tm, err := NewISO8601("2006-01-02T15:04:05.999Z")
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
|
||||
// result := tm.ToIso8601()
|
||||
|
||||
// fmt.Println(result)
|
||||
|
||||
// // Output:
|
||||
// // 2006-01-02T23:04:05+08:00
|
||||
// }
|
||||
|
||||
func ExampleIsLeapYear() {
|
||||
result1 := IsLeapYear(2000)
|
||||
result2 := IsLeapYear(2001)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleBetweenSeconds() {
|
||||
today := time.Now()
|
||||
tomorrow := AddDay(today, 1)
|
||||
yesterday := AddDay(today, -1)
|
||||
|
||||
result1 := BetweenSeconds(today, tomorrow)
|
||||
result2 := BetweenSeconds(today, yesterday)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
|
||||
// Output:
|
||||
// 86400
|
||||
// -86400
|
||||
}
|
||||
|
||||
func ExampleDayOfYear() {
|
||||
date1 := time.Date(2023, 02, 01, 1, 1, 1, 0, time.Local)
|
||||
result1 := DayOfYear(date1)
|
||||
|
||||
date2 := time.Date(2023, 01, 02, 1, 1, 1, 0, time.Local)
|
||||
result2 := DayOfYear(date2)
|
||||
|
||||
date3 := time.Date(2023, 01, 01, 1, 1, 1, 0, time.Local)
|
||||
result3 := DayOfYear(date3)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 31
|
||||
// 1
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleIsWeekend() {
|
||||
date1 := time.Date(2023, 06, 03, 0, 0, 0, 0, time.Local)
|
||||
date2 := time.Date(2023, 06, 04, 0, 0, 0, 0, time.Local)
|
||||
date3 := time.Date(2023, 06, 02, 0, 0, 0, 0, time.Local)
|
||||
|
||||
result1 := IsWeekend(date1)
|
||||
result2 := IsWeekend(date2)
|
||||
result3 := IsWeekend(date3)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
@@ -4,10 +4,43 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/internal"
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestAddYear(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestAddDay")
|
||||
|
||||
now := time.Now()
|
||||
after2Years := AddYear(now, 1)
|
||||
diff1 := after2Years.Sub(now)
|
||||
assert.Equal(float64(8760), diff1.Hours())
|
||||
|
||||
before2Years := AddYear(now, -1)
|
||||
diff2 := before2Years.Sub(now)
|
||||
assert.Equal(float64(-8760), diff2.Hours())
|
||||
}
|
||||
|
||||
func TestBetweenSeconds(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestBetweenSeconds")
|
||||
|
||||
today := time.Now()
|
||||
tomorrow := AddDay(today, 1)
|
||||
yesterday := AddDay(today, -1)
|
||||
|
||||
result1 := BetweenSeconds(today, tomorrow)
|
||||
result2 := BetweenSeconds(today, yesterday)
|
||||
|
||||
assert.Equal(int64(86400), result1)
|
||||
assert.Equal(int64(-86400), result2)
|
||||
}
|
||||
|
||||
func TestAddDay(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestAddDay")
|
||||
|
||||
now := time.Now()
|
||||
@@ -21,6 +54,8 @@ func TestAddDay(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAddHour(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestAddHour")
|
||||
|
||||
now := time.Now()
|
||||
@@ -34,6 +69,8 @@ func TestAddHour(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAddMinute(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestAddMinute")
|
||||
|
||||
now := time.Now()
|
||||
@@ -47,45 +84,77 @@ func TestAddMinute(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetNowDate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestGetNowDate")
|
||||
expected := time.Now().Format("2006-01-02")
|
||||
assert.Equal(expected, GetNowDate())
|
||||
}
|
||||
|
||||
func TestGetNotTime(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestGetNotTime")
|
||||
func TestGetNowTime(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestGetNowTime")
|
||||
expected := time.Now().Format("15:04:05")
|
||||
assert.Equal(expected, GetNowTime())
|
||||
}
|
||||
|
||||
func TestGetNowDateTime(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestGetNowDateTime")
|
||||
expected := time.Now().Format("2006-01-02 15:04:05")
|
||||
assert.Equal(expected, GetNowDateTime())
|
||||
}
|
||||
|
||||
func TestGetTodayStartTime(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestGetTodayStartTime")
|
||||
expected := time.Now().Format("2006-01-02") + " 00:00:00"
|
||||
assert.Equal(expected, GetTodayStartTime())
|
||||
}
|
||||
|
||||
func TestGetTodayEndTime(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestGetTodayEndTime")
|
||||
expected := time.Now().Format("2006-01-02") + " 23:59:59"
|
||||
assert.Equal(expected, GetTodayEndTime())
|
||||
}
|
||||
|
||||
func TestFormatTimeToStr(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestFormatTimeToStr")
|
||||
|
||||
datetime, _ := time.Parse("2006-01-02 15:04:05", "2021-01-02 16:04:08")
|
||||
cases := []string{
|
||||
"yyyy-mm-dd hh:mm:ss", "yyyy-mm-dd",
|
||||
"dd-mm-yy hh:mm:ss", "yyyy/mm/dd hh:mm:ss",
|
||||
"hh:mm:ss", "yyyy/mm"}
|
||||
"hh:mm:ss", "yyyy/mm",
|
||||
"yyyy-mm-dd hh",
|
||||
}
|
||||
|
||||
expected := []string{
|
||||
"2021-01-02 16:04:08", "2021-01-02",
|
||||
"02-01-21 16:04:08", "2021/01/02 16:04:08",
|
||||
"16:04:08", "2021/01"}
|
||||
"16:04:08", "2021/01",
|
||||
"2021-01-02 16",
|
||||
}
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
actual := FormatTimeToStr(datetime, cases[i])
|
||||
assert.Equal(expected[i], actual)
|
||||
|
||||
}
|
||||
|
||||
ds := FormatTimeToStr(datetime, "yyyy-mm-dd hh:mm:ss", "EST")
|
||||
t.Log(ds)
|
||||
}
|
||||
|
||||
func TestFormatStrToTime(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestFormatStrToTime")
|
||||
|
||||
formats := []string{
|
||||
@@ -97,22 +166,28 @@ func TestFormatStrToTime(t *testing.T) {
|
||||
"dd-mm-yy hh:mm:ss", "yyyy/mm/dd hh:mm:ss",
|
||||
"yyyy/mm"}
|
||||
|
||||
datetimeStr := []string{
|
||||
expected := []string{
|
||||
"2021-01-02 16:04:08", "2021-01-02",
|
||||
"02-01-21 16:04:08", "2021/01/02 16:04:08",
|
||||
"2021/01"}
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
actual, err := FormatStrToTime(datetimeStr[i], cases[i])
|
||||
actual, err := FormatStrToTime(expected[i], cases[i])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected, _ := time.Parse(formats[i], datetimeStr[i])
|
||||
expected, _ := time.Parse(formats[i], expected[i])
|
||||
assert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
estTime, err := FormatStrToTime("2021-01-02 16:04:08", "yyyy-mm-dd hh:mm:ss", "EST")
|
||||
t.Log(estTime)
|
||||
assert.IsNil(err)
|
||||
}
|
||||
|
||||
func TestBeginOfMinute(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestBeginOfMinute")
|
||||
|
||||
expected := time.Date(2022, 2, 15, 15, 48, 0, 0, time.Local)
|
||||
@@ -123,6 +198,8 @@ func TestBeginOfMinute(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEndOfMinute(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestEndOfMinute")
|
||||
|
||||
expected := time.Date(2022, 2, 15, 15, 48, 59, 999999999, time.Local)
|
||||
@@ -133,6 +210,8 @@ func TestEndOfMinute(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBeginOfHour(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestBeginOfHour")
|
||||
|
||||
expected := time.Date(2022, 2, 15, 15, 0, 0, 0, time.Local)
|
||||
@@ -143,6 +222,8 @@ func TestBeginOfHour(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEndOfHour(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestEndOfHour")
|
||||
|
||||
expected := time.Date(2022, 2, 15, 15, 59, 59, 999999999, time.Local)
|
||||
@@ -153,6 +234,8 @@ func TestEndOfHour(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBeginOfDay(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestBeginOfDay")
|
||||
|
||||
expected := time.Date(2022, 2, 15, 0, 0, 0, 0, time.Local)
|
||||
@@ -163,6 +246,8 @@ func TestBeginOfDay(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEndOfDay(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestEndOfDay")
|
||||
|
||||
expected := time.Date(2022, 2, 15, 23, 59, 59, 999999999, time.Local)
|
||||
@@ -173,6 +258,8 @@ func TestEndOfDay(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBeginOfWeek(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestBeginOfWeek")
|
||||
|
||||
expected := time.Date(2022, 2, 13, 0, 0, 0, 0, time.Local)
|
||||
@@ -183,6 +270,8 @@ func TestBeginOfWeek(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEndOfWeek(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestEndOfWeek")
|
||||
|
||||
expected := time.Date(2022, 2, 19, 23, 59, 59, 999999999, time.Local)
|
||||
@@ -193,6 +282,8 @@ func TestEndOfWeek(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBeginOfMonth(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestBeginOfMonth")
|
||||
|
||||
expected := time.Date(2022, 2, 1, 0, 0, 0, 0, time.Local)
|
||||
@@ -203,6 +294,8 @@ func TestBeginOfMonth(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEndOfMonth(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestEndOfMonth")
|
||||
|
||||
expected := time.Date(2022, 2, 28, 23, 59, 59, 999999999, time.Local)
|
||||
@@ -213,6 +306,8 @@ func TestEndOfMonth(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBeginOfYear(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestBeginOfYear")
|
||||
|
||||
expected := time.Date(2022, 1, 1, 0, 0, 0, 0, time.Local)
|
||||
@@ -223,6 +318,8 @@ func TestBeginOfYear(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEndOfYear(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestEndOfYear")
|
||||
|
||||
expected := time.Date(2022, 12, 31, 23, 59, 59, 999999999, time.Local)
|
||||
@@ -231,3 +328,85 @@ func TestEndOfYear(t *testing.T) {
|
||||
|
||||
assert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
func TestIsLeapYear(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestEndOfYear")
|
||||
|
||||
result1 := IsLeapYear(2000)
|
||||
result2 := IsLeapYear(2001)
|
||||
|
||||
assert.Equal(true, result1)
|
||||
assert.Equal(false, result2)
|
||||
}
|
||||
|
||||
func TestDayOfYear(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestDayOfYear")
|
||||
date1 := time.Date(2023, 02, 01, 1, 1, 1, 0, time.Local)
|
||||
result1 := DayOfYear(date1)
|
||||
assert.Equal(31, result1)
|
||||
|
||||
date2 := time.Date(2023, 01, 02, 1, 1, 1, 0, time.Local)
|
||||
result2 := DayOfYear(date2)
|
||||
assert.Equal(1, result2)
|
||||
|
||||
date3 := time.Date(2023, 01, 01, 1, 1, 1, 0, time.Local)
|
||||
result3 := DayOfYear(date3)
|
||||
assert.Equal(0, result3)
|
||||
}
|
||||
|
||||
func TestIsWeekend(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestIsWeekend")
|
||||
|
||||
date := time.Date(2023, 06, 03, 0, 0, 0, 0, time.Local)
|
||||
result := IsWeekend(date)
|
||||
assert.Equal(true, result)
|
||||
|
||||
date1 := time.Date(2023, 06, 04, 0, 0, 0, 0, time.Local)
|
||||
result1 := IsWeekend(date1)
|
||||
assert.Equal(true, result1)
|
||||
|
||||
date2 := time.Date(2023, 06, 02, 0, 0, 0, 0, time.Local)
|
||||
result2 := IsWeekend(date2)
|
||||
assert.Equal(false, result2)
|
||||
}
|
||||
|
||||
func TestNowDateOrTime(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
formats := []string{
|
||||
"yyyy-mm-dd hh:mm:ss",
|
||||
"yyyy-mm-dd",
|
||||
"dd-mm-yy hh:mm:ss",
|
||||
"yyyy/mm/dd hh:mm:ss",
|
||||
"hh:mm:ss",
|
||||
"yyyy/mm",
|
||||
"yyyy-mm-dd hh",
|
||||
}
|
||||
|
||||
for i := 0; i < len(formats); i++ {
|
||||
result := NowDateOrTime(formats[i], "UTC")
|
||||
t.Log(result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimestamp(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ts1 := Timestamp()
|
||||
t.Log(ts1)
|
||||
|
||||
ts2 := TimestampMilli()
|
||||
t.Log(ts2)
|
||||
|
||||
ts3 := TimestampMicro()
|
||||
t.Log(ts3)
|
||||
|
||||
ts4 := TimestampNano()
|
||||
t.Log(ts4)
|
||||
}
|
||||
|
||||
89
docs/.vitepress/common.ts
Normal file
89
docs/.vitepress/common.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { defineConfig, HeadConfig } from 'vitepress'
|
||||
|
||||
export const META_IMAGE = '/lancet_logo.png'
|
||||
export const isProduction = process.env.NETLIFY && process.env.CONTEXT === 'production'
|
||||
|
||||
if (process.env.NETLIFY) {
|
||||
console.log('Netlify build', process.env.CONTEXT)
|
||||
}
|
||||
|
||||
const productionHead: HeadConfig[] = [
|
||||
[
|
||||
'script',
|
||||
{
|
||||
src: 'https://unpkg.com/thesemetrics@latest',
|
||||
async: '',
|
||||
type: 'text/javascript',
|
||||
},
|
||||
],
|
||||
]
|
||||
|
||||
const rControl = /[\u0000-\u001f]/g
|
||||
const rSpecial = /[\s~`!@#$%^&*()\-_+=[\]{}|\\;:"'“”‘’<>,.?/]+/g
|
||||
const rCombining = /[\u0300-\u036F]/g
|
||||
|
||||
/**
|
||||
* Default slugification function
|
||||
*/
|
||||
export const slugify = (str: string): string =>
|
||||
str
|
||||
.normalize('NFKD')
|
||||
// Remove accents
|
||||
.replace(rCombining, '')
|
||||
// Remove control characters
|
||||
.replace(rControl, '')
|
||||
// Replace special characters
|
||||
.replace(rSpecial, '-')
|
||||
// ensure it doesn't start with a number
|
||||
.replace(/^(\d)/, '_$1')
|
||||
|
||||
export const commonConfig = defineConfig({
|
||||
title: 'Lancet',
|
||||
appearance: true,
|
||||
|
||||
markdown: {
|
||||
theme: {
|
||||
dark: 'dracula-soft',
|
||||
light: 'vitesse-light',
|
||||
},
|
||||
|
||||
attrs: {
|
||||
leftDelimiter: '%{',
|
||||
rightDelimiter: '}%',
|
||||
},
|
||||
|
||||
anchor: {
|
||||
slugify,
|
||||
},
|
||||
},
|
||||
|
||||
head: [
|
||||
// ['link', { rel: 'icon', type: 'image/svg+xml', href: '/logo.svg' }],
|
||||
['link', { rel: 'icon', type: 'image/png', href: '/lancet_logo_mini.png' }],
|
||||
['meta', { name: 'theme-color', content: '#5f67ee' }],
|
||||
['meta', { name: 'og:type', content: 'website' }],
|
||||
['meta', { name: 'og:locale', content: 'zh' }],
|
||||
|
||||
...(isProduction ? productionHead : []),
|
||||
],
|
||||
|
||||
themeConfig: {
|
||||
logo: { src: '/lancet_logo_mini.png', width: 24, height: 24 },
|
||||
outline: [2, 3],
|
||||
|
||||
search: {
|
||||
provider: 'local',
|
||||
},
|
||||
socialLinks: [
|
||||
{
|
||||
icon: 'github',
|
||||
link: 'https://github.com/duke-git/lancet',
|
||||
},
|
||||
],
|
||||
|
||||
footer: {
|
||||
copyright: 'Copyright © 2023-present Duke Du',
|
||||
message: '备案号: 京ICP备2023022770号',
|
||||
},
|
||||
},
|
||||
})
|
||||
14
docs/.vitepress/config.mts
Normal file
14
docs/.vitepress/config.mts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { defineConfig } from 'vitepress'
|
||||
import { commonConfig } from './common'
|
||||
import { zhConfig } from './zh'
|
||||
import { enConfig } from './en'
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({
|
||||
...commonConfig,
|
||||
|
||||
locales: {
|
||||
root: { label: '简体中文', lang: 'zh-CN', link: '/', ...zhConfig },
|
||||
en: { label: 'English', lang: 'en-US', link: '/en/', ...enConfig },
|
||||
},
|
||||
})
|
||||
139
docs/.vitepress/en.ts
Normal file
139
docs/.vitepress/en.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
import type { DefaultTheme, LocaleSpecificConfig } from 'vitepress'
|
||||
|
||||
export const META_URL = 'https://www.golancet.cn/en/'
|
||||
export const META_TITLE = 'Lancet'
|
||||
export const META_DESCRIPTION = 'A powerful util function library of Go'
|
||||
|
||||
export const enConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
|
||||
description: META_DESCRIPTION,
|
||||
|
||||
head: [
|
||||
['meta', { property: 'og:url', content: META_URL }],
|
||||
['meta', { property: 'og:description', content: META_DESCRIPTION }],
|
||||
],
|
||||
|
||||
themeConfig: {
|
||||
editLink: {
|
||||
pattern: 'https://github.com/duke-git/lancet/edit/v2/docs/:path',
|
||||
text: 'Suggest changes to this page',
|
||||
},
|
||||
nav: [
|
||||
{
|
||||
text: 'Home',
|
||||
link: '/en/',
|
||||
activeMatch: '^/en/',
|
||||
},
|
||||
{
|
||||
text: 'Guide',
|
||||
link: '/en/guide/introduction',
|
||||
activeMatch: '^/en/guide/',
|
||||
},
|
||||
{ text: 'API', link: '/en/api/overview', activeMatch: '^/en/api/' },
|
||||
{
|
||||
text: 'Links',
|
||||
items: [
|
||||
{
|
||||
text: 'Discussion',
|
||||
link: 'https://github.com/duke-git/lancet/discussions',
|
||||
},
|
||||
{
|
||||
text: 'Changelog',
|
||||
link: 'https://github.com/duke-git/lancet/releases',
|
||||
},
|
||||
{
|
||||
text: 'Contribution',
|
||||
link: 'https://github.com/duke-git/lancet/blob/main/CONTRIBUTING.en-US.md',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
sidebar: {
|
||||
'/en/guide/': [
|
||||
{
|
||||
text: 'Introduction',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{
|
||||
text: 'What is Lancet?',
|
||||
link: '/en/guide/introduction',
|
||||
},
|
||||
{
|
||||
text: 'Getting started',
|
||||
link: '/en/guide/getting_started',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Contribute Code',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{
|
||||
text: 'Contribution guide',
|
||||
link: '/en/guide/contribution_guide',
|
||||
},
|
||||
{
|
||||
text: 'Contributors',
|
||||
link: '/en/guide/contributors',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'API Reference',
|
||||
link: '/en/api/overview'
|
||||
},
|
||||
],
|
||||
'/en/api/': [
|
||||
{
|
||||
text: 'Overview',
|
||||
items: [{ text: 'API overview', link: '/en/api/overview' }],
|
||||
},
|
||||
{
|
||||
text: 'Packages',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: 'algorithm', link: '/en/api/packages/algorithm' },
|
||||
{ text: 'compare', link: '/en/api/packages/compare' },
|
||||
{ text: 'concurrency', link: '/en/api/packages/concurrency' },
|
||||
{ text: 'condition', link: '/en/api/packages/condition' },
|
||||
{ text: 'convertor', link: '/en/api/packages/convertor' },
|
||||
{ text: 'cryptor', link: '/en/api/packages/cryptor' },
|
||||
{
|
||||
text: 'datastructure',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'list', link: '/en/api/packages/datastructure/list' },
|
||||
{ text: 'safelist', link: '/en/api/packages/datastructure/copyonwritelist' },
|
||||
{ text: 'link', link: '/en/api/packages/datastructure/link' },
|
||||
{ text: 'stack', link: '/en/api/packages/datastructure/stack' },
|
||||
{ text: 'queue', link: '/en/api/packages/datastructure/queue' },
|
||||
{ text: 'heap', link: '/en/api/packages/datastructure/heap' },
|
||||
{ text: 'tree', link: '/en/api/packages/datastructure/tree' },
|
||||
{ text: 'set', link: '/en/api/packages/datastructure/set' },
|
||||
{ text: 'hashmap', link: '/en/api/packages/datastructure/hashmap' },
|
||||
],
|
||||
},
|
||||
{ text: 'datetime', link: '/en/api/packages/datetime' },
|
||||
{ text: 'fileutil', link: '/en/api/packages/fileutil' },
|
||||
{ text: 'formatter', link: '/en/api/packages/formatter' },
|
||||
{ text: 'function', link: '/en/api/packages/function' },
|
||||
{ text: 'mathutil', link: '/en/api/packages/mathutil' },
|
||||
{ text: 'maputil', link: '/en/api/packages/maputil' },
|
||||
{ text: 'netutil', link: '/en/api/packages/netutil' },
|
||||
{ text: 'pointer', link: '/en/api/packages/pointer' },
|
||||
{ text: 'random', link: '/en/api/packages/random' },
|
||||
{ text: 'retry', link: '/en/api/packages/retry' },
|
||||
{ text: 'slice', link: '/en/api/packages/slice' },
|
||||
{ text: 'stream', link: '/en/api/packages/stream' },
|
||||
{ text: 'struct', link: '/en/api/packages/struct' },
|
||||
{ text: 'strutil', link: '/en/api/packages/strutil' },
|
||||
{ text: 'system', link: '/en/api/packages/system' },
|
||||
{ text: 'tuple', link: '/en/api/packages/tuple' },
|
||||
{ text: 'validator', link: '/en/api/packages/validator' },
|
||||
{ text: 'xerror', link: '/en/api/packages/xerror' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
152
docs/.vitepress/zh.ts
Normal file
152
docs/.vitepress/zh.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
import type { DefaultTheme, LocaleSpecificConfig } from 'vitepress'
|
||||
|
||||
export const META_URL = 'https://www.golancet.cn'
|
||||
export const META_TITLE = 'Lancet'
|
||||
export const META_DESCRIPTION = '一个强大的Go语言工具函数库'
|
||||
|
||||
export const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
|
||||
description: META_DESCRIPTION,
|
||||
|
||||
head: [
|
||||
['meta', { property: 'og:url', content: META_URL }],
|
||||
['meta', { property: 'og:description', content: META_DESCRIPTION }],
|
||||
],
|
||||
|
||||
themeConfig: {
|
||||
editLink: {
|
||||
pattern: 'https://github.com/duke-git/lancet/edit/v2/docs/:path',
|
||||
text: '对本页提出修改建议',
|
||||
},
|
||||
outline: {
|
||||
label: '本页内容',
|
||||
},
|
||||
|
||||
docFooter: {
|
||||
prev: '上一页',
|
||||
next: '下一页',
|
||||
},
|
||||
|
||||
nav: [
|
||||
{
|
||||
text: '首页',
|
||||
link: '/',
|
||||
activeMatch: '^/',
|
||||
},
|
||||
{
|
||||
text: '指南',
|
||||
link: '/guide/introduction',
|
||||
activeMatch: '^/guide/',
|
||||
},
|
||||
{ text: 'API', link: '/api/overview', activeMatch: '^/api/' },
|
||||
{
|
||||
text: '相关链接',
|
||||
items: [
|
||||
{
|
||||
text: '论坛',
|
||||
link: 'https://github.com/duke-git/lancet/discussions',
|
||||
},
|
||||
{
|
||||
text: '更新日志',
|
||||
link: 'https://github.com/duke-git/lancet/releases',
|
||||
},
|
||||
{
|
||||
text: '参与贡献',
|
||||
link: 'https://github.com/duke-git/lancet/blob/main/CONTRIBUTING.zh-CN.md',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
sidebar: {
|
||||
'/guide/': [
|
||||
{
|
||||
text: '介绍',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{
|
||||
text: 'Lancet是什么?',
|
||||
link: '/guide/introduction',
|
||||
},
|
||||
{
|
||||
text: '开始',
|
||||
link: '/guide/getting_started',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '贡献代码',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{
|
||||
text: '贡献指南',
|
||||
link: '/guide/contribution_guide',
|
||||
},
|
||||
{
|
||||
text: '贡献者',
|
||||
link: '/guide/contributors',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'API手册',
|
||||
link: '/api/overview'
|
||||
},
|
||||
],
|
||||
|
||||
'/api/': [
|
||||
{
|
||||
text: '概览',
|
||||
items: [{ text: 'API概述', link: '/api/overview' }],
|
||||
},
|
||||
{
|
||||
text: 'API文档',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: '算法', link: '/api/packages/algorithm' },
|
||||
{ text: '比较器', link: '/api/packages/compare' },
|
||||
{ text: '并发处理', link: '/api/packages/concurrency' },
|
||||
{ text: '条件判断', link: '/api/packages/condition' },
|
||||
{ text: '类型转换', link: '/api/packages/convertor' },
|
||||
{ text: '加密&解密', link: '/api/packages/cryptor' },
|
||||
{
|
||||
text: '数据结构',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: '线性表', link: '/api/packages/datastructure/list' },
|
||||
{
|
||||
text: '线性表(线程安全)',
|
||||
link: '/api/packages/datastructure/copyonwritelist',
|
||||
},
|
||||
{ text: '链表', link: '/api/packages/datastructure/link' },
|
||||
{ text: '栈', link: '/api/packages/datastructure/stack' },
|
||||
{ text: '队列', link: '/api/packages/datastructure/queue' },
|
||||
{ text: '堆', link: '/api/packages/datastructure/heap' },
|
||||
{ text: '树', link: '/api/packages/datastructure/tree' },
|
||||
{ text: '集合', link: '/api/packages/datastructure/set' },
|
||||
{ text: 'HashMap', link: '/api/packages/datastructure/hashmap' },
|
||||
],
|
||||
},
|
||||
{ text: '日期&时间', link: '/api/packages/datetime' },
|
||||
{ text: '文件', link: '/api/packages/fileutil' },
|
||||
{ text: '格式化工具', link: '/api/packages/formatter' },
|
||||
{ text: '函数', link: '/api/packages/function' },
|
||||
{ text: '数学工具', link: '/api/packages/mathutil' },
|
||||
{ text: 'Map', link: '/api/packages/maputil' },
|
||||
{ text: '网络', link: '/api/packages/netutil' },
|
||||
{ text: '指针', link: '/api/packages/pointer' },
|
||||
{ text: '随机数', link: '/api/packages/random' },
|
||||
{ text: '重试', link: '/api/packages/retry' },
|
||||
{ text: '切片', link: '/api/packages/slice' },
|
||||
{ text: '流', link: '/api/packages/stream' },
|
||||
{ text: '结构体', link: '/api/packages/struct' },
|
||||
{ text: '字符串', link: '/api/packages/strutil' },
|
||||
{ text: '系统', link: '/api/packages/system' },
|
||||
{ text: '元组', link: '/api/packages/tuple' },
|
||||
{ text: '验证器', link: '/api/packages/validator' },
|
||||
{ text: '错误处理', link: '/api/packages/xerror' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
69
docs/api/overview.md
Normal file
69
docs/api/overview.md
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# API概述
|
||||
|
||||
<b>lancet(柳叶刀)是一个功能强大、全面、高效、可复用的go语言工具函数库。包含25个包,超过600个工具函数。功能涵盖字符串处理、切片处理、网络、并发、加解密、文件处理、时间/日期、流处理、迭代器等等。</b>
|
||||
|
||||
|
||||
<style>
|
||||
.package-title {
|
||||
color: black;
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
.package-container {
|
||||
font-size: 16px;
|
||||
border: 1px dashed;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.package-cell {
|
||||
height: 40px;
|
||||
width: 140px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
line-height: 40px;
|
||||
background: #10b981;
|
||||
border: 1px solid;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 6px;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div>
|
||||
<p class="package-title">lancet功能模块</p>
|
||||
<div class="package-container">
|
||||
<div class="package-cell">algorithm</div>
|
||||
<div class="package-cell">compare</div>
|
||||
<div class="package-cell">concurrency</div>
|
||||
<div class="package-cell">condition</div>
|
||||
<div class="package-cell">convertor</div>
|
||||
<div class="package-cell">cryptor</div>
|
||||
<div class="package-cell">datastructure</div>
|
||||
<div class="package-cell">datetime</div>
|
||||
<div class="package-cell">fileutil</div>
|
||||
<div class="package-cell">formatter</div>
|
||||
<div class="package-cell">function</div>
|
||||
<div class="package-cell">iterator</div>
|
||||
<div class="package-cell">maputil</div>
|
||||
<div class="package-cell">mathutil</div>
|
||||
<div class="package-cell">netutil</div>
|
||||
<div class="package-cell">pointer</div>
|
||||
<div class="package-cell">random</div>
|
||||
<div class="package-cell">retry</div>
|
||||
<div class="package-cell">slice</div>
|
||||
<div class="package-cell">stream</div>
|
||||
<div class="package-cell">structs</div>
|
||||
<div class="package-cell">strutil</div>
|
||||
<div class="package-cell">system</div>
|
||||
<div class="package-cell">tuple</div>
|
||||
<div class="package-cell">validator</div>
|
||||
<div class="package-cell">xerror</div>
|
||||
</div>
|
||||
</div>
|
||||
636
docs/api/packages/algorithm.md
Normal file
636
docs/api/packages/algorithm.md
Normal file
@@ -0,0 +1,636 @@
|
||||
# Algorithm
|
||||
|
||||
algorithm 算法包实现一些基本算法,sort,search,lrucache。
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/algorithm/sort.go](https://github.com/duke-git/lancet/blob/main/algorithm/sort.go)
|
||||
- [https://github.com/duke-git/lancet/blob/main/algorithm/search.go](https://github.com/duke-git/lancet/blob/main/algorithm/search.go)
|
||||
- [https://github.com/duke-git/lancet/blob/main/algorithm/lru_cache.go](https://github.com/duke-git/lancet/blob/main/algorithm/lru_cache.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 用法
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/algorithm"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 目录
|
||||
|
||||
- [BubbleSort](#BubbleSort)
|
||||
- [InsertionSort](#InsertionSort)
|
||||
- [SelectionSort](#SelectionSort)
|
||||
- [ShellSort](#ShellSort)
|
||||
- [QuickSort](#QuickSort)
|
||||
- [HeapSort](#HeapSort)
|
||||
- [MergeSort](#MergeSort)
|
||||
- [CountSort](#CountSort)
|
||||
- [BinarySearch](#BinarySearch)
|
||||
- [BinaryIterativeSearch](#BinaryIterativeSearch)
|
||||
- [LinearSearch](#LinearSearch)
|
||||
- [LRUCache](#LRUCache)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 文档
|
||||
|
||||
### <span id="BubbleSort">BubbleSort</span>
|
||||
|
||||
<p>冒泡排序,参数comparator需要实现包constraints.Comparator。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func BubbleSort[T any](slice []T, comparator constraints.Comparator)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/GNdv7Jg2Taj)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/algorithm"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1 any, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
//ascending order
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
numbers := []int{2, 1, 5, 3, 6, 4}
|
||||
comparator := &intComparator{}
|
||||
|
||||
algorithm.BubbleSort(numbers, comparator)
|
||||
|
||||
fmt.Println(numbers)
|
||||
|
||||
// Output:
|
||||
// [1 2 3 4 5 6]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="InsertionSort">InsertionSort</span>
|
||||
|
||||
<p>插入排序,参数comparator需要实现包constraints.Comparator。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func InsertionSort[T any](slice []T, comparator constraints.Comparator)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/G5LJiWgJJW6)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/algorithm"
|
||||
)
|
||||
|
||||
type people struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
// PeopleAageComparator sort people slice by age field
|
||||
type peopleAgeComparator struct{}
|
||||
|
||||
// Compare implements github.com/duke-git/lancet/constraints/constraints.go/Comparator
|
||||
func (pc *peopleAgeComparator) Compare(v1 any, v2 any) int {
|
||||
p1, _ := v1.(people)
|
||||
p2, _ := v2.(people)
|
||||
|
||||
//ascending order
|
||||
if p1.Age < p2.Age {
|
||||
return -1
|
||||
} else if p1.Age > p2.Age {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
peoples := []people{
|
||||
{Name: "a", Age: 20},
|
||||
{Name: "b", Age: 10},
|
||||
{Name: "c", Age: 17},
|
||||
{Name: "d", Age: 8},
|
||||
{Name: "e", Age: 28},
|
||||
}
|
||||
|
||||
comparator := &peopleAgeComparator{}
|
||||
|
||||
algorithm.InsertionSort(peoples, comparator)
|
||||
|
||||
fmt.Println(peoples)
|
||||
|
||||
// Output:
|
||||
// [{d 8} {b 10} {c 17} {a 20} {e 28}]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="SelectionSort">SelectionSort</span>
|
||||
|
||||
<p>选择排序,参数comparator需要实现包constraints.Comparator。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func SelectionSort[T any](slice []T, comparator constraints.Comparator)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/oXovbkekayS)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/algorithm"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1 any, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
//ascending order
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
numbers := []int{2, 1, 5, 3, 6, 4}
|
||||
comparator := &intComparator{}
|
||||
|
||||
algorithm.SelectionSort(numbers, comparator)
|
||||
|
||||
fmt.Println(numbers)
|
||||
|
||||
// Output:
|
||||
// [1 2 3 4 5 6]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ShellSort">ShellSort</span>
|
||||
|
||||
<p>希尔排序,参数comparator需要实现包constraints.Comparator。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ShellSort[T any](slice []T, comparator constraints.Comparator)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/3ibkszpJEu3)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/algorithm"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1 any, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
//ascending order
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
numbers := []int{2, 1, 5, 3, 6, 4}
|
||||
comparator := &intComparator{}
|
||||
|
||||
algorithm.ShellSort(numbers, comparator)
|
||||
|
||||
fmt.Println(numbers)
|
||||
|
||||
// Output:
|
||||
// [1 2 3 4 5 6]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="QuickSort">QuickSort</span>
|
||||
|
||||
<p>快速排序,参数comparator需要实现包constraints.Comparator。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func QuickSort[T any](slice []T comparator constraints.Comparator)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/7Y7c1Elk3ax)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/algorithm"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1 any, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
//ascending order
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
numbers := []int{2, 1, 5, 3, 6, 4}
|
||||
comparator := &intComparator{}
|
||||
|
||||
algorithm.QuickSort(numbers, comparator)
|
||||
|
||||
fmt.Println(numbers)
|
||||
|
||||
// Output:
|
||||
// [1 2 3 4 5 6]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="HeapSort">HeapSort</span>
|
||||
|
||||
<p>堆排序,参数comparator需要实现包constraints.Comparator。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func HeapSort[T any](slice []T, comparator constraints.Comparator)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/u6Iwa1VZS_f)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/algorithm"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1 any, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
//ascending order
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
numbers := []int{2, 1, 5, 3, 6, 4}
|
||||
comparator := &intComparator{}
|
||||
|
||||
algorithm.HeapSort(numbers, comparator)
|
||||
|
||||
fmt.Println(numbers)
|
||||
|
||||
// Output:
|
||||
// [1 2 3 4 5 6]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="MergeSort">MergeSort</span>
|
||||
|
||||
<p>归并排序,参数comparator需要实现包constraints.Comparator。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func MergeSort[T any](slice []T, comparator constraints.Comparator)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ydinn9YzUJn)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/algorithm"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1 any, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
//ascending order
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
numbers := []int{2, 1, 5, 3, 6, 4}
|
||||
comparator := &intComparator{}
|
||||
|
||||
algorithm.MergeSort(numbers, comparator)
|
||||
|
||||
fmt.Println(numbers)
|
||||
|
||||
// Output:
|
||||
// [1 2 3 4 5 6]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CountSort">CountSort</span>
|
||||
|
||||
<p>计数排序,参数comparator需要实现包constraints.Comparator。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func CountSort[T any](slice []T, comparator constraints.Comparator) []T
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/tB-Umgm0DrP)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/algorithm"
|
||||
)
|
||||
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1 any, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
//ascending order
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
numbers := []int{2, 1, 5, 3, 6, 4}
|
||||
comparator := &intComparator{}
|
||||
|
||||
sortedNums := algorithm.CountSort(numbers, comparator)
|
||||
|
||||
fmt.Println(sortedNums)
|
||||
|
||||
// Output:
|
||||
// [1 2 3 4 5 6]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="BinarySearch">BinarySearch</span>
|
||||
|
||||
<p>二分递归查找,返回元素索引,未找到元素返回-1,参数comparator需要实现包constraints.Comparator。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func BinarySearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator constraints.Comparator) int
|
||||
```
|
||||
|
||||
<b>示例: <span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/t6MeGiUSN47)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/algorithm"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1 any, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
//ascending order
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8}
|
||||
comparator := &intComparator{}
|
||||
|
||||
result1 := algorithm.BinarySearch(numbers, 5, 0, len(numbers)-1, comparator)
|
||||
result2 := algorithm.BinarySearch(numbers, 9, 0, len(numbers)-1, comparator)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
|
||||
// Output:
|
||||
// 4
|
||||
// -1
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="BinaryIterativeSearch">BinaryIterativeSearch</span>
|
||||
|
||||
<p>二分迭代查找,返回元素索引,未找到元素返回-1,参数comparator需要实现包constraints.Comparator。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func BinaryIterativeSearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator constraints.Comparator) int
|
||||
```
|
||||
|
||||
<b>示例: <span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Anozfr8ZLH3)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/algorithm"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1 any, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
//ascending order
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8}
|
||||
comparator := &intComparator{}
|
||||
|
||||
result1 := algorithm.BinaryIterativeSearch(numbers, 5, 0, len(numbers)-1, comparator)
|
||||
result2 := algorithm.BinaryIterativeSearch(numbers, 9, 0, len(numbers)-1, comparator)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
|
||||
// Output:
|
||||
// 4
|
||||
// -1
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="LinearSearch">LinearSearch</span>
|
||||
|
||||
<p>基于传入的相等函数线性查找元素,返回元素索引,未找到元素返回-1。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func LinearSearch[T any](slice []T, target T, equal func(a, b T) bool) int
|
||||
```
|
||||
|
||||
<b>示例: <span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/IsS7rgn5s3x)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/algorithm"
|
||||
)
|
||||
|
||||
func main() {
|
||||
numbers := []int{3, 4, 5, 3, 2, 1}
|
||||
|
||||
equalFunc := func(a, b int) bool {
|
||||
return a == b
|
||||
}
|
||||
|
||||
result1 := algorithm.LinearSearch(numbers, 3, equalFunc)
|
||||
result2 := algorithm.LinearSearch(numbers, 6, equalFunc)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
// -1
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="LRUCache">LRUCache</span>
|
||||
|
||||
<p>lru算法实现缓存。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func NewLRUCache[K comparable, V any](capacity int) *LRUCache[K, V]
|
||||
func (l *LRUCache[K, V]) Get(key K) (V, bool)
|
||||
func (l *LRUCache[K, V]) Put(key K, value V)
|
||||
func (l *LRUCache[K, V]) Delete(key K) bool
|
||||
func (l *LRUCache[K, V]) Len() int
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/-EZjgOURufP)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/algorithm"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cache := algorithm.NewLRUCache[int, int](2)
|
||||
|
||||
cache.Put(1, 1)
|
||||
cache.Put(2, 2)
|
||||
|
||||
result1, ok1 := cache.Get(1)
|
||||
result2, ok2 := cache.Get(2)
|
||||
result3, ok3 := cache.Get(3)
|
||||
|
||||
fmt.Println(result1, ok1)
|
||||
fmt.Println(result2, ok2)
|
||||
fmt.Println(result3, ok3)
|
||||
|
||||
fmt.Println(cache.Len())
|
||||
|
||||
ok := cache.Delete(2)
|
||||
fmt.Println(ok)
|
||||
|
||||
|
||||
// Output:
|
||||
// 1 true
|
||||
// 2 true
|
||||
// 0 false
|
||||
// 2
|
||||
// true
|
||||
}
|
||||
```
|
||||
375
docs/api/packages/compare.md
Normal file
375
docs/api/packages/compare.md
Normal file
@@ -0,0 +1,375 @@
|
||||
# Compare
|
||||
|
||||
compare包提供几个轻量级的类型比较函数。
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码:
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/compare/compare.go](https://github.com/duke-git/lancet/blob/main/compare/compare.go)
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/compare/compare_internal.go](https://github.com/duke-git/lancet/blob/main/compare/compare_internal.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 用法:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/condition"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 目录
|
||||
|
||||
- [Equal](#Equal)
|
||||
- [EqualValue](#EqualValue)
|
||||
- [LessThan](#LessThan)
|
||||
- [GreaterThan](#GreaterThan)
|
||||
- [LessOrEqual](#LessOrEqual)
|
||||
- [GreaterOrEqual](#GreaterOrEqual)
|
||||
- [InDelta](#InDelta)
|
||||
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 文档
|
||||
|
||||
### <span id="Equal">Equal</span>
|
||||
|
||||
<p>检查两个值是否相等(检查类型和值)</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Equal(left, right any) bool
|
||||
```
|
||||
|
||||
<b>示例: <span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/wmVxR-to4lz)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/compare"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := compare.Equal(1, 1)
|
||||
result2 := compare.Equal("1", "1")
|
||||
result3 := compare.Equal([]int{1, 2, 3}, []int{1, 2, 3})
|
||||
result4 := compare.Equal(map[int]string{1: "a", 2: "b"}, map[int]string{1: "a", 2: "b"})
|
||||
|
||||
result5 := compare.Equal(1, "1")
|
||||
result6 := compare.Equal(1, int64(1))
|
||||
result7 := compare.Equal([]int{1, 2}, []int{1, 2, 3})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
fmt.Println(result7)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="EqualValue">EqualValue</span>
|
||||
|
||||
<p>检查两个值是否相等(只检查值)</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func EqualValue(left, right any) bool
|
||||
```
|
||||
|
||||
<b>示例: <span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/fxnna_LLD9u)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/compare"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := compare.EqualValue(1, 1)
|
||||
result2 := compare.EqualValue(int(1), int64(1))
|
||||
result3 := compare.EqualValue(1, "1")
|
||||
result4 := compare.EqualValue(1, "2")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="LessThan">LessThan</span>
|
||||
|
||||
<p>验证参数`left`的值是否小于参数`right`的值。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func LessThan(left, right any) bool
|
||||
```
|
||||
|
||||
<b>示例: <span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/cYh7FQQj0ne)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/compare"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := compare.LessThan(1, 2)
|
||||
result2 := compare.LessThan(1.1, 2.2)
|
||||
result3 := compare.LessThan("a", "b")
|
||||
|
||||
time1 := time.Now()
|
||||
time2 := time1.Add(time.Second)
|
||||
result4 := compare.LessThan(time1, time2)
|
||||
|
||||
result5 := compare.LessThan(2, 1)
|
||||
result6 := compare.LessThan(1, int64(2))
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GreaterThan">GreaterThan</span>
|
||||
|
||||
<p>验证参数`left`的值是否大于参数`right`的值。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func GreaterThan(left, right any) bool
|
||||
```
|
||||
|
||||
<b>示例: <span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/9-NYDFZmIMp)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/compare"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := compare.GreaterThan(2, 1)
|
||||
result2 := compare.GreaterThan(2.2, 1.1)
|
||||
result3 := compare.GreaterThan("b", "a")
|
||||
|
||||
time1 := time.Now()
|
||||
time2 := time1.Add(time.Second)
|
||||
result4 := compare.GreaterThan(time2, time1)
|
||||
|
||||
result5 := compare.GreaterThan(1, 2)
|
||||
result6 := compare.GreaterThan(int64(2), 1)
|
||||
result7 := compare.GreaterThan("b", "c")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
fmt.Println(result7)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="LessOrEqual">LessOrEqual</span>
|
||||
|
||||
<p>验证参数`left`的值是否小于或等于参数`right`的值。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func LessOrEqual(left, right any) bool
|
||||
```
|
||||
|
||||
<b>示例: <span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/e4T_scwoQzp)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/compare"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := compare.LessOrEqual(1, 1)
|
||||
result2 := compare.LessOrEqual(1.1, 2.2)
|
||||
result3 := compare.LessOrEqual("a", "b")
|
||||
|
||||
time1 := time.Now()
|
||||
time2 := time1.Add(time.Second)
|
||||
result4 := compare.LessOrEqual(time1, time2)
|
||||
|
||||
result5 := compare.LessOrEqual(2, 1)
|
||||
result6 := compare.LessOrEqual(1, int64(2))
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GreaterOrEqual">GreaterOrEqual</span>
|
||||
|
||||
<p>验证参数`left`的值是否大于或参数`right`的值。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func GreaterOrEqual(left, right any) bool
|
||||
```
|
||||
|
||||
<b>示例: <span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/vx8mP0U8DFk)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/compare"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := compare.GreaterOrEqual(1, 1)
|
||||
result2 := compare.GreaterOrEqual(2.2, 1.1)
|
||||
result3 := compare.GreaterOrEqual("b", "b")
|
||||
|
||||
time1 := time.Now()
|
||||
time2 := time1.Add(time.Second)
|
||||
result4 := compare.GreaterOrEqual(time2, time1)
|
||||
|
||||
result5 := compare.GreaterOrEqual(1, 2)
|
||||
result6 := compare.GreaterOrEqual(int64(2), 1)
|
||||
result7 := compare.GreaterOrEqual("b", "c")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
fmt.Println(result7)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="InDelta">InDelta</span>
|
||||
|
||||
<p>检查增量内两个值是否相等。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func InDelta[T constraints.Integer | constraints.Float](left, right T, delta float64) bool
|
||||
```
|
||||
|
||||
<b>示例: <span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/TuDdcNtMkjo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/compare"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := InDelta(1, 1, 0)
|
||||
result2 := InDelta(1, 2, 0)
|
||||
|
||||
result3 := InDelta(2.0/3.0, 0.66667, 0.001)
|
||||
result4 := InDelta(2.0/3.0, 0.0, 0.001)
|
||||
|
||||
result5 := InDelta(float64(74.96)-float64(20.48), 54.48, 0)
|
||||
result6 := InDelta(float64(74.96)-float64(20.48), 54.48, 1e-14)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
// true
|
||||
}
|
||||
```
|
||||
454
docs/api/packages/concurrency.md
Normal file
454
docs/api/packages/concurrency.md
Normal file
@@ -0,0 +1,454 @@
|
||||
# Concurrency
|
||||
|
||||
并发包包含一些支持并发编程的功能。例如:goroutine, channel 等。
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码:
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/concurrency/channel.go](https://github.com/duke-git/lancet/blob/main/concurrency/channel.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 用法:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 目录
|
||||
|
||||
### Channel
|
||||
|
||||
- [NewChannel](#NewChannel)
|
||||
- [Bridge](#Bridge)
|
||||
- [FanIn](#FanIn)
|
||||
- [Generate](#Generate)
|
||||
- [Or](#Or)
|
||||
- [OrDone](#OrDone)
|
||||
- [Repeat](#Repeat)
|
||||
- [RepeatFn](#RepeatFn)
|
||||
- [Take](#Take)
|
||||
- [Tee](#Tee)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 文档
|
||||
|
||||
### Channel
|
||||
|
||||
### <span id="NewChannel">NewChannel</span>
|
||||
|
||||
<p>返回一个Channel指针实例</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
type Channel[T any] struct
|
||||
func NewChannel[T any]() *Channel[T]
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/7aB4KyMMp9A)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
c := concurrency.NewChannel[int]()
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Bridge">Bridge</span>
|
||||
|
||||
<p>将多个channel链接到一个channel,直到取消上下文。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (c *Channel[T]) Bridge(ctx context.Context, chanStream <-chan <-chan T) <-chan T
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/qmWSy1NVF-Y)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
c := concurrency.NewChannel[int]()
|
||||
genVals := func() <-chan <-chan int {
|
||||
out := make(chan (<-chan int))
|
||||
go func() {
|
||||
defer close(out)
|
||||
for i := 1; i <= 5; i++ {
|
||||
stream := make(chan int, 1)
|
||||
stream <- i
|
||||
close(stream)
|
||||
out <- stream
|
||||
}
|
||||
}()
|
||||
return out
|
||||
}
|
||||
|
||||
for v := range c.Bridge(ctx, genVals()) {
|
||||
fmt.Println(v)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 2
|
||||
// 3
|
||||
// 4
|
||||
// 5
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="FanIn">FanIn</span>
|
||||
|
||||
<p>将多个channel合并为一个channel,直到取消上下文。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (c *Channel[T]) FanIn(ctx context.Context, channels ...<-chan T) <-chan T
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/2VYFMexEvTm)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
c := concurrency.NewChannel[int]()
|
||||
channels := make([]<-chan int, 2)
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
channels[i] = c.Take(ctx, c.Repeat(ctx, i), 2)
|
||||
}
|
||||
|
||||
chs := c.FanIn(ctx, channels...)
|
||||
|
||||
for v := range chs {
|
||||
fmt.Println(v) //1 1 0 0 or 0 0 1 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Generate">Generate</span>
|
||||
|
||||
<p>根据传入的值,生成channel.</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (c *Channel[T]) Generate(ctx context.Context, values ...T) <-chan T
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/7aB4KyMMp9A)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
c := concurrency.NewChannel[int]()
|
||||
intStream := c.Generate(ctx, 1, 2, 3)
|
||||
|
||||
fmt.Println(<-intStream)
|
||||
fmt.Println(<-intStream)
|
||||
fmt.Println(<-intStream)
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 2
|
||||
// 3
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Repeat">Repeat</span>
|
||||
|
||||
<p>返回一个channel,将参数`values`重复放入channel,直到取消上下文。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (c *Channel[T]) Repeat(ctx context.Context, values ...T) <-chan T
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/k5N_ALVmYjE)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
c := concurrency.NewChannel[int]()
|
||||
intStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 4)
|
||||
|
||||
for v := range intStream {
|
||||
fmt.Println(v)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 2
|
||||
// 1
|
||||
// 2
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RepeatFn">RepeatFn</span>
|
||||
|
||||
<p>返回一个channel,重复执行函数fn,并将结果放入返回的channel,直到取消上下文。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (c *Channel[T]) RepeatFn(ctx context.Context, fn func() T) <-chan T
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/4J1zAWttP85)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
fn := func() string {
|
||||
return "hello"
|
||||
}
|
||||
|
||||
c := concurrency.NewChannel[string]()
|
||||
intStream := c.Take(ctx, c.RepeatFn(ctx, fn), 3)
|
||||
|
||||
for v := range intStream {
|
||||
fmt.Println(v)
|
||||
}
|
||||
// Output:
|
||||
// hello
|
||||
// hello
|
||||
// hello
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Or">Or</span>
|
||||
|
||||
<p>将一个或多个channel读取到一个channel中,当任何读取channel关闭时将结束读取。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (c *Channel[T]) Or(channels ...<-chan T) <-chan T
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Wqz9rwioPww)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sig := func(after time.Duration) <-chan any {
|
||||
c := make(chan any)
|
||||
go func() {
|
||||
defer close(c)
|
||||
time.Sleep(after)
|
||||
}()
|
||||
return c
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
|
||||
c := concurrency.NewChannel[any]()
|
||||
<-c.Or(
|
||||
sig(1*time.Second),
|
||||
sig(2*time.Second),
|
||||
sig(3*time.Second),
|
||||
)
|
||||
|
||||
fmt.Println("done after %v", time.Since(start)) //1.003s
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="OrDone">OrDone</span>
|
||||
|
||||
<p>将一个channel读入另一个channel,直到取消上下文。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (c *Channel[T]) OrDone(ctx context.Context, channel <-chan T) <-chan T
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/lm_GoS6aDjo)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
c := concurrency.NewChannel[int]()
|
||||
intStream := c.Take(ctx, c.Repeat(ctx, 1), 3)
|
||||
|
||||
for v := range c.OrDone(ctx, intStream) {
|
||||
fmt.Println(v)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 1
|
||||
// 1
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Take">Take</span>
|
||||
|
||||
<p>返回一个channel,其值从另一个channel获取,直到取消上下文。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (c *Channel[T]) Take(ctx context.Context, valueStream <-chan T, number int) <-chan T
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/9Utt-1pDr2J)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
numbers := make(chan int, 5)
|
||||
numbers <- 1
|
||||
numbers <- 2
|
||||
numbers <- 3
|
||||
numbers <- 4
|
||||
numbers <- 5
|
||||
defer close(numbers)
|
||||
|
||||
c := concurrency.NewChannel[int]()
|
||||
intStream := c.Take(ctx, numbers, 3)
|
||||
|
||||
for v := range intStream {
|
||||
fmt.Println(v)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 2
|
||||
// 3
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Tee">Tee</span>
|
||||
|
||||
<p>将一个channel分成两个channel,直到取消上下文。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (c *Channel[T]) Tee(ctx context.Context, in <-chan T) (<-chan T, <-chan T)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/3TQPKnCirrP)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/concurrency"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
c := concurrency.NewChannel[int]()
|
||||
intStream := c.Take(ctx, c.Repeat(ctx, 1), 2)
|
||||
|
||||
ch1, ch2 := c.Tee(ctx, intStream)
|
||||
|
||||
for v := range ch1 {
|
||||
fmt.Println(v)
|
||||
fmt.Println(<-ch2)
|
||||
}
|
||||
// Output:
|
||||
// 1
|
||||
// 1
|
||||
// 1
|
||||
// 1
|
||||
}
|
||||
```
|
||||
298
docs/api/packages/condition.md
Normal file
298
docs/api/packages/condition.md
Normal file
@@ -0,0 +1,298 @@
|
||||
# Condition
|
||||
condition包含一些用于条件判断的函数。这个包的实现参考了carlmjohnson的truthy包的实现,更多有用的信息可以在[truthy](https://github.com/carlmjohnson/truthy)中找到,感谢carlmjohnson。
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码:
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/condition/condition.go](https://github.com/duke-git/lancet/blob/main/condition/condition.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 用法:
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/condition"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 目录
|
||||
|
||||
- [Bool](#Bool)
|
||||
- [And](#And)
|
||||
- [Or](#Or)
|
||||
- [Xor](#Generate)
|
||||
- [Nor](#Nor)
|
||||
- [Xnor](#Xnor)
|
||||
- [Nand](#Nand)
|
||||
- [TernaryOperator](#TernaryOperator)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 文档
|
||||
|
||||
### <span id="Bool">Bool</span>
|
||||
<p>返回传入参数的bool值.<br/>
|
||||
如果出入类型参数含有Bool方法, 会调用该方法并返回<br/>
|
||||
如果传入类型参数有IsZero方法, 返回IsZero方法返回值的取反<br/>
|
||||
slices和map的length大于0时,返回true,否则返回false<br/>
|
||||
其他类型会判断是否是零值</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Bool[T any](value T) bool
|
||||
```
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ETzeDJRSvhm)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/condition"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// bool
|
||||
result1 := condition.Bool(false)
|
||||
result2 := condition.Bool(true)
|
||||
fmt.Println(result1) // false
|
||||
fmt.Println(result2) // true
|
||||
|
||||
// integer
|
||||
result3 := condition.Bool(0)
|
||||
result4 := condition.Bool(1)
|
||||
fmt.Println(result3) // false
|
||||
fmt.Println(result4) // true
|
||||
|
||||
// string
|
||||
result5 := condition.Bool("")
|
||||
result6 := condition.Bool(" ")
|
||||
fmt.Println(result5) // false
|
||||
fmt.Println(result6) // true
|
||||
|
||||
// slice
|
||||
nums := []int{}
|
||||
result7 := condition.Bool(nums)
|
||||
|
||||
nums = append(nums, 1, 2)
|
||||
result8 := condition.Bool(nums)
|
||||
fmt.Println(result7) // false
|
||||
fmt.Println(result8) // true
|
||||
|
||||
// struct
|
||||
result9 = condition.Bool(struct{}{})
|
||||
fmt.Println(result8) // false
|
||||
|
||||
|
||||
// Output:
|
||||
// false
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="And">And</span>
|
||||
<p>逻辑且操作,当切仅当a和b都为true时返回true</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func And[T, U any](a T, b U) bool
|
||||
```
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/W1SSUmt6pvr)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/condition"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(condition.And(0, 1)) // false
|
||||
fmt.Println(condition.And(0, "")) // false
|
||||
fmt.Println(condition.And(0, "0")) // false
|
||||
fmt.Println(condition.And(1, "0")) // true
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Or">Or</span>
|
||||
<p>逻辑或操作,当切仅当a和b都为false时返回false</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Or[T, U any](a T, b U) bool
|
||||
```
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/UlQTxHaeEkq)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/condition"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(condition.Or(0, "")) // false
|
||||
fmt.Println(condition.Or(0, 1)) // true
|
||||
fmt.Println(condition.Or(0, "0")) // true
|
||||
fmt.Println(condition.Or(1, "0")) // true
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Xor">Xor</span>
|
||||
<p>逻辑异或操作,a和b相同返回false,a和b不相同返回true</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Xor[T, U any](a T, b U) bool
|
||||
```
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/gObZrW7ZbG8)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/condition"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(condition.Xor(0, 0)) // false
|
||||
fmt.Println(condition.Xor(0, 1)) // true
|
||||
fmt.Println(condition.Xor(1, 0)) // true
|
||||
fmt.Println(condition.Xor(1, 1)) // false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Nor">Nor</span>
|
||||
<p>异或的取反操作</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Nor[T, U any](a T, b U) bool
|
||||
```
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/g2j08F_zZky)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/condition"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(condition.Nor(0, 0)) // true
|
||||
fmt.Println(condition.Nor(0, 1)) // false
|
||||
fmt.Println(condition.Nor(1, 0)) // false
|
||||
fmt.Println(condition.Nor(1, 1)) // false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Xnor">Xnor</span>
|
||||
<p>如果a和b都是真的或a和b均是假的,则返回true。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Xnor[T, U any](a T, b U) bool
|
||||
```
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/OuDB9g51643)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/condition"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(condition.Xnor(0, 0)) // true
|
||||
fmt.Println(condition.Xnor(0, 1)) // false
|
||||
fmt.Println(condition.Xnor(1, 0)) // false
|
||||
fmt.Println(condition.Xnor(1, 1)) // true
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Nand">Nand</span>
|
||||
<p>如果a和b都为真,返回false,否则返回true</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Nand[T, U any](a T, b U) bool
|
||||
```
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/vSRMLxLIbq8)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/condition"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(condition.Nand(0, 0)) // true
|
||||
fmt.Println(condition.Nand(0, 1)) // true
|
||||
fmt.Println(condition.Nand(1, 0)) // true
|
||||
fmt.Println(condition.Nand(1, 1)) // false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="TernaryOperator">TernaryOperator</span>
|
||||
<p>三元运算符</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func TernaryOperator[T, U any](isTrue T, ifValue U, elseValue U) U
|
||||
```
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ElllPZY0guT)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/condition"
|
||||
)
|
||||
|
||||
func main() {
|
||||
conditionTrue := 2 > 1
|
||||
result1 := condition.TernaryOperator(conditionTrue, 0, 1)
|
||||
|
||||
conditionFalse := 2 > 3
|
||||
result2 := condition.TernaryOperator(conditionFalse, 0, 1)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
// 1
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
878
docs/api/packages/convertor.md
Normal file
878
docs/api/packages/convertor.md
Normal file
@@ -0,0 +1,878 @@
|
||||
# Convertor
|
||||
|
||||
convertor 转换器包支持一些常见的数据类型转换
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码:
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/convertor/convertor.go](https://github.com/duke-git/lancet/blob/main/convertor/convertor.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 用法:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 目录
|
||||
|
||||
- [ColorHexToRGB](#ColorHexToRGB)
|
||||
- [ColorRGBToHex](#ColorRGBToHex)
|
||||
- [ToBool](#ToBool)
|
||||
- [ToBytes](#ToBytes)
|
||||
- [ToChar](#ToChar)
|
||||
- [ToChannel](#ToChannel)
|
||||
- [ToFloat](#ToFloat)
|
||||
- [ToInt](#ToInt)
|
||||
- [ToJson](#ToJson)
|
||||
- [ToMap](#ToMap)
|
||||
- [ToPointer](#ToPointer)
|
||||
- [ToString](#ToString)
|
||||
- [StructToMap](#StructToMap)
|
||||
- [MapToSlice](#MapToSlice)
|
||||
- [EncodeByte](#EncodeByte)
|
||||
- [DecodeByte](#DecodeByte)
|
||||
- [DeepClone](#DeepClone)
|
||||
- [CopyProperties](#CopyProperties)
|
||||
- [ToInterface](#ToInterface)
|
||||
- [Utf8ToGbk](#Utf8ToGbk)
|
||||
- [GbkToUtf8](#GbkToUtf8)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 文档
|
||||
|
||||
### <span id="ColorHexToRGB">ColorHexToRGB</span>
|
||||
|
||||
<p>颜色值十六进制转rgb。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ColorHexToRGB(colorHex string) (red, green, blue int)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/o7_ft-JCJBV)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
colorHex := "#003366"
|
||||
r, g, b := convertor.ColorHexToRGB(colorHex)
|
||||
|
||||
fmt.Println(r, g, b)
|
||||
|
||||
// Output:
|
||||
// 0 51 102
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ColorRGBToHex">ColorRGBToHex</span>
|
||||
|
||||
<p>颜色值rgb转十六进制。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ColorRGBToHex(red, green, blue int) string
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/nzKS2Ro87J1)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
r := 0
|
||||
g := 51
|
||||
b := 102
|
||||
colorHex := ColorRGBToHex(r, g, b)
|
||||
|
||||
fmt.Println(colorHex)
|
||||
|
||||
// Output:
|
||||
// #003366
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToBool">ToBool</span>
|
||||
|
||||
<p>字符串转布尔类型,使用strconv.ParseBool。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ToBool(s string) (bool, error)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ARht2WnGdIN)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cases := []string{"1", "true", "True", "false", "False", "0", "123", "0.0", "abc"}
|
||||
|
||||
for i := 0; i < len(cases); i++ {
|
||||
result, _ := convertor.ToBool(cases[i])
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
// false
|
||||
// false
|
||||
// false
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToBytes">ToBytes</span>
|
||||
|
||||
<p>Interface转字节切片。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ToBytes(data any) ([]byte, error)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/fAMXYFDvOvr)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
bytesData, err := convertor.ToBytes("abc")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
fmt.Println(bytesData)
|
||||
|
||||
// Output:
|
||||
// [97 98 99]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToChar">ToChar</span>
|
||||
|
||||
<p>字符串转字符切片。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ToChar(s string) []string
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/JJ1SvbFkVdM)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := convertor.ToChar("")
|
||||
result2 := convertor.ToChar("abc")
|
||||
result3 := convertor.ToChar("1 2#3")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// []
|
||||
// [a b c]
|
||||
// [1 2 # 3]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToChannel">ToChannel</span>
|
||||
|
||||
<p>将切片转为只读channel。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ToChannel[T any](array []T) <-chan T
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/hOx_oYZbAnL)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ch := convertor.ToChannel([]int{1, 2, 3})
|
||||
result1 := <-ch
|
||||
result2 := <-ch
|
||||
result3 := <-ch
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 2
|
||||
// 3
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToFloat">ToFloat</span>
|
||||
|
||||
<p>将interface转成float64类型,如果参数无法转换,会返回0和error。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ToFloat(value any) (float64, error)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/4YTmPCibqHJ)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1, _ := convertor.ToFloat("")
|
||||
result2, err := convertor.ToFloat("abc")
|
||||
result3, _ := convertor.ToFloat("-1")
|
||||
result4, _ := convertor.ToFloat("-.11")
|
||||
result5, _ := convertor.ToFloat("1.23e3")
|
||||
result6, _ := convertor.ToFloat(true)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2, err)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
// 0 strconv.ParseFloat: parsing "": invalid syntax
|
||||
// -1
|
||||
// -0.11
|
||||
// 1230
|
||||
// 0
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToInt">ToInt</span>
|
||||
|
||||
<p>将interface转成int64类型,如果参数无法转换,会返回0和error。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ToInt(value any) (int64, error)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/9_h9vIt-QZ_b)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1, _ := convertor.ToInt("123")
|
||||
result2, _ := convertor.ToInt("-123")
|
||||
result3, _ := convertor.ToInt(float64(12.3))
|
||||
result4, err := convertor.ToInt("abc")
|
||||
result5, _ := convertor.ToInt(true)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4, err)
|
||||
fmt.Println(result5)
|
||||
|
||||
// Output:
|
||||
// 123
|
||||
// -123
|
||||
// 12
|
||||
// 0 strconv.ParseInt: parsing "": invalid syntax
|
||||
// 0
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToJson">ToJson</span>
|
||||
|
||||
<p>将interface转成json字符串,如果参数无法转换,会返回""和error。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ToJson(value any) (string, error)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/2rLIkMmXWvR)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
aMap := map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
result, err := ToJson(aMap)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("%v", err)
|
||||
}
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// {"a":1,"b":2,"c":3}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToMap">ToMap</span>
|
||||
|
||||
<p>将切片转为map。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ToMap[T any, K comparable, V any](array []T, iteratee func(T) (K, V)) map[K]V
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/tVFy7E-t24l)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Message struct {
|
||||
name string
|
||||
code int
|
||||
}
|
||||
messages := []Message{
|
||||
{name: "Hello", code: 100},
|
||||
{name: "Hi", code: 101},
|
||||
}
|
||||
|
||||
result := convertor.ToMap(messages, func(msg Message) (int, string) {
|
||||
return msg.code, msg.name
|
||||
})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[100:Hello 101:Hi]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToPointer">ToPointer</span>
|
||||
|
||||
<p>返回传入值的指针。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ToPointer[T any](value T) *T
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ASf_etHNlw1)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result := convertor.ToPointer(123)
|
||||
fmt.Println(*result)
|
||||
|
||||
// Output:
|
||||
// 123
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToString">ToString</span>
|
||||
|
||||
<p>将值转换为字符串,对于数字、字符串、[]byte,将转换为字符串。 对于其他类型(切片、映射、数组、结构体)将调用 json.Marshal</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ToString(value any) string
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/nF1zOOslpQq)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := convertor.ToString("")
|
||||
result2 := convertor.ToString(nil)
|
||||
result3 := convertor.ToString(0)
|
||||
result4 := convertor.ToString(1.23)
|
||||
result5 := convertor.ToString(true)
|
||||
result6 := convertor.ToString(false)
|
||||
result7 := convertor.ToString([]int{1, 2, 3})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
fmt.Println(result7)
|
||||
|
||||
// Output:
|
||||
//
|
||||
//
|
||||
// 0
|
||||
// 1.23
|
||||
// true
|
||||
// false
|
||||
// [1,2,3]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="StructToMap">StructToMap</span>
|
||||
|
||||
<p>将struct转成map,只会转换struct中可导出的字段。struct中导出字段需要设置json tag标记。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func StructToMap(value any) (map[string]any, error)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/KYGYJqNUBOI)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type People struct {
|
||||
Name string `json:"name"`
|
||||
age int
|
||||
}
|
||||
p := People{
|
||||
"test",
|
||||
100,
|
||||
}
|
||||
pm, _ := convertor.StructToMap(p)
|
||||
|
||||
fmt.Println(pm)
|
||||
|
||||
// Output:
|
||||
// map[name:test]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="MapToSlice">MapToSlice</span>
|
||||
|
||||
<p>map中key和value执行函数iteratee后,转为切片。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func MapToSlice[T any, K comparable, V any](aMap map[K]V, iteratee func(K, V) T) []T
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/dmX4Ix5V6Wl)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
aMap := map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
result := MapToSlice(aMap, func(key string, value int) string {
|
||||
return key + ":" + strconv.Itoa(value)
|
||||
})
|
||||
|
||||
fmt.Println(result) //[]string{"a:1", "b:2", "c:3"}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="EncodeByte">EncodeByte</span>
|
||||
|
||||
<p>将data编码成字节切片。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func EncodeByte(data any) ([]byte, error)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/DVmM1G5JfuP)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
byteData, _ := convertor.EncodeByte("abc")
|
||||
fmt.Println(byteData)
|
||||
|
||||
// Output:
|
||||
// [6 12 0 3 97 98 99]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="DecodeByte">DecodeByte</span>
|
||||
|
||||
<p>解码字节切片到目标对象,目标对象需要传入一个指针实例。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func DecodeByte(data []byte, target any) error
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/zI6xsmuQRbn)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var result string
|
||||
byteData := []byte{6, 12, 0, 3, 97, 98, 99}
|
||||
|
||||
err := convertor.DecodeByte(byteData, &result)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// abc
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="DeepClone">DeepClone</span>
|
||||
|
||||
<p>创建一个传入值的深拷贝, 无法克隆结构体的非导出字段。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func DeepClone[T any](src T) T
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/j4DP5dquxnk)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Struct struct {
|
||||
Str string
|
||||
Int int
|
||||
Float float64
|
||||
Bool bool
|
||||
Nil interface{}
|
||||
unexported string
|
||||
}
|
||||
|
||||
cases := []interface{}{
|
||||
true,
|
||||
1,
|
||||
0.1,
|
||||
map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
},
|
||||
&Struct{
|
||||
Str: "test",
|
||||
Int: 1,
|
||||
Float: 0.1,
|
||||
Bool: true,
|
||||
Nil: nil,
|
||||
// unexported: "can't be cloned",
|
||||
},
|
||||
}
|
||||
|
||||
for _, item := range cases {
|
||||
cloned := convertor.DeepClone(item)
|
||||
|
||||
isPointerEqual := &cloned == &item
|
||||
fmt.Println(cloned, isPointerEqual)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true false
|
||||
// 1 false
|
||||
// 0.1 false
|
||||
// map[a:1 b:2] false
|
||||
// &{test 1 0.1 true <nil> } false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CopyProperties">CopyProperties</span>
|
||||
|
||||
<p>拷贝不同结构体之间的同名字段。使用json.Marshal序列化,需要设置dst和src struct字段的json tag。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func CopyProperties[T, U any](dst T, src U) (err error)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/oZujoB5Sgg5)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Disk struct {
|
||||
Name string `json:"name"`
|
||||
Total string `json:"total"`
|
||||
Used string `json:"used"`
|
||||
Percent float64 `json:"percent"`
|
||||
}
|
||||
|
||||
type DiskVO struct {
|
||||
Name string `json:"name"`
|
||||
Total string `json:"total"`
|
||||
Used string `json:"used"`
|
||||
Percent float64 `json:"percent"`
|
||||
}
|
||||
|
||||
type Indicator struct {
|
||||
Id string `json:"id"`
|
||||
Ip string `json:"ip"`
|
||||
UpTime string `json:"upTime"`
|
||||
LoadAvg string `json:"loadAvg"`
|
||||
Cpu int `json:"cpu"`
|
||||
Disk []Disk `json:"disk"`
|
||||
Stop chan bool `json:"-"`
|
||||
}
|
||||
|
||||
type IndicatorVO struct {
|
||||
Id string `json:"id"`
|
||||
Ip string `json:"ip"`
|
||||
UpTime string `json:"upTime"`
|
||||
LoadAvg string `json:"loadAvg"`
|
||||
Cpu int64 `json:"cpu"`
|
||||
Disk []DiskVO `json:"disk"`
|
||||
}
|
||||
|
||||
indicator := &Indicator{Id: "001", Ip: "127.0.0.1", Cpu: 1, Disk: []Disk{
|
||||
{Name: "disk-001", Total: "100", Used: "1", Percent: 10},
|
||||
{Name: "disk-002", Total: "200", Used: "1", Percent: 20},
|
||||
{Name: "disk-003", Total: "300", Used: "1", Percent: 30},
|
||||
}}
|
||||
|
||||
indicatorVO := IndicatorVO{}
|
||||
|
||||
err := convertor.CopyProperties(&indicatorVO, indicator)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(indicatorVO.Id)
|
||||
fmt.Println(indicatorVO.Ip)
|
||||
fmt.Println(len(indicatorVO.Disk))
|
||||
|
||||
// Output:
|
||||
// 001
|
||||
// 127.0.0.1
|
||||
// 3
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToInterface">ToInterface</span>
|
||||
|
||||
<p>将反射值转换成对应的interface类型。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ToInterface(v reflect.Value) (value interface{}, ok bool)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/syqw0-WG7Xd)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
val := reflect.ValueOf("abc")
|
||||
iVal, ok := convertor.ToInterface(val)
|
||||
|
||||
fmt.Printf("%T\n", iVal)
|
||||
fmt.Printf("%v\n", iVal)
|
||||
fmt.Println(ok)
|
||||
|
||||
// Output:
|
||||
// string
|
||||
// abc
|
||||
// true
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Utf8ToGbk">Utf8ToGbk</span>
|
||||
|
||||
<p>utf8编码转GBK编码。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Utf8ToGbk(bs []byte) ([]byte, error)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/9FlIaFLArIL)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
"github.com/duke-git/lancet/v2/validator"
|
||||
)
|
||||
|
||||
func main() {
|
||||
utf8Data := []byte("hello")
|
||||
gbkData, _ := convertor.Utf8ToGbk(utf8Data)
|
||||
|
||||
fmt.Println(utf8.Valid(utf8Data))
|
||||
fmt.Println(validator.IsGBK(gbkData))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GbkToUtf8">GbkToUtf8</span>
|
||||
|
||||
<p>GBK编码转utf8编码。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func GbkToUtf8(bs []byte) ([]byte, error)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/OphmHCN_9u8)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
gbkData, _ := convertor.Utf8ToGbk([]byte("hello"))
|
||||
utf8Data, _ := convertor.GbkToUtf8(gbkData)
|
||||
|
||||
fmt.Println(utf8.Valid(utf8Data))
|
||||
fmt.Println(string(utf8Data))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// hello
|
||||
}
|
||||
```
|
||||
1539
docs/api/packages/cryptor.md
Normal file
1539
docs/api/packages/cryptor.md
Normal file
File diff suppressed because it is too large
Load Diff
471
docs/api/packages/datastructure/copyonwritelist.md
Normal file
471
docs/api/packages/datastructure/copyonwritelist.md
Normal file
@@ -0,0 +1,471 @@
|
||||
# CopyOnWriteList
|
||||
|
||||
CopyOnWriteList 是一个线程安全的 List 实现,底层使用 go 切片。写入时,会复制一份新的切片,写入完成后,再将新的切片赋值给原来的切片。读取时,直接读取原来的切片。
|
||||
|
||||
## 源码
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/datastructure/list/copyonwritelist.go](https://github.com/duke-git/lancet/blob/main/datastructure/list/copyonwritelist.go)
|
||||
|
||||
## 用法
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/datastructure/list"
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 目录
|
||||
|
||||
- [NewCopyOnWriteList](#NewCopyOnWriteList)
|
||||
- [Size](#Size)
|
||||
- [Get](#Get)
|
||||
- [Set](#Set)
|
||||
- [Remove](#Remove)
|
||||
- [IndexOf](#IndexOf)
|
||||
- [LastIndexOf](#LastIndexOf)
|
||||
- [IsEmpty](#IsEmpty)
|
||||
- [Contain](#Contain)
|
||||
- [ValueOf](#ValueOf)
|
||||
- [Add](#Add)
|
||||
- [AddAll](#AddAll)
|
||||
- [AddByIndex](#AddByIndex)
|
||||
- [DeleteAt](#DeleteAt)
|
||||
- [DeleteIf](#DeleteIf)
|
||||
- [DeleteBy](#DeleteBy)
|
||||
- [DeleteRange](#DeleteRange)
|
||||
- [Equal](#Equal)
|
||||
|
||||
## 文档
|
||||
|
||||
### NewCopyOnWriteList
|
||||
|
||||
返回一个具有空切片的 CopyOnWriteList。
|
||||
|
||||
```go
|
||||
type CopyOnWriteList[T any] struct {
|
||||
data []T
|
||||
lock sync.Locker
|
||||
}
|
||||
|
||||
func NewCopyOnWriteList() *CopyOnWriteList
|
||||
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/datastructure/list"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := list.NewCopyOnWriteList([]int{1,2,3})
|
||||
fmt.Println(l)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Size
|
||||
|
||||
返回 CopyOnWriteList 的长度。
|
||||
|
||||
```go
|
||||
func (l *CopyOnWriteList[T]) Size() int
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/datastructure/list"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := list.NewCopyOnWriteList([]int{1,2,3})
|
||||
fmt.Println(l.Size())
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Get
|
||||
|
||||
返回列表中指定位置的元素
|
||||
|
||||
```go
|
||||
func (c *CopyOnWriteList[T]) Get(index int) *T
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/datastructure/list"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := list.NewCopyOnWriteList([]int{1,2,3})
|
||||
fmt.Println(l.Get(2))
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Set
|
||||
|
||||
将此列表中指定位置的元素替换为指定元素。
|
||||
|
||||
```go
|
||||
func (c *CopyOnWriteList[T]) Set(index int, e T) (oldValue *T, ok bool)
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/datastructure/list"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := list.NewCopyOnWriteList([]int{1,2,3})
|
||||
fmt.Println(l.Set(2, 4))
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Remove
|
||||
|
||||
### IndexOf
|
||||
|
||||
返回列表中值的索引,如果没有找到返回-1。
|
||||
|
||||
```go
|
||||
func (c *CopyOnWriteList[T]) IndexOf(e T) int
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/datastructure/list"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := list.NewCopyOnWriteList([]int{1,2,3})
|
||||
fmt.Println(l.IndexOf(1))
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### LastIndexOf
|
||||
|
||||
返回指定元素在此列表中最后出现的索引,如果此列表不包含该元素,则返回-1。
|
||||
|
||||
```go
|
||||
func (c *CopyOnWriteList[T]) LastIndexOf(e T) int
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/datastructure/list"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := list.NewCopyOnWriteList([]int{1,2,3,1})
|
||||
fmt.Println(l.LastIndexOf(1))
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### IsEmpty
|
||||
|
||||
如果此列表不包含任何元素,则返回 true。
|
||||
|
||||
```go
|
||||
func (c *CopyOnWriteList[T]) IsEmpty() bool
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/datastructure/list"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := list.NewCopyOnWriteList([]int{})
|
||||
fmt.Println(l.IsEmpty())
|
||||
}
|
||||
```
|
||||
|
||||
### Contain
|
||||
|
||||
判断 CopyOnWriteList 是否包含某个元素
|
||||
|
||||
```go
|
||||
func (c *CopyOnWriteList[T]) Contain(e T) bool
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/datastructure/list"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := list.NewCopyOnWriteList([]int{1,2,3})
|
||||
fmt.Println(l.Contain(1))
|
||||
}
|
||||
```
|
||||
|
||||
### ValueOf
|
||||
|
||||
返回列表中索引处的值指针
|
||||
|
||||
```go
|
||||
func (c *CopyOnWriteList[T]) ValueOf(index int) []T
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/datastructure/list"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := list.NewCopyOnWriteList([]int{1,2,3})
|
||||
fmt.Println(l.ValueOf(2))
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Add
|
||||
|
||||
将指定的元素追加到此列表的末尾。
|
||||
|
||||
```go
|
||||
func (c *CopyOnWriteList[T]) Add(e T) bool
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/datastructure/list"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := list.NewCopyOnWriteList([]int{1,2,3})
|
||||
l.Add(4)
|
||||
fmt.Println(l.getList())
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### AddAll
|
||||
|
||||
将指定集合中的所有元素追加到此列表的末尾
|
||||
|
||||
```go
|
||||
func (c *CopyOnWriteList[T]) AddAll(e []T) bool
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/datastructure/list"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := list.NewCopyOnWriteList([]int{1,2,3})
|
||||
l.AddAll([]int{4,5,6})
|
||||
fmt.Println(l.getList())
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### AddByIndex
|
||||
|
||||
将指定元素插入此列表中的指定位置。
|
||||
|
||||
```go
|
||||
func (c *CopyOnWriteList[T]) AddByIndex(index int, e T) bool
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```go
|
||||
package main
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/datastructure/list"
|
||||
)
|
||||
func main() {
|
||||
l := list.NewCopyOnWriteList([]int{1,2,3})
|
||||
list.AddByIndex(2, 6)
|
||||
fmt.Println(l.getList())
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### DeleteAt
|
||||
|
||||
移除此列表中指定位置的元素。
|
||||
|
||||
```go
|
||||
func (c *CopyOnWriteList[T]) DeleteAt(index int) (oldValue *T, ok bool)
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```go
|
||||
package main
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/datastructure/list"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := list.NewCopyOnWriteList([]int{1,2,3})
|
||||
list.DeleteAt(2)
|
||||
fmt.Println(l.getList())
|
||||
}
|
||||
```
|
||||
|
||||
### DeleteIf
|
||||
|
||||
从此列表中删除第一个出现的指定元素(如果该元素存在)。
|
||||
|
||||
```go
|
||||
func (c *CopyOnWriteList[T]) DeleteIf(f func(T) bool) (oldValue *T, ok bool)
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```go
|
||||
package main
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/datastructure/list"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := list.NewCopyOnWriteList([]int{1,2,3})
|
||||
list.DeleteIf(func(i int) bool {
|
||||
return i == 2
|
||||
})
|
||||
fmt.Println(l.getList())
|
||||
}
|
||||
```
|
||||
|
||||
### DeleteBy
|
||||
|
||||
从此列表中删除第一个出现的指定元素(如果该元素存在)。
|
||||
|
||||
```go
|
||||
func (c *CopyOnWriteList[T]) DeleteBy(e T) (*T bool)
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```go
|
||||
package main
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/datastructure/list"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := list.NewCopyOnWriteList([]int{1,2,3})
|
||||
list.DeleteBy(2)
|
||||
fmt.Println(l.getList())
|
||||
}
|
||||
```
|
||||
|
||||
### DeleteRange
|
||||
|
||||
从该列表中删除索引介于 fromIndex(包含)和 toIndex(不包含)之间的所有元素。
|
||||
(左闭右开)。
|
||||
|
||||
```go
|
||||
func (c *CopyOnWriteList[T]) DeleteRange(start int, end int)
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```go
|
||||
package main
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/datastructure/list"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := list.NewCopyOnWriteList([]int{1,2,3,4,5,6,7,8,9})
|
||||
list.DeleteRange(2, 5)
|
||||
fmt.Println(l.getList())
|
||||
}
|
||||
```
|
||||
|
||||
### Equal
|
||||
|
||||
如果指定的对象等于此列表,则返回 true。
|
||||
|
||||
```go
|
||||
func (c *CopyOnWriteList[T]) Equal(e []T) bool
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```go
|
||||
package main
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/datastructure/list"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := list.NewCopyOnWriteList([]int{1,2,3,4,5,6,7,8,9})
|
||||
fmt.Println(l.Equal([]int{1,2,3,4,5,6,7,8,9}))
|
||||
}
|
||||
```
|
||||
308
docs/api/packages/datastructure/hashmap.md
Normal file
308
docs/api/packages/datastructure/hashmap.md
Normal file
@@ -0,0 +1,308 @@
|
||||
# HashMap
|
||||
|
||||
HashMap 数据结构实现
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/datastructure/hashmap/hashmap.go](https://github.com/duke-git/lancet/blob/main/datastructure/hashmap/hashmap.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 用法
|
||||
|
||||
```go
|
||||
import (
|
||||
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 目录
|
||||
|
||||
- [NewHashMap](#NewHashMap)
|
||||
- [NewHashMapWithCapacity](#NewHashMapWithCapacity)
|
||||
- [Get](#Get)
|
||||
- [Put](#Put)
|
||||
- [Delete](#Delete)
|
||||
- [Contains](#Contains)
|
||||
- [Iterate](#Iterate)
|
||||
- [Keys](#Keys)
|
||||
- [Values](#Values)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## API 文档
|
||||
|
||||
### <span id="NewHashMap">NewHashMap</span>
|
||||
|
||||
<p>新建默认容量(1 << 10)的HashMap指针实例</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func NewHashMap() *HashMap
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
hm := heap.NewHashMap()
|
||||
fmt.Println(hm)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="NewHashMapWithCapacity">NewHashMapWithCapacity</span>
|
||||
|
||||
<p>新建指定容量和长度的HashMap指针实例.</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func NewHashMapWithCapacity(size, capacity uint64) *HashMap
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
hm := heap.NewHashMapWithCapacity(uint64(100), uint64(1000))
|
||||
fmt.Println(hm)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Get">Get</span>
|
||||
|
||||
<p>在hashmap中根据key获取值</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (hm *HashMap) Get(key any) any
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
hm := heap.NewHashMap()
|
||||
val := hm.Get("a")
|
||||
|
||||
fmt.Println(val) //nil
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Put">Put</span>
|
||||
|
||||
<p>将key-value放入hashmap中</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (hm *HashMap) Put(key any, value any) any
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
hm := heap.NewHashMap()
|
||||
hm.Put("a", 1)
|
||||
|
||||
val := hm.Get("a")
|
||||
fmt.Println(val) //1
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Delete">Delete</span>
|
||||
|
||||
<p>将指定的key从hashmap中删除</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (hm *HashMap) Delete(key any)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
hm := heap.NewHashMap()
|
||||
hm.Put("a", 1)
|
||||
val := hm.Get("a")
|
||||
fmt.Println(val) //1
|
||||
|
||||
hm.Delete("a")
|
||||
val = hm.Get("a")
|
||||
fmt.Println(val) //nil
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Contains">Contains</span>
|
||||
|
||||
<p>判断hashmap中是否包含指定的key</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (hm *HashMap) Contains(key any) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
hm := heap.NewHashMap()
|
||||
hm.Put("a", 1)
|
||||
|
||||
fmt.Println(hm.Contains("a")) //true
|
||||
fmt.Println(hm.Contains("b")) //false
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="Iterate">Iterate</span>
|
||||
|
||||
<p>迭代hashmap,对每个key和value执行iteratee函数</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (hm *HashMap) Iterate(iteratee func(key, value any))
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
hm := heap.NewHashMap()
|
||||
hm.Put("a", 1)
|
||||
hm.Put("b", 2)
|
||||
hm.Put("c", 3)
|
||||
|
||||
hm.Iterate(func(key, value any) {
|
||||
fmt.Println(key)
|
||||
fmt.Println(value)
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Keys">Keys</span>
|
||||
|
||||
<p>返回hashmap所有key的切片 (随机顺序)</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (hm *HashMap) Keys() []any
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
hm := heap.NewHashMap()
|
||||
hm.Put("a", 1)
|
||||
hm.Put("b", 2)
|
||||
hm.Put("c", 3)
|
||||
|
||||
keys := hm.Keys()
|
||||
fmt.Println(keys) //[]interface{"a", "b", "c"}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="Values">Values</span>
|
||||
|
||||
<p>返回hashmap所有值的切片 (随机顺序).</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (hm *HashMap) Values() []any
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
hm := heap.NewHashMap()
|
||||
hm.Put("a", 1)
|
||||
hm.Put("b", 2)
|
||||
hm.Put("c", 3)
|
||||
|
||||
values := hm.Values()
|
||||
fmt.Println(values) //[]interface{2, 1, 3}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
364
docs/api/packages/datastructure/heap.md
Normal file
364
docs/api/packages/datastructure/heap.md
Normal file
@@ -0,0 +1,364 @@
|
||||
# Heap
|
||||
堆,切片实现的二叉堆数据结构。
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/datastructure/heap/maxheap.go](https://github.com/duke-git/lancet/blob/main/datastructure/heap/maxheap.go)
|
||||
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 用法
|
||||
```go
|
||||
import (
|
||||
heap "github.com/duke-git/lancet/v2/datastructure/heap"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 目录
|
||||
|
||||
- [MaxHeap](#MaxHeap)
|
||||
- [Push](#Push)
|
||||
- [Pop](#Pop)
|
||||
- [Peek](#Peek)
|
||||
- [Data](#Data)
|
||||
- [Size](#Size)
|
||||
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## API文档
|
||||
|
||||
### 1. MaxHeap
|
||||
MaxHeap是通过slice实现的二叉堆树,根节点的key既大于等于左子树的key值且大于等于右子树的key值。
|
||||
|
||||
### <span id="NewMaxHeap">NewMaxHeap</span>
|
||||
<p>返回NewMaxHeap指针实例</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
type MaxHeap[T any] struct {
|
||||
data []T
|
||||
comparator constraints.Comparator
|
||||
}
|
||||
func NewMaxHeap[T any](comparator constraints.Comparator) *MaxHeap[T]
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
heap "github.com/duke-git/lancet/v2/datastructure/heap"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
maxHeap := heap.NewMaxHeap[int](&intComparator{})
|
||||
fmt.Println(maxHeap)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="Push">Push</span>
|
||||
<p>向堆中插入数据</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (h *MaxHeap[T]) Push(value T)
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
heap "github.com/duke-git/lancet/v2/datastructure/heap"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
maxHeap := heap.NewMaxHeap[int](&intComparator{})
|
||||
values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11}
|
||||
|
||||
for _, v := range values {
|
||||
maxHeap.Push(v)
|
||||
}
|
||||
|
||||
fmt.Println(maxHeap.Data()) //[]int{12, 9, 11, 4, 8, 10, 7, 1, 3, 5, 6, 2}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="Pop">Pop</span>
|
||||
<p>返回堆中最大值并将其从堆中删除,如果堆为空,返回零值并返回false</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (h *MaxHeap[T]) Pop() (T, bool)
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
heap "github.com/duke-git/lancet/v2/datastructure/heap"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
maxHeap := heap.NewMaxHeap[int](&intComparator{})
|
||||
values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11}
|
||||
|
||||
for _, v := range values {
|
||||
maxHeap.Push(v)
|
||||
}
|
||||
val, ok := maxHeap.Pop()
|
||||
|
||||
fmt.Println(val) //12
|
||||
fmt.Println(ok) //true
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Peek">Peek</span>
|
||||
<p>返回堆中最大值,如果堆为空,返回零值并返回false</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (h *MaxHeap[T]) Peek() (T, bool)
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
heap "github.com/duke-git/lancet/v2/datastructure/heap"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
maxHeap := heap.NewMaxHeap[int](&intComparator{})
|
||||
values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11}
|
||||
|
||||
for _, v := range values {
|
||||
maxHeap.Push(v)
|
||||
}
|
||||
val, ok := maxHeap.Peek()
|
||||
|
||||
fmt.Println(val) //12
|
||||
fmt.Println(maxHeap.Size()) //12
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Data">Data</span>
|
||||
<p>返回堆中全部元素的切片</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (h *MaxHeap[T]) Data() []T
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
heap "github.com/duke-git/lancet/v2/datastructure/heap"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
maxHeap := heap.NewMaxHeap[int](&intComparator{})
|
||||
values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11}
|
||||
|
||||
for _, v := range values {
|
||||
maxHeap.Push(v)
|
||||
}
|
||||
|
||||
fmt.Println(maxHeap.Data()) //[]int{12, 9, 11, 4, 8, 10, 7, 1, 3, 5, 6, 2}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="Size">Size</span>
|
||||
<p>返回堆中元素的数量</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (h *MaxHeap[T]) Size() int
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
heap "github.com/duke-git/lancet/v2/datastructure/heap"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
maxHeap := heap.NewMaxHeap[int](&intComparator{})
|
||||
values := []int{6, 5, 2}
|
||||
|
||||
for _, v := range values {
|
||||
maxHeap.Push(v)
|
||||
}
|
||||
|
||||
fmt.Println(maxHeap.Size()) //3
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="PrintStructure">PrintStructure</span>
|
||||
<p>打印堆的树形结构</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (h *MaxHeap[T]) PrintStructure()
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
heap "github.com/duke-git/lancet/v2/datastructure/heap"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
maxHeap := heap.NewMaxHeap[int](&intComparator{})
|
||||
values := []int{6, 5, 2, 4, 7, 10, 12, 1, 3, 8, 9, 11}
|
||||
|
||||
for _, v := range values {
|
||||
maxHeap.Push(v)
|
||||
}
|
||||
|
||||
fmt.Println(maxHeap.PrintStructure())
|
||||
// 12
|
||||
// 9 11
|
||||
// 4 8 10 7
|
||||
// 1 3 5 6 2
|
||||
}
|
||||
```
|
||||
1017
docs/api/packages/datastructure/link.md
Normal file
1017
docs/api/packages/datastructure/link.md
Normal file
File diff suppressed because it is too large
Load Diff
1116
docs/api/packages/datastructure/list.md
Normal file
1116
docs/api/packages/datastructure/list.md
Normal file
File diff suppressed because it is too large
Load Diff
1387
docs/api/packages/datastructure/queue.md
Normal file
1387
docs/api/packages/datastructure/queue.md
Normal file
File diff suppressed because it is too large
Load Diff
649
docs/api/packages/datastructure/set.md
Normal file
649
docs/api/packages/datastructure/set.md
Normal file
@@ -0,0 +1,649 @@
|
||||
# Set
|
||||
|
||||
Set 集合数据结构,类似列表。Set 中元素不重复。
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/datastructure/set/set.go](https://github.com/duke-git/lancet/blob/main/datastructure/set/set.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 用法
|
||||
|
||||
```go
|
||||
import (
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 目录
|
||||
|
||||
- [NewSet](#NewSet)
|
||||
- [NewSetFromSlice](#NewSetFromSlice)
|
||||
- [Values](#Values)
|
||||
- [Add](#Add)
|
||||
- [AddIfNotExist](#AddIfNotExist)
|
||||
- [AddIfNotExistBy](#AddIfNotExistBy)
|
||||
- [Delete](#Delete)
|
||||
- [Contain](#Contain)
|
||||
- [ContainAll](#ContainAll)
|
||||
- [Clone](#Clone)
|
||||
- [Size](#Size)
|
||||
- [Equal](#Equal)
|
||||
- [Iterate](#Iterate)
|
||||
- [IsEmpty](#IsEmpty)
|
||||
- [Union](#Union)
|
||||
- [Intersection](#Intersection)
|
||||
- [SymmetricDifference](#SymmetricDifference)
|
||||
- [Minus](#Minus)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 文档
|
||||
|
||||
### <span id="NewSet">NewSet</span>
|
||||
|
||||
<p>返回Set结构体对象</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
type Set[T comparable] map[T]bool
|
||||
func NewSet[T comparable](items ...T) Set[T]
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int](1,2,2,3)
|
||||
fmt.Println(st.Values()) //1,2,3
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="NewSetFromSlice">NewSetFromSlice</span>
|
||||
|
||||
<p>基于切片创建集合</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func NewSetFromSlice[T comparable](items []T) Set[T]
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSetFromSlice([]int{1, 2, 2, 3})
|
||||
fmt.Println(st.Values()) //1,2,3
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Values">Values</span>
|
||||
|
||||
<p>获取集合中所有元素的切片</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Values() []T
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int](1,2,2,3)
|
||||
fmt.Println(st.Values()) //1,2,3
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Add">Add</span>
|
||||
|
||||
<p>向集合中添加元素</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Add(items ...T)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int]()
|
||||
st.Add(1, 2, 3)
|
||||
|
||||
fmt.Println(st.Values()) //1,2,3
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="AddIfNotExist">AddIfNotExist</span>
|
||||
|
||||
<p>如果集合中不存在元素,则添加该元素返回true, 如果集合中存在元素, 不做任何操作,返回false</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) AddIfNotExist(item T) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int]()
|
||||
st.Add(1, 2, 3)
|
||||
|
||||
r1 := st.AddIfNotExist(1)
|
||||
r2 := st.AddIfNotExist(4)
|
||||
|
||||
fmt.Println(r1) // false
|
||||
fmt.Println(r2) // true
|
||||
fmt.Println(st.Values()) // 1,2,3,4
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="AddIfNotExistBy">AddIfNotExistBy</span>
|
||||
|
||||
<p>根据checker函数判断元素是否在集合中,如果集合中不存在元素且checker返回true,则添加该元素返回true, 否则不做任何操作,返回false</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) AddIfNotExistBy(item T, checker func(element T) bool) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int]()
|
||||
st.Add(1, 2)
|
||||
|
||||
ok := st.AddIfNotExistBy(3, func(val int) bool {
|
||||
return val%2 != 0
|
||||
})
|
||||
fmt.Println(ok) // true
|
||||
|
||||
|
||||
notOk := st.AddIfNotExistBy(4, func(val int) bool {
|
||||
return val%2 != 0
|
||||
})
|
||||
fmt.Println(notOk) // false
|
||||
|
||||
fmt.Println(st.Values()) // 1, 2, 3
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Delete">Delete</span>
|
||||
|
||||
<p>删除集合中元素</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Delete(items ...T)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int]()
|
||||
st.Add(1, 2, 3)
|
||||
|
||||
set.Delete(3)
|
||||
fmt.Println(st.Values()) //1,2
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Contain">Contain</span>
|
||||
|
||||
<p>判断集合是否包含某个值</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Contain(item T) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int]()
|
||||
st.Add(1, 2, 3)
|
||||
|
||||
fmt.Println(st.Contain(1)) //true
|
||||
fmt.Println(st.Contain(4)) //false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ContainAll">ContainAll</span>
|
||||
|
||||
<p>判断集合是否包含另一个集合</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) ContainAll(other Set[T]) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(1, 2)
|
||||
set3 := set.NewSet(1, 2, 3, 4)
|
||||
|
||||
fmt.Println(set1.ContainAll(set2)) //true
|
||||
fmt.Println(set1.ContainAll(set3)) //false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Size">Size</span>
|
||||
|
||||
<p>获取集合中元素的个数</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Size() int
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
|
||||
fmt.Println(set1.Size()) //3
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Clone">Clone</span>
|
||||
|
||||
<p>克隆一个集合</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Clone() Set[T]
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set1.Clone()
|
||||
|
||||
fmt.Println(set1.Size() == set2.Size()) //true
|
||||
fmt.Println(set1.ContainAll(set2)) //true
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Equal">Equal</span>
|
||||
|
||||
<p>比较两个集合是否相等,包含相同元素为相等</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Equal(other Set[T]) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(1, 2, 3)
|
||||
set3 := set.NewSet(1, 2, 3, 4)
|
||||
|
||||
fmt.Println(set1.Equal(set2)) //true
|
||||
fmt.Println(set1.Equal(set3)) //false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Iterate">Iterate</span>
|
||||
|
||||
<p>迭代结合,在每个元素上调用函数</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Iterate(fn func(item T))
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
arr := []int{}
|
||||
set.Iterate(func(item int) {
|
||||
arr = append(arr, item)
|
||||
})
|
||||
|
||||
fmt.Println(arr) //1,2,3
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="EachWithBreak">EachWithBreak</span>
|
||||
|
||||
<p>遍历集合的元素并为每个元素调用iteratee函数,当iteratee函数返回false时,终止遍历。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) EachWithBreak(iteratee func(item T) bool)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := set.NewSet(1, 2, 3, 4, 5)
|
||||
|
||||
var sum int
|
||||
|
||||
s.EachWithBreak(func(n int) bool {
|
||||
if n > 3 {
|
||||
return false
|
||||
}
|
||||
sum += n
|
||||
return true
|
||||
})
|
||||
|
||||
fmt.Println(sum) //6
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsEmpty">IsEmpty</span>
|
||||
|
||||
<p>判断集合是否为空</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) IsEmpty() bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet()
|
||||
|
||||
fmt.Println(set1.IsEmpty()) //false
|
||||
fmt.Println(set2.IsEmpty()) //true
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Union">Union</span>
|
||||
|
||||
<p>求两个集合的并集</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Union(other Set[T]) Set[T]
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(2, 3, 4, 5)
|
||||
set3 := set1.Union(set2)
|
||||
|
||||
fmt.Println(set3.Values()) //1,2,3,4,5
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Intersection">Intersection</span>
|
||||
|
||||
<p>求两个集合的交集</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Intersection(other Set[T]) Set[T]
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(2, 3, 4, 5)
|
||||
set3 := set1.Intersection(set2)
|
||||
|
||||
fmt.Println(set3.Values()) //2,3
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="SymmetricDifference">SymmetricDifference</span>
|
||||
|
||||
<p>返回一个集合,其中元素在第一个集合或第二个集合中,且不同时存在于两个集合中</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) SymmetricDifference(other Set[T]) Set[T]
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(2, 3, 4, 5)
|
||||
set3 := set1.SymmetricDifference(set2)
|
||||
|
||||
fmt.Println(set3.Values()) //1,4,5
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Minus">Minus</span>
|
||||
|
||||
<p>创建一个集合,其元素在原始集中但不在比较集中</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Minus(comparedSet Set[T]) Set[T]
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(2, 3, 4, 5)
|
||||
set3 := set.NewSet(2, 3)
|
||||
|
||||
res1 := set1.Minus(set2)
|
||||
fmt.Println(res1.Values()) //1
|
||||
|
||||
res2 := set2.Minus(set3)
|
||||
fmt.Println(res2.Values()) //4,5
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Pop">Pop</span>
|
||||
|
||||
<p>删除并返回集合中的顶部元素</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Pop() (v T, ok bool)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := set.NewSet[int]()
|
||||
s.Add(1)
|
||||
s.Add(2)
|
||||
s.Add(3)
|
||||
|
||||
val, ok = s.Pop()
|
||||
|
||||
fmt.Println(val) // 3
|
||||
fmt.Println(ok) // true
|
||||
}
|
||||
```
|
||||
611
docs/api/packages/datastructure/stack.md
Normal file
611
docs/api/packages/datastructure/stack.md
Normal file
@@ -0,0 +1,611 @@
|
||||
# Stack
|
||||
栈数据结构,包括ArrayStack(数组栈)和LinkedStack(链表栈)。
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/datastructure/stack/arraystack.go](https://github.com/duke-git/lancet/blob/main/datastructure/stack/arraystack.go)
|
||||
- [https://github.com/duke-git/lancet/blob/main/datastructure/stack/linkedstack.go](https://github.com/duke-git/lancet/blob/main/datastructure/stack/linkedstack.go)
|
||||
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 用法
|
||||
```go
|
||||
import (
|
||||
stack "github.com/duke-git/lancet/v2/datastructure/stack"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 目录
|
||||
|
||||
### 1. ArrayStack(数组栈)
|
||||
|
||||
- [NewArrayStack](#NewArrayStack)
|
||||
- [Push](#ArrayStack_Push)
|
||||
- [Pop](#ArrayStack_Pop)
|
||||
- [Peak](#ArrayStack_Peak)
|
||||
- [Data](#ArrayStack_Data)
|
||||
- [Size](#ArrayStack_Size)
|
||||
- [IsEmpty](#ArrayStack_IsEmpty)
|
||||
- [Clear](#ArrayStack_Clear)
|
||||
|
||||
### 2. LinkedStack(链表栈)
|
||||
|
||||
- [NewLinkedStack](#NewLinkedStack)
|
||||
- [Push](#LinkedStack_Push)
|
||||
- [Pop](#LinkedStack_Pop)
|
||||
- [Peak](#LinkedStack_Peak)
|
||||
- [Data](#LinkedStack_Data)
|
||||
- [Size](#LinkedStack_Size)
|
||||
- [IsEmpty](#LinkedStack_IsEmpty)
|
||||
- [Clear](#LinkedStack_Clear)
|
||||
- [Print](#LinkedStack_Print)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 文档
|
||||
|
||||
### 1. ArrayStack
|
||||
用切片实现栈结构
|
||||
|
||||
### <span id="NewArrayStack">NewArrayStack</span>
|
||||
<p>返回ArrayStack指针实例</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
type ArrayStack[T any] struct {
|
||||
data []T
|
||||
length int
|
||||
}
|
||||
func NewArrayStack[T any]() *ArrayStack[T]
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stack "github.com/duke-git/lancet/v2/datastructure/stack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sk := stack.NewArrayStack[int]()
|
||||
fmt.Println(sk)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="ArrayStack_Push">Push</span>
|
||||
<p>将元素加入数组栈</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s *ArrayStack[T]) Push(value T)
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stack "github.com/duke-git/lancet/v2/datastructure/stack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sk := stack.NewArrayStack[int]()
|
||||
sk.Push(1)
|
||||
sk.Push(2)
|
||||
sk.Push(3)
|
||||
|
||||
fmt.Println(sk.Data()) //[]int{3, 2, 1}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="ArrayStack_Pop">Pop</span>
|
||||
<p>删除栈顶元素并返回该元素指针</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s *ArrayStack[T]) Pop() (*T, error)
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stack "github.com/duke-git/lancet/v2/datastructure/stack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sk := stack.NewArrayStack[int]()
|
||||
sk.Push(1)
|
||||
sk.Push(2)
|
||||
sk.Push(3)
|
||||
|
||||
val, err := sk.Pop()
|
||||
fmt.Println(err) //nil
|
||||
fmt.Println(*val) //3
|
||||
|
||||
fmt.Println(sk.Data()) //[]int{2, 1}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="ArrayStack_Peak">Peak</span>
|
||||
<p>返回栈顶元素指针</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s *ArrayStack[T]) Peak() (*T, error)
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stack "github.com/duke-git/lancet/v2/datastructure/stack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sk := stack.NewArrayStack[int]()
|
||||
sk.Push(1)
|
||||
sk.Push(2)
|
||||
sk.Push(3)
|
||||
|
||||
val, err := sk.Peak()
|
||||
fmt.Println(err) //nil
|
||||
fmt.Println(*val) //3
|
||||
|
||||
fmt.Println(sk.Data()) //[]int{3, 2, 1}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="ArrayStack_Data">Data</span>
|
||||
<p>返回栈中所有元素组成的切片</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s *ArrayStack[T]) Data() []T
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stack "github.com/duke-git/lancet/v2/datastructure/stack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sk := stack.NewArrayStack[int]()
|
||||
sk.Push(1)
|
||||
sk.Push(2)
|
||||
sk.Push(3)
|
||||
|
||||
fmt.Println(sk.Data()) //[]int{3, 2, 1}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="ArrayStack_Size">Size</span>
|
||||
<p>返回栈中元素的数量</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s *ArrayStack[T]) Size() int
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stack "github.com/duke-git/lancet/v2/datastructure/stack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sk := stack.NewArrayStack[int]()
|
||||
sk.Push(1)
|
||||
sk.Push(2)
|
||||
sk.Push(3)
|
||||
|
||||
fmt.Println(sk.Size()) //3
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="ArrayStack_IsEmpty">IsEmpty</span>
|
||||
<p>判断栈是否为空</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s *ArrayStack[T]) IsEmpty() bool
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stack "github.com/duke-git/lancet/v2/datastructure/stack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sk := stack.NewArrayStack[int]()
|
||||
fmt.Println(sk.IsEmpty()) //true
|
||||
|
||||
sk.Push(1)
|
||||
sk.Push(2)
|
||||
sk.Push(3)
|
||||
|
||||
fmt.Println(sk.IsEmpty()) //false
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="ArrayStack_Clear">Clear</span>
|
||||
<p>清空栈元素,使栈为空</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s *ArrayStack[T]) Clear()
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stack "github.com/duke-git/lancet/v2/datastructure/stack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sk := stack.NewArrayStack[int]()
|
||||
|
||||
sk.Push(1)
|
||||
sk.Push(2)
|
||||
sk.Push(3)
|
||||
|
||||
sk.Clear()
|
||||
|
||||
fmt.Println(sk.Data()) //[]int{}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 2. LinkedStack
|
||||
链表实现的栈结构。
|
||||
|
||||
### <span id="NewLinkedStack">NewLinkedStack</span>
|
||||
<p>返回LinkedStack指针实例</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
type StackNode[T any] struct {
|
||||
Value T
|
||||
Next *StackNode[T]
|
||||
}
|
||||
type LinkedStack[T any] struct {
|
||||
top *datastructure.StackNode[T]
|
||||
length int
|
||||
}
|
||||
func NewLinkedStack[T any]() *LinkedStack[T]
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stack "github.com/duke-git/lancet/v2/datastructure/stack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sk := stack.NewLinkedStack[int]()
|
||||
fmt.Println(sk)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="LinkedStack_Push">Push</span>
|
||||
<p>将元素加入链表栈</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s *LinkedStack[T]) Push(value T)
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stack "github.com/duke-git/lancet/v2/datastructure/stack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sk := stack.NewLinkedStack[int]()
|
||||
sk.Push(1)
|
||||
sk.Push(2)
|
||||
sk.Push(3)
|
||||
|
||||
fmt.Println(sk.Data()) //[]int{3, 2, 1}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="LinkedStack_Pop">Pop</span>
|
||||
<p>删除栈顶元素并返回该元素指针</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s *LinkedStack[T]) Pop() (*T, error)
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stack "github.com/duke-git/lancet/v2/datastructure/stack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sk := stack.NewLinkedStack[int]()
|
||||
sk.Push(1)
|
||||
sk.Push(2)
|
||||
sk.Push(3)
|
||||
|
||||
val, err := sk.Pop()
|
||||
fmt.Println(err) //nil
|
||||
fmt.Println(*val) //3
|
||||
|
||||
fmt.Println(sk.Data()) //[]int{2, 1}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="LinkedStack_Peak">Peak</span>
|
||||
<p>返回栈顶元素指针</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s *LinkedStack[T]) Peak() (*T, error)
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stack "github.com/duke-git/lancet/v2/datastructure/stack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sk := stack.NewLinkedStack[int]()
|
||||
sk.Push(1)
|
||||
sk.Push(2)
|
||||
sk.Push(3)
|
||||
|
||||
val, err := sk.Peak()
|
||||
fmt.Println(err) //nil
|
||||
fmt.Println(*val) //3
|
||||
|
||||
fmt.Println(sk.Data()) //[]int{3, 2, 1}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="LinkedStack_Data">Data</span>
|
||||
<p>返回栈中所有元素组成的切片</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s *LinkedStack[T]) Data() []T
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stack "github.com/duke-git/lancet/v2/datastructure/stack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sk := stack.NewLinkedStack[int]()
|
||||
sk.Push(1)
|
||||
sk.Push(2)
|
||||
sk.Push(3)
|
||||
|
||||
fmt.Println(sk.Data()) //[]int{3, 2, 1}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="LinkedStack_Size">Size</span>
|
||||
<p>返回栈中元素的数量</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s *LinkedStack[T]) Size() int
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stack "github.com/duke-git/lancet/v2/datastructure/stack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sk := stack.NewLinkedStack[int]()
|
||||
sk.Push(1)
|
||||
sk.Push(2)
|
||||
sk.Push(3)
|
||||
|
||||
fmt.Println(sk.Size()) //3
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="LinkedStack_IsEmpty">IsEmpty</span>
|
||||
<p>判断栈是否为空</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s *LinkedStack[T]) IsEmpty() bool
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stack "github.com/duke-git/lancet/v2/datastructure/stack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sk := stack.NewLinkedStack[int]()
|
||||
fmt.Println(sk.IsEmpty()) //true
|
||||
|
||||
sk.Push(1)
|
||||
sk.Push(2)
|
||||
sk.Push(3)
|
||||
|
||||
fmt.Println(sk.IsEmpty()) //false
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="LinkedStack_Clear">Clear</span>
|
||||
<p>清空栈元素,使栈为空</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s *LinkedStack[T]) Clear()
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stack "github.com/duke-git/lancet/v2/datastructure/stack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sk := stack.NewLinkedStack[int]()
|
||||
|
||||
sk.Push(1)
|
||||
sk.Push(2)
|
||||
sk.Push(3)
|
||||
|
||||
sk.Clear()
|
||||
|
||||
fmt.Println(sk.Data()) //[]int{}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="LinkedStack_Print">Print</span>
|
||||
<p>打印链表栈结构</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s *LinkedStack[T]) Print()
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stack "github.com/duke-git/lancet/v2/datastructure/stack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sk := stack.NewLinkedStack[int]()
|
||||
|
||||
sk.Push(1)
|
||||
sk.Push(2)
|
||||
sk.Push(3)
|
||||
|
||||
|
||||
sk.Print() //[ &{Value:3 Next:0xc000010260}, &{Value:2 Next:0xc000010250}, &{Value:1 Next:<nil>}, ]
|
||||
}
|
||||
```
|
||||
526
docs/api/packages/datastructure/tree.md
Normal file
526
docs/api/packages/datastructure/tree.md
Normal file
@@ -0,0 +1,526 @@
|
||||
# Tree
|
||||
树是树节点的集合。 每个树节点都有一个值,一个指向左节点的左指针和一个指向右节点的右指针。
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/datastructure/tree/bstree.go](https://github.com/duke-git/lancet/blob/main/datastructure/tree/bstree.go)
|
||||
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 用法
|
||||
```go
|
||||
import (
|
||||
tree "github.com/duke-git/lancet/v2/datastructure/tree"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 目录
|
||||
|
||||
### 1. BSTree
|
||||
|
||||
- [NewBSTree](#NewBSTree)
|
||||
- [Insert](#BSTree_Insert)
|
||||
- [Delete](#BSTree_Delete)
|
||||
- [PreOrderTraverse](#BSTree_PreOrderTraverse)
|
||||
- [InOrderTraverse](#BSTree_InOrderTraverse)
|
||||
- [PostOrderTraverse](#BSTree_PostOrderTraverse)
|
||||
- [LevelOrderTraverse](#BSTree_LevelOrderTraverse)
|
||||
- [Depth](#BSTree_Depth)
|
||||
- [HasSubTree](#BSTree_HasSubTree)
|
||||
- [Print](#BSTree_Print)
|
||||
|
||||
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 文档
|
||||
|
||||
## 1. BSTree
|
||||
BSTree是一种二叉搜索树数据结构,其中每个节点有两个孩子,分别称为左孩子和右孩子。 在 BSTree 中:leftNode < rootNode < rightNode。 T类型应该实现constraints.Comparator。
|
||||
|
||||
### <span id="NewBSTree">NewBSTree</span>
|
||||
<p>返回BSTree指针实例</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func NewBSTree[T any](rootData T, comparator constraints.Comparator) *BSTree[T]
|
||||
|
||||
type BSTree[T any] struct {
|
||||
root *datastructure.TreeNode[T]
|
||||
comparator constraints.Comparator
|
||||
}
|
||||
|
||||
type TreeNode[T any] struct {
|
||||
Value T
|
||||
Left *TreeNode[T]
|
||||
Right *TreeNode[T]
|
||||
}
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
tree "github.com/duke-git/lancet/v2/datastructure/tree"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
bstree := tree.NewBSTree(6, &intComparator{})
|
||||
fmt.Println(bstree) //
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="BSTree_Insert">Insert</span>
|
||||
<p>将值插入二叉搜索树</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (t *BSTree[T]) Insert(data T)
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
tree "github.com/duke-git/lancet/v2/datastructure/tree"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
bstree := tree.NewBSTree(6, &intComparator{})
|
||||
bstree.Insert(7)
|
||||
bstree.Insert(5)
|
||||
bstree.Insert(2)
|
||||
bstree.Insert(4)
|
||||
|
||||
fmt.Println(bstree.PreOrderTraverse()) //6, 5, 2, 4, 7
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="BSTree_Delete">Delete</span>
|
||||
<p>删除插入二叉搜索树中指定的值</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (t *BSTree[T]) Delete(data T)
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
tree "github.com/duke-git/lancet/v2/datastructure/tree"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
bstree := tree.NewBSTree(6, &intComparator{})
|
||||
bstree.Insert(7)
|
||||
bstree.Insert(5)
|
||||
bstree.Insert(2)
|
||||
bstree.Insert(4)
|
||||
|
||||
bstree.Delete(4)
|
||||
|
||||
fmt.Println(bstree.PreOrderTraverse()) //2, 5, 6, 7
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="BSTree_PreOrderTraverse">PreOrderTraverse</span>
|
||||
<p>按前序遍历树节点</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (t *BSTree[T]) PreOrderTraverse() []T
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
tree "github.com/duke-git/lancet/v2/datastructure/tree"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
bstree := tree.NewBSTree(6, &intComparator{})
|
||||
bstree.Insert(7)
|
||||
bstree.Insert(5)
|
||||
bstree.Insert(2)
|
||||
bstree.Insert(4)
|
||||
|
||||
fmt.Println(bstree.PreOrderTraverse()) //6, 5, 2, 4, 7
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="BSTree_InOrderTraverse">InOrderTraverse</span>
|
||||
<p>按中序遍历树节点</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (t *BSTree[T]) InOrderTraverse() []T
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
tree "github.com/duke-git/lancet/v2/datastructure/tree"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
bstree := tree.NewBSTree(6, &intComparator{})
|
||||
bstree.Insert(7)
|
||||
bstree.Insert(5)
|
||||
bstree.Insert(2)
|
||||
bstree.Insert(4)
|
||||
|
||||
fmt.Println(bstree.InOrderTraverse()) //2, 4, 5, 6, 7
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="BSTree_PostOrderTraverse">PostOrderTraverse</span>
|
||||
<p>按后序遍历树节点</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (t *BSTree[T]) PostOrderTraverse() []T
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
tree "github.com/duke-git/lancet/v2/datastructure/tree"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
bstree := tree.NewBSTree(6, &intComparator{})
|
||||
bstree.Insert(7)
|
||||
bstree.Insert(5)
|
||||
bstree.Insert(2)
|
||||
bstree.Insert(4)
|
||||
|
||||
fmt.Println(bstree.PostOrderTraverse()) //5, 2, 4, 7, 6
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="BSTree_LevelOrderTraverse">LevelOrderTraverse</span>
|
||||
<p>按节点层次遍历树节点</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (t *BSTree[T]) LevelOrderTraverse() []T
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
tree "github.com/duke-git/lancet/v2/datastructure/tree"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
bstree := tree.NewBSTree(6, &intComparator{})
|
||||
bstree.Insert(7)
|
||||
bstree.Insert(5)
|
||||
bstree.Insert(2)
|
||||
bstree.Insert(4)
|
||||
|
||||
fmt.Println(bstree.LevelOrderTraverse()) //6, 5, 7, 2, 4
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="BSTree_Depth">Depth</span>
|
||||
<p>获取树的深度</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (t *BSTree[T]) Depth() int
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
tree "github.com/duke-git/lancet/v2/datastructure/tree"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
bstree := tree.NewBSTree(6, &intComparator{})
|
||||
bstree.Insert(7)
|
||||
bstree.Insert(5)
|
||||
bstree.Insert(2)
|
||||
bstree.Insert(4)
|
||||
|
||||
fmt.Println(bstree.Depth()) //4
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="BSTree_HasSubTree">HasSubTree</span>
|
||||
<p>判断给定树是否是子树</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (t *BSTree[T]) HasSubTree(subTree *BSTree[T]) bool
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
tree "github.com/duke-git/lancet/v2/datastructure/tree"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
superTree := tree.NewBSTree(8, &intComparator{})
|
||||
superTree.Insert(4)
|
||||
superTree.Insert(5)
|
||||
superTree.Insert(6)
|
||||
superTree.Insert(9)
|
||||
superTree.Insert(4)
|
||||
|
||||
subTree := tree.NewBSTree(5, &intComparator{})
|
||||
subTree.Insert(4)
|
||||
subTree.Insert(6)
|
||||
|
||||
fmt.Println(superTree.HasSubTree(subTree)) //true
|
||||
fmt.Println(subTree.HasSubTree(superTree)) //false
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="BSTree_Print">Print</span>
|
||||
<p>打印树结构</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (t *BSTree[T]) Print()
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
tree "github.com/duke-git/lancet/v2/datastructure/tree"
|
||||
)
|
||||
|
||||
type intComparator struct{}
|
||||
|
||||
func (c *intComparator) Compare(v1, v2 any) int {
|
||||
val1, _ := v1.(int)
|
||||
val2, _ := v2.(int)
|
||||
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
bstree := tree.NewBSTree(6, &intComparator{})
|
||||
bstree.Insert(7)
|
||||
bstree.Insert(5)
|
||||
bstree.Insert(2)
|
||||
bstree.Insert(4)
|
||||
|
||||
fmt.Println(bstree.Print())
|
||||
// 6
|
||||
// / \
|
||||
// / \
|
||||
// / \
|
||||
// / \
|
||||
// 5 7
|
||||
// /
|
||||
// /
|
||||
// 2
|
||||
// \
|
||||
// 4
|
||||
}
|
||||
```
|
||||
1467
docs/api/packages/datetime.md
Normal file
1467
docs/api/packages/datetime.md
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user