Compare commits
838
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24e691925c | ||
|
|
bcf51aed83 | ||
|
|
1a11ce6211 | ||
|
|
e869e3c534 | ||
|
|
7f0593cd2d | ||
|
|
e4c41718d8 | ||
|
|
7234553990 | ||
|
|
5761efdb73 | ||
|
|
1133192a86 | ||
|
|
af3288043a | ||
|
|
24a348f6a8 | ||
|
|
5528b6c231 | ||
|
|
245bd1eb17 | ||
|
|
03506db76c | ||
|
|
cb5ef26020 | ||
|
|
2493ae4c2c | ||
|
|
2bd88344ad | ||
|
|
4950980d69 | ||
|
|
2361a0dd6e | ||
|
|
152cdd1750 | ||
|
|
31797a5831 | ||
|
|
9308c42cff | ||
|
|
c096cd34b9 | ||
|
|
5fc0808f28 | ||
|
|
e6866ee980 | ||
|
|
45631d30b0 | ||
|
|
0f432a0844 | ||
|
|
df59bc7194 | ||
|
|
ff4706e450 | ||
|
|
fc013bd04c | ||
|
|
7e29a8b927 | ||
|
|
67ae7a0b6c | ||
|
|
bd5a64bac0 | ||
|
|
1bd85d8baf | ||
|
|
fe34b08ece | ||
|
|
33048f88b8 | ||
|
|
0ec01f4e78 | ||
|
|
d0ebf06ff8 | ||
|
|
687b249034 | ||
|
|
d1528dcff0 | ||
|
|
1c31cf6319 | ||
|
|
67f0c9bef0 | ||
|
|
d54c366150 | ||
|
|
4ff535f883 | ||
|
|
58b15f9452 | ||
|
|
dedca59ac9 | ||
|
|
3cbddfe545 | ||
|
|
e3cae69495 | ||
|
|
aee40316f8 | ||
|
|
d754f9ae89 | ||
|
|
3c50b3a9e0 | ||
|
|
fb312a71f7 | ||
|
|
136d79eaa3 | ||
|
|
834336499a | ||
|
|
c9da8237df | ||
|
|
9638dcda0a | ||
|
|
756c5a2604 | ||
|
|
a9c31652b6 | ||
|
|
32a76901a9 | ||
|
|
a5e11c7489 | ||
|
|
f2b12014e1 | ||
|
|
60fcaebfdb | ||
|
|
32fe2cb659 | ||
|
|
1207d54fdd | ||
|
|
3932884688 | ||
|
|
19a2042746 | ||
|
|
4c6eb137da | ||
|
|
57ec2ff915 | ||
|
|
77a161a087 | ||
|
|
0642402449 | ||
|
|
50d377d9fe | ||
|
|
f5211b0697 | ||
|
|
fd4ea46fd7 | ||
|
|
8d8546868d | ||
|
|
8ce547edeb | ||
|
|
a17c48aed6 | ||
|
|
d12db3e7b8 | ||
|
|
15b87ae297 | ||
|
|
02fdf59839 | ||
|
|
d9da02b7a8 | ||
|
|
8f2ad6418d | ||
|
|
ff984425a3 | ||
|
|
ac1058359f | ||
|
|
9afbca3001 | ||
|
|
ec3f17cb9c | ||
|
|
73b9d5c5f9 | ||
|
|
ecc8591c95 | ||
|
|
696b67e4b1 | ||
|
|
266a5116a1 | ||
|
|
131f2be857 | ||
|
|
be7b3a9952 | ||
|
|
bb31b1785b | ||
|
|
2a60f4b1e9 | ||
|
|
33a4fb5a1a | ||
|
|
aece6e8b6c | ||
|
|
7bf55dd14f | ||
|
|
e158f17c2b | ||
|
|
c5027d9478 | ||
|
|
36c1d82146 | ||
|
|
bd4f404d45 | ||
|
|
43d39844f7 | ||
|
|
e041a4d212 | ||
|
|
433b923ea7 | ||
|
|
f8f1c72b44 | ||
|
|
542716e216 | ||
|
|
b35958d024 | ||
|
|
9ee3541655 | ||
|
|
bf7d84c12a | ||
|
|
34c691087e | ||
|
|
08c383012f | ||
|
|
e2420495f3 | ||
|
|
d530c5eda7 | ||
|
|
ef7420ecf6 | ||
|
|
c905a41e2a | ||
|
|
42ff4b5bf0 | ||
|
|
4fb74a32cc | ||
|
|
c741465328 | ||
|
|
fbca537a40 | ||
|
|
83420b0199 | ||
|
|
33d3ba1b45 | ||
|
|
497f85a236 | ||
|
|
a624c302ab | ||
|
|
cebe21a3af | ||
|
|
9eb679d70a | ||
|
|
6d84443db8 | ||
|
|
da8a1f242c | ||
|
|
946d98b71f | ||
|
|
dff51fc707 | ||
|
|
7d954dd5d1 | ||
|
|
c6300a5da8 | ||
|
|
9359daa0d9 | ||
|
|
2322e9cff7 | ||
|
|
a876e1e348 | ||
|
|
6a863c8f71 | ||
|
|
392b006b06 | ||
|
|
96289f42b7 | ||
|
|
1b69c2441c | ||
|
|
8ca85a4918 | ||
|
|
2a31031cbc | ||
|
|
d148cd8ccc | ||
|
|
d1cc1828b8 | ||
|
|
069e8cf122 | ||
|
|
45cbcaca6d | ||
|
|
102a2db1f3 | ||
|
|
9f81c85ca7 | ||
|
|
ba4a6fc0c5 | ||
|
|
aa803ce2ff | ||
|
|
a027a60f5d | ||
|
|
270649535e | ||
|
|
cf80ba71f4 | ||
|
|
b74df18a4a | ||
|
|
5cd2906a39 | ||
|
|
bc37b69d17 | ||
|
|
94f6e400ad | ||
|
|
b95a6ccf80 | ||
|
|
7df9c1b6e4 | ||
|
|
75348c0158 | ||
|
|
75fb14acaf | ||
|
|
5350315b68 | ||
|
|
658e39c270 | ||
|
|
ef7ce6c7e1 | ||
|
|
509e2411bf | ||
|
|
65c906f951 | ||
|
|
1f159e8233 | ||
|
|
936c76119d | ||
|
|
f45865606a | ||
|
|
cfc9776bae | ||
|
|
0cb7ed9e4e | ||
|
|
4b07609458 | ||
|
|
e41e58e781 | ||
|
|
f5030f1c2c | ||
|
|
2a48fb8e87 | ||
|
|
df6dbc5fa4 | ||
|
|
4b1d2839e8 | ||
|
|
a892f80e86 | ||
|
|
b2a79855ae | ||
|
|
ff4974178a | ||
|
|
d7100fd9bc | ||
|
|
0bfb40ae51 | ||
|
|
11c83670d6 | ||
|
|
68ff4f3842 | ||
|
|
ab25cd09ed | ||
|
|
8f05b8f982 | ||
|
|
63ae2f64cf | ||
|
|
105103fae0 | ||
|
|
70f4792ab1 | ||
|
|
defd9fa322 | ||
|
|
e884d0fda6 | ||
|
|
5f6a8fdc20 | ||
|
|
196a9ddbb0 | ||
|
|
746140bd11 | ||
|
|
7b99a5fbac | ||
|
|
b74c31e520 | ||
|
|
221f43e4bd | ||
|
|
a17333d73e | ||
|
|
207b43499c | ||
|
|
0c0de17b38 | ||
|
|
77882e6086 | ||
|
|
19a9834843 | ||
|
|
23dab30ca5 | ||
|
|
b5d7ce8ebe | ||
|
|
bf4eb4b269 | ||
|
|
a5edb6807e | ||
|
|
ecadf30fe7 | ||
|
|
515f0db5b4 | ||
|
|
c2f367cf70 | ||
|
|
f21dfea965 | ||
|
|
b84cad4db0 | ||
|
|
16ae019c8c | ||
|
|
bd2051febd | ||
|
|
6fb1e03ed4 | ||
|
|
8d41a762b6 | ||
|
|
6d3003716c | ||
|
|
2c87c3bac3 | ||
|
|
739c525a98 | ||
|
|
ab287ebf40 | ||
|
|
ab6bcab78a | ||
|
|
6e317896e9 | ||
|
|
b08ee3ff81 | ||
|
|
17fd09102e | ||
|
|
a598cd2b18 | ||
|
|
55e434d67a | ||
|
|
04d4b5d8a0 | ||
|
|
7ea00bcb78 | ||
|
|
0ab56ffde8 | ||
|
|
e7e945533e | ||
|
|
7fd1047832 | ||
|
|
19dfa88258 | ||
|
|
65923b5c20 | ||
|
|
ac731aa50c | ||
|
|
2aa3182476 | ||
|
|
529c386943 | ||
|
|
b659da8a4b | ||
|
|
eba98717c9 | ||
|
|
e4dba99cc0 | ||
|
|
454e688c3d | ||
|
|
54752deaa1 | ||
|
|
a3cf37cb2e | ||
|
|
6459d11d32 | ||
|
|
15f2fabaaf | ||
|
|
d6030b8d68 | ||
|
|
e1757ee726 | ||
|
|
9fed75d59c | ||
|
|
5fe15475a4 | ||
|
|
c7f6f4f48d | ||
|
|
747c6c2714 | ||
|
|
2951f128f6 | ||
|
|
53cb66eeaf | ||
|
|
bcf8f798e2 | ||
|
|
7406176fad | ||
|
|
34ba5678c3 | ||
|
|
da0b78c67a | ||
|
|
47e64ae503 | ||
|
|
520bb74626 | ||
|
|
4beef5cc66 | ||
|
|
ba575f55ec | ||
|
|
2012ce02e8 | ||
|
|
c67e2c2a5a | ||
|
|
4b1ce250c1 | ||
|
|
0401a07507 | ||
|
|
c12265499a | ||
|
|
0e341832e0 | ||
|
|
489e2e6ad5 | ||
|
|
941f637bca | ||
|
|
fc0cb704f2 | ||
|
|
960c0cbddf | ||
|
|
9f67d86b30 | ||
|
|
66f7d83baa | ||
|
|
b44e87c6e8 | ||
|
|
6da7f17c4a | ||
|
|
b4f45d1e79 | ||
|
|
7d766bf7c7 | ||
|
|
75dc7e6671 | ||
|
|
3fd887fc57 | ||
|
|
128447a681 | ||
|
|
23bae932c7 | ||
|
|
9701998f82 | ||
|
|
0289c50ad9 | ||
|
|
50490f5b26 | ||
|
|
d12f802027 | ||
|
|
ac7097b4d0 | ||
|
|
66087e4332 | ||
|
|
3706f9bcb8 | ||
|
|
c505218896 | ||
|
|
6186a746e0 | ||
|
|
3e98bae5ec | ||
|
|
2e1e8f764e | ||
|
|
a7492f8612 | ||
|
|
123b1f01e4 | ||
|
|
865f62e3eb | ||
|
|
3ea93f52ee | ||
|
|
b53e545ebc | ||
|
|
157a4c891c | ||
|
|
ad9ea07309 | ||
|
|
fc483cdfc6 | ||
|
|
9033838cf2 | ||
|
|
8e5d2d5905 | ||
|
|
18aa66dabb | ||
|
|
a2f7b78453 | ||
|
|
5c026cbe1d | ||
|
|
dc51476897 | ||
|
|
39eaa577e0 | ||
|
|
1f006481ee | ||
|
|
60faabcbe2 | ||
|
|
d3f1eaf1a3 | ||
|
|
f568e76fd4 | ||
|
|
e947223aaa | ||
|
|
8311162be3 | ||
|
|
75523556e8 | ||
|
|
c82b5d4982 | ||
|
|
1c3158099c | ||
|
|
bdbca75dfa | ||
|
|
124b189cc0 | ||
|
|
de38b46392 | ||
|
|
e1975644d6 | ||
|
|
d9fd27a9e8 | ||
|
|
32425c5561 | ||
|
|
8d20923881 | ||
|
|
3a35b8b26c | ||
|
|
36c93b755a | ||
|
|
ea8c3debea | ||
|
|
b2425b2a25 | ||
|
|
49bc74e7a0 | ||
|
|
51c932164f | ||
|
|
19e82e93b1 | ||
|
|
3f785eaecf | ||
|
|
e59c0f38d9 | ||
|
|
64004c6bc0 | ||
|
|
422332de7e | ||
|
|
5a15ba7451 | ||
|
|
d3686bb1e2 | ||
|
|
3a6eeef580 | ||
|
|
1dc5c6b8a8 | ||
|
|
3532a560d8 | ||
|
|
d2d894d808 | ||
|
|
2aa38bfc4b | ||
|
|
9c3cee9ae4 | ||
|
|
fc521b5f9d | ||
|
|
5253368acc | ||
|
|
51cfc3d4be | ||
|
|
80bffd93e7 | ||
|
|
df4f22e899 | ||
|
|
8cc70843a5 | ||
|
|
70c841f23a | ||
|
|
7b22e09805 | ||
|
|
c5838c143c | ||
|
|
eaf71db7c9 | ||
|
|
b322b527b3 | ||
|
|
cc4b231875 | ||
|
|
05642a3e17 | ||
|
|
7dcc6bb579 | ||
|
|
f15c416e59 | ||
|
|
6fa97eeec7 | ||
|
|
03bbf273b3 | ||
|
|
1e376cd3a6 | ||
|
|
49cf939c04 | ||
|
|
338394f8c3 | ||
|
|
575b62d77b | ||
|
|
57fc0eb5b1 | ||
|
|
3a19fe3663 | ||
|
|
0c049179b4 | ||
|
|
e22c873ec4 | ||
|
|
d644ebab09 | ||
|
|
2fdc578a88 | ||
|
|
aeb3a3f7b5 | ||
|
|
044b7ce070 | ||
|
|
815e538f10 | ||
|
|
758233f001 | ||
|
|
f4f4fda520 | ||
|
|
1d77aeb69c | ||
|
|
29dbfc647d | ||
|
|
55d9514e83 | ||
|
|
46bd7956a3 | ||
|
|
e1ee394c26 | ||
|
|
19884ade99 | ||
|
|
f0a88061db | ||
|
|
6057138466 | ||
|
|
aaaa6556f3 | ||
|
|
4745431cda | ||
|
|
0455a948a9 | ||
|
|
bf3e249237 | ||
|
|
fb649e9525 | ||
|
|
9d1e2d9f46 | ||
|
|
9876d93b60 | ||
|
|
b3dd05580b | ||
|
|
32847f33fd | ||
|
|
bff9723fe3 | ||
|
|
d114648c16 | ||
|
|
44d0da02d0 | ||
|
|
c25107eff3 | ||
|
|
af5c36d2a8 | ||
|
|
0828a67145 | ||
|
|
617fb84983 | ||
|
|
6f8ac2b61c | ||
|
|
6f2b4b96cf | ||
|
|
3cc288a169 | ||
|
|
0bbbf3eb3b | ||
|
|
4b1b56fee8 | ||
|
|
154fc59e93 | ||
|
|
218c4c128c | ||
|
|
53f1af0cab | ||
|
|
f9577a38dc | ||
|
|
fadc7d9ba5 | ||
|
|
1e4b2133f6 | ||
|
|
bfefa6d016 | ||
|
|
8b66472949 | ||
|
|
3b3aa94c4e | ||
|
|
dc05275670 | ||
|
|
7921082ece | ||
|
|
efd6a29909 | ||
|
|
88c44b303d | ||
|
|
e7dbb8ccdc | ||
|
|
fe2a743c8d | ||
|
|
1f5c124ac4 | ||
|
|
64a5bc038a | ||
|
|
5d9396334c | ||
|
|
ec160f1f0a | ||
|
|
bbaeca96eb | ||
|
|
3ab779895f | ||
|
|
203c7360e7 | ||
|
|
a831f174ef | ||
|
|
153091f52f | ||
|
|
35d3af5039 | ||
|
|
57e8cd6eab | ||
|
|
fc123a71af | ||
|
|
f14836cf02 | ||
|
|
4178feb65f | ||
|
|
2edaf22590 | ||
|
|
acd3dab957 | ||
|
|
6bbd74adcd | ||
|
|
9bb928bb38 | ||
|
|
c87a6c5969 | ||
|
|
c482c13dcb | ||
|
|
b87ed97402 | ||
|
|
ee000dabfd | ||
|
|
a73a011ee0 | ||
|
|
2a8e5e2c14 | ||
|
|
5d9a41f712 | ||
|
|
ebcf4b60f6 | ||
|
|
f976b78917 | ||
|
|
078790bd0f | ||
|
|
b88c5a89a8 | ||
|
|
57028e3acc | ||
|
|
c586a17926 | ||
|
|
9d078bac54 | ||
|
|
38eaefcabd | ||
|
|
ba8cadc2f1 | ||
|
|
380d5dfa6d | ||
|
|
32af626630 | ||
|
|
8358fedaf4 | ||
|
|
ec82b0c648 | ||
|
|
81a87f873f | ||
|
|
d91b8ac444 | ||
|
|
952e51ac75 | ||
|
|
11267cd44f | ||
|
|
0e59e0aebd | ||
|
|
ae1d3b3dd3 | ||
|
|
1a91dbee5f | ||
|
|
fd507e3e41 | ||
|
|
9c1a67cf47 | ||
|
|
69e3824840 | ||
|
|
fcb1a98129 | ||
|
|
6d942635af | ||
|
|
cda2c5d459 | ||
|
|
969bb5a742 | ||
|
|
4bccc611c3 | ||
|
|
d18c4ece0c | ||
|
|
25c664b13a | ||
|
|
7c680c955f | ||
|
|
f037d1b6ca | ||
|
|
2c8b627008 | ||
|
|
221e3eddd5 | ||
|
|
74c39c677b | ||
|
|
e6558832bf | ||
|
|
9c50625c55 | ||
|
|
d372435e92 | ||
|
|
a53facf709 | ||
|
|
ffc39dfbcb | ||
|
|
cba38b15a9 | ||
|
|
5ac7564bfe | ||
|
|
53cd289b90 | ||
|
|
8dc13bcf1a | ||
|
|
261825a89b | ||
|
|
f47a5a309d | ||
|
|
a40f2b9fa0 | ||
|
|
cfcd3892f7 | ||
|
|
703987f61c | ||
|
|
e50a8917ec | ||
|
|
74d7c8e625 | ||
|
|
a5d1383fe8 | ||
|
|
bf2bcf515c | ||
|
|
4c5e94c64b | ||
|
|
b4043216b6 | ||
|
|
4371014667 | ||
|
|
fbb3222d29 | ||
|
|
4ca3889bed | ||
|
|
eef1aebe8c | ||
|
|
48382c4b59 | ||
|
|
9a45f0b31c | ||
|
|
25c26e2f81 | ||
|
|
e4837f14b1 | ||
|
|
ce86131d12 | ||
|
|
e6c9baf6ef | ||
|
|
8d6db7be31 | ||
|
|
a2548b1fd0 | ||
|
|
e4658bb99d | ||
|
|
bf2e4a561a | ||
|
|
1816320124 | ||
|
|
f09bfe293d | ||
|
|
7b4e8fda4b | ||
|
|
c95812353f | ||
|
|
b622ec7a28 | ||
|
|
d8fbe7b77f | ||
|
|
dbcac37d91 | ||
|
|
d4d391b34f | ||
|
|
571cf7d490 | ||
|
|
e18b19ca5a | ||
|
|
48651bf482 | ||
|
|
5034a41c08 | ||
|
|
6795173e77 | ||
|
|
219ef996f5 | ||
|
|
00af1db275 | ||
|
|
5935ea896f | ||
|
|
459983c05e | ||
|
|
ebf4f029ac | ||
|
|
0eec945df1 | ||
|
|
8824b9d68f | ||
|
|
d2862814c5 | ||
|
|
25fece2d50 | ||
|
|
beb4239d1b | ||
|
|
2b78e37d92 | ||
|
|
a2070d9ce4 | ||
|
|
5827a686b8 | ||
|
|
dec479532e | ||
|
|
5d173168cc | ||
|
|
2aac1cde04 | ||
|
|
3676f0268f | ||
|
|
a7b75a54bb | ||
|
|
961a87b743 | ||
|
|
e03d59e381 | ||
|
|
d46ce5003c | ||
|
|
4c4143d9be | ||
|
|
8bc7d259f4 | ||
|
|
2d047fa428 | ||
|
|
735d420d40 | ||
|
|
bc9fc1aece | ||
|
|
b88e3c99c1 | ||
|
|
ce3e6e084c | ||
|
|
2a58ca7697 | ||
|
|
af96f7a0cd | ||
|
|
7d39d1a925 | ||
|
|
1b6c700e18 | ||
|
|
6304bd60ee | ||
|
|
4ad4417740 | ||
|
|
6a4c259a73 | ||
|
|
12eabb220d | ||
|
|
d68ce2d68c | ||
|
|
8e02c040eb | ||
|
|
a7a317c284 | ||
|
|
9d6ef24660 | ||
|
|
14014408fb | ||
|
|
b933e9666a | ||
|
|
7aff59bcce | ||
|
|
8e2760cb3d | ||
|
|
7a9fc6dbd3 | ||
|
|
75d0dc251e | ||
|
|
9a50c4d93f | ||
|
|
010d5a0192 | ||
|
|
cf1594829a | ||
|
|
854d720ce0 | ||
|
|
2f43c74ece | ||
|
|
b9817ac6b4 | ||
|
|
1e8da0d494 | ||
|
|
c47be7b415 | ||
|
|
d3f6cb860f | ||
|
|
83d25f09a3 | ||
|
|
ed747a2d3d | ||
|
|
3a8ee4ce2e | ||
|
|
5ac01a3af4 | ||
|
|
46343f2f9e | ||
|
|
56ccb5b2ab | ||
|
|
9a946eed80 | ||
|
|
9c6cb0f630 | ||
|
|
1b066d6965 | ||
|
|
54c3caad53 | ||
|
|
9b5e8aaf83 | ||
|
|
5143c09bcf | ||
|
|
2496185629 | ||
|
|
34deb82aea | ||
|
|
8f72ae9da2 | ||
|
|
b753f01ac1 | ||
|
|
fd0a147ae6 | ||
|
|
e94bd90782 | ||
|
|
ce4b897d0e | ||
|
|
a7694029e2 | ||
|
|
1e9110b763 | ||
|
|
6f3fbbbe49 | ||
|
|
d346ec7bfe | ||
|
|
26a3613397 | ||
|
|
e6318bddf3 | ||
|
|
514bb0beda | ||
|
|
41b1bd2f05 | ||
|
|
bf40dadf04 | ||
|
|
cb1678ebec | ||
|
|
0c1ac568b5 | ||
|
|
0f9550c747 | ||
|
|
b13ae17a47 | ||
|
|
f762a12d18 | ||
|
|
20d30a80be | ||
|
|
229b218203 | ||
|
|
4b668aaca8 | ||
|
|
8c7f1421c6 | ||
|
|
d90b2c1d52 | ||
|
|
22f39be197 | ||
|
|
2fa45436c2 | ||
|
|
cadbb6bbce | ||
|
|
2c89f04be7 | ||
|
|
597011e3a9 | ||
|
|
0d433b58ba | ||
|
|
cde8ef56e5 | ||
|
|
110816c7aa | ||
|
|
fbb1e168f7 | ||
|
|
23085eb5ae | ||
|
|
7344a6205f | ||
|
|
4b76ec40c0 | ||
|
|
90101d0269 | ||
|
|
7ac84c0660 | ||
|
|
2090530bbb | ||
|
|
b6cb7ddbaf | ||
|
|
3422d9335c | ||
|
|
e91f9a944e | ||
|
|
e7ddc7cf0f | ||
|
|
40dfa48756 | ||
|
|
579f92cf5f | ||
|
|
4565125da9 | ||
|
|
ce13a01e65 | ||
|
|
618a8682b7 | ||
|
|
963077f918 | ||
|
|
3704d2d86b | ||
|
|
fc6a029311 | ||
|
|
7c7b1e6c2d | ||
|
|
892920039d | ||
|
|
51cdd38c3e | ||
|
|
80977bd4c0 | ||
|
|
d8022f94ef | ||
|
|
1c43587d7d | ||
|
|
b2ed32b118 | ||
|
|
0cc815d816 | ||
|
|
d452b7593f | ||
|
|
5346bdc683 | ||
|
|
dc5c1e2002 | ||
|
|
2e48e298a2 | ||
|
|
7a1aaaf5c4 | ||
|
|
bde92d5cfe | ||
|
|
691f0f4845 | ||
|
|
fdd458d2fe | ||
|
|
d2c0b8374a | ||
|
|
c96c78892d | ||
|
|
957643f523 | ||
|
|
749bbec566 | ||
|
|
25e363c5fb | ||
|
|
febeed3277 | ||
|
|
9d07aa006d | ||
|
|
12d69e25dd | ||
|
|
0c9f1efc75 | ||
|
|
665b4506e7 | ||
|
|
cb5548ceb8 | ||
|
|
c9492e54f7 | ||
|
|
12490eafff | ||
|
|
6e83d11d5f | ||
|
|
9c6aedc91b | ||
|
|
a9339d0627 | ||
|
|
4d9aa10532 | ||
|
|
b00264b594 | ||
|
|
e329c7015e | ||
|
|
1392cfc72d | ||
|
|
c6688d8f89 | ||
|
|
87abea0ba3 | ||
|
|
f9fcb44f3c | ||
|
|
996cbbca38 | ||
|
|
581f4b89bd | ||
|
|
88a347dce0 | ||
|
|
3e7b197a1d | ||
|
|
94ab06e92f | ||
|
|
d38c81fcff | ||
|
|
3e26fdfb67 | ||
|
|
e1be73232d | ||
|
|
06fd2268d9 | ||
|
|
15251dfae1 | ||
|
|
f62812a8dc | ||
|
|
c6041d2590 | ||
|
|
c7e779107c | ||
|
|
7cd25c919f | ||
|
|
47d67d3985 | ||
|
|
1a7921b46c | ||
|
|
43d569741b | ||
|
|
52c6869eab | ||
|
|
6dff9097a2 | ||
|
|
4ff211662a | ||
|
|
05eab51a0d | ||
|
|
604a4e7dbc | ||
|
|
80dca96ee8 | ||
|
|
7f97037190 | ||
|
|
b658afd857 | ||
|
|
992ad97ad5 | ||
|
|
5af6cbae2c | ||
|
|
2abe792f36 | ||
|
|
1ff9bb8fdc | ||
|
|
5cb1039daf | ||
|
|
591c5dabf4 | ||
|
|
770fff287e | ||
|
|
cea7a179ae | ||
|
|
d80c40cfbf | ||
|
|
12e83374e9 | ||
|
|
98344d2e5e | ||
|
|
99dc1eec50 | ||
|
|
2a886576a6 | ||
|
|
919d005550 | ||
|
|
97abdaca5a | ||
|
|
9cc8b7c858 | ||
|
|
0726472b91 | ||
|
|
3cbe92d797 | ||
|
|
72a278c9ed | ||
|
|
e567c8adce | ||
|
|
dde8045109 | ||
|
|
c922c4c383 | ||
|
|
bc8907e90d | ||
|
|
a8ba7786ae | ||
|
|
c734e48ad0 | ||
|
|
d30d0b29a9 | ||
|
|
2912defb97 | ||
|
|
69f8ac6b56 | ||
|
|
e7441ff6e8 | ||
|
|
8a34158fa4 | ||
|
|
8d2a6d96f2 | ||
|
|
bb50b677c7 | ||
|
|
0fde4b3b2e | ||
|
|
ee9c109f07 | ||
|
|
1219423091 | ||
|
|
cf00ab854f | ||
|
|
c417dcb7e2 | ||
|
|
7ad711f554 | ||
|
|
2d7b0cf94d | ||
|
|
d669c07e8a | ||
|
|
8bd52946b4 | ||
|
|
27e81637be | ||
|
|
5c67e27a30 | ||
|
|
59af9809fe | ||
|
|
a564510c49 | ||
|
|
285b614927 | ||
|
|
fd2d2c035e | ||
|
|
78981862be | ||
|
|
7f1253ff83 | ||
|
|
e0265aed05 | ||
|
|
9d36d88a65 | ||
|
|
5dd5602229 | ||
|
|
126c4e9a06 | ||
|
|
5dbaf6ceb0 | ||
|
|
90de5659ea | ||
|
|
367e50edab | ||
|
|
42b8dafafe | ||
|
|
577aaf8ad6 | ||
|
|
07cdf0364c | ||
|
|
7f829f0159 | ||
|
|
a918aa97d9 | ||
|
|
4fdecc9b85 | ||
|
|
7af25c785d | ||
|
|
4de39b205d | ||
|
|
2748a2e97f | ||
|
|
2926bbfe15 | ||
|
|
254c63763a | ||
|
|
2de834f1f4 | ||
|
|
7273eab80e | ||
|
|
13e79c777a | ||
|
|
8aa7d4b463 | ||
|
|
5251f1c9db | ||
|
|
82e923dfc8 | ||
|
|
decf16b92c | ||
|
|
b84d960a81 | ||
|
|
34cb305755 | ||
|
|
ed85bfa915 | ||
|
|
06ef33ff5e | ||
|
|
57f121178c | ||
|
|
3b88ee623b | ||
|
|
8588625937 | ||
|
|
3417839726 | ||
|
|
c1069052ae | ||
|
|
ea17542e4b | ||
|
|
c7d779fe88 | ||
|
|
a70f3f12c5 | ||
|
|
90a31589bb | ||
|
|
b48d9a3a82 | ||
|
|
0255311bbe | ||
|
|
bd91519df9 | ||
|
|
58fe8b0cf1 | ||
|
|
064aa64f20 | ||
|
|
d9f79853fb | ||
|
|
2e68ee5c8b | ||
|
|
a9544ca890 | ||
|
|
9a549a853b | ||
|
|
2dad769a00 | ||
|
|
0ceb14dbf6 | ||
|
|
bab1e26d9b | ||
|
|
9a91cc232c | ||
|
|
5a46cf1d48 | ||
|
|
f1e241940b | ||
|
|
47b344ba12 | ||
|
|
afbb06a72f | ||
|
|
e336cd463f | ||
|
|
3a8315971e | ||
|
|
4ccfa98771 | ||
|
|
1db120bf06 | ||
|
|
262cf63956 | ||
|
|
fe2ae4c6c3 | ||
|
|
16d9944dbb | ||
|
|
e9956cc71e | ||
|
|
59a85c1d75 | ||
|
|
4427149a38 | ||
|
|
20dee618ea | ||
|
|
37ebbb53be | ||
|
|
ba019efaf1 | ||
|
|
ce948fc512 | ||
|
|
2cd9e7fb55 | ||
|
|
1e2d151684 | ||
|
|
ce5651f5fa | ||
|
|
20ba0bf4ed | ||
|
|
c325ffd0f8 | ||
|
|
6e88d9688b | ||
|
|
bf898f10fb | ||
|
|
c891999e1d | ||
|
|
938e287501 | ||
|
|
edcfc32b1a | ||
|
|
904b211d98 | ||
|
|
af08567f24 | ||
|
|
75ef658962 | ||
|
|
fe2dd79838 | ||
|
|
bbe7e6525d | ||
|
|
ef20df719c |
@@ -0,0 +1,9 @@
|
|||||||
|
# Text files use LF line endings in this repository
|
||||||
|
* text=auto
|
||||||
|
|
||||||
|
# Except the dependencies, which we leave alone
|
||||||
|
Godeps/** -text=auto
|
||||||
|
|
||||||
|
# Diffs on these files are meaningless
|
||||||
|
gui.files.go -diff
|
||||||
|
*.svg -diff
|
||||||
+5
-2
@@ -1,12 +1,15 @@
|
|||||||
syncthing
|
./syncthing
|
||||||
syncthing.exe
|
syncthing.exe
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
*.zip
|
*.zip
|
||||||
*.asc
|
*.asc
|
||||||
*.sublime*
|
|
||||||
.jshintrc
|
.jshintrc
|
||||||
coverage.out
|
coverage.out
|
||||||
files/pidx
|
files/pidx
|
||||||
bin
|
bin
|
||||||
perfstats*.csv
|
perfstats*.csv
|
||||||
coverage.xml
|
coverage.xml
|
||||||
|
!gui/scripts/syncthing
|
||||||
|
syncthing.md5
|
||||||
|
syncthing.exe.md5
|
||||||
|
RELEASE
|
||||||
|
|||||||
@@ -5,25 +5,52 @@ Alexander Graf <register-github@alex-graf.de>
|
|||||||
Andrew Dunham <andrew@du.nham.ca>
|
Andrew Dunham <andrew@du.nham.ca>
|
||||||
Audrius Butkevicius <audrius.butkevicius@gmail.com>
|
Audrius Butkevicius <audrius.butkevicius@gmail.com>
|
||||||
Arthur Axel fREW Schmidt <frew@afoolishmanifesto.com> <frioux@gmail.com>
|
Arthur Axel fREW Schmidt <frew@afoolishmanifesto.com> <frioux@gmail.com>
|
||||||
|
Ben Curthoys <ben@bencurthoys.com>
|
||||||
|
Ben Schulz <ueomkail@gmail.com> <uok@users.noreply.github.com>
|
||||||
Ben Sidhom <bsidhom@gmail.com>
|
Ben Sidhom <bsidhom@gmail.com>
|
||||||
Brandon Philips <brandon@ifup.org>
|
Brandon Philips <brandon@ifup.org>
|
||||||
|
Brendan Long <self@brendanlong.com>
|
||||||
Caleb Callaway <enlightened.despot@gmail.com>
|
Caleb Callaway <enlightened.despot@gmail.com>
|
||||||
|
Carsten Hagemann <moter8@gmail.com>
|
||||||
|
Cathryne Linenweaver <cathryne.linenweaver@gmail.com> <Cathryne@users.noreply.github.com>
|
||||||
Chris Joel <chris@scriptolo.gy>
|
Chris Joel <chris@scriptolo.gy>
|
||||||
|
Colin Kennedy <moshen.colin@gmail.com>
|
||||||
Daniel Martí <mvdan@mvdan.cc>
|
Daniel Martí <mvdan@mvdan.cc>
|
||||||
|
Dennis Wilson <dw@risu.io>
|
||||||
|
Dominik Heidler <dominik@heidler.eu>
|
||||||
|
Elias Jarlebring <jarlebring@gmail.com>
|
||||||
Emil Hessman <emil@hessman.se>
|
Emil Hessman <emil@hessman.se>
|
||||||
|
Federico Castagnini <federico.castagnini@gmail.com>
|
||||||
Felix Ableitner <me@nutomic.com>
|
Felix Ableitner <me@nutomic.com>
|
||||||
Felix Unterpaintner <bigbear2nd@gmail.com>
|
Felix Unterpaintner <bigbear2nd@gmail.com>
|
||||||
|
Francois-Xavier Gsell <fxgsell@gmail.com>
|
||||||
Gilli Sigurdsson <gilli@vx.is>
|
Gilli Sigurdsson <gilli@vx.is>
|
||||||
Jakob Borg <jakob@nym.se>
|
Jakob Borg <jakob@nym.se>
|
||||||
James Patterson <jamespatterson@operamail.com> <jpjp@users.noreply.github.com>
|
James Patterson <jamespatterson@operamail.com> <jpjp@users.noreply.github.com>
|
||||||
|
Jaroslav Malec <dzardacz@gmail.com>
|
||||||
Jens Diemer <github.com@jensdiemer.de> <git@jensdiemer.de>
|
Jens Diemer <github.com@jensdiemer.de> <git@jensdiemer.de>
|
||||||
Jochen Voss <voss@seehuhn.de>
|
Jochen Voss <voss@seehuhn.de>
|
||||||
|
Johan Vromans <jvromans@squirrel.nl>
|
||||||
|
Karol Różycki <rozycki.karol@gmail.com>
|
||||||
|
Ken'ichi Kamada <kamada@nanohz.org>
|
||||||
Lode Hoste <zillode@zillode.be>
|
Lode Hoste <zillode@zillode.be>
|
||||||
|
Lord Landon Agahnim <lordlandon@gmail.com>
|
||||||
Marcin Dziadus <dziadus.marcin@gmail.com>
|
Marcin Dziadus <dziadus.marcin@gmail.com>
|
||||||
|
Marc Laporte <marc@marclaporte.com> <marc@laporte.name>
|
||||||
|
Marc Pujol <kilburn@la3.org>
|
||||||
|
Michael Jephcote <rewt0r@gmx.com> <Rewt0r@users.noreply.github.com>
|
||||||
Michael Tilli <pyfisch@gmail.com>
|
Michael Tilli <pyfisch@gmail.com>
|
||||||
|
Pascal Jungblut <github@pascalj.com> <mail@pascal-jungblut.com>
|
||||||
|
Peter Hoeg <peter@speartail.com>
|
||||||
Philippe Schommers <philippe@schommers.be>
|
Philippe Schommers <philippe@schommers.be>
|
||||||
Phill Luby <phill.luby@newredo.com>
|
Phill Luby <phill.luby@newredo.com>
|
||||||
|
Piotr Bejda <piotrb10@gmail.com>
|
||||||
Ryan Sullivan <kayoticsully@gmail.com>
|
Ryan Sullivan <kayoticsully@gmail.com>
|
||||||
|
Sergey Mishin <ralder@yandex.ru>
|
||||||
|
Stefan Tatschner <stefan@sevenbyte.org>
|
||||||
|
Tim Abell <tim@timwise.co.uk>
|
||||||
|
Tobias Nygren <tnn@nygren.pp.se>
|
||||||
|
Tomas Cerveny <kozec@kozec.com>
|
||||||
Tully Robinson <tully@tojr.org>
|
Tully Robinson <tully@tojr.org>
|
||||||
Veeti Paananen <veeti.paananen@rojekti.fi>
|
Veeti Paananen <veeti.paananen@rojekti.fi>
|
||||||
Vil Brekin <vilbrekin@gmail.com>
|
Vil Brekin <vilbrekin@gmail.com>
|
||||||
|
|||||||
+92
@@ -0,0 +1,92 @@
|
|||||||
|
## Conduct
|
||||||
|
|
||||||
|
* We are committed to providing a friendly, safe and welcoming
|
||||||
|
environment for all, regardless of gender, sexual orientation,
|
||||||
|
disability, ethnicity, religion, or similar personal characteristic.
|
||||||
|
|
||||||
|
* On IRC, please avoid using overtly sexual nicknames or other nicknames
|
||||||
|
that might detract from a friendly, safe and welcoming environment for
|
||||||
|
all.
|
||||||
|
|
||||||
|
* Please be kind and courteous. There's no need to be mean or rude.
|
||||||
|
|
||||||
|
* Respect that people have differences of opinion and that every design
|
||||||
|
or implementation choice carries a trade-off and numerous costs. There
|
||||||
|
is seldom a right answer.
|
||||||
|
|
||||||
|
* Please keep unstructured critique to a minimum. If you have solid
|
||||||
|
ideas you want to experiment with, make a fork and see how it works.
|
||||||
|
|
||||||
|
* We will exclude you from interaction if you insult, demean or harass
|
||||||
|
anyone. That is not welcome behaviour. We interpret the term
|
||||||
|
"harassment" as including the definition in the <a
|
||||||
|
href="http://citizencodeofconduct.org/">Citizen Code of Conduct</a>;
|
||||||
|
if you have any lack of clarity about what might be included in that
|
||||||
|
concept, please read their definition. In particular, we don't
|
||||||
|
tolerate behavior that excludes people in socially marginalized
|
||||||
|
groups.
|
||||||
|
|
||||||
|
* Private harassment is also unacceptable. No matter who you are, if you
|
||||||
|
feel you have been or are being harassed or made uncomfortable by a
|
||||||
|
community member, please contact one of the channel ops or any of the
|
||||||
|
Syncthing core team immediately. Whether you're a regular contributor
|
||||||
|
or a newcomer, we care about making this community a safe place for
|
||||||
|
you and we've got your back.
|
||||||
|
|
||||||
|
* Likewise any spamming, trolling, flaming, baiting or other
|
||||||
|
attention-stealing behaviour is not welcome.
|
||||||
|
|
||||||
|
## Moderation
|
||||||
|
|
||||||
|
These are the policies for upholding our community's standards of
|
||||||
|
conduct in our communication channels, most notably in Syncthing-related
|
||||||
|
IRC channels and on the web forum.
|
||||||
|
|
||||||
|
1. Remarks that violate the Syncthing standards of conduct, including
|
||||||
|
hateful, hurtful, oppressive, or exclusionary remarks, are not
|
||||||
|
allowed. (Cursing is allowed, but never targeting another user, and
|
||||||
|
never in a hateful manner.)
|
||||||
|
|
||||||
|
2. Remarks that moderators find inappropriate, whether listed in the
|
||||||
|
code of conduct or not, are also not allowed.
|
||||||
|
|
||||||
|
3. Moderators will first respond to such remarks with a warning.
|
||||||
|
|
||||||
|
4. If the warning is unheeded, the user will be "kicked," i.e., kicked
|
||||||
|
out of the communication channel to cool off.
|
||||||
|
|
||||||
|
5. If the user comes back and continues to make trouble, they will be
|
||||||
|
banned, i.e., indefinitely excluded.
|
||||||
|
|
||||||
|
6. Moderators may choose at their discretion to un-ban the user if it
|
||||||
|
was a first offense and they offer the offended party a genuine
|
||||||
|
apology.
|
||||||
|
|
||||||
|
7. If a moderator bans someone and you think it was unjustified, please
|
||||||
|
take it up with that moderator, or with a different moderator, **in
|
||||||
|
private**. Complaints about bans in-channel are not allowed.
|
||||||
|
|
||||||
|
8. Moderators are held to a higher standard than other community
|
||||||
|
members. If a moderator creates an inappropriate situation, they
|
||||||
|
should expect less leeway than others.
|
||||||
|
|
||||||
|
In the Syncthing community we strive to go the extra step to look out
|
||||||
|
for each other. Don't just aim to be technically unimpeachable, try to
|
||||||
|
be your best self. In particular, avoid flirting with offensive or
|
||||||
|
sensitive issues, particularly if they're off-topic; this all too
|
||||||
|
often leads to unnecessary fights, hurt feelings, and damaged trust;
|
||||||
|
worse, it can drive people away from the community entirely.
|
||||||
|
|
||||||
|
And if someone takes issue with something you said or did, resist the
|
||||||
|
urge to be defensive. Just stop doing what it was they complained about
|
||||||
|
and apologize. Even if you feel you were misinterpreted or unfairly
|
||||||
|
accused, chances are good there was something you could've communicated
|
||||||
|
better — remember that it's your responsibility to make your fellow
|
||||||
|
community members comfortable. Everyone wants to get along and we are
|
||||||
|
all here first and foremost because we want to talk about cool
|
||||||
|
technology. You will find that people will be eager to assume good
|
||||||
|
intent and forgive as long as you earn their trust.
|
||||||
|
|
||||||
|
*Adapted from the [Rust Code of Conduct](https://github.com/rust-lang/rust/wiki/Note-development-policy#conduct)*
|
||||||
|
|
||||||
|
*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling)*
|
||||||
+41
-17
@@ -33,10 +33,40 @@ latest info on Transifex.
|
|||||||
|
|
||||||
Every contribution is welcome. If you want to contribute but are unsure
|
Every contribution is welcome. If you want to contribute but are unsure
|
||||||
where to start, any open issues are fair game! Be prepared for a
|
where to start, any open issues are fair game! Be prepared for a
|
||||||
[certain amount of review](https://discourse.syncthing.net/t/733); it's
|
[certain amount of review](https://github.com/syncthing/syncthing/wiki/FAQ#why-are-you-being-so-hard-on-my-pull-request);
|
||||||
all in the name of quality. :) Following the points below will make this
|
it's all in the name of quality. :) Following the points below will make this
|
||||||
a smoother process.
|
a smoother process.
|
||||||
|
|
||||||
|
Individuals making significant and valuable contributions are given
|
||||||
|
commit-access to the project. If you make a significant contribution and
|
||||||
|
are not considered for commit-access, please contact any of the
|
||||||
|
Syncthing core team members.
|
||||||
|
|
||||||
|
All nontrivial contributions should go through the pull request
|
||||||
|
mechanism for internal review. Determining what is "nontrivial" is left
|
||||||
|
at the discretion of the contributor.
|
||||||
|
|
||||||
|
### Authorship
|
||||||
|
|
||||||
|
All code authors are listed in the AUTHORS file. Commits must be made
|
||||||
|
with the same name and email as listed in the AUTHORS file. To
|
||||||
|
accomplish this, ensure that your git configuration is set correctly
|
||||||
|
prior to making your first commit;
|
||||||
|
|
||||||
|
$ git config --global user.name "Jane Doe"
|
||||||
|
$ git config --global user.email janedoe@example.com
|
||||||
|
|
||||||
|
You must be reachable on the given email address. If you do not wish to
|
||||||
|
use your real name for whatever reason, using a nickname or pseudonym is
|
||||||
|
perfectly acceptable.
|
||||||
|
|
||||||
|
### Core Team
|
||||||
|
|
||||||
|
The Syncthing core team currently consists of the following members;
|
||||||
|
|
||||||
|
- Jakob Borg (@calmh)
|
||||||
|
- Audrius Butkevicius (@AudriusButkevicius)
|
||||||
|
|
||||||
## Coding Style
|
## Coding Style
|
||||||
|
|
||||||
- Follow the conventions laid out in [Effective Go](https://golang.org/doc/effective_go.html)
|
- Follow the conventions laid out in [Effective Go](https://golang.org/doc/effective_go.html)
|
||||||
@@ -59,24 +89,24 @@ a smoother process.
|
|||||||
feature should probably be a single commit based on the current
|
feature should probably be a single commit based on the current
|
||||||
`master` branch. You may be asked to "rebase" or "squash" your pull
|
`master` branch. You may be asked to "rebase" or "squash" your pull
|
||||||
request to make sure this is the case, especially if there have been
|
request to make sure this is the case, especially if there have been
|
||||||
amendments during review.
|
amendments during review.
|
||||||
|
|
||||||
## Licensing
|
## Licensing
|
||||||
|
|
||||||
All contributions are made under the same GPL license as the rest of the
|
All contributions are made under the same MPLv2 license as the rest of
|
||||||
project, except documentation, user interface text and translation
|
the project, except documentation, user interface text and translation
|
||||||
strings which are licensed under the Creative Commons Attribution 4.0
|
strings which are licensed under the Creative Commons Attribution 4.0
|
||||||
International License. You retain the copyright to code you have
|
International License. You retain the copyright to code you have
|
||||||
written.
|
written.
|
||||||
|
|
||||||
When accepting your first contribution, the maintainer of the project
|
When accepting your first contribution, the maintainer of the project
|
||||||
will ensure that you are added to the CONTRIBUTORS file. You are welcome
|
will ensure that you are added to the AUTHORS file. You are welcome to
|
||||||
to add yourself as a separate commit in your first pull request.
|
add yourself as a separate commit in your first pull request.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
[See the documentation](http://discourse.syncthing.net/t/44) on how to
|
[See the documentation](https://github.com/syncthing/syncthing/wiki/Building)
|
||||||
get started with a build environment.
|
on how to get started with a build environment.
|
||||||
|
|
||||||
## Branches
|
## Branches
|
||||||
|
|
||||||
@@ -101,16 +131,10 @@ signed by GPG key BCE524C7.
|
|||||||
|
|
||||||
Yes please!
|
Yes please!
|
||||||
|
|
||||||
## Style
|
|
||||||
|
|
||||||
- `go fmt`
|
|
||||||
|
|
||||||
- Unix line breaks
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
[Over here!](http://discourse.syncthing.net/category/documentation)
|
[Over here!](https://github.com/syncthing/syncthing/wiki)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
GPLv3
|
MPLv2
|
||||||
|
|||||||
Generated
+36
-32
@@ -1,61 +1,49 @@
|
|||||||
{
|
{
|
||||||
"ImportPath": "github.com/syncthing/syncthing",
|
"ImportPath": "github.com/syncthing/syncthing",
|
||||||
"GoVersion": "go1.3.3",
|
"GoVersion": "go1.4",
|
||||||
"Packages": [
|
"Packages": [
|
||||||
"./cmd/..."
|
"./cmd/..."
|
||||||
],
|
],
|
||||||
"Deps": [
|
"Deps": [
|
||||||
{
|
|
||||||
"ImportPath": "code.google.com/p/go.crypto/bcrypt",
|
|
||||||
"Comment": "null-216",
|
|
||||||
"Rev": "41cd4647fccc72b0b79ef1bd1fe6735e718257cd"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "code.google.com/p/go.crypto/blowfish",
|
|
||||||
"Comment": "null-216",
|
|
||||||
"Rev": "41cd4647fccc72b0b79ef1bd1fe6735e718257cd"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "code.google.com/p/go.text/transform",
|
|
||||||
"Comment": "null-90",
|
|
||||||
"Rev": "d65bffbc88a153d23a6d2a864531e6e7c2cde59b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "code.google.com/p/go.text/unicode/norm",
|
|
||||||
"Comment": "null-90",
|
|
||||||
"Rev": "d65bffbc88a153d23a6d2a864531e6e7c2cde59b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/AudriusButkevicius/lfu-go",
|
|
||||||
"Rev": "164bcecceb92fd6037f4d18a8d97b495ec6ef669"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/bkaradzic/go-lz4",
|
"ImportPath": "github.com/bkaradzic/go-lz4",
|
||||||
"Rev": "93a831dcee242be64a9cc9803dda84af25932de7"
|
"Rev": "93a831dcee242be64a9cc9803dda84af25932de7"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/calmh/logger",
|
"ImportPath": "github.com/calmh/logger",
|
||||||
"Rev": "f50d32b313bec2933a3e1049f7416a29f3413d29"
|
"Rev": "4d4e2801954c5581e4c2a80a3d3beb3b3645fd04"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/calmh/osext",
|
"ImportPath": "github.com/calmh/luhn",
|
||||||
"Rev": "9bf61584e5f1f172e8766ddc9022d9c401faaa5e"
|
"Rev": "0c8388ff95fa92d4094011e5a04fc99dea3d1632"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/calmh/xdr",
|
"ImportPath": "github.com/calmh/xdr",
|
||||||
"Rev": "ec3d404f43731551258977b38dd72cf557d00398"
|
"Rev": "5f7208e86762911861c94f1849eddbfc0a60cbf0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/juju/ratelimit",
|
"ImportPath": "github.com/juju/ratelimit",
|
||||||
"Rev": "f9f36d11773655c0485207f0ad30dc2655f69d56"
|
"Rev": "c5abe513796336ee2869745bff0638508450e9c5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/kardianos/osext",
|
||||||
|
"Rev": "efacde03154693404c65e7aa7d461ac9014acd0c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/syncthing/protocol",
|
||||||
|
"Rev": "e7db2648034fb71b051902a02bc25d4468ed492e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/syndtr/goleveldb/leveldb",
|
"ImportPath": "github.com/syndtr/goleveldb/leveldb",
|
||||||
"Rev": "d8d1d2a5cc2d34c950dffa2f554525415d59f737"
|
"Rev": "87e4e645d80ae9c537e8f2dee52b28036a5dd75e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/syndtr/gosnappy/snappy",
|
"ImportPath": "github.com/syndtr/gosnappy/snappy",
|
||||||
"Rev": "ce8acff4829e0c2458a67ead32390ac0a381c862"
|
"Rev": "156a073208e131d7d2e212cb749feae7c339e846"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/thejerf/suture",
|
||||||
|
"Rev": "ff19fb384c3fe30f42717967eaa69da91e5f317c"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/vitrun/qart/coding",
|
"ImportPath": "github.com/vitrun/qart/coding",
|
||||||
@@ -68,6 +56,22 @@
|
|||||||
{
|
{
|
||||||
"ImportPath": "github.com/vitrun/qart/qr",
|
"ImportPath": "github.com/vitrun/qart/qr",
|
||||||
"Rev": "ccb109cf25f0cd24474da73b9fee4e7a3e8a8ce0"
|
"Rev": "ccb109cf25f0cd24474da73b9fee4e7a3e8a8ce0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/crypto/bcrypt",
|
||||||
|
"Rev": "c57d4a71915a248dbad846d60825145062b4c18e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/crypto/blowfish",
|
||||||
|
"Rev": "c57d4a71915a248dbad846d60825145062b4c18e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/text/transform",
|
||||||
|
"Rev": "2076e9cab4147459c82bc81169e46c139d358547"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/text/unicode/norm",
|
||||||
|
"Rev": "2076e9cab4147459c82bc81169e46c139d358547"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
-30
@@ -1,30 +0,0 @@
|
|||||||
# Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style
|
|
||||||
# license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
maketables: maketables.go triegen.go
|
|
||||||
go build $^
|
|
||||||
|
|
||||||
maketesttables: maketesttables.go triegen.go
|
|
||||||
go build $^
|
|
||||||
|
|
||||||
normregtest: normregtest.go
|
|
||||||
go build $^
|
|
||||||
|
|
||||||
tables: maketables
|
|
||||||
./maketables > tables.go
|
|
||||||
gofmt -w tables.go
|
|
||||||
|
|
||||||
trietesttables: maketesttables
|
|
||||||
./maketesttables > triedata_test.go
|
|
||||||
gofmt -w triedata_test.go
|
|
||||||
|
|
||||||
# Downloads from www.unicode.org, so not part
|
|
||||||
# of standard test scripts.
|
|
||||||
test: testtables regtest
|
|
||||||
|
|
||||||
testtables: maketables
|
|
||||||
./maketables -test > data_test.go && go test -tags=test
|
|
||||||
|
|
||||||
regtest: normregtest
|
|
||||||
./normregtest
|
|
||||||
-45
@@ -1,45 +0,0 @@
|
|||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build ignore
|
|
||||||
|
|
||||||
// Generate test data for trie code.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
printTestTables()
|
|
||||||
}
|
|
||||||
|
|
||||||
// We take the smallest, largest and an arbitrary value for each
|
|
||||||
// of the UTF-8 sequence lengths.
|
|
||||||
var testRunes = []rune{
|
|
||||||
0x01, 0x0C, 0x7F, // 1-byte sequences
|
|
||||||
0x80, 0x100, 0x7FF, // 2-byte sequences
|
|
||||||
0x800, 0x999, 0xFFFF, // 3-byte sequences
|
|
||||||
0x10000, 0x10101, 0x10FFFF, // 4-byte sequences
|
|
||||||
0x200, 0x201, 0x202, 0x210, 0x215, // five entries in one sparse block
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileHeader = `// Generated by running
|
|
||||||
// maketesttables
|
|
||||||
// DO NOT EDIT
|
|
||||||
|
|
||||||
package norm
|
|
||||||
|
|
||||||
`
|
|
||||||
|
|
||||||
func printTestTables() {
|
|
||||||
fmt.Print(fileHeader)
|
|
||||||
fmt.Printf("var testRunes = %#v\n\n", testRunes)
|
|
||||||
t := newNode()
|
|
||||||
for i, r := range testRunes {
|
|
||||||
t.insert(r, uint16(i))
|
|
||||||
}
|
|
||||||
t.printTables("testdata")
|
|
||||||
}
|
|
||||||
-6989
File diff suppressed because it is too large
Load Diff
-232
@@ -1,232 +0,0 @@
|
|||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package norm
|
|
||||||
|
|
||||||
type valueRange struct {
|
|
||||||
value uint16 // header: value:stride
|
|
||||||
lo, hi byte // header: lo:n
|
|
||||||
}
|
|
||||||
|
|
||||||
type trie struct {
|
|
||||||
index []uint8
|
|
||||||
values []uint16
|
|
||||||
sparse []valueRange
|
|
||||||
sparseOffset []uint16
|
|
||||||
cutoff uint8 // indices >= cutoff are sparse
|
|
||||||
}
|
|
||||||
|
|
||||||
// lookupValue determines the type of block n and looks up the value for b.
|
|
||||||
// For n < t.cutoff, the block is a simple lookup table. Otherwise, the block
|
|
||||||
// is a list of ranges with an accompanying value. Given a matching range r,
|
|
||||||
// the value for b is by r.value + (b - r.lo) * stride.
|
|
||||||
func (t *trie) lookupValue(n uint8, b byte) uint16 {
|
|
||||||
if n < t.cutoff {
|
|
||||||
return t.values[uint16(n)<<6+uint16(b)]
|
|
||||||
}
|
|
||||||
offset := t.sparseOffset[n-t.cutoff]
|
|
||||||
header := t.sparse[offset]
|
|
||||||
lo := offset + 1
|
|
||||||
hi := lo + uint16(header.lo)
|
|
||||||
for lo < hi {
|
|
||||||
m := lo + (hi-lo)/2
|
|
||||||
r := t.sparse[m]
|
|
||||||
if r.lo <= b && b <= r.hi {
|
|
||||||
return r.value + uint16(b-r.lo)*header.value
|
|
||||||
}
|
|
||||||
if b < r.lo {
|
|
||||||
hi = m
|
|
||||||
} else {
|
|
||||||
lo = m + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
t1 = 0x00 // 0000 0000
|
|
||||||
tx = 0x80 // 1000 0000
|
|
||||||
t2 = 0xC0 // 1100 0000
|
|
||||||
t3 = 0xE0 // 1110 0000
|
|
||||||
t4 = 0xF0 // 1111 0000
|
|
||||||
t5 = 0xF8 // 1111 1000
|
|
||||||
t6 = 0xFC // 1111 1100
|
|
||||||
te = 0xFE // 1111 1110
|
|
||||||
)
|
|
||||||
|
|
||||||
// lookup returns the trie value for the first UTF-8 encoding in s and
|
|
||||||
// the width in bytes of this encoding. The size will be 0 if s does not
|
|
||||||
// hold enough bytes to complete the encoding. len(s) must be greater than 0.
|
|
||||||
func (t *trie) lookup(s []byte) (v uint16, sz int) {
|
|
||||||
c0 := s[0]
|
|
||||||
switch {
|
|
||||||
case c0 < tx:
|
|
||||||
return t.values[c0], 1
|
|
||||||
case c0 < t2:
|
|
||||||
return 0, 1
|
|
||||||
case c0 < t3:
|
|
||||||
if len(s) < 2 {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
i := t.index[c0]
|
|
||||||
c1 := s[1]
|
|
||||||
if c1 < tx || t2 <= c1 {
|
|
||||||
return 0, 1
|
|
||||||
}
|
|
||||||
return t.lookupValue(i, c1), 2
|
|
||||||
case c0 < t4:
|
|
||||||
if len(s) < 3 {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
i := t.index[c0]
|
|
||||||
c1 := s[1]
|
|
||||||
if c1 < tx || t2 <= c1 {
|
|
||||||
return 0, 1
|
|
||||||
}
|
|
||||||
o := uint16(i)<<6 + uint16(c1)
|
|
||||||
i = t.index[o]
|
|
||||||
c2 := s[2]
|
|
||||||
if c2 < tx || t2 <= c2 {
|
|
||||||
return 0, 2
|
|
||||||
}
|
|
||||||
return t.lookupValue(i, c2), 3
|
|
||||||
case c0 < t5:
|
|
||||||
if len(s) < 4 {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
i := t.index[c0]
|
|
||||||
c1 := s[1]
|
|
||||||
if c1 < tx || t2 <= c1 {
|
|
||||||
return 0, 1
|
|
||||||
}
|
|
||||||
o := uint16(i)<<6 + uint16(c1)
|
|
||||||
i = t.index[o]
|
|
||||||
c2 := s[2]
|
|
||||||
if c2 < tx || t2 <= c2 {
|
|
||||||
return 0, 2
|
|
||||||
}
|
|
||||||
o = uint16(i)<<6 + uint16(c2)
|
|
||||||
i = t.index[o]
|
|
||||||
c3 := s[3]
|
|
||||||
if c3 < tx || t2 <= c3 {
|
|
||||||
return 0, 3
|
|
||||||
}
|
|
||||||
return t.lookupValue(i, c3), 4
|
|
||||||
}
|
|
||||||
// Illegal rune
|
|
||||||
return 0, 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// lookupString returns the trie value for the first UTF-8 encoding in s and
|
|
||||||
// the width in bytes of this encoding. The size will be 0 if s does not
|
|
||||||
// hold enough bytes to complete the encoding. len(s) must be greater than 0.
|
|
||||||
func (t *trie) lookupString(s string) (v uint16, sz int) {
|
|
||||||
c0 := s[0]
|
|
||||||
switch {
|
|
||||||
case c0 < tx:
|
|
||||||
return t.values[c0], 1
|
|
||||||
case c0 < t2:
|
|
||||||
return 0, 1
|
|
||||||
case c0 < t3:
|
|
||||||
if len(s) < 2 {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
i := t.index[c0]
|
|
||||||
c1 := s[1]
|
|
||||||
if c1 < tx || t2 <= c1 {
|
|
||||||
return 0, 1
|
|
||||||
}
|
|
||||||
return t.lookupValue(i, c1), 2
|
|
||||||
case c0 < t4:
|
|
||||||
if len(s) < 3 {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
i := t.index[c0]
|
|
||||||
c1 := s[1]
|
|
||||||
if c1 < tx || t2 <= c1 {
|
|
||||||
return 0, 1
|
|
||||||
}
|
|
||||||
o := uint16(i)<<6 + uint16(c1)
|
|
||||||
i = t.index[o]
|
|
||||||
c2 := s[2]
|
|
||||||
if c2 < tx || t2 <= c2 {
|
|
||||||
return 0, 2
|
|
||||||
}
|
|
||||||
return t.lookupValue(i, c2), 3
|
|
||||||
case c0 < t5:
|
|
||||||
if len(s) < 4 {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
i := t.index[c0]
|
|
||||||
c1 := s[1]
|
|
||||||
if c1 < tx || t2 <= c1 {
|
|
||||||
return 0, 1
|
|
||||||
}
|
|
||||||
o := uint16(i)<<6 + uint16(c1)
|
|
||||||
i = t.index[o]
|
|
||||||
c2 := s[2]
|
|
||||||
if c2 < tx || t2 <= c2 {
|
|
||||||
return 0, 2
|
|
||||||
}
|
|
||||||
o = uint16(i)<<6 + uint16(c2)
|
|
||||||
i = t.index[o]
|
|
||||||
c3 := s[3]
|
|
||||||
if c3 < tx || t2 <= c3 {
|
|
||||||
return 0, 3
|
|
||||||
}
|
|
||||||
return t.lookupValue(i, c3), 4
|
|
||||||
}
|
|
||||||
// Illegal rune
|
|
||||||
return 0, 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// lookupUnsafe returns the trie value for the first UTF-8 encoding in s.
|
|
||||||
// s must hold a full encoding.
|
|
||||||
func (t *trie) lookupUnsafe(s []byte) uint16 {
|
|
||||||
c0 := s[0]
|
|
||||||
if c0 < tx {
|
|
||||||
return t.values[c0]
|
|
||||||
}
|
|
||||||
if c0 < t2 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
i := t.index[c0]
|
|
||||||
if c0 < t3 {
|
|
||||||
return t.lookupValue(i, s[1])
|
|
||||||
}
|
|
||||||
i = t.index[uint16(i)<<6+uint16(s[1])]
|
|
||||||
if c0 < t4 {
|
|
||||||
return t.lookupValue(i, s[2])
|
|
||||||
}
|
|
||||||
i = t.index[uint16(i)<<6+uint16(s[2])]
|
|
||||||
if c0 < t5 {
|
|
||||||
return t.lookupValue(i, s[3])
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// lookupStringUnsafe returns the trie value for the first UTF-8 encoding in s.
|
|
||||||
// s must hold a full encoding.
|
|
||||||
func (t *trie) lookupStringUnsafe(s string) uint16 {
|
|
||||||
c0 := s[0]
|
|
||||||
if c0 < tx {
|
|
||||||
return t.values[c0]
|
|
||||||
}
|
|
||||||
if c0 < t2 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
i := t.index[c0]
|
|
||||||
if c0 < t3 {
|
|
||||||
return t.lookupValue(i, s[1])
|
|
||||||
}
|
|
||||||
i = t.index[uint16(i)<<6+uint16(s[1])]
|
|
||||||
if c0 < t4 {
|
|
||||||
return t.lookupValue(i, s[2])
|
|
||||||
}
|
|
||||||
i = t.index[uint16(i)<<6+uint16(s[2])]
|
|
||||||
if c0 < t5 {
|
|
||||||
return t.lookupValue(i, s[3])
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
-152
@@ -1,152 +0,0 @@
|
|||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package norm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Test data is located in triedata_test.go; generated by maketesttables.
|
|
||||||
var testdata = testdataTrie
|
|
||||||
|
|
||||||
type rangeTest struct {
|
|
||||||
block uint8
|
|
||||||
lookup byte
|
|
||||||
result uint16
|
|
||||||
table []valueRange
|
|
||||||
offsets []uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
var range1Off = []uint16{0, 2}
|
|
||||||
var range1 = []valueRange{
|
|
||||||
{0, 1, 0},
|
|
||||||
{1, 0x80, 0x80},
|
|
||||||
{0, 2, 0},
|
|
||||||
{1, 0x80, 0x80},
|
|
||||||
{9, 0xff, 0xff},
|
|
||||||
}
|
|
||||||
|
|
||||||
var rangeTests = []rangeTest{
|
|
||||||
{10, 0x80, 1, range1, range1Off},
|
|
||||||
{10, 0x00, 0, range1, range1Off},
|
|
||||||
{11, 0x80, 1, range1, range1Off},
|
|
||||||
{11, 0xff, 9, range1, range1Off},
|
|
||||||
{11, 0x00, 0, range1, range1Off},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLookupSparse(t *testing.T) {
|
|
||||||
for i, test := range rangeTests {
|
|
||||||
n := trie{sparse: test.table, sparseOffset: test.offsets, cutoff: 10}
|
|
||||||
v := n.lookupValue(test.block, test.lookup)
|
|
||||||
if v != test.result {
|
|
||||||
t.Errorf("LookupSparse:%d: found %X; want %X", i, v, test.result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test cases for illegal runes.
|
|
||||||
type trietest struct {
|
|
||||||
size int
|
|
||||||
bytes []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
var tests = []trietest{
|
|
||||||
// illegal runes
|
|
||||||
{1, []byte{0x80}},
|
|
||||||
{1, []byte{0xFF}},
|
|
||||||
{1, []byte{t2, tx - 1}},
|
|
||||||
{1, []byte{t2, t2}},
|
|
||||||
{2, []byte{t3, tx, tx - 1}},
|
|
||||||
{2, []byte{t3, tx, t2}},
|
|
||||||
{1, []byte{t3, tx - 1, tx}},
|
|
||||||
{3, []byte{t4, tx, tx, tx - 1}},
|
|
||||||
{3, []byte{t4, tx, tx, t2}},
|
|
||||||
{1, []byte{t4, t2, tx, tx - 1}},
|
|
||||||
{2, []byte{t4, tx, t2, tx - 1}},
|
|
||||||
|
|
||||||
// short runes
|
|
||||||
{0, []byte{t2}},
|
|
||||||
{0, []byte{t3, tx}},
|
|
||||||
{0, []byte{t4, tx, tx}},
|
|
||||||
|
|
||||||
// we only support UTF-8 up to utf8.UTFMax bytes (4 bytes)
|
|
||||||
{1, []byte{t5, tx, tx, tx, tx}},
|
|
||||||
{1, []byte{t6, tx, tx, tx, tx, tx}},
|
|
||||||
}
|
|
||||||
|
|
||||||
func mkUTF8(r rune) ([]byte, int) {
|
|
||||||
var b [utf8.UTFMax]byte
|
|
||||||
sz := utf8.EncodeRune(b[:], r)
|
|
||||||
return b[:sz], sz
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLookup(t *testing.T) {
|
|
||||||
for i, tt := range testRunes {
|
|
||||||
b, szg := mkUTF8(tt)
|
|
||||||
v, szt := testdata.lookup(b)
|
|
||||||
if int(v) != i {
|
|
||||||
t.Errorf("lookup(%U): found value %#x, expected %#x", tt, v, i)
|
|
||||||
}
|
|
||||||
if szt != szg {
|
|
||||||
t.Errorf("lookup(%U): found size %d, expected %d", tt, szt, szg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i, tt := range tests {
|
|
||||||
v, sz := testdata.lookup(tt.bytes)
|
|
||||||
if v != 0 {
|
|
||||||
t.Errorf("lookup of illegal rune, case %d: found value %#x, expected 0", i, v)
|
|
||||||
}
|
|
||||||
if sz != tt.size {
|
|
||||||
t.Errorf("lookup of illegal rune, case %d: found size %d, expected %d", i, sz, tt.size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Verify defaults.
|
|
||||||
if v, _ := testdata.lookup([]byte{0xC1, 0x8C}); v != 0 {
|
|
||||||
t.Errorf("lookup of non-existing rune should be 0; found %X", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLookupUnsafe(t *testing.T) {
|
|
||||||
for i, tt := range testRunes {
|
|
||||||
b, _ := mkUTF8(tt)
|
|
||||||
v := testdata.lookupUnsafe(b)
|
|
||||||
if int(v) != i {
|
|
||||||
t.Errorf("lookupUnsafe(%U): found value %#x, expected %#x", i, v, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLookupString(t *testing.T) {
|
|
||||||
for i, tt := range testRunes {
|
|
||||||
b, szg := mkUTF8(tt)
|
|
||||||
v, szt := testdata.lookupString(string(b))
|
|
||||||
if int(v) != i {
|
|
||||||
t.Errorf("lookup(%U): found value %#x, expected %#x", i, v, i)
|
|
||||||
}
|
|
||||||
if szt != szg {
|
|
||||||
t.Errorf("lookup(%U): found size %d, expected %d", i, szt, szg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i, tt := range tests {
|
|
||||||
v, sz := testdata.lookupString(string(tt.bytes))
|
|
||||||
if int(v) != 0 {
|
|
||||||
t.Errorf("lookup of illegal rune, case %d: found value %#x, expected 0", i, v)
|
|
||||||
}
|
|
||||||
if sz != tt.size {
|
|
||||||
t.Errorf("lookup of illegal rune, case %d: found size %d, expected %d", i, sz, tt.size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLookupStringUnsafe(t *testing.T) {
|
|
||||||
for i, tt := range testRunes {
|
|
||||||
b, _ := mkUTF8(tt)
|
|
||||||
v := testdata.lookupStringUnsafe(string(b))
|
|
||||||
if int(v) != i {
|
|
||||||
t.Errorf("lookupUnsafe(%U): found value %#x, expected %#x", i, v, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-85
@@ -1,85 +0,0 @@
|
|||||||
// Generated by running
|
|
||||||
// maketesttables
|
|
||||||
// DO NOT EDIT
|
|
||||||
|
|
||||||
package norm
|
|
||||||
|
|
||||||
var testRunes = []int32{1, 12, 127, 128, 256, 2047, 2048, 2457, 65535, 65536, 65793, 1114111, 512, 513, 514, 528, 533}
|
|
||||||
|
|
||||||
// testdataValues: 192 entries, 384 bytes
|
|
||||||
// Block 2 is the null block.
|
|
||||||
var testdataValues = [192]uint16{
|
|
||||||
// Block 0x0, offset 0x0
|
|
||||||
0x000c: 0x0001,
|
|
||||||
// Block 0x1, offset 0x40
|
|
||||||
0x007f: 0x0002,
|
|
||||||
// Block 0x2, offset 0x80
|
|
||||||
}
|
|
||||||
|
|
||||||
// testdataSparseOffset: 10 entries, 20 bytes
|
|
||||||
var testdataSparseOffset = []uint16{0x0, 0x2, 0x4, 0x8, 0xa, 0xc, 0xe, 0x10, 0x12, 0x14}
|
|
||||||
|
|
||||||
// testdataSparseValues: 22 entries, 88 bytes
|
|
||||||
var testdataSparseValues = [22]valueRange{
|
|
||||||
// Block 0x0, offset 0x1
|
|
||||||
{value: 0x0000, lo: 0x01},
|
|
||||||
{value: 0x0003, lo: 0x80, hi: 0x80},
|
|
||||||
// Block 0x1, offset 0x2
|
|
||||||
{value: 0x0000, lo: 0x01},
|
|
||||||
{value: 0x0004, lo: 0x80, hi: 0x80},
|
|
||||||
// Block 0x2, offset 0x3
|
|
||||||
{value: 0x0001, lo: 0x03},
|
|
||||||
{value: 0x000c, lo: 0x80, hi: 0x82},
|
|
||||||
{value: 0x000f, lo: 0x90, hi: 0x90},
|
|
||||||
{value: 0x0010, lo: 0x95, hi: 0x95},
|
|
||||||
// Block 0x3, offset 0x4
|
|
||||||
{value: 0x0000, lo: 0x01},
|
|
||||||
{value: 0x0005, lo: 0xbf, hi: 0xbf},
|
|
||||||
// Block 0x4, offset 0x5
|
|
||||||
{value: 0x0000, lo: 0x01},
|
|
||||||
{value: 0x0006, lo: 0x80, hi: 0x80},
|
|
||||||
// Block 0x5, offset 0x6
|
|
||||||
{value: 0x0000, lo: 0x01},
|
|
||||||
{value: 0x0007, lo: 0x99, hi: 0x99},
|
|
||||||
// Block 0x6, offset 0x7
|
|
||||||
{value: 0x0000, lo: 0x01},
|
|
||||||
{value: 0x0008, lo: 0xbf, hi: 0xbf},
|
|
||||||
// Block 0x7, offset 0x8
|
|
||||||
{value: 0x0000, lo: 0x01},
|
|
||||||
{value: 0x0009, lo: 0x80, hi: 0x80},
|
|
||||||
// Block 0x8, offset 0x9
|
|
||||||
{value: 0x0000, lo: 0x01},
|
|
||||||
{value: 0x000a, lo: 0x81, hi: 0x81},
|
|
||||||
// Block 0x9, offset 0xa
|
|
||||||
{value: 0x0000, lo: 0x01},
|
|
||||||
{value: 0x000b, lo: 0xbf, hi: 0xbf},
|
|
||||||
}
|
|
||||||
|
|
||||||
// testdataLookup: 640 bytes
|
|
||||||
// Block 0 is the null block.
|
|
||||||
var testdataLookup = [640]uint8{
|
|
||||||
// Block 0x0, offset 0x0
|
|
||||||
// Block 0x1, offset 0x40
|
|
||||||
// Block 0x2, offset 0x80
|
|
||||||
// Block 0x3, offset 0xc0
|
|
||||||
0x0c2: 0x01, 0x0c4: 0x02,
|
|
||||||
0x0c8: 0x03,
|
|
||||||
0x0df: 0x04,
|
|
||||||
0x0e0: 0x02,
|
|
||||||
0x0ef: 0x03,
|
|
||||||
0x0f0: 0x05, 0x0f4: 0x07,
|
|
||||||
// Block 0x4, offset 0x100
|
|
||||||
0x120: 0x05, 0x126: 0x06,
|
|
||||||
// Block 0x5, offset 0x140
|
|
||||||
0x17f: 0x07,
|
|
||||||
// Block 0x6, offset 0x180
|
|
||||||
0x180: 0x08, 0x184: 0x09,
|
|
||||||
// Block 0x7, offset 0x1c0
|
|
||||||
0x1d0: 0x04,
|
|
||||||
// Block 0x8, offset 0x200
|
|
||||||
0x23f: 0x0a,
|
|
||||||
// Block 0x9, offset 0x240
|
|
||||||
0x24f: 0x06,
|
|
||||||
}
|
|
||||||
|
|
||||||
var testdataTrie = trie{testdataLookup[:], testdataValues[:], testdataSparseValues[:], testdataSparseOffset[:], 1}
|
|
||||||
-317
@@ -1,317 +0,0 @@
|
|||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build ignore
|
|
||||||
|
|
||||||
// Trie table generator.
|
|
||||||
// Used by make*tables tools to generate a go file with trie data structures
|
|
||||||
// for mapping UTF-8 to a 16-bit value. All but the last byte in a UTF-8 byte
|
|
||||||
// sequence are used to lookup offsets in the index table to be used for the
|
|
||||||
// next byte. The last byte is used to index into a table with 16-bit values.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"hash/crc32"
|
|
||||||
"log"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
blockSize = 64
|
|
||||||
blockOffset = 2 // Subtract two blocks to compensate for the 0x80 added to continuation bytes.
|
|
||||||
maxSparseEntries = 16
|
|
||||||
)
|
|
||||||
|
|
||||||
// Intermediate trie structure
|
|
||||||
type trieNode struct {
|
|
||||||
table [256]*trieNode
|
|
||||||
value int
|
|
||||||
b byte
|
|
||||||
leaf bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func newNode() *trieNode {
|
|
||||||
return new(trieNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n trieNode) String() string {
|
|
||||||
s := fmt.Sprint("trieNode{table: { non-nil at index: ")
|
|
||||||
for i, v := range n.table {
|
|
||||||
if v != nil {
|
|
||||||
s += fmt.Sprintf("%d, ", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s += fmt.Sprintf("}, value:%#x, b:%#x leaf:%v}", n.value, n.b, n.leaf)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n trieNode) isInternal() bool {
|
|
||||||
internal := true
|
|
||||||
for i := 0; i < 256; i++ {
|
|
||||||
if nn := n.table[i]; nn != nil {
|
|
||||||
if !internal && !nn.leaf {
|
|
||||||
log.Fatalf("triegen: isInternal: node contains both leaf and non-leaf children (%v)", n)
|
|
||||||
}
|
|
||||||
internal = internal && !nn.leaf
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return internal
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n trieNode) mostFrequentStride() int {
|
|
||||||
counts := make(map[int]int)
|
|
||||||
v := 0
|
|
||||||
for _, t := range n.table[0x80 : 0x80+blockSize] {
|
|
||||||
if t != nil {
|
|
||||||
if stride := t.value - v; v != 0 && stride >= 0 {
|
|
||||||
counts[stride]++
|
|
||||||
}
|
|
||||||
v = t.value
|
|
||||||
} else {
|
|
||||||
v = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var maxs, maxc int
|
|
||||||
for stride, cnt := range counts {
|
|
||||||
if cnt > maxc || (cnt == maxc && stride < maxs) {
|
|
||||||
maxs, maxc = stride, cnt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return maxs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n trieNode) countSparseEntries() int {
|
|
||||||
stride := n.mostFrequentStride()
|
|
||||||
var count, v int
|
|
||||||
for _, t := range n.table[0x80 : 0x80+blockSize] {
|
|
||||||
tv := 0
|
|
||||||
if t != nil {
|
|
||||||
tv = t.value
|
|
||||||
}
|
|
||||||
if tv-v != stride {
|
|
||||||
if tv != 0 {
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
v = tv
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *trieNode) insert(r rune, value uint16) {
|
|
||||||
var p [utf8.UTFMax]byte
|
|
||||||
sz := utf8.EncodeRune(p[:], r)
|
|
||||||
|
|
||||||
for i := 0; i < sz; i++ {
|
|
||||||
if n.leaf {
|
|
||||||
log.Fatalf("triegen: insert: node (%#v) should not be a leaf", n)
|
|
||||||
}
|
|
||||||
nn := n.table[p[i]]
|
|
||||||
if nn == nil {
|
|
||||||
nn = newNode()
|
|
||||||
nn.b = p[i]
|
|
||||||
n.table[p[i]] = nn
|
|
||||||
}
|
|
||||||
n = nn
|
|
||||||
}
|
|
||||||
n.value = int(value)
|
|
||||||
n.leaf = true
|
|
||||||
}
|
|
||||||
|
|
||||||
type nodeIndex struct {
|
|
||||||
lookupBlocks []*trieNode
|
|
||||||
valueBlocks []*trieNode
|
|
||||||
sparseBlocks []*trieNode
|
|
||||||
sparseOffset []uint16
|
|
||||||
sparseCount int
|
|
||||||
|
|
||||||
lookupBlockIdx map[uint32]int
|
|
||||||
valueBlockIdx map[uint32]int
|
|
||||||
}
|
|
||||||
|
|
||||||
func newIndex() *nodeIndex {
|
|
||||||
index := &nodeIndex{}
|
|
||||||
index.lookupBlocks = make([]*trieNode, 0)
|
|
||||||
index.valueBlocks = make([]*trieNode, 0)
|
|
||||||
index.sparseBlocks = make([]*trieNode, 0)
|
|
||||||
index.sparseOffset = make([]uint16, 1)
|
|
||||||
index.lookupBlockIdx = make(map[uint32]int)
|
|
||||||
index.valueBlockIdx = make(map[uint32]int)
|
|
||||||
return index
|
|
||||||
}
|
|
||||||
|
|
||||||
func computeOffsets(index *nodeIndex, n *trieNode) int {
|
|
||||||
if n.leaf {
|
|
||||||
return n.value
|
|
||||||
}
|
|
||||||
hasher := crc32.New(crc32.MakeTable(crc32.IEEE))
|
|
||||||
// We only index continuation bytes.
|
|
||||||
for i := 0; i < blockSize; i++ {
|
|
||||||
v := 0
|
|
||||||
if nn := n.table[0x80+i]; nn != nil {
|
|
||||||
v = computeOffsets(index, nn)
|
|
||||||
}
|
|
||||||
hasher.Write([]byte{uint8(v >> 8), uint8(v)})
|
|
||||||
}
|
|
||||||
h := hasher.Sum32()
|
|
||||||
if n.isInternal() {
|
|
||||||
v, ok := index.lookupBlockIdx[h]
|
|
||||||
if !ok {
|
|
||||||
v = len(index.lookupBlocks) - blockOffset
|
|
||||||
index.lookupBlocks = append(index.lookupBlocks, n)
|
|
||||||
index.lookupBlockIdx[h] = v
|
|
||||||
}
|
|
||||||
n.value = v
|
|
||||||
} else {
|
|
||||||
v, ok := index.valueBlockIdx[h]
|
|
||||||
if !ok {
|
|
||||||
if c := n.countSparseEntries(); c > maxSparseEntries {
|
|
||||||
v = len(index.valueBlocks) - blockOffset
|
|
||||||
index.valueBlocks = append(index.valueBlocks, n)
|
|
||||||
index.valueBlockIdx[h] = v
|
|
||||||
} else {
|
|
||||||
v = -len(index.sparseOffset)
|
|
||||||
index.sparseBlocks = append(index.sparseBlocks, n)
|
|
||||||
index.sparseOffset = append(index.sparseOffset, uint16(index.sparseCount))
|
|
||||||
index.sparseCount += c + 1
|
|
||||||
index.valueBlockIdx[h] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
n.value = v
|
|
||||||
}
|
|
||||||
return n.value
|
|
||||||
}
|
|
||||||
|
|
||||||
func printValueBlock(nr int, n *trieNode, offset int) {
|
|
||||||
boff := nr * blockSize
|
|
||||||
fmt.Printf("\n// Block %#x, offset %#x", nr, boff)
|
|
||||||
var printnewline bool
|
|
||||||
for i := 0; i < blockSize; i++ {
|
|
||||||
if i%6 == 0 {
|
|
||||||
printnewline = true
|
|
||||||
}
|
|
||||||
v := 0
|
|
||||||
if nn := n.table[i+offset]; nn != nil {
|
|
||||||
v = nn.value
|
|
||||||
}
|
|
||||||
if v != 0 {
|
|
||||||
if printnewline {
|
|
||||||
fmt.Printf("\n")
|
|
||||||
printnewline = false
|
|
||||||
}
|
|
||||||
fmt.Printf("%#04x:%#04x, ", boff+i, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func printSparseBlock(nr int, n *trieNode) {
|
|
||||||
boff := -n.value
|
|
||||||
fmt.Printf("\n// Block %#x, offset %#x", nr, boff)
|
|
||||||
v := 0
|
|
||||||
//stride := f(n)
|
|
||||||
stride := n.mostFrequentStride()
|
|
||||||
c := n.countSparseEntries()
|
|
||||||
fmt.Printf("\n{value:%#04x,lo:%#02x},", stride, uint8(c))
|
|
||||||
for i, nn := range n.table[0x80 : 0x80+blockSize] {
|
|
||||||
nv := 0
|
|
||||||
if nn != nil {
|
|
||||||
nv = nn.value
|
|
||||||
}
|
|
||||||
if nv-v != stride {
|
|
||||||
if v != 0 {
|
|
||||||
fmt.Printf(",hi:%#02x},", 0x80+i-1)
|
|
||||||
}
|
|
||||||
if nv != 0 {
|
|
||||||
fmt.Printf("\n{value:%#04x,lo:%#02x", nv, nn.b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
v = nv
|
|
||||||
}
|
|
||||||
if v != 0 {
|
|
||||||
fmt.Printf(",hi:%#02x},", 0x80+blockSize-1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func printLookupBlock(nr int, n *trieNode, offset, cutoff int) {
|
|
||||||
boff := nr * blockSize
|
|
||||||
fmt.Printf("\n// Block %#x, offset %#x", nr, boff)
|
|
||||||
var printnewline bool
|
|
||||||
for i := 0; i < blockSize; i++ {
|
|
||||||
if i%8 == 0 {
|
|
||||||
printnewline = true
|
|
||||||
}
|
|
||||||
v := 0
|
|
||||||
if nn := n.table[i+offset]; nn != nil {
|
|
||||||
v = nn.value
|
|
||||||
}
|
|
||||||
if v != 0 {
|
|
||||||
if v < 0 {
|
|
||||||
v = -v - 1 + cutoff
|
|
||||||
}
|
|
||||||
if printnewline {
|
|
||||||
fmt.Printf("\n")
|
|
||||||
printnewline = false
|
|
||||||
}
|
|
||||||
fmt.Printf("%#03x:%#02x, ", boff+i, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// printTables returns the size in bytes of the generated tables.
|
|
||||||
func (t *trieNode) printTables(name string) int {
|
|
||||||
index := newIndex()
|
|
||||||
// Values for 7-bit ASCII are stored in first two block, followed by nil block.
|
|
||||||
index.valueBlocks = append(index.valueBlocks, nil, nil, nil)
|
|
||||||
// First byte of multi-byte UTF-8 codepoints are indexed in 4th block.
|
|
||||||
index.lookupBlocks = append(index.lookupBlocks, nil, nil, nil, nil)
|
|
||||||
// Index starter bytes of multi-byte UTF-8.
|
|
||||||
for i := 0xC0; i < 0x100; i++ {
|
|
||||||
if t.table[i] != nil {
|
|
||||||
computeOffsets(index, t.table[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nv := len(index.valueBlocks) * blockSize
|
|
||||||
fmt.Printf("// %sValues: %d entries, %d bytes\n", name, nv, nv*2)
|
|
||||||
fmt.Printf("// Block 2 is the null block.\n")
|
|
||||||
fmt.Printf("var %sValues = [%d]uint16 {", name, nv)
|
|
||||||
printValueBlock(0, t, 0)
|
|
||||||
printValueBlock(1, t, 64)
|
|
||||||
printValueBlock(2, newNode(), 0)
|
|
||||||
for i := 3; i < len(index.valueBlocks); i++ {
|
|
||||||
printValueBlock(i, index.valueBlocks[i], 0x80)
|
|
||||||
}
|
|
||||||
fmt.Print("\n}\n\n")
|
|
||||||
|
|
||||||
ls := len(index.sparseBlocks)
|
|
||||||
fmt.Printf("// %sSparseOffset: %d entries, %d bytes\n", name, ls, ls*2)
|
|
||||||
fmt.Printf("var %sSparseOffset = %#v\n\n", name, index.sparseOffset[1:])
|
|
||||||
|
|
||||||
ns := index.sparseCount
|
|
||||||
fmt.Printf("// %sSparseValues: %d entries, %d bytes\n", name, ns, ns*4)
|
|
||||||
fmt.Printf("var %sSparseValues = [%d]valueRange {", name, ns)
|
|
||||||
for i, n := range index.sparseBlocks {
|
|
||||||
printSparseBlock(i, n)
|
|
||||||
}
|
|
||||||
fmt.Print("\n}\n\n")
|
|
||||||
|
|
||||||
cutoff := len(index.valueBlocks) - blockOffset
|
|
||||||
ni := len(index.lookupBlocks) * blockSize
|
|
||||||
fmt.Printf("// %sLookup: %d bytes\n", name, ni)
|
|
||||||
fmt.Printf("// Block 0 is the null block.\n")
|
|
||||||
fmt.Printf("var %sLookup = [%d]uint8 {", name, ni)
|
|
||||||
printLookupBlock(0, newNode(), 0, cutoff)
|
|
||||||
printLookupBlock(1, newNode(), 0, cutoff)
|
|
||||||
printLookupBlock(2, newNode(), 0, cutoff)
|
|
||||||
printLookupBlock(3, t, 0xC0, cutoff)
|
|
||||||
for i := 4; i < len(index.lookupBlocks); i++ {
|
|
||||||
printLookupBlock(i, index.lookupBlocks[i], 0x80, cutoff)
|
|
||||||
}
|
|
||||||
fmt.Print("\n}\n\n")
|
|
||||||
fmt.Printf("var %sTrie = trie{ %sLookup[:], %sValues[:], %sSparseValues[:], %sSparseOffset[:], %d}\n\n",
|
|
||||||
name, name, name, name, name, cutoff)
|
|
||||||
return nv*2 + ns*4 + ni + ls*2
|
|
||||||
}
|
|
||||||
-19
@@ -1,19 +0,0 @@
|
|||||||
Copyright (C) 2012 Dave Grijalva
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
copy of this software and associated documentation files (the "Software"),
|
|
||||||
to deal in the Software without restriction, including without limitation
|
|
||||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included
|
|
||||||
in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
||||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
||||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
-19
@@ -1,19 +0,0 @@
|
|||||||
A simple LFU cache for golang. Based on the paper [An O(1) algorithm for implementing the LFU cache eviction scheme](http://dhruvbird.com/lfu.pdf).
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/dgrijalva/lfu-go"
|
|
||||||
|
|
||||||
// Make a new thing
|
|
||||||
c := lfu.New()
|
|
||||||
|
|
||||||
// Set some values
|
|
||||||
c.Set("myKey", myValue)
|
|
||||||
|
|
||||||
// Retrieve some values
|
|
||||||
myValue = c.Get("myKey")
|
|
||||||
|
|
||||||
// Evict some values
|
|
||||||
c.Evict(1)
|
|
||||||
```
|
|
||||||
-156
@@ -1,156 +0,0 @@
|
|||||||
package lfu
|
|
||||||
|
|
||||||
import (
|
|
||||||
"container/list"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Eviction struct {
|
|
||||||
Key string
|
|
||||||
Value interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Cache struct {
|
|
||||||
// If len > UpperBound, cache will automatically evict
|
|
||||||
// down to LowerBound. If either value is 0, this behavior
|
|
||||||
// is disabled.
|
|
||||||
UpperBound int
|
|
||||||
LowerBound int
|
|
||||||
values map[string]*cacheEntry
|
|
||||||
freqs *list.List
|
|
||||||
len int
|
|
||||||
lock *sync.Mutex
|
|
||||||
EvictionChannel chan<- Eviction
|
|
||||||
}
|
|
||||||
|
|
||||||
type cacheEntry struct {
|
|
||||||
key string
|
|
||||||
value interface{}
|
|
||||||
freqNode *list.Element
|
|
||||||
}
|
|
||||||
|
|
||||||
type listEntry struct {
|
|
||||||
entries map[*cacheEntry]byte
|
|
||||||
freq int
|
|
||||||
}
|
|
||||||
|
|
||||||
func New() *Cache {
|
|
||||||
c := new(Cache)
|
|
||||||
c.values = make(map[string]*cacheEntry)
|
|
||||||
c.freqs = list.New()
|
|
||||||
c.lock = new(sync.Mutex)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cache) Get(key string) interface{} {
|
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
if e, ok := c.values[key]; ok {
|
|
||||||
c.increment(e)
|
|
||||||
return e.value
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cache) Set(key string, value interface{}) {
|
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
if e, ok := c.values[key]; ok {
|
|
||||||
// value already exists for key. overwrite
|
|
||||||
e.value = value
|
|
||||||
c.increment(e)
|
|
||||||
} else {
|
|
||||||
// value doesn't exist. insert
|
|
||||||
e := new(cacheEntry)
|
|
||||||
e.key = key
|
|
||||||
e.value = value
|
|
||||||
c.values[key] = e
|
|
||||||
c.increment(e)
|
|
||||||
c.len++
|
|
||||||
// bounds mgmt
|
|
||||||
if c.UpperBound > 0 && c.LowerBound > 0 {
|
|
||||||
if c.len > c.UpperBound {
|
|
||||||
c.evict(c.len - c.LowerBound)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cache) Len() int {
|
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
return c.len
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cache) Evict(count int) int {
|
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
return c.evict(count)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cache) evict(count int) int {
|
|
||||||
// No lock here so it can be called
|
|
||||||
// from within the lock (during Set)
|
|
||||||
var evicted int
|
|
||||||
for i := 0; i < count; {
|
|
||||||
if place := c.freqs.Front(); place != nil {
|
|
||||||
for entry, _ := range place.Value.(*listEntry).entries {
|
|
||||||
if i < count {
|
|
||||||
if c.EvictionChannel != nil {
|
|
||||||
c.EvictionChannel <- Eviction{
|
|
||||||
Key: entry.key,
|
|
||||||
Value: entry.value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete(c.values, entry.key)
|
|
||||||
c.remEntry(place, entry)
|
|
||||||
evicted++
|
|
||||||
c.len--
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return evicted
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cache) increment(e *cacheEntry) {
|
|
||||||
currentPlace := e.freqNode
|
|
||||||
var nextFreq int
|
|
||||||
var nextPlace *list.Element
|
|
||||||
if currentPlace == nil {
|
|
||||||
// new entry
|
|
||||||
nextFreq = 1
|
|
||||||
nextPlace = c.freqs.Front()
|
|
||||||
} else {
|
|
||||||
// move up
|
|
||||||
nextFreq = currentPlace.Value.(*listEntry).freq + 1
|
|
||||||
nextPlace = currentPlace.Next()
|
|
||||||
}
|
|
||||||
|
|
||||||
if nextPlace == nil || nextPlace.Value.(*listEntry).freq != nextFreq {
|
|
||||||
// create a new list entry
|
|
||||||
li := new(listEntry)
|
|
||||||
li.freq = nextFreq
|
|
||||||
li.entries = make(map[*cacheEntry]byte)
|
|
||||||
if currentPlace != nil {
|
|
||||||
nextPlace = c.freqs.InsertAfter(li, currentPlace)
|
|
||||||
} else {
|
|
||||||
nextPlace = c.freqs.PushFront(li)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
e.freqNode = nextPlace
|
|
||||||
nextPlace.Value.(*listEntry).entries[e] = 1
|
|
||||||
if currentPlace != nil {
|
|
||||||
// remove from current position
|
|
||||||
c.remEntry(currentPlace, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cache) remEntry(place *list.Element, entry *cacheEntry) {
|
|
||||||
entries := place.Value.(*listEntry).entries
|
|
||||||
delete(entries, entry)
|
|
||||||
if len(entries) == 0 {
|
|
||||||
c.freqs.Remove(place)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-68
@@ -1,68 +0,0 @@
|
|||||||
package lfu
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestLFU(t *testing.T) {
|
|
||||||
c := New()
|
|
||||||
c.Set("a", "a")
|
|
||||||
if v := c.Get("a"); v != "a" {
|
|
||||||
t.Errorf("Value was not saved: %v != 'a'", v)
|
|
||||||
}
|
|
||||||
if l := c.Len(); l != 1 {
|
|
||||||
t.Errorf("Length was not updated: %v != 1", l)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Set("b", "b")
|
|
||||||
if v := c.Get("b"); v != "b" {
|
|
||||||
t.Errorf("Value was not saved: %v != 'b'", v)
|
|
||||||
}
|
|
||||||
if l := c.Len(); l != 2 {
|
|
||||||
t.Errorf("Length was not updated: %v != 2", l)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Get("a")
|
|
||||||
evicted := c.Evict(1)
|
|
||||||
if v := c.Get("a"); v != "a" {
|
|
||||||
t.Errorf("Value was improperly evicted: %v != 'a'", v)
|
|
||||||
}
|
|
||||||
if v := c.Get("b"); v != nil {
|
|
||||||
t.Errorf("Value was not evicted: %v", v)
|
|
||||||
}
|
|
||||||
if l := c.Len(); l != 1 {
|
|
||||||
t.Errorf("Length was not updated: %v != 1", l)
|
|
||||||
}
|
|
||||||
if evicted != 1 {
|
|
||||||
t.Errorf("Number of evicted items is wrong: %v != 1", evicted)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBoundsMgmt(t *testing.T) {
|
|
||||||
c := New()
|
|
||||||
c.UpperBound = 10
|
|
||||||
c.LowerBound = 5
|
|
||||||
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
c.Set(fmt.Sprintf("%v", i), i)
|
|
||||||
}
|
|
||||||
if c.Len() > 10 {
|
|
||||||
t.Errorf("Bounds management failed to evict properly: %v", c.Len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEviction(t *testing.T) {
|
|
||||||
ch := make(chan Eviction, 1)
|
|
||||||
|
|
||||||
c := New()
|
|
||||||
c.EvictionChannel = ch
|
|
||||||
c.Set("a", "b")
|
|
||||||
c.Evict(1)
|
|
||||||
|
|
||||||
ev := <-ch
|
|
||||||
|
|
||||||
if ev.Key != "a" || ev.Value.(string) != "b" {
|
|
||||||
t.Error("Incorrect item")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+194
-194
@@ -1,194 +1,194 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2011-2012 Branimir Karadzic. All rights reserved.
|
* Copyright 2011-2012 Branimir Karadzic. All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without modification,
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
* are permitted provided that the following conditions are met:
|
* are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR
|
* THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR
|
||||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||||
* SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
* SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package lz4
|
package lz4
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ErrCorrupt indicates the input was corrupt
|
// ErrCorrupt indicates the input was corrupt
|
||||||
ErrCorrupt = errors.New("corrupt input")
|
ErrCorrupt = errors.New("corrupt input")
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
mlBits = 4
|
mlBits = 4
|
||||||
mlMask = (1 << mlBits) - 1
|
mlMask = (1 << mlBits) - 1
|
||||||
runBits = 8 - mlBits
|
runBits = 8 - mlBits
|
||||||
runMask = (1 << runBits) - 1
|
runMask = (1 << runBits) - 1
|
||||||
)
|
)
|
||||||
|
|
||||||
type decoder struct {
|
type decoder struct {
|
||||||
src []byte
|
src []byte
|
||||||
dst []byte
|
dst []byte
|
||||||
spos uint32
|
spos uint32
|
||||||
dpos uint32
|
dpos uint32
|
||||||
ref uint32
|
ref uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) readByte() (uint8, error) {
|
func (d *decoder) readByte() (uint8, error) {
|
||||||
if int(d.spos) == len(d.src) {
|
if int(d.spos) == len(d.src) {
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
}
|
}
|
||||||
b := d.src[d.spos]
|
b := d.src[d.spos]
|
||||||
d.spos++
|
d.spos++
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) getLen() (uint32, error) {
|
func (d *decoder) getLen() (uint32, error) {
|
||||||
|
|
||||||
length := uint32(0)
|
length := uint32(0)
|
||||||
ln, err := d.readByte()
|
ln, err := d.readByte()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, ErrCorrupt
|
return 0, ErrCorrupt
|
||||||
}
|
}
|
||||||
for ln == 255 {
|
for ln == 255 {
|
||||||
length += 255
|
length += 255
|
||||||
ln, err = d.readByte()
|
ln, err = d.readByte()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, ErrCorrupt
|
return 0, ErrCorrupt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
length += uint32(ln)
|
length += uint32(ln)
|
||||||
|
|
||||||
return length, nil
|
return length, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) cp(length, decr uint32) {
|
func (d *decoder) cp(length, decr uint32) {
|
||||||
|
|
||||||
if int(d.ref+length) < int(d.dpos) {
|
if int(d.ref+length) < int(d.dpos) {
|
||||||
copy(d.dst[d.dpos:], d.dst[d.ref:d.ref+length])
|
copy(d.dst[d.dpos:], d.dst[d.ref:d.ref+length])
|
||||||
} else {
|
} else {
|
||||||
for ii := uint32(0); ii < length; ii++ {
|
for ii := uint32(0); ii < length; ii++ {
|
||||||
d.dst[d.dpos+ii] = d.dst[d.ref+ii]
|
d.dst[d.dpos+ii] = d.dst[d.ref+ii]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
d.dpos += length
|
d.dpos += length
|
||||||
d.ref += length - decr
|
d.ref += length - decr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) finish(err error) error {
|
func (d *decoder) finish(err error) error {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode returns the decoded form of src. The returned slice may be a
|
// Decode returns the decoded form of src. The returned slice may be a
|
||||||
// subslice of dst if it was large enough to hold the entire decoded block.
|
// subslice of dst if it was large enough to hold the entire decoded block.
|
||||||
func Decode(dst, src []byte) ([]byte, error) {
|
func Decode(dst, src []byte) ([]byte, error) {
|
||||||
|
|
||||||
if len(src) < 4 {
|
if len(src) < 4 {
|
||||||
return nil, ErrCorrupt
|
return nil, ErrCorrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
uncompressedLen := binary.LittleEndian.Uint32(src)
|
uncompressedLen := binary.LittleEndian.Uint32(src)
|
||||||
|
|
||||||
if uncompressedLen == 0 {
|
if uncompressedLen == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if uncompressedLen > MaxInputSize {
|
if uncompressedLen > MaxInputSize {
|
||||||
return nil, ErrTooLarge
|
return nil, ErrTooLarge
|
||||||
}
|
}
|
||||||
|
|
||||||
if dst == nil || len(dst) < int(uncompressedLen) {
|
if dst == nil || len(dst) < int(uncompressedLen) {
|
||||||
dst = make([]byte, uncompressedLen)
|
dst = make([]byte, uncompressedLen)
|
||||||
}
|
}
|
||||||
|
|
||||||
d := decoder{src: src, dst: dst[:uncompressedLen], spos: 4}
|
d := decoder{src: src, dst: dst[:uncompressedLen], spos: 4}
|
||||||
|
|
||||||
decr := []uint32{0, 3, 2, 3}
|
decr := []uint32{0, 3, 2, 3}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
code, err := d.readByte()
|
code, err := d.readByte()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return d.dst, d.finish(err)
|
return d.dst, d.finish(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
length := uint32(code >> mlBits)
|
length := uint32(code >> mlBits)
|
||||||
if length == runMask {
|
if length == runMask {
|
||||||
ln, err := d.getLen()
|
ln, err := d.getLen()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ErrCorrupt
|
return nil, ErrCorrupt
|
||||||
}
|
}
|
||||||
length += ln
|
length += ln
|
||||||
}
|
}
|
||||||
|
|
||||||
if int(d.spos+length) > len(d.src) {
|
if int(d.spos+length) > len(d.src) {
|
||||||
return nil, ErrCorrupt
|
return nil, ErrCorrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
for ii := uint32(0); ii < length; ii++ {
|
for ii := uint32(0); ii < length; ii++ {
|
||||||
d.dst[d.dpos+ii] = d.src[d.spos+ii]
|
d.dst[d.dpos+ii] = d.src[d.spos+ii]
|
||||||
}
|
}
|
||||||
|
|
||||||
d.spos += length
|
d.spos += length
|
||||||
d.dpos += length
|
d.dpos += length
|
||||||
|
|
||||||
if int(d.spos) == len(d.src) {
|
if int(d.spos) == len(d.src) {
|
||||||
return d.dst, nil
|
return d.dst, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if int(d.spos+2) >= len(d.src) {
|
if int(d.spos+2) >= len(d.src) {
|
||||||
return nil, ErrCorrupt
|
return nil, ErrCorrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
back := uint32(d.src[d.spos]) | uint32(d.src[d.spos+1])<<8
|
back := uint32(d.src[d.spos]) | uint32(d.src[d.spos+1])<<8
|
||||||
|
|
||||||
if back > d.dpos {
|
if back > d.dpos {
|
||||||
return nil, ErrCorrupt
|
return nil, ErrCorrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
d.spos += 2
|
d.spos += 2
|
||||||
d.ref = d.dpos - back
|
d.ref = d.dpos - back
|
||||||
|
|
||||||
length = uint32(code & mlMask)
|
length = uint32(code & mlMask)
|
||||||
if length == mlMask {
|
if length == mlMask {
|
||||||
ln, err := d.getLen()
|
ln, err := d.getLen()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ErrCorrupt
|
return nil, ErrCorrupt
|
||||||
}
|
}
|
||||||
length += ln
|
length += ln
|
||||||
}
|
}
|
||||||
|
|
||||||
literal := d.dpos - d.ref
|
literal := d.dpos - d.ref
|
||||||
if literal < 4 {
|
if literal < 4 {
|
||||||
d.cp(4, decr[literal])
|
d.cp(4, decr[literal])
|
||||||
} else {
|
} else {
|
||||||
length += 4
|
length += 4
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.dpos+length > uncompressedLen {
|
if d.dpos+length > uncompressedLen {
|
||||||
return nil, ErrCorrupt
|
return nil, ErrCorrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
d.cp(length, 0)
|
d.cp(length, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+188
-188
@@ -1,188 +1,188 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2011-2012 Branimir Karadzic. All rights reserved.
|
* Copyright 2011-2012 Branimir Karadzic. All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without modification,
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
* are permitted provided that the following conditions are met:
|
* are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR
|
* THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR
|
||||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||||
* SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
* SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package lz4
|
package lz4
|
||||||
|
|
||||||
import "encoding/binary"
|
import "encoding/binary"
|
||||||
import "errors"
|
import "errors"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
minMatch = 4
|
minMatch = 4
|
||||||
hashLog = 17
|
hashLog = 17
|
||||||
hashTableSize = 1 << hashLog
|
hashTableSize = 1 << hashLog
|
||||||
hashShift = (minMatch * 8) - hashLog
|
hashShift = (minMatch * 8) - hashLog
|
||||||
incompressible uint32 = 128
|
incompressible uint32 = 128
|
||||||
uninitHash = 0x88888888
|
uninitHash = 0x88888888
|
||||||
|
|
||||||
// MaxInputSize is the largest buffer than can be compressed in a single block
|
// MaxInputSize is the largest buffer than can be compressed in a single block
|
||||||
MaxInputSize = 0x7E000000
|
MaxInputSize = 0x7E000000
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ErrTooLarge indicates the input buffer was too large
|
// ErrTooLarge indicates the input buffer was too large
|
||||||
ErrTooLarge = errors.New("input too large")
|
ErrTooLarge = errors.New("input too large")
|
||||||
)
|
)
|
||||||
|
|
||||||
type encoder struct {
|
type encoder struct {
|
||||||
src []byte
|
src []byte
|
||||||
dst []byte
|
dst []byte
|
||||||
hashTable []uint32
|
hashTable []uint32
|
||||||
pos uint32
|
pos uint32
|
||||||
anchor uint32
|
anchor uint32
|
||||||
dpos uint32
|
dpos uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompressBound returns the maximum length of a lz4 block, given it's uncompressed length
|
// CompressBound returns the maximum length of a lz4 block, given it's uncompressed length
|
||||||
func CompressBound(isize int) int {
|
func CompressBound(isize int) int {
|
||||||
if isize > MaxInputSize {
|
if isize > MaxInputSize {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return isize + ((isize) / 255) + 16 + 4
|
return isize + ((isize) / 255) + 16 + 4
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *encoder) writeLiterals(length, mlLen, pos uint32) {
|
func (e *encoder) writeLiterals(length, mlLen, pos uint32) {
|
||||||
|
|
||||||
ln := length
|
ln := length
|
||||||
|
|
||||||
var code byte
|
var code byte
|
||||||
if ln > runMask-1 {
|
if ln > runMask-1 {
|
||||||
code = runMask
|
code = runMask
|
||||||
} else {
|
} else {
|
||||||
code = byte(ln)
|
code = byte(ln)
|
||||||
}
|
}
|
||||||
|
|
||||||
if mlLen > mlMask-1 {
|
if mlLen > mlMask-1 {
|
||||||
e.dst[e.dpos] = (code << mlBits) + byte(mlMask)
|
e.dst[e.dpos] = (code << mlBits) + byte(mlMask)
|
||||||
} else {
|
} else {
|
||||||
e.dst[e.dpos] = (code << mlBits) + byte(mlLen)
|
e.dst[e.dpos] = (code << mlBits) + byte(mlLen)
|
||||||
}
|
}
|
||||||
e.dpos++
|
e.dpos++
|
||||||
|
|
||||||
if code == runMask {
|
if code == runMask {
|
||||||
ln -= runMask
|
ln -= runMask
|
||||||
for ; ln > 254; ln -= 255 {
|
for ; ln > 254; ln -= 255 {
|
||||||
e.dst[e.dpos] = 255
|
e.dst[e.dpos] = 255
|
||||||
e.dpos++
|
e.dpos++
|
||||||
}
|
}
|
||||||
|
|
||||||
e.dst[e.dpos] = byte(ln)
|
e.dst[e.dpos] = byte(ln)
|
||||||
e.dpos++
|
e.dpos++
|
||||||
}
|
}
|
||||||
|
|
||||||
for ii := uint32(0); ii < length; ii++ {
|
for ii := uint32(0); ii < length; ii++ {
|
||||||
e.dst[e.dpos+ii] = e.src[pos+ii]
|
e.dst[e.dpos+ii] = e.src[pos+ii]
|
||||||
}
|
}
|
||||||
|
|
||||||
e.dpos += length
|
e.dpos += length
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode returns the encoded form of src. The returned array may be a
|
// Encode returns the encoded form of src. The returned array may be a
|
||||||
// sub-slice of dst if it was large enough to hold the entire output.
|
// sub-slice of dst if it was large enough to hold the entire output.
|
||||||
func Encode(dst, src []byte) ([]byte, error) {
|
func Encode(dst, src []byte) ([]byte, error) {
|
||||||
|
|
||||||
if len(src) >= MaxInputSize {
|
if len(src) >= MaxInputSize {
|
||||||
return nil, ErrTooLarge
|
return nil, ErrTooLarge
|
||||||
}
|
}
|
||||||
|
|
||||||
if n := CompressBound(len(src)); len(dst) < n {
|
if n := CompressBound(len(src)); len(dst) < n {
|
||||||
dst = make([]byte, n)
|
dst = make([]byte, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
e := encoder{src: src, dst: dst, hashTable: make([]uint32, hashTableSize)}
|
e := encoder{src: src, dst: dst, hashTable: make([]uint32, hashTableSize)}
|
||||||
|
|
||||||
binary.LittleEndian.PutUint32(dst, uint32(len(src)))
|
binary.LittleEndian.PutUint32(dst, uint32(len(src)))
|
||||||
e.dpos = 4
|
e.dpos = 4
|
||||||
|
|
||||||
var (
|
var (
|
||||||
step uint32 = 1
|
step uint32 = 1
|
||||||
limit = incompressible
|
limit = incompressible
|
||||||
)
|
)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if int(e.pos)+12 >= len(e.src) {
|
if int(e.pos)+12 >= len(e.src) {
|
||||||
e.writeLiterals(uint32(len(e.src))-e.anchor, 0, e.anchor)
|
e.writeLiterals(uint32(len(e.src))-e.anchor, 0, e.anchor)
|
||||||
return e.dst[:e.dpos], nil
|
return e.dst[:e.dpos], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
sequence := uint32(e.src[e.pos+3])<<24 | uint32(e.src[e.pos+2])<<16 | uint32(e.src[e.pos+1])<<8 | uint32(e.src[e.pos+0])
|
sequence := uint32(e.src[e.pos+3])<<24 | uint32(e.src[e.pos+2])<<16 | uint32(e.src[e.pos+1])<<8 | uint32(e.src[e.pos+0])
|
||||||
|
|
||||||
hash := (sequence * 2654435761) >> hashShift
|
hash := (sequence * 2654435761) >> hashShift
|
||||||
ref := e.hashTable[hash] + uninitHash
|
ref := e.hashTable[hash] + uninitHash
|
||||||
e.hashTable[hash] = e.pos - uninitHash
|
e.hashTable[hash] = e.pos - uninitHash
|
||||||
|
|
||||||
if ((e.pos-ref)>>16) != 0 || uint32(e.src[ref+3])<<24|uint32(e.src[ref+2])<<16|uint32(e.src[ref+1])<<8|uint32(e.src[ref+0]) != sequence {
|
if ((e.pos-ref)>>16) != 0 || uint32(e.src[ref+3])<<24|uint32(e.src[ref+2])<<16|uint32(e.src[ref+1])<<8|uint32(e.src[ref+0]) != sequence {
|
||||||
if e.pos-e.anchor > limit {
|
if e.pos-e.anchor > limit {
|
||||||
limit <<= 1
|
limit <<= 1
|
||||||
step += 1 + (step >> 2)
|
step += 1 + (step >> 2)
|
||||||
}
|
}
|
||||||
e.pos += step
|
e.pos += step
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if step > 1 {
|
if step > 1 {
|
||||||
e.hashTable[hash] = ref - uninitHash
|
e.hashTable[hash] = ref - uninitHash
|
||||||
e.pos -= step - 1
|
e.pos -= step - 1
|
||||||
step = 1
|
step = 1
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
limit = incompressible
|
limit = incompressible
|
||||||
|
|
||||||
ln := e.pos - e.anchor
|
ln := e.pos - e.anchor
|
||||||
back := e.pos - ref
|
back := e.pos - ref
|
||||||
|
|
||||||
anchor := e.anchor
|
anchor := e.anchor
|
||||||
|
|
||||||
e.pos += minMatch
|
e.pos += minMatch
|
||||||
ref += minMatch
|
ref += minMatch
|
||||||
e.anchor = e.pos
|
e.anchor = e.pos
|
||||||
|
|
||||||
for int(e.pos) < len(e.src)-5 && e.src[e.pos] == e.src[ref] {
|
for int(e.pos) < len(e.src)-5 && e.src[e.pos] == e.src[ref] {
|
||||||
e.pos++
|
e.pos++
|
||||||
ref++
|
ref++
|
||||||
}
|
}
|
||||||
|
|
||||||
mlLen := e.pos - e.anchor
|
mlLen := e.pos - e.anchor
|
||||||
|
|
||||||
e.writeLiterals(ln, mlLen, anchor)
|
e.writeLiterals(ln, mlLen, anchor)
|
||||||
e.dst[e.dpos] = uint8(back)
|
e.dst[e.dpos] = uint8(back)
|
||||||
e.dst[e.dpos+1] = uint8(back >> 8)
|
e.dst[e.dpos+1] = uint8(back >> 8)
|
||||||
e.dpos += 2
|
e.dpos += 2
|
||||||
|
|
||||||
if mlLen > mlMask-1 {
|
if mlLen > mlMask-1 {
|
||||||
mlLen -= mlMask
|
mlLen -= mlMask
|
||||||
for mlLen > 254 {
|
for mlLen > 254 {
|
||||||
mlLen -= 255
|
mlLen -= 255
|
||||||
|
|
||||||
e.dst[e.dpos] = 255
|
e.dst[e.dpos] = 255
|
||||||
e.dpos++
|
e.dpos++
|
||||||
}
|
}
|
||||||
|
|
||||||
e.dst[e.dpos] = byte(mlLen)
|
e.dst[e.dpos] = byte(mlLen)
|
||||||
e.dpos++
|
e.dpos++
|
||||||
}
|
}
|
||||||
|
|
||||||
e.anchor = e.pos
|
e.anchor = e.pos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
@@ -16,6 +16,7 @@ type LogLevel int
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
LevelDebug LogLevel = iota
|
LevelDebug LogLevel = iota
|
||||||
|
LevelVerbose
|
||||||
LevelInfo
|
LevelInfo
|
||||||
LevelOK
|
LevelOK
|
||||||
LevelWarn
|
LevelWarn
|
||||||
@@ -83,6 +84,24 @@ func (l *Logger) Debugf(format string, vals ...interface{}) {
|
|||||||
l.callHandlers(LevelDebug, s)
|
l.callHandlers(LevelDebug, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Infoln logs a line with a VERBOSE prefix.
|
||||||
|
func (l *Logger) Verboseln(vals ...interface{}) {
|
||||||
|
l.mut.Lock()
|
||||||
|
defer l.mut.Unlock()
|
||||||
|
s := fmt.Sprintln(vals...)
|
||||||
|
l.logger.Output(2, "VERBOSE: "+s)
|
||||||
|
l.callHandlers(LevelVerbose, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infof logs a formatted line with a VERBOSE prefix.
|
||||||
|
func (l *Logger) Verbosef(format string, vals ...interface{}) {
|
||||||
|
l.mut.Lock()
|
||||||
|
defer l.mut.Unlock()
|
||||||
|
s := fmt.Sprintf(format, vals...)
|
||||||
|
l.logger.Output(2, "VERBOSE: "+s)
|
||||||
|
l.callHandlers(LevelVerbose, s)
|
||||||
|
}
|
||||||
|
|
||||||
// Infoln logs a line with an INFO prefix.
|
// Infoln logs a line with an INFO prefix.
|
||||||
func (l *Logger) Infoln(vals ...interface{}) {
|
func (l *Logger) Infoln(vals ...interface{}) {
|
||||||
l.mut.Lock()
|
l.mut.Lock()
|
||||||
|
|||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
Copyright (C) 2014 Jakob Borg
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
- The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
+1
-14
@@ -1,17 +1,4 @@
|
|||||||
// Copyright (C) 2014 The Syncthing Authors.
|
// Copyright (C) 2014 Jakob Borg
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it
|
|
||||||
// under the terms of the GNU General Public License as published by the Free
|
|
||||||
// Software Foundation, either version 3 of the License, or (at your option)
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
// more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along
|
|
||||||
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
// Package luhn generates and validates Luhn mod N check digits.
|
// Package luhn generates and validates Luhn mod N check digits.
|
||||||
package luhn
|
package luhn
|
||||||
Generated
Vendored
+2
-15
@@ -1,24 +1,11 @@
|
|||||||
// Copyright (C) 2014 The Syncthing Authors.
|
// Copyright (C) 2014 Jakob Borg
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it
|
|
||||||
// under the terms of the GNU General Public License as published by the Free
|
|
||||||
// Software Foundation, either version 3 of the License, or (at your option)
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
// more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along
|
|
||||||
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package luhn_test
|
package luhn_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/syncthing/syncthing/internal/luhn"
|
"github.com/calmh/luhn"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGenerate(t *testing.T) {
|
func TestGenerate(t *testing.T) {
|
||||||
-20
@@ -1,20 +0,0 @@
|
|||||||
Copyright (c) 2012 Daniel Theophanes
|
|
||||||
|
|
||||||
This software is provided 'as-is', without any express or implied
|
|
||||||
warranty. In no event will the authors be held liable for any damages
|
|
||||||
arising from the use of this software.
|
|
||||||
|
|
||||||
Permission is granted to anyone to use this software for any purpose,
|
|
||||||
including commercial applications, and to alter it and redistribute it
|
|
||||||
freely, subject to the following restrictions:
|
|
||||||
|
|
||||||
1. The origin of this software must not be misrepresented; you must not
|
|
||||||
claim that you wrote the original software. If you use this software
|
|
||||||
in a product, an acknowledgment in the product documentation would be
|
|
||||||
appreciated but is not required.
|
|
||||||
|
|
||||||
2. Altered source versions must be plainly marked as such, and must not be
|
|
||||||
misrepresented as being the original software.
|
|
||||||
|
|
||||||
3. This notice may not be removed or altered from any source
|
|
||||||
distribution.
|
|
||||||
-79
@@ -1,79 +0,0 @@
|
|||||||
// Copyright 2012 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build darwin linux freebsd netbsd windows
|
|
||||||
|
|
||||||
package osext
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
oexec "os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
const execPath_EnvVar = "OSTEST_OUTPUT_EXECPATH"
|
|
||||||
|
|
||||||
func TestExecPath(t *testing.T) {
|
|
||||||
ep, err := Executable()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("ExecPath failed: %v", err)
|
|
||||||
}
|
|
||||||
// we want fn to be of the form "dir/prog"
|
|
||||||
dir := filepath.Dir(filepath.Dir(ep))
|
|
||||||
fn, err := filepath.Rel(dir, ep)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("filepath.Rel: %v", err)
|
|
||||||
}
|
|
||||||
cmd := &oexec.Cmd{}
|
|
||||||
// make child start with a relative program path
|
|
||||||
cmd.Dir = dir
|
|
||||||
cmd.Path = fn
|
|
||||||
// forge argv[0] for child, so that we can verify we could correctly
|
|
||||||
// get real path of the executable without influenced by argv[0].
|
|
||||||
cmd.Args = []string{"-", "-test.run=XXXX"}
|
|
||||||
cmd.Env = []string{fmt.Sprintf("%s=1", execPath_EnvVar)}
|
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("exec(self) failed: %v", err)
|
|
||||||
}
|
|
||||||
outs := string(out)
|
|
||||||
if !filepath.IsAbs(outs) {
|
|
||||||
t.Fatalf("Child returned %q, want an absolute path", out)
|
|
||||||
}
|
|
||||||
if !sameFile(outs, ep) {
|
|
||||||
t.Fatalf("Child returned %q, not the same file as %q", out, ep)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sameFile(fn1, fn2 string) bool {
|
|
||||||
fi1, err := os.Stat(fn1)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
fi2, err := os.Stat(fn2)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return os.SameFile(fi1, fi2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
if e := os.Getenv(execPath_EnvVar); e != "" {
|
|
||||||
// first chdir to another path
|
|
||||||
dir := "/"
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
dir = filepath.VolumeName(".")
|
|
||||||
}
|
|
||||||
os.Chdir(dir)
|
|
||||||
if ep, err := Executable(); err != nil {
|
|
||||||
fmt.Fprint(os.Stderr, "ERROR: ", err)
|
|
||||||
} else {
|
|
||||||
fmt.Fprint(os.Stderr, ep)
|
|
||||||
}
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-2
@@ -67,7 +67,7 @@ func BenchmarkThisEncode(b *testing.B) {
|
|||||||
func BenchmarkThisEncoder(b *testing.B) {
|
func BenchmarkThisEncoder(b *testing.B) {
|
||||||
w := xdr.NewWriter(ioutil.Discard)
|
w := xdr.NewWriter(ioutil.Discard)
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, err := s.encodeXDR(w)
|
_, err := s.EncodeXDRInto(w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -108,7 +108,7 @@ func BenchmarkThisDecoder(b *testing.B) {
|
|||||||
r := xdr.NewReader(rr)
|
r := xdr.NewReader(rr)
|
||||||
var t XDRBenchStruct
|
var t XDRBenchStruct
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
err := t.decodeXDR(r)
|
err := t.DecodeXDRFrom(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-13
@@ -26,7 +26,9 @@ XDRBenchStruct Structure:
|
|||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
| 0x0000 | I3 |
|
| 0x0000 | I3 |
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
| uint8 |
|
/ /
|
||||||
|
\ uint8 Structure \
|
||||||
|
/ /
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
| Length of Bs0 |
|
| Length of Bs0 |
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
@@ -69,7 +71,7 @@ struct XDRBenchStruct {
|
|||||||
|
|
||||||
func (o XDRBenchStruct) EncodeXDR(w io.Writer) (int, error) {
|
func (o XDRBenchStruct) EncodeXDR(w io.Writer) (int, error) {
|
||||||
var xw = xdr.NewWriter(w)
|
var xw = xdr.NewWriter(w)
|
||||||
return o.encodeXDR(xw)
|
return o.EncodeXDRInto(xw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o XDRBenchStruct) MarshalXDR() ([]byte, error) {
|
func (o XDRBenchStruct) MarshalXDR() ([]byte, error) {
|
||||||
@@ -87,11 +89,11 @@ func (o XDRBenchStruct) MustMarshalXDR() []byte {
|
|||||||
func (o XDRBenchStruct) AppendXDR(bs []byte) ([]byte, error) {
|
func (o XDRBenchStruct) AppendXDR(bs []byte) ([]byte, error) {
|
||||||
var aw = xdr.AppendWriter(bs)
|
var aw = xdr.AppendWriter(bs)
|
||||||
var xw = xdr.NewWriter(&aw)
|
var xw = xdr.NewWriter(&aw)
|
||||||
_, err := o.encodeXDR(xw)
|
_, err := o.EncodeXDRInto(xw)
|
||||||
return []byte(aw), err
|
return []byte(aw), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o XDRBenchStruct) encodeXDR(xw *xdr.Writer) (int, error) {
|
func (o XDRBenchStruct) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||||
xw.WriteUint64(o.I1)
|
xw.WriteUint64(o.I1)
|
||||||
xw.WriteUint32(o.I2)
|
xw.WriteUint32(o.I2)
|
||||||
xw.WriteUint16(o.I3)
|
xw.WriteUint16(o.I3)
|
||||||
@@ -111,16 +113,16 @@ func (o XDRBenchStruct) encodeXDR(xw *xdr.Writer) (int, error) {
|
|||||||
|
|
||||||
func (o *XDRBenchStruct) DecodeXDR(r io.Reader) error {
|
func (o *XDRBenchStruct) DecodeXDR(r io.Reader) error {
|
||||||
xr := xdr.NewReader(r)
|
xr := xdr.NewReader(r)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *XDRBenchStruct) UnmarshalXDR(bs []byte) error {
|
func (o *XDRBenchStruct) UnmarshalXDR(bs []byte) error {
|
||||||
var br = bytes.NewReader(bs)
|
var br = bytes.NewReader(bs)
|
||||||
var xr = xdr.NewReader(br)
|
var xr = xdr.NewReader(br)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *XDRBenchStruct) decodeXDR(xr *xdr.Reader) error {
|
func (o *XDRBenchStruct) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||||
o.I1 = xr.ReadUint64()
|
o.I1 = xr.ReadUint64()
|
||||||
o.I2 = xr.ReadUint32()
|
o.I2 = xr.ReadUint32()
|
||||||
o.I3 = xr.ReadUint16()
|
o.I3 = xr.ReadUint16()
|
||||||
@@ -155,7 +157,7 @@ struct repeatReader {
|
|||||||
|
|
||||||
func (o repeatReader) EncodeXDR(w io.Writer) (int, error) {
|
func (o repeatReader) EncodeXDR(w io.Writer) (int, error) {
|
||||||
var xw = xdr.NewWriter(w)
|
var xw = xdr.NewWriter(w)
|
||||||
return o.encodeXDR(xw)
|
return o.EncodeXDRInto(xw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o repeatReader) MarshalXDR() ([]byte, error) {
|
func (o repeatReader) MarshalXDR() ([]byte, error) {
|
||||||
@@ -173,27 +175,27 @@ func (o repeatReader) MustMarshalXDR() []byte {
|
|||||||
func (o repeatReader) AppendXDR(bs []byte) ([]byte, error) {
|
func (o repeatReader) AppendXDR(bs []byte) ([]byte, error) {
|
||||||
var aw = xdr.AppendWriter(bs)
|
var aw = xdr.AppendWriter(bs)
|
||||||
var xw = xdr.NewWriter(&aw)
|
var xw = xdr.NewWriter(&aw)
|
||||||
_, err := o.encodeXDR(xw)
|
_, err := o.EncodeXDRInto(xw)
|
||||||
return []byte(aw), err
|
return []byte(aw), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o repeatReader) encodeXDR(xw *xdr.Writer) (int, error) {
|
func (o repeatReader) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||||
xw.WriteBytes(o.data)
|
xw.WriteBytes(o.data)
|
||||||
return xw.Tot(), xw.Error()
|
return xw.Tot(), xw.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *repeatReader) DecodeXDR(r io.Reader) error {
|
func (o *repeatReader) DecodeXDR(r io.Reader) error {
|
||||||
xr := xdr.NewReader(r)
|
xr := xdr.NewReader(r)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *repeatReader) UnmarshalXDR(bs []byte) error {
|
func (o *repeatReader) UnmarshalXDR(bs []byte) error {
|
||||||
var br = bytes.NewReader(bs)
|
var br = bytes.NewReader(bs)
|
||||||
var xr = xdr.NewReader(br)
|
var xr = xdr.NewReader(br)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *repeatReader) decodeXDR(xr *xdr.Reader) error {
|
func (o *repeatReader) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||||
o.data = xr.ReadBytes()
|
o.data = xr.ReadBytes()
|
||||||
return xr.Error()
|
return xr.Error()
|
||||||
}
|
}
|
||||||
|
|||||||
+91
-66
@@ -11,6 +11,8 @@ import (
|
|||||||
"go/format"
|
"go/format"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -50,7 +52,7 @@ import (
|
|||||||
var encodeTpl = template.Must(template.New("encoder").Parse(`
|
var encodeTpl = template.Must(template.New("encoder").Parse(`
|
||||||
func (o {{.TypeName}}) EncodeXDR(w io.Writer) (int, error) {
|
func (o {{.TypeName}}) EncodeXDR(w io.Writer) (int, error) {
|
||||||
var xw = xdr.NewWriter(w)
|
var xw = xdr.NewWriter(w)
|
||||||
return o.encodeXDR(xw)
|
return o.EncodeXDRInto(xw)
|
||||||
}//+n
|
}//+n
|
||||||
|
|
||||||
func (o {{.TypeName}}) MarshalXDR() ([]byte, error) {
|
func (o {{.TypeName}}) MarshalXDR() ([]byte, error) {
|
||||||
@@ -68,11 +70,11 @@ func (o {{.TypeName}}) MustMarshalXDR() []byte {
|
|||||||
func (o {{.TypeName}}) AppendXDR(bs []byte) ([]byte, error) {
|
func (o {{.TypeName}}) AppendXDR(bs []byte) ([]byte, error) {
|
||||||
var aw = xdr.AppendWriter(bs)
|
var aw = xdr.AppendWriter(bs)
|
||||||
var xw = xdr.NewWriter(&aw)
|
var xw = xdr.NewWriter(&aw)
|
||||||
_, err := o.encodeXDR(xw)
|
_, err := o.EncodeXDRInto(xw)
|
||||||
return []byte(aw), err
|
return []byte(aw), err
|
||||||
}//+n
|
}//+n
|
||||||
|
|
||||||
func (o {{.TypeName}}) encodeXDR(xw *xdr.Writer) (int, error) {
|
func (o {{.TypeName}}) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||||
{{range $fieldInfo := .Fields}}
|
{{range $fieldInfo := .Fields}}
|
||||||
{{if not $fieldInfo.IsSlice}}
|
{{if not $fieldInfo.IsSlice}}
|
||||||
{{if ne $fieldInfo.Convert ""}}
|
{{if ne $fieldInfo.Convert ""}}
|
||||||
@@ -85,7 +87,7 @@ func (o {{.TypeName}}) encodeXDR(xw *xdr.Writer) (int, error) {
|
|||||||
{{end}}
|
{{end}}
|
||||||
xw.Write{{$fieldInfo.Encoder}}(o.{{$fieldInfo.Name}})
|
xw.Write{{$fieldInfo.Encoder}}(o.{{$fieldInfo.Name}})
|
||||||
{{else}}
|
{{else}}
|
||||||
_, err := o.{{$fieldInfo.Name}}.encodeXDR(xw)
|
_, err := o.{{$fieldInfo.Name}}.EncodeXDRInto(xw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xw.Tot(), err
|
return xw.Tot(), err
|
||||||
}
|
}
|
||||||
@@ -103,7 +105,7 @@ func (o {{.TypeName}}) encodeXDR(xw *xdr.Writer) (int, error) {
|
|||||||
{{else if $fieldInfo.IsBasic}}
|
{{else if $fieldInfo.IsBasic}}
|
||||||
xw.Write{{$fieldInfo.Encoder}}(o.{{$fieldInfo.Name}}[i])
|
xw.Write{{$fieldInfo.Encoder}}(o.{{$fieldInfo.Name}}[i])
|
||||||
{{else}}
|
{{else}}
|
||||||
_, err := o.{{$fieldInfo.Name}}[i].encodeXDR(xw)
|
_, err := o.{{$fieldInfo.Name}}[i].EncodeXDRInto(xw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xw.Tot(), err
|
return xw.Tot(), err
|
||||||
}
|
}
|
||||||
@@ -116,16 +118,16 @@ func (o {{.TypeName}}) encodeXDR(xw *xdr.Writer) (int, error) {
|
|||||||
|
|
||||||
func (o *{{.TypeName}}) DecodeXDR(r io.Reader) error {
|
func (o *{{.TypeName}}) DecodeXDR(r io.Reader) error {
|
||||||
xr := xdr.NewReader(r)
|
xr := xdr.NewReader(r)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}//+n
|
}//+n
|
||||||
|
|
||||||
func (o *{{.TypeName}}) UnmarshalXDR(bs []byte) error {
|
func (o *{{.TypeName}}) UnmarshalXDR(bs []byte) error {
|
||||||
var br = bytes.NewReader(bs)
|
var br = bytes.NewReader(bs)
|
||||||
var xr = xdr.NewReader(br)
|
var xr = xdr.NewReader(br)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}//+n
|
}//+n
|
||||||
|
|
||||||
func (o *{{.TypeName}}) decodeXDR(xr *xdr.Reader) error {
|
func (o *{{.TypeName}}) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||||
{{range $fieldInfo := .Fields}}
|
{{range $fieldInfo := .Fields}}
|
||||||
{{if not $fieldInfo.IsSlice}}
|
{{if not $fieldInfo.IsSlice}}
|
||||||
{{if ne $fieldInfo.Convert ""}}
|
{{if ne $fieldInfo.Convert ""}}
|
||||||
@@ -137,10 +139,13 @@ func (o *{{.TypeName}}) decodeXDR(xr *xdr.Reader) error {
|
|||||||
o.{{$fieldInfo.Name}} = xr.Read{{$fieldInfo.Encoder}}()
|
o.{{$fieldInfo.Name}} = xr.Read{{$fieldInfo.Encoder}}()
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else}}
|
{{else}}
|
||||||
(&o.{{$fieldInfo.Name}}).decodeXDR(xr)
|
(&o.{{$fieldInfo.Name}}).DecodeXDRFrom(xr)
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else}}
|
{{else}}
|
||||||
_{{$fieldInfo.Name}}Size := int(xr.ReadUint32())
|
_{{$fieldInfo.Name}}Size := int(xr.ReadUint32())
|
||||||
|
if _{{$fieldInfo.Name}}Size < 0 {
|
||||||
|
return xdr.ElementSizeExceeded("{{$fieldInfo.Name}}", _{{$fieldInfo.Name}}Size, {{$fieldInfo.Max}})
|
||||||
|
}
|
||||||
{{if ge $fieldInfo.Max 1}}
|
{{if ge $fieldInfo.Max 1}}
|
||||||
if _{{$fieldInfo.Name}}Size > {{$fieldInfo.Max}} {
|
if _{{$fieldInfo.Name}}Size > {{$fieldInfo.Max}} {
|
||||||
return xdr.ElementSizeExceeded("{{$fieldInfo.Name}}", _{{$fieldInfo.Name}}Size, {{$fieldInfo.Max}})
|
return xdr.ElementSizeExceeded("{{$fieldInfo.Name}}", _{{$fieldInfo.Name}}Size, {{$fieldInfo.Max}})
|
||||||
@@ -153,7 +158,7 @@ func (o *{{.TypeName}}) decodeXDR(xr *xdr.Reader) error {
|
|||||||
{{else if $fieldInfo.IsBasic}}
|
{{else if $fieldInfo.IsBasic}}
|
||||||
o.{{$fieldInfo.Name}}[i] = xr.Read{{$fieldInfo.Encoder}}()
|
o.{{$fieldInfo.Name}}[i] = xr.Read{{$fieldInfo.Encoder}}()
|
||||||
{{else}}
|
{{else}}
|
||||||
(&o.{{$fieldInfo.Name}}[i]).decodeXDR(xr)
|
(&o.{{$fieldInfo.Name}}[i]).DecodeXDRFrom(xr)
|
||||||
{{end}}
|
{{end}}
|
||||||
}
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -255,12 +260,18 @@ func handleStruct(t *ast.StructType) []fieldInfo {
|
|||||||
} else {
|
} else {
|
||||||
f = fieldInfo{
|
f = fieldInfo{
|
||||||
Name: fn,
|
Name: fn,
|
||||||
IsBasic: false,
|
|
||||||
IsSlice: true,
|
IsSlice: true,
|
||||||
FieldType: tn,
|
FieldType: tn,
|
||||||
Max: max,
|
Max: max,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
f = fieldInfo{
|
||||||
|
Name: fn,
|
||||||
|
FieldType: ft.Sel.Name,
|
||||||
|
Max: max,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fs = append(fs, f)
|
fs = append(fs, f)
|
||||||
@@ -269,7 +280,7 @@ func handleStruct(t *ast.StructType) []fieldInfo {
|
|||||||
return fs
|
return fs
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateCode(s structInfo) {
|
func generateCode(output io.Writer, s structInfo) {
|
||||||
name := s.Name
|
name := s.Name
|
||||||
fs := s.Fields
|
fs := s.Fields
|
||||||
|
|
||||||
@@ -286,7 +297,7 @@ func generateCode(s structInfo) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
fmt.Println(string(bs))
|
fmt.Fprintln(output, string(bs))
|
||||||
}
|
}
|
||||||
|
|
||||||
func uncamelize(s string) string {
|
func uncamelize(s string) string {
|
||||||
@@ -295,69 +306,71 @@ func uncamelize(s string) string {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateDiagram(s structInfo) {
|
func generateDiagram(output io.Writer, s structInfo) {
|
||||||
sn := s.Name
|
sn := s.Name
|
||||||
fs := s.Fields
|
fs := s.Fields
|
||||||
|
|
||||||
fmt.Println(sn + " Structure:")
|
fmt.Fprintln(output, sn+" Structure:")
|
||||||
fmt.Println()
|
fmt.Fprintln(output)
|
||||||
fmt.Println(" 0 1 2 3")
|
fmt.Fprintln(output, " 0 1 2 3")
|
||||||
fmt.Println(" 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1")
|
fmt.Fprintln(output, " 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1")
|
||||||
line := "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+"
|
line := "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+"
|
||||||
fmt.Println(line)
|
fmt.Fprintln(output, line)
|
||||||
|
|
||||||
for _, f := range fs {
|
for _, f := range fs {
|
||||||
tn := f.FieldType
|
tn := f.FieldType
|
||||||
sl := f.IsSlice
|
|
||||||
name := uncamelize(f.Name)
|
name := uncamelize(f.Name)
|
||||||
|
|
||||||
if sl {
|
if f.IsSlice {
|
||||||
fmt.Printf("| %s |\n", center("Number of "+name, 61))
|
fmt.Fprintf(output, "| %s |\n", center("Number of "+name, 61))
|
||||||
fmt.Println(line)
|
fmt.Fprintln(output, line)
|
||||||
}
|
}
|
||||||
switch tn {
|
switch tn {
|
||||||
case "bool":
|
case "bool":
|
||||||
fmt.Printf("| %s |V|\n", center(name+" (V=0 or 1)", 59))
|
fmt.Fprintf(output, "| %s |V|\n", center(name+" (V=0 or 1)", 59))
|
||||||
fmt.Println(line)
|
fmt.Fprintln(output, line)
|
||||||
case "uint16":
|
case "int16", "uint16":
|
||||||
fmt.Printf("| %s | %s |\n", center("0x0000", 29), center(name, 29))
|
fmt.Fprintf(output, "| %s | %s |\n", center("0x0000", 29), center(name, 29))
|
||||||
fmt.Println(line)
|
fmt.Fprintln(output, line)
|
||||||
case "uint32":
|
case "int32", "uint32":
|
||||||
fmt.Printf("| %s |\n", center(name, 61))
|
fmt.Fprintf(output, "| %s |\n", center(name, 61))
|
||||||
fmt.Println(line)
|
fmt.Fprintln(output, line)
|
||||||
case "int64", "uint64":
|
case "int64", "uint64":
|
||||||
fmt.Printf("| %-61s |\n", "")
|
fmt.Fprintf(output, "| %-61s |\n", "")
|
||||||
fmt.Printf("+ %s +\n", center(name+" (64 bits)", 61))
|
fmt.Fprintf(output, "+ %s +\n", center(name+" (64 bits)", 61))
|
||||||
fmt.Printf("| %-61s |\n", "")
|
fmt.Fprintf(output, "| %-61s |\n", "")
|
||||||
fmt.Println(line)
|
fmt.Fprintln(output, line)
|
||||||
case "string", "byte": // XXX We assume slice of byte!
|
case "string", "byte": // XXX We assume slice of byte!
|
||||||
fmt.Printf("| %s |\n", center("Length of "+name, 61))
|
fmt.Fprintf(output, "| %s |\n", center("Length of "+name, 61))
|
||||||
fmt.Println(line)
|
fmt.Fprintln(output, line)
|
||||||
fmt.Printf("/ %61s /\n", "")
|
fmt.Fprintf(output, "/ %61s /\n", "")
|
||||||
fmt.Printf("\\ %s \\\n", center(name+" (variable length)", 61))
|
fmt.Fprintf(output, "\\ %s \\\n", center(name+" (variable length)", 61))
|
||||||
fmt.Printf("/ %61s /\n", "")
|
fmt.Fprintf(output, "/ %61s /\n", "")
|
||||||
fmt.Println(line)
|
fmt.Fprintln(output, line)
|
||||||
default:
|
default:
|
||||||
if sl {
|
if f.IsSlice {
|
||||||
tn = "Zero or more " + tn + " Structures"
|
tn = "Zero or more " + tn + " Structures"
|
||||||
fmt.Printf("/ %s /\n", center("", 61))
|
fmt.Fprintf(output, "/ %s /\n", center("", 61))
|
||||||
fmt.Printf("\\ %s \\\n", center(tn, 61))
|
fmt.Fprintf(output, "\\ %s \\\n", center(tn, 61))
|
||||||
fmt.Printf("/ %s /\n", center("", 61))
|
fmt.Fprintf(output, "/ %s /\n", center("", 61))
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("| %s |\n", center(tn, 61))
|
tn = tn + " Structure"
|
||||||
|
fmt.Fprintf(output, "/ %s /\n", center("", 61))
|
||||||
|
fmt.Fprintf(output, "\\ %s \\\n", center(tn, 61))
|
||||||
|
fmt.Fprintf(output, "/ %s /\n", center("", 61))
|
||||||
}
|
}
|
||||||
fmt.Println(line)
|
fmt.Fprintln(output, line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Fprintln(output)
|
||||||
fmt.Println()
|
fmt.Fprintln(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateXdr(s structInfo) {
|
func generateXdr(output io.Writer, s structInfo) {
|
||||||
sn := s.Name
|
sn := s.Name
|
||||||
fs := s.Fields
|
fs := s.Fields
|
||||||
|
|
||||||
fmt.Printf("struct %s {\n", sn)
|
fmt.Fprintf(output, "struct %s {\n", sn)
|
||||||
|
|
||||||
for _, f := range fs {
|
for _, f := range fs {
|
||||||
tn := f.FieldType
|
tn := f.FieldType
|
||||||
@@ -372,22 +385,24 @@ func generateXdr(s structInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch tn {
|
switch tn {
|
||||||
|
case "int16", "int32":
|
||||||
|
fmt.Fprintf(output, "\tint %s%s;\n", fn, suf)
|
||||||
case "uint16", "uint32":
|
case "uint16", "uint32":
|
||||||
fmt.Printf("\tunsigned int %s%s;\n", fn, suf)
|
fmt.Fprintf(output, "\tunsigned int %s%s;\n", fn, suf)
|
||||||
case "int64":
|
case "int64":
|
||||||
fmt.Printf("\thyper %s%s;\n", fn, suf)
|
fmt.Fprintf(output, "\thyper %s%s;\n", fn, suf)
|
||||||
case "uint64":
|
case "uint64":
|
||||||
fmt.Printf("\tunsigned hyper %s%s;\n", fn, suf)
|
fmt.Fprintf(output, "\tunsigned hyper %s%s;\n", fn, suf)
|
||||||
case "string":
|
case "string":
|
||||||
fmt.Printf("\tstring %s<%s>;\n", fn, l)
|
fmt.Fprintf(output, "\tstring %s<%s>;\n", fn, l)
|
||||||
case "byte":
|
case "byte":
|
||||||
fmt.Printf("\topaque %s<%s>;\n", fn, l)
|
fmt.Fprintf(output, "\topaque %s<%s>;\n", fn, l)
|
||||||
default:
|
default:
|
||||||
fmt.Printf("\t%s %s%s;\n", tn, fn, suf)
|
fmt.Fprintf(output, "\t%s %s%s;\n", tn, fn, suf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Println("}")
|
fmt.Fprintln(output, "}")
|
||||||
fmt.Println()
|
fmt.Fprintln(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
func center(s string, w int) string {
|
func center(s string, w int) string {
|
||||||
@@ -418,25 +433,35 @@ func inspector(structs *[]structInfo) func(ast.Node) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
outputFile := flag.String("o", "", "Output file, blank for stdout")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
fname := flag.Arg(0)
|
fname := flag.Arg(0)
|
||||||
|
|
||||||
fset := token.NewFileSet()
|
fset := token.NewFileSet()
|
||||||
f, err := parser.ParseFile(fset, fname, nil, parser.ParseComments)
|
f, err := parser.ParseFile(fset, fname, nil, parser.ParseComments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var structs []structInfo
|
var structs []structInfo
|
||||||
i := inspector(&structs)
|
i := inspector(&structs)
|
||||||
ast.Inspect(f, i)
|
ast.Inspect(f, i)
|
||||||
|
|
||||||
headerTpl.Execute(os.Stdout, map[string]string{"Package": f.Name.Name})
|
var output io.Writer = os.Stdout
|
||||||
|
if *outputFile != "" {
|
||||||
|
fd, err := os.Create(*outputFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
output = fd
|
||||||
|
}
|
||||||
|
|
||||||
|
headerTpl.Execute(output, map[string]string{"Package": f.Name.Name})
|
||||||
for _, s := range structs {
|
for _, s := range structs {
|
||||||
fmt.Printf("\n/*\n\n")
|
fmt.Fprintf(output, "\n/*\n\n")
|
||||||
generateDiagram(s)
|
generateDiagram(output, s)
|
||||||
generateXdr(s)
|
generateXdr(output, s)
|
||||||
fmt.Printf("*/\n")
|
fmt.Fprintf(output, "*/\n")
|
||||||
generateCode(s)
|
generateCode(output, s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -32,11 +32,11 @@ type TestStruct struct {
|
|||||||
|
|
||||||
type Opaque [32]byte
|
type Opaque [32]byte
|
||||||
|
|
||||||
func (u *Opaque) encodeXDR(w *xdr.Writer) (int, error) {
|
func (u *Opaque) EncodeXDRInto(w *xdr.Writer) (int, error) {
|
||||||
return w.WriteRaw(u[:])
|
return w.WriteRaw(u[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Opaque) decodeXDR(r *xdr.Reader) (int, error) {
|
func (u *Opaque) DecodeXDRFrom(r *xdr.Reader) (int, error) {
|
||||||
return r.ReadRaw(u[:])
|
return r.ReadRaw(u[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+27
-16
@@ -18,17 +18,23 @@ TestStruct Structure:
|
|||||||
0 1 2 3
|
0 1 2 3
|
||||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
| int |
|
/ /
|
||||||
|
\ int Structure \
|
||||||
|
/ /
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
| int8 |
|
/ /
|
||||||
|
\ int8 Structure \
|
||||||
|
/ /
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
| uint8 |
|
/ /
|
||||||
|
\ uint8 Structure \
|
||||||
|
/ /
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
| int16 |
|
| 0x0000 | I16 |
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
| 0x0000 | UI16 |
|
| 0x0000 | UI16 |
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
| int32 |
|
| I32 |
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
| UI32 |
|
| UI32 |
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
@@ -52,7 +58,9 @@ TestStruct Structure:
|
|||||||
\ S (variable length) \
|
\ S (variable length) \
|
||||||
/ /
|
/ /
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
| Opaque |
|
/ /
|
||||||
|
\ Opaque Structure \
|
||||||
|
/ /
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
| Number of SS |
|
| Number of SS |
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
@@ -68,9 +76,9 @@ struct TestStruct {
|
|||||||
int I;
|
int I;
|
||||||
int8 I8;
|
int8 I8;
|
||||||
uint8 UI8;
|
uint8 UI8;
|
||||||
int16 I16;
|
int I16;
|
||||||
unsigned int UI16;
|
unsigned int UI16;
|
||||||
int32 I32;
|
int I32;
|
||||||
unsigned int UI32;
|
unsigned int UI32;
|
||||||
hyper I64;
|
hyper I64;
|
||||||
unsigned hyper UI64;
|
unsigned hyper UI64;
|
||||||
@@ -84,7 +92,7 @@ struct TestStruct {
|
|||||||
|
|
||||||
func (o TestStruct) EncodeXDR(w io.Writer) (int, error) {
|
func (o TestStruct) EncodeXDR(w io.Writer) (int, error) {
|
||||||
var xw = xdr.NewWriter(w)
|
var xw = xdr.NewWriter(w)
|
||||||
return o.encodeXDR(xw)
|
return o.EncodeXDRInto(xw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o TestStruct) MarshalXDR() ([]byte, error) {
|
func (o TestStruct) MarshalXDR() ([]byte, error) {
|
||||||
@@ -102,11 +110,11 @@ func (o TestStruct) MustMarshalXDR() []byte {
|
|||||||
func (o TestStruct) AppendXDR(bs []byte) ([]byte, error) {
|
func (o TestStruct) AppendXDR(bs []byte) ([]byte, error) {
|
||||||
var aw = xdr.AppendWriter(bs)
|
var aw = xdr.AppendWriter(bs)
|
||||||
var xw = xdr.NewWriter(&aw)
|
var xw = xdr.NewWriter(&aw)
|
||||||
_, err := o.encodeXDR(xw)
|
_, err := o.EncodeXDRInto(xw)
|
||||||
return []byte(aw), err
|
return []byte(aw), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o TestStruct) encodeXDR(xw *xdr.Writer) (int, error) {
|
func (o TestStruct) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||||
xw.WriteUint64(uint64(o.I))
|
xw.WriteUint64(uint64(o.I))
|
||||||
xw.WriteUint8(uint8(o.I8))
|
xw.WriteUint8(uint8(o.I8))
|
||||||
xw.WriteUint8(o.UI8)
|
xw.WriteUint8(o.UI8)
|
||||||
@@ -124,7 +132,7 @@ func (o TestStruct) encodeXDR(xw *xdr.Writer) (int, error) {
|
|||||||
return xw.Tot(), xdr.ElementSizeExceeded("S", l, 1024)
|
return xw.Tot(), xdr.ElementSizeExceeded("S", l, 1024)
|
||||||
}
|
}
|
||||||
xw.WriteString(o.S)
|
xw.WriteString(o.S)
|
||||||
_, err := o.C.encodeXDR(xw)
|
_, err := o.C.EncodeXDRInto(xw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xw.Tot(), err
|
return xw.Tot(), err
|
||||||
}
|
}
|
||||||
@@ -140,16 +148,16 @@ func (o TestStruct) encodeXDR(xw *xdr.Writer) (int, error) {
|
|||||||
|
|
||||||
func (o *TestStruct) DecodeXDR(r io.Reader) error {
|
func (o *TestStruct) DecodeXDR(r io.Reader) error {
|
||||||
xr := xdr.NewReader(r)
|
xr := xdr.NewReader(r)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *TestStruct) UnmarshalXDR(bs []byte) error {
|
func (o *TestStruct) UnmarshalXDR(bs []byte) error {
|
||||||
var br = bytes.NewReader(bs)
|
var br = bytes.NewReader(bs)
|
||||||
var xr = xdr.NewReader(br)
|
var xr = xdr.NewReader(br)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *TestStruct) decodeXDR(xr *xdr.Reader) error {
|
func (o *TestStruct) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||||
o.I = int(xr.ReadUint64())
|
o.I = int(xr.ReadUint64())
|
||||||
o.I8 = int8(xr.ReadUint8())
|
o.I8 = int8(xr.ReadUint8())
|
||||||
o.UI8 = xr.ReadUint8()
|
o.UI8 = xr.ReadUint8()
|
||||||
@@ -161,8 +169,11 @@ func (o *TestStruct) decodeXDR(xr *xdr.Reader) error {
|
|||||||
o.UI64 = xr.ReadUint64()
|
o.UI64 = xr.ReadUint64()
|
||||||
o.BS = xr.ReadBytesMax(1024)
|
o.BS = xr.ReadBytesMax(1024)
|
||||||
o.S = xr.ReadStringMax(1024)
|
o.S = xr.ReadStringMax(1024)
|
||||||
(&o.C).decodeXDR(xr)
|
(&o.C).DecodeXDRFrom(xr)
|
||||||
_SSSize := int(xr.ReadUint32())
|
_SSSize := int(xr.ReadUint32())
|
||||||
|
if _SSSize < 0 {
|
||||||
|
return xdr.ElementSizeExceeded("SS", _SSSize, 1024)
|
||||||
|
}
|
||||||
if _SSSize > 1024 {
|
if _SSSize > 1024 {
|
||||||
return xdr.ElementSizeExceeded("SS", _SSSize, 1024)
|
return xdr.ElementSizeExceeded("SS", _SSSize, 1024)
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -68,7 +68,8 @@ func (r *Reader) ReadBytesMaxInto(max int, dst []byte) []byte {
|
|||||||
if r.err != nil {
|
if r.err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if max > 0 && l > max {
|
if l < 0 || max > 0 && l > max {
|
||||||
|
// l may be negative on 32 bit builds
|
||||||
r.err = ElementSizeExceeded("bytes field", l, max)
|
r.err = ElementSizeExceeded("bytes field", l, max)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -154,6 +155,10 @@ func (e XDRError) Error() string {
|
|||||||
return "xdr " + e.op + ": " + e.err.Error()
|
return "xdr " + e.op + ": " + e.err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e XDRError) IsEOF() bool {
|
||||||
|
return e.err == io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Reader) Error() error {
|
func (r *Reader) Error() error {
|
||||||
if r.err == nil {
|
if r.err == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
+3
@@ -1,3 +1,6 @@
|
|||||||
|
This package contains an efficient token-bucket-based rate limiter.
|
||||||
|
Copyright (C) 2015 Canonical Ltd.
|
||||||
|
|
||||||
This software is licensed under the LGPLv3, included below.
|
This software is licensed under the LGPLv3, included below.
|
||||||
|
|
||||||
As a special exception to the GNU Lesser General Public License version 3
|
As a special exception to the GNU Lesser General Public License version 3
|
||||||
|
|||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
### Extensions to the "os" package.
|
||||||
|
|
||||||
|
## Find the current Executable and ExecutableFolder.
|
||||||
|
|
||||||
|
There is sometimes utility in finding the current executable file
|
||||||
|
that is running. This can be used for upgrading the current executable
|
||||||
|
or finding resources located relative to the executable file.
|
||||||
|
|
||||||
|
Multi-platform and supports:
|
||||||
|
* Linux
|
||||||
|
* OS X
|
||||||
|
* Windows
|
||||||
|
* Plan 9
|
||||||
|
* BSDs.
|
||||||
Generated
Vendored
-5
@@ -25,8 +25,3 @@ func ExecutableFolder() (string, error) {
|
|||||||
folder, _ := filepath.Split(p)
|
folder, _ := filepath.Split(p)
|
||||||
return folder, nil
|
return folder, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Depricated. Same as Executable().
|
|
||||||
func GetExePath() (exePath string, err error) {
|
|
||||||
return Executable()
|
|
||||||
}
|
|
||||||
Generated
Vendored
+9
-9
@@ -5,16 +5,16 @@
|
|||||||
package osext
|
package osext
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"syscall"
|
"os"
|
||||||
"os"
|
"strconv"
|
||||||
"strconv"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
func executable() (string, error) {
|
func executable() (string, error) {
|
||||||
f, err := os.Open("/proc/" + strconv.Itoa(os.Getpid()) + "/text")
|
f, err := os.Open("/proc/" + strconv.Itoa(os.Getpid()) + "/text")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
return syscall.Fd2path(int(f.Fd()))
|
return syscall.Fd2path(int(f.Fd()))
|
||||||
}
|
}
|
||||||
Generated
Vendored
+9
-3
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build linux netbsd openbsd solaris
|
// +build linux netbsd openbsd solaris dragonfly
|
||||||
|
|
||||||
package osext
|
package osext
|
||||||
|
|
||||||
@@ -11,15 +11,21 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func executable() (string, error) {
|
func executable() (string, error) {
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "linux":
|
case "linux":
|
||||||
return os.Readlink("/proc/self/exe")
|
const deletedSuffix = " (deleted)"
|
||||||
|
execpath, err := os.Readlink("/proc/self/exe")
|
||||||
|
if err != nil {
|
||||||
|
return execpath, err
|
||||||
|
}
|
||||||
|
return strings.TrimSuffix(execpath, deletedSuffix), nil
|
||||||
case "netbsd":
|
case "netbsd":
|
||||||
return os.Readlink("/proc/curproc/exe")
|
return os.Readlink("/proc/curproc/exe")
|
||||||
case "openbsd":
|
case "openbsd", "dragonfly":
|
||||||
return os.Readlink("/proc/curproc/file")
|
return os.Readlink("/proc/curproc/file")
|
||||||
case "solaris":
|
case "solaris":
|
||||||
return os.Readlink(fmt.Sprintf("/proc/%d/path/a.out", os.Getpid()))
|
return os.Readlink(fmt.Sprintf("/proc/%d/path/a.out", os.Getpid()))
|
||||||
Generated
Vendored
+180
@@ -0,0 +1,180 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build darwin linux freebsd netbsd windows
|
||||||
|
|
||||||
|
package osext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
executableEnvVar = "OSTEST_OUTPUT_EXECUTABLE"
|
||||||
|
|
||||||
|
executableEnvValueMatch = "match"
|
||||||
|
executableEnvValueDelete = "delete"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestExecutableMatch(t *testing.T) {
|
||||||
|
ep, err := Executable()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Executable failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fullpath to be of the form "dir/prog".
|
||||||
|
dir := filepath.Dir(filepath.Dir(ep))
|
||||||
|
fullpath, err := filepath.Rel(dir, ep)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("filepath.Rel: %v", err)
|
||||||
|
}
|
||||||
|
// Make child start with a relative program path.
|
||||||
|
// Alter argv[0] for child to verify getting real path without argv[0].
|
||||||
|
cmd := &exec.Cmd{
|
||||||
|
Dir: dir,
|
||||||
|
Path: fullpath,
|
||||||
|
Env: []string{fmt.Sprintf("%s=%s", executableEnvVar, executableEnvValueMatch)},
|
||||||
|
}
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("exec(self) failed: %v", err)
|
||||||
|
}
|
||||||
|
outs := string(out)
|
||||||
|
if !filepath.IsAbs(outs) {
|
||||||
|
t.Fatalf("Child returned %q, want an absolute path", out)
|
||||||
|
}
|
||||||
|
if !sameFile(outs, ep) {
|
||||||
|
t.Fatalf("Child returned %q, not the same file as %q", out, ep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExecutableDelete(t *testing.T) {
|
||||||
|
if runtime.GOOS != "linux" {
|
||||||
|
t.Skip()
|
||||||
|
}
|
||||||
|
fpath, err := Executable()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Executable failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r, w := io.Pipe()
|
||||||
|
stderrBuff := &bytes.Buffer{}
|
||||||
|
stdoutBuff := &bytes.Buffer{}
|
||||||
|
cmd := &exec.Cmd{
|
||||||
|
Path: fpath,
|
||||||
|
Env: []string{fmt.Sprintf("%s=%s", executableEnvVar, executableEnvValueDelete)},
|
||||||
|
Stdin: r,
|
||||||
|
Stderr: stderrBuff,
|
||||||
|
Stdout: stdoutBuff,
|
||||||
|
}
|
||||||
|
err = cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("exec(self) start failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tempPath := fpath + "_copy"
|
||||||
|
_ = os.Remove(tempPath)
|
||||||
|
|
||||||
|
err = copyFile(tempPath, fpath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("copy file failed: %v", err)
|
||||||
|
}
|
||||||
|
err = os.Remove(fpath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("remove running test file failed: %v", err)
|
||||||
|
}
|
||||||
|
err = os.Rename(tempPath, fpath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("rename copy to previous name failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write([]byte{0})
|
||||||
|
w.Close()
|
||||||
|
|
||||||
|
err = cmd.Wait()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("exec wait failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
childPath := stderrBuff.String()
|
||||||
|
if !filepath.IsAbs(childPath) {
|
||||||
|
t.Fatalf("Child returned %q, want an absolute path", childPath)
|
||||||
|
}
|
||||||
|
if !sameFile(childPath, fpath) {
|
||||||
|
t.Fatalf("Child returned %q, not the same file as %q", childPath, fpath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sameFile(fn1, fn2 string) bool {
|
||||||
|
fi1, err := os.Stat(fn1)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
fi2, err := os.Stat(fn2)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return os.SameFile(fi1, fi2)
|
||||||
|
}
|
||||||
|
func copyFile(dest, src string) error {
|
||||||
|
df, err := os.Create(dest)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer df.Close()
|
||||||
|
|
||||||
|
sf, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer sf.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(df, sf)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
env := os.Getenv(executableEnvVar)
|
||||||
|
switch env {
|
||||||
|
case "":
|
||||||
|
os.Exit(m.Run())
|
||||||
|
case executableEnvValueMatch:
|
||||||
|
// First chdir to another path.
|
||||||
|
dir := "/"
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
dir = filepath.VolumeName(".")
|
||||||
|
}
|
||||||
|
os.Chdir(dir)
|
||||||
|
if ep, err := Executable(); err != nil {
|
||||||
|
fmt.Fprint(os.Stderr, "ERROR: ", err)
|
||||||
|
} else {
|
||||||
|
fmt.Fprint(os.Stderr, ep)
|
||||||
|
}
|
||||||
|
case executableEnvValueDelete:
|
||||||
|
bb := make([]byte, 1)
|
||||||
|
var err error
|
||||||
|
n, err := os.Stdin.Read(bb)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprint(os.Stderr, "ERROR: ", err)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
if n != 1 {
|
||||||
|
fmt.Fprint(os.Stderr, "ERROR: n != 1, n == ", n)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
if ep, err := Executable(); err != nil {
|
||||||
|
fmt.Fprint(os.Stderr, "ERROR: ", err)
|
||||||
|
} else {
|
||||||
|
fmt.Fprint(os.Stderr, ep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
Generated
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
# This is the official list of Protocol Authors for copyright purposes.
|
||||||
|
|
||||||
|
Audrius Butkevicius <audrius.butkevicius@gmail.com>
|
||||||
|
Jakob Borg <jakob@nym.se>
|
||||||
+76
@@ -0,0 +1,76 @@
|
|||||||
|
## Reporting Bugs
|
||||||
|
|
||||||
|
Please file bugs in the [Github Issue
|
||||||
|
Tracker](https://github.com/syncthing/protocol/issues).
|
||||||
|
|
||||||
|
## Contributing Code
|
||||||
|
|
||||||
|
Every contribution is welcome. Following the points below will make this
|
||||||
|
a smoother process.
|
||||||
|
|
||||||
|
Individuals making significant and valuable contributions are given
|
||||||
|
commit-access to the project. If you make a significant contribution and
|
||||||
|
are not considered for commit-access, please contact any of the
|
||||||
|
Syncthing core team members.
|
||||||
|
|
||||||
|
All nontrivial contributions should go through the pull request
|
||||||
|
mechanism for internal review. Determining what is "nontrivial" is left
|
||||||
|
at the discretion of the contributor.
|
||||||
|
|
||||||
|
### Authorship
|
||||||
|
|
||||||
|
All code authors are listed in the AUTHORS file. Commits must be made
|
||||||
|
with the same name and email as listed in the AUTHORS file. To
|
||||||
|
accomplish this, ensure that your git configuration is set correctly
|
||||||
|
prior to making your first commit;
|
||||||
|
|
||||||
|
$ git config --global user.name "Jane Doe"
|
||||||
|
$ git config --global user.email janedoe@example.com
|
||||||
|
|
||||||
|
You must be reachable on the given email address. If you do not wish to
|
||||||
|
use your real name for whatever reason, using a nickname or pseudonym is
|
||||||
|
perfectly acceptable.
|
||||||
|
|
||||||
|
## Coding Style
|
||||||
|
|
||||||
|
- Follow the conventions laid out in [Effective Go](https://golang.org/doc/effective_go.html)
|
||||||
|
as much as makes sense.
|
||||||
|
|
||||||
|
- All text files use Unix line endings.
|
||||||
|
|
||||||
|
- Each commit should be `go fmt` clean.
|
||||||
|
|
||||||
|
- The commit message subject should be a single short sentence
|
||||||
|
describing the change, starting with a capital letter.
|
||||||
|
|
||||||
|
- Commits that resolve an existing issue must include the issue number
|
||||||
|
as `(fixes #123)` at the end of the commit message subject.
|
||||||
|
|
||||||
|
- Imports are grouped per `goimports` standard; that is, standard
|
||||||
|
library first, then third party libraries after a blank line.
|
||||||
|
|
||||||
|
- A contribution solving a single issue or introducing a single new
|
||||||
|
feature should probably be a single commit based on the current
|
||||||
|
`master` branch. You may be asked to "rebase" or "squash" your pull
|
||||||
|
request to make sure this is the case, especially if there have been
|
||||||
|
amendments during review.
|
||||||
|
|
||||||
|
## Licensing
|
||||||
|
|
||||||
|
All contributions are made under the same MIT license as the rest of the
|
||||||
|
project, except documentation, user interface text and translation
|
||||||
|
strings which are licensed under the Creative Commons Attribution 4.0
|
||||||
|
International License. You retain the copyright to code you have
|
||||||
|
written.
|
||||||
|
|
||||||
|
When accepting your first contribution, the maintainer of the project
|
||||||
|
will ensure that you are added to the AUTHORS file. You are welcome to
|
||||||
|
add yourself as a separate commit in your first pull request.
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
Yes please!
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
Copyright (C) 2014-2015 The Protocol Authors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
- The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
The BEPv1 Protocol
|
||||||
|
==================
|
||||||
|
|
||||||
|
[](http://build.syncthing.net/job/protocol/lastBuild/)
|
||||||
|
[](http://godoc.org/github.com/syncthing/protocol)
|
||||||
|
[](http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
This is the protocol implementation used by Syncthing.
|
||||||
|
|
||||||
|
License
|
||||||
|
=======
|
||||||
|
|
||||||
|
MIT
|
||||||
Generated
Vendored
+10
-17
@@ -1,17 +1,4 @@
|
|||||||
// Copyright (C) 2014 The Syncthing Authors.
|
// Copyright (C) 2014 The Protocol Authors.
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it
|
|
||||||
// under the terms of the GNU General Public License as published by the Free
|
|
||||||
// Software Foundation, either version 3 of the License, or (at your option)
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
// more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along
|
|
||||||
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
@@ -26,6 +13,9 @@ type TestModel struct {
|
|||||||
name string
|
name string
|
||||||
offset int64
|
offset int64
|
||||||
size int
|
size int
|
||||||
|
hash []byte
|
||||||
|
flags uint32
|
||||||
|
options []Option
|
||||||
closedCh chan bool
|
closedCh chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,17 +25,20 @@ func newTestModel() *TestModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TestModel) Index(deviceID DeviceID, folder string, files []FileInfo) {
|
func (t *TestModel) Index(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TestModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo) {
|
func (t *TestModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TestModel) Request(deviceID DeviceID, folder, name string, offset int64, size int) ([]byte, error) {
|
func (t *TestModel) Request(deviceID DeviceID, folder, name string, offset int64, size int, hash []byte, flags uint32, options []Option) ([]byte, error) {
|
||||||
t.folder = folder
|
t.folder = folder
|
||||||
t.name = name
|
t.name = name
|
||||||
t.offset = offset
|
t.offset = offset
|
||||||
t.size = size
|
t.size = size
|
||||||
|
t.hash = hash
|
||||||
|
t.flags = flags
|
||||||
|
t.options = options
|
||||||
return t.data, nil
|
return t.data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (C) 2015 The Protocol Authors.
|
||||||
|
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type Compression int
|
||||||
|
|
||||||
|
const (
|
||||||
|
CompressMetadata Compression = iota // zero value is the default, default should be "metadata"
|
||||||
|
CompressNever
|
||||||
|
CompressAlways
|
||||||
|
|
||||||
|
compressionThreshold = 128 // don't bother compressing messages smaller than this many bytes
|
||||||
|
)
|
||||||
|
|
||||||
|
var compressionMarshal = map[Compression]string{
|
||||||
|
CompressNever: "never",
|
||||||
|
CompressMetadata: "metadata",
|
||||||
|
CompressAlways: "always",
|
||||||
|
}
|
||||||
|
|
||||||
|
var compressionUnmarshal = map[string]Compression{
|
||||||
|
// Legacy
|
||||||
|
"false": CompressNever,
|
||||||
|
"true": CompressMetadata,
|
||||||
|
|
||||||
|
// Current
|
||||||
|
"never": CompressNever,
|
||||||
|
"metadata": CompressMetadata,
|
||||||
|
"always": CompressAlways,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Compression) String() string {
|
||||||
|
s, ok := compressionMarshal[c]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Sprintf("unknown:%d", c)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Compression) GoString() string {
|
||||||
|
return fmt.Sprintf("%q", c.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Compression) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(compressionMarshal[c]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compression) UnmarshalText(bs []byte) error {
|
||||||
|
*c = compressionUnmarshal[string(bs)]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright (C) 2015 The Protocol Authors.
|
||||||
|
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestCompressionMarshal(t *testing.T) {
|
||||||
|
uTestcases := []struct {
|
||||||
|
s string
|
||||||
|
c Compression
|
||||||
|
}{
|
||||||
|
{"true", CompressMetadata},
|
||||||
|
{"false", CompressNever},
|
||||||
|
{"never", CompressNever},
|
||||||
|
{"metadata", CompressMetadata},
|
||||||
|
{"always", CompressAlways},
|
||||||
|
{"whatever", CompressMetadata},
|
||||||
|
}
|
||||||
|
|
||||||
|
mTestcases := []struct {
|
||||||
|
s string
|
||||||
|
c Compression
|
||||||
|
}{
|
||||||
|
{"never", CompressNever},
|
||||||
|
{"metadata", CompressMetadata},
|
||||||
|
{"always", CompressAlways},
|
||||||
|
}
|
||||||
|
|
||||||
|
var c Compression
|
||||||
|
for _, tc := range uTestcases {
|
||||||
|
err := c.UnmarshalText([]byte(tc.s))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if c != tc.c {
|
||||||
|
t.Errorf("%s unmarshalled to %d, not %d", tc.s, c, tc.c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range mTestcases {
|
||||||
|
bs, err := tc.c.MarshalText()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if s := string(bs); s != tc.s {
|
||||||
|
t.Errorf("%d marshalled to %q, not %q", tc.c, s, tc.s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
// Copyright (C) 2014 The Protocol Authors.
|
||||||
|
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type countingReader struct {
|
||||||
|
io.Reader
|
||||||
|
tot int64 // bytes
|
||||||
|
last int64 // unix nanos
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
totalIncoming int64
|
||||||
|
totalOutgoing int64
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *countingReader) Read(bs []byte) (int, error) {
|
||||||
|
n, err := c.Reader.Read(bs)
|
||||||
|
atomic.AddInt64(&c.tot, int64(n))
|
||||||
|
atomic.AddInt64(&totalIncoming, int64(n))
|
||||||
|
atomic.StoreInt64(&c.last, time.Now().UnixNano())
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *countingReader) Tot() int64 {
|
||||||
|
return atomic.LoadInt64(&c.tot)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *countingReader) Last() time.Time {
|
||||||
|
return time.Unix(0, atomic.LoadInt64(&c.last))
|
||||||
|
}
|
||||||
|
|
||||||
|
type countingWriter struct {
|
||||||
|
io.Writer
|
||||||
|
tot int64 // bytes
|
||||||
|
last int64 // unix nanos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *countingWriter) Write(bs []byte) (int, error) {
|
||||||
|
n, err := c.Writer.Write(bs)
|
||||||
|
atomic.AddInt64(&c.tot, int64(n))
|
||||||
|
atomic.AddInt64(&totalOutgoing, int64(n))
|
||||||
|
atomic.StoreInt64(&c.last, time.Now().UnixNano())
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *countingWriter) Tot() int64 {
|
||||||
|
return atomic.LoadInt64(&c.tot)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *countingWriter) Last() time.Time {
|
||||||
|
return time.Unix(0, atomic.LoadInt64(&c.last))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TotalInOut() (int64, int64) {
|
||||||
|
return atomic.LoadInt64(&totalIncoming), atomic.LoadInt64(&totalOutgoing)
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
// Copyright (C) 2014 The Protocol Authors.
|
||||||
|
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/calmh/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
debug = strings.Contains(os.Getenv("STTRACE"), "protocol") || os.Getenv("STTRACE") == "all"
|
||||||
|
l = logger.DefaultLogger
|
||||||
|
)
|
||||||
Generated
Vendored
+8
-15
@@ -1,17 +1,4 @@
|
|||||||
// Copyright (C) 2014 The Syncthing Authors.
|
// Copyright (C) 2014 The Protocol Authors.
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it
|
|
||||||
// under the terms of the GNU General Public License as published by the Free
|
|
||||||
// Software Foundation, either version 3 of the License, or (at your option)
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
// more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along
|
|
||||||
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
@@ -19,12 +6,13 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/base32"
|
"encoding/base32"
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/syncthing/syncthing/internal/luhn"
|
"github.com/calmh/luhn"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeviceID [32]byte
|
type DeviceID [32]byte
|
||||||
@@ -80,6 +68,11 @@ func (n DeviceID) Equals(other DeviceID) bool {
|
|||||||
return bytes.Compare(n[:], other[:]) == 0
|
return bytes.Compare(n[:], other[:]) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Short returns an integer representing bits 0-63 of the device ID.
|
||||||
|
func (n DeviceID) Short() uint64 {
|
||||||
|
return binary.BigEndian.Uint64(n[:])
|
||||||
|
}
|
||||||
|
|
||||||
func (n *DeviceID) MarshalText() ([]byte, error) {
|
func (n *DeviceID) MarshalText() ([]byte, error) {
|
||||||
return []byte(n.String()), nil
|
return []byte(n.String()), nil
|
||||||
}
|
}
|
||||||
Generated
Vendored
+1
-14
@@ -1,17 +1,4 @@
|
|||||||
// Copyright (C) 2014 The Syncthing Authors.
|
// Copyright (C) 2014 The Protocol Authors.
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it
|
|
||||||
// under the terms of the GNU General Public License as published by the Free
|
|
||||||
// Software Foundation, either version 3 of the License, or (at your option)
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
// more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along
|
|
||||||
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
// Copyright (C) 2014 The Protocol Authors.
|
||||||
|
|
||||||
|
// Package protocol implements the Block Exchange Protocol.
|
||||||
|
package protocol
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
// Copyright (C) 2014 The Protocol Authors.
|
||||||
|
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ecNoError int32 = iota
|
||||||
|
ecGeneric
|
||||||
|
ecNoSuchFile
|
||||||
|
ecInvalid
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNoError error = nil
|
||||||
|
ErrGeneric = errors.New("generic error")
|
||||||
|
ErrNoSuchFile = errors.New("no such file")
|
||||||
|
ErrInvalid = errors.New("file is invalid")
|
||||||
|
)
|
||||||
|
|
||||||
|
var lookupError = map[int32]error{
|
||||||
|
ecNoError: ErrNoError,
|
||||||
|
ecGeneric: ErrGeneric,
|
||||||
|
ecNoSuchFile: ErrNoSuchFile,
|
||||||
|
ecInvalid: ErrInvalid,
|
||||||
|
}
|
||||||
|
|
||||||
|
var lookupCode = map[error]int32{
|
||||||
|
ErrNoError: ecNoError,
|
||||||
|
ErrGeneric: ecGeneric,
|
||||||
|
ErrNoSuchFile: ecNoSuchFile,
|
||||||
|
ErrInvalid: ecInvalid,
|
||||||
|
}
|
||||||
|
|
||||||
|
func codeToError(errcode int32) error {
|
||||||
|
err, ok := lookupError[errcode]
|
||||||
|
if !ok {
|
||||||
|
return ErrGeneric
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func errorToCode(err error) int32 {
|
||||||
|
code, ok := lookupCode[err]
|
||||||
|
if !ok {
|
||||||
|
return ecGeneric
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
Generated
Vendored
+1
-14
@@ -1,17 +1,4 @@
|
|||||||
// Copyright (C) 2014 The Syncthing Authors.
|
// Copyright (C) 2014 The Protocol Authors.
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it
|
|
||||||
// under the terms of the GNU General Public License as published by the Free
|
|
||||||
// Software Foundation, either version 3 of the License, or (at your option)
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
// more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along
|
|
||||||
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
+126
@@ -0,0 +1,126 @@
|
|||||||
|
// Copyright (C) 2014 The Protocol Authors.
|
||||||
|
|
||||||
|
//go:generate -command genxdr go run ../syncthing/Godeps/_workspace/src/github.com/calmh/xdr/cmd/genxdr/main.go
|
||||||
|
//go:generate genxdr -o message_xdr.go message.go
|
||||||
|
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type IndexMessage struct {
|
||||||
|
Folder string
|
||||||
|
Files []FileInfo
|
||||||
|
Flags uint32
|
||||||
|
Options []Option // max:64
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileInfo struct {
|
||||||
|
Name string // max:8192
|
||||||
|
Flags uint32
|
||||||
|
Modified int64
|
||||||
|
Version Vector
|
||||||
|
LocalVersion int64
|
||||||
|
Blocks []BlockInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FileInfo) String() string {
|
||||||
|
return fmt.Sprintf("File{Name:%q, Flags:0%o, Modified:%d, Version:%v, Size:%d, Blocks:%v}",
|
||||||
|
f.Name, f.Flags, f.Modified, f.Version, f.Size(), f.Blocks)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FileInfo) Size() (bytes int64) {
|
||||||
|
if f.IsDeleted() || f.IsDirectory() {
|
||||||
|
return 128
|
||||||
|
}
|
||||||
|
for _, b := range f.Blocks {
|
||||||
|
bytes += int64(b.Size)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FileInfo) IsDeleted() bool {
|
||||||
|
return f.Flags&FlagDeleted != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FileInfo) IsInvalid() bool {
|
||||||
|
return f.Flags&FlagInvalid != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FileInfo) IsDirectory() bool {
|
||||||
|
return f.Flags&FlagDirectory != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FileInfo) IsSymlink() bool {
|
||||||
|
return f.Flags&FlagSymlink != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FileInfo) HasPermissionBits() bool {
|
||||||
|
return f.Flags&FlagNoPermBits == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlockInfo struct {
|
||||||
|
Offset int64 // noencode (cache only)
|
||||||
|
Size int32
|
||||||
|
Hash []byte // max:64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BlockInfo) String() string {
|
||||||
|
return fmt.Sprintf("Block{%d/%d/%x}", b.Offset, b.Size, b.Hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
type RequestMessage struct {
|
||||||
|
Folder string // max:64
|
||||||
|
Name string // max:8192
|
||||||
|
Offset int64
|
||||||
|
Size int32
|
||||||
|
Hash []byte // max:64
|
||||||
|
Flags uint32
|
||||||
|
Options []Option // max:64
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResponseMessage struct {
|
||||||
|
Data []byte
|
||||||
|
Code int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClusterConfigMessage struct {
|
||||||
|
ClientName string // max:64
|
||||||
|
ClientVersion string // max:64
|
||||||
|
Folders []Folder
|
||||||
|
Options []Option // max:64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *ClusterConfigMessage) GetOption(key string) string {
|
||||||
|
for _, option := range o.Options {
|
||||||
|
if option.Key == key {
|
||||||
|
return option.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type Folder struct {
|
||||||
|
ID string // max:64
|
||||||
|
Devices []Device
|
||||||
|
Flags uint32
|
||||||
|
Options []Option // max:64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Device struct {
|
||||||
|
ID []byte // max:32
|
||||||
|
MaxLocalVersion int64
|
||||||
|
Flags uint32
|
||||||
|
Options []Option // max:64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Option struct {
|
||||||
|
Key string // max:64
|
||||||
|
Value string // max:1024
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloseMessage struct {
|
||||||
|
Reason string // max:1024
|
||||||
|
Code int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmptyMessage struct{}
|
||||||
Generated
Vendored
+268
-210
@@ -30,18 +30,28 @@ IndexMessage Structure:
|
|||||||
\ Zero or more FileInfo Structures \
|
\ Zero or more FileInfo Structures \
|
||||||
/ /
|
/ /
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Flags |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Number of Options |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
/ /
|
||||||
|
\ Zero or more Option Structures \
|
||||||
|
/ /
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
|
||||||
|
|
||||||
struct IndexMessage {
|
struct IndexMessage {
|
||||||
string Folder<64>;
|
string Folder<>;
|
||||||
FileInfo Files<>;
|
FileInfo Files<>;
|
||||||
|
unsigned int Flags;
|
||||||
|
Option Options<64>;
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func (o IndexMessage) EncodeXDR(w io.Writer) (int, error) {
|
func (o IndexMessage) EncodeXDR(w io.Writer) (int, error) {
|
||||||
var xw = xdr.NewWriter(w)
|
var xw = xdr.NewWriter(w)
|
||||||
return o.encodeXDR(xw)
|
return o.EncodeXDRInto(xw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o IndexMessage) MarshalXDR() ([]byte, error) {
|
func (o IndexMessage) MarshalXDR() ([]byte, error) {
|
||||||
@@ -59,18 +69,26 @@ func (o IndexMessage) MustMarshalXDR() []byte {
|
|||||||
func (o IndexMessage) AppendXDR(bs []byte) ([]byte, error) {
|
func (o IndexMessage) AppendXDR(bs []byte) ([]byte, error) {
|
||||||
var aw = xdr.AppendWriter(bs)
|
var aw = xdr.AppendWriter(bs)
|
||||||
var xw = xdr.NewWriter(&aw)
|
var xw = xdr.NewWriter(&aw)
|
||||||
_, err := o.encodeXDR(xw)
|
_, err := o.EncodeXDRInto(xw)
|
||||||
return []byte(aw), err
|
return []byte(aw), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o IndexMessage) encodeXDR(xw *xdr.Writer) (int, error) {
|
func (o IndexMessage) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||||
if l := len(o.Folder); l > 64 {
|
|
||||||
return xw.Tot(), xdr.ElementSizeExceeded("Folder", l, 64)
|
|
||||||
}
|
|
||||||
xw.WriteString(o.Folder)
|
xw.WriteString(o.Folder)
|
||||||
xw.WriteUint32(uint32(len(o.Files)))
|
xw.WriteUint32(uint32(len(o.Files)))
|
||||||
for i := range o.Files {
|
for i := range o.Files {
|
||||||
_, err := o.Files[i].encodeXDR(xw)
|
_, err := o.Files[i].EncodeXDRInto(xw)
|
||||||
|
if err != nil {
|
||||||
|
return xw.Tot(), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xw.WriteUint32(o.Flags)
|
||||||
|
if l := len(o.Options); l > 64 {
|
||||||
|
return xw.Tot(), xdr.ElementSizeExceeded("Options", l, 64)
|
||||||
|
}
|
||||||
|
xw.WriteUint32(uint32(len(o.Options)))
|
||||||
|
for i := range o.Options {
|
||||||
|
_, err := o.Options[i].EncodeXDRInto(xw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xw.Tot(), err
|
return xw.Tot(), err
|
||||||
}
|
}
|
||||||
@@ -80,21 +98,36 @@ func (o IndexMessage) encodeXDR(xw *xdr.Writer) (int, error) {
|
|||||||
|
|
||||||
func (o *IndexMessage) DecodeXDR(r io.Reader) error {
|
func (o *IndexMessage) DecodeXDR(r io.Reader) error {
|
||||||
xr := xdr.NewReader(r)
|
xr := xdr.NewReader(r)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *IndexMessage) UnmarshalXDR(bs []byte) error {
|
func (o *IndexMessage) UnmarshalXDR(bs []byte) error {
|
||||||
var br = bytes.NewReader(bs)
|
var br = bytes.NewReader(bs)
|
||||||
var xr = xdr.NewReader(br)
|
var xr = xdr.NewReader(br)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *IndexMessage) decodeXDR(xr *xdr.Reader) error {
|
func (o *IndexMessage) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||||
o.Folder = xr.ReadStringMax(64)
|
o.Folder = xr.ReadString()
|
||||||
_FilesSize := int(xr.ReadUint32())
|
_FilesSize := int(xr.ReadUint32())
|
||||||
|
if _FilesSize < 0 {
|
||||||
|
return xdr.ElementSizeExceeded("Files", _FilesSize, 0)
|
||||||
|
}
|
||||||
o.Files = make([]FileInfo, _FilesSize)
|
o.Files = make([]FileInfo, _FilesSize)
|
||||||
for i := range o.Files {
|
for i := range o.Files {
|
||||||
(&o.Files[i]).decodeXDR(xr)
|
(&o.Files[i]).DecodeXDRFrom(xr)
|
||||||
|
}
|
||||||
|
o.Flags = xr.ReadUint32()
|
||||||
|
_OptionsSize := int(xr.ReadUint32())
|
||||||
|
if _OptionsSize < 0 {
|
||||||
|
return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
|
||||||
|
}
|
||||||
|
if _OptionsSize > 64 {
|
||||||
|
return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
|
||||||
|
}
|
||||||
|
o.Options = make([]Option, _OptionsSize)
|
||||||
|
for i := range o.Options {
|
||||||
|
(&o.Options[i]).DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
return xr.Error()
|
return xr.Error()
|
||||||
}
|
}
|
||||||
@@ -118,9 +151,9 @@ FileInfo Structure:
|
|||||||
+ Modified (64 bits) +
|
+ Modified (64 bits) +
|
||||||
| |
|
| |
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
| |
|
/ /
|
||||||
+ Version (64 bits) +
|
\ Vector Structure \
|
||||||
| |
|
/ /
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
| |
|
| |
|
||||||
+ Local Version (64 bits) +
|
+ Local Version (64 bits) +
|
||||||
@@ -138,8 +171,8 @@ struct FileInfo {
|
|||||||
string Name<8192>;
|
string Name<8192>;
|
||||||
unsigned int Flags;
|
unsigned int Flags;
|
||||||
hyper Modified;
|
hyper Modified;
|
||||||
unsigned hyper Version;
|
Vector Version;
|
||||||
unsigned hyper LocalVersion;
|
hyper LocalVersion;
|
||||||
BlockInfo Blocks<>;
|
BlockInfo Blocks<>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +180,7 @@ struct FileInfo {
|
|||||||
|
|
||||||
func (o FileInfo) EncodeXDR(w io.Writer) (int, error) {
|
func (o FileInfo) EncodeXDR(w io.Writer) (int, error) {
|
||||||
var xw = xdr.NewWriter(w)
|
var xw = xdr.NewWriter(w)
|
||||||
return o.encodeXDR(xw)
|
return o.EncodeXDRInto(xw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o FileInfo) MarshalXDR() ([]byte, error) {
|
func (o FileInfo) MarshalXDR() ([]byte, error) {
|
||||||
@@ -165,22 +198,25 @@ func (o FileInfo) MustMarshalXDR() []byte {
|
|||||||
func (o FileInfo) AppendXDR(bs []byte) ([]byte, error) {
|
func (o FileInfo) AppendXDR(bs []byte) ([]byte, error) {
|
||||||
var aw = xdr.AppendWriter(bs)
|
var aw = xdr.AppendWriter(bs)
|
||||||
var xw = xdr.NewWriter(&aw)
|
var xw = xdr.NewWriter(&aw)
|
||||||
_, err := o.encodeXDR(xw)
|
_, err := o.EncodeXDRInto(xw)
|
||||||
return []byte(aw), err
|
return []byte(aw), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o FileInfo) encodeXDR(xw *xdr.Writer) (int, error) {
|
func (o FileInfo) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||||
if l := len(o.Name); l > 8192 {
|
if l := len(o.Name); l > 8192 {
|
||||||
return xw.Tot(), xdr.ElementSizeExceeded("Name", l, 8192)
|
return xw.Tot(), xdr.ElementSizeExceeded("Name", l, 8192)
|
||||||
}
|
}
|
||||||
xw.WriteString(o.Name)
|
xw.WriteString(o.Name)
|
||||||
xw.WriteUint32(o.Flags)
|
xw.WriteUint32(o.Flags)
|
||||||
xw.WriteUint64(uint64(o.Modified))
|
xw.WriteUint64(uint64(o.Modified))
|
||||||
xw.WriteUint64(o.Version)
|
_, err := o.Version.EncodeXDRInto(xw)
|
||||||
xw.WriteUint64(o.LocalVersion)
|
if err != nil {
|
||||||
|
return xw.Tot(), err
|
||||||
|
}
|
||||||
|
xw.WriteUint64(uint64(o.LocalVersion))
|
||||||
xw.WriteUint32(uint32(len(o.Blocks)))
|
xw.WriteUint32(uint32(len(o.Blocks)))
|
||||||
for i := range o.Blocks {
|
for i := range o.Blocks {
|
||||||
_, err := o.Blocks[i].encodeXDR(xw)
|
_, err := o.Blocks[i].EncodeXDRInto(xw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xw.Tot(), err
|
return xw.Tot(), err
|
||||||
}
|
}
|
||||||
@@ -190,131 +226,34 @@ func (o FileInfo) encodeXDR(xw *xdr.Writer) (int, error) {
|
|||||||
|
|
||||||
func (o *FileInfo) DecodeXDR(r io.Reader) error {
|
func (o *FileInfo) DecodeXDR(r io.Reader) error {
|
||||||
xr := xdr.NewReader(r)
|
xr := xdr.NewReader(r)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *FileInfo) UnmarshalXDR(bs []byte) error {
|
func (o *FileInfo) UnmarshalXDR(bs []byte) error {
|
||||||
var br = bytes.NewReader(bs)
|
var br = bytes.NewReader(bs)
|
||||||
var xr = xdr.NewReader(br)
|
var xr = xdr.NewReader(br)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *FileInfo) decodeXDR(xr *xdr.Reader) error {
|
func (o *FileInfo) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||||
o.Name = xr.ReadStringMax(8192)
|
o.Name = xr.ReadStringMax(8192)
|
||||||
o.Flags = xr.ReadUint32()
|
o.Flags = xr.ReadUint32()
|
||||||
o.Modified = int64(xr.ReadUint64())
|
o.Modified = int64(xr.ReadUint64())
|
||||||
o.Version = xr.ReadUint64()
|
(&o.Version).DecodeXDRFrom(xr)
|
||||||
o.LocalVersion = xr.ReadUint64()
|
o.LocalVersion = int64(xr.ReadUint64())
|
||||||
_BlocksSize := int(xr.ReadUint32())
|
_BlocksSize := int(xr.ReadUint32())
|
||||||
|
if _BlocksSize < 0 {
|
||||||
|
return xdr.ElementSizeExceeded("Blocks", _BlocksSize, 0)
|
||||||
|
}
|
||||||
o.Blocks = make([]BlockInfo, _BlocksSize)
|
o.Blocks = make([]BlockInfo, _BlocksSize)
|
||||||
for i := range o.Blocks {
|
for i := range o.Blocks {
|
||||||
(&o.Blocks[i]).decodeXDR(xr)
|
(&o.Blocks[i]).DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
return xr.Error()
|
return xr.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
FileInfoTruncated Structure:
|
|
||||||
|
|
||||||
0 1 2 3
|
|
||||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| Length of Name |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
/ /
|
|
||||||
\ Name (variable length) \
|
|
||||||
/ /
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| Flags |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| |
|
|
||||||
+ Modified (64 bits) +
|
|
||||||
| |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| |
|
|
||||||
+ Version (64 bits) +
|
|
||||||
| |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| |
|
|
||||||
+ Local Version (64 bits) +
|
|
||||||
| |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| Num Blocks |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
|
|
||||||
|
|
||||||
struct FileInfoTruncated {
|
|
||||||
string Name<8192>;
|
|
||||||
unsigned int Flags;
|
|
||||||
hyper Modified;
|
|
||||||
unsigned hyper Version;
|
|
||||||
unsigned hyper LocalVersion;
|
|
||||||
unsigned int NumBlocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
func (o FileInfoTruncated) EncodeXDR(w io.Writer) (int, error) {
|
|
||||||
var xw = xdr.NewWriter(w)
|
|
||||||
return o.encodeXDR(xw)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o FileInfoTruncated) MarshalXDR() ([]byte, error) {
|
|
||||||
return o.AppendXDR(make([]byte, 0, 128))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o FileInfoTruncated) MustMarshalXDR() []byte {
|
|
||||||
bs, err := o.MarshalXDR()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return bs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o FileInfoTruncated) AppendXDR(bs []byte) ([]byte, error) {
|
|
||||||
var aw = xdr.AppendWriter(bs)
|
|
||||||
var xw = xdr.NewWriter(&aw)
|
|
||||||
_, err := o.encodeXDR(xw)
|
|
||||||
return []byte(aw), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o FileInfoTruncated) encodeXDR(xw *xdr.Writer) (int, error) {
|
|
||||||
if l := len(o.Name); l > 8192 {
|
|
||||||
return xw.Tot(), xdr.ElementSizeExceeded("Name", l, 8192)
|
|
||||||
}
|
|
||||||
xw.WriteString(o.Name)
|
|
||||||
xw.WriteUint32(o.Flags)
|
|
||||||
xw.WriteUint64(uint64(o.Modified))
|
|
||||||
xw.WriteUint64(o.Version)
|
|
||||||
xw.WriteUint64(o.LocalVersion)
|
|
||||||
xw.WriteUint32(o.NumBlocks)
|
|
||||||
return xw.Tot(), xw.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *FileInfoTruncated) DecodeXDR(r io.Reader) error {
|
|
||||||
xr := xdr.NewReader(r)
|
|
||||||
return o.decodeXDR(xr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *FileInfoTruncated) UnmarshalXDR(bs []byte) error {
|
|
||||||
var br = bytes.NewReader(bs)
|
|
||||||
var xr = xdr.NewReader(br)
|
|
||||||
return o.decodeXDR(xr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *FileInfoTruncated) decodeXDR(xr *xdr.Reader) error {
|
|
||||||
o.Name = xr.ReadStringMax(8192)
|
|
||||||
o.Flags = xr.ReadUint32()
|
|
||||||
o.Modified = int64(xr.ReadUint64())
|
|
||||||
o.Version = xr.ReadUint64()
|
|
||||||
o.LocalVersion = xr.ReadUint64()
|
|
||||||
o.NumBlocks = xr.ReadUint32()
|
|
||||||
return xr.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
BlockInfo Structure:
|
BlockInfo Structure:
|
||||||
|
|
||||||
0 1 2 3
|
0 1 2 3
|
||||||
@@ -331,7 +270,7 @@ BlockInfo Structure:
|
|||||||
|
|
||||||
|
|
||||||
struct BlockInfo {
|
struct BlockInfo {
|
||||||
unsigned int Size;
|
int Size;
|
||||||
opaque Hash<64>;
|
opaque Hash<64>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,7 +278,7 @@ struct BlockInfo {
|
|||||||
|
|
||||||
func (o BlockInfo) EncodeXDR(w io.Writer) (int, error) {
|
func (o BlockInfo) EncodeXDR(w io.Writer) (int, error) {
|
||||||
var xw = xdr.NewWriter(w)
|
var xw = xdr.NewWriter(w)
|
||||||
return o.encodeXDR(xw)
|
return o.EncodeXDRInto(xw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o BlockInfo) MarshalXDR() ([]byte, error) {
|
func (o BlockInfo) MarshalXDR() ([]byte, error) {
|
||||||
@@ -357,12 +296,12 @@ func (o BlockInfo) MustMarshalXDR() []byte {
|
|||||||
func (o BlockInfo) AppendXDR(bs []byte) ([]byte, error) {
|
func (o BlockInfo) AppendXDR(bs []byte) ([]byte, error) {
|
||||||
var aw = xdr.AppendWriter(bs)
|
var aw = xdr.AppendWriter(bs)
|
||||||
var xw = xdr.NewWriter(&aw)
|
var xw = xdr.NewWriter(&aw)
|
||||||
_, err := o.encodeXDR(xw)
|
_, err := o.EncodeXDRInto(xw)
|
||||||
return []byte(aw), err
|
return []byte(aw), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o BlockInfo) encodeXDR(xw *xdr.Writer) (int, error) {
|
func (o BlockInfo) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||||
xw.WriteUint32(o.Size)
|
xw.WriteUint32(uint32(o.Size))
|
||||||
if l := len(o.Hash); l > 64 {
|
if l := len(o.Hash); l > 64 {
|
||||||
return xw.Tot(), xdr.ElementSizeExceeded("Hash", l, 64)
|
return xw.Tot(), xdr.ElementSizeExceeded("Hash", l, 64)
|
||||||
}
|
}
|
||||||
@@ -372,17 +311,17 @@ func (o BlockInfo) encodeXDR(xw *xdr.Writer) (int, error) {
|
|||||||
|
|
||||||
func (o *BlockInfo) DecodeXDR(r io.Reader) error {
|
func (o *BlockInfo) DecodeXDR(r io.Reader) error {
|
||||||
xr := xdr.NewReader(r)
|
xr := xdr.NewReader(r)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *BlockInfo) UnmarshalXDR(bs []byte) error {
|
func (o *BlockInfo) UnmarshalXDR(bs []byte) error {
|
||||||
var br = bytes.NewReader(bs)
|
var br = bytes.NewReader(bs)
|
||||||
var xr = xdr.NewReader(br)
|
var xr = xdr.NewReader(br)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *BlockInfo) decodeXDR(xr *xdr.Reader) error {
|
func (o *BlockInfo) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||||
o.Size = xr.ReadUint32()
|
o.Size = int32(xr.ReadUint32())
|
||||||
o.Hash = xr.ReadBytesMax(64)
|
o.Hash = xr.ReadBytesMax(64)
|
||||||
return xr.Error()
|
return xr.Error()
|
||||||
}
|
}
|
||||||
@@ -412,20 +351,37 @@ RequestMessage Structure:
|
|||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
| Size |
|
| Size |
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Length of Hash |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
/ /
|
||||||
|
\ Hash (variable length) \
|
||||||
|
/ /
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Flags |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Number of Options |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
/ /
|
||||||
|
\ Zero or more Option Structures \
|
||||||
|
/ /
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
|
||||||
|
|
||||||
struct RequestMessage {
|
struct RequestMessage {
|
||||||
string Folder<64>;
|
string Folder<64>;
|
||||||
string Name<8192>;
|
string Name<8192>;
|
||||||
unsigned hyper Offset;
|
hyper Offset;
|
||||||
unsigned int Size;
|
int Size;
|
||||||
|
opaque Hash<64>;
|
||||||
|
unsigned int Flags;
|
||||||
|
Option Options<64>;
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func (o RequestMessage) EncodeXDR(w io.Writer) (int, error) {
|
func (o RequestMessage) EncodeXDR(w io.Writer) (int, error) {
|
||||||
var xw = xdr.NewWriter(w)
|
var xw = xdr.NewWriter(w)
|
||||||
return o.encodeXDR(xw)
|
return o.EncodeXDRInto(xw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o RequestMessage) MarshalXDR() ([]byte, error) {
|
func (o RequestMessage) MarshalXDR() ([]byte, error) {
|
||||||
@@ -443,11 +399,11 @@ func (o RequestMessage) MustMarshalXDR() []byte {
|
|||||||
func (o RequestMessage) AppendXDR(bs []byte) ([]byte, error) {
|
func (o RequestMessage) AppendXDR(bs []byte) ([]byte, error) {
|
||||||
var aw = xdr.AppendWriter(bs)
|
var aw = xdr.AppendWriter(bs)
|
||||||
var xw = xdr.NewWriter(&aw)
|
var xw = xdr.NewWriter(&aw)
|
||||||
_, err := o.encodeXDR(xw)
|
_, err := o.EncodeXDRInto(xw)
|
||||||
return []byte(aw), err
|
return []byte(aw), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o RequestMessage) encodeXDR(xw *xdr.Writer) (int, error) {
|
func (o RequestMessage) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||||
if l := len(o.Folder); l > 64 {
|
if l := len(o.Folder); l > 64 {
|
||||||
return xw.Tot(), xdr.ElementSizeExceeded("Folder", l, 64)
|
return xw.Tot(), xdr.ElementSizeExceeded("Folder", l, 64)
|
||||||
}
|
}
|
||||||
@@ -456,27 +412,55 @@ func (o RequestMessage) encodeXDR(xw *xdr.Writer) (int, error) {
|
|||||||
return xw.Tot(), xdr.ElementSizeExceeded("Name", l, 8192)
|
return xw.Tot(), xdr.ElementSizeExceeded("Name", l, 8192)
|
||||||
}
|
}
|
||||||
xw.WriteString(o.Name)
|
xw.WriteString(o.Name)
|
||||||
xw.WriteUint64(o.Offset)
|
xw.WriteUint64(uint64(o.Offset))
|
||||||
xw.WriteUint32(o.Size)
|
xw.WriteUint32(uint32(o.Size))
|
||||||
|
if l := len(o.Hash); l > 64 {
|
||||||
|
return xw.Tot(), xdr.ElementSizeExceeded("Hash", l, 64)
|
||||||
|
}
|
||||||
|
xw.WriteBytes(o.Hash)
|
||||||
|
xw.WriteUint32(o.Flags)
|
||||||
|
if l := len(o.Options); l > 64 {
|
||||||
|
return xw.Tot(), xdr.ElementSizeExceeded("Options", l, 64)
|
||||||
|
}
|
||||||
|
xw.WriteUint32(uint32(len(o.Options)))
|
||||||
|
for i := range o.Options {
|
||||||
|
_, err := o.Options[i].EncodeXDRInto(xw)
|
||||||
|
if err != nil {
|
||||||
|
return xw.Tot(), err
|
||||||
|
}
|
||||||
|
}
|
||||||
return xw.Tot(), xw.Error()
|
return xw.Tot(), xw.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *RequestMessage) DecodeXDR(r io.Reader) error {
|
func (o *RequestMessage) DecodeXDR(r io.Reader) error {
|
||||||
xr := xdr.NewReader(r)
|
xr := xdr.NewReader(r)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *RequestMessage) UnmarshalXDR(bs []byte) error {
|
func (o *RequestMessage) UnmarshalXDR(bs []byte) error {
|
||||||
var br = bytes.NewReader(bs)
|
var br = bytes.NewReader(bs)
|
||||||
var xr = xdr.NewReader(br)
|
var xr = xdr.NewReader(br)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *RequestMessage) decodeXDR(xr *xdr.Reader) error {
|
func (o *RequestMessage) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||||
o.Folder = xr.ReadStringMax(64)
|
o.Folder = xr.ReadStringMax(64)
|
||||||
o.Name = xr.ReadStringMax(8192)
|
o.Name = xr.ReadStringMax(8192)
|
||||||
o.Offset = xr.ReadUint64()
|
o.Offset = int64(xr.ReadUint64())
|
||||||
o.Size = xr.ReadUint32()
|
o.Size = int32(xr.ReadUint32())
|
||||||
|
o.Hash = xr.ReadBytesMax(64)
|
||||||
|
o.Flags = xr.ReadUint32()
|
||||||
|
_OptionsSize := int(xr.ReadUint32())
|
||||||
|
if _OptionsSize < 0 {
|
||||||
|
return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
|
||||||
|
}
|
||||||
|
if _OptionsSize > 64 {
|
||||||
|
return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
|
||||||
|
}
|
||||||
|
o.Options = make([]Option, _OptionsSize)
|
||||||
|
for i := range o.Options {
|
||||||
|
(&o.Options[i]).DecodeXDRFrom(xr)
|
||||||
|
}
|
||||||
return xr.Error()
|
return xr.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -493,17 +477,20 @@ ResponseMessage Structure:
|
|||||||
\ Data (variable length) \
|
\ Data (variable length) \
|
||||||
/ /
|
/ /
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Code |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
|
||||||
|
|
||||||
struct ResponseMessage {
|
struct ResponseMessage {
|
||||||
opaque Data<>;
|
opaque Data<>;
|
||||||
|
int Code;
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func (o ResponseMessage) EncodeXDR(w io.Writer) (int, error) {
|
func (o ResponseMessage) EncodeXDR(w io.Writer) (int, error) {
|
||||||
var xw = xdr.NewWriter(w)
|
var xw = xdr.NewWriter(w)
|
||||||
return o.encodeXDR(xw)
|
return o.EncodeXDRInto(xw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o ResponseMessage) MarshalXDR() ([]byte, error) {
|
func (o ResponseMessage) MarshalXDR() ([]byte, error) {
|
||||||
@@ -521,28 +508,30 @@ func (o ResponseMessage) MustMarshalXDR() []byte {
|
|||||||
func (o ResponseMessage) AppendXDR(bs []byte) ([]byte, error) {
|
func (o ResponseMessage) AppendXDR(bs []byte) ([]byte, error) {
|
||||||
var aw = xdr.AppendWriter(bs)
|
var aw = xdr.AppendWriter(bs)
|
||||||
var xw = xdr.NewWriter(&aw)
|
var xw = xdr.NewWriter(&aw)
|
||||||
_, err := o.encodeXDR(xw)
|
_, err := o.EncodeXDRInto(xw)
|
||||||
return []byte(aw), err
|
return []byte(aw), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o ResponseMessage) encodeXDR(xw *xdr.Writer) (int, error) {
|
func (o ResponseMessage) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||||
xw.WriteBytes(o.Data)
|
xw.WriteBytes(o.Data)
|
||||||
|
xw.WriteUint32(uint32(o.Code))
|
||||||
return xw.Tot(), xw.Error()
|
return xw.Tot(), xw.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *ResponseMessage) DecodeXDR(r io.Reader) error {
|
func (o *ResponseMessage) DecodeXDR(r io.Reader) error {
|
||||||
xr := xdr.NewReader(r)
|
xr := xdr.NewReader(r)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *ResponseMessage) UnmarshalXDR(bs []byte) error {
|
func (o *ResponseMessage) UnmarshalXDR(bs []byte) error {
|
||||||
var br = bytes.NewReader(bs)
|
var br = bytes.NewReader(bs)
|
||||||
var xr = xdr.NewReader(br)
|
var xr = xdr.NewReader(br)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *ResponseMessage) decodeXDR(xr *xdr.Reader) error {
|
func (o *ResponseMessage) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||||
o.Data = xr.ReadBytes()
|
o.Data = xr.ReadBytes()
|
||||||
|
o.Code = int32(xr.ReadUint32())
|
||||||
return xr.Error()
|
return xr.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,7 +571,7 @@ ClusterConfigMessage Structure:
|
|||||||
struct ClusterConfigMessage {
|
struct ClusterConfigMessage {
|
||||||
string ClientName<64>;
|
string ClientName<64>;
|
||||||
string ClientVersion<64>;
|
string ClientVersion<64>;
|
||||||
Folder Folders<64>;
|
Folder Folders<>;
|
||||||
Option Options<64>;
|
Option Options<64>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -590,7 +579,7 @@ struct ClusterConfigMessage {
|
|||||||
|
|
||||||
func (o ClusterConfigMessage) EncodeXDR(w io.Writer) (int, error) {
|
func (o ClusterConfigMessage) EncodeXDR(w io.Writer) (int, error) {
|
||||||
var xw = xdr.NewWriter(w)
|
var xw = xdr.NewWriter(w)
|
||||||
return o.encodeXDR(xw)
|
return o.EncodeXDRInto(xw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o ClusterConfigMessage) MarshalXDR() ([]byte, error) {
|
func (o ClusterConfigMessage) MarshalXDR() ([]byte, error) {
|
||||||
@@ -608,11 +597,11 @@ func (o ClusterConfigMessage) MustMarshalXDR() []byte {
|
|||||||
func (o ClusterConfigMessage) AppendXDR(bs []byte) ([]byte, error) {
|
func (o ClusterConfigMessage) AppendXDR(bs []byte) ([]byte, error) {
|
||||||
var aw = xdr.AppendWriter(bs)
|
var aw = xdr.AppendWriter(bs)
|
||||||
var xw = xdr.NewWriter(&aw)
|
var xw = xdr.NewWriter(&aw)
|
||||||
_, err := o.encodeXDR(xw)
|
_, err := o.EncodeXDRInto(xw)
|
||||||
return []byte(aw), err
|
return []byte(aw), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o ClusterConfigMessage) encodeXDR(xw *xdr.Writer) (int, error) {
|
func (o ClusterConfigMessage) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||||
if l := len(o.ClientName); l > 64 {
|
if l := len(o.ClientName); l > 64 {
|
||||||
return xw.Tot(), xdr.ElementSizeExceeded("ClientName", l, 64)
|
return xw.Tot(), xdr.ElementSizeExceeded("ClientName", l, 64)
|
||||||
}
|
}
|
||||||
@@ -621,12 +610,9 @@ func (o ClusterConfigMessage) encodeXDR(xw *xdr.Writer) (int, error) {
|
|||||||
return xw.Tot(), xdr.ElementSizeExceeded("ClientVersion", l, 64)
|
return xw.Tot(), xdr.ElementSizeExceeded("ClientVersion", l, 64)
|
||||||
}
|
}
|
||||||
xw.WriteString(o.ClientVersion)
|
xw.WriteString(o.ClientVersion)
|
||||||
if l := len(o.Folders); l > 64 {
|
|
||||||
return xw.Tot(), xdr.ElementSizeExceeded("Folders", l, 64)
|
|
||||||
}
|
|
||||||
xw.WriteUint32(uint32(len(o.Folders)))
|
xw.WriteUint32(uint32(len(o.Folders)))
|
||||||
for i := range o.Folders {
|
for i := range o.Folders {
|
||||||
_, err := o.Folders[i].encodeXDR(xw)
|
_, err := o.Folders[i].EncodeXDRInto(xw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xw.Tot(), err
|
return xw.Tot(), err
|
||||||
}
|
}
|
||||||
@@ -636,7 +622,7 @@ func (o ClusterConfigMessage) encodeXDR(xw *xdr.Writer) (int, error) {
|
|||||||
}
|
}
|
||||||
xw.WriteUint32(uint32(len(o.Options)))
|
xw.WriteUint32(uint32(len(o.Options)))
|
||||||
for i := range o.Options {
|
for i := range o.Options {
|
||||||
_, err := o.Options[i].encodeXDR(xw)
|
_, err := o.Options[i].EncodeXDRInto(xw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xw.Tot(), err
|
return xw.Tot(), err
|
||||||
}
|
}
|
||||||
@@ -646,33 +632,36 @@ func (o ClusterConfigMessage) encodeXDR(xw *xdr.Writer) (int, error) {
|
|||||||
|
|
||||||
func (o *ClusterConfigMessage) DecodeXDR(r io.Reader) error {
|
func (o *ClusterConfigMessage) DecodeXDR(r io.Reader) error {
|
||||||
xr := xdr.NewReader(r)
|
xr := xdr.NewReader(r)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *ClusterConfigMessage) UnmarshalXDR(bs []byte) error {
|
func (o *ClusterConfigMessage) UnmarshalXDR(bs []byte) error {
|
||||||
var br = bytes.NewReader(bs)
|
var br = bytes.NewReader(bs)
|
||||||
var xr = xdr.NewReader(br)
|
var xr = xdr.NewReader(br)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *ClusterConfigMessage) decodeXDR(xr *xdr.Reader) error {
|
func (o *ClusterConfigMessage) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||||
o.ClientName = xr.ReadStringMax(64)
|
o.ClientName = xr.ReadStringMax(64)
|
||||||
o.ClientVersion = xr.ReadStringMax(64)
|
o.ClientVersion = xr.ReadStringMax(64)
|
||||||
_FoldersSize := int(xr.ReadUint32())
|
_FoldersSize := int(xr.ReadUint32())
|
||||||
if _FoldersSize > 64 {
|
if _FoldersSize < 0 {
|
||||||
return xdr.ElementSizeExceeded("Folders", _FoldersSize, 64)
|
return xdr.ElementSizeExceeded("Folders", _FoldersSize, 0)
|
||||||
}
|
}
|
||||||
o.Folders = make([]Folder, _FoldersSize)
|
o.Folders = make([]Folder, _FoldersSize)
|
||||||
for i := range o.Folders {
|
for i := range o.Folders {
|
||||||
(&o.Folders[i]).decodeXDR(xr)
|
(&o.Folders[i]).DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
_OptionsSize := int(xr.ReadUint32())
|
_OptionsSize := int(xr.ReadUint32())
|
||||||
|
if _OptionsSize < 0 {
|
||||||
|
return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
|
||||||
|
}
|
||||||
if _OptionsSize > 64 {
|
if _OptionsSize > 64 {
|
||||||
return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
|
return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
|
||||||
}
|
}
|
||||||
o.Options = make([]Option, _OptionsSize)
|
o.Options = make([]Option, _OptionsSize)
|
||||||
for i := range o.Options {
|
for i := range o.Options {
|
||||||
(&o.Options[i]).decodeXDR(xr)
|
(&o.Options[i]).DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
return xr.Error()
|
return xr.Error()
|
||||||
}
|
}
|
||||||
@@ -696,18 +685,28 @@ Folder Structure:
|
|||||||
\ Zero or more Device Structures \
|
\ Zero or more Device Structures \
|
||||||
/ /
|
/ /
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Flags |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Number of Options |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
/ /
|
||||||
|
\ Zero or more Option Structures \
|
||||||
|
/ /
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
|
||||||
|
|
||||||
struct Folder {
|
struct Folder {
|
||||||
string ID<64>;
|
string ID<64>;
|
||||||
Device Devices<>;
|
Device Devices<>;
|
||||||
|
unsigned int Flags;
|
||||||
|
Option Options<64>;
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func (o Folder) EncodeXDR(w io.Writer) (int, error) {
|
func (o Folder) EncodeXDR(w io.Writer) (int, error) {
|
||||||
var xw = xdr.NewWriter(w)
|
var xw = xdr.NewWriter(w)
|
||||||
return o.encodeXDR(xw)
|
return o.EncodeXDRInto(xw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o Folder) MarshalXDR() ([]byte, error) {
|
func (o Folder) MarshalXDR() ([]byte, error) {
|
||||||
@@ -725,18 +724,29 @@ func (o Folder) MustMarshalXDR() []byte {
|
|||||||
func (o Folder) AppendXDR(bs []byte) ([]byte, error) {
|
func (o Folder) AppendXDR(bs []byte) ([]byte, error) {
|
||||||
var aw = xdr.AppendWriter(bs)
|
var aw = xdr.AppendWriter(bs)
|
||||||
var xw = xdr.NewWriter(&aw)
|
var xw = xdr.NewWriter(&aw)
|
||||||
_, err := o.encodeXDR(xw)
|
_, err := o.EncodeXDRInto(xw)
|
||||||
return []byte(aw), err
|
return []byte(aw), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o Folder) encodeXDR(xw *xdr.Writer) (int, error) {
|
func (o Folder) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||||
if l := len(o.ID); l > 64 {
|
if l := len(o.ID); l > 64 {
|
||||||
return xw.Tot(), xdr.ElementSizeExceeded("ID", l, 64)
|
return xw.Tot(), xdr.ElementSizeExceeded("ID", l, 64)
|
||||||
}
|
}
|
||||||
xw.WriteString(o.ID)
|
xw.WriteString(o.ID)
|
||||||
xw.WriteUint32(uint32(len(o.Devices)))
|
xw.WriteUint32(uint32(len(o.Devices)))
|
||||||
for i := range o.Devices {
|
for i := range o.Devices {
|
||||||
_, err := o.Devices[i].encodeXDR(xw)
|
_, err := o.Devices[i].EncodeXDRInto(xw)
|
||||||
|
if err != nil {
|
||||||
|
return xw.Tot(), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xw.WriteUint32(o.Flags)
|
||||||
|
if l := len(o.Options); l > 64 {
|
||||||
|
return xw.Tot(), xdr.ElementSizeExceeded("Options", l, 64)
|
||||||
|
}
|
||||||
|
xw.WriteUint32(uint32(len(o.Options)))
|
||||||
|
for i := range o.Options {
|
||||||
|
_, err := o.Options[i].EncodeXDRInto(xw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xw.Tot(), err
|
return xw.Tot(), err
|
||||||
}
|
}
|
||||||
@@ -746,21 +756,36 @@ func (o Folder) encodeXDR(xw *xdr.Writer) (int, error) {
|
|||||||
|
|
||||||
func (o *Folder) DecodeXDR(r io.Reader) error {
|
func (o *Folder) DecodeXDR(r io.Reader) error {
|
||||||
xr := xdr.NewReader(r)
|
xr := xdr.NewReader(r)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Folder) UnmarshalXDR(bs []byte) error {
|
func (o *Folder) UnmarshalXDR(bs []byte) error {
|
||||||
var br = bytes.NewReader(bs)
|
var br = bytes.NewReader(bs)
|
||||||
var xr = xdr.NewReader(br)
|
var xr = xdr.NewReader(br)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Folder) decodeXDR(xr *xdr.Reader) error {
|
func (o *Folder) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||||
o.ID = xr.ReadStringMax(64)
|
o.ID = xr.ReadStringMax(64)
|
||||||
_DevicesSize := int(xr.ReadUint32())
|
_DevicesSize := int(xr.ReadUint32())
|
||||||
|
if _DevicesSize < 0 {
|
||||||
|
return xdr.ElementSizeExceeded("Devices", _DevicesSize, 0)
|
||||||
|
}
|
||||||
o.Devices = make([]Device, _DevicesSize)
|
o.Devices = make([]Device, _DevicesSize)
|
||||||
for i := range o.Devices {
|
for i := range o.Devices {
|
||||||
(&o.Devices[i]).decodeXDR(xr)
|
(&o.Devices[i]).DecodeXDRFrom(xr)
|
||||||
|
}
|
||||||
|
o.Flags = xr.ReadUint32()
|
||||||
|
_OptionsSize := int(xr.ReadUint32())
|
||||||
|
if _OptionsSize < 0 {
|
||||||
|
return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
|
||||||
|
}
|
||||||
|
if _OptionsSize > 64 {
|
||||||
|
return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
|
||||||
|
}
|
||||||
|
o.Options = make([]Option, _OptionsSize)
|
||||||
|
for i := range o.Options {
|
||||||
|
(&o.Options[i]).DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
return xr.Error()
|
return xr.Error()
|
||||||
}
|
}
|
||||||
@@ -778,25 +803,32 @@ Device Structure:
|
|||||||
\ ID (variable length) \
|
\ ID (variable length) \
|
||||||
/ /
|
/ /
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
| Flags |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| |
|
| |
|
||||||
+ Max Local Version (64 bits) +
|
+ Max Local Version (64 bits) +
|
||||||
| |
|
| |
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Flags |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Number of Options |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
/ /
|
||||||
|
\ Zero or more Option Structures \
|
||||||
|
/ /
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
|
||||||
|
|
||||||
struct Device {
|
struct Device {
|
||||||
opaque ID<32>;
|
opaque ID<32>;
|
||||||
|
hyper MaxLocalVersion;
|
||||||
unsigned int Flags;
|
unsigned int Flags;
|
||||||
unsigned hyper MaxLocalVersion;
|
Option Options<64>;
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func (o Device) EncodeXDR(w io.Writer) (int, error) {
|
func (o Device) EncodeXDR(w io.Writer) (int, error) {
|
||||||
var xw = xdr.NewWriter(w)
|
var xw = xdr.NewWriter(w)
|
||||||
return o.encodeXDR(xw)
|
return o.EncodeXDRInto(xw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o Device) MarshalXDR() ([]byte, error) {
|
func (o Device) MarshalXDR() ([]byte, error) {
|
||||||
@@ -814,35 +846,56 @@ func (o Device) MustMarshalXDR() []byte {
|
|||||||
func (o Device) AppendXDR(bs []byte) ([]byte, error) {
|
func (o Device) AppendXDR(bs []byte) ([]byte, error) {
|
||||||
var aw = xdr.AppendWriter(bs)
|
var aw = xdr.AppendWriter(bs)
|
||||||
var xw = xdr.NewWriter(&aw)
|
var xw = xdr.NewWriter(&aw)
|
||||||
_, err := o.encodeXDR(xw)
|
_, err := o.EncodeXDRInto(xw)
|
||||||
return []byte(aw), err
|
return []byte(aw), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o Device) encodeXDR(xw *xdr.Writer) (int, error) {
|
func (o Device) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||||
if l := len(o.ID); l > 32 {
|
if l := len(o.ID); l > 32 {
|
||||||
return xw.Tot(), xdr.ElementSizeExceeded("ID", l, 32)
|
return xw.Tot(), xdr.ElementSizeExceeded("ID", l, 32)
|
||||||
}
|
}
|
||||||
xw.WriteBytes(o.ID)
|
xw.WriteBytes(o.ID)
|
||||||
|
xw.WriteUint64(uint64(o.MaxLocalVersion))
|
||||||
xw.WriteUint32(o.Flags)
|
xw.WriteUint32(o.Flags)
|
||||||
xw.WriteUint64(o.MaxLocalVersion)
|
if l := len(o.Options); l > 64 {
|
||||||
|
return xw.Tot(), xdr.ElementSizeExceeded("Options", l, 64)
|
||||||
|
}
|
||||||
|
xw.WriteUint32(uint32(len(o.Options)))
|
||||||
|
for i := range o.Options {
|
||||||
|
_, err := o.Options[i].EncodeXDRInto(xw)
|
||||||
|
if err != nil {
|
||||||
|
return xw.Tot(), err
|
||||||
|
}
|
||||||
|
}
|
||||||
return xw.Tot(), xw.Error()
|
return xw.Tot(), xw.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Device) DecodeXDR(r io.Reader) error {
|
func (o *Device) DecodeXDR(r io.Reader) error {
|
||||||
xr := xdr.NewReader(r)
|
xr := xdr.NewReader(r)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Device) UnmarshalXDR(bs []byte) error {
|
func (o *Device) UnmarshalXDR(bs []byte) error {
|
||||||
var br = bytes.NewReader(bs)
|
var br = bytes.NewReader(bs)
|
||||||
var xr = xdr.NewReader(br)
|
var xr = xdr.NewReader(br)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Device) decodeXDR(xr *xdr.Reader) error {
|
func (o *Device) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||||
o.ID = xr.ReadBytesMax(32)
|
o.ID = xr.ReadBytesMax(32)
|
||||||
|
o.MaxLocalVersion = int64(xr.ReadUint64())
|
||||||
o.Flags = xr.ReadUint32()
|
o.Flags = xr.ReadUint32()
|
||||||
o.MaxLocalVersion = xr.ReadUint64()
|
_OptionsSize := int(xr.ReadUint32())
|
||||||
|
if _OptionsSize < 0 {
|
||||||
|
return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
|
||||||
|
}
|
||||||
|
if _OptionsSize > 64 {
|
||||||
|
return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
|
||||||
|
}
|
||||||
|
o.Options = make([]Option, _OptionsSize)
|
||||||
|
for i := range o.Options {
|
||||||
|
(&o.Options[i]).DecodeXDRFrom(xr)
|
||||||
|
}
|
||||||
return xr.Error()
|
return xr.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -876,7 +929,7 @@ struct Option {
|
|||||||
|
|
||||||
func (o Option) EncodeXDR(w io.Writer) (int, error) {
|
func (o Option) EncodeXDR(w io.Writer) (int, error) {
|
||||||
var xw = xdr.NewWriter(w)
|
var xw = xdr.NewWriter(w)
|
||||||
return o.encodeXDR(xw)
|
return o.EncodeXDRInto(xw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o Option) MarshalXDR() ([]byte, error) {
|
func (o Option) MarshalXDR() ([]byte, error) {
|
||||||
@@ -894,11 +947,11 @@ func (o Option) MustMarshalXDR() []byte {
|
|||||||
func (o Option) AppendXDR(bs []byte) ([]byte, error) {
|
func (o Option) AppendXDR(bs []byte) ([]byte, error) {
|
||||||
var aw = xdr.AppendWriter(bs)
|
var aw = xdr.AppendWriter(bs)
|
||||||
var xw = xdr.NewWriter(&aw)
|
var xw = xdr.NewWriter(&aw)
|
||||||
_, err := o.encodeXDR(xw)
|
_, err := o.EncodeXDRInto(xw)
|
||||||
return []byte(aw), err
|
return []byte(aw), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o Option) encodeXDR(xw *xdr.Writer) (int, error) {
|
func (o Option) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||||
if l := len(o.Key); l > 64 {
|
if l := len(o.Key); l > 64 {
|
||||||
return xw.Tot(), xdr.ElementSizeExceeded("Key", l, 64)
|
return xw.Tot(), xdr.ElementSizeExceeded("Key", l, 64)
|
||||||
}
|
}
|
||||||
@@ -912,16 +965,16 @@ func (o Option) encodeXDR(xw *xdr.Writer) (int, error) {
|
|||||||
|
|
||||||
func (o *Option) DecodeXDR(r io.Reader) error {
|
func (o *Option) DecodeXDR(r io.Reader) error {
|
||||||
xr := xdr.NewReader(r)
|
xr := xdr.NewReader(r)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Option) UnmarshalXDR(bs []byte) error {
|
func (o *Option) UnmarshalXDR(bs []byte) error {
|
||||||
var br = bytes.NewReader(bs)
|
var br = bytes.NewReader(bs)
|
||||||
var xr = xdr.NewReader(br)
|
var xr = xdr.NewReader(br)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Option) decodeXDR(xr *xdr.Reader) error {
|
func (o *Option) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||||
o.Key = xr.ReadStringMax(64)
|
o.Key = xr.ReadStringMax(64)
|
||||||
o.Value = xr.ReadStringMax(1024)
|
o.Value = xr.ReadStringMax(1024)
|
||||||
return xr.Error()
|
return xr.Error()
|
||||||
@@ -940,17 +993,20 @@ CloseMessage Structure:
|
|||||||
\ Reason (variable length) \
|
\ Reason (variable length) \
|
||||||
/ /
|
/ /
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Code |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
|
||||||
|
|
||||||
struct CloseMessage {
|
struct CloseMessage {
|
||||||
string Reason<1024>;
|
string Reason<1024>;
|
||||||
|
int Code;
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func (o CloseMessage) EncodeXDR(w io.Writer) (int, error) {
|
func (o CloseMessage) EncodeXDR(w io.Writer) (int, error) {
|
||||||
var xw = xdr.NewWriter(w)
|
var xw = xdr.NewWriter(w)
|
||||||
return o.encodeXDR(xw)
|
return o.EncodeXDRInto(xw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o CloseMessage) MarshalXDR() ([]byte, error) {
|
func (o CloseMessage) MarshalXDR() ([]byte, error) {
|
||||||
@@ -968,31 +1024,33 @@ func (o CloseMessage) MustMarshalXDR() []byte {
|
|||||||
func (o CloseMessage) AppendXDR(bs []byte) ([]byte, error) {
|
func (o CloseMessage) AppendXDR(bs []byte) ([]byte, error) {
|
||||||
var aw = xdr.AppendWriter(bs)
|
var aw = xdr.AppendWriter(bs)
|
||||||
var xw = xdr.NewWriter(&aw)
|
var xw = xdr.NewWriter(&aw)
|
||||||
_, err := o.encodeXDR(xw)
|
_, err := o.EncodeXDRInto(xw)
|
||||||
return []byte(aw), err
|
return []byte(aw), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o CloseMessage) encodeXDR(xw *xdr.Writer) (int, error) {
|
func (o CloseMessage) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||||
if l := len(o.Reason); l > 1024 {
|
if l := len(o.Reason); l > 1024 {
|
||||||
return xw.Tot(), xdr.ElementSizeExceeded("Reason", l, 1024)
|
return xw.Tot(), xdr.ElementSizeExceeded("Reason", l, 1024)
|
||||||
}
|
}
|
||||||
xw.WriteString(o.Reason)
|
xw.WriteString(o.Reason)
|
||||||
|
xw.WriteUint32(uint32(o.Code))
|
||||||
return xw.Tot(), xw.Error()
|
return xw.Tot(), xw.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *CloseMessage) DecodeXDR(r io.Reader) error {
|
func (o *CloseMessage) DecodeXDR(r io.Reader) error {
|
||||||
xr := xdr.NewReader(r)
|
xr := xdr.NewReader(r)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *CloseMessage) UnmarshalXDR(bs []byte) error {
|
func (o *CloseMessage) UnmarshalXDR(bs []byte) error {
|
||||||
var br = bytes.NewReader(bs)
|
var br = bytes.NewReader(bs)
|
||||||
var xr = xdr.NewReader(br)
|
var xr = xdr.NewReader(br)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *CloseMessage) decodeXDR(xr *xdr.Reader) error {
|
func (o *CloseMessage) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||||
o.Reason = xr.ReadStringMax(1024)
|
o.Reason = xr.ReadStringMax(1024)
|
||||||
|
o.Code = int32(xr.ReadUint32())
|
||||||
return xr.Error()
|
return xr.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1012,7 +1070,7 @@ struct EmptyMessage {
|
|||||||
|
|
||||||
func (o EmptyMessage) EncodeXDR(w io.Writer) (int, error) {
|
func (o EmptyMessage) EncodeXDR(w io.Writer) (int, error) {
|
||||||
var xw = xdr.NewWriter(w)
|
var xw = xdr.NewWriter(w)
|
||||||
return o.encodeXDR(xw)
|
return o.EncodeXDRInto(xw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o EmptyMessage) MarshalXDR() ([]byte, error) {
|
func (o EmptyMessage) MarshalXDR() ([]byte, error) {
|
||||||
@@ -1030,25 +1088,25 @@ func (o EmptyMessage) MustMarshalXDR() []byte {
|
|||||||
func (o EmptyMessage) AppendXDR(bs []byte) ([]byte, error) {
|
func (o EmptyMessage) AppendXDR(bs []byte) ([]byte, error) {
|
||||||
var aw = xdr.AppendWriter(bs)
|
var aw = xdr.AppendWriter(bs)
|
||||||
var xw = xdr.NewWriter(&aw)
|
var xw = xdr.NewWriter(&aw)
|
||||||
_, err := o.encodeXDR(xw)
|
_, err := o.EncodeXDRInto(xw)
|
||||||
return []byte(aw), err
|
return []byte(aw), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o EmptyMessage) encodeXDR(xw *xdr.Writer) (int, error) {
|
func (o EmptyMessage) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||||
return xw.Tot(), xw.Error()
|
return xw.Tot(), xw.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *EmptyMessage) DecodeXDR(r io.Reader) error {
|
func (o *EmptyMessage) DecodeXDR(r io.Reader) error {
|
||||||
xr := xdr.NewReader(r)
|
xr := xdr.NewReader(r)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *EmptyMessage) UnmarshalXDR(bs []byte) error {
|
func (o *EmptyMessage) UnmarshalXDR(bs []byte) error {
|
||||||
var br = bytes.NewReader(bs)
|
var br = bytes.NewReader(bs)
|
||||||
var xr = xdr.NewReader(br)
|
var xr = xdr.NewReader(br)
|
||||||
return o.decodeXDR(xr)
|
return o.DecodeXDRFrom(xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *EmptyMessage) decodeXDR(xr *xdr.Reader) error {
|
func (o *EmptyMessage) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||||
return xr.Error()
|
return xr.Error()
|
||||||
}
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
// Copyright (C) 2014 The Protocol Authors.
|
||||||
|
|
||||||
|
// +build darwin
|
||||||
|
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
// Darwin uses NFD normalization
|
||||||
|
|
||||||
|
import "golang.org/x/text/unicode/norm"
|
||||||
|
|
||||||
|
type nativeModel struct {
|
||||||
|
next Model
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m nativeModel) Index(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) {
|
||||||
|
for i := range files {
|
||||||
|
files[i].Name = norm.NFD.String(files[i].Name)
|
||||||
|
}
|
||||||
|
m.next.Index(deviceID, folder, files, flags, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m nativeModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) {
|
||||||
|
for i := range files {
|
||||||
|
files[i].Name = norm.NFD.String(files[i].Name)
|
||||||
|
}
|
||||||
|
m.next.IndexUpdate(deviceID, folder, files, flags, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m nativeModel) Request(deviceID DeviceID, folder string, name string, offset int64, size int, hash []byte, flags uint32, options []Option) ([]byte, error) {
|
||||||
|
name = norm.NFD.String(name)
|
||||||
|
return m.next.Request(deviceID, folder, name, offset, size, hash, flags, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m nativeModel) ClusterConfig(deviceID DeviceID, config ClusterConfigMessage) {
|
||||||
|
m.next.ClusterConfig(deviceID, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m nativeModel) Close(deviceID DeviceID, err error) {
|
||||||
|
m.next.Close(deviceID, err)
|
||||||
|
}
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
// Copyright (C) 2014 The Protocol Authors.
|
||||||
|
|
||||||
|
// +build !windows,!darwin
|
||||||
|
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
// Normal Unixes uses NFC and slashes, which is the wire format.
|
||||||
|
|
||||||
|
type nativeModel struct {
|
||||||
|
next Model
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m nativeModel) Index(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) {
|
||||||
|
m.next.Index(deviceID, folder, files, flags, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m nativeModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) {
|
||||||
|
m.next.IndexUpdate(deviceID, folder, files, flags, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m nativeModel) Request(deviceID DeviceID, folder string, name string, offset int64, size int, hash []byte, flags uint32, options []Option) ([]byte, error) {
|
||||||
|
return m.next.Request(deviceID, folder, name, offset, size, hash, flags, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m nativeModel) ClusterConfig(deviceID DeviceID, config ClusterConfigMessage) {
|
||||||
|
m.next.ClusterConfig(deviceID, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m nativeModel) Close(deviceID DeviceID, err error) {
|
||||||
|
m.next.Close(deviceID, err)
|
||||||
|
}
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
// Copyright (C) 2014 The Protocol Authors.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
// Windows uses backslashes as file separator and disallows a bunch of
|
||||||
|
// characters in the filename
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var disallowedCharacters = string([]rune{
|
||||||
|
'<', '>', ':', '"', '|', '?', '*',
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
||||||
|
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
|
||||||
|
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
|
||||||
|
31,
|
||||||
|
})
|
||||||
|
|
||||||
|
type nativeModel struct {
|
||||||
|
next Model
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m nativeModel) Index(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) {
|
||||||
|
fixupFiles(files)
|
||||||
|
m.next.Index(deviceID, folder, files, flags, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m nativeModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) {
|
||||||
|
fixupFiles(files)
|
||||||
|
m.next.IndexUpdate(deviceID, folder, files, flags, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m nativeModel) Request(deviceID DeviceID, folder string, name string, offset int64, size int, hash []byte, flags uint32, options []Option) ([]byte, error) {
|
||||||
|
name = filepath.FromSlash(name)
|
||||||
|
return m.next.Request(deviceID, folder, name, offset, size, hash, flags, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m nativeModel) ClusterConfig(deviceID DeviceID, config ClusterConfigMessage) {
|
||||||
|
m.next.ClusterConfig(deviceID, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m nativeModel) Close(deviceID DeviceID, err error) {
|
||||||
|
m.next.Close(deviceID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fixupFiles(files []FileInfo) {
|
||||||
|
for i, f := range files {
|
||||||
|
if strings.ContainsAny(f.Name, disallowedCharacters) {
|
||||||
|
if f.IsDeleted() {
|
||||||
|
// Don't complain if the file is marked as deleted, since it
|
||||||
|
// can't possibly exist here anyway.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
files[i].Flags |= FlagInvalid
|
||||||
|
l.Warnf("File name %q contains invalid characters; marked as invalid.", f.Name)
|
||||||
|
}
|
||||||
|
files[i].Name = filepath.FromSlash(files[i].Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
Vendored
+177
-96
@@ -1,17 +1,4 @@
|
|||||||
// Copyright (C) 2014 The Syncthing Authors.
|
// Copyright (C) 2014 The Protocol Authors.
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it
|
|
||||||
// under the terms of the GNU General Public License as published by the Free
|
|
||||||
// Software Foundation, either version 3 of the License, or (at your option)
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
// more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along
|
|
||||||
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
@@ -48,13 +35,31 @@ const (
|
|||||||
stateIdxRcvd
|
stateIdxRcvd
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FileInfo flags
|
||||||
const (
|
const (
|
||||||
FlagDeleted uint32 = 1 << 12
|
FlagDeleted uint32 = 1 << 12
|
||||||
FlagInvalid = 1 << 13
|
FlagInvalid = 1 << 13
|
||||||
FlagDirectory = 1 << 14
|
FlagDirectory = 1 << 14
|
||||||
FlagNoPermBits = 1 << 15
|
FlagNoPermBits = 1 << 15
|
||||||
|
FlagSymlink = 1 << 16
|
||||||
|
FlagSymlinkMissingTarget = 1 << 17
|
||||||
|
|
||||||
|
FlagsAll = (1 << 18) - 1
|
||||||
|
|
||||||
|
SymlinkTypeMask = FlagDirectory | FlagSymlinkMissingTarget
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IndexMessage message flags (for IndexUpdate)
|
||||||
|
const (
|
||||||
|
FlagIndexTemporary uint32 = 1 << iota
|
||||||
|
)
|
||||||
|
|
||||||
|
// Request message flags
|
||||||
|
const (
|
||||||
|
FlagRequestTemporary uint32 = 1 << iota
|
||||||
|
)
|
||||||
|
|
||||||
|
// ClusterConfigMessage.Folders.Devices flags
|
||||||
const (
|
const (
|
||||||
FlagShareTrusted uint32 = 1 << 0
|
FlagShareTrusted uint32 = 1 << 0
|
||||||
FlagShareReadOnly = 1 << 1
|
FlagShareReadOnly = 1 << 1
|
||||||
@@ -67,13 +72,17 @@ var (
|
|||||||
ErrClosed = errors.New("connection closed")
|
ErrClosed = errors.New("connection closed")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Specific variants of empty messages...
|
||||||
|
type pingMessage struct{ EmptyMessage }
|
||||||
|
type pongMessage struct{ EmptyMessage }
|
||||||
|
|
||||||
type Model interface {
|
type Model interface {
|
||||||
// An index was received from the peer device
|
// An index was received from the peer device
|
||||||
Index(deviceID DeviceID, folder string, files []FileInfo)
|
Index(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option)
|
||||||
// An index update was received from the peer device
|
// An index update was received from the peer device
|
||||||
IndexUpdate(deviceID DeviceID, folder string, files []FileInfo)
|
IndexUpdate(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option)
|
||||||
// A request was made by the peer device
|
// A request was made by the peer device
|
||||||
Request(deviceID DeviceID, folder string, name string, offset int64, size int) ([]byte, error)
|
Request(deviceID DeviceID, folder string, name string, offset int64, size int, hash []byte, flags uint32, options []Option) ([]byte, error)
|
||||||
// A cluster configuration message was received
|
// A cluster configuration message was received
|
||||||
ClusterConfig(deviceID DeviceID, config ClusterConfigMessage)
|
ClusterConfig(deviceID DeviceID, config ClusterConfigMessage)
|
||||||
// The peer device closed the connection
|
// The peer device closed the connection
|
||||||
@@ -83,9 +92,9 @@ type Model interface {
|
|||||||
type Connection interface {
|
type Connection interface {
|
||||||
ID() DeviceID
|
ID() DeviceID
|
||||||
Name() string
|
Name() string
|
||||||
Index(folder string, files []FileInfo) error
|
Index(folder string, files []FileInfo, flags uint32, options []Option) error
|
||||||
IndexUpdate(folder string, files []FileInfo) error
|
IndexUpdate(folder string, files []FileInfo, flags uint32, options []Option) error
|
||||||
Request(folder string, name string, offset int64, size int) ([]byte, error)
|
Request(folder string, name string, offset int64, size int, hash []byte, flags uint32, options []Option) ([]byte, error)
|
||||||
ClusterConfig(config ClusterConfigMessage)
|
ClusterConfig(config ClusterConfigMessage)
|
||||||
Statistics() Statistics
|
Statistics() Statistics
|
||||||
}
|
}
|
||||||
@@ -109,7 +118,7 @@ type rawConnection struct {
|
|||||||
closed chan struct{}
|
closed chan struct{}
|
||||||
once sync.Once
|
once sync.Once
|
||||||
|
|
||||||
compressionThreshold int // compress messages larger than this many bytes
|
compression Compression
|
||||||
|
|
||||||
rdbuf0 []byte // used & reused by readMessage
|
rdbuf0 []byte // used & reused by readMessage
|
||||||
rdbuf1 []byte // used & reused by readMessage
|
rdbuf1 []byte // used & reused by readMessage
|
||||||
@@ -129,30 +138,30 @@ type encodable interface {
|
|||||||
AppendXDR([]byte) ([]byte, error)
|
AppendXDR([]byte) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type isEofer interface {
|
||||||
|
IsEOF() bool
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
pingTimeout = 30 * time.Second
|
pingTimeout = 30 * time.Second
|
||||||
pingIdleTime = 60 * time.Second
|
pingIdleTime = 60 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, receiver Model, name string, compress bool) Connection {
|
func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, receiver Model, name string, compress Compression) Connection {
|
||||||
cr := &countingReader{Reader: reader}
|
cr := &countingReader{Reader: reader}
|
||||||
cw := &countingWriter{Writer: writer}
|
cw := &countingWriter{Writer: writer}
|
||||||
|
|
||||||
compThres := 1<<31 - 1 // compression disabled
|
|
||||||
if compress {
|
|
||||||
compThres = 128 // compress messages that are 128 bytes long or larger
|
|
||||||
}
|
|
||||||
c := rawConnection{
|
c := rawConnection{
|
||||||
id: deviceID,
|
id: deviceID,
|
||||||
name: name,
|
name: name,
|
||||||
receiver: nativeModel{receiver},
|
receiver: nativeModel{receiver},
|
||||||
state: stateInitial,
|
state: stateInitial,
|
||||||
cr: cr,
|
cr: cr,
|
||||||
cw: cw,
|
cw: cw,
|
||||||
outbox: make(chan hdrMsg),
|
outbox: make(chan hdrMsg),
|
||||||
nextID: make(chan int),
|
nextID: make(chan int),
|
||||||
closed: make(chan struct{}),
|
closed: make(chan struct{}),
|
||||||
compressionThreshold: compThres,
|
compression: compress,
|
||||||
}
|
}
|
||||||
|
|
||||||
go c.readerLoop()
|
go c.readerLoop()
|
||||||
@@ -172,33 +181,43 @@ func (c *rawConnection) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Index writes the list of file information to the connected peer device
|
// Index writes the list of file information to the connected peer device
|
||||||
func (c *rawConnection) Index(folder string, idx []FileInfo) error {
|
func (c *rawConnection) Index(folder string, idx []FileInfo, flags uint32, options []Option) error {
|
||||||
select {
|
select {
|
||||||
case <-c.closed:
|
case <-c.closed:
|
||||||
return ErrClosed
|
return ErrClosed
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
c.idxMut.Lock()
|
c.idxMut.Lock()
|
||||||
c.send(-1, messageTypeIndex, IndexMessage{folder, idx})
|
c.send(-1, messageTypeIndex, IndexMessage{
|
||||||
|
Folder: folder,
|
||||||
|
Files: idx,
|
||||||
|
Flags: flags,
|
||||||
|
Options: options,
|
||||||
|
})
|
||||||
c.idxMut.Unlock()
|
c.idxMut.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IndexUpdate writes the list of file information to the connected peer device as an update
|
// IndexUpdate writes the list of file information to the connected peer device as an update
|
||||||
func (c *rawConnection) IndexUpdate(folder string, idx []FileInfo) error {
|
func (c *rawConnection) IndexUpdate(folder string, idx []FileInfo, flags uint32, options []Option) error {
|
||||||
select {
|
select {
|
||||||
case <-c.closed:
|
case <-c.closed:
|
||||||
return ErrClosed
|
return ErrClosed
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
c.idxMut.Lock()
|
c.idxMut.Lock()
|
||||||
c.send(-1, messageTypeIndexUpdate, IndexMessage{folder, idx})
|
c.send(-1, messageTypeIndexUpdate, IndexMessage{
|
||||||
|
Folder: folder,
|
||||||
|
Files: idx,
|
||||||
|
Flags: flags,
|
||||||
|
Options: options,
|
||||||
|
})
|
||||||
c.idxMut.Unlock()
|
c.idxMut.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request returns the bytes for the specified block after fetching them from the connected peer.
|
// Request returns the bytes for the specified block after fetching them from the connected peer.
|
||||||
func (c *rawConnection) Request(folder string, name string, offset int64, size int) ([]byte, error) {
|
func (c *rawConnection) Request(folder string, name string, offset int64, size int, hash []byte, flags uint32, options []Option) ([]byte, error) {
|
||||||
var id int
|
var id int
|
||||||
select {
|
select {
|
||||||
case id = <-c.nextID:
|
case id = <-c.nextID:
|
||||||
@@ -214,7 +233,15 @@ func (c *rawConnection) Request(folder string, name string, offset int64, size i
|
|||||||
c.awaiting[id] = rc
|
c.awaiting[id] = rc
|
||||||
c.awaitingMut.Unlock()
|
c.awaitingMut.Unlock()
|
||||||
|
|
||||||
ok := c.send(id, messageTypeRequest, RequestMessage{folder, name, uint64(offset), uint32(size)})
|
ok := c.send(id, messageTypeRequest, RequestMessage{
|
||||||
|
Folder: folder,
|
||||||
|
Name: name,
|
||||||
|
Offset: offset,
|
||||||
|
Size: int32(size),
|
||||||
|
Hash: hash,
|
||||||
|
Flags: flags,
|
||||||
|
Options: options,
|
||||||
|
})
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, ErrClosed
|
return nil, ErrClosed
|
||||||
}
|
}
|
||||||
@@ -270,48 +297,51 @@ func (c *rawConnection) readerLoop() (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch hdr.msgType {
|
switch msg := msg.(type) {
|
||||||
case messageTypeIndex:
|
case IndexMessage:
|
||||||
if c.state < stateCCRcvd {
|
switch hdr.msgType {
|
||||||
return fmt.Errorf("protocol error: index message in state %d", c.state)
|
case messageTypeIndex:
|
||||||
}
|
if c.state < stateCCRcvd {
|
||||||
c.handleIndex(msg.(IndexMessage))
|
return fmt.Errorf("protocol error: index message in state %d", c.state)
|
||||||
c.state = stateIdxRcvd
|
}
|
||||||
|
c.handleIndex(msg)
|
||||||
|
c.state = stateIdxRcvd
|
||||||
|
|
||||||
case messageTypeIndexUpdate:
|
case messageTypeIndexUpdate:
|
||||||
if c.state < stateIdxRcvd {
|
if c.state < stateIdxRcvd {
|
||||||
return fmt.Errorf("protocol error: index update message in state %d", c.state)
|
return fmt.Errorf("protocol error: index update message in state %d", c.state)
|
||||||
|
}
|
||||||
|
c.handleIndexUpdate(msg)
|
||||||
}
|
}
|
||||||
c.handleIndexUpdate(msg.(IndexMessage))
|
|
||||||
|
|
||||||
case messageTypeRequest:
|
case RequestMessage:
|
||||||
if c.state < stateIdxRcvd {
|
if c.state < stateIdxRcvd {
|
||||||
return fmt.Errorf("protocol error: request message in state %d", c.state)
|
return fmt.Errorf("protocol error: request message in state %d", c.state)
|
||||||
}
|
}
|
||||||
// Requests are handled asynchronously
|
// Requests are handled asynchronously
|
||||||
go c.handleRequest(hdr.msgID, msg.(RequestMessage))
|
go c.handleRequest(hdr.msgID, msg)
|
||||||
|
|
||||||
case messageTypeResponse:
|
case ResponseMessage:
|
||||||
if c.state < stateIdxRcvd {
|
if c.state < stateIdxRcvd {
|
||||||
return fmt.Errorf("protocol error: response message in state %d", c.state)
|
return fmt.Errorf("protocol error: response message in state %d", c.state)
|
||||||
}
|
}
|
||||||
c.handleResponse(hdr.msgID, msg.(ResponseMessage))
|
c.handleResponse(hdr.msgID, msg)
|
||||||
|
|
||||||
case messageTypePing:
|
case pingMessage:
|
||||||
c.send(hdr.msgID, messageTypePong, EmptyMessage{})
|
c.send(hdr.msgID, messageTypePong, pongMessage{})
|
||||||
|
|
||||||
case messageTypePong:
|
case pongMessage:
|
||||||
c.handlePong(hdr.msgID)
|
c.handlePong(hdr.msgID)
|
||||||
|
|
||||||
case messageTypeClusterConfig:
|
case ClusterConfigMessage:
|
||||||
if c.state != stateInitial {
|
if c.state != stateInitial {
|
||||||
return fmt.Errorf("protocol error: cluster config message in state %d", c.state)
|
return fmt.Errorf("protocol error: cluster config message in state %d", c.state)
|
||||||
}
|
}
|
||||||
go c.receiver.ClusterConfig(c.id, msg.(ClusterConfigMessage))
|
go c.receiver.ClusterConfig(c.id, msg)
|
||||||
c.state = stateCCRcvd
|
c.state = stateCCRcvd
|
||||||
|
|
||||||
case messageTypeClose:
|
case CloseMessage:
|
||||||
return errors.New(msg.(CloseMessage).Reason)
|
return errors.New(msg.Reason)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("protocol error: %s: unknown message type %#x", c.id, hdr.msgType)
|
return fmt.Errorf("protocol error: %s: unknown message type %#x", c.id, hdr.msgType)
|
||||||
@@ -337,6 +367,11 @@ func (c *rawConnection) readMessage() (hdr header, msg encodable, err error) {
|
|||||||
l.Debugf("read header %v (msglen=%d)", hdr, msglen)
|
l.Debugf("read header %v (msglen=%d)", hdr, msglen)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if hdr.version != 0 {
|
||||||
|
err = fmt.Errorf("unknown protocol version 0x%x", hdr.version)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if cap(c.rdbuf0) < msglen {
|
if cap(c.rdbuf0) < msglen {
|
||||||
c.rdbuf0 = make([]byte, msglen)
|
c.rdbuf0 = make([]byte, msglen)
|
||||||
} else {
|
} else {
|
||||||
@@ -372,33 +407,58 @@ func (c *rawConnection) readMessage() (hdr header, msg encodable, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We check each returned error for the XDRError.IsEOF() method.
|
||||||
|
// IsEOF()==true here means that the message contained fewer fields than
|
||||||
|
// expected. It does not signify an EOF on the socket, because we've
|
||||||
|
// successfully read a size value and that many bytes already. New fields
|
||||||
|
// we expected but the other peer didn't send should be interpreted as
|
||||||
|
// zero/nil, and if that's not valid we'll verify it somewhere else.
|
||||||
|
|
||||||
switch hdr.msgType {
|
switch hdr.msgType {
|
||||||
case messageTypeIndex, messageTypeIndexUpdate:
|
case messageTypeIndex, messageTypeIndexUpdate:
|
||||||
var idx IndexMessage
|
var idx IndexMessage
|
||||||
err = idx.UnmarshalXDR(msgBuf)
|
err = idx.UnmarshalXDR(msgBuf)
|
||||||
|
if xdrErr, ok := err.(isEofer); ok && xdrErr.IsEOF() {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
msg = idx
|
msg = idx
|
||||||
|
|
||||||
case messageTypeRequest:
|
case messageTypeRequest:
|
||||||
var req RequestMessage
|
var req RequestMessage
|
||||||
err = req.UnmarshalXDR(msgBuf)
|
err = req.UnmarshalXDR(msgBuf)
|
||||||
|
if xdrErr, ok := err.(isEofer); ok && xdrErr.IsEOF() {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
msg = req
|
msg = req
|
||||||
|
|
||||||
case messageTypeResponse:
|
case messageTypeResponse:
|
||||||
var resp ResponseMessage
|
var resp ResponseMessage
|
||||||
err = resp.UnmarshalXDR(msgBuf)
|
err = resp.UnmarshalXDR(msgBuf)
|
||||||
|
if xdrErr, ok := err.(isEofer); ok && xdrErr.IsEOF() {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
msg = resp
|
msg = resp
|
||||||
|
|
||||||
case messageTypePing, messageTypePong:
|
case messageTypePing:
|
||||||
msg = EmptyMessage{}
|
msg = pingMessage{}
|
||||||
|
|
||||||
|
case messageTypePong:
|
||||||
|
msg = pongMessage{}
|
||||||
|
|
||||||
case messageTypeClusterConfig:
|
case messageTypeClusterConfig:
|
||||||
var cc ClusterConfigMessage
|
var cc ClusterConfigMessage
|
||||||
err = cc.UnmarshalXDR(msgBuf)
|
err = cc.UnmarshalXDR(msgBuf)
|
||||||
|
if xdrErr, ok := err.(isEofer); ok && xdrErr.IsEOF() {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
msg = cc
|
msg = cc
|
||||||
|
|
||||||
case messageTypeClose:
|
case messageTypeClose:
|
||||||
var cm CloseMessage
|
var cm CloseMessage
|
||||||
err = cm.UnmarshalXDR(msgBuf)
|
err = cm.UnmarshalXDR(msgBuf)
|
||||||
|
if xdrErr, ok := err.(isEofer); ok && xdrErr.IsEOF() {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
msg = cm
|
msg = cm
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -410,29 +470,58 @@ func (c *rawConnection) readMessage() (hdr header, msg encodable, err error) {
|
|||||||
|
|
||||||
func (c *rawConnection) handleIndex(im IndexMessage) {
|
func (c *rawConnection) handleIndex(im IndexMessage) {
|
||||||
if debug {
|
if debug {
|
||||||
l.Debugf("Index(%v, %v, %d files)", c.id, im.Folder, len(im.Files))
|
l.Debugf("Index(%v, %v, %d file, flags %x, opts: %s)", c.id, im.Folder, len(im.Files), im.Flags, im.Options)
|
||||||
}
|
}
|
||||||
c.receiver.Index(c.id, im.Folder, im.Files)
|
c.receiver.Index(c.id, im.Folder, filterIndexMessageFiles(im.Files), im.Flags, im.Options)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *rawConnection) handleIndexUpdate(im IndexMessage) {
|
func (c *rawConnection) handleIndexUpdate(im IndexMessage) {
|
||||||
if debug {
|
if debug {
|
||||||
l.Debugf("queueing IndexUpdate(%v, %v, %d files)", c.id, im.Folder, len(im.Files))
|
l.Debugf("queueing IndexUpdate(%v, %v, %d files, flags %x, opts: %s)", c.id, im.Folder, len(im.Files), im.Flags, im.Options)
|
||||||
}
|
}
|
||||||
c.receiver.IndexUpdate(c.id, im.Folder, im.Files)
|
c.receiver.IndexUpdate(c.id, im.Folder, filterIndexMessageFiles(im.Files), im.Flags, im.Options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterIndexMessageFiles(fs []FileInfo) []FileInfo {
|
||||||
|
var out []FileInfo
|
||||||
|
for i, f := range fs {
|
||||||
|
switch f.Name {
|
||||||
|
case "", ".", "..", "/": // A few obviously invalid filenames
|
||||||
|
l.Infof("Dropping invalid filename %q from incoming index", f.Name)
|
||||||
|
if out == nil {
|
||||||
|
// Most incoming updates won't contain anything invalid, so we
|
||||||
|
// delay the allocation and copy to output slice until we
|
||||||
|
// really need to do it, then copy all the so var valid files
|
||||||
|
// to it.
|
||||||
|
out = make([]FileInfo, i, len(fs)-1)
|
||||||
|
copy(out, fs)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if out != nil {
|
||||||
|
out = append(out, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if out != nil {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
return fs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *rawConnection) handleRequest(msgID int, req RequestMessage) {
|
func (c *rawConnection) handleRequest(msgID int, req RequestMessage) {
|
||||||
data, _ := c.receiver.Request(c.id, req.Folder, req.Name, int64(req.Offset), int(req.Size))
|
data, err := c.receiver.Request(c.id, req.Folder, req.Name, int64(req.Offset), int(req.Size), req.Hash, req.Flags, req.Options)
|
||||||
|
|
||||||
c.send(msgID, messageTypeResponse, ResponseMessage{data})
|
c.send(msgID, messageTypeResponse, ResponseMessage{
|
||||||
|
Data: data,
|
||||||
|
Code: errorToCode(err),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *rawConnection) handleResponse(msgID int, resp ResponseMessage) {
|
func (c *rawConnection) handleResponse(msgID int, resp ResponseMessage) {
|
||||||
c.awaitingMut.Lock()
|
c.awaitingMut.Lock()
|
||||||
if rc := c.awaiting[msgID]; rc != nil {
|
if rc := c.awaiting[msgID]; rc != nil {
|
||||||
c.awaiting[msgID] = nil
|
c.awaiting[msgID] = nil
|
||||||
rc <- asyncResult{resp.Data, nil}
|
rc <- asyncResult{resp.Data, codeToError(resp.Code)}
|
||||||
close(rc)
|
close(rc)
|
||||||
}
|
}
|
||||||
c.awaitingMut.Unlock()
|
c.awaitingMut.Unlock()
|
||||||
@@ -489,7 +578,15 @@ func (c *rawConnection) writerLoop() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(uncBuf) >= c.compressionThreshold {
|
compress := false
|
||||||
|
switch c.compression {
|
||||||
|
case CompressAlways:
|
||||||
|
compress = true
|
||||||
|
case CompressMetadata:
|
||||||
|
compress = hm.hdr.msgType != messageTypeResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
if compress && len(uncBuf) >= compressionThreshold {
|
||||||
// Use compression for large messages
|
// Use compression for large messages
|
||||||
hm.hdr.compression = true
|
hm.hdr.compression = true
|
||||||
|
|
||||||
@@ -626,8 +723,8 @@ func (c *rawConnection) pingerLoop() {
|
|||||||
|
|
||||||
type Statistics struct {
|
type Statistics struct {
|
||||||
At time.Time
|
At time.Time
|
||||||
InBytesTotal uint64
|
InBytesTotal int64
|
||||||
OutBytesTotal uint64
|
OutBytesTotal int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *rawConnection) Statistics() Statistics {
|
func (c *rawConnection) Statistics() Statistics {
|
||||||
@@ -637,19 +734,3 @@ func (c *rawConnection) Statistics() Statistics {
|
|||||||
OutBytesTotal: c.cw.Tot(),
|
OutBytesTotal: c.cw.Tot(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsDeleted(bits uint32) bool {
|
|
||||||
return bits&FlagDeleted != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsInvalid(bits uint32) bool {
|
|
||||||
return bits&FlagInvalid != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsDirectory(bits uint32) bool {
|
|
||||||
return bits&FlagDirectory != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func HasPermissionBits(bits uint32) bool {
|
|
||||||
return bits&FlagNoPermBits == 0
|
|
||||||
}
|
|
||||||
Generated
Vendored
+16
-29
@@ -1,17 +1,4 @@
|
|||||||
// Copyright (C) 2014 The Syncthing Authors.
|
// Copyright (C) 2014 The Protocol Authors.
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it
|
|
||||||
// under the terms of the GNU General Public License as published by the Free
|
|
||||||
// Software Foundation, either version 3 of the License, or (at your option)
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
// more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along
|
|
||||||
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
@@ -80,8 +67,8 @@ func TestPing(t *testing.T) {
|
|||||||
ar, aw := io.Pipe()
|
ar, aw := io.Pipe()
|
||||||
br, bw := io.Pipe()
|
br, bw := io.Pipe()
|
||||||
|
|
||||||
c0 := NewConnection(c0ID, ar, bw, nil, "name", true).(wireFormatConnection).next.(*rawConnection)
|
c0 := NewConnection(c0ID, ar, bw, nil, "name", CompressAlways).(wireFormatConnection).next.(*rawConnection)
|
||||||
c1 := NewConnection(c1ID, br, aw, nil, "name", true).(wireFormatConnection).next.(*rawConnection)
|
c1 := NewConnection(c1ID, br, aw, nil, "name", CompressAlways).(wireFormatConnection).next.(*rawConnection)
|
||||||
|
|
||||||
if ok := c0.ping(); !ok {
|
if ok := c0.ping(); !ok {
|
||||||
t.Error("c0 ping failed")
|
t.Error("c0 ping failed")
|
||||||
@@ -104,8 +91,8 @@ func TestPingErr(t *testing.T) {
|
|||||||
eaw := &ErrPipe{PipeWriter: *aw, max: i, err: e}
|
eaw := &ErrPipe{PipeWriter: *aw, max: i, err: e}
|
||||||
ebw := &ErrPipe{PipeWriter: *bw, max: j, err: e}
|
ebw := &ErrPipe{PipeWriter: *bw, max: j, err: e}
|
||||||
|
|
||||||
c0 := NewConnection(c0ID, ar, ebw, m0, "name", true).(wireFormatConnection).next.(*rawConnection)
|
c0 := NewConnection(c0ID, ar, ebw, m0, "name", CompressAlways).(wireFormatConnection).next.(*rawConnection)
|
||||||
NewConnection(c1ID, br, eaw, m1, "name", true)
|
NewConnection(c1ID, br, eaw, m1, "name", CompressAlways)
|
||||||
|
|
||||||
res := c0.ping()
|
res := c0.ping()
|
||||||
if (i < 8 || j < 8) && res {
|
if (i < 8 || j < 8) && res {
|
||||||
@@ -180,8 +167,8 @@ func TestVersionErr(t *testing.T) {
|
|||||||
ar, aw := io.Pipe()
|
ar, aw := io.Pipe()
|
||||||
br, bw := io.Pipe()
|
br, bw := io.Pipe()
|
||||||
|
|
||||||
c0 := NewConnection(c0ID, ar, bw, m0, "name", true).(wireFormatConnection).next.(*rawConnection)
|
c0 := NewConnection(c0ID, ar, bw, m0, "name", CompressAlways).(wireFormatConnection).next.(*rawConnection)
|
||||||
NewConnection(c1ID, br, aw, m1, "name", true)
|
NewConnection(c1ID, br, aw, m1, "name", CompressAlways)
|
||||||
|
|
||||||
w := xdr.NewWriter(c0.cw)
|
w := xdr.NewWriter(c0.cw)
|
||||||
w.WriteUint32(encodeHeader(header{
|
w.WriteUint32(encodeHeader(header{
|
||||||
@@ -189,7 +176,7 @@ func TestVersionErr(t *testing.T) {
|
|||||||
msgID: 0,
|
msgID: 0,
|
||||||
msgType: 0,
|
msgType: 0,
|
||||||
}))
|
}))
|
||||||
w.WriteUint32(0)
|
w.WriteUint32(0) // Avoids reader closing due to EOF
|
||||||
|
|
||||||
if !m1.isClosed() {
|
if !m1.isClosed() {
|
||||||
t.Error("Connection should close due to unknown version")
|
t.Error("Connection should close due to unknown version")
|
||||||
@@ -203,8 +190,8 @@ func TestTypeErr(t *testing.T) {
|
|||||||
ar, aw := io.Pipe()
|
ar, aw := io.Pipe()
|
||||||
br, bw := io.Pipe()
|
br, bw := io.Pipe()
|
||||||
|
|
||||||
c0 := NewConnection(c0ID, ar, bw, m0, "name", true).(wireFormatConnection).next.(*rawConnection)
|
c0 := NewConnection(c0ID, ar, bw, m0, "name", CompressAlways).(wireFormatConnection).next.(*rawConnection)
|
||||||
NewConnection(c1ID, br, aw, m1, "name", true)
|
NewConnection(c1ID, br, aw, m1, "name", CompressAlways)
|
||||||
|
|
||||||
w := xdr.NewWriter(c0.cw)
|
w := xdr.NewWriter(c0.cw)
|
||||||
w.WriteUint32(encodeHeader(header{
|
w.WriteUint32(encodeHeader(header{
|
||||||
@@ -212,7 +199,7 @@ func TestTypeErr(t *testing.T) {
|
|||||||
msgID: 0,
|
msgID: 0,
|
||||||
msgType: 42,
|
msgType: 42,
|
||||||
}))
|
}))
|
||||||
w.WriteUint32(0)
|
w.WriteUint32(0) // Avoids reader closing due to EOF
|
||||||
|
|
||||||
if !m1.isClosed() {
|
if !m1.isClosed() {
|
||||||
t.Error("Connection should close due to unknown message type")
|
t.Error("Connection should close due to unknown message type")
|
||||||
@@ -226,8 +213,8 @@ func TestClose(t *testing.T) {
|
|||||||
ar, aw := io.Pipe()
|
ar, aw := io.Pipe()
|
||||||
br, bw := io.Pipe()
|
br, bw := io.Pipe()
|
||||||
|
|
||||||
c0 := NewConnection(c0ID, ar, bw, m0, "name", true).(wireFormatConnection).next.(*rawConnection)
|
c0 := NewConnection(c0ID, ar, bw, m0, "name", CompressAlways).(wireFormatConnection).next.(*rawConnection)
|
||||||
NewConnection(c1ID, br, aw, m1, "name", true)
|
NewConnection(c1ID, br, aw, m1, "name", CompressAlways)
|
||||||
|
|
||||||
c0.close(nil)
|
c0.close(nil)
|
||||||
|
|
||||||
@@ -242,10 +229,10 @@ func TestClose(t *testing.T) {
|
|||||||
t.Error("Ping should not return true")
|
t.Error("Ping should not return true")
|
||||||
}
|
}
|
||||||
|
|
||||||
c0.Index("default", nil)
|
c0.Index("default", nil, 0, nil)
|
||||||
c0.Index("default", nil)
|
c0.Index("default", nil, 0, nil)
|
||||||
|
|
||||||
if _, err := c0.Request("default", "foo", 0, 0); err == nil {
|
if _, err := c0.Request("default", "foo", 0, 0, nil, 0, nil); err == nil {
|
||||||
t.Error("Request should return an error")
|
t.Error("Request should return an error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
// Copyright (C) 2015 The Protocol Authors.
|
||||||
|
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
// The Vector type represents a version vector. The zero value is a usable
|
||||||
|
// version vector. The vector has slice semantics and some operations on it
|
||||||
|
// are "append-like" in that they may return the same vector modified, or a
|
||||||
|
// new allocated Vector with the modified contents.
|
||||||
|
type Vector []Counter
|
||||||
|
|
||||||
|
// Counter represents a single counter in the version vector.
|
||||||
|
type Counter struct {
|
||||||
|
ID uint64
|
||||||
|
Value uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update returns a Vector with the index for the specific ID incremented by
|
||||||
|
// one. If it is possible, the vector v is updated and returned. If it is not,
|
||||||
|
// a copy will be created, updated and returned.
|
||||||
|
func (v Vector) Update(ID uint64) Vector {
|
||||||
|
for i := range v {
|
||||||
|
if v[i].ID == ID {
|
||||||
|
// Update an existing index
|
||||||
|
v[i].Value++
|
||||||
|
return v
|
||||||
|
} else if v[i].ID > ID {
|
||||||
|
// Insert a new index
|
||||||
|
nv := make(Vector, len(v)+1)
|
||||||
|
copy(nv, v[:i])
|
||||||
|
nv[i].ID = ID
|
||||||
|
nv[i].Value = 1
|
||||||
|
copy(nv[i+1:], v[i:])
|
||||||
|
return nv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Append a new new index
|
||||||
|
return append(v, Counter{ID, 1})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge returns the vector containing the maximum indexes from a and b. If it
|
||||||
|
// is possible, the vector a is updated and returned. If it is not, a copy
|
||||||
|
// will be created, updated and returned.
|
||||||
|
func (a Vector) Merge(b Vector) Vector {
|
||||||
|
var ai, bi int
|
||||||
|
for bi < len(b) {
|
||||||
|
if ai == len(a) {
|
||||||
|
// We've reach the end of a, all that remains are appends
|
||||||
|
return append(a, b[bi:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if a[ai].ID > b[bi].ID {
|
||||||
|
// The index from b should be inserted here
|
||||||
|
n := make(Vector, len(a)+1)
|
||||||
|
copy(n, a[:ai])
|
||||||
|
n[ai] = b[bi]
|
||||||
|
copy(n[ai+1:], a[ai:])
|
||||||
|
a = n
|
||||||
|
}
|
||||||
|
|
||||||
|
if a[ai].ID == b[bi].ID {
|
||||||
|
if v := b[bi].Value; v > a[ai].Value {
|
||||||
|
a[ai].Value = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if bi < len(b) && a[ai].ID == b[bi].ID {
|
||||||
|
bi++
|
||||||
|
}
|
||||||
|
ai++
|
||||||
|
}
|
||||||
|
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns an identical vector that is not shared with v.
|
||||||
|
func (v Vector) Copy() Vector {
|
||||||
|
nv := make(Vector, len(v))
|
||||||
|
copy(nv, v)
|
||||||
|
return nv
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns true when the two vectors are equivalent.
|
||||||
|
func (a Vector) Equal(b Vector) bool {
|
||||||
|
return a.Compare(b) == Equal
|
||||||
|
}
|
||||||
|
|
||||||
|
// LesserEqual returns true when the two vectors are equivalent or a is Lesser
|
||||||
|
// than b.
|
||||||
|
func (a Vector) LesserEqual(b Vector) bool {
|
||||||
|
comp := a.Compare(b)
|
||||||
|
return comp == Lesser || comp == Equal
|
||||||
|
}
|
||||||
|
|
||||||
|
// LesserEqual returns true when the two vectors are equivalent or a is Greater
|
||||||
|
// than b.
|
||||||
|
func (a Vector) GreaterEqual(b Vector) bool {
|
||||||
|
comp := a.Compare(b)
|
||||||
|
return comp == Greater || comp == Equal
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concurrent returns true when the two vectors are concrurrent.
|
||||||
|
func (a Vector) Concurrent(b Vector) bool {
|
||||||
|
comp := a.Compare(b)
|
||||||
|
return comp == ConcurrentGreater || comp == ConcurrentLesser
|
||||||
|
}
|
||||||
|
|
||||||
|
// Counter returns the current value of the given counter ID.
|
||||||
|
func (v Vector) Counter(id uint64) uint64 {
|
||||||
|
for _, c := range v {
|
||||||
|
if c.ID == id {
|
||||||
|
return c.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
// Copyright (C) 2015 The Protocol Authors.
|
||||||
|
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
// Ordering represents the relationship between two Vectors.
|
||||||
|
type Ordering int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Equal Ordering = iota
|
||||||
|
Greater
|
||||||
|
Lesser
|
||||||
|
ConcurrentLesser
|
||||||
|
ConcurrentGreater
|
||||||
|
)
|
||||||
|
|
||||||
|
// There's really no such thing as "concurrent lesser" and "concurrent
|
||||||
|
// greater" in version vectors, just "concurrent". But it's useful to be able
|
||||||
|
// to get a strict ordering between versions for stable sorts and so on, so we
|
||||||
|
// return both variants. The convenience method Concurrent() can be used to
|
||||||
|
// check for either case.
|
||||||
|
|
||||||
|
// Compare returns the Ordering that describes a's relation to b.
|
||||||
|
func (a Vector) Compare(b Vector) Ordering {
|
||||||
|
var ai, bi int // index into a and b
|
||||||
|
var av, bv Counter // value at current index
|
||||||
|
|
||||||
|
result := Equal
|
||||||
|
|
||||||
|
for ai < len(a) || bi < len(b) {
|
||||||
|
var aMissing, bMissing bool
|
||||||
|
|
||||||
|
if ai < len(a) {
|
||||||
|
av = a[ai]
|
||||||
|
} else {
|
||||||
|
av = Counter{}
|
||||||
|
aMissing = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if bi < len(b) {
|
||||||
|
bv = b[bi]
|
||||||
|
} else {
|
||||||
|
bv = Counter{}
|
||||||
|
bMissing = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case av.ID == bv.ID:
|
||||||
|
// We have a counter value for each side
|
||||||
|
if av.Value > bv.Value {
|
||||||
|
if result == Lesser {
|
||||||
|
return ConcurrentLesser
|
||||||
|
}
|
||||||
|
result = Greater
|
||||||
|
} else if av.Value < bv.Value {
|
||||||
|
if result == Greater {
|
||||||
|
return ConcurrentGreater
|
||||||
|
}
|
||||||
|
result = Lesser
|
||||||
|
}
|
||||||
|
|
||||||
|
case !aMissing && av.ID < bv.ID || bMissing:
|
||||||
|
// Value is missing on the b side
|
||||||
|
if av.Value > 0 {
|
||||||
|
if result == Lesser {
|
||||||
|
return ConcurrentLesser
|
||||||
|
}
|
||||||
|
result = Greater
|
||||||
|
}
|
||||||
|
|
||||||
|
case !bMissing && bv.ID < av.ID || aMissing:
|
||||||
|
// Value is missing on the a side
|
||||||
|
if bv.Value > 0 {
|
||||||
|
if result == Greater {
|
||||||
|
return ConcurrentGreater
|
||||||
|
}
|
||||||
|
result = Lesser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ai < len(a) && (av.ID <= bv.ID || bMissing) {
|
||||||
|
ai++
|
||||||
|
}
|
||||||
|
if bi < len(b) && (bv.ID <= av.ID || aMissing) {
|
||||||
|
bi++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
+249
@@ -0,0 +1,249 @@
|
|||||||
|
// Copyright (C) 2015 The Protocol Authors.
|
||||||
|
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCompare(t *testing.T) {
|
||||||
|
testcases := []struct {
|
||||||
|
a, b Vector
|
||||||
|
r Ordering
|
||||||
|
}{
|
||||||
|
// Empty vectors are identical
|
||||||
|
{Vector{}, Vector{}, Equal},
|
||||||
|
{Vector{}, nil, Equal},
|
||||||
|
{nil, Vector{}, Equal},
|
||||||
|
{nil, Vector{Counter{42, 0}}, Equal},
|
||||||
|
{Vector{}, Vector{Counter{42, 0}}, Equal},
|
||||||
|
{Vector{Counter{42, 0}}, nil, Equal},
|
||||||
|
{Vector{Counter{42, 0}}, Vector{}, Equal},
|
||||||
|
|
||||||
|
// Zero is the implied value for a missing Counter
|
||||||
|
{
|
||||||
|
Vector{Counter{42, 0}},
|
||||||
|
Vector{Counter{77, 0}},
|
||||||
|
Equal,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Equal vectors are equal
|
||||||
|
{
|
||||||
|
Vector{Counter{42, 33}},
|
||||||
|
Vector{Counter{42, 33}},
|
||||||
|
Equal,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{42, 33}, Counter{77, 24}},
|
||||||
|
Vector{Counter{42, 33}, Counter{77, 24}},
|
||||||
|
Equal,
|
||||||
|
},
|
||||||
|
|
||||||
|
// These a-vectors are all greater than the b-vector
|
||||||
|
{
|
||||||
|
Vector{Counter{42, 1}},
|
||||||
|
nil,
|
||||||
|
Greater,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{42, 1}},
|
||||||
|
Vector{},
|
||||||
|
Greater,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{0, 1}},
|
||||||
|
Vector{Counter{0, 0}},
|
||||||
|
Greater,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{42, 1}},
|
||||||
|
Vector{Counter{42, 0}},
|
||||||
|
Greater,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{math.MaxUint64, 1}},
|
||||||
|
Vector{Counter{math.MaxUint64, 0}},
|
||||||
|
Greater,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{0, math.MaxUint64}},
|
||||||
|
Vector{Counter{0, 0}},
|
||||||
|
Greater,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{42, math.MaxUint64}},
|
||||||
|
Vector{Counter{42, 0}},
|
||||||
|
Greater,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{math.MaxUint64, math.MaxUint64}},
|
||||||
|
Vector{Counter{math.MaxUint64, 0}},
|
||||||
|
Greater,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{0, math.MaxUint64}},
|
||||||
|
Vector{Counter{0, math.MaxUint64 - 1}},
|
||||||
|
Greater,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{42, math.MaxUint64}},
|
||||||
|
Vector{Counter{42, math.MaxUint64 - 1}},
|
||||||
|
Greater,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{math.MaxUint64, math.MaxUint64}},
|
||||||
|
Vector{Counter{math.MaxUint64, math.MaxUint64 - 1}},
|
||||||
|
Greater,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{42, 2}},
|
||||||
|
Vector{Counter{42, 1}},
|
||||||
|
Greater,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{22, 22}, Counter{42, 2}},
|
||||||
|
Vector{Counter{22, 22}, Counter{42, 1}},
|
||||||
|
Greater,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{42, 2}, Counter{77, 3}},
|
||||||
|
Vector{Counter{42, 1}, Counter{77, 3}},
|
||||||
|
Greater,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{22, 22}, Counter{42, 2}, Counter{77, 3}},
|
||||||
|
Vector{Counter{22, 22}, Counter{42, 1}, Counter{77, 3}},
|
||||||
|
Greater,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{22, 23}, Counter{42, 2}, Counter{77, 4}},
|
||||||
|
Vector{Counter{22, 22}, Counter{42, 1}, Counter{77, 3}},
|
||||||
|
Greater,
|
||||||
|
},
|
||||||
|
|
||||||
|
// These a-vectors are all lesser than the b-vector
|
||||||
|
{nil, Vector{Counter{42, 1}}, Lesser},
|
||||||
|
{Vector{}, Vector{Counter{42, 1}}, Lesser},
|
||||||
|
{
|
||||||
|
Vector{Counter{42, 0}},
|
||||||
|
Vector{Counter{42, 1}},
|
||||||
|
Lesser,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{42, 1}},
|
||||||
|
Vector{Counter{42, 2}},
|
||||||
|
Lesser,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{22, 22}, Counter{42, 1}},
|
||||||
|
Vector{Counter{22, 22}, Counter{42, 2}},
|
||||||
|
Lesser,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{42, 1}, Counter{77, 3}},
|
||||||
|
Vector{Counter{42, 2}, Counter{77, 3}},
|
||||||
|
Lesser,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{22, 22}, Counter{42, 1}, Counter{77, 3}},
|
||||||
|
Vector{Counter{22, 22}, Counter{42, 2}, Counter{77, 3}},
|
||||||
|
Lesser,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{22, 22}, Counter{42, 1}, Counter{77, 3}},
|
||||||
|
Vector{Counter{22, 23}, Counter{42, 2}, Counter{77, 4}},
|
||||||
|
Lesser,
|
||||||
|
},
|
||||||
|
|
||||||
|
// These are all in conflict
|
||||||
|
{
|
||||||
|
Vector{Counter{42, 2}},
|
||||||
|
Vector{Counter{43, 1}},
|
||||||
|
ConcurrentGreater,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{43, 1}},
|
||||||
|
Vector{Counter{42, 2}},
|
||||||
|
ConcurrentLesser,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{22, 23}, Counter{42, 1}},
|
||||||
|
Vector{Counter{22, 22}, Counter{42, 2}},
|
||||||
|
ConcurrentGreater,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{22, 21}, Counter{42, 2}},
|
||||||
|
Vector{Counter{22, 22}, Counter{42, 1}},
|
||||||
|
ConcurrentLesser,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{22, 21}, Counter{42, 2}, Counter{43, 1}},
|
||||||
|
Vector{Counter{20, 1}, Counter{22, 22}, Counter{42, 1}},
|
||||||
|
ConcurrentLesser,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range testcases {
|
||||||
|
// Test real Compare
|
||||||
|
if r := tc.a.Compare(tc.b); r != tc.r {
|
||||||
|
t.Errorf("%d: %+v.Compare(%+v) == %v (expected %v)", i, tc.a, tc.b, r, tc.r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test convenience functions
|
||||||
|
switch tc.r {
|
||||||
|
case Greater:
|
||||||
|
if tc.a.Equal(tc.b) {
|
||||||
|
t.Errorf("%+v == %+v", tc.a, tc.b)
|
||||||
|
}
|
||||||
|
if tc.a.Concurrent(tc.b) {
|
||||||
|
t.Errorf("%+v concurrent %+v", tc.a, tc.b)
|
||||||
|
}
|
||||||
|
if !tc.a.GreaterEqual(tc.b) {
|
||||||
|
t.Errorf("%+v not >= %+v", tc.a, tc.b)
|
||||||
|
}
|
||||||
|
if tc.a.LesserEqual(tc.b) {
|
||||||
|
t.Errorf("%+v <= %+v", tc.a, tc.b)
|
||||||
|
}
|
||||||
|
case Lesser:
|
||||||
|
if tc.a.Concurrent(tc.b) {
|
||||||
|
t.Errorf("%+v concurrent %+v", tc.a, tc.b)
|
||||||
|
}
|
||||||
|
if tc.a.Equal(tc.b) {
|
||||||
|
t.Errorf("%+v == %+v", tc.a, tc.b)
|
||||||
|
}
|
||||||
|
if tc.a.GreaterEqual(tc.b) {
|
||||||
|
t.Errorf("%+v >= %+v", tc.a, tc.b)
|
||||||
|
}
|
||||||
|
if !tc.a.LesserEqual(tc.b) {
|
||||||
|
t.Errorf("%+v not <= %+v", tc.a, tc.b)
|
||||||
|
}
|
||||||
|
case Equal:
|
||||||
|
if tc.a.Concurrent(tc.b) {
|
||||||
|
t.Errorf("%+v concurrent %+v", tc.a, tc.b)
|
||||||
|
}
|
||||||
|
if !tc.a.Equal(tc.b) {
|
||||||
|
t.Errorf("%+v not == %+v", tc.a, tc.b)
|
||||||
|
}
|
||||||
|
if !tc.a.GreaterEqual(tc.b) {
|
||||||
|
t.Errorf("%+v not <= %+v", tc.a, tc.b)
|
||||||
|
}
|
||||||
|
if !tc.a.LesserEqual(tc.b) {
|
||||||
|
t.Errorf("%+v not <= %+v", tc.a, tc.b)
|
||||||
|
}
|
||||||
|
case ConcurrentLesser, ConcurrentGreater:
|
||||||
|
if !tc.a.Concurrent(tc.b) {
|
||||||
|
t.Errorf("%+v not concurrent %+v", tc.a, tc.b)
|
||||||
|
}
|
||||||
|
if tc.a.Equal(tc.b) {
|
||||||
|
t.Errorf("%+v == %+v", tc.a, tc.b)
|
||||||
|
}
|
||||||
|
if tc.a.GreaterEqual(tc.b) {
|
||||||
|
t.Errorf("%+v >= %+v", tc.a, tc.b)
|
||||||
|
}
|
||||||
|
if tc.a.LesserEqual(tc.b) {
|
||||||
|
t.Errorf("%+v <= %+v", tc.a, tc.b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+134
@@ -0,0 +1,134 @@
|
|||||||
|
// Copyright (C) 2015 The Protocol Authors.
|
||||||
|
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestUpdate(t *testing.T) {
|
||||||
|
var v Vector
|
||||||
|
|
||||||
|
// Append
|
||||||
|
|
||||||
|
v = v.Update(42)
|
||||||
|
expected := Vector{Counter{42, 1}}
|
||||||
|
|
||||||
|
if v.Compare(expected) != Equal {
|
||||||
|
t.Errorf("Update error, %+v != %+v", v, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert at front
|
||||||
|
|
||||||
|
v = v.Update(36)
|
||||||
|
expected = Vector{Counter{36, 1}, Counter{42, 1}}
|
||||||
|
|
||||||
|
if v.Compare(expected) != Equal {
|
||||||
|
t.Errorf("Update error, %+v != %+v", v, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert in moddle
|
||||||
|
|
||||||
|
v = v.Update(37)
|
||||||
|
expected = Vector{Counter{36, 1}, Counter{37, 1}, Counter{42, 1}}
|
||||||
|
|
||||||
|
if v.Compare(expected) != Equal {
|
||||||
|
t.Errorf("Update error, %+v != %+v", v, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update existing
|
||||||
|
|
||||||
|
v = v.Update(37)
|
||||||
|
expected = Vector{Counter{36, 1}, Counter{37, 2}, Counter{42, 1}}
|
||||||
|
|
||||||
|
if v.Compare(expected) != Equal {
|
||||||
|
t.Errorf("Update error, %+v != %+v", v, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopy(t *testing.T) {
|
||||||
|
v0 := Vector{Counter{42, 1}}
|
||||||
|
v1 := v0.Copy()
|
||||||
|
v1.Update(42)
|
||||||
|
if v0.Compare(v1) != Lesser {
|
||||||
|
t.Errorf("Copy error, %+v should be ancestor of %+v", v0, v1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMerge(t *testing.T) {
|
||||||
|
testcases := []struct {
|
||||||
|
a, b, m Vector
|
||||||
|
}{
|
||||||
|
// No-ops
|
||||||
|
{
|
||||||
|
Vector{},
|
||||||
|
Vector{},
|
||||||
|
Vector{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{22, 1}, Counter{42, 1}},
|
||||||
|
Vector{Counter{22, 1}, Counter{42, 1}},
|
||||||
|
Vector{Counter{22, 1}, Counter{42, 1}},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Appends
|
||||||
|
{
|
||||||
|
Vector{},
|
||||||
|
Vector{Counter{22, 1}, Counter{42, 1}},
|
||||||
|
Vector{Counter{22, 1}, Counter{42, 1}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{22, 1}},
|
||||||
|
Vector{Counter{42, 1}},
|
||||||
|
Vector{Counter{22, 1}, Counter{42, 1}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{22, 1}},
|
||||||
|
Vector{Counter{22, 1}, Counter{42, 1}},
|
||||||
|
Vector{Counter{22, 1}, Counter{42, 1}},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Insert
|
||||||
|
{
|
||||||
|
Vector{Counter{22, 1}, Counter{42, 1}},
|
||||||
|
Vector{Counter{22, 1}, Counter{23, 2}, Counter{42, 1}},
|
||||||
|
Vector{Counter{22, 1}, Counter{23, 2}, Counter{42, 1}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector{Counter{42, 1}},
|
||||||
|
Vector{Counter{22, 1}},
|
||||||
|
Vector{Counter{22, 1}, Counter{42, 1}},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Update
|
||||||
|
{
|
||||||
|
Vector{Counter{22, 1}, Counter{42, 2}},
|
||||||
|
Vector{Counter{22, 2}, Counter{42, 1}},
|
||||||
|
Vector{Counter{22, 2}, Counter{42, 2}},
|
||||||
|
},
|
||||||
|
|
||||||
|
// All of the above
|
||||||
|
{
|
||||||
|
Vector{Counter{10, 1}, Counter{20, 2}, Counter{30, 1}},
|
||||||
|
Vector{Counter{5, 1}, Counter{10, 2}, Counter{15, 1}, Counter{20, 1}, Counter{25, 1}, Counter{35, 1}},
|
||||||
|
Vector{Counter{5, 1}, Counter{10, 2}, Counter{15, 1}, Counter{20, 2}, Counter{25, 1}, Counter{30, 1}, Counter{35, 1}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range testcases {
|
||||||
|
if m := tc.a.Merge(tc.b); m.Compare(tc.m) != Equal {
|
||||||
|
t.Errorf("%d: %+v.Merge(%+v) == %+v (expected %+v)", i, tc.a, tc.b, m, tc.m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCounterValue(t *testing.T) {
|
||||||
|
v0 := Vector{Counter{42, 1}, Counter{64, 5}}
|
||||||
|
if v0.Counter(42) != 1 {
|
||||||
|
t.Error("Counter error, %d != %d", v0.Counter(42), 1)
|
||||||
|
}
|
||||||
|
if v0.Counter(64) != 5 {
|
||||||
|
t.Error("Counter error, %d != %d", v0.Counter(64), 5)
|
||||||
|
}
|
||||||
|
if v0.Counter(72) != 0 {
|
||||||
|
t.Error("Counter error, %d != %d", v0.Counter(72), 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (C) 2015 The Protocol Authors.
|
||||||
|
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
// This stuff is hacked up manually because genxdr doesn't support 'type
|
||||||
|
// Vector []Counter' declarations and it was tricky when I tried to add it...
|
||||||
|
|
||||||
|
type xdrWriter interface {
|
||||||
|
WriteUint32(uint32) (int, error)
|
||||||
|
WriteUint64(uint64) (int, error)
|
||||||
|
}
|
||||||
|
type xdrReader interface {
|
||||||
|
ReadUint32() uint32
|
||||||
|
ReadUint64() uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeXDRInto encodes the vector as an XDR object into the given XDR
|
||||||
|
// encoder.
|
||||||
|
func (v Vector) EncodeXDRInto(w xdrWriter) (int, error) {
|
||||||
|
w.WriteUint32(uint32(len(v)))
|
||||||
|
for i := range v {
|
||||||
|
w.WriteUint64(v[i].ID)
|
||||||
|
w.WriteUint64(v[i].Value)
|
||||||
|
}
|
||||||
|
return 4 + 16*len(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeXDRFrom decodes the XDR objects from the given reader into itself.
|
||||||
|
func (v *Vector) DecodeXDRFrom(r xdrReader) error {
|
||||||
|
l := int(r.ReadUint32())
|
||||||
|
n := make(Vector, l)
|
||||||
|
for i := range n {
|
||||||
|
n[i].ID = r.ReadUint64()
|
||||||
|
n[i].Value = r.ReadUint64()
|
||||||
|
}
|
||||||
|
*v = n
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Generated
Vendored
+8
-21
@@ -1,24 +1,11 @@
|
|||||||
// Copyright (C) 2014 The Syncthing Authors.
|
// Copyright (C) 2014 The Protocol Authors.
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it
|
|
||||||
// under the terms of the GNU General Public License as published by the Free
|
|
||||||
// Software Foundation, either version 3 of the License, or (at your option)
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
// more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along
|
|
||||||
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"code.google.com/p/go.text/unicode/norm"
|
"golang.org/x/text/unicode/norm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type wireFormatConnection struct {
|
type wireFormatConnection struct {
|
||||||
@@ -33,7 +20,7 @@ func (c wireFormatConnection) Name() string {
|
|||||||
return c.next.Name()
|
return c.next.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c wireFormatConnection) Index(folder string, fs []FileInfo) error {
|
func (c wireFormatConnection) Index(folder string, fs []FileInfo, flags uint32, options []Option) error {
|
||||||
var myFs = make([]FileInfo, len(fs))
|
var myFs = make([]FileInfo, len(fs))
|
||||||
copy(myFs, fs)
|
copy(myFs, fs)
|
||||||
|
|
||||||
@@ -41,10 +28,10 @@ func (c wireFormatConnection) Index(folder string, fs []FileInfo) error {
|
|||||||
myFs[i].Name = norm.NFC.String(filepath.ToSlash(myFs[i].Name))
|
myFs[i].Name = norm.NFC.String(filepath.ToSlash(myFs[i].Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.next.Index(folder, myFs)
|
return c.next.Index(folder, myFs, flags, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c wireFormatConnection) IndexUpdate(folder string, fs []FileInfo) error {
|
func (c wireFormatConnection) IndexUpdate(folder string, fs []FileInfo, flags uint32, options []Option) error {
|
||||||
var myFs = make([]FileInfo, len(fs))
|
var myFs = make([]FileInfo, len(fs))
|
||||||
copy(myFs, fs)
|
copy(myFs, fs)
|
||||||
|
|
||||||
@@ -52,12 +39,12 @@ func (c wireFormatConnection) IndexUpdate(folder string, fs []FileInfo) error {
|
|||||||
myFs[i].Name = norm.NFC.String(filepath.ToSlash(myFs[i].Name))
|
myFs[i].Name = norm.NFC.String(filepath.ToSlash(myFs[i].Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.next.IndexUpdate(folder, myFs)
|
return c.next.IndexUpdate(folder, myFs, flags, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c wireFormatConnection) Request(folder, name string, offset int64, size int) ([]byte, error) {
|
func (c wireFormatConnection) Request(folder, name string, offset int64, size int, hash []byte, flags uint32, options []Option) ([]byte, error) {
|
||||||
name = norm.NFC.String(filepath.ToSlash(name))
|
name = norm.NFC.String(filepath.ToSlash(name))
|
||||||
return c.next.Request(folder, name, offset, size)
|
return c.next.Request(folder, name, offset, size, hash, flags, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c wireFormatConnection) ClusterConfig(config ClusterConfigMessage) {
|
func (c wireFormatConnection) ClusterConfig(config ClusterConfigMessage) {
|
||||||
Generated
Vendored
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
// +build go1.3
|
// +build !go1.2
|
||||||
|
|
||||||
package leveldb
|
package leveldb
|
||||||
|
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !go1.2
|
||||||
|
|
||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkLRUCache(b *testing.B) {
|
||||||
|
c := NewCache(NewLRU(10000))
|
||||||
|
|
||||||
|
b.SetParallelism(10)
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
|
||||||
|
for pb.Next() {
|
||||||
|
key := uint64(r.Intn(1000000))
|
||||||
|
c.Get(0, key, func() (int, Value) {
|
||||||
|
return 1, key
|
||||||
|
}).Release()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
+635
-118
@@ -8,152 +8,669 @@
|
|||||||
package cache
|
package cache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/syndtr/goleveldb/leveldb/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetFunc is the function that will be called by Namespace.Get to create
|
// Cacher provides interface to implements a caching functionality.
|
||||||
// a cache object, if charge is less than one than the cache object will
|
// An implementation must be goroutine-safe.
|
||||||
// not be registered to cache tree, if value is nil then the cache object
|
type Cacher interface {
|
||||||
// will not be created.
|
// Capacity returns cache capacity.
|
||||||
type SetFunc func() (charge int, value interface{})
|
|
||||||
|
|
||||||
// DelFin is the function that will be called as the result of a delete operation.
|
|
||||||
// Exist == true is indication that the object is exist, and pending == true is
|
|
||||||
// indication of deletion already happen but haven't done yet (wait for all handles
|
|
||||||
// to be released). And exist == false means the object doesn't exist.
|
|
||||||
type DelFin func(exist, pending bool)
|
|
||||||
|
|
||||||
// PurgeFin is the function that will be called as the result of a purge operation.
|
|
||||||
type PurgeFin func(ns, key uint64)
|
|
||||||
|
|
||||||
// Cache is a cache tree. A cache instance must be goroutine-safe.
|
|
||||||
type Cache interface {
|
|
||||||
// SetCapacity sets cache tree capacity.
|
|
||||||
SetCapacity(capacity int)
|
|
||||||
|
|
||||||
// Capacity returns cache tree capacity.
|
|
||||||
Capacity() int
|
Capacity() int
|
||||||
|
|
||||||
// Used returns used cache tree capacity.
|
// SetCapacity sets cache capacity.
|
||||||
Used() int
|
SetCapacity(capacity int)
|
||||||
|
|
||||||
// Size returns entire alive cache objects size.
|
// Promote promotes the 'cache node'.
|
||||||
Size() int
|
Promote(n *Node)
|
||||||
|
|
||||||
// NumObjects returns number of alive objects.
|
// Ban evicts the 'cache node' and prevent subsequent 'promote'.
|
||||||
NumObjects() int
|
Ban(n *Node)
|
||||||
|
|
||||||
// GetNamespace gets cache namespace with the given id.
|
// Evict evicts the 'cache node'.
|
||||||
// GetNamespace is never return nil.
|
Evict(n *Node)
|
||||||
GetNamespace(id uint64) Namespace
|
|
||||||
|
|
||||||
// PurgeNamespace purges cache namespace with the given id from this cache tree.
|
// EvictNS evicts 'cache node' with the given namespace.
|
||||||
// Also read Namespace.Purge.
|
EvictNS(ns uint64)
|
||||||
PurgeNamespace(id uint64, fin PurgeFin)
|
|
||||||
|
|
||||||
// ZapNamespace detaches cache namespace with the given id from this cache tree.
|
// EvictAll evicts all 'cache node'.
|
||||||
// Also read Namespace.Zap.
|
EvictAll()
|
||||||
ZapNamespace(id uint64)
|
|
||||||
|
|
||||||
// Purge purges all cache namespace from this cache tree.
|
// Close closes the 'cache tree'
|
||||||
// This is behave the same as calling Namespace.Purge method on all cache namespace.
|
Close() error
|
||||||
Purge(fin PurgeFin)
|
|
||||||
|
|
||||||
// Zap detaches all cache namespace from this cache tree.
|
|
||||||
// This is behave the same as calling Namespace.Zap method on all cache namespace.
|
|
||||||
Zap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Namespace is a cache namespace. A namespace instance must be goroutine-safe.
|
// Value is a 'cacheable object'. It may implements util.Releaser, if
|
||||||
type Namespace interface {
|
// so the the Release method will be called once object is released.
|
||||||
// Get gets cache object with the given key.
|
type Value interface{}
|
||||||
// If cache object is not found and setf is not nil, Get will atomically creates
|
|
||||||
// the cache object by calling setf. Otherwise Get will returns nil.
|
|
||||||
//
|
|
||||||
// The returned cache handle should be released after use by calling Release
|
|
||||||
// method.
|
|
||||||
Get(key uint64, setf SetFunc) Handle
|
|
||||||
|
|
||||||
// Delete removes cache object with the given key from cache tree.
|
type CacheGetter struct {
|
||||||
// A deleted cache object will be released as soon as all of its handles have
|
Cache *Cache
|
||||||
// been released.
|
NS uint64
|
||||||
// Delete only happen once, subsequent delete will consider cache object doesn't
|
|
||||||
// exist, even if the cache object ins't released yet.
|
|
||||||
//
|
|
||||||
// If not nil, fin will be called if the cache object doesn't exist or when
|
|
||||||
// finally be released.
|
|
||||||
//
|
|
||||||
// Delete returns true if such cache object exist and never been deleted.
|
|
||||||
Delete(key uint64, fin DelFin) bool
|
|
||||||
|
|
||||||
// Purge removes all cache objects within this namespace from cache tree.
|
|
||||||
// This is the same as doing delete on all cache objects.
|
|
||||||
//
|
|
||||||
// If not nil, fin will be called on all cache objects when its finally be
|
|
||||||
// released.
|
|
||||||
Purge(fin PurgeFin)
|
|
||||||
|
|
||||||
// Zap detaches namespace from cache tree and release all its cache objects.
|
|
||||||
// A zapped namespace can never be filled again.
|
|
||||||
// Calling Get on zapped namespace will always return nil.
|
|
||||||
Zap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle is a cache handle.
|
func (g *CacheGetter) Get(key uint64, setFunc func() (size int, value Value)) *Handle {
|
||||||
type Handle interface {
|
return g.Cache.Get(g.NS, key, setFunc)
|
||||||
// Release releases this cache handle. This method can be safely called mutiple
|
|
||||||
// times.
|
|
||||||
Release()
|
|
||||||
|
|
||||||
// Value returns value of this cache handle.
|
|
||||||
// Value will returns nil after this cache handle have be released.
|
|
||||||
Value() interface{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The hash tables implementation is based on:
|
||||||
|
// "Dynamic-Sized Nonblocking Hash Tables", by Yujie Liu, Kunlong Zhang, and Michael Spear. ACM Symposium on Principles of Distributed Computing, Jul 2014.
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DelNotExist = iota
|
mInitialSize = 1 << 4
|
||||||
DelExist
|
mOverflowThreshold = 1 << 5
|
||||||
DelPendig
|
mOverflowGrowThreshold = 1 << 7
|
||||||
)
|
)
|
||||||
|
|
||||||
// Namespace state.
|
type mBucket struct {
|
||||||
type nsState int
|
mu sync.Mutex
|
||||||
|
node []*Node
|
||||||
const (
|
frozen bool
|
||||||
nsEffective nsState = iota
|
|
||||||
nsZapped
|
|
||||||
)
|
|
||||||
|
|
||||||
// Node state.
|
|
||||||
type nodeState int
|
|
||||||
|
|
||||||
const (
|
|
||||||
nodeZero nodeState = iota
|
|
||||||
nodeEffective
|
|
||||||
nodeEvicted
|
|
||||||
nodeDeleted
|
|
||||||
)
|
|
||||||
|
|
||||||
// Fake handle.
|
|
||||||
type fakeHandle struct {
|
|
||||||
value interface{}
|
|
||||||
fin func()
|
|
||||||
once uint32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *fakeHandle) Value() interface{} {
|
func (b *mBucket) freeze() []*Node {
|
||||||
if atomic.LoadUint32(&h.once) == 0 {
|
b.mu.Lock()
|
||||||
return h.value
|
defer b.mu.Unlock()
|
||||||
|
if !b.frozen {
|
||||||
|
b.frozen = true
|
||||||
|
}
|
||||||
|
return b.node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *mBucket) get(r *Cache, h *mNode, hash uint32, ns, key uint64, noset bool) (done, added bool, n *Node) {
|
||||||
|
b.mu.Lock()
|
||||||
|
|
||||||
|
if b.frozen {
|
||||||
|
b.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan the node.
|
||||||
|
for _, n := range b.node {
|
||||||
|
if n.hash == hash && n.ns == ns && n.key == key {
|
||||||
|
atomic.AddInt32(&n.ref, 1)
|
||||||
|
b.mu.Unlock()
|
||||||
|
return true, false, n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get only.
|
||||||
|
if noset {
|
||||||
|
b.mu.Unlock()
|
||||||
|
return true, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create node.
|
||||||
|
n = &Node{
|
||||||
|
r: r,
|
||||||
|
hash: hash,
|
||||||
|
ns: ns,
|
||||||
|
key: key,
|
||||||
|
ref: 1,
|
||||||
|
}
|
||||||
|
// Add node to bucket.
|
||||||
|
b.node = append(b.node, n)
|
||||||
|
bLen := len(b.node)
|
||||||
|
b.mu.Unlock()
|
||||||
|
|
||||||
|
// Update counter.
|
||||||
|
grow := atomic.AddInt32(&r.nodes, 1) >= h.growThreshold
|
||||||
|
if bLen > mOverflowThreshold {
|
||||||
|
grow = grow || atomic.AddInt32(&h.overflow, 1) >= mOverflowGrowThreshold
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grow.
|
||||||
|
if grow && atomic.CompareAndSwapInt32(&h.resizeInProgess, 0, 1) {
|
||||||
|
nhLen := len(h.buckets) << 1
|
||||||
|
nh := &mNode{
|
||||||
|
buckets: make([]unsafe.Pointer, nhLen),
|
||||||
|
mask: uint32(nhLen) - 1,
|
||||||
|
pred: unsafe.Pointer(h),
|
||||||
|
growThreshold: int32(nhLen * mOverflowThreshold),
|
||||||
|
shrinkThreshold: int32(nhLen >> 1),
|
||||||
|
}
|
||||||
|
ok := atomic.CompareAndSwapPointer(&r.mHead, unsafe.Pointer(h), unsafe.Pointer(nh))
|
||||||
|
if !ok {
|
||||||
|
panic("BUG: failed swapping head")
|
||||||
|
}
|
||||||
|
go nh.initBuckets()
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, true, n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *mBucket) delete(r *Cache, h *mNode, hash uint32, ns, key uint64) (done, deleted bool) {
|
||||||
|
b.mu.Lock()
|
||||||
|
|
||||||
|
if b.frozen {
|
||||||
|
b.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan the node.
|
||||||
|
var (
|
||||||
|
n *Node
|
||||||
|
bLen int
|
||||||
|
)
|
||||||
|
for i := range b.node {
|
||||||
|
n = b.node[i]
|
||||||
|
if n.ns == ns && n.key == key {
|
||||||
|
if atomic.LoadInt32(&n.ref) == 0 {
|
||||||
|
deleted = true
|
||||||
|
|
||||||
|
// Call releaser.
|
||||||
|
if n.value != nil {
|
||||||
|
if r, ok := n.value.(util.Releaser); ok {
|
||||||
|
r.Release()
|
||||||
|
}
|
||||||
|
n.value = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove node from bucket.
|
||||||
|
b.node = append(b.node[:i], b.node[i+1:]...)
|
||||||
|
bLen = len(b.node)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.mu.Unlock()
|
||||||
|
|
||||||
|
if deleted {
|
||||||
|
// Call OnDel.
|
||||||
|
for _, f := range n.onDel {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update counter.
|
||||||
|
atomic.AddInt32(&r.size, int32(n.size)*-1)
|
||||||
|
shrink := atomic.AddInt32(&r.nodes, -1) < h.shrinkThreshold
|
||||||
|
if bLen >= mOverflowThreshold {
|
||||||
|
atomic.AddInt32(&h.overflow, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shrink.
|
||||||
|
if shrink && len(h.buckets) > mInitialSize && atomic.CompareAndSwapInt32(&h.resizeInProgess, 0, 1) {
|
||||||
|
nhLen := len(h.buckets) >> 1
|
||||||
|
nh := &mNode{
|
||||||
|
buckets: make([]unsafe.Pointer, nhLen),
|
||||||
|
mask: uint32(nhLen) - 1,
|
||||||
|
pred: unsafe.Pointer(h),
|
||||||
|
growThreshold: int32(nhLen * mOverflowThreshold),
|
||||||
|
shrinkThreshold: int32(nhLen >> 1),
|
||||||
|
}
|
||||||
|
ok := atomic.CompareAndSwapPointer(&r.mHead, unsafe.Pointer(h), unsafe.Pointer(nh))
|
||||||
|
if !ok {
|
||||||
|
panic("BUG: failed swapping head")
|
||||||
|
}
|
||||||
|
go nh.initBuckets()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, deleted
|
||||||
|
}
|
||||||
|
|
||||||
|
type mNode struct {
|
||||||
|
buckets []unsafe.Pointer // []*mBucket
|
||||||
|
mask uint32
|
||||||
|
pred unsafe.Pointer // *mNode
|
||||||
|
resizeInProgess int32
|
||||||
|
|
||||||
|
overflow int32
|
||||||
|
growThreshold int32
|
||||||
|
shrinkThreshold int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *mNode) initBucket(i uint32) *mBucket {
|
||||||
|
if b := (*mBucket)(atomic.LoadPointer(&n.buckets[i])); b != nil {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
p := (*mNode)(atomic.LoadPointer(&n.pred))
|
||||||
|
if p != nil {
|
||||||
|
var node []*Node
|
||||||
|
if n.mask > p.mask {
|
||||||
|
// Grow.
|
||||||
|
pb := (*mBucket)(atomic.LoadPointer(&p.buckets[i&p.mask]))
|
||||||
|
if pb == nil {
|
||||||
|
pb = p.initBucket(i & p.mask)
|
||||||
|
}
|
||||||
|
m := pb.freeze()
|
||||||
|
// Split nodes.
|
||||||
|
for _, x := range m {
|
||||||
|
if x.hash&n.mask == i {
|
||||||
|
node = append(node, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Shrink.
|
||||||
|
pb0 := (*mBucket)(atomic.LoadPointer(&p.buckets[i]))
|
||||||
|
if pb0 == nil {
|
||||||
|
pb0 = p.initBucket(i)
|
||||||
|
}
|
||||||
|
pb1 := (*mBucket)(atomic.LoadPointer(&p.buckets[i+uint32(len(n.buckets))]))
|
||||||
|
if pb1 == nil {
|
||||||
|
pb1 = p.initBucket(i + uint32(len(n.buckets)))
|
||||||
|
}
|
||||||
|
m0 := pb0.freeze()
|
||||||
|
m1 := pb1.freeze()
|
||||||
|
// Merge nodes.
|
||||||
|
node = make([]*Node, 0, len(m0)+len(m1))
|
||||||
|
node = append(node, m0...)
|
||||||
|
node = append(node, m1...)
|
||||||
|
}
|
||||||
|
b := &mBucket{node: node}
|
||||||
|
if atomic.CompareAndSwapPointer(&n.buckets[i], nil, unsafe.Pointer(b)) {
|
||||||
|
if len(node) > mOverflowThreshold {
|
||||||
|
atomic.AddInt32(&n.overflow, int32(len(node)-mOverflowThreshold))
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*mBucket)(atomic.LoadPointer(&n.buckets[i]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *mNode) initBuckets() {
|
||||||
|
for i := range n.buckets {
|
||||||
|
n.initBucket(uint32(i))
|
||||||
|
}
|
||||||
|
atomic.StorePointer(&n.pred, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache is a 'cache map'.
|
||||||
|
type Cache struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
mHead unsafe.Pointer // *mNode
|
||||||
|
nodes int32
|
||||||
|
size int32
|
||||||
|
cacher Cacher
|
||||||
|
closed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCache creates a new 'cache map'. The cacher is optional and
|
||||||
|
// may be nil.
|
||||||
|
func NewCache(cacher Cacher) *Cache {
|
||||||
|
h := &mNode{
|
||||||
|
buckets: make([]unsafe.Pointer, mInitialSize),
|
||||||
|
mask: mInitialSize - 1,
|
||||||
|
growThreshold: int32(mInitialSize * mOverflowThreshold),
|
||||||
|
shrinkThreshold: 0,
|
||||||
|
}
|
||||||
|
for i := range h.buckets {
|
||||||
|
h.buckets[i] = unsafe.Pointer(&mBucket{})
|
||||||
|
}
|
||||||
|
r := &Cache{
|
||||||
|
mHead: unsafe.Pointer(h),
|
||||||
|
cacher: cacher,
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Cache) getBucket(hash uint32) (*mNode, *mBucket) {
|
||||||
|
h := (*mNode)(atomic.LoadPointer(&r.mHead))
|
||||||
|
i := hash & h.mask
|
||||||
|
b := (*mBucket)(atomic.LoadPointer(&h.buckets[i]))
|
||||||
|
if b == nil {
|
||||||
|
b = h.initBucket(i)
|
||||||
|
}
|
||||||
|
return h, b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Cache) delete(n *Node) bool {
|
||||||
|
for {
|
||||||
|
h, b := r.getBucket(n.hash)
|
||||||
|
done, deleted := b.delete(r, h, n.hash, n.ns, n.key)
|
||||||
|
if done {
|
||||||
|
return deleted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nodes returns number of 'cache node' in the map.
|
||||||
|
func (r *Cache) Nodes() int {
|
||||||
|
return int(atomic.LoadInt32(&r.nodes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size returns sums of 'cache node' size in the map.
|
||||||
|
func (r *Cache) Size() int {
|
||||||
|
return int(atomic.LoadInt32(&r.size))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capacity returns cache capacity.
|
||||||
|
func (r *Cache) Capacity() int {
|
||||||
|
if r.cacher == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return r.cacher.Capacity()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCapacity sets cache capacity.
|
||||||
|
func (r *Cache) SetCapacity(capacity int) {
|
||||||
|
if r.cacher != nil {
|
||||||
|
r.cacher.SetCapacity(capacity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get gets 'cache node' with the given namespace and key.
|
||||||
|
// If cache node is not found and setFunc is not nil, Get will atomically creates
|
||||||
|
// the 'cache node' by calling setFunc. Otherwise Get will returns nil.
|
||||||
|
//
|
||||||
|
// The returned 'cache handle' should be released after use by calling Release
|
||||||
|
// method.
|
||||||
|
func (r *Cache) Get(ns, key uint64, setFunc func() (size int, value Value)) *Handle {
|
||||||
|
r.mu.RLock()
|
||||||
|
defer r.mu.RUnlock()
|
||||||
|
if r.closed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := murmur32(ns, key, 0xf00)
|
||||||
|
for {
|
||||||
|
h, b := r.getBucket(hash)
|
||||||
|
done, _, n := b.get(r, h, hash, ns, key, setFunc == nil)
|
||||||
|
if done {
|
||||||
|
if n != nil {
|
||||||
|
n.mu.Lock()
|
||||||
|
if n.value == nil {
|
||||||
|
if setFunc == nil {
|
||||||
|
n.mu.Unlock()
|
||||||
|
n.unref()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
n.size, n.value = setFunc()
|
||||||
|
if n.value == nil {
|
||||||
|
n.size = 0
|
||||||
|
n.mu.Unlock()
|
||||||
|
n.unref()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
atomic.AddInt32(&r.size, int32(n.size))
|
||||||
|
}
|
||||||
|
n.mu.Unlock()
|
||||||
|
if r.cacher != nil {
|
||||||
|
r.cacher.Promote(n)
|
||||||
|
}
|
||||||
|
return &Handle{unsafe.Pointer(n)}
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *fakeHandle) Release() {
|
// Delete removes and ban 'cache node' with the given namespace and key.
|
||||||
if !atomic.CompareAndSwapUint32(&h.once, 0, 1) {
|
// A banned 'cache node' will never inserted into the 'cache tree'. Ban
|
||||||
|
// only attributed to the particular 'cache node', so when a 'cache node'
|
||||||
|
// is recreated it will not be banned.
|
||||||
|
//
|
||||||
|
// If onDel is not nil, then it will be executed if such 'cache node'
|
||||||
|
// doesn't exist or once the 'cache node' is released.
|
||||||
|
//
|
||||||
|
// Delete return true is such 'cache node' exist.
|
||||||
|
func (r *Cache) Delete(ns, key uint64, onDel func()) bool {
|
||||||
|
r.mu.RLock()
|
||||||
|
defer r.mu.RUnlock()
|
||||||
|
if r.closed {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := murmur32(ns, key, 0xf00)
|
||||||
|
for {
|
||||||
|
h, b := r.getBucket(hash)
|
||||||
|
done, _, n := b.get(r, h, hash, ns, key, true)
|
||||||
|
if done {
|
||||||
|
if n != nil {
|
||||||
|
if onDel != nil {
|
||||||
|
n.mu.Lock()
|
||||||
|
n.onDel = append(n.onDel, onDel)
|
||||||
|
n.mu.Unlock()
|
||||||
|
}
|
||||||
|
if r.cacher != nil {
|
||||||
|
r.cacher.Ban(n)
|
||||||
|
}
|
||||||
|
n.unref()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if onDel != nil {
|
||||||
|
onDel()
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evict evicts 'cache node' with the given namespace and key. This will
|
||||||
|
// simply call Cacher.Evict.
|
||||||
|
//
|
||||||
|
// Evict return true is such 'cache node' exist.
|
||||||
|
func (r *Cache) Evict(ns, key uint64) bool {
|
||||||
|
r.mu.RLock()
|
||||||
|
defer r.mu.RUnlock()
|
||||||
|
if r.closed {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := murmur32(ns, key, 0xf00)
|
||||||
|
for {
|
||||||
|
h, b := r.getBucket(hash)
|
||||||
|
done, _, n := b.get(r, h, hash, ns, key, true)
|
||||||
|
if done {
|
||||||
|
if n != nil {
|
||||||
|
if r.cacher != nil {
|
||||||
|
r.cacher.Evict(n)
|
||||||
|
}
|
||||||
|
n.unref()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// EvictNS evicts 'cache node' with the given namespace. This will
|
||||||
|
// simply call Cacher.EvictNS.
|
||||||
|
func (r *Cache) EvictNS(ns uint64) {
|
||||||
|
r.mu.RLock()
|
||||||
|
defer r.mu.RUnlock()
|
||||||
|
if r.closed {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if h.fin != nil {
|
|
||||||
h.fin()
|
if r.cacher != nil {
|
||||||
h.fin = nil
|
r.cacher.EvictNS(ns)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EvictAll evicts all 'cache node'. This will simply call Cacher.EvictAll.
|
||||||
|
func (r *Cache) EvictAll() {
|
||||||
|
r.mu.RLock()
|
||||||
|
defer r.mu.RUnlock()
|
||||||
|
if r.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.cacher != nil {
|
||||||
|
r.cacher.EvictAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the 'cache map' and releases all 'cache node'.
|
||||||
|
func (r *Cache) Close() error {
|
||||||
|
r.mu.Lock()
|
||||||
|
if !r.closed {
|
||||||
|
r.closed = true
|
||||||
|
|
||||||
|
if r.cacher != nil {
|
||||||
|
if err := r.cacher.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h := (*mNode)(r.mHead)
|
||||||
|
h.initBuckets()
|
||||||
|
|
||||||
|
for i := range h.buckets {
|
||||||
|
b := (*mBucket)(h.buckets[i])
|
||||||
|
for _, n := range b.node {
|
||||||
|
// Call releaser.
|
||||||
|
if n.value != nil {
|
||||||
|
if r, ok := n.value.(util.Releaser); ok {
|
||||||
|
r.Release()
|
||||||
|
}
|
||||||
|
n.value = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call OnDel.
|
||||||
|
for _, f := range n.onDel {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.mu.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node is a 'cache node'.
|
||||||
|
type Node struct {
|
||||||
|
r *Cache
|
||||||
|
|
||||||
|
hash uint32
|
||||||
|
ns, key uint64
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
size int
|
||||||
|
value Value
|
||||||
|
|
||||||
|
ref int32
|
||||||
|
onDel []func()
|
||||||
|
|
||||||
|
CacheData unsafe.Pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NS returns this 'cache node' namespace.
|
||||||
|
func (n *Node) NS() uint64 {
|
||||||
|
return n.ns
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key returns this 'cache node' key.
|
||||||
|
func (n *Node) Key() uint64 {
|
||||||
|
return n.key
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size returns this 'cache node' size.
|
||||||
|
func (n *Node) Size() int {
|
||||||
|
return n.size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns this 'cache node' value.
|
||||||
|
func (n *Node) Value() Value {
|
||||||
|
return n.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ref returns this 'cache node' ref counter.
|
||||||
|
func (n *Node) Ref() int32 {
|
||||||
|
return atomic.LoadInt32(&n.ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHandle returns an handle for this 'cache node'.
|
||||||
|
func (n *Node) GetHandle() *Handle {
|
||||||
|
if atomic.AddInt32(&n.ref, 1) <= 1 {
|
||||||
|
panic("BUG: Node.GetHandle on zero ref")
|
||||||
|
}
|
||||||
|
return &Handle{unsafe.Pointer(n)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) unref() {
|
||||||
|
if atomic.AddInt32(&n.ref, -1) == 0 {
|
||||||
|
n.r.delete(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) unrefLocked() {
|
||||||
|
if atomic.AddInt32(&n.ref, -1) == 0 {
|
||||||
|
n.r.mu.RLock()
|
||||||
|
if !n.r.closed {
|
||||||
|
n.r.delete(n)
|
||||||
|
}
|
||||||
|
n.r.mu.RUnlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Handle struct {
|
||||||
|
n unsafe.Pointer // *Node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handle) Value() Value {
|
||||||
|
n := (*Node)(atomic.LoadPointer(&h.n))
|
||||||
|
if n != nil {
|
||||||
|
return n.value
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handle) Release() {
|
||||||
|
nPtr := atomic.LoadPointer(&h.n)
|
||||||
|
if nPtr != nil && atomic.CompareAndSwapPointer(&h.n, nPtr, nil) {
|
||||||
|
n := (*Node)(nPtr)
|
||||||
|
n.unrefLocked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func murmur32(ns, key uint64, seed uint32) uint32 {
|
||||||
|
const (
|
||||||
|
m = uint32(0x5bd1e995)
|
||||||
|
r = 24
|
||||||
|
)
|
||||||
|
|
||||||
|
k1 := uint32(ns >> 32)
|
||||||
|
k2 := uint32(ns)
|
||||||
|
k3 := uint32(key >> 32)
|
||||||
|
k4 := uint32(key)
|
||||||
|
|
||||||
|
k1 *= m
|
||||||
|
k1 ^= k1 >> r
|
||||||
|
k1 *= m
|
||||||
|
|
||||||
|
k2 *= m
|
||||||
|
k2 ^= k2 >> r
|
||||||
|
k2 *= m
|
||||||
|
|
||||||
|
k3 *= m
|
||||||
|
k3 ^= k3 >> r
|
||||||
|
k3 *= m
|
||||||
|
|
||||||
|
k4 *= m
|
||||||
|
k4 ^= k4 >> r
|
||||||
|
k4 *= m
|
||||||
|
|
||||||
|
h := seed
|
||||||
|
|
||||||
|
h *= m
|
||||||
|
h ^= k1
|
||||||
|
h *= m
|
||||||
|
h ^= k2
|
||||||
|
h *= m
|
||||||
|
h ^= k3
|
||||||
|
h *= m
|
||||||
|
h ^= k4
|
||||||
|
|
||||||
|
h ^= h >> 13
|
||||||
|
h *= m
|
||||||
|
h ^= h >> 15
|
||||||
|
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|||||||
+382
-483
@@ -13,11 +13,26 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type int32o int32
|
||||||
|
|
||||||
|
func (o *int32o) acquire() {
|
||||||
|
if atomic.AddInt32((*int32)(o), 1) != 1 {
|
||||||
|
panic("BUG: invalid ref")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *int32o) Release() {
|
||||||
|
if atomic.AddInt32((*int32)(o), -1) != 0 {
|
||||||
|
panic("BUG: invalid ref")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type releaserFunc struct {
|
type releaserFunc struct {
|
||||||
fn func()
|
fn func()
|
||||||
value interface{}
|
value Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r releaserFunc) Release() {
|
func (r releaserFunc) Release() {
|
||||||
@@ -26,8 +41,8 @@ func (r releaserFunc) Release() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func set(ns Namespace, key uint64, value interface{}, charge int, relf func()) Handle {
|
func set(c *Cache, ns, key uint64, value Value, charge int, relf func()) *Handle {
|
||||||
return ns.Get(key, func() (int, interface{}) {
|
return c.Get(ns, key, func() (int, Value) {
|
||||||
if relf != nil {
|
if relf != nil {
|
||||||
return charge, releaserFunc{relf, value}
|
return charge, releaserFunc{relf, value}
|
||||||
} else {
|
} else {
|
||||||
@@ -36,7 +51,246 @@ func set(ns Namespace, key uint64, value interface{}, charge int, relf func()) H
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCache_HitMiss(t *testing.T) {
|
func TestCacheMap(t *testing.T) {
|
||||||
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
|
||||||
|
nsx := []struct {
|
||||||
|
nobjects, nhandles, concurrent, repeat int
|
||||||
|
}{
|
||||||
|
{10000, 400, 50, 3},
|
||||||
|
{100000, 1000, 100, 10},
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
objects [][]int32o
|
||||||
|
handles [][]unsafe.Pointer
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, x := range nsx {
|
||||||
|
objects = append(objects, make([]int32o, x.nobjects))
|
||||||
|
handles = append(handles, make([]unsafe.Pointer, x.nhandles))
|
||||||
|
}
|
||||||
|
|
||||||
|
c := NewCache(nil)
|
||||||
|
|
||||||
|
wg := new(sync.WaitGroup)
|
||||||
|
var done int32
|
||||||
|
|
||||||
|
for ns, x := range nsx {
|
||||||
|
for i := 0; i < x.concurrent; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(ns, i, repeat int, objects []int32o, handles []unsafe.Pointer) {
|
||||||
|
defer wg.Done()
|
||||||
|
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
|
||||||
|
for j := len(objects) * repeat; j >= 0; j-- {
|
||||||
|
key := uint64(r.Intn(len(objects)))
|
||||||
|
h := c.Get(uint64(ns), key, func() (int, Value) {
|
||||||
|
o := &objects[key]
|
||||||
|
o.acquire()
|
||||||
|
return 1, o
|
||||||
|
})
|
||||||
|
if v := h.Value().(*int32o); v != &objects[key] {
|
||||||
|
t.Fatalf("#%d invalid value: want=%p got=%p", ns, &objects[key], v)
|
||||||
|
}
|
||||||
|
if objects[key] != 1 {
|
||||||
|
t.Fatalf("#%d invalid object %d: %d", ns, key, objects[key])
|
||||||
|
}
|
||||||
|
if !atomic.CompareAndSwapPointer(&handles[r.Intn(len(handles))], nil, unsafe.Pointer(h)) {
|
||||||
|
h.Release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(ns, i, x.repeat, objects[ns], handles[ns])
|
||||||
|
}
|
||||||
|
|
||||||
|
go func(handles []unsafe.Pointer) {
|
||||||
|
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
|
||||||
|
for atomic.LoadInt32(&done) == 0 {
|
||||||
|
i := r.Intn(len(handles))
|
||||||
|
h := (*Handle)(atomic.LoadPointer(&handles[i]))
|
||||||
|
if h != nil && atomic.CompareAndSwapPointer(&handles[i], unsafe.Pointer(h), nil) {
|
||||||
|
h.Release()
|
||||||
|
}
|
||||||
|
time.Sleep(time.Millisecond)
|
||||||
|
}
|
||||||
|
}(handles[ns])
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
handles := make([]*Handle, 100000)
|
||||||
|
for atomic.LoadInt32(&done) == 0 {
|
||||||
|
for i := range handles {
|
||||||
|
handles[i] = c.Get(999999999, uint64(i), func() (int, Value) {
|
||||||
|
return 1, 1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, h := range handles {
|
||||||
|
h.Release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
atomic.StoreInt32(&done, 1)
|
||||||
|
|
||||||
|
for _, handles0 := range handles {
|
||||||
|
for i := range handles0 {
|
||||||
|
h := (*Handle)(atomic.LoadPointer(&handles0[i]))
|
||||||
|
if h != nil && atomic.CompareAndSwapPointer(&handles0[i], unsafe.Pointer(h), nil) {
|
||||||
|
h.Release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ns, objects0 := range objects {
|
||||||
|
for i, o := range objects0 {
|
||||||
|
if o != 0 {
|
||||||
|
t.Fatalf("invalid object #%d.%d: ref=%d", ns, i, o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCacheMap_NodesAndSize(t *testing.T) {
|
||||||
|
c := NewCache(nil)
|
||||||
|
if c.Nodes() != 0 {
|
||||||
|
t.Errorf("invalid nodes counter: want=%d got=%d", 0, c.Nodes())
|
||||||
|
}
|
||||||
|
if c.Size() != 0 {
|
||||||
|
t.Errorf("invalid size counter: want=%d got=%d", 0, c.Size())
|
||||||
|
}
|
||||||
|
set(c, 0, 1, 1, 1, nil)
|
||||||
|
set(c, 0, 2, 2, 2, nil)
|
||||||
|
set(c, 1, 1, 3, 3, nil)
|
||||||
|
set(c, 2, 1, 4, 1, nil)
|
||||||
|
if c.Nodes() != 4 {
|
||||||
|
t.Errorf("invalid nodes counter: want=%d got=%d", 4, c.Nodes())
|
||||||
|
}
|
||||||
|
if c.Size() != 7 {
|
||||||
|
t.Errorf("invalid size counter: want=%d got=%d", 4, c.Size())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLRUCache_Capacity(t *testing.T) {
|
||||||
|
c := NewCache(NewLRU(10))
|
||||||
|
if c.Capacity() != 10 {
|
||||||
|
t.Errorf("invalid capacity: want=%d got=%d", 10, c.Capacity())
|
||||||
|
}
|
||||||
|
set(c, 0, 1, 1, 1, nil).Release()
|
||||||
|
set(c, 0, 2, 2, 2, nil).Release()
|
||||||
|
set(c, 1, 1, 3, 3, nil).Release()
|
||||||
|
set(c, 2, 1, 4, 1, nil).Release()
|
||||||
|
set(c, 2, 2, 5, 1, nil).Release()
|
||||||
|
set(c, 2, 3, 6, 1, nil).Release()
|
||||||
|
set(c, 2, 4, 7, 1, nil).Release()
|
||||||
|
set(c, 2, 5, 8, 1, nil).Release()
|
||||||
|
if c.Nodes() != 7 {
|
||||||
|
t.Errorf("invalid nodes counter: want=%d got=%d", 7, c.Nodes())
|
||||||
|
}
|
||||||
|
if c.Size() != 10 {
|
||||||
|
t.Errorf("invalid size counter: want=%d got=%d", 10, c.Size())
|
||||||
|
}
|
||||||
|
c.SetCapacity(9)
|
||||||
|
if c.Capacity() != 9 {
|
||||||
|
t.Errorf("invalid capacity: want=%d got=%d", 9, c.Capacity())
|
||||||
|
}
|
||||||
|
if c.Nodes() != 6 {
|
||||||
|
t.Errorf("invalid nodes counter: want=%d got=%d", 6, c.Nodes())
|
||||||
|
}
|
||||||
|
if c.Size() != 8 {
|
||||||
|
t.Errorf("invalid size counter: want=%d got=%d", 8, c.Size())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCacheMap_NilValue(t *testing.T) {
|
||||||
|
c := NewCache(NewLRU(10))
|
||||||
|
h := c.Get(0, 0, func() (size int, value Value) {
|
||||||
|
return 1, nil
|
||||||
|
})
|
||||||
|
if h != nil {
|
||||||
|
t.Error("cache handle is non-nil")
|
||||||
|
}
|
||||||
|
if c.Nodes() != 0 {
|
||||||
|
t.Errorf("invalid nodes counter: want=%d got=%d", 0, c.Nodes())
|
||||||
|
}
|
||||||
|
if c.Size() != 0 {
|
||||||
|
t.Errorf("invalid size counter: want=%d got=%d", 0, c.Size())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLRUCache_GetLatency(t *testing.T) {
|
||||||
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
|
||||||
|
const (
|
||||||
|
concurrentSet = 30
|
||||||
|
concurrentGet = 3
|
||||||
|
duration = 3 * time.Second
|
||||||
|
delay = 3 * time.Millisecond
|
||||||
|
maxkey = 100000
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
set, getHit, getAll int32
|
||||||
|
getMaxLatency, getDuration int64
|
||||||
|
)
|
||||||
|
|
||||||
|
c := NewCache(NewLRU(5000))
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
until := time.Now().Add(duration)
|
||||||
|
for i := 0; i < concurrentSet; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(i int) {
|
||||||
|
defer wg.Done()
|
||||||
|
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
for time.Now().Before(until) {
|
||||||
|
c.Get(0, uint64(r.Intn(maxkey)), func() (int, Value) {
|
||||||
|
time.Sleep(delay)
|
||||||
|
atomic.AddInt32(&set, 1)
|
||||||
|
return 1, 1
|
||||||
|
}).Release()
|
||||||
|
}
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
for i := 0; i < concurrentGet; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(i int) {
|
||||||
|
defer wg.Done()
|
||||||
|
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
for {
|
||||||
|
mark := time.Now()
|
||||||
|
if mark.Before(until) {
|
||||||
|
h := c.Get(0, uint64(r.Intn(maxkey)), nil)
|
||||||
|
latency := int64(time.Now().Sub(mark))
|
||||||
|
m := atomic.LoadInt64(&getMaxLatency)
|
||||||
|
if latency > m {
|
||||||
|
atomic.CompareAndSwapInt64(&getMaxLatency, m, latency)
|
||||||
|
}
|
||||||
|
atomic.AddInt64(&getDuration, latency)
|
||||||
|
if h != nil {
|
||||||
|
atomic.AddInt32(&getHit, 1)
|
||||||
|
h.Release()
|
||||||
|
}
|
||||||
|
atomic.AddInt32(&getAll, 1)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
getAvglatency := time.Duration(getDuration) / time.Duration(getAll)
|
||||||
|
t.Logf("set=%d getHit=%d getAll=%d getMaxLatency=%v getAvgLatency=%v",
|
||||||
|
set, getHit, getAll, time.Duration(getMaxLatency), getAvglatency)
|
||||||
|
|
||||||
|
if getAvglatency > delay/3 {
|
||||||
|
t.Errorf("get avg latency > %v: got=%v", delay/3, getAvglatency)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLRUCache_HitMiss(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
key uint64
|
key uint64
|
||||||
value string
|
value string
|
||||||
@@ -54,14 +308,13 @@ func TestCache_HitMiss(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setfin := 0
|
setfin := 0
|
||||||
c := NewLRUCache(1000)
|
c := NewCache(NewLRU(1000))
|
||||||
ns := c.GetNamespace(0)
|
|
||||||
for i, x := range cases {
|
for i, x := range cases {
|
||||||
set(ns, x.key, x.value, len(x.value), func() {
|
set(c, 0, x.key, x.value, len(x.value), func() {
|
||||||
setfin++
|
setfin++
|
||||||
}).Release()
|
}).Release()
|
||||||
for j, y := range cases {
|
for j, y := range cases {
|
||||||
h := ns.Get(y.key, nil)
|
h := c.Get(0, y.key, nil)
|
||||||
if j <= i {
|
if j <= i {
|
||||||
// should hit
|
// should hit
|
||||||
if h == nil {
|
if h == nil {
|
||||||
@@ -85,7 +338,7 @@ func TestCache_HitMiss(t *testing.T) {
|
|||||||
|
|
||||||
for i, x := range cases {
|
for i, x := range cases {
|
||||||
finalizerOk := false
|
finalizerOk := false
|
||||||
ns.Delete(x.key, func(exist, pending bool) {
|
c.Delete(0, x.key, func() {
|
||||||
finalizerOk = true
|
finalizerOk = true
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -94,7 +347,7 @@ func TestCache_HitMiss(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for j, y := range cases {
|
for j, y := range cases {
|
||||||
h := ns.Get(y.key, nil)
|
h := c.Get(0, y.key, nil)
|
||||||
if j > i {
|
if j > i {
|
||||||
// should hit
|
// should hit
|
||||||
if h == nil {
|
if h == nil {
|
||||||
@@ -122,20 +375,19 @@ func TestCache_HitMiss(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestLRUCache_Eviction(t *testing.T) {
|
func TestLRUCache_Eviction(t *testing.T) {
|
||||||
c := NewLRUCache(12)
|
c := NewCache(NewLRU(12))
|
||||||
ns := c.GetNamespace(0)
|
o1 := set(c, 0, 1, 1, 1, nil)
|
||||||
o1 := set(ns, 1, 1, 1, nil)
|
set(c, 0, 2, 2, 1, nil).Release()
|
||||||
set(ns, 2, 2, 1, nil).Release()
|
set(c, 0, 3, 3, 1, nil).Release()
|
||||||
set(ns, 3, 3, 1, nil).Release()
|
set(c, 0, 4, 4, 1, nil).Release()
|
||||||
set(ns, 4, 4, 1, nil).Release()
|
set(c, 0, 5, 5, 1, nil).Release()
|
||||||
set(ns, 5, 5, 1, nil).Release()
|
if h := c.Get(0, 2, nil); h != nil { // 1,3,4,5,2
|
||||||
if h := ns.Get(2, nil); h != nil { // 1,3,4,5,2
|
|
||||||
h.Release()
|
h.Release()
|
||||||
}
|
}
|
||||||
set(ns, 9, 9, 10, nil).Release() // 5,2,9
|
set(c, 0, 9, 9, 10, nil).Release() // 5,2,9
|
||||||
|
|
||||||
for _, key := range []uint64{9, 2, 5, 1} {
|
for _, key := range []uint64{9, 2, 5, 1} {
|
||||||
h := ns.Get(key, nil)
|
h := c.Get(0, key, nil)
|
||||||
if h == nil {
|
if h == nil {
|
||||||
t.Errorf("miss for key '%d'", key)
|
t.Errorf("miss for key '%d'", key)
|
||||||
} else {
|
} else {
|
||||||
@@ -147,7 +399,7 @@ func TestLRUCache_Eviction(t *testing.T) {
|
|||||||
}
|
}
|
||||||
o1.Release()
|
o1.Release()
|
||||||
for _, key := range []uint64{1, 2, 5} {
|
for _, key := range []uint64{1, 2, 5} {
|
||||||
h := ns.Get(key, nil)
|
h := c.Get(0, key, nil)
|
||||||
if h == nil {
|
if h == nil {
|
||||||
t.Errorf("miss for key '%d'", key)
|
t.Errorf("miss for key '%d'", key)
|
||||||
} else {
|
} else {
|
||||||
@@ -158,7 +410,7 @@ func TestLRUCache_Eviction(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, key := range []uint64{3, 4, 9} {
|
for _, key := range []uint64{3, 4, 9} {
|
||||||
h := ns.Get(key, nil)
|
h := c.Get(0, key, nil)
|
||||||
if h != nil {
|
if h != nil {
|
||||||
t.Errorf("hit for key '%d'", key)
|
t.Errorf("hit for key '%d'", key)
|
||||||
if x := h.Value().(int); x != int(key) {
|
if x := h.Value().(int); x != int(key) {
|
||||||
@@ -169,487 +421,134 @@ func TestLRUCache_Eviction(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLRUCache_SetGet(t *testing.T) {
|
func TestLRUCache_Evict(t *testing.T) {
|
||||||
c := NewLRUCache(13)
|
c := NewCache(NewLRU(6))
|
||||||
ns := c.GetNamespace(0)
|
set(c, 0, 1, 1, 1, nil).Release()
|
||||||
for i := 0; i < 200; i++ {
|
set(c, 0, 2, 2, 1, nil).Release()
|
||||||
n := uint64(rand.Intn(99999) % 20)
|
set(c, 1, 1, 4, 1, nil).Release()
|
||||||
set(ns, n, n, 1, nil).Release()
|
set(c, 1, 2, 5, 1, nil).Release()
|
||||||
if h := ns.Get(n, nil); h != nil {
|
set(c, 2, 1, 6, 1, nil).Release()
|
||||||
if h.Value() == nil {
|
set(c, 2, 2, 7, 1, nil).Release()
|
||||||
t.Errorf("key '%d' contains nil value", n)
|
|
||||||
|
for ns := 0; ns < 3; ns++ {
|
||||||
|
for key := 1; key < 3; key++ {
|
||||||
|
if h := c.Get(uint64(ns), uint64(key), nil); h != nil {
|
||||||
|
h.Release()
|
||||||
} else {
|
} else {
|
||||||
if x := h.Value().(uint64); x != n {
|
t.Errorf("Cache.Get on #%d.%d return nil", ns, key)
|
||||||
t.Errorf("invalid value for key '%d' want '%d', got '%d'", n, n, x)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok := c.Evict(0, 1); !ok {
|
||||||
|
t.Error("first Cache.Evict on #0.1 return false")
|
||||||
|
}
|
||||||
|
if ok := c.Evict(0, 1); ok {
|
||||||
|
t.Error("second Cache.Evict on #0.1 return true")
|
||||||
|
}
|
||||||
|
if h := c.Get(0, 1, nil); h != nil {
|
||||||
|
t.Errorf("Cache.Get on #0.1 return non-nil: %v", h.Value())
|
||||||
|
}
|
||||||
|
|
||||||
|
c.EvictNS(1)
|
||||||
|
if h := c.Get(1, 1, nil); h != nil {
|
||||||
|
t.Errorf("Cache.Get on #1.1 return non-nil: %v", h.Value())
|
||||||
|
}
|
||||||
|
if h := c.Get(1, 2, nil); h != nil {
|
||||||
|
t.Errorf("Cache.Get on #1.2 return non-nil: %v", h.Value())
|
||||||
|
}
|
||||||
|
|
||||||
|
c.EvictAll()
|
||||||
|
for ns := 0; ns < 3; ns++ {
|
||||||
|
for key := 1; key < 3; key++ {
|
||||||
|
if h := c.Get(uint64(ns), uint64(key), nil); h != nil {
|
||||||
|
t.Errorf("Cache.Get on #%d.%d return non-nil: %v", ns, key, h.Value())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLRUCache_Delete(t *testing.T) {
|
||||||
|
delFuncCalled := 0
|
||||||
|
delFunc := func() {
|
||||||
|
delFuncCalled++
|
||||||
|
}
|
||||||
|
|
||||||
|
c := NewCache(NewLRU(2))
|
||||||
|
set(c, 0, 1, 1, 1, nil).Release()
|
||||||
|
set(c, 0, 2, 2, 1, nil).Release()
|
||||||
|
|
||||||
|
if ok := c.Delete(0, 1, delFunc); !ok {
|
||||||
|
t.Error("Cache.Delete on #1 return false")
|
||||||
|
}
|
||||||
|
if h := c.Get(0, 1, nil); h != nil {
|
||||||
|
t.Errorf("Cache.Get on #1 return non-nil: %v", h.Value())
|
||||||
|
}
|
||||||
|
if ok := c.Delete(0, 1, delFunc); ok {
|
||||||
|
t.Error("Cache.Delete on #1 return true")
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 := c.Get(0, 2, nil)
|
||||||
|
if h2 == nil {
|
||||||
|
t.Error("Cache.Get on #2 return nil")
|
||||||
|
}
|
||||||
|
if ok := c.Delete(0, 2, delFunc); !ok {
|
||||||
|
t.Error("(1) Cache.Delete on #2 return false")
|
||||||
|
}
|
||||||
|
if ok := c.Delete(0, 2, delFunc); !ok {
|
||||||
|
t.Error("(2) Cache.Delete on #2 return false")
|
||||||
|
}
|
||||||
|
|
||||||
|
set(c, 0, 3, 3, 1, nil).Release()
|
||||||
|
set(c, 0, 4, 4, 1, nil).Release()
|
||||||
|
c.Get(0, 2, nil).Release()
|
||||||
|
|
||||||
|
for key := 2; key <= 4; key++ {
|
||||||
|
if h := c.Get(0, uint64(key), nil); h != nil {
|
||||||
h.Release()
|
h.Release()
|
||||||
} else {
|
} else {
|
||||||
t.Errorf("key '%d' doesn't exist", n)
|
t.Errorf("Cache.Get on #%d return nil", key)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLRUCache_Purge(t *testing.T) {
|
|
||||||
c := NewLRUCache(3)
|
|
||||||
ns1 := c.GetNamespace(0)
|
|
||||||
o1 := set(ns1, 1, 1, 1, nil)
|
|
||||||
o2 := set(ns1, 2, 2, 1, nil)
|
|
||||||
ns1.Purge(nil)
|
|
||||||
set(ns1, 3, 3, 1, nil).Release()
|
|
||||||
for _, key := range []uint64{1, 2, 3} {
|
|
||||||
h := ns1.Get(key, nil)
|
|
||||||
if h == nil {
|
|
||||||
t.Errorf("miss for key '%d'", key)
|
|
||||||
} else {
|
|
||||||
if x := h.Value().(int); x != int(key) {
|
|
||||||
t.Errorf("invalid value for key '%d' want '%d', got '%d'", key, key, x)
|
|
||||||
}
|
|
||||||
h.Release()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
o1.Release()
|
|
||||||
o2.Release()
|
|
||||||
for _, key := range []uint64{1, 2} {
|
|
||||||
h := ns1.Get(key, nil)
|
|
||||||
if h != nil {
|
|
||||||
t.Errorf("hit for key '%d'", key)
|
|
||||||
if x := h.Value().(int); x != int(key) {
|
|
||||||
t.Errorf("invalid value for key '%d' want '%d', got '%d'", key, key, x)
|
|
||||||
}
|
|
||||||
h.Release()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type testingCacheObjectCounter struct {
|
|
||||||
created uint
|
|
||||||
released uint
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *testingCacheObjectCounter) createOne() {
|
|
||||||
c.created++
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *testingCacheObjectCounter) releaseOne() {
|
|
||||||
c.released++
|
|
||||||
}
|
|
||||||
|
|
||||||
type testingCacheObject struct {
|
|
||||||
t *testing.T
|
|
||||||
cnt *testingCacheObjectCounter
|
|
||||||
|
|
||||||
ns, key uint64
|
|
||||||
|
|
||||||
releaseCalled bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *testingCacheObject) Release() {
|
|
||||||
if !x.releaseCalled {
|
|
||||||
x.releaseCalled = true
|
|
||||||
x.cnt.releaseOne()
|
|
||||||
} else {
|
|
||||||
x.t.Errorf("duplicate setfin NS#%d KEY#%d", x.ns, x.key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLRUCache_ConcurrentSetGet(t *testing.T) {
|
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
|
||||||
|
|
||||||
seed := time.Now().UnixNano()
|
|
||||||
t.Logf("seed=%d", seed)
|
|
||||||
|
|
||||||
const (
|
|
||||||
N = 2000000
|
|
||||||
M = 4000
|
|
||||||
C = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
var set, get uint32
|
|
||||||
|
|
||||||
wg := &sync.WaitGroup{}
|
|
||||||
c := NewLRUCache(M / 4)
|
|
||||||
for ni := uint64(0); ni < C; ni++ {
|
|
||||||
r0 := rand.New(rand.NewSource(seed + int64(ni)))
|
|
||||||
r1 := rand.New(rand.NewSource(seed + int64(ni) + 1))
|
|
||||||
ns := c.GetNamespace(ni)
|
|
||||||
|
|
||||||
wg.Add(2)
|
|
||||||
go func(ns Namespace, r *rand.Rand) {
|
|
||||||
for i := 0; i < N; i++ {
|
|
||||||
x := uint64(r.Int63n(M))
|
|
||||||
o := ns.Get(x, func() (int, interface{}) {
|
|
||||||
atomic.AddUint32(&set, 1)
|
|
||||||
return 1, x
|
|
||||||
})
|
|
||||||
if v := o.Value().(uint64); v != x {
|
|
||||||
t.Errorf("#%d invalid value, got=%d", x, v)
|
|
||||||
}
|
|
||||||
o.Release()
|
|
||||||
}
|
|
||||||
wg.Done()
|
|
||||||
}(ns, r0)
|
|
||||||
go func(ns Namespace, r *rand.Rand) {
|
|
||||||
for i := 0; i < N; i++ {
|
|
||||||
x := uint64(r.Int63n(M))
|
|
||||||
o := ns.Get(x, nil)
|
|
||||||
if o != nil {
|
|
||||||
atomic.AddUint32(&get, 1)
|
|
||||||
if v := o.Value().(uint64); v != x {
|
|
||||||
t.Errorf("#%d invalid value, got=%d", x, v)
|
|
||||||
}
|
|
||||||
o.Release()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wg.Done()
|
|
||||||
}(ns, r1)
|
|
||||||
}
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
t.Logf("set=%d get=%d", set, get)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLRUCache_Finalizer(t *testing.T) {
|
|
||||||
const (
|
|
||||||
capacity = 100
|
|
||||||
goroutines = 100
|
|
||||||
iterations = 10000
|
|
||||||
keymax = 8000
|
|
||||||
)
|
|
||||||
|
|
||||||
cnt := &testingCacheObjectCounter{}
|
|
||||||
|
|
||||||
c := NewLRUCache(capacity)
|
|
||||||
|
|
||||||
type instance struct {
|
|
||||||
seed int64
|
|
||||||
rnd *rand.Rand
|
|
||||||
nsid uint64
|
|
||||||
ns Namespace
|
|
||||||
effective int
|
|
||||||
handles []Handle
|
|
||||||
handlesMap map[uint64]int
|
|
||||||
|
|
||||||
delete bool
|
|
||||||
purge bool
|
|
||||||
zap bool
|
|
||||||
wantDel int
|
|
||||||
delfinCalled int
|
|
||||||
delfinCalledAll int
|
|
||||||
delfinCalledEff int
|
|
||||||
purgefinCalled int
|
|
||||||
}
|
|
||||||
|
|
||||||
instanceGet := func(p *instance, key uint64) {
|
|
||||||
h := p.ns.Get(key, func() (charge int, value interface{}) {
|
|
||||||
to := &testingCacheObject{
|
|
||||||
t: t, cnt: cnt,
|
|
||||||
ns: p.nsid,
|
|
||||||
key: key,
|
|
||||||
}
|
|
||||||
p.effective++
|
|
||||||
cnt.createOne()
|
|
||||||
return 1, releaserFunc{func() {
|
|
||||||
to.Release()
|
|
||||||
p.effective--
|
|
||||||
}, to}
|
|
||||||
})
|
|
||||||
p.handles = append(p.handles, h)
|
|
||||||
p.handlesMap[key] = p.handlesMap[key] + 1
|
|
||||||
}
|
|
||||||
instanceRelease := func(p *instance, i int) {
|
|
||||||
h := p.handles[i]
|
|
||||||
key := h.Value().(releaserFunc).value.(*testingCacheObject).key
|
|
||||||
if n := p.handlesMap[key]; n == 0 {
|
|
||||||
t.Fatal("key ref == 0")
|
|
||||||
} else if n > 1 {
|
|
||||||
p.handlesMap[key] = n - 1
|
|
||||||
} else {
|
|
||||||
delete(p.handlesMap, key)
|
|
||||||
}
|
|
||||||
h.Release()
|
|
||||||
p.handles = append(p.handles[:i], p.handles[i+1:]...)
|
|
||||||
p.handles[len(p.handles) : len(p.handles)+1][0] = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
seed := time.Now().UnixNano()
|
|
||||||
t.Logf("seed=%d", seed)
|
|
||||||
|
|
||||||
instances := make([]*instance, goroutines)
|
|
||||||
for i := range instances {
|
|
||||||
p := &instance{}
|
|
||||||
p.handlesMap = make(map[uint64]int)
|
|
||||||
p.seed = seed + int64(i)
|
|
||||||
p.rnd = rand.New(rand.NewSource(p.seed))
|
|
||||||
p.nsid = uint64(i)
|
|
||||||
p.ns = c.GetNamespace(p.nsid)
|
|
||||||
p.delete = i%6 == 0
|
|
||||||
p.purge = i%8 == 0
|
|
||||||
p.zap = i%12 == 0 || i%3 == 0
|
|
||||||
instances[i] = p
|
|
||||||
}
|
|
||||||
|
|
||||||
runr := rand.New(rand.NewSource(seed - 1))
|
|
||||||
run := func(rnd *rand.Rand, x []*instance, init func(p *instance) bool, fn func(p *instance, i int) bool) {
|
|
||||||
var (
|
|
||||||
rx []*instance
|
|
||||||
rn []int
|
|
||||||
)
|
|
||||||
if init == nil {
|
|
||||||
rx = append([]*instance{}, x...)
|
|
||||||
rn = make([]int, len(x))
|
|
||||||
} else {
|
|
||||||
for _, p := range x {
|
|
||||||
if init(p) {
|
|
||||||
rx = append(rx, p)
|
|
||||||
rn = append(rn, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for len(rx) > 0 {
|
|
||||||
i := rand.Intn(len(rx))
|
|
||||||
if fn(rx[i], rn[i]) {
|
|
||||||
rn[i]++
|
|
||||||
} else {
|
|
||||||
rx = append(rx[:i], rx[i+1:]...)
|
|
||||||
rn = append(rn[:i], rn[i+1:]...)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get and release.
|
h2.Release()
|
||||||
run(runr, instances, nil, func(p *instance, i int) bool {
|
if h := c.Get(0, 2, nil); h != nil {
|
||||||
if i < iterations {
|
t.Errorf("Cache.Get on #2 return non-nil: %v", h.Value())
|
||||||
if len(p.handles) == 0 || p.rnd.Int()%2 == 0 {
|
|
||||||
instanceGet(p, uint64(p.rnd.Intn(keymax)))
|
|
||||||
} else {
|
|
||||||
instanceRelease(p, p.rnd.Intn(len(p.handles)))
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if used, cap := c.Used(), c.Capacity(); used > cap {
|
|
||||||
t.Errorf("Used > capacity, used=%d cap=%d", used, cap)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check effective objects.
|
if delFuncCalled != 4 {
|
||||||
for i, p := range instances {
|
t.Errorf("delFunc isn't called 4 times: got=%d", delFuncCalled)
|
||||||
if int(p.effective) < len(p.handlesMap) {
|
|
||||||
t.Errorf("#%d effective objects < acquired handle, eo=%d ah=%d", i, p.effective, len(p.handlesMap))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if want := int(cnt.created - cnt.released); c.Size() != want {
|
|
||||||
t.Errorf("Invalid cache size, want=%d got=%d", want, c.Size())
|
|
||||||
}
|
|
||||||
|
|
||||||
// First delete.
|
|
||||||
run(runr, instances, func(p *instance) bool {
|
|
||||||
p.wantDel = p.effective
|
|
||||||
return p.delete
|
|
||||||
}, func(p *instance, i int) bool {
|
|
||||||
key := uint64(i)
|
|
||||||
if key < keymax {
|
|
||||||
_, wantExist := p.handlesMap[key]
|
|
||||||
gotExist := p.ns.Delete(key, func(exist, pending bool) {
|
|
||||||
p.delfinCalledAll++
|
|
||||||
if exist {
|
|
||||||
p.delfinCalledEff++
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if !gotExist && wantExist {
|
|
||||||
t.Errorf("delete on NS#%d KEY#%d not found", p.nsid, key)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Second delete.
|
|
||||||
run(runr, instances, func(p *instance) bool {
|
|
||||||
p.delfinCalled = 0
|
|
||||||
return p.delete
|
|
||||||
}, func(p *instance, i int) bool {
|
|
||||||
key := uint64(i)
|
|
||||||
if key < keymax {
|
|
||||||
gotExist := p.ns.Delete(key, func(exist, pending bool) {
|
|
||||||
if exist && !pending {
|
|
||||||
t.Errorf("delete fin on NS#%d KEY#%d exist and not pending for deletion", p.nsid, key)
|
|
||||||
}
|
|
||||||
p.delfinCalled++
|
|
||||||
})
|
|
||||||
if gotExist {
|
|
||||||
t.Errorf("delete on NS#%d KEY#%d found", p.nsid, key)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
if p.delfinCalled != keymax {
|
|
||||||
t.Errorf("(2) NS#%d not all delete fin called, diff=%d", p.nsid, keymax-p.delfinCalled)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Purge.
|
|
||||||
run(runr, instances, func(p *instance) bool {
|
|
||||||
return p.purge
|
|
||||||
}, func(p *instance, i int) bool {
|
|
||||||
p.ns.Purge(func(ns, key uint64) {
|
|
||||||
p.purgefinCalled++
|
|
||||||
})
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
if want := int(cnt.created - cnt.released); c.Size() != want {
|
|
||||||
t.Errorf("Invalid cache size, want=%d got=%d", want, c.Size())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Release.
|
|
||||||
run(runr, instances, func(p *instance) bool {
|
|
||||||
return !p.zap
|
|
||||||
}, func(p *instance, i int) bool {
|
|
||||||
if len(p.handles) > 0 {
|
|
||||||
instanceRelease(p, len(p.handles)-1)
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if want := int(cnt.created - cnt.released); c.Size() != want {
|
|
||||||
t.Errorf("Invalid cache size, want=%d got=%d", want, c.Size())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zap.
|
|
||||||
run(runr, instances, func(p *instance) bool {
|
|
||||||
return p.zap
|
|
||||||
}, func(p *instance, i int) bool {
|
|
||||||
p.ns.Zap()
|
|
||||||
p.handles = nil
|
|
||||||
p.handlesMap = nil
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
if want := int(cnt.created - cnt.released); c.Size() != want {
|
|
||||||
t.Errorf("Invalid cache size, want=%d got=%d", want, c.Size())
|
|
||||||
}
|
|
||||||
|
|
||||||
if notrel, used := int(cnt.created-cnt.released), c.Used(); notrel != used {
|
|
||||||
t.Errorf("Invalid used value, want=%d got=%d", notrel, used)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Purge(nil)
|
|
||||||
|
|
||||||
for _, p := range instances {
|
|
||||||
if p.delete {
|
|
||||||
if p.delfinCalledAll != keymax {
|
|
||||||
t.Errorf("#%d not all delete fin called, purge=%v zap=%v diff=%d", p.nsid, p.purge, p.zap, keymax-p.delfinCalledAll)
|
|
||||||
}
|
|
||||||
if p.delfinCalledEff != p.wantDel {
|
|
||||||
t.Errorf("#%d not all effective delete fin called, diff=%d", p.nsid, p.wantDel-p.delfinCalledEff)
|
|
||||||
}
|
|
||||||
if p.purge && p.purgefinCalled > 0 {
|
|
||||||
t.Errorf("#%d some purge fin called, delete=%v zap=%v n=%d", p.nsid, p.delete, p.zap, p.purgefinCalled)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if p.purge {
|
|
||||||
if p.purgefinCalled != p.wantDel {
|
|
||||||
t.Errorf("#%d not all purge fin called, delete=%v zap=%v diff=%d", p.nsid, p.delete, p.zap, p.wantDel-p.purgefinCalled)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if cnt.created != cnt.released {
|
|
||||||
t.Errorf("Some cache object weren't released, created=%d released=%d", cnt.created, cnt.released)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkLRUCache_Set(b *testing.B) {
|
func TestLRUCache_Close(t *testing.T) {
|
||||||
c := NewLRUCache(0)
|
relFuncCalled := 0
|
||||||
ns := c.GetNamespace(0)
|
relFunc := func() {
|
||||||
b.ResetTimer()
|
relFuncCalled++
|
||||||
for i := uint64(0); i < uint64(b.N); i++ {
|
|
||||||
set(ns, i, "", 1, nil)
|
|
||||||
}
|
}
|
||||||
}
|
delFuncCalled := 0
|
||||||
|
delFunc := func() {
|
||||||
func BenchmarkLRUCache_Get(b *testing.B) {
|
delFuncCalled++
|
||||||
c := NewLRUCache(0)
|
|
||||||
ns := c.GetNamespace(0)
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := uint64(0); i < uint64(b.N); i++ {
|
|
||||||
set(ns, i, "", 1, nil)
|
|
||||||
}
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := uint64(0); i < uint64(b.N); i++ {
|
|
||||||
ns.Get(i, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkLRUCache_Get2(b *testing.B) {
|
|
||||||
c := NewLRUCache(0)
|
|
||||||
ns := c.GetNamespace(0)
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := uint64(0); i < uint64(b.N); i++ {
|
|
||||||
set(ns, i, "", 1, nil)
|
|
||||||
}
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := uint64(0); i < uint64(b.N); i++ {
|
|
||||||
ns.Get(i, func() (charge int, value interface{}) {
|
|
||||||
return 0, nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkLRUCache_Release(b *testing.B) {
|
c := NewCache(NewLRU(2))
|
||||||
c := NewLRUCache(0)
|
set(c, 0, 1, 1, 1, relFunc).Release()
|
||||||
ns := c.GetNamespace(0)
|
set(c, 0, 2, 2, 1, relFunc).Release()
|
||||||
handles := make([]Handle, b.N)
|
|
||||||
for i := uint64(0); i < uint64(b.N); i++ {
|
|
||||||
handles[i] = set(ns, i, "", 1, nil)
|
|
||||||
}
|
|
||||||
b.ResetTimer()
|
|
||||||
for _, h := range handles {
|
|
||||||
h.Release()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkLRUCache_SetRelease(b *testing.B) {
|
h3 := set(c, 0, 3, 3, 1, relFunc)
|
||||||
capacity := b.N / 100
|
if h3 == nil {
|
||||||
if capacity <= 0 {
|
t.Error("Cache.Get on #3 return nil")
|
||||||
capacity = 10
|
|
||||||
}
|
}
|
||||||
c := NewLRUCache(capacity)
|
if ok := c.Delete(0, 3, delFunc); !ok {
|
||||||
ns := c.GetNamespace(0)
|
t.Error("Cache.Delete on #3 return false")
|
||||||
b.ResetTimer()
|
|
||||||
for i := uint64(0); i < uint64(b.N); i++ {
|
|
||||||
set(ns, i, "", 1, nil).Release()
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkLRUCache_SetReleaseTwice(b *testing.B) {
|
c.Close()
|
||||||
capacity := b.N / 100
|
|
||||||
if capacity <= 0 {
|
|
||||||
capacity = 10
|
|
||||||
}
|
|
||||||
c := NewLRUCache(capacity)
|
|
||||||
ns := c.GetNamespace(0)
|
|
||||||
b.ResetTimer()
|
|
||||||
|
|
||||||
na := b.N / 2
|
if relFuncCalled != 3 {
|
||||||
nb := b.N - na
|
t.Errorf("relFunc isn't called 3 times: got=%d", relFuncCalled)
|
||||||
|
|
||||||
for i := uint64(0); i < uint64(na); i++ {
|
|
||||||
set(ns, i, "", 1, nil).Release()
|
|
||||||
}
|
}
|
||||||
|
if delFuncCalled != 1 {
|
||||||
for i := uint64(0); i < uint64(nb); i++ {
|
t.Errorf("delFunc isn't called 1 times: got=%d", delFuncCalled)
|
||||||
set(ns, i, "", 1, nil).Release()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+195
@@ -0,0 +1,195 @@
|
|||||||
|
// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type lruNode struct {
|
||||||
|
n *Node
|
||||||
|
h *Handle
|
||||||
|
ban bool
|
||||||
|
|
||||||
|
next, prev *lruNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *lruNode) insert(at *lruNode) {
|
||||||
|
x := at.next
|
||||||
|
at.next = n
|
||||||
|
n.prev = at
|
||||||
|
n.next = x
|
||||||
|
x.prev = n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *lruNode) remove() {
|
||||||
|
if n.prev != nil {
|
||||||
|
n.prev.next = n.next
|
||||||
|
n.next.prev = n.prev
|
||||||
|
n.prev = nil
|
||||||
|
n.next = nil
|
||||||
|
} else {
|
||||||
|
panic("BUG: removing removed node")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type lru struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
capacity int
|
||||||
|
used int
|
||||||
|
recent lruNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *lru) reset() {
|
||||||
|
r.recent.next = &r.recent
|
||||||
|
r.recent.prev = &r.recent
|
||||||
|
r.used = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *lru) Capacity() int {
|
||||||
|
r.mu.Lock()
|
||||||
|
defer r.mu.Unlock()
|
||||||
|
return r.capacity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *lru) SetCapacity(capacity int) {
|
||||||
|
var evicted []*lruNode
|
||||||
|
|
||||||
|
r.mu.Lock()
|
||||||
|
r.capacity = capacity
|
||||||
|
for r.used > r.capacity {
|
||||||
|
rn := r.recent.prev
|
||||||
|
if rn == nil {
|
||||||
|
panic("BUG: invalid LRU used or capacity counter")
|
||||||
|
}
|
||||||
|
rn.remove()
|
||||||
|
rn.n.CacheData = nil
|
||||||
|
r.used -= rn.n.Size()
|
||||||
|
evicted = append(evicted, rn)
|
||||||
|
}
|
||||||
|
r.mu.Unlock()
|
||||||
|
|
||||||
|
for _, rn := range evicted {
|
||||||
|
rn.h.Release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *lru) Promote(n *Node) {
|
||||||
|
var evicted []*lruNode
|
||||||
|
|
||||||
|
r.mu.Lock()
|
||||||
|
if n.CacheData == nil {
|
||||||
|
if n.Size() <= r.capacity {
|
||||||
|
rn := &lruNode{n: n, h: n.GetHandle()}
|
||||||
|
rn.insert(&r.recent)
|
||||||
|
n.CacheData = unsafe.Pointer(rn)
|
||||||
|
r.used += n.Size()
|
||||||
|
|
||||||
|
for r.used > r.capacity {
|
||||||
|
rn := r.recent.prev
|
||||||
|
if rn == nil {
|
||||||
|
panic("BUG: invalid LRU used or capacity counter")
|
||||||
|
}
|
||||||
|
rn.remove()
|
||||||
|
rn.n.CacheData = nil
|
||||||
|
r.used -= rn.n.Size()
|
||||||
|
evicted = append(evicted, rn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rn := (*lruNode)(n.CacheData)
|
||||||
|
if !rn.ban {
|
||||||
|
rn.remove()
|
||||||
|
rn.insert(&r.recent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.mu.Unlock()
|
||||||
|
|
||||||
|
for _, rn := range evicted {
|
||||||
|
rn.h.Release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *lru) Ban(n *Node) {
|
||||||
|
r.mu.Lock()
|
||||||
|
if n.CacheData == nil {
|
||||||
|
n.CacheData = unsafe.Pointer(&lruNode{n: n, ban: true})
|
||||||
|
} else {
|
||||||
|
rn := (*lruNode)(n.CacheData)
|
||||||
|
if !rn.ban {
|
||||||
|
rn.remove()
|
||||||
|
rn.ban = true
|
||||||
|
r.used -= rn.n.Size()
|
||||||
|
r.mu.Unlock()
|
||||||
|
|
||||||
|
rn.h.Release()
|
||||||
|
rn.h = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *lru) Evict(n *Node) {
|
||||||
|
r.mu.Lock()
|
||||||
|
rn := (*lruNode)(n.CacheData)
|
||||||
|
if rn == nil || rn.ban {
|
||||||
|
r.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n.CacheData = nil
|
||||||
|
r.mu.Unlock()
|
||||||
|
|
||||||
|
rn.h.Release()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *lru) EvictNS(ns uint64) {
|
||||||
|
var evicted []*lruNode
|
||||||
|
|
||||||
|
r.mu.Lock()
|
||||||
|
for e := r.recent.prev; e != &r.recent; {
|
||||||
|
rn := e
|
||||||
|
e = e.prev
|
||||||
|
if rn.n.NS() == ns {
|
||||||
|
rn.remove()
|
||||||
|
rn.n.CacheData = nil
|
||||||
|
r.used -= rn.n.Size()
|
||||||
|
evicted = append(evicted, rn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.mu.Unlock()
|
||||||
|
|
||||||
|
for _, rn := range evicted {
|
||||||
|
rn.h.Release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *lru) EvictAll() {
|
||||||
|
r.mu.Lock()
|
||||||
|
back := r.recent.prev
|
||||||
|
for rn := back; rn != &r.recent; rn = rn.prev {
|
||||||
|
rn.n.CacheData = nil
|
||||||
|
}
|
||||||
|
r.reset()
|
||||||
|
r.mu.Unlock()
|
||||||
|
|
||||||
|
for rn := back; rn != &r.recent; rn = rn.prev {
|
||||||
|
rn.h.Release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *lru) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLRU create a new LRU-cache.
|
||||||
|
func NewLRU(capacity int) Cacher {
|
||||||
|
r := &lru{capacity: capacity}
|
||||||
|
r.reset()
|
||||||
|
return r
|
||||||
|
}
|
||||||
-622
@@ -1,622 +0,0 @@
|
|||||||
// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
package cache
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
|
|
||||||
"github.com/syndtr/goleveldb/leveldb/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The LLRB implementation were taken from https://github.com/petar/GoLLRB.
|
|
||||||
// Which contains the following header:
|
|
||||||
//
|
|
||||||
// Copyright 2010 Petar Maymounkov. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// lruCache represent a LRU cache state.
|
|
||||||
type lruCache struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
recent lruNode
|
|
||||||
table map[uint64]*lruNs
|
|
||||||
capacity int
|
|
||||||
used, size, alive int
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewLRUCache creates a new initialized LRU cache with the given capacity.
|
|
||||||
func NewLRUCache(capacity int) Cache {
|
|
||||||
c := &lruCache{
|
|
||||||
table: make(map[uint64]*lruNs),
|
|
||||||
capacity: capacity,
|
|
||||||
}
|
|
||||||
c.recent.rNext = &c.recent
|
|
||||||
c.recent.rPrev = &c.recent
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *lruCache) Capacity() int {
|
|
||||||
c.mu.Lock()
|
|
||||||
defer c.mu.Unlock()
|
|
||||||
return c.capacity
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *lruCache) Used() int {
|
|
||||||
c.mu.Lock()
|
|
||||||
defer c.mu.Unlock()
|
|
||||||
return c.used
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *lruCache) Size() int {
|
|
||||||
c.mu.Lock()
|
|
||||||
defer c.mu.Unlock()
|
|
||||||
return c.size
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *lruCache) NumObjects() int {
|
|
||||||
c.mu.Lock()
|
|
||||||
defer c.mu.Unlock()
|
|
||||||
return c.alive
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCapacity set cache capacity.
|
|
||||||
func (c *lruCache) SetCapacity(capacity int) {
|
|
||||||
c.mu.Lock()
|
|
||||||
c.capacity = capacity
|
|
||||||
c.evict()
|
|
||||||
c.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetNamespace return namespace object for given id.
|
|
||||||
func (c *lruCache) GetNamespace(id uint64) Namespace {
|
|
||||||
c.mu.Lock()
|
|
||||||
defer c.mu.Unlock()
|
|
||||||
|
|
||||||
if ns, ok := c.table[id]; ok {
|
|
||||||
return ns
|
|
||||||
}
|
|
||||||
|
|
||||||
ns := &lruNs{lru: c, id: id}
|
|
||||||
c.table[id] = ns
|
|
||||||
return ns
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *lruCache) ZapNamespace(id uint64) {
|
|
||||||
c.mu.Lock()
|
|
||||||
if ns, exist := c.table[id]; exist {
|
|
||||||
ns.zapNB()
|
|
||||||
delete(c.table, id)
|
|
||||||
}
|
|
||||||
c.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *lruCache) PurgeNamespace(id uint64, fin PurgeFin) {
|
|
||||||
c.mu.Lock()
|
|
||||||
if ns, exist := c.table[id]; exist {
|
|
||||||
ns.purgeNB(fin)
|
|
||||||
}
|
|
||||||
c.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Purge purge entire cache.
|
|
||||||
func (c *lruCache) Purge(fin PurgeFin) {
|
|
||||||
c.mu.Lock()
|
|
||||||
for _, ns := range c.table {
|
|
||||||
ns.purgeNB(fin)
|
|
||||||
}
|
|
||||||
c.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *lruCache) Zap() {
|
|
||||||
c.mu.Lock()
|
|
||||||
for _, ns := range c.table {
|
|
||||||
ns.zapNB()
|
|
||||||
}
|
|
||||||
c.table = make(map[uint64]*lruNs)
|
|
||||||
c.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *lruCache) evict() {
|
|
||||||
top := &c.recent
|
|
||||||
for n := c.recent.rPrev; c.used > c.capacity && n != top; {
|
|
||||||
if n.state != nodeEffective {
|
|
||||||
panic("evicting non effective node")
|
|
||||||
}
|
|
||||||
n.state = nodeEvicted
|
|
||||||
n.rRemove()
|
|
||||||
n.derefNB()
|
|
||||||
c.used -= n.charge
|
|
||||||
n = c.recent.rPrev
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type lruNs struct {
|
|
||||||
lru *lruCache
|
|
||||||
id uint64
|
|
||||||
rbRoot *lruNode
|
|
||||||
state nsState
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *lruNs) rbGetOrCreateNode(h *lruNode, key uint64) (hn, n *lruNode) {
|
|
||||||
if h == nil {
|
|
||||||
n = &lruNode{ns: ns, key: key}
|
|
||||||
return n, n
|
|
||||||
}
|
|
||||||
|
|
||||||
if key < h.key {
|
|
||||||
hn, n = ns.rbGetOrCreateNode(h.rbLeft, key)
|
|
||||||
if hn != nil {
|
|
||||||
h.rbLeft = hn
|
|
||||||
} else {
|
|
||||||
return nil, n
|
|
||||||
}
|
|
||||||
} else if key > h.key {
|
|
||||||
hn, n = ns.rbGetOrCreateNode(h.rbRight, key)
|
|
||||||
if hn != nil {
|
|
||||||
h.rbRight = hn
|
|
||||||
} else {
|
|
||||||
return nil, n
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, h
|
|
||||||
}
|
|
||||||
|
|
||||||
if rbIsRed(h.rbRight) && !rbIsRed(h.rbLeft) {
|
|
||||||
h = rbRotLeft(h)
|
|
||||||
}
|
|
||||||
if rbIsRed(h.rbLeft) && rbIsRed(h.rbLeft.rbLeft) {
|
|
||||||
h = rbRotRight(h)
|
|
||||||
}
|
|
||||||
if rbIsRed(h.rbLeft) && rbIsRed(h.rbRight) {
|
|
||||||
rbFlip(h)
|
|
||||||
}
|
|
||||||
return h, n
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *lruNs) getOrCreateNode(key uint64) *lruNode {
|
|
||||||
hn, n := ns.rbGetOrCreateNode(ns.rbRoot, key)
|
|
||||||
if hn != nil {
|
|
||||||
ns.rbRoot = hn
|
|
||||||
ns.rbRoot.rbBlack = true
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *lruNs) rbGetNode(key uint64) *lruNode {
|
|
||||||
h := ns.rbRoot
|
|
||||||
for h != nil {
|
|
||||||
switch {
|
|
||||||
case key < h.key:
|
|
||||||
h = h.rbLeft
|
|
||||||
case key > h.key:
|
|
||||||
h = h.rbRight
|
|
||||||
default:
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *lruNs) getNode(key uint64) *lruNode {
|
|
||||||
return ns.rbGetNode(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *lruNs) rbDeleteNode(h *lruNode, key uint64) *lruNode {
|
|
||||||
if h == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if key < h.key {
|
|
||||||
if h.rbLeft == nil { // key not present. Nothing to delete
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
if !rbIsRed(h.rbLeft) && !rbIsRed(h.rbLeft.rbLeft) {
|
|
||||||
h = rbMoveLeft(h)
|
|
||||||
}
|
|
||||||
h.rbLeft = ns.rbDeleteNode(h.rbLeft, key)
|
|
||||||
} else {
|
|
||||||
if rbIsRed(h.rbLeft) {
|
|
||||||
h = rbRotRight(h)
|
|
||||||
}
|
|
||||||
// If @key equals @h.key and no right children at @h
|
|
||||||
if h.key == key && h.rbRight == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if h.rbRight != nil && !rbIsRed(h.rbRight) && !rbIsRed(h.rbRight.rbLeft) {
|
|
||||||
h = rbMoveRight(h)
|
|
||||||
}
|
|
||||||
// If @key equals @h.key, and (from above) 'h.Right != nil'
|
|
||||||
if h.key == key {
|
|
||||||
var x *lruNode
|
|
||||||
h.rbRight, x = rbDeleteMin(h.rbRight)
|
|
||||||
if x == nil {
|
|
||||||
panic("logic")
|
|
||||||
}
|
|
||||||
x.rbLeft, h.rbLeft = h.rbLeft, nil
|
|
||||||
x.rbRight, h.rbRight = h.rbRight, nil
|
|
||||||
x.rbBlack = h.rbBlack
|
|
||||||
h = x
|
|
||||||
} else { // Else, @key is bigger than @h.key
|
|
||||||
h.rbRight = ns.rbDeleteNode(h.rbRight, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rbFixup(h)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *lruNs) deleteNode(key uint64) {
|
|
||||||
ns.rbRoot = ns.rbDeleteNode(ns.rbRoot, key)
|
|
||||||
if ns.rbRoot != nil {
|
|
||||||
ns.rbRoot.rbBlack = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *lruNs) rbIterateNodes(h *lruNode, pivot uint64, iter func(n *lruNode) bool) bool {
|
|
||||||
if h == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if h.key >= pivot {
|
|
||||||
if !ns.rbIterateNodes(h.rbLeft, pivot, iter) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if !iter(h) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ns.rbIterateNodes(h.rbRight, pivot, iter)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *lruNs) iterateNodes(iter func(n *lruNode) bool) {
|
|
||||||
ns.rbIterateNodes(ns.rbRoot, 0, iter)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *lruNs) Get(key uint64, setf SetFunc) Handle {
|
|
||||||
ns.lru.mu.Lock()
|
|
||||||
defer ns.lru.mu.Unlock()
|
|
||||||
|
|
||||||
if ns.state != nsEffective {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var n *lruNode
|
|
||||||
if setf == nil {
|
|
||||||
n = ns.getNode(key)
|
|
||||||
if n == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
n = ns.getOrCreateNode(key)
|
|
||||||
}
|
|
||||||
switch n.state {
|
|
||||||
case nodeZero:
|
|
||||||
charge, value := setf()
|
|
||||||
if value == nil {
|
|
||||||
ns.deleteNode(key)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if charge < 0 {
|
|
||||||
charge = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
n.value = value
|
|
||||||
n.charge = charge
|
|
||||||
n.state = nodeEvicted
|
|
||||||
|
|
||||||
ns.lru.size += charge
|
|
||||||
ns.lru.alive++
|
|
||||||
|
|
||||||
fallthrough
|
|
||||||
case nodeEvicted:
|
|
||||||
if n.charge == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert to recent list.
|
|
||||||
n.state = nodeEffective
|
|
||||||
n.ref++
|
|
||||||
ns.lru.used += n.charge
|
|
||||||
ns.lru.evict()
|
|
||||||
|
|
||||||
fallthrough
|
|
||||||
case nodeEffective:
|
|
||||||
// Bump to front.
|
|
||||||
n.rRemove()
|
|
||||||
n.rInsert(&ns.lru.recent)
|
|
||||||
case nodeDeleted:
|
|
||||||
// Do nothing.
|
|
||||||
default:
|
|
||||||
panic("invalid state")
|
|
||||||
}
|
|
||||||
n.ref++
|
|
||||||
|
|
||||||
return &lruHandle{node: n}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *lruNs) Delete(key uint64, fin DelFin) bool {
|
|
||||||
ns.lru.mu.Lock()
|
|
||||||
defer ns.lru.mu.Unlock()
|
|
||||||
|
|
||||||
if ns.state != nsEffective {
|
|
||||||
if fin != nil {
|
|
||||||
fin(false, false)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
n := ns.getNode(key)
|
|
||||||
if n == nil {
|
|
||||||
if fin != nil {
|
|
||||||
fin(false, false)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
switch n.state {
|
|
||||||
case nodeEffective:
|
|
||||||
ns.lru.used -= n.charge
|
|
||||||
n.state = nodeDeleted
|
|
||||||
n.delfin = fin
|
|
||||||
n.rRemove()
|
|
||||||
n.derefNB()
|
|
||||||
case nodeEvicted:
|
|
||||||
n.state = nodeDeleted
|
|
||||||
n.delfin = fin
|
|
||||||
case nodeDeleted:
|
|
||||||
if fin != nil {
|
|
||||||
fin(true, true)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
default:
|
|
||||||
panic("invalid state")
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *lruNs) purgeNB(fin PurgeFin) {
|
|
||||||
if ns.state == nsEffective {
|
|
||||||
var nodes []*lruNode
|
|
||||||
ns.iterateNodes(func(n *lruNode) bool {
|
|
||||||
nodes = append(nodes, n)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
for _, n := range nodes {
|
|
||||||
switch n.state {
|
|
||||||
case nodeEffective:
|
|
||||||
ns.lru.used -= n.charge
|
|
||||||
n.state = nodeDeleted
|
|
||||||
n.purgefin = fin
|
|
||||||
n.rRemove()
|
|
||||||
n.derefNB()
|
|
||||||
case nodeEvicted:
|
|
||||||
n.state = nodeDeleted
|
|
||||||
n.purgefin = fin
|
|
||||||
case nodeDeleted:
|
|
||||||
default:
|
|
||||||
panic("invalid state")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *lruNs) Purge(fin PurgeFin) {
|
|
||||||
ns.lru.mu.Lock()
|
|
||||||
ns.purgeNB(fin)
|
|
||||||
ns.lru.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *lruNs) zapNB() {
|
|
||||||
if ns.state == nsEffective {
|
|
||||||
ns.state = nsZapped
|
|
||||||
|
|
||||||
ns.iterateNodes(func(n *lruNode) bool {
|
|
||||||
if n.state == nodeEffective {
|
|
||||||
ns.lru.used -= n.charge
|
|
||||||
n.rRemove()
|
|
||||||
}
|
|
||||||
ns.lru.size -= n.charge
|
|
||||||
n.state = nodeDeleted
|
|
||||||
n.fin()
|
|
||||||
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
ns.rbRoot = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *lruNs) Zap() {
|
|
||||||
ns.lru.mu.Lock()
|
|
||||||
ns.zapNB()
|
|
||||||
delete(ns.lru.table, ns.id)
|
|
||||||
ns.lru.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
type lruNode struct {
|
|
||||||
ns *lruNs
|
|
||||||
|
|
||||||
rNext, rPrev *lruNode
|
|
||||||
rbLeft, rbRight *lruNode
|
|
||||||
rbBlack bool
|
|
||||||
|
|
||||||
key uint64
|
|
||||||
value interface{}
|
|
||||||
charge int
|
|
||||||
ref int
|
|
||||||
state nodeState
|
|
||||||
delfin DelFin
|
|
||||||
purgefin PurgeFin
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *lruNode) rInsert(at *lruNode) {
|
|
||||||
x := at.rNext
|
|
||||||
at.rNext = n
|
|
||||||
n.rPrev = at
|
|
||||||
n.rNext = x
|
|
||||||
x.rPrev = n
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *lruNode) rRemove() bool {
|
|
||||||
if n.rPrev == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
n.rPrev.rNext = n.rNext
|
|
||||||
n.rNext.rPrev = n.rPrev
|
|
||||||
n.rPrev = nil
|
|
||||||
n.rNext = nil
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *lruNode) fin() {
|
|
||||||
if r, ok := n.value.(util.Releaser); ok {
|
|
||||||
r.Release()
|
|
||||||
}
|
|
||||||
if n.purgefin != nil {
|
|
||||||
if n.delfin != nil {
|
|
||||||
panic("conflicting delete and purge fin")
|
|
||||||
}
|
|
||||||
n.purgefin(n.ns.id, n.key)
|
|
||||||
n.purgefin = nil
|
|
||||||
} else if n.delfin != nil {
|
|
||||||
n.delfin(true, false)
|
|
||||||
n.delfin = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *lruNode) derefNB() {
|
|
||||||
n.ref--
|
|
||||||
if n.ref == 0 {
|
|
||||||
if n.ns.state == nsEffective {
|
|
||||||
// Remove elemement.
|
|
||||||
n.ns.deleteNode(n.key)
|
|
||||||
n.ns.lru.size -= n.charge
|
|
||||||
n.ns.lru.alive--
|
|
||||||
n.fin()
|
|
||||||
}
|
|
||||||
n.value = nil
|
|
||||||
} else if n.ref < 0 {
|
|
||||||
panic("leveldb/cache: lruCache: negative node reference")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *lruNode) deref() {
|
|
||||||
n.ns.lru.mu.Lock()
|
|
||||||
n.derefNB()
|
|
||||||
n.ns.lru.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
type lruHandle struct {
|
|
||||||
node *lruNode
|
|
||||||
once uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *lruHandle) Value() interface{} {
|
|
||||||
if atomic.LoadUint32(&h.once) == 0 {
|
|
||||||
return h.node.value
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *lruHandle) Release() {
|
|
||||||
if !atomic.CompareAndSwapUint32(&h.once, 0, 1) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
h.node.deref()
|
|
||||||
h.node = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func rbIsRed(h *lruNode) bool {
|
|
||||||
if h == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return !h.rbBlack
|
|
||||||
}
|
|
||||||
|
|
||||||
func rbRotLeft(h *lruNode) *lruNode {
|
|
||||||
x := h.rbRight
|
|
||||||
if x.rbBlack {
|
|
||||||
panic("rotating a black link")
|
|
||||||
}
|
|
||||||
h.rbRight = x.rbLeft
|
|
||||||
x.rbLeft = h
|
|
||||||
x.rbBlack = h.rbBlack
|
|
||||||
h.rbBlack = false
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
func rbRotRight(h *lruNode) *lruNode {
|
|
||||||
x := h.rbLeft
|
|
||||||
if x.rbBlack {
|
|
||||||
panic("rotating a black link")
|
|
||||||
}
|
|
||||||
h.rbLeft = x.rbRight
|
|
||||||
x.rbRight = h
|
|
||||||
x.rbBlack = h.rbBlack
|
|
||||||
h.rbBlack = false
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
func rbFlip(h *lruNode) {
|
|
||||||
h.rbBlack = !h.rbBlack
|
|
||||||
h.rbLeft.rbBlack = !h.rbLeft.rbBlack
|
|
||||||
h.rbRight.rbBlack = !h.rbRight.rbBlack
|
|
||||||
}
|
|
||||||
|
|
||||||
func rbMoveLeft(h *lruNode) *lruNode {
|
|
||||||
rbFlip(h)
|
|
||||||
if rbIsRed(h.rbRight.rbLeft) {
|
|
||||||
h.rbRight = rbRotRight(h.rbRight)
|
|
||||||
h = rbRotLeft(h)
|
|
||||||
rbFlip(h)
|
|
||||||
}
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
func rbMoveRight(h *lruNode) *lruNode {
|
|
||||||
rbFlip(h)
|
|
||||||
if rbIsRed(h.rbLeft.rbLeft) {
|
|
||||||
h = rbRotRight(h)
|
|
||||||
rbFlip(h)
|
|
||||||
}
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
func rbFixup(h *lruNode) *lruNode {
|
|
||||||
if rbIsRed(h.rbRight) {
|
|
||||||
h = rbRotLeft(h)
|
|
||||||
}
|
|
||||||
|
|
||||||
if rbIsRed(h.rbLeft) && rbIsRed(h.rbLeft.rbLeft) {
|
|
||||||
h = rbRotRight(h)
|
|
||||||
}
|
|
||||||
|
|
||||||
if rbIsRed(h.rbLeft) && rbIsRed(h.rbRight) {
|
|
||||||
rbFlip(h)
|
|
||||||
}
|
|
||||||
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
func rbDeleteMin(h *lruNode) (hn, n *lruNode) {
|
|
||||||
if h == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
if h.rbLeft == nil {
|
|
||||||
return nil, h
|
|
||||||
}
|
|
||||||
|
|
||||||
if !rbIsRed(h.rbLeft) && !rbIsRed(h.rbLeft.rbLeft) {
|
|
||||||
h = rbMoveLeft(h)
|
|
||||||
}
|
|
||||||
|
|
||||||
h.rbLeft, n = rbDeleteMin(h.rbLeft)
|
|
||||||
|
|
||||||
return rbFixup(h), n
|
|
||||||
}
|
|
||||||
+52
-24
@@ -9,13 +9,12 @@ package leveldb
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/syndtr/goleveldb/leveldb/filter"
|
||||||
|
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||||
|
"github.com/syndtr/goleveldb/leveldb/storage"
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/syndtr/goleveldb/leveldb/cache"
|
|
||||||
"github.com/syndtr/goleveldb/leveldb/opt"
|
|
||||||
"github.com/syndtr/goleveldb/leveldb/storage"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const ctValSize = 1000
|
const ctValSize = 1000
|
||||||
@@ -32,8 +31,8 @@ func newDbCorruptHarnessWopt(t *testing.T, o *opt.Options) *dbCorruptHarness {
|
|||||||
|
|
||||||
func newDbCorruptHarness(t *testing.T) *dbCorruptHarness {
|
func newDbCorruptHarness(t *testing.T) *dbCorruptHarness {
|
||||||
return newDbCorruptHarnessWopt(t, &opt.Options{
|
return newDbCorruptHarnessWopt(t, &opt.Options{
|
||||||
BlockCache: cache.NewLRUCache(100),
|
BlockCacheCapacity: 100,
|
||||||
Strict: opt.StrictJournalChecksum,
|
Strict: opt.StrictJournalChecksum,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,21 +95,22 @@ func (h *dbCorruptHarness) deleteRand(n, max int, rnd *rand.Rand) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *dbCorruptHarness) corrupt(ft storage.FileType, offset, n int) {
|
func (h *dbCorruptHarness) corrupt(ft storage.FileType, fi, offset, n int) {
|
||||||
p := &h.dbHarness
|
p := &h.dbHarness
|
||||||
t := p.t
|
t := p.t
|
||||||
|
|
||||||
var file storage.File
|
|
||||||
ff, _ := p.stor.GetFiles(ft)
|
ff, _ := p.stor.GetFiles(ft)
|
||||||
for _, f := range ff {
|
sff := files(ff)
|
||||||
if file == nil || f.Num() > file.Num() {
|
sff.sort()
|
||||||
file = f
|
if fi < 0 {
|
||||||
}
|
fi = len(sff) - 1
|
||||||
}
|
}
|
||||||
if file == nil {
|
if fi >= len(sff) {
|
||||||
t.Fatalf("no such file with type %q", ft)
|
t.Fatalf("no such file with type %q with index %d", ft, fi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
file := sff[fi]
|
||||||
|
|
||||||
r, err := file.Open()
|
r, err := file.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("cannot open file: ", err)
|
t.Fatal("cannot open file: ", err)
|
||||||
@@ -225,8 +225,8 @@ func TestCorruptDB_Journal(t *testing.T) {
|
|||||||
h.build(100)
|
h.build(100)
|
||||||
h.check(100, 100)
|
h.check(100, 100)
|
||||||
h.closeDB()
|
h.closeDB()
|
||||||
h.corrupt(storage.TypeJournal, 19, 1)
|
h.corrupt(storage.TypeJournal, -1, 19, 1)
|
||||||
h.corrupt(storage.TypeJournal, 32*1024+1000, 1)
|
h.corrupt(storage.TypeJournal, -1, 32*1024+1000, 1)
|
||||||
|
|
||||||
h.openDB()
|
h.openDB()
|
||||||
h.check(36, 36)
|
h.check(36, 36)
|
||||||
@@ -242,7 +242,7 @@ func TestCorruptDB_Table(t *testing.T) {
|
|||||||
h.compactRangeAt(0, "", "")
|
h.compactRangeAt(0, "", "")
|
||||||
h.compactRangeAt(1, "", "")
|
h.compactRangeAt(1, "", "")
|
||||||
h.closeDB()
|
h.closeDB()
|
||||||
h.corrupt(storage.TypeTable, 100, 1)
|
h.corrupt(storage.TypeTable, -1, 100, 1)
|
||||||
|
|
||||||
h.openDB()
|
h.openDB()
|
||||||
h.check(99, 99)
|
h.check(99, 99)
|
||||||
@@ -256,7 +256,7 @@ func TestCorruptDB_TableIndex(t *testing.T) {
|
|||||||
h.build(10000)
|
h.build(10000)
|
||||||
h.compactMem()
|
h.compactMem()
|
||||||
h.closeDB()
|
h.closeDB()
|
||||||
h.corrupt(storage.TypeTable, -2000, 500)
|
h.corrupt(storage.TypeTable, -1, -2000, 500)
|
||||||
|
|
||||||
h.openDB()
|
h.openDB()
|
||||||
h.check(5000, 9999)
|
h.check(5000, 9999)
|
||||||
@@ -267,9 +267,9 @@ func TestCorruptDB_TableIndex(t *testing.T) {
|
|||||||
func TestCorruptDB_MissingManifest(t *testing.T) {
|
func TestCorruptDB_MissingManifest(t *testing.T) {
|
||||||
rnd := rand.New(rand.NewSource(0x0badda7a))
|
rnd := rand.New(rand.NewSource(0x0badda7a))
|
||||||
h := newDbCorruptHarnessWopt(t, &opt.Options{
|
h := newDbCorruptHarnessWopt(t, &opt.Options{
|
||||||
BlockCache: cache.NewLRUCache(100),
|
BlockCacheCapacity: 100,
|
||||||
Strict: opt.StrictJournalChecksum,
|
Strict: opt.StrictJournalChecksum,
|
||||||
WriteBuffer: 1000 * 60,
|
WriteBuffer: 1000 * 60,
|
||||||
})
|
})
|
||||||
|
|
||||||
h.build(1000)
|
h.build(1000)
|
||||||
@@ -355,7 +355,7 @@ func TestCorruptDB_CorruptedManifest(t *testing.T) {
|
|||||||
h.compactMem()
|
h.compactMem()
|
||||||
h.compactRange("", "")
|
h.compactRange("", "")
|
||||||
h.closeDB()
|
h.closeDB()
|
||||||
h.corrupt(storage.TypeManifest, 0, 1000)
|
h.corrupt(storage.TypeManifest, -1, 0, 1000)
|
||||||
h.openAssert(false)
|
h.openAssert(false)
|
||||||
|
|
||||||
h.recover()
|
h.recover()
|
||||||
@@ -370,7 +370,7 @@ func TestCorruptDB_CompactionInputError(t *testing.T) {
|
|||||||
h.build(10)
|
h.build(10)
|
||||||
h.compactMem()
|
h.compactMem()
|
||||||
h.closeDB()
|
h.closeDB()
|
||||||
h.corrupt(storage.TypeTable, 100, 1)
|
h.corrupt(storage.TypeTable, -1, 100, 1)
|
||||||
|
|
||||||
h.openDB()
|
h.openDB()
|
||||||
h.check(9, 9)
|
h.check(9, 9)
|
||||||
@@ -387,7 +387,7 @@ func TestCorruptDB_UnrelatedKeys(t *testing.T) {
|
|||||||
h.build(10)
|
h.build(10)
|
||||||
h.compactMem()
|
h.compactMem()
|
||||||
h.closeDB()
|
h.closeDB()
|
||||||
h.corrupt(storage.TypeTable, 100, 1)
|
h.corrupt(storage.TypeTable, -1, 100, 1)
|
||||||
|
|
||||||
h.openDB()
|
h.openDB()
|
||||||
h.put(string(tkey(1000)), string(tval(1000, ctValSize)))
|
h.put(string(tkey(1000)), string(tval(1000, ctValSize)))
|
||||||
@@ -470,3 +470,31 @@ func TestCorruptDB_MissingTableFiles(t *testing.T) {
|
|||||||
|
|
||||||
h.close()
|
h.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCorruptDB_RecoverTable(t *testing.T) {
|
||||||
|
h := newDbCorruptHarnessWopt(t, &opt.Options{
|
||||||
|
WriteBuffer: 112 * opt.KiB,
|
||||||
|
CompactionTableSize: 90 * opt.KiB,
|
||||||
|
Filter: filter.NewBloomFilter(10),
|
||||||
|
})
|
||||||
|
|
||||||
|
h.build(1000)
|
||||||
|
h.compactMem()
|
||||||
|
h.compactRangeAt(0, "", "")
|
||||||
|
h.compactRangeAt(1, "", "")
|
||||||
|
seq := h.db.seq
|
||||||
|
h.closeDB()
|
||||||
|
h.corrupt(storage.TypeTable, 0, 1000, 1)
|
||||||
|
h.corrupt(storage.TypeTable, 3, 10000, 1)
|
||||||
|
// Corrupted filter shouldn't affect recovery.
|
||||||
|
h.corrupt(storage.TypeTable, 3, 113888, 10)
|
||||||
|
h.corrupt(storage.TypeTable, -1, 20000, 1)
|
||||||
|
|
||||||
|
h.recover()
|
||||||
|
if h.db.seq != seq {
|
||||||
|
t.Errorf("invalid seq, want=%d got=%d", seq, h.db.seq)
|
||||||
|
}
|
||||||
|
h.check(985, 985)
|
||||||
|
|
||||||
|
h.close()
|
||||||
|
}
|
||||||
|
|||||||
+81
-16
@@ -269,7 +269,7 @@ func recoverTable(s *session, o *opt.Options) error {
|
|||||||
tableFiles.sort()
|
tableFiles.sort()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
mSeq uint64
|
maxSeq uint64
|
||||||
recoveredKey, goodKey, corruptedKey, corruptedBlock, droppedTable int
|
recoveredKey, goodKey, corruptedKey, corruptedBlock, droppedTable int
|
||||||
|
|
||||||
// We will drop corrupted table.
|
// We will drop corrupted table.
|
||||||
@@ -324,7 +324,12 @@ func recoverTable(s *session, o *opt.Options) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer reader.Close()
|
var closed bool
|
||||||
|
defer func() {
|
||||||
|
if !closed {
|
||||||
|
reader.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// Get file size.
|
// Get file size.
|
||||||
size, err := reader.Seek(0, 2)
|
size, err := reader.Seek(0, 2)
|
||||||
@@ -342,12 +347,14 @@ func recoverTable(s *session, o *opt.Options) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
iter := tr.NewIterator(nil, nil)
|
iter := tr.NewIterator(nil, nil)
|
||||||
iter.(iterator.ErrorCallbackSetter).SetErrorCallback(func(err error) {
|
if itererr, ok := iter.(iterator.ErrorCallbackSetter); ok {
|
||||||
if errors.IsCorrupted(err) {
|
itererr.SetErrorCallback(func(err error) {
|
||||||
s.logf("table@recovery block corruption @%d %q", file.Num(), err)
|
if errors.IsCorrupted(err) {
|
||||||
tcorruptedBlock++
|
s.logf("table@recovery block corruption @%d %q", file.Num(), err)
|
||||||
}
|
tcorruptedBlock++
|
||||||
})
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Scan the table.
|
// Scan the table.
|
||||||
for iter.Next() {
|
for iter.Next() {
|
||||||
@@ -392,14 +399,15 @@ func recoverTable(s *session, o *opt.Options) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
closed = true
|
||||||
reader.Close()
|
reader.Close()
|
||||||
if err := file.Replace(tmp); err != nil {
|
if err := file.Replace(tmp); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
size = newSize
|
size = newSize
|
||||||
}
|
}
|
||||||
if tSeq > mSeq {
|
if tSeq > maxSeq {
|
||||||
mSeq = tSeq
|
maxSeq = tSeq
|
||||||
}
|
}
|
||||||
recoveredKey += tgoodKey
|
recoveredKey += tgoodKey
|
||||||
// Add table to level 0.
|
// Add table to level 0.
|
||||||
@@ -426,11 +434,11 @@ func recoverTable(s *session, o *opt.Options) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logf("table@recovery recovered F·%d N·%d Gk·%d Ck·%d Q·%d", len(tableFiles), recoveredKey, goodKey, corruptedKey, mSeq)
|
s.logf("table@recovery recovered F·%d N·%d Gk·%d Ck·%d Q·%d", len(tableFiles), recoveredKey, goodKey, corruptedKey, maxSeq)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set sequence number.
|
// Set sequence number.
|
||||||
rec.setSeqNum(mSeq + 1)
|
rec.setSeqNum(maxSeq)
|
||||||
|
|
||||||
// Create new manifest.
|
// Create new manifest.
|
||||||
if err := s.create(); err != nil {
|
if err := s.create(); err != nil {
|
||||||
@@ -625,7 +633,7 @@ func (db *DB) get(key []byte, seq uint64, ro *opt.ReadOptions) (value []byte, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
v := db.s.version()
|
v := db.s.version()
|
||||||
value, cSched, err := v.get(ikey, ro)
|
value, cSched, err := v.get(ikey, ro, false)
|
||||||
v.release()
|
v.release()
|
||||||
if cSched {
|
if cSched {
|
||||||
// Trigger table compaction.
|
// Trigger table compaction.
|
||||||
@@ -634,8 +642,51 @@ func (db *DB) get(key []byte, seq uint64, ro *opt.ReadOptions) (value []byte, er
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *DB) has(key []byte, seq uint64, ro *opt.ReadOptions) (ret bool, err error) {
|
||||||
|
ikey := newIkey(key, seq, ktSeek)
|
||||||
|
|
||||||
|
em, fm := db.getMems()
|
||||||
|
for _, m := range [...]*memDB{em, fm} {
|
||||||
|
if m == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
defer m.decref()
|
||||||
|
|
||||||
|
mk, _, me := m.mdb.Find(ikey)
|
||||||
|
if me == nil {
|
||||||
|
ukey, _, kt, kerr := parseIkey(mk)
|
||||||
|
if kerr != nil {
|
||||||
|
// Shouldn't have had happen.
|
||||||
|
panic(kerr)
|
||||||
|
}
|
||||||
|
if db.s.icmp.uCompare(ukey, key) == 0 {
|
||||||
|
if kt == ktDel {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
} else if me != ErrNotFound {
|
||||||
|
return false, me
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
v := db.s.version()
|
||||||
|
_, cSched, err := v.get(ikey, ro, true)
|
||||||
|
v.release()
|
||||||
|
if cSched {
|
||||||
|
// Trigger table compaction.
|
||||||
|
db.compSendTrigger(db.tcompCmdC)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
ret = true
|
||||||
|
} else if err == ErrNotFound {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Get gets the value for the given key. It returns ErrNotFound if the
|
// Get gets the value for the given key. It returns ErrNotFound if the
|
||||||
// DB does not contain the key.
|
// DB does not contains the key.
|
||||||
//
|
//
|
||||||
// The returned slice is its own copy, it is safe to modify the contents
|
// The returned slice is its own copy, it is safe to modify the contents
|
||||||
// of the returned slice.
|
// of the returned slice.
|
||||||
@@ -651,6 +702,20 @@ func (db *DB) Get(key []byte, ro *opt.ReadOptions) (value []byte, err error) {
|
|||||||
return db.get(key, se.seq, ro)
|
return db.get(key, se.seq, ro)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Has returns true if the DB does contains the given key.
|
||||||
|
//
|
||||||
|
// It is safe to modify the contents of the argument after Get returns.
|
||||||
|
func (db *DB) Has(key []byte, ro *opt.ReadOptions) (ret bool, err error) {
|
||||||
|
err = db.ok()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
se := db.acquireSnapshot()
|
||||||
|
defer db.releaseSnapshot(se)
|
||||||
|
return db.has(key, se.seq, ro)
|
||||||
|
}
|
||||||
|
|
||||||
// NewIterator returns an iterator for the latest snapshot of the
|
// NewIterator returns an iterator for the latest snapshot of the
|
||||||
// uderlying DB.
|
// uderlying DB.
|
||||||
// The returned iterator is not goroutine-safe, but it is safe to use
|
// The returned iterator is not goroutine-safe, but it is safe to use
|
||||||
@@ -760,8 +825,8 @@ func (db *DB) GetProperty(name string) (value string, err error) {
|
|||||||
case p == "blockpool":
|
case p == "blockpool":
|
||||||
value = fmt.Sprintf("%v", db.s.tops.bpool)
|
value = fmt.Sprintf("%v", db.s.tops.bpool)
|
||||||
case p == "cachedblock":
|
case p == "cachedblock":
|
||||||
if bc := db.s.o.GetBlockCache(); bc != nil {
|
if db.s.tops.bcache != nil {
|
||||||
value = fmt.Sprintf("%d", bc.Size())
|
value = fmt.Sprintf("%d", db.s.tops.bcache.Size())
|
||||||
} else {
|
} else {
|
||||||
value = "<nil>"
|
value = "<nil>"
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-1
@@ -8,6 +8,7 @@ package leveldb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"container/list"
|
"container/list"
|
||||||
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
@@ -89,8 +90,12 @@ func (db *DB) newSnapshot() *Snapshot {
|
|||||||
return snap
|
return snap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (snap *Snapshot) String() string {
|
||||||
|
return fmt.Sprintf("leveldb.Snapshot{%d}", snap.elem.seq)
|
||||||
|
}
|
||||||
|
|
||||||
// Get gets the value for the given key. It returns ErrNotFound if
|
// Get gets the value for the given key. It returns ErrNotFound if
|
||||||
// the DB does not contain the key.
|
// the DB does not contains the key.
|
||||||
//
|
//
|
||||||
// The caller should not modify the contents of the returned slice, but
|
// The caller should not modify the contents of the returned slice, but
|
||||||
// it is safe to modify the contents of the argument after Get returns.
|
// it is safe to modify the contents of the argument after Get returns.
|
||||||
@@ -108,6 +113,23 @@ func (snap *Snapshot) Get(key []byte, ro *opt.ReadOptions) (value []byte, err er
|
|||||||
return snap.db.get(key, snap.elem.seq, ro)
|
return snap.db.get(key, snap.elem.seq, ro)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Has returns true if the DB does contains the given key.
|
||||||
|
//
|
||||||
|
// It is safe to modify the contents of the argument after Get returns.
|
||||||
|
func (snap *Snapshot) Has(key []byte, ro *opt.ReadOptions) (ret bool, err error) {
|
||||||
|
err = snap.db.ok()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
snap.mu.RLock()
|
||||||
|
defer snap.mu.RUnlock()
|
||||||
|
if snap.released {
|
||||||
|
err = ErrSnapshotReleased
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return snap.db.has(key, snap.elem.seq, ro)
|
||||||
|
}
|
||||||
|
|
||||||
// NewIterator returns an iterator for the snapshot of the uderlying DB.
|
// NewIterator returns an iterator for the snapshot of the uderlying DB.
|
||||||
// The returned iterator is not goroutine-safe, but it is safe to use
|
// The returned iterator is not goroutine-safe, but it is safe to use
|
||||||
// multiple iterators concurrently, with each in a dedicated goroutine.
|
// multiple iterators concurrently, with each in a dedicated goroutine.
|
||||||
|
|||||||
+74
-63
@@ -530,7 +530,7 @@ func Test_FieldsAligned(t *testing.T) {
|
|||||||
testAligned(t, "session.stSeqNum", unsafe.Offsetof(p2.stSeqNum))
|
testAligned(t, "session.stSeqNum", unsafe.Offsetof(p2.stSeqNum))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_Locking(t *testing.T) {
|
func TestDB_Locking(t *testing.T) {
|
||||||
h := newDbHarness(t)
|
h := newDbHarness(t)
|
||||||
defer h.stor.Close()
|
defer h.stor.Close()
|
||||||
h.openAssert(false)
|
h.openAssert(false)
|
||||||
@@ -538,7 +538,7 @@ func TestDb_Locking(t *testing.T) {
|
|||||||
h.openAssert(true)
|
h.openAssert(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_Empty(t *testing.T) {
|
func TestDB_Empty(t *testing.T) {
|
||||||
trun(t, func(h *dbHarness) {
|
trun(t, func(h *dbHarness) {
|
||||||
h.get("foo", false)
|
h.get("foo", false)
|
||||||
|
|
||||||
@@ -547,7 +547,7 @@ func TestDb_Empty(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_ReadWrite(t *testing.T) {
|
func TestDB_ReadWrite(t *testing.T) {
|
||||||
trun(t, func(h *dbHarness) {
|
trun(t, func(h *dbHarness) {
|
||||||
h.put("foo", "v1")
|
h.put("foo", "v1")
|
||||||
h.getVal("foo", "v1")
|
h.getVal("foo", "v1")
|
||||||
@@ -562,7 +562,7 @@ func TestDb_ReadWrite(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_PutDeleteGet(t *testing.T) {
|
func TestDB_PutDeleteGet(t *testing.T) {
|
||||||
trun(t, func(h *dbHarness) {
|
trun(t, func(h *dbHarness) {
|
||||||
h.put("foo", "v1")
|
h.put("foo", "v1")
|
||||||
h.getVal("foo", "v1")
|
h.getVal("foo", "v1")
|
||||||
@@ -576,7 +576,7 @@ func TestDb_PutDeleteGet(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_EmptyBatch(t *testing.T) {
|
func TestDB_EmptyBatch(t *testing.T) {
|
||||||
h := newDbHarness(t)
|
h := newDbHarness(t)
|
||||||
defer h.close()
|
defer h.close()
|
||||||
|
|
||||||
@@ -588,7 +588,7 @@ func TestDb_EmptyBatch(t *testing.T) {
|
|||||||
h.get("foo", false)
|
h.get("foo", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_GetFromFrozen(t *testing.T) {
|
func TestDB_GetFromFrozen(t *testing.T) {
|
||||||
h := newDbHarnessWopt(t, &opt.Options{WriteBuffer: 100100})
|
h := newDbHarnessWopt(t, &opt.Options{WriteBuffer: 100100})
|
||||||
defer h.close()
|
defer h.close()
|
||||||
|
|
||||||
@@ -614,7 +614,7 @@ func TestDb_GetFromFrozen(t *testing.T) {
|
|||||||
h.get("k2", true)
|
h.get("k2", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_GetFromTable(t *testing.T) {
|
func TestDB_GetFromTable(t *testing.T) {
|
||||||
trun(t, func(h *dbHarness) {
|
trun(t, func(h *dbHarness) {
|
||||||
h.put("foo", "v1")
|
h.put("foo", "v1")
|
||||||
h.compactMem()
|
h.compactMem()
|
||||||
@@ -622,7 +622,7 @@ func TestDb_GetFromTable(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_GetSnapshot(t *testing.T) {
|
func TestDB_GetSnapshot(t *testing.T) {
|
||||||
trun(t, func(h *dbHarness) {
|
trun(t, func(h *dbHarness) {
|
||||||
bar := strings.Repeat("b", 200)
|
bar := strings.Repeat("b", 200)
|
||||||
h.put("foo", "v1")
|
h.put("foo", "v1")
|
||||||
@@ -656,7 +656,7 @@ func TestDb_GetSnapshot(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_GetLevel0Ordering(t *testing.T) {
|
func TestDB_GetLevel0Ordering(t *testing.T) {
|
||||||
trun(t, func(h *dbHarness) {
|
trun(t, func(h *dbHarness) {
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
h.put("bar", fmt.Sprintf("b%d", i))
|
h.put("bar", fmt.Sprintf("b%d", i))
|
||||||
@@ -679,7 +679,7 @@ func TestDb_GetLevel0Ordering(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_GetOrderedByLevels(t *testing.T) {
|
func TestDB_GetOrderedByLevels(t *testing.T) {
|
||||||
trun(t, func(h *dbHarness) {
|
trun(t, func(h *dbHarness) {
|
||||||
h.put("foo", "v1")
|
h.put("foo", "v1")
|
||||||
h.compactMem()
|
h.compactMem()
|
||||||
@@ -691,7 +691,7 @@ func TestDb_GetOrderedByLevels(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_GetPicksCorrectFile(t *testing.T) {
|
func TestDB_GetPicksCorrectFile(t *testing.T) {
|
||||||
trun(t, func(h *dbHarness) {
|
trun(t, func(h *dbHarness) {
|
||||||
// Arrange to have multiple files in a non-level-0 level.
|
// Arrange to have multiple files in a non-level-0 level.
|
||||||
h.put("a", "va")
|
h.put("a", "va")
|
||||||
@@ -715,7 +715,7 @@ func TestDb_GetPicksCorrectFile(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_GetEncountersEmptyLevel(t *testing.T) {
|
func TestDB_GetEncountersEmptyLevel(t *testing.T) {
|
||||||
trun(t, func(h *dbHarness) {
|
trun(t, func(h *dbHarness) {
|
||||||
// Arrange for the following to happen:
|
// Arrange for the following to happen:
|
||||||
// * sstable A in level 0
|
// * sstable A in level 0
|
||||||
@@ -770,7 +770,7 @@ func TestDb_GetEncountersEmptyLevel(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_IterMultiWithDelete(t *testing.T) {
|
func TestDB_IterMultiWithDelete(t *testing.T) {
|
||||||
trun(t, func(h *dbHarness) {
|
trun(t, func(h *dbHarness) {
|
||||||
h.put("a", "va")
|
h.put("a", "va")
|
||||||
h.put("b", "vb")
|
h.put("b", "vb")
|
||||||
@@ -796,7 +796,7 @@ func TestDb_IterMultiWithDelete(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_IteratorPinsRef(t *testing.T) {
|
func TestDB_IteratorPinsRef(t *testing.T) {
|
||||||
h := newDbHarness(t)
|
h := newDbHarness(t)
|
||||||
defer h.close()
|
defer h.close()
|
||||||
|
|
||||||
@@ -820,7 +820,7 @@ func TestDb_IteratorPinsRef(t *testing.T) {
|
|||||||
iter.Release()
|
iter.Release()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_Recover(t *testing.T) {
|
func TestDB_Recover(t *testing.T) {
|
||||||
trun(t, func(h *dbHarness) {
|
trun(t, func(h *dbHarness) {
|
||||||
h.put("foo", "v1")
|
h.put("foo", "v1")
|
||||||
h.put("baz", "v5")
|
h.put("baz", "v5")
|
||||||
@@ -842,7 +842,7 @@ func TestDb_Recover(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_RecoverWithEmptyJournal(t *testing.T) {
|
func TestDB_RecoverWithEmptyJournal(t *testing.T) {
|
||||||
trun(t, func(h *dbHarness) {
|
trun(t, func(h *dbHarness) {
|
||||||
h.put("foo", "v1")
|
h.put("foo", "v1")
|
||||||
h.put("foo", "v2")
|
h.put("foo", "v2")
|
||||||
@@ -856,7 +856,7 @@ func TestDb_RecoverWithEmptyJournal(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_RecoverDuringMemtableCompaction(t *testing.T) {
|
func TestDB_RecoverDuringMemtableCompaction(t *testing.T) {
|
||||||
truno(t, &opt.Options{WriteBuffer: 1000000}, func(h *dbHarness) {
|
truno(t, &opt.Options{WriteBuffer: 1000000}, func(h *dbHarness) {
|
||||||
|
|
||||||
h.stor.DelaySync(storage.TypeTable)
|
h.stor.DelaySync(storage.TypeTable)
|
||||||
@@ -872,7 +872,7 @@ func TestDb_RecoverDuringMemtableCompaction(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_MinorCompactionsHappen(t *testing.T) {
|
func TestDB_MinorCompactionsHappen(t *testing.T) {
|
||||||
h := newDbHarnessWopt(t, &opt.Options{WriteBuffer: 10000})
|
h := newDbHarnessWopt(t, &opt.Options{WriteBuffer: 10000})
|
||||||
defer h.close()
|
defer h.close()
|
||||||
|
|
||||||
@@ -896,7 +896,7 @@ func TestDb_MinorCompactionsHappen(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_RecoverWithLargeJournal(t *testing.T) {
|
func TestDB_RecoverWithLargeJournal(t *testing.T) {
|
||||||
h := newDbHarness(t)
|
h := newDbHarness(t)
|
||||||
defer h.close()
|
defer h.close()
|
||||||
|
|
||||||
@@ -921,7 +921,7 @@ func TestDb_RecoverWithLargeJournal(t *testing.T) {
|
|||||||
v.release()
|
v.release()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_CompactionsGenerateMultipleFiles(t *testing.T) {
|
func TestDB_CompactionsGenerateMultipleFiles(t *testing.T) {
|
||||||
h := newDbHarnessWopt(t, &opt.Options{
|
h := newDbHarnessWopt(t, &opt.Options{
|
||||||
WriteBuffer: 10000000,
|
WriteBuffer: 10000000,
|
||||||
Compression: opt.NoCompression,
|
Compression: opt.NoCompression,
|
||||||
@@ -959,7 +959,7 @@ func TestDb_CompactionsGenerateMultipleFiles(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_RepeatedWritesToSameKey(t *testing.T) {
|
func TestDB_RepeatedWritesToSameKey(t *testing.T) {
|
||||||
h := newDbHarnessWopt(t, &opt.Options{WriteBuffer: 100000})
|
h := newDbHarnessWopt(t, &opt.Options{WriteBuffer: 100000})
|
||||||
defer h.close()
|
defer h.close()
|
||||||
|
|
||||||
@@ -975,7 +975,7 @@ func TestDb_RepeatedWritesToSameKey(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_RepeatedWritesToSameKeyAfterReopen(t *testing.T) {
|
func TestDB_RepeatedWritesToSameKeyAfterReopen(t *testing.T) {
|
||||||
h := newDbHarnessWopt(t, &opt.Options{WriteBuffer: 100000})
|
h := newDbHarnessWopt(t, &opt.Options{WriteBuffer: 100000})
|
||||||
defer h.close()
|
defer h.close()
|
||||||
|
|
||||||
@@ -993,7 +993,7 @@ func TestDb_RepeatedWritesToSameKeyAfterReopen(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_SparseMerge(t *testing.T) {
|
func TestDB_SparseMerge(t *testing.T) {
|
||||||
h := newDbHarnessWopt(t, &opt.Options{Compression: opt.NoCompression})
|
h := newDbHarnessWopt(t, &opt.Options{Compression: opt.NoCompression})
|
||||||
defer h.close()
|
defer h.close()
|
||||||
|
|
||||||
@@ -1031,7 +1031,7 @@ func TestDb_SparseMerge(t *testing.T) {
|
|||||||
h.maxNextLevelOverlappingBytes(20 * 1048576)
|
h.maxNextLevelOverlappingBytes(20 * 1048576)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_SizeOf(t *testing.T) {
|
func TestDB_SizeOf(t *testing.T) {
|
||||||
h := newDbHarnessWopt(t, &opt.Options{
|
h := newDbHarnessWopt(t, &opt.Options{
|
||||||
Compression: opt.NoCompression,
|
Compression: opt.NoCompression,
|
||||||
WriteBuffer: 10000000,
|
WriteBuffer: 10000000,
|
||||||
@@ -1081,7 +1081,7 @@ func TestDb_SizeOf(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_SizeOf_MixOfSmallAndLarge(t *testing.T) {
|
func TestDB_SizeOf_MixOfSmallAndLarge(t *testing.T) {
|
||||||
h := newDbHarnessWopt(t, &opt.Options{Compression: opt.NoCompression})
|
h := newDbHarnessWopt(t, &opt.Options{Compression: opt.NoCompression})
|
||||||
defer h.close()
|
defer h.close()
|
||||||
|
|
||||||
@@ -1119,7 +1119,7 @@ func TestDb_SizeOf_MixOfSmallAndLarge(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_Snapshot(t *testing.T) {
|
func TestDB_Snapshot(t *testing.T) {
|
||||||
trun(t, func(h *dbHarness) {
|
trun(t, func(h *dbHarness) {
|
||||||
h.put("foo", "v1")
|
h.put("foo", "v1")
|
||||||
s1 := h.getSnapshot()
|
s1 := h.getSnapshot()
|
||||||
@@ -1148,7 +1148,7 @@ func TestDb_Snapshot(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_SnapshotList(t *testing.T) {
|
func TestDB_SnapshotList(t *testing.T) {
|
||||||
db := &DB{snapsList: list.New()}
|
db := &DB{snapsList: list.New()}
|
||||||
e0a := db.acquireSnapshot()
|
e0a := db.acquireSnapshot()
|
||||||
e0b := db.acquireSnapshot()
|
e0b := db.acquireSnapshot()
|
||||||
@@ -1186,7 +1186,7 @@ func TestDb_SnapshotList(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_HiddenValuesAreRemoved(t *testing.T) {
|
func TestDB_HiddenValuesAreRemoved(t *testing.T) {
|
||||||
trun(t, func(h *dbHarness) {
|
trun(t, func(h *dbHarness) {
|
||||||
s := h.db.s
|
s := h.db.s
|
||||||
|
|
||||||
@@ -1229,7 +1229,7 @@ func TestDb_HiddenValuesAreRemoved(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_DeletionMarkers2(t *testing.T) {
|
func TestDB_DeletionMarkers2(t *testing.T) {
|
||||||
h := newDbHarness(t)
|
h := newDbHarness(t)
|
||||||
defer h.close()
|
defer h.close()
|
||||||
s := h.db.s
|
s := h.db.s
|
||||||
@@ -1270,8 +1270,8 @@ func TestDb_DeletionMarkers2(t *testing.T) {
|
|||||||
h.allEntriesFor("foo", "[ ]")
|
h.allEntriesFor("foo", "[ ]")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_CompactionTableOpenError(t *testing.T) {
|
func TestDB_CompactionTableOpenError(t *testing.T) {
|
||||||
h := newDbHarnessWopt(t, &opt.Options{CachedOpenFiles: -1})
|
h := newDbHarnessWopt(t, &opt.Options{OpenFilesCacheCapacity: -1})
|
||||||
defer h.close()
|
defer h.close()
|
||||||
|
|
||||||
im := 10
|
im := 10
|
||||||
@@ -1305,7 +1305,7 @@ func TestDb_CompactionTableOpenError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_OverlapInLevel0(t *testing.T) {
|
func TestDB_OverlapInLevel0(t *testing.T) {
|
||||||
trun(t, func(h *dbHarness) {
|
trun(t, func(h *dbHarness) {
|
||||||
if h.o.GetMaxMemCompationLevel() != 2 {
|
if h.o.GetMaxMemCompationLevel() != 2 {
|
||||||
t.Fatal("fix test to reflect the config")
|
t.Fatal("fix test to reflect the config")
|
||||||
@@ -1348,7 +1348,7 @@ func TestDb_OverlapInLevel0(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_L0_CompactionBug_Issue44_a(t *testing.T) {
|
func TestDB_L0_CompactionBug_Issue44_a(t *testing.T) {
|
||||||
h := newDbHarness(t)
|
h := newDbHarness(t)
|
||||||
defer h.close()
|
defer h.close()
|
||||||
|
|
||||||
@@ -1368,7 +1368,7 @@ func TestDb_L0_CompactionBug_Issue44_a(t *testing.T) {
|
|||||||
h.getKeyVal("(a->v)")
|
h.getKeyVal("(a->v)")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_L0_CompactionBug_Issue44_b(t *testing.T) {
|
func TestDB_L0_CompactionBug_Issue44_b(t *testing.T) {
|
||||||
h := newDbHarness(t)
|
h := newDbHarness(t)
|
||||||
defer h.close()
|
defer h.close()
|
||||||
|
|
||||||
@@ -1397,7 +1397,7 @@ func TestDb_L0_CompactionBug_Issue44_b(t *testing.T) {
|
|||||||
h.getKeyVal("(->)(c->cv)")
|
h.getKeyVal("(->)(c->cv)")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_SingleEntryMemCompaction(t *testing.T) {
|
func TestDB_SingleEntryMemCompaction(t *testing.T) {
|
||||||
trun(t, func(h *dbHarness) {
|
trun(t, func(h *dbHarness) {
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
h.put("big", strings.Repeat("v", opt.DefaultWriteBuffer))
|
h.put("big", strings.Repeat("v", opt.DefaultWriteBuffer))
|
||||||
@@ -1414,7 +1414,7 @@ func TestDb_SingleEntryMemCompaction(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_ManifestWriteError(t *testing.T) {
|
func TestDB_ManifestWriteError(t *testing.T) {
|
||||||
for i := 0; i < 2; i++ {
|
for i := 0; i < 2; i++ {
|
||||||
func() {
|
func() {
|
||||||
h := newDbHarness(t)
|
h := newDbHarness(t)
|
||||||
@@ -1464,7 +1464,7 @@ func assertErr(t *testing.T, err error, wanterr bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_ClosedIsClosed(t *testing.T) {
|
func TestDB_ClosedIsClosed(t *testing.T) {
|
||||||
h := newDbHarness(t)
|
h := newDbHarness(t)
|
||||||
db := h.db
|
db := h.db
|
||||||
|
|
||||||
@@ -1559,7 +1559,7 @@ func (p numberComparer) Compare(a, b []byte) int {
|
|||||||
func (numberComparer) Separator(dst, a, b []byte) []byte { return nil }
|
func (numberComparer) Separator(dst, a, b []byte) []byte { return nil }
|
||||||
func (numberComparer) Successor(dst, b []byte) []byte { return nil }
|
func (numberComparer) Successor(dst, b []byte) []byte { return nil }
|
||||||
|
|
||||||
func TestDb_CustomComparer(t *testing.T) {
|
func TestDB_CustomComparer(t *testing.T) {
|
||||||
h := newDbHarnessWopt(t, &opt.Options{
|
h := newDbHarnessWopt(t, &opt.Options{
|
||||||
Comparer: numberComparer{},
|
Comparer: numberComparer{},
|
||||||
WriteBuffer: 1000,
|
WriteBuffer: 1000,
|
||||||
@@ -1589,7 +1589,7 @@ func TestDb_CustomComparer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_ManualCompaction(t *testing.T) {
|
func TestDB_ManualCompaction(t *testing.T) {
|
||||||
h := newDbHarness(t)
|
h := newDbHarness(t)
|
||||||
defer h.close()
|
defer h.close()
|
||||||
|
|
||||||
@@ -1627,10 +1627,10 @@ func TestDb_ManualCompaction(t *testing.T) {
|
|||||||
h.tablesPerLevel("0,0,1")
|
h.tablesPerLevel("0,0,1")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_BloomFilter(t *testing.T) {
|
func TestDB_BloomFilter(t *testing.T) {
|
||||||
h := newDbHarnessWopt(t, &opt.Options{
|
h := newDbHarnessWopt(t, &opt.Options{
|
||||||
BlockCache: opt.NoCache,
|
DisableBlockCache: true,
|
||||||
Filter: filter.NewBloomFilter(10),
|
Filter: filter.NewBloomFilter(10),
|
||||||
})
|
})
|
||||||
defer h.close()
|
defer h.close()
|
||||||
|
|
||||||
@@ -1680,7 +1680,7 @@ func TestDb_BloomFilter(t *testing.T) {
|
|||||||
h.stor.ReleaseSync(storage.TypeTable)
|
h.stor.ReleaseSync(storage.TypeTable)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_Concurrent(t *testing.T) {
|
func TestDB_Concurrent(t *testing.T) {
|
||||||
const n, secs, maxkey = 4, 2, 1000
|
const n, secs, maxkey = 4, 2, 1000
|
||||||
|
|
||||||
runtime.GOMAXPROCS(n)
|
runtime.GOMAXPROCS(n)
|
||||||
@@ -1745,7 +1745,7 @@ func TestDb_Concurrent(t *testing.T) {
|
|||||||
runtime.GOMAXPROCS(1)
|
runtime.GOMAXPROCS(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_Concurrent2(t *testing.T) {
|
func TestDB_Concurrent2(t *testing.T) {
|
||||||
const n, n2 = 4, 4000
|
const n, n2 = 4, 4000
|
||||||
|
|
||||||
runtime.GOMAXPROCS(n*2 + 2)
|
runtime.GOMAXPROCS(n*2 + 2)
|
||||||
@@ -1816,7 +1816,7 @@ func TestDb_Concurrent2(t *testing.T) {
|
|||||||
runtime.GOMAXPROCS(1)
|
runtime.GOMAXPROCS(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_CreateReopenDbOnFile(t *testing.T) {
|
func TestDB_CreateReopenDbOnFile(t *testing.T) {
|
||||||
dbpath := filepath.Join(os.TempDir(), fmt.Sprintf("goleveldbtestCreateReopenDbOnFile-%d", os.Getuid()))
|
dbpath := filepath.Join(os.TempDir(), fmt.Sprintf("goleveldbtestCreateReopenDbOnFile-%d", os.Getuid()))
|
||||||
if err := os.RemoveAll(dbpath); err != nil {
|
if err := os.RemoveAll(dbpath); err != nil {
|
||||||
t.Fatal("cannot remove old db: ", err)
|
t.Fatal("cannot remove old db: ", err)
|
||||||
@@ -1844,7 +1844,7 @@ func TestDb_CreateReopenDbOnFile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_CreateReopenDbOnFile2(t *testing.T) {
|
func TestDB_CreateReopenDbOnFile2(t *testing.T) {
|
||||||
dbpath := filepath.Join(os.TempDir(), fmt.Sprintf("goleveldbtestCreateReopenDbOnFile2-%d", os.Getuid()))
|
dbpath := filepath.Join(os.TempDir(), fmt.Sprintf("goleveldbtestCreateReopenDbOnFile2-%d", os.Getuid()))
|
||||||
if err := os.RemoveAll(dbpath); err != nil {
|
if err := os.RemoveAll(dbpath); err != nil {
|
||||||
t.Fatal("cannot remove old db: ", err)
|
t.Fatal("cannot remove old db: ", err)
|
||||||
@@ -1865,7 +1865,7 @@ func TestDb_CreateReopenDbOnFile2(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_DeletionMarkersOnMemdb(t *testing.T) {
|
func TestDB_DeletionMarkersOnMemdb(t *testing.T) {
|
||||||
h := newDbHarness(t)
|
h := newDbHarness(t)
|
||||||
defer h.close()
|
defer h.close()
|
||||||
|
|
||||||
@@ -1876,7 +1876,7 @@ func TestDb_DeletionMarkersOnMemdb(t *testing.T) {
|
|||||||
h.getKeyVal("")
|
h.getKeyVal("")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_LeveldbIssue178(t *testing.T) {
|
func TestDB_LeveldbIssue178(t *testing.T) {
|
||||||
nKeys := (opt.DefaultCompactionTableSize / 30) * 5
|
nKeys := (opt.DefaultCompactionTableSize / 30) * 5
|
||||||
key1 := func(i int) string {
|
key1 := func(i int) string {
|
||||||
return fmt.Sprintf("my_key_%d", i)
|
return fmt.Sprintf("my_key_%d", i)
|
||||||
@@ -1919,7 +1919,7 @@ func TestDb_LeveldbIssue178(t *testing.T) {
|
|||||||
h.assertNumKeys(nKeys)
|
h.assertNumKeys(nKeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_LeveldbIssue200(t *testing.T) {
|
func TestDB_LeveldbIssue200(t *testing.T) {
|
||||||
h := newDbHarness(t)
|
h := newDbHarness(t)
|
||||||
defer h.close()
|
defer h.close()
|
||||||
|
|
||||||
@@ -1946,7 +1946,7 @@ func TestDb_LeveldbIssue200(t *testing.T) {
|
|||||||
assertBytes(t, []byte("5"), iter.Key())
|
assertBytes(t, []byte("5"), iter.Key())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_GoleveldbIssue74(t *testing.T) {
|
func TestDB_GoleveldbIssue74(t *testing.T) {
|
||||||
h := newDbHarnessWopt(t, &opt.Options{
|
h := newDbHarnessWopt(t, &opt.Options{
|
||||||
WriteBuffer: 1 * opt.MiB,
|
WriteBuffer: 1 * opt.MiB,
|
||||||
})
|
})
|
||||||
@@ -2044,7 +2044,7 @@ func TestDb_GoleveldbIssue74(t *testing.T) {
|
|||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_GetProperties(t *testing.T) {
|
func TestDB_GetProperties(t *testing.T) {
|
||||||
h := newDbHarness(t)
|
h := newDbHarness(t)
|
||||||
defer h.close()
|
defer h.close()
|
||||||
|
|
||||||
@@ -2064,10 +2064,10 @@ func TestDb_GetProperties(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_GoleveldbIssue72and83(t *testing.T) {
|
func TestDB_GoleveldbIssue72and83(t *testing.T) {
|
||||||
h := newDbHarnessWopt(t, &opt.Options{
|
h := newDbHarnessWopt(t, &opt.Options{
|
||||||
WriteBuffer: 1 * opt.MiB,
|
WriteBuffer: 1 * opt.MiB,
|
||||||
CachedOpenFiles: 3,
|
OpenFilesCacheCapacity: 3,
|
||||||
})
|
})
|
||||||
defer h.close()
|
defer h.close()
|
||||||
|
|
||||||
@@ -2077,12 +2077,13 @@ func TestDb_GoleveldbIssue72and83(t *testing.T) {
|
|||||||
|
|
||||||
randomData := func(prefix byte, i int) []byte {
|
randomData := func(prefix byte, i int) []byte {
|
||||||
data := make([]byte, 1+4+32+64+32)
|
data := make([]byte, 1+4+32+64+32)
|
||||||
_, err := crand.Reader.Read(data[1 : len(data)-4])
|
_, err := crand.Reader.Read(data[1 : len(data)-8])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
data[0] = prefix
|
data[0] = prefix
|
||||||
binary.LittleEndian.PutUint32(data[len(data)-4:], uint32(i))
|
binary.LittleEndian.PutUint32(data[len(data)-8:], uint32(i))
|
||||||
|
binary.LittleEndian.PutUint32(data[len(data)-4:], util.NewCRC(data[:len(data)-4]).Value())
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2131,12 +2132,22 @@ func TestDb_GoleveldbIssue72and83(t *testing.T) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
iter := snap.NewIterator(util.BytesPrefix([]byte{1}), nil)
|
iter := snap.NewIterator(util.BytesPrefix([]byte{1}), nil)
|
||||||
writei := int(snap.elem.seq/(n*2) - 1)
|
writei := int(seq/(n*2) - 1)
|
||||||
var k int
|
var k int
|
||||||
for ; iter.Next(); k++ {
|
for ; iter.Next(); k++ {
|
||||||
k1 := iter.Key()
|
k1 := iter.Key()
|
||||||
k2 := iter.Value()
|
k2 := iter.Value()
|
||||||
kwritei := int(binary.LittleEndian.Uint32(k2[len(k2)-4:]))
|
k1checksum0 := binary.LittleEndian.Uint32(k1[len(k1)-4:])
|
||||||
|
k1checksum1 := util.NewCRC(k1[:len(k1)-4]).Value()
|
||||||
|
if k1checksum0 != k1checksum1 {
|
||||||
|
t.Fatalf("READER0 #%d.%d W#%d invalid K1 checksum: %#x != %#x", i, k, k1checksum0, k1checksum0)
|
||||||
|
}
|
||||||
|
k2checksum0 := binary.LittleEndian.Uint32(k2[len(k2)-4:])
|
||||||
|
k2checksum1 := util.NewCRC(k2[:len(k2)-4]).Value()
|
||||||
|
if k2checksum0 != k2checksum1 {
|
||||||
|
t.Fatalf("READER0 #%d.%d W#%d invalid K2 checksum: %#x != %#x", i, k, k2checksum0, k2checksum1)
|
||||||
|
}
|
||||||
|
kwritei := int(binary.LittleEndian.Uint32(k2[len(k2)-8:]))
|
||||||
if writei != kwritei {
|
if writei != kwritei {
|
||||||
t.Fatalf("READER0 #%d.%d W#%d invalid write iteration num: %d", i, k, writei, kwritei)
|
t.Fatalf("READER0 #%d.%d W#%d invalid write iteration num: %d", i, k, writei, kwritei)
|
||||||
}
|
}
|
||||||
@@ -2186,10 +2197,10 @@ func TestDb_GoleveldbIssue72and83(t *testing.T) {
|
|||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_TransientError(t *testing.T) {
|
func TestDB_TransientError(t *testing.T) {
|
||||||
h := newDbHarnessWopt(t, &opt.Options{
|
h := newDbHarnessWopt(t, &opt.Options{
|
||||||
WriteBuffer: 128 * opt.KiB,
|
WriteBuffer: 128 * opt.KiB,
|
||||||
CachedOpenFiles: 3,
|
OpenFilesCacheCapacity: 3,
|
||||||
DisableCompactionBackoff: true,
|
DisableCompactionBackoff: true,
|
||||||
})
|
})
|
||||||
defer h.close()
|
defer h.close()
|
||||||
@@ -2299,7 +2310,7 @@ func TestDb_TransientError(t *testing.T) {
|
|||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_UkeyShouldntHopAcrossTable(t *testing.T) {
|
func TestDB_UkeyShouldntHopAcrossTable(t *testing.T) {
|
||||||
h := newDbHarnessWopt(t, &opt.Options{
|
h := newDbHarnessWopt(t, &opt.Options{
|
||||||
WriteBuffer: 112 * opt.KiB,
|
WriteBuffer: 112 * opt.KiB,
|
||||||
CompactionTableSize: 90 * opt.KiB,
|
CompactionTableSize: 90 * opt.KiB,
|
||||||
@@ -2388,7 +2399,7 @@ func TestDb_UkeyShouldntHopAcrossTable(t *testing.T) {
|
|||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDb_TableCompactionBuilder(t *testing.T) {
|
func TestDB_TableCompactionBuilder(t *testing.T) {
|
||||||
stor := newTestStorage(t)
|
stor := newTestStorage(t)
|
||||||
defer stor.Close()
|
defer stor.Close()
|
||||||
|
|
||||||
@@ -2399,7 +2410,7 @@ func TestDb_TableCompactionBuilder(t *testing.T) {
|
|||||||
CompactionTableSize: 43 * opt.KiB,
|
CompactionTableSize: 43 * opt.KiB,
|
||||||
CompactionExpandLimitFactor: 1,
|
CompactionExpandLimitFactor: 1,
|
||||||
CompactionGPOverlapsFactor: 1,
|
CompactionGPOverlapsFactor: 1,
|
||||||
BlockCache: opt.NoCache,
|
DisableBlockCache: true,
|
||||||
}
|
}
|
||||||
s, err := newSession(stor, o)
|
s, err := newSession(stor, o)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+1
-1
@@ -112,9 +112,9 @@ func (db *DB) flush(n int) (mem *memDB, nn int, err error) {
|
|||||||
db.writeDelay += time.Since(start)
|
db.writeDelay += time.Since(start)
|
||||||
db.writeDelayN++
|
db.writeDelayN++
|
||||||
} else if db.writeDelayN > 0 {
|
} else if db.writeDelayN > 0 {
|
||||||
|
db.logf("db@write was delayed N·%d T·%v", db.writeDelayN, db.writeDelay)
|
||||||
db.writeDelay = 0
|
db.writeDelay = 0
|
||||||
db.writeDelayN = 0
|
db.writeDelayN = 0
|
||||||
db.logf("db@write was delayed N·%d T·%v", db.writeDelayN, db.writeDelay)
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-7
@@ -17,13 +17,14 @@ import (
|
|||||||
var _ = testutil.Defer(func() {
|
var _ = testutil.Defer(func() {
|
||||||
Describe("Leveldb external", func() {
|
Describe("Leveldb external", func() {
|
||||||
o := &opt.Options{
|
o := &opt.Options{
|
||||||
BlockCache: opt.NoCache,
|
DisableBlockCache: true,
|
||||||
BlockRestartInterval: 5,
|
BlockRestartInterval: 5,
|
||||||
BlockSize: 50,
|
BlockSize: 80,
|
||||||
Compression: opt.NoCompression,
|
Compression: opt.NoCompression,
|
||||||
CachedOpenFiles: -1,
|
OpenFilesCacheCapacity: -1,
|
||||||
Strict: opt.StrictAll,
|
Strict: opt.StrictAll,
|
||||||
WriteBuffer: 1000,
|
WriteBuffer: 1000,
|
||||||
|
CompactionTableSize: 2000,
|
||||||
}
|
}
|
||||||
|
|
||||||
Describe("write test", func() {
|
Describe("write test", func() {
|
||||||
|
|||||||
Generated
Vendored
+1
-7
@@ -3,15 +3,9 @@ package iterator_test
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
|
|
||||||
"github.com/syndtr/goleveldb/leveldb/testutil"
|
"github.com/syndtr/goleveldb/leveldb/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIterator(t *testing.T) {
|
func TestIterator(t *testing.T) {
|
||||||
testutil.RunDefer()
|
testutil.RunSuite(t, "Iterator Suite")
|
||||||
|
|
||||||
RegisterFailHandler(Fail)
|
|
||||||
RunSpecs(t, "Iterator Suite")
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -106,7 +106,7 @@ func (ik iKey) assert() {
|
|||||||
panic("leveldb: nil iKey")
|
panic("leveldb: nil iKey")
|
||||||
}
|
}
|
||||||
if len(ik) < 8 {
|
if len(ik) < 8 {
|
||||||
panic(fmt.Sprintf("leveldb: iKey %q, len=%d: invalid length", ik, len(ik)))
|
panic(fmt.Sprintf("leveldb: iKey %q, len=%d: invalid length", []byte(ik), len(ik)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ func (ik iKey) parseNum() (seq uint64, kt kType) {
|
|||||||
num := ik.num()
|
num := ik.num()
|
||||||
seq, kt = uint64(num>>8), kType(num&0xff)
|
seq, kt = uint64(num>>8), kType(num&0xff)
|
||||||
if kt > ktVal {
|
if kt > ktVal {
|
||||||
panic(fmt.Sprintf("leveldb: iKey %q, len=%d: invalid type %#x", ik, len(ik), kt))
|
panic(fmt.Sprintf("leveldb: iKey %q, len=%d: invalid type %#x", []byte(ik), len(ik), kt))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-11
@@ -3,18 +3,9 @@ package leveldb
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
|
|
||||||
"github.com/syndtr/goleveldb/leveldb/testutil"
|
"github.com/syndtr/goleveldb/leveldb/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLeveldb(t *testing.T) {
|
func TestLevelDB(t *testing.T) {
|
||||||
testutil.RunDefer()
|
testutil.RunSuite(t, "LevelDB Suite")
|
||||||
|
|
||||||
RegisterFailHandler(Fail)
|
|
||||||
RunSpecs(t, "Leveldb Suite")
|
|
||||||
|
|
||||||
RegisterTestingT(t)
|
|
||||||
testutil.RunDefer("teardown")
|
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
Vendored
+2
-8
@@ -3,15 +3,9 @@ package memdb
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
|
|
||||||
"github.com/syndtr/goleveldb/leveldb/testutil"
|
"github.com/syndtr/goleveldb/leveldb/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMemdb(t *testing.T) {
|
func TestMemDB(t *testing.T) {
|
||||||
testutil.RunDefer()
|
testutil.RunSuite(t, "MemDB Suite")
|
||||||
|
|
||||||
RegisterFailHandler(Fail)
|
|
||||||
RunSpecs(t, "Memdb Suite")
|
|
||||||
}
|
}
|
||||||
|
|||||||
+93
-42
@@ -20,8 +20,9 @@ const (
|
|||||||
GiB = MiB * 1024
|
GiB = MiB * 1024
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
var (
|
||||||
DefaultBlockCacheSize = 8 * MiB
|
DefaultBlockCacher = LRUCacher
|
||||||
|
DefaultBlockCacheCapacity = 8 * MiB
|
||||||
DefaultBlockRestartInterval = 16
|
DefaultBlockRestartInterval = 16
|
||||||
DefaultBlockSize = 4 * KiB
|
DefaultBlockSize = 4 * KiB
|
||||||
DefaultCompactionExpandLimitFactor = 25
|
DefaultCompactionExpandLimitFactor = 25
|
||||||
@@ -33,7 +34,8 @@ const (
|
|||||||
DefaultCompactionTotalSize = 10 * MiB
|
DefaultCompactionTotalSize = 10 * MiB
|
||||||
DefaultCompactionTotalSizeMultiplier = 10.0
|
DefaultCompactionTotalSizeMultiplier = 10.0
|
||||||
DefaultCompressionType = SnappyCompression
|
DefaultCompressionType = SnappyCompression
|
||||||
DefaultCachedOpenFiles = 500
|
DefaultOpenFilesCacher = LRUCacher
|
||||||
|
DefaultOpenFilesCacheCapacity = 500
|
||||||
DefaultMaxMemCompationLevel = 2
|
DefaultMaxMemCompationLevel = 2
|
||||||
DefaultNumLevel = 7
|
DefaultNumLevel = 7
|
||||||
DefaultWriteBuffer = 4 * MiB
|
DefaultWriteBuffer = 4 * MiB
|
||||||
@@ -41,22 +43,33 @@ const (
|
|||||||
DefaultWriteL0SlowdownTrigger = 8
|
DefaultWriteL0SlowdownTrigger = 8
|
||||||
)
|
)
|
||||||
|
|
||||||
type noCache struct{}
|
// Cacher is a caching algorithm.
|
||||||
|
type Cacher interface {
|
||||||
|
New(capacity int) cache.Cacher
|
||||||
|
}
|
||||||
|
|
||||||
func (noCache) SetCapacity(capacity int) {}
|
type CacherFunc struct {
|
||||||
func (noCache) Capacity() int { return 0 }
|
NewFunc func(capacity int) cache.Cacher
|
||||||
func (noCache) Used() int { return 0 }
|
}
|
||||||
func (noCache) Size() int { return 0 }
|
|
||||||
func (noCache) NumObjects() int { return 0 }
|
|
||||||
func (noCache) GetNamespace(id uint64) cache.Namespace { return nil }
|
|
||||||
func (noCache) PurgeNamespace(id uint64, fin cache.PurgeFin) {}
|
|
||||||
func (noCache) ZapNamespace(id uint64) {}
|
|
||||||
func (noCache) Purge(fin cache.PurgeFin) {}
|
|
||||||
func (noCache) Zap() {}
|
|
||||||
|
|
||||||
var NoCache cache.Cache = noCache{}
|
func (f *CacherFunc) New(capacity int) cache.Cacher {
|
||||||
|
if f.NewFunc != nil {
|
||||||
|
return f.NewFunc(capacity)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Compression is the per-block compression algorithm to use.
|
func noCacher(int) cache.Cacher { return nil }
|
||||||
|
|
||||||
|
var (
|
||||||
|
// LRUCacher is the LRU-cache algorithm.
|
||||||
|
LRUCacher = &CacherFunc{cache.NewLRU}
|
||||||
|
|
||||||
|
// NoCacher is the value to disable caching algorithm.
|
||||||
|
NoCacher = &CacherFunc{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compression is the 'sorted table' block compression algorithm to use.
|
||||||
type Compression uint
|
type Compression uint
|
||||||
|
|
||||||
func (c Compression) String() string {
|
func (c Compression) String() string {
|
||||||
@@ -114,7 +127,7 @@ const (
|
|||||||
StrictOverride
|
StrictOverride
|
||||||
|
|
||||||
// StrictAll enables all strict flags.
|
// StrictAll enables all strict flags.
|
||||||
StrictAll = StrictManifest | StrictJournalChecksum | StrictJournal | StrictBlockChecksum | StrictCompaction | StrictReader
|
StrictAll = StrictManifest | StrictJournalChecksum | StrictJournal | StrictBlockChecksum | StrictCompaction | StrictReader | StrictRecovery
|
||||||
|
|
||||||
// DefaultStrict is the default strict flags. Specify any strict flags
|
// DefaultStrict is the default strict flags. Specify any strict flags
|
||||||
// will override default strict flags as whole (i.e. not OR'ed).
|
// will override default strict flags as whole (i.e. not OR'ed).
|
||||||
@@ -133,11 +146,17 @@ type Options struct {
|
|||||||
// The default value is nil
|
// The default value is nil
|
||||||
AltFilters []filter.Filter
|
AltFilters []filter.Filter
|
||||||
|
|
||||||
// BlockCache provides per-block caching for LevelDB. Specify NoCache to
|
// BlockCacher provides cache algorithm for LevelDB 'sorted table' block caching.
|
||||||
// disable block caching.
|
// Specify NoCacher to disable caching algorithm.
|
||||||
//
|
//
|
||||||
// By default LevelDB will create LRU-cache with capacity of 8MiB.
|
// The default value is LRUCacher.
|
||||||
BlockCache cache.Cache
|
BlockCacher Cacher
|
||||||
|
|
||||||
|
// BlockCacheCapacity defines the capacity of the 'sorted table' block caching.
|
||||||
|
// Use -1 for zero, this has same effect as specifying NoCacher to BlockCacher.
|
||||||
|
//
|
||||||
|
// The default value is 8MiB.
|
||||||
|
BlockCacheCapacity int
|
||||||
|
|
||||||
// BlockRestartInterval is the number of keys between restart points for
|
// BlockRestartInterval is the number of keys between restart points for
|
||||||
// delta encoding of keys.
|
// delta encoding of keys.
|
||||||
@@ -151,13 +170,6 @@ type Options struct {
|
|||||||
// The default value is 4KiB.
|
// The default value is 4KiB.
|
||||||
BlockSize int
|
BlockSize int
|
||||||
|
|
||||||
// CachedOpenFiles defines number of open files to kept around when not
|
|
||||||
// in-use, the counting includes still in-use files.
|
|
||||||
// Set this to negative value to disable caching.
|
|
||||||
//
|
|
||||||
// The default value is 500.
|
|
||||||
CachedOpenFiles int
|
|
||||||
|
|
||||||
// CompactionExpandLimitFactor limits compaction size after expanded.
|
// CompactionExpandLimitFactor limits compaction size after expanded.
|
||||||
// This will be multiplied by table size limit at compaction target level.
|
// This will be multiplied by table size limit at compaction target level.
|
||||||
//
|
//
|
||||||
@@ -232,11 +244,17 @@ type Options struct {
|
|||||||
// The default value uses the same ordering as bytes.Compare.
|
// The default value uses the same ordering as bytes.Compare.
|
||||||
Comparer comparer.Comparer
|
Comparer comparer.Comparer
|
||||||
|
|
||||||
// Compression defines the per-block compression to use.
|
// Compression defines the 'sorted table' block compression to use.
|
||||||
//
|
//
|
||||||
// The default value (DefaultCompression) uses snappy compression.
|
// The default value (DefaultCompression) uses snappy compression.
|
||||||
Compression Compression
|
Compression Compression
|
||||||
|
|
||||||
|
// DisableBlockCache allows disable use of cache.Cache functionality on
|
||||||
|
// 'sorted table' block.
|
||||||
|
//
|
||||||
|
// The default value is false.
|
||||||
|
DisableBlockCache bool
|
||||||
|
|
||||||
// DisableCompactionBackoff allows disable compaction retry backoff.
|
// DisableCompactionBackoff allows disable compaction retry backoff.
|
||||||
//
|
//
|
||||||
// The default value is false.
|
// The default value is false.
|
||||||
@@ -283,6 +301,18 @@ type Options struct {
|
|||||||
// The default is 7.
|
// The default is 7.
|
||||||
NumLevel int
|
NumLevel int
|
||||||
|
|
||||||
|
// OpenFilesCacher provides cache algorithm for open files caching.
|
||||||
|
// Specify NoCacher to disable caching algorithm.
|
||||||
|
//
|
||||||
|
// The default value is LRUCacher.
|
||||||
|
OpenFilesCacher Cacher
|
||||||
|
|
||||||
|
// OpenFilesCacheCapacity defines the capacity of the open files caching.
|
||||||
|
// Use -1 for zero, this has same effect as specifying NoCacher to OpenFilesCacher.
|
||||||
|
//
|
||||||
|
// The default value is 500.
|
||||||
|
OpenFilesCacheCapacity int
|
||||||
|
|
||||||
// Strict defines the DB strict level.
|
// Strict defines the DB strict level.
|
||||||
Strict Strict
|
Strict Strict
|
||||||
|
|
||||||
@@ -315,11 +345,22 @@ func (o *Options) GetAltFilters() []filter.Filter {
|
|||||||
return o.AltFilters
|
return o.AltFilters
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Options) GetBlockCache() cache.Cache {
|
func (o *Options) GetBlockCacher() Cacher {
|
||||||
if o == nil {
|
if o == nil || o.BlockCacher == nil {
|
||||||
|
return DefaultBlockCacher
|
||||||
|
} else if o.BlockCacher == NoCacher {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return o.BlockCache
|
return o.BlockCacher
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) GetBlockCacheCapacity() int {
|
||||||
|
if o == nil || o.BlockCacheCapacity == 0 {
|
||||||
|
return DefaultBlockCacheCapacity
|
||||||
|
} else if o.BlockCacheCapacity < 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return o.BlockCacheCapacity
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Options) GetBlockRestartInterval() int {
|
func (o *Options) GetBlockRestartInterval() int {
|
||||||
@@ -336,15 +377,6 @@ func (o *Options) GetBlockSize() int {
|
|||||||
return o.BlockSize
|
return o.BlockSize
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Options) GetCachedOpenFiles() int {
|
|
||||||
if o == nil || o.CachedOpenFiles == 0 {
|
|
||||||
return DefaultCachedOpenFiles
|
|
||||||
} else if o.CachedOpenFiles < 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return o.CachedOpenFiles
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Options) GetCompactionExpandLimit(level int) int {
|
func (o *Options) GetCompactionExpandLimit(level int) int {
|
||||||
factor := DefaultCompactionExpandLimitFactor
|
factor := DefaultCompactionExpandLimitFactor
|
||||||
if o != nil && o.CompactionExpandLimitFactor > 0 {
|
if o != nil && o.CompactionExpandLimitFactor > 0 {
|
||||||
@@ -465,7 +497,7 @@ func (o *Options) GetMaxMemCompationLevel() int {
|
|||||||
if o != nil {
|
if o != nil {
|
||||||
if o.MaxMemCompationLevel > 0 {
|
if o.MaxMemCompationLevel > 0 {
|
||||||
level = o.MaxMemCompationLevel
|
level = o.MaxMemCompationLevel
|
||||||
} else if o.MaxMemCompationLevel == -1 {
|
} else if o.MaxMemCompationLevel < 0 {
|
||||||
level = 0
|
level = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -482,6 +514,25 @@ func (o *Options) GetNumLevel() int {
|
|||||||
return o.NumLevel
|
return o.NumLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *Options) GetOpenFilesCacher() Cacher {
|
||||||
|
if o == nil || o.OpenFilesCacher == nil {
|
||||||
|
return DefaultOpenFilesCacher
|
||||||
|
}
|
||||||
|
if o.OpenFilesCacher == NoCacher {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return o.OpenFilesCacher
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) GetOpenFilesCacheCapacity() int {
|
||||||
|
if o == nil || o.OpenFilesCacheCapacity == 0 {
|
||||||
|
return DefaultOpenFilesCacheCapacity
|
||||||
|
} else if o.OpenFilesCacheCapacity < 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return o.OpenFilesCacheCapacity
|
||||||
|
}
|
||||||
|
|
||||||
func (o *Options) GetStrict(strict Strict) bool {
|
func (o *Options) GetStrict(strict Strict) bool {
|
||||||
if o == nil || o.Strict == 0 {
|
if o == nil || o.Strict == 0 {
|
||||||
return DefaultStrict&strict != 0
|
return DefaultStrict&strict != 0
|
||||||
|
|||||||
+3
-8
@@ -7,7 +7,6 @@
|
|||||||
package leveldb
|
package leveldb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/syndtr/goleveldb/leveldb/cache"
|
|
||||||
"github.com/syndtr/goleveldb/leveldb/filter"
|
"github.com/syndtr/goleveldb/leveldb/filter"
|
||||||
"github.com/syndtr/goleveldb/leveldb/opt"
|
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||||
)
|
)
|
||||||
@@ -17,6 +16,9 @@ func dupOptions(o *opt.Options) *opt.Options {
|
|||||||
if o != nil {
|
if o != nil {
|
||||||
*newo = *o
|
*newo = *o
|
||||||
}
|
}
|
||||||
|
if newo.Strict == 0 {
|
||||||
|
newo.Strict = opt.DefaultStrict
|
||||||
|
}
|
||||||
return newo
|
return newo
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,13 +31,6 @@ func (s *session) setOptions(o *opt.Options) {
|
|||||||
no.AltFilters[i] = &iFilter{filter}
|
no.AltFilters[i] = &iFilter{filter}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Block cache.
|
|
||||||
switch o.GetBlockCache() {
|
|
||||||
case nil:
|
|
||||||
no.BlockCache = cache.NewLRUCache(opt.DefaultBlockCacheSize)
|
|
||||||
case opt.NoCache:
|
|
||||||
no.BlockCache = nil
|
|
||||||
}
|
|
||||||
// Comparer.
|
// Comparer.
|
||||||
s.icmp = &iComparer{o.GetComparer()}
|
s.icmp = &iComparer{o.GetComparer()}
|
||||||
no.Comparer = s.icmp
|
no.Comparer = s.icmp
|
||||||
|
|||||||
+1
-4
@@ -73,7 +73,7 @@ func newSession(stor storage.Storage, o *opt.Options) (s *session, err error) {
|
|||||||
stCompPtrs: make([]iKey, o.GetNumLevel()),
|
stCompPtrs: make([]iKey, o.GetNumLevel()),
|
||||||
}
|
}
|
||||||
s.setOptions(o)
|
s.setOptions(o)
|
||||||
s.tops = newTableOps(s, s.o.GetCachedOpenFiles())
|
s.tops = newTableOps(s)
|
||||||
s.setVersion(newVersion(s))
|
s.setVersion(newVersion(s))
|
||||||
s.log("log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed")
|
s.log("log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed")
|
||||||
return
|
return
|
||||||
@@ -82,9 +82,6 @@ func newSession(stor storage.Storage, o *opt.Options) (s *session, err error) {
|
|||||||
// Close session.
|
// Close session.
|
||||||
func (s *session) close() {
|
func (s *session) close() {
|
||||||
s.tops.close()
|
s.tops.close()
|
||||||
if bc := s.o.GetBlockCache(); bc != nil {
|
|
||||||
bc.Purge(nil)
|
|
||||||
}
|
|
||||||
if s.manifest != nil {
|
if s.manifest != nil {
|
||||||
s.manifest.Close()
|
s.manifest.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
Vendored
+1
-1
@@ -221,7 +221,7 @@ func (fs *fileStorage) GetManifest() (f File, err error) {
|
|||||||
fs.log(fmt.Sprintf("skipping %s: invalid file name", fn))
|
fs.log(fmt.Sprintf("skipping %s: invalid file name", fn))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, e1 := strconv.ParseUint(fn[7:], 10, 0); e1 != nil {
|
if _, e1 := strconv.ParseUint(fn[8:], 10, 0); e1 != nil {
|
||||||
fs.log(fmt.Sprintf("skipping %s: invalid file num: %v", fn, e1))
|
fs.log(fmt.Sprintf("skipping %s: invalid file num: %v", fn, e1))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
+21
@@ -233,6 +233,23 @@ func (tf tsFile) Create() (w storage.Writer, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tf tsFile) Replace(newfile storage.File) (err error) {
|
||||||
|
ts := tf.ts
|
||||||
|
ts.mu.Lock()
|
||||||
|
defer ts.mu.Unlock()
|
||||||
|
err = tf.checkOpen("replace")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = tf.File.Replace(newfile.(tsFile).File)
|
||||||
|
if err != nil {
|
||||||
|
ts.t.Errorf("E: cannot replace file, num=%d type=%v: %v", tf.Num(), tf.Type(), err)
|
||||||
|
} else {
|
||||||
|
ts.t.Logf("I: file replace, num=%d type=%v", tf.Num(), tf.Type())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (tf tsFile) Remove() (err error) {
|
func (tf tsFile) Remove() (err error) {
|
||||||
ts := tf.ts
|
ts := tf.ts
|
||||||
ts.mu.Lock()
|
ts.mu.Lock()
|
||||||
@@ -492,6 +509,10 @@ func newTestStorage(t *testing.T) *testStorage {
|
|||||||
}
|
}
|
||||||
f.Close()
|
f.Close()
|
||||||
}
|
}
|
||||||
|
if t.Failed() {
|
||||||
|
t.Logf("testing failed, test DB preserved at %s", path)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if tsKeepFS {
|
if tsKeepFS {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+54
-28
@@ -286,10 +286,10 @@ func (x *tFilesSortByNum) Less(i, j int) bool {
|
|||||||
|
|
||||||
// Table operations.
|
// Table operations.
|
||||||
type tOps struct {
|
type tOps struct {
|
||||||
s *session
|
s *session
|
||||||
cache cache.Cache
|
cache *cache.Cache
|
||||||
cacheNS cache.Namespace
|
bcache *cache.Cache
|
||||||
bpool *util.BufferPool
|
bpool *util.BufferPool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates an empty table and returns table writer.
|
// Creates an empty table and returns table writer.
|
||||||
@@ -338,26 +338,28 @@ func (t *tOps) createFrom(src iterator.Iterator) (f *tFile, n int, err error) {
|
|||||||
|
|
||||||
// Opens table. It returns a cache handle, which should
|
// Opens table. It returns a cache handle, which should
|
||||||
// be released after use.
|
// be released after use.
|
||||||
func (t *tOps) open(f *tFile) (ch cache.Handle, err error) {
|
func (t *tOps) open(f *tFile) (ch *cache.Handle, err error) {
|
||||||
num := f.file.Num()
|
num := f.file.Num()
|
||||||
ch = t.cacheNS.Get(num, func() (charge int, value interface{}) {
|
ch = t.cache.Get(0, num, func() (size int, value cache.Value) {
|
||||||
var r storage.Reader
|
var r storage.Reader
|
||||||
r, err = f.file.Open()
|
r, err = f.file.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var bcacheNS cache.Namespace
|
var bcache *cache.CacheGetter
|
||||||
if bc := t.s.o.GetBlockCache(); bc != nil {
|
if t.bcache != nil {
|
||||||
bcacheNS = bc.GetNamespace(num)
|
bcache = &cache.CacheGetter{Cache: t.bcache, NS: num}
|
||||||
}
|
}
|
||||||
|
|
||||||
var tr *table.Reader
|
var tr *table.Reader
|
||||||
tr, err = table.NewReader(r, int64(f.size), storage.NewFileInfo(f.file), bcacheNS, t.bpool, t.s.o.Options)
|
tr, err = table.NewReader(r, int64(f.size), storage.NewFileInfo(f.file), bcache, t.bpool, t.s.o.Options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Close()
|
r.Close()
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
return 1, tr
|
return 1, tr
|
||||||
|
|
||||||
})
|
})
|
||||||
if ch == nil && err == nil {
|
if ch == nil && err == nil {
|
||||||
err = ErrClosed
|
err = ErrClosed
|
||||||
@@ -373,7 +375,17 @@ func (t *tOps) find(f *tFile, key []byte, ro *opt.ReadOptions) (rkey, rvalue []b
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
defer ch.Release()
|
defer ch.Release()
|
||||||
return ch.Value().(*table.Reader).Find(key, ro)
|
return ch.Value().(*table.Reader).Find(key, true, ro)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finds key that is greater than or equal to the given key.
|
||||||
|
func (t *tOps) findKey(f *tFile, key []byte, ro *opt.ReadOptions) (rkey []byte, err error) {
|
||||||
|
ch, err := t.open(f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer ch.Release()
|
||||||
|
return ch.Value().(*table.Reader).FindKey(key, true, ro)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns approximate offset of the given key.
|
// Returns approximate offset of the given key.
|
||||||
@@ -402,16 +414,14 @@ func (t *tOps) newIterator(f *tFile, slice *util.Range, ro *opt.ReadOptions) ite
|
|||||||
// no one use the the table.
|
// no one use the the table.
|
||||||
func (t *tOps) remove(f *tFile) {
|
func (t *tOps) remove(f *tFile) {
|
||||||
num := f.file.Num()
|
num := f.file.Num()
|
||||||
t.cacheNS.Delete(num, func(exist, pending bool) {
|
t.cache.Delete(0, num, func() {
|
||||||
if !pending {
|
if err := f.file.Remove(); err != nil {
|
||||||
if err := f.file.Remove(); err != nil {
|
t.s.logf("table@remove removing @%d %q", num, err)
|
||||||
t.s.logf("table@remove removing @%d %q", num, err)
|
} else {
|
||||||
} else {
|
t.s.logf("table@remove removed @%d", num)
|
||||||
t.s.logf("table@remove removed @%d", num)
|
}
|
||||||
}
|
if t.bcache != nil {
|
||||||
if bc := t.s.o.GetBlockCache(); bc != nil {
|
t.bcache.EvictNS(num)
|
||||||
bc.ZapNamespace(num)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -419,18 +429,34 @@ func (t *tOps) remove(f *tFile) {
|
|||||||
// Closes the table ops instance. It will close all tables,
|
// Closes the table ops instance. It will close all tables,
|
||||||
// regadless still used or not.
|
// regadless still used or not.
|
||||||
func (t *tOps) close() {
|
func (t *tOps) close() {
|
||||||
t.cache.Zap()
|
|
||||||
t.bpool.Close()
|
t.bpool.Close()
|
||||||
|
t.cache.Close()
|
||||||
|
if t.bcache != nil {
|
||||||
|
t.bcache.Close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates new initialized table ops instance.
|
// Creates new initialized table ops instance.
|
||||||
func newTableOps(s *session, cacheCap int) *tOps {
|
func newTableOps(s *session) *tOps {
|
||||||
c := cache.NewLRUCache(cacheCap)
|
var (
|
||||||
|
cacher cache.Cacher
|
||||||
|
bcache *cache.Cache
|
||||||
|
)
|
||||||
|
if s.o.GetOpenFilesCacheCapacity() > 0 {
|
||||||
|
cacher = cache.NewLRU(s.o.GetOpenFilesCacheCapacity())
|
||||||
|
}
|
||||||
|
if !s.o.DisableBlockCache {
|
||||||
|
var bcacher cache.Cacher
|
||||||
|
if s.o.GetBlockCacheCapacity() > 0 {
|
||||||
|
bcacher = cache.NewLRU(s.o.GetBlockCacheCapacity())
|
||||||
|
}
|
||||||
|
bcache = cache.NewCache(bcacher)
|
||||||
|
}
|
||||||
return &tOps{
|
return &tOps{
|
||||||
s: s,
|
s: s,
|
||||||
cache: c,
|
cache: cache.NewCache(cacher),
|
||||||
cacheNS: c.GetNamespace(0),
|
bcache: bcache,
|
||||||
bpool: util.NewBufferPool(s.o.GetBlockSize() + 5),
|
bpool: util.NewBufferPool(s.o.GetBlockSize() + 5),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -122,6 +122,7 @@ var _ = testutil.Defer(func() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
testutil.DoIteratorTesting(&t)
|
testutil.DoIteratorTesting(&t)
|
||||||
|
iter.Release()
|
||||||
done <- true
|
done <- true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+120
-72
@@ -50,13 +50,6 @@ func max(x, y int) int {
|
|||||||
return y
|
return y
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyBlockChecksum(data []byte) bool {
|
|
||||||
n := len(data) - 4
|
|
||||||
checksum0 := binary.LittleEndian.Uint32(data[n:])
|
|
||||||
checksum1 := util.NewCRC(data[:n]).Value()
|
|
||||||
return checksum0 == checksum1
|
|
||||||
}
|
|
||||||
|
|
||||||
type block struct {
|
type block struct {
|
||||||
bpool *util.BufferPool
|
bpool *util.BufferPool
|
||||||
bh blockHandle
|
bh blockHandle
|
||||||
@@ -516,7 +509,7 @@ type Reader struct {
|
|||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
fi *storage.FileInfo
|
fi *storage.FileInfo
|
||||||
reader io.ReaderAt
|
reader io.ReaderAt
|
||||||
cache cache.Namespace
|
cache *cache.CacheGetter
|
||||||
err error
|
err error
|
||||||
bpool *util.BufferPool
|
bpool *util.BufferPool
|
||||||
// Options
|
// Options
|
||||||
@@ -525,21 +518,24 @@ type Reader struct {
|
|||||||
filter filter.Filter
|
filter filter.Filter
|
||||||
verifyChecksum bool
|
verifyChecksum bool
|
||||||
|
|
||||||
dataEnd int64
|
dataEnd int64
|
||||||
indexBH, filterBH blockHandle
|
metaBH, indexBH, filterBH blockHandle
|
||||||
indexBlock *block
|
indexBlock *block
|
||||||
filterBlock *filterBlock
|
filterBlock *filterBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reader) blockKind(bh blockHandle) string {
|
func (r *Reader) blockKind(bh blockHandle) string {
|
||||||
switch bh.offset {
|
switch bh.offset {
|
||||||
|
case r.metaBH.offset:
|
||||||
|
return "meta-block"
|
||||||
case r.indexBH.offset:
|
case r.indexBH.offset:
|
||||||
return "index-block"
|
return "index-block"
|
||||||
case r.filterBH.offset:
|
case r.filterBH.offset:
|
||||||
return "filter-block"
|
if r.filterBH.length > 0 {
|
||||||
default:
|
return "filter-block"
|
||||||
return "data-block"
|
}
|
||||||
}
|
}
|
||||||
|
return "data-block"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reader) newErrCorrupted(pos, size int64, kind, reason string) error {
|
func (r *Reader) newErrCorrupted(pos, size int64, kind, reason string) error {
|
||||||
@@ -565,10 +561,17 @@ func (r *Reader) readRawBlock(bh blockHandle, verifyChecksum bool) ([]byte, erro
|
|||||||
if _, err := r.reader.ReadAt(data, int64(bh.offset)); err != nil && err != io.EOF {
|
if _, err := r.reader.ReadAt(data, int64(bh.offset)); err != nil && err != io.EOF {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if verifyChecksum && !verifyBlockChecksum(data) {
|
|
||||||
r.bpool.Put(data)
|
if verifyChecksum {
|
||||||
return nil, r.newErrCorruptedBH(bh, "checksum mismatch")
|
n := bh.length + 1
|
||||||
|
checksum0 := binary.LittleEndian.Uint32(data[n:])
|
||||||
|
checksum1 := util.NewCRC(data[:n]).Value()
|
||||||
|
if checksum0 != checksum1 {
|
||||||
|
r.bpool.Put(data)
|
||||||
|
return nil, r.newErrCorruptedBH(bh, fmt.Sprintf("checksum mismatch, want=%#x got=%#x", checksum0, checksum1))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch data[bh.length] {
|
switch data[bh.length] {
|
||||||
case blockTypeNoCompression:
|
case blockTypeNoCompression:
|
||||||
data = data[:bh.length]
|
data = data[:bh.length]
|
||||||
@@ -610,18 +613,22 @@ func (r *Reader) readBlock(bh blockHandle, verifyChecksum bool) (*block, error)
|
|||||||
|
|
||||||
func (r *Reader) readBlockCached(bh blockHandle, verifyChecksum, fillCache bool) (*block, util.Releaser, error) {
|
func (r *Reader) readBlockCached(bh blockHandle, verifyChecksum, fillCache bool) (*block, util.Releaser, error) {
|
||||||
if r.cache != nil {
|
if r.cache != nil {
|
||||||
var err error
|
var (
|
||||||
ch := r.cache.Get(bh.offset, func() (charge int, value interface{}) {
|
err error
|
||||||
if !fillCache {
|
ch *cache.Handle
|
||||||
return 0, nil
|
)
|
||||||
}
|
if fillCache {
|
||||||
var b *block
|
ch = r.cache.Get(bh.offset, func() (size int, value cache.Value) {
|
||||||
b, err = r.readBlock(bh, verifyChecksum)
|
var b *block
|
||||||
if err != nil {
|
b, err = r.readBlock(bh, verifyChecksum)
|
||||||
return 0, nil
|
if err != nil {
|
||||||
}
|
return 0, nil
|
||||||
return cap(b.data), b
|
}
|
||||||
})
|
return cap(b.data), b
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ch = r.cache.Get(bh.offset, nil)
|
||||||
|
}
|
||||||
if ch != nil {
|
if ch != nil {
|
||||||
b, ok := ch.Value().(*block)
|
b, ok := ch.Value().(*block)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -664,18 +671,22 @@ func (r *Reader) readFilterBlock(bh blockHandle) (*filterBlock, error) {
|
|||||||
|
|
||||||
func (r *Reader) readFilterBlockCached(bh blockHandle, fillCache bool) (*filterBlock, util.Releaser, error) {
|
func (r *Reader) readFilterBlockCached(bh blockHandle, fillCache bool) (*filterBlock, util.Releaser, error) {
|
||||||
if r.cache != nil {
|
if r.cache != nil {
|
||||||
var err error
|
var (
|
||||||
ch := r.cache.Get(bh.offset, func() (charge int, value interface{}) {
|
err error
|
||||||
if !fillCache {
|
ch *cache.Handle
|
||||||
return 0, nil
|
)
|
||||||
}
|
if fillCache {
|
||||||
var b *filterBlock
|
ch = r.cache.Get(bh.offset, func() (size int, value cache.Value) {
|
||||||
b, err = r.readFilterBlock(bh)
|
var b *filterBlock
|
||||||
if err != nil {
|
b, err = r.readFilterBlock(bh)
|
||||||
return 0, nil
|
if err != nil {
|
||||||
}
|
return 0, nil
|
||||||
return cap(b.data), b
|
}
|
||||||
})
|
return cap(b.data), b
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ch = r.cache.Get(bh.offset, nil)
|
||||||
|
}
|
||||||
if ch != nil {
|
if ch != nil {
|
||||||
b, ok := ch.Value().(*filterBlock)
|
b, ok := ch.Value().(*filterBlock)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -798,13 +809,7 @@ func (r *Reader) NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.It
|
|||||||
return iterator.NewIndexedIterator(index, opt.GetStrict(r.o, ro, opt.StrictReader))
|
return iterator.NewIndexedIterator(index, opt.GetStrict(r.o, ro, opt.StrictReader))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find finds key/value pair whose key is greater than or equal to the
|
func (r *Reader) find(key []byte, filtered bool, ro *opt.ReadOptions, noValue bool) (rkey, value []byte, err error) {
|
||||||
// given key. It returns ErrNotFound if the table doesn't contain
|
|
||||||
// such pair.
|
|
||||||
//
|
|
||||||
// The caller should not modify the contents of the returned slice, but
|
|
||||||
// it is safe to modify the contents of the argument after Find returns.
|
|
||||||
func (r *Reader) Find(key []byte, ro *opt.ReadOptions) (rkey, value []byte, err error) {
|
|
||||||
r.mu.RLock()
|
r.mu.RLock()
|
||||||
defer r.mu.RUnlock()
|
defer r.mu.RUnlock()
|
||||||
|
|
||||||
@@ -833,14 +838,17 @@ func (r *Reader) Find(key []byte, ro *opt.ReadOptions) (rkey, value []byte, err
|
|||||||
r.err = r.newErrCorruptedBH(r.indexBH, "bad data block handle")
|
r.err = r.newErrCorruptedBH(r.indexBH, "bad data block handle")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if r.filter != nil {
|
if filtered && r.filter != nil {
|
||||||
filterBlock, rel, ferr := r.getFilterBlock(true)
|
filterBlock, frel, ferr := r.getFilterBlock(true)
|
||||||
if ferr == nil {
|
if ferr == nil {
|
||||||
if !filterBlock.contains(r.filter, dataBH.offset, key) {
|
if !filterBlock.contains(r.filter, dataBH.offset, key) {
|
||||||
rel.Release()
|
frel.Release()
|
||||||
return nil, nil, ErrNotFound
|
return nil, nil, ErrNotFound
|
||||||
}
|
}
|
||||||
rel.Release()
|
frel.Release()
|
||||||
|
} else if !errors.IsCorrupted(ferr) {
|
||||||
|
err = ferr
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data := r.getDataIter(dataBH, nil, r.verifyChecksum, !ro.GetDontFillCache())
|
data := r.getDataIter(dataBH, nil, r.verifyChecksum, !ro.GetDontFillCache())
|
||||||
@@ -854,21 +862,52 @@ func (r *Reader) Find(key []byte, ro *opt.ReadOptions) (rkey, value []byte, err
|
|||||||
}
|
}
|
||||||
// Don't use block buffer, no need to copy the buffer.
|
// Don't use block buffer, no need to copy the buffer.
|
||||||
rkey = data.Key()
|
rkey = data.Key()
|
||||||
if r.bpool == nil {
|
if !noValue {
|
||||||
value = data.Value()
|
if r.bpool == nil {
|
||||||
} else {
|
value = data.Value()
|
||||||
// Use block buffer, and since the buffer will be recycled, the buffer
|
} else {
|
||||||
// need to be copied.
|
// Use block buffer, and since the buffer will be recycled, the buffer
|
||||||
value = append([]byte{}, data.Value()...)
|
// need to be copied.
|
||||||
|
value = append([]byte{}, data.Value()...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find finds key/value pair whose key is greater than or equal to the
|
||||||
|
// given key. It returns ErrNotFound if the table doesn't contain
|
||||||
|
// such pair.
|
||||||
|
// If filtered is true then the nearest 'block' will be checked against
|
||||||
|
// 'filter data' (if present) and will immediately return ErrNotFound if
|
||||||
|
// 'filter data' indicates that such pair doesn't exist.
|
||||||
|
//
|
||||||
|
// The caller may modify the contents of the returned slice as it is its
|
||||||
|
// own copy.
|
||||||
|
// It is safe to modify the contents of the argument after Find returns.
|
||||||
|
func (r *Reader) Find(key []byte, filtered bool, ro *opt.ReadOptions) (rkey, value []byte, err error) {
|
||||||
|
return r.find(key, filtered, ro, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find finds key that is greater than or equal to the given key.
|
||||||
|
// It returns ErrNotFound if the table doesn't contain such key.
|
||||||
|
// If filtered is true then the nearest 'block' will be checked against
|
||||||
|
// 'filter data' (if present) and will immediately return ErrNotFound if
|
||||||
|
// 'filter data' indicates that such key doesn't exist.
|
||||||
|
//
|
||||||
|
// The caller may modify the contents of the returned slice as it is its
|
||||||
|
// own copy.
|
||||||
|
// It is safe to modify the contents of the argument after Find returns.
|
||||||
|
func (r *Reader) FindKey(key []byte, filtered bool, ro *opt.ReadOptions) (rkey []byte, err error) {
|
||||||
|
rkey, _, err = r.find(key, filtered, ro, true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Get gets the value for the given key. It returns errors.ErrNotFound
|
// Get gets the value for the given key. It returns errors.ErrNotFound
|
||||||
// if the table does not contain the key.
|
// if the table does not contain the key.
|
||||||
//
|
//
|
||||||
// The caller should not modify the contents of the returned slice, but
|
// The caller may modify the contents of the returned slice as it is its
|
||||||
// it is safe to modify the contents of the argument after Get returns.
|
// own copy.
|
||||||
|
// It is safe to modify the contents of the argument after Find returns.
|
||||||
func (r *Reader) Get(key []byte, ro *opt.ReadOptions) (value []byte, err error) {
|
func (r *Reader) Get(key []byte, ro *opt.ReadOptions) (value []byte, err error) {
|
||||||
r.mu.RLock()
|
r.mu.RLock()
|
||||||
defer r.mu.RUnlock()
|
defer r.mu.RUnlock()
|
||||||
@@ -878,7 +917,7 @@ func (r *Reader) Get(key []byte, ro *opt.ReadOptions) (value []byte, err error)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rkey, value, err := r.Find(key, ro)
|
rkey, value, err := r.find(key, false, ro, false)
|
||||||
if err == nil && r.cmp.Compare(rkey, key) != 0 {
|
if err == nil && r.cmp.Compare(rkey, key) != 0 {
|
||||||
value = nil
|
value = nil
|
||||||
err = ErrNotFound
|
err = ErrNotFound
|
||||||
@@ -949,7 +988,11 @@ func (r *Reader) Release() {
|
|||||||
// The fi, cache and bpool is optional and can be nil.
|
// The fi, cache and bpool is optional and can be nil.
|
||||||
//
|
//
|
||||||
// The returned table reader instance is goroutine-safe.
|
// The returned table reader instance is goroutine-safe.
|
||||||
func NewReader(f io.ReaderAt, size int64, fi *storage.FileInfo, cache cache.Namespace, bpool *util.BufferPool, o *opt.Options) (*Reader, error) {
|
func NewReader(f io.ReaderAt, size int64, fi *storage.FileInfo, cache *cache.CacheGetter, bpool *util.BufferPool, o *opt.Options) (*Reader, error) {
|
||||||
|
if f == nil {
|
||||||
|
return nil, errors.New("leveldb/table: nil file")
|
||||||
|
}
|
||||||
|
|
||||||
r := &Reader{
|
r := &Reader{
|
||||||
fi: fi,
|
fi: fi,
|
||||||
reader: f,
|
reader: f,
|
||||||
@@ -959,13 +1002,12 @@ func NewReader(f io.ReaderAt, size int64, fi *storage.FileInfo, cache cache.Name
|
|||||||
cmp: o.GetComparer(),
|
cmp: o.GetComparer(),
|
||||||
verifyChecksum: o.GetStrict(opt.StrictBlockChecksum),
|
verifyChecksum: o.GetStrict(opt.StrictBlockChecksum),
|
||||||
}
|
}
|
||||||
if f == nil {
|
|
||||||
return nil, errors.New("leveldb/table: nil file")
|
|
||||||
}
|
|
||||||
if size < footerLen {
|
if size < footerLen {
|
||||||
r.err = r.newErrCorrupted(0, size, "table", "too small")
|
r.err = r.newErrCorrupted(0, size, "table", "too small")
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
footerPos := size - footerLen
|
footerPos := size - footerLen
|
||||||
var footer [footerLen]byte
|
var footer [footerLen]byte
|
||||||
if _, err := r.reader.ReadAt(footer[:], footerPos); err != nil && err != io.EOF {
|
if _, err := r.reader.ReadAt(footer[:], footerPos); err != nil && err != io.EOF {
|
||||||
@@ -975,20 +1017,24 @@ func NewReader(f io.ReaderAt, size int64, fi *storage.FileInfo, cache cache.Name
|
|||||||
r.err = r.newErrCorrupted(footerPos, footerLen, "table-footer", "bad magic number")
|
r.err = r.newErrCorrupted(footerPos, footerLen, "table-footer", "bad magic number")
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var n int
|
||||||
// Decode the metaindex block handle.
|
// Decode the metaindex block handle.
|
||||||
metaBH, n := decodeBlockHandle(footer[:])
|
r.metaBH, n = decodeBlockHandle(footer[:])
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
r.err = r.newErrCorrupted(footerPos, footerLen, "table-footer", "bad metaindex block handle")
|
r.err = r.newErrCorrupted(footerPos, footerLen, "table-footer", "bad metaindex block handle")
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode the index block handle.
|
// Decode the index block handle.
|
||||||
r.indexBH, n = decodeBlockHandle(footer[n:])
|
r.indexBH, n = decodeBlockHandle(footer[n:])
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
r.err = r.newErrCorrupted(footerPos, footerLen, "table-footer", "bad index block handle")
|
r.err = r.newErrCorrupted(footerPos, footerLen, "table-footer", "bad index block handle")
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read metaindex block.
|
// Read metaindex block.
|
||||||
metaBlock, err := r.readBlock(metaBH, true)
|
metaBlock, err := r.readBlock(r.metaBH, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.IsCorrupted(err) {
|
if errors.IsCorrupted(err) {
|
||||||
r.err = err
|
r.err = err
|
||||||
@@ -997,9 +1043,12 @@ func NewReader(f io.ReaderAt, size int64, fi *storage.FileInfo, cache cache.Name
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set data end.
|
// Set data end.
|
||||||
r.dataEnd = int64(metaBH.offset)
|
r.dataEnd = int64(r.metaBH.offset)
|
||||||
metaIter := r.newBlockIter(metaBlock, nil, nil, false)
|
|
||||||
|
// Read metaindex.
|
||||||
|
metaIter := r.newBlockIter(metaBlock, nil, nil, true)
|
||||||
for metaIter.Next() {
|
for metaIter.Next() {
|
||||||
key := string(metaIter.Key())
|
key := string(metaIter.Key())
|
||||||
if !strings.HasPrefix(key, "filter.") {
|
if !strings.HasPrefix(key, "filter.") {
|
||||||
@@ -1044,13 +1093,12 @@ func NewReader(f io.ReaderAt, size int64, fi *storage.FileInfo, cache cache.Name
|
|||||||
if r.filter != nil {
|
if r.filter != nil {
|
||||||
r.filterBlock, err = r.readFilterBlock(r.filterBH)
|
r.filterBlock, err = r.readFilterBlock(r.filterBH)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.IsCorrupted(r.err) {
|
if !errors.IsCorrupted(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't use filter then.
|
// Don't use filter then.
|
||||||
r.filter = nil
|
r.filter = nil
|
||||||
r.filterBH = blockHandle{}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
Vendored
+1
-7
@@ -3,15 +3,9 @@ package table
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
|
|
||||||
"github.com/syndtr/goleveldb/leveldb/testutil"
|
"github.com/syndtr/goleveldb/leveldb/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTable(t *testing.T) {
|
func TestTable(t *testing.T) {
|
||||||
testutil.RunDefer()
|
testutil.RunSuite(t, "Table Suite")
|
||||||
|
|
||||||
RegisterFailHandler(Fail)
|
|
||||||
RunSpecs(t, "Table Suite")
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ type tableWrapper struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t tableWrapper) TestFind(key []byte) (rkey, rvalue []byte, err error) {
|
func (t tableWrapper) TestFind(key []byte) (rkey, rvalue []byte, err error) {
|
||||||
return t.Reader.Find(key, nil)
|
return t.Reader.Find(key, false, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t tableWrapper) TestGet(key []byte) (value []byte, err error) {
|
func (t tableWrapper) TestGet(key []byte) (value []byte, err error) {
|
||||||
|
|||||||
+5
@@ -35,6 +35,10 @@ type Get interface {
|
|||||||
TestGet(key []byte) (value []byte, err error)
|
TestGet(key []byte) (value []byte, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Has interface {
|
||||||
|
TestHas(key []byte) (ret bool, err error)
|
||||||
|
}
|
||||||
|
|
||||||
type NewIterator interface {
|
type NewIterator interface {
|
||||||
TestNewIterator(slice *util.Range) iterator.Iterator
|
TestNewIterator(slice *util.Range) iterator.Iterator
|
||||||
}
|
}
|
||||||
@@ -213,5 +217,6 @@ func DoDBTesting(t *DBTesting) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DoIteratorTesting(&it)
|
DoIteratorTesting(&it)
|
||||||
|
iter.Release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
package testutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RunSuite(t GinkgoTestingT, name string) {
|
||||||
|
RunDefer()
|
||||||
|
|
||||||
|
SynchronizedBeforeSuite(func() []byte {
|
||||||
|
RunDefer("setup")
|
||||||
|
return nil
|
||||||
|
}, func(data []byte) {})
|
||||||
|
SynchronizedAfterSuite(func() {
|
||||||
|
RunDefer("teardown")
|
||||||
|
}, func() {})
|
||||||
|
|
||||||
|
RegisterFailHandler(Fail)
|
||||||
|
RunSpecs(t, name)
|
||||||
|
}
|
||||||
+42
-6
@@ -26,9 +26,11 @@ func KeyValueTesting(rnd *rand.Rand, kv KeyValue, p DB, setup func(KeyValue) DB,
|
|||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
p = setup(kv)
|
p = setup(kv)
|
||||||
})
|
})
|
||||||
AfterEach(func() {
|
if teardown != nil {
|
||||||
teardown(p)
|
AfterEach(func() {
|
||||||
})
|
teardown(p)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
It("Should find all keys with Find", func() {
|
It("Should find all keys with Find", func() {
|
||||||
@@ -84,6 +86,26 @@ func KeyValueTesting(rnd *rand.Rand, kv KeyValue, p DB, setup func(KeyValue) DB,
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("Should only find present key with Has", func() {
|
||||||
|
if db, ok := p.(Has); ok {
|
||||||
|
ShuffledIndex(nil, kv.Len(), 1, func(i int) {
|
||||||
|
key_, key, _ := kv.IndexInexact(i)
|
||||||
|
|
||||||
|
// Using exact key.
|
||||||
|
ret, err := db.TestHas(key)
|
||||||
|
Expect(err).ShouldNot(HaveOccurred(), "Error for key %q", key)
|
||||||
|
Expect(ret).Should(BeTrue(), "False for key %q", key)
|
||||||
|
|
||||||
|
// Using inexact key.
|
||||||
|
if len(key_) > 0 {
|
||||||
|
ret, err = db.TestHas(key_)
|
||||||
|
Expect(err).ShouldNot(HaveOccurred(), "Error for key %q", key_)
|
||||||
|
Expect(ret).ShouldNot(BeTrue(), "True for key %q", key)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
TestIter := func(r *util.Range, _kv KeyValue) {
|
TestIter := func(r *util.Range, _kv KeyValue) {
|
||||||
if db, ok := p.(NewIterator); ok {
|
if db, ok := p.(NewIterator); ok {
|
||||||
iter := db.TestNewIterator(r)
|
iter := db.TestNewIterator(r)
|
||||||
@@ -95,6 +117,7 @@ func KeyValueTesting(rnd *rand.Rand, kv KeyValue, p DB, setup func(KeyValue) DB,
|
|||||||
}
|
}
|
||||||
|
|
||||||
DoIteratorTesting(&t)
|
DoIteratorTesting(&t)
|
||||||
|
iter.Release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +126,7 @@ func KeyValueTesting(rnd *rand.Rand, kv KeyValue, p DB, setup func(KeyValue) DB,
|
|||||||
done <- true
|
done <- true
|
||||||
}, 3.0)
|
}, 3.0)
|
||||||
|
|
||||||
RandomIndex(rnd, kv.Len(), kv.Len(), func(i int) {
|
RandomIndex(rnd, kv.Len(), Min(kv.Len(), 50), func(i int) {
|
||||||
type slice struct {
|
type slice struct {
|
||||||
r *util.Range
|
r *util.Range
|
||||||
start, limit int
|
start, limit int
|
||||||
@@ -121,7 +144,7 @@ func KeyValueTesting(rnd *rand.Rand, kv KeyValue, p DB, setup func(KeyValue) DB,
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
RandomRange(rnd, kv.Len(), kv.Len(), func(start, limit int) {
|
RandomRange(rnd, kv.Len(), Min(kv.Len(), 50), func(start, limit int) {
|
||||||
It(fmt.Sprintf("Should iterates and seeks correctly of a slice %d .. %d", start, limit), func(done Done) {
|
It(fmt.Sprintf("Should iterates and seeks correctly of a slice %d .. %d", start, limit), func(done Done) {
|
||||||
r := kv.Range(start, limit)
|
r := kv.Range(start, limit)
|
||||||
TestIter(&r, kv.Slice(start, limit))
|
TestIter(&r, kv.Slice(start, limit))
|
||||||
@@ -134,10 +157,22 @@ func AllKeyValueTesting(rnd *rand.Rand, body, setup func(KeyValue) DB, teardown
|
|||||||
Test := func(kv *KeyValue) func() {
|
Test := func(kv *KeyValue) func() {
|
||||||
return func() {
|
return func() {
|
||||||
var p DB
|
var p DB
|
||||||
|
if setup != nil {
|
||||||
|
Defer("setup", func() {
|
||||||
|
p = setup(*kv)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if teardown != nil {
|
||||||
|
Defer("teardown", func() {
|
||||||
|
teardown(p)
|
||||||
|
})
|
||||||
|
}
|
||||||
if body != nil {
|
if body != nil {
|
||||||
p = body(*kv)
|
p = body(*kv)
|
||||||
}
|
}
|
||||||
KeyValueTesting(rnd, *kv, p, setup, teardown)
|
KeyValueTesting(rnd, *kv, p, func(KeyValue) DB {
|
||||||
|
return p
|
||||||
|
}, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,4 +183,5 @@ func AllKeyValueTesting(rnd *rand.Rand, body, setup func(KeyValue) DB, teardown
|
|||||||
Describe("with big value", Test(KeyValue_BigValue()))
|
Describe("with big value", Test(KeyValue_BigValue()))
|
||||||
Describe("with special key", Test(KeyValue_SpecialKey()))
|
Describe("with special key", Test(KeyValue_SpecialKey()))
|
||||||
Describe("with multiple key/value", Test(KeyValue_MultipleKeyValue()))
|
Describe("with multiple key/value", Test(KeyValue_MultipleKeyValue()))
|
||||||
|
Describe("with generated key/value", Test(KeyValue_Generate(nil, 120, 1, 50, 10, 120)))
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -397,6 +397,7 @@ func (s *Storage) logI(format string, args ...interface{}) {
|
|||||||
|
|
||||||
func (s *Storage) Log(str string) {
|
func (s *Storage) Log(str string) {
|
||||||
s.log(1, "Log: "+str)
|
s.log(1, "Log: "+str)
|
||||||
|
s.Storage.Log(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Storage) Lock() (r util.Releaser, err error) {
|
func (s *Storage) Lock() (r util.Releaser, err error) {
|
||||||
|
|||||||
+14
@@ -155,3 +155,17 @@ func RandomRange(rnd *rand.Rand, n, round int, fn func(start, limit int)) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Max(x, y int) int {
|
||||||
|
if x > y {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|
||||||
|
func Min(x, y int) int {
|
||||||
|
if x < y {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|||||||
+4
@@ -34,6 +34,10 @@ func (t *testingDB) TestGet(key []byte) (value []byte, err error) {
|
|||||||
return t.Get(key, t.ro)
|
return t.Get(key, t.ro)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *testingDB) TestHas(key []byte) (ret bool, err error) {
|
||||||
|
return t.Has(key, t.ro)
|
||||||
|
}
|
||||||
|
|
||||||
func (t *testingDB) TestNewIterator(slice *util.Range) iterator.Iterator {
|
func (t *testingDB) TestNewIterator(slice *util.Range) iterator.Iterator {
|
||||||
return t.NewIterator(slice, t.ro)
|
return t.NewIterator(slice, t.ro)
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user