mirror of
https://github.com/syncthing/syncthing.git
synced 2026-08-07 05:43:24 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b2a643626 | ||
|
|
1ebc9a9a88 | ||
|
|
f0c8b7ce40 | ||
|
|
ec54550f21 | ||
|
|
4474d200b0 | ||
|
|
f062e35641 | ||
|
|
321ef9816c | ||
|
|
be01e925c7 | ||
|
|
6d11006b54 | ||
|
|
ed792b97c0 | ||
|
|
a4b8c2298a | ||
|
|
e5b33ce9f6 | ||
|
|
30374d46c9 | ||
|
|
9edf8233f7 | ||
|
|
bd4a14519c | ||
|
|
f12bf8c09a | ||
|
|
53cc45a0d5 | ||
|
|
fa4b4dece1 | ||
|
|
02f044a2a1 | ||
|
|
431d51f5c4 | ||
|
|
45c1357bab | ||
|
|
5136675fae | ||
|
|
db4f23f377 | ||
|
|
6ff02761a9 | ||
|
|
d46f267663 | ||
|
|
6a98534d5d | ||
|
|
eeb5d99942 | ||
|
|
dcc5f333c1 | ||
|
|
ff8a66d22f | ||
|
|
f7ad97918a | ||
|
|
9047d56aa0 | ||
|
|
945ddc2403 | ||
|
|
92158b0611 | ||
|
|
bed6155c79 | ||
|
|
f2459e61dd | ||
|
|
69dcdafb3d | ||
|
|
6dbb072d11 | ||
|
|
dc96849718 | ||
|
|
d7a3cc505c | ||
|
|
c6936ced6c | ||
|
|
91048f655c | ||
|
|
66bf524394 | ||
|
|
ba9448bdd7 | ||
|
|
c5cb5cba18 | ||
|
|
aa853ac833 | ||
|
|
8b759d0e1e | ||
|
|
a8a2192cf9 | ||
|
|
37f866b47f | ||
|
|
9cf653d673 | ||
|
|
0167b4b4a3 | ||
|
|
dcb773e446 | ||
|
|
b1a86fbc98 | ||
|
|
cdd0cf7f18 | ||
|
|
9ae419201d | ||
|
|
e37cefdbee | ||
|
|
ad8c266f76 | ||
|
|
807c3bdcc7 | ||
|
|
62efbd17de | ||
|
|
d99350fd61 | ||
|
|
40c70a9a2a | ||
|
|
8fb7f40a6a | ||
|
|
2f12d41d9d | ||
|
|
9fbdb6b305 | ||
|
|
73285cadb6 | ||
|
|
56f1c295b6 | ||
|
|
7f76ed8413 | ||
|
|
f7edd36931 | ||
|
|
c39f2b7c05 | ||
|
|
342036408e | ||
|
|
f4904fce17 | ||
|
|
2abb2de753 | ||
|
|
ef0a0db07e | ||
|
|
a45795efec | ||
|
|
88ae353aef | ||
|
|
97b9690711 | ||
|
|
48ce356d5c | ||
|
|
92451f94bc | ||
|
|
593632045d | ||
|
|
e3c55ef307 | ||
|
|
edf1730fd2 | ||
|
|
34cd8e3f95 | ||
|
|
242cce022a | ||
|
|
19bf51cefb | ||
|
|
74a2e80142 | ||
|
|
b9b630e3b6 | ||
|
|
f0bdf833d1 | ||
|
|
81bc6bf34b | ||
|
|
7de736e8d0 | ||
|
|
cd097e0fce | ||
|
|
cc81a7ccfe | ||
|
|
58d320c270 | ||
|
|
59565fd1d1 | ||
|
|
55592137a2 | ||
|
|
58523060f0 | ||
|
|
07d53be9fc | ||
|
|
d4b0235a8b | ||
|
|
34aa41e17b | ||
|
|
36f6a9347c | ||
|
|
d49d386ef2 | ||
|
|
00c363829c | ||
|
|
a9691dbdf4 | ||
|
|
9df701906f | ||
|
|
283671fa9d | ||
|
|
435c29755d | ||
|
|
686f91777c | ||
|
|
2aa028facb | ||
|
|
b4bbd050c2 | ||
|
|
2a4fc28318 | ||
|
|
313485e406 | ||
|
|
faf4267c73 | ||
|
|
e6277d799f | ||
|
|
cdbc8004fb | ||
|
|
1fac2f686d | ||
|
|
08c8d679ac | ||
|
|
48c34b7234 | ||
|
|
28603f0d2c | ||
|
|
b2855f02fe | ||
|
|
bef3d88076 | ||
|
|
e1a8ea7dec | ||
|
|
c4ad97136f | ||
|
|
eab1d6782b | ||
|
|
fd7b8ec77e | ||
|
|
e28c991331 | ||
|
|
a52811dfa3 | ||
|
|
9e210d705d | ||
|
|
c42f1b53ab | ||
|
|
d171173e90 | ||
|
|
679f0f9363 | ||
|
|
724c1e297f | ||
|
|
83154569b1 | ||
|
|
e3c0fba34b | ||
|
|
2b6a6b91f3 | ||
|
|
09a555fdd2 | ||
|
|
dc32f7f0a3 | ||
|
|
1efd8d6c75 | ||
|
|
898fc72313 | ||
|
|
21c5806cbf | ||
|
|
464e6bec95 | ||
|
|
2ae832d919 | ||
|
|
5b03c2d949 | ||
|
|
f629a998a0 | ||
|
|
fe88781bc8 | ||
|
|
e725c97967 | ||
|
|
63caf22671 | ||
|
|
44790b1333 | ||
|
|
b40bb64612 | ||
|
|
7b5ab29a6d | ||
|
|
4fd614be09 | ||
|
|
73236e58c5 | ||
|
|
32414853c6 | ||
|
|
f3dc78d457 | ||
|
|
a32ac62208 | ||
|
|
d7a934cf0e | ||
|
|
503491392d | ||
|
|
b3a2bf367b | ||
|
|
c19eff4872 | ||
|
|
2941a813c2 | ||
|
|
0a022d38fa | ||
|
|
7ed3b3dd3a | ||
|
|
ce52963d2b | ||
|
|
9a1922fdc6 | ||
|
|
967424a538 | ||
|
|
83131103cf | ||
|
|
9f4a0d3216 | ||
|
|
c1591a5efd | ||
|
|
918ef4dff8 | ||
|
|
0d9a04c713 | ||
|
|
0c0c69f0cf | ||
|
|
943e80e26c | ||
|
|
058a327584 | ||
|
|
1eca4170f7 | ||
|
|
c268e4ad1b | ||
|
|
d4f81e8791 | ||
|
|
4f0680c3c8 | ||
|
|
8c26fe44c3 | ||
|
|
8c7d9f3dd2 | ||
|
|
f241b7e79a | ||
|
|
dc1f3503be | ||
|
|
c2a5e180b8 | ||
|
|
7351217489 | ||
|
|
aa42aafe33 | ||
|
|
b2da0120d6 | ||
|
|
9e84e09c26 | ||
|
|
32c1a9bc45 | ||
|
|
a7e95922c1 | ||
|
|
1392d0bc14 | ||
|
|
0f9fa9507e | ||
|
|
1087535d8f | ||
|
|
0e51f51979 | ||
|
|
90e0141ac5 | ||
|
|
8435a8678e | ||
|
|
bd2888fc3b | ||
|
|
6578ffe2c9 | ||
|
|
175340522f | ||
|
|
afc917b582 | ||
|
|
9f4cd7716e | ||
|
|
29b0017445 | ||
|
|
910a7c619a | ||
|
|
273fac2028 | ||
|
|
a323d85d32 | ||
|
|
491a33de0b | ||
|
|
d6a0a44432 | ||
|
|
752533489a | ||
|
|
e4e3c19e96 | ||
|
|
4ddb066728 | ||
|
|
958bbbc8cb | ||
|
|
e15be5c2bf | ||
|
|
cc436dc8cb | ||
|
|
abbcd1f436 | ||
|
|
db494f2afc | ||
|
|
985ea29940 | ||
|
|
76359da58e | ||
|
|
368cd44558 | ||
|
|
cc1387ec0c | ||
|
|
7c79985a29 | ||
|
|
2b56961b54 | ||
|
|
ff9920cbdc | ||
|
|
953a67bc3a | ||
|
|
29343aec3a | ||
|
|
2972472179 | ||
|
|
240e7b0835 | ||
|
|
baf5191433 | ||
|
|
2645e87766 | ||
|
|
ec8bc02d33 | ||
|
|
054bc970e2 | ||
|
|
c1c41242bb | ||
|
|
169ff73d26 | ||
|
|
d985ed553a | ||
|
|
dea1ef24d9 | ||
|
|
49f29a0453 | ||
|
|
76af9ba53d | ||
|
|
2de364414f | ||
|
|
5ae84970e7 | ||
|
|
141b0d38a6 | ||
|
|
44891b6924 | ||
|
|
f008588307 | ||
|
|
e481d03b5e | ||
|
|
a8a73b60c4 | ||
|
|
7e8b76e8ea | ||
|
|
36c746bd9f | ||
|
|
7476c583e7 | ||
|
|
89928ca8e4 | ||
|
|
38a3bf3ada | ||
|
|
96b3d31b42 | ||
|
|
362ae5c4bb | ||
|
|
dc303c2a71 | ||
|
|
be2ca0ea22 | ||
|
|
460cb19839 | ||
|
|
ddfebb17cf | ||
|
|
375c9dd116 | ||
|
|
15716a0772 | ||
|
|
6f6c1cd330 | ||
|
|
36ac757c3a | ||
|
|
b614cfffcb | ||
|
|
b58a52c7b4 | ||
|
|
a80fc1b062 | ||
|
|
90d18189da | ||
|
|
22a2e95126 | ||
|
|
11e1a99e14 | ||
|
|
3c6bfb880d | ||
|
|
ad2c05c3f5 | ||
|
|
fa75f54a05 | ||
|
|
6405fd4770 | ||
|
|
f42f20c70c | ||
|
|
209bc59e8c | ||
|
|
84ee86f6b3 | ||
|
|
ed7791d824 | ||
|
|
0580b10385 | ||
|
|
47c7351b16 | ||
|
|
b158072a15 | ||
|
|
b6486c26e6 | ||
|
|
da0ac96704 | ||
|
|
af1fbda892 | ||
|
|
2234c45c19 | ||
|
|
fd38fb684a | ||
|
|
e0a16e08dd | ||
|
|
43189dfe3a | ||
|
|
46d4f6037d | ||
|
|
e522811a52 | ||
|
|
6124bbb12a | ||
|
|
fbf911cf7e | ||
|
|
7fdfa81fb8 | ||
|
|
a4673f3007 | ||
|
|
24c499d282 | ||
|
|
4581c57478 | ||
|
|
f177924629 | ||
|
|
633e888ba7 | ||
|
|
4316992d95 | ||
|
|
5ecb8bdd8a | ||
|
|
3b81d4b8a5 | ||
|
|
6e3b3dc4e7 | ||
|
|
8d421a62d2 | ||
|
|
185b0690c8 | ||
|
|
bd0e97023e | ||
|
|
34ff0706a3 | ||
|
|
acba61babb | ||
|
|
f91191218b | ||
|
|
05c79ac8c2 | ||
|
|
24d2a93c0d | ||
|
|
de43080228 | ||
|
|
b0cd7be39b | ||
|
|
a7169a6348 | ||
|
|
6e126fb97e | ||
|
|
22783d8f6c | ||
|
|
1d710bdcd9 | ||
|
|
5feffba1ff | ||
|
|
6c56586a6b | ||
|
|
a32b66c7c3 | ||
|
|
7e3c06191e | ||
|
|
372c96c6b2 | ||
|
|
7073b8721a | ||
|
|
fccb9c0bf4 | ||
|
|
3d09090c4e | ||
|
|
596a49c112 | ||
|
|
95fc253d6b | ||
|
|
e6d5372029 | ||
|
|
8e5c692244 | ||
|
|
e694c664e5 | ||
|
|
8779f93746 | ||
|
|
1f0f5c1e23 | ||
|
|
f9f12131ae | ||
|
|
7e0106da0c | ||
|
|
aaf6bf3cd2 | ||
|
|
fa95c82daf | ||
|
|
0da7142a59 | ||
|
|
446a938b06 | ||
|
|
21c3994650 | ||
|
|
f90f3a5ca6 | ||
|
|
a34d2b72c0 | ||
|
|
9d617dcfec | ||
|
|
55506a0fc3 | ||
|
|
19c03f504f | ||
|
|
985a3436e2 | ||
|
|
9dae87c80c | ||
|
|
46364a38c6 | ||
|
|
148b2b9d02 | ||
|
|
64354b51c9 | ||
|
|
d180bc794b | ||
|
|
eab5fd5bdd | ||
|
|
e3ca797dad | ||
|
|
f2db1c8ab2 | ||
|
|
36b8a75ede | ||
|
|
11b2815b88 | ||
|
|
0a42b85c06 | ||
|
|
baf231e3b6 | ||
|
|
e26e85b6d6 | ||
|
|
b2193b23e5 | ||
|
|
2af3a92833 | ||
|
|
d2af6dcf38 | ||
|
|
dd6b66167e | ||
|
|
51a4a88a81 | ||
|
|
516efb21cf | ||
|
|
2d4397af53 | ||
|
|
06f319a380 | ||
|
|
37ed5a01e0 | ||
|
|
4a9997e449 | ||
|
|
54269553c8 | ||
|
|
541d05df1b | ||
|
|
3d6ea23511 | ||
|
|
c0554c9fbf | ||
|
|
61130ea191 | ||
|
|
2581e56503 | ||
|
|
fea0ae7f2f | ||
|
|
3299438cbd | ||
|
|
5c160048df | ||
|
|
e3e1036dda | ||
|
|
876d7ac85e | ||
|
|
70b37dc469 | ||
|
|
6393f69138 | ||
|
|
0ca39f6c65 | ||
|
|
02493251d5 | ||
|
|
ff04648112 | ||
|
|
55002d7adf | ||
|
|
0664c6b5b0 | ||
|
|
a4ebac147b | ||
|
|
cf802dc67e | ||
|
|
84365882de | ||
|
|
37fb6473b3 | ||
|
|
ba676f2810 | ||
|
|
b3d7c622c3 | ||
|
|
bc016e360e | ||
|
|
594918dd3c | ||
|
|
ec5feb41e8 | ||
|
|
94c52e3a77 | ||
|
|
875de4f637 | ||
|
|
72fa5a69f1 | ||
|
|
d63e54237b | ||
|
|
e256d93b43 | ||
|
|
4d12df5424 | ||
|
|
cae120fd4d | ||
|
|
be332a6223 | ||
|
|
bda0bb6f13 | ||
|
|
68c5dcd83d | ||
|
|
9bdcadf634 | ||
|
|
037be433f8 | ||
|
|
2bed62dd9e | ||
|
|
a27bc4ebea | ||
|
|
d6e34761dc | ||
|
|
98effcd8e3 | ||
|
|
baa87bc823 | ||
|
|
944d9c84a0 | ||
|
|
1e447741ee | ||
|
|
4405ac7386 | ||
|
|
e1190f0f0f | ||
|
|
a7f2416c0c | ||
|
|
40d0100132 | ||
|
|
37f7c48cfc | ||
|
|
21adf752c8 | ||
|
|
aec143b882 | ||
|
|
f691040936 | ||
|
|
42acf0ed60 | ||
|
|
ca4a3589e5 | ||
|
|
2eead17224 | ||
|
|
626b26a227 | ||
|
|
c46db0761e | ||
|
|
cfed06697d | ||
|
|
a0d9183b14 | ||
|
|
d3eb674b30 | ||
|
|
7fe1fdd8c7 | ||
|
|
37cbe68204 | ||
|
|
f76a66fc55 | ||
|
|
d7949aa58e | ||
|
|
f0c0c5483f | ||
|
|
7d444021bb | ||
|
|
388a29bbe2 | ||
|
|
a03dd1bd41 | ||
|
|
4b366f2857 | ||
|
|
c87faace6b | ||
|
|
e2be051558 | ||
|
|
1e8b185377 | ||
|
|
031804827f | ||
|
|
6cccd9b6fc | ||
|
|
687fbb0a7e | ||
|
|
8f2db99c86 | ||
|
|
2c0f8dc546 | ||
|
|
a388fb0bb7 | ||
|
|
27465353c1 | ||
|
|
c2ccab4361 | ||
|
|
bb876eac82 | ||
|
|
34c04babbe | ||
|
|
7c6a310179 | ||
|
|
50702eda94 | ||
|
|
59eeafbdfa | ||
|
|
abc606608c | ||
|
|
1487552b48 | ||
|
|
c7dbe18df6 | ||
|
|
c2bc3358cc | ||
|
|
47a1494d68 | ||
|
|
dbb388719e | ||
|
|
283c91548a | ||
|
|
38b93bd310 | ||
|
|
8dcc30ac83 | ||
|
|
0ee123375d | ||
|
|
be18cbef8b | ||
|
|
6a36ec63d7 | ||
|
|
9c8b907ff1 | ||
|
|
f769df16e8 | ||
|
|
c6f5075721 | ||
|
|
8eb494c13e | ||
|
|
b6b6375f70 | ||
|
|
8783688391 | ||
|
|
98afc3e99c | ||
|
|
50a1858367 | ||
|
|
f3f586773b | ||
|
|
61a182077f | ||
|
|
1c9513e770 | ||
|
|
5e5eb9bf8e | ||
|
|
7a9bb65e03 | ||
|
|
a5345ac71e | ||
|
|
ae5079f7b4 | ||
|
|
ea1ecfbc38 | ||
|
|
a84b6b4bcc | ||
|
|
93023128fd | ||
|
|
77157f16a1 | ||
|
|
99736e5066 | ||
|
|
681306b7a1 | ||
|
|
5f36c9d4de | ||
|
|
6cbc8791b1 | ||
|
|
c08de67b0d | ||
|
|
b21e18dfad | ||
|
|
1e497915be | ||
|
|
3704c41dda | ||
|
|
6ff31ac666 | ||
|
|
a2f73a7d35 | ||
|
|
1492e57676 | ||
|
|
7504fc53b6 | ||
|
|
daa2bcefad | ||
|
|
49aa9399be | ||
|
|
a71090df81 | ||
|
|
0bfcafc5c6 | ||
|
|
161d5c8379 | ||
|
|
5cfb578170 | ||
|
|
9b0d47e9eb | ||
|
|
13f4706067 | ||
|
|
7ebdb1736f | ||
|
|
2bcb57c994 | ||
|
|
a2df691c7d | ||
|
|
58f1191f2d | ||
|
|
dfaa999291 | ||
|
|
a693698279 | ||
|
|
6a58033f2b | ||
|
|
7705a6c1f1 | ||
|
|
0a803891a4 | ||
|
|
7d620a93b9 | ||
|
|
3f3b2f4c99 | ||
|
|
9eb4089710 | ||
|
|
257d1afdf8 | ||
|
|
dad1fb7805 | ||
|
|
e312fdd4f8 | ||
|
|
9b6681d543 | ||
|
|
bcc04623c1 | ||
|
|
d75d162058 | ||
|
|
8f2294bbd4 | ||
|
|
d1b91c7619 | ||
|
|
1b6b481fcc | ||
|
|
dd64ba1910 | ||
|
|
82a5d1cd79 | ||
|
|
c26c172d01 | ||
|
|
a7b7aaa7cb | ||
|
|
4e5c02c05c | ||
|
|
2baf61fda3 | ||
|
|
219a25fe80 | ||
|
|
b1dd704819 | ||
|
|
9513e91d66 | ||
|
|
26d52bedb3 | ||
|
|
19451e0654 | ||
|
|
fa922d7792 | ||
|
|
4949e3ba41 | ||
|
|
bbe1de3119 | ||
|
|
f87e9b596d | ||
|
|
917e12952e | ||
|
|
1977c526e4 | ||
|
|
b63351074c | ||
|
|
ebcdea63c0 | ||
|
|
d78eb1247e | ||
|
|
9b9fe0d65c | ||
|
|
5a2db802d9 | ||
|
|
d3972b88f2 | ||
|
|
e62cf13760 | ||
|
|
a5f6e3bba0 | ||
|
|
d170660c25 | ||
|
|
2202aaed51 | ||
|
|
cbefcd50cf | ||
|
|
1acfa291a0 | ||
|
|
21accd534c | ||
|
|
eb29989dff | ||
|
|
de89d7a976 | ||
|
|
78ef42daa1 | ||
|
|
319abebd70 | ||
|
|
76480adda5 | ||
|
|
e205f8afbb | ||
|
|
42dc51784e | ||
|
|
a4a46f480d | ||
|
|
e34be16237 | ||
|
|
dfcc166918 | ||
|
|
895d56ed04 | ||
|
|
12eab4a8ba | ||
|
|
3eb2b1f7a2 | ||
|
|
6ecc9bf93a | ||
|
|
22e24fc387 | ||
|
|
516d88b072 | ||
|
|
da4ebb6535 | ||
|
|
77457e91e9 | ||
|
|
6e4d33c741 | ||
|
|
d3387e2a28 | ||
|
|
491452a19d | ||
|
|
7d3257b222 | ||
|
|
1836ef2884 | ||
|
|
43d6322d0f | ||
|
|
f0684d83e9 | ||
|
|
3f3170818d | ||
|
|
7683096fe1 | ||
|
|
bb438bfb17 | ||
|
|
a11aa295de | ||
|
|
049d92b525 | ||
|
|
f9bd59f031 | ||
|
|
6d0d5bd566 | ||
|
|
35d20a19bc | ||
|
|
dab1c4cfc9 | ||
|
|
9a50f4ac1f | ||
|
|
59e829e595 | ||
|
|
f86946c6df | ||
|
|
e97f75cad5 | ||
|
|
2505f82ce5 | ||
|
|
78dca5fe8b | ||
|
|
8c816f64e4 | ||
|
|
22c525e3fe | ||
|
|
f3f6b03d85 | ||
|
|
00bebc317e | ||
|
|
8f38e83aaf | ||
|
|
8fab7ec5e3 | ||
|
|
50eb968109 | ||
|
|
569314be45 | ||
|
|
909d60464e | ||
|
|
d855abf8b0 | ||
|
|
b611f72e08 | ||
|
|
44e3bec42e | ||
|
|
a04b005e93 | ||
|
|
1b837116e6 | ||
|
|
d16b04b683 | ||
|
|
d2e7a8004d | ||
|
|
7996ef0d45 | ||
|
|
0c28216ee5 | ||
|
|
b05c1a5bb9 | ||
|
|
5bb8ea7449 | ||
|
|
b78c515724 | ||
|
|
cb1a7a7bdc | ||
|
|
b8dcb7c884 | ||
|
|
9dd6f848bd | ||
|
|
1ded554a15 | ||
|
|
bc0ce7b820 | ||
|
|
1da3a57fe7 | ||
|
|
8697982302 | ||
|
|
c4168cf855 | ||
|
|
7023d3ca2b | ||
|
|
57a5d13c47 | ||
|
|
500b96240b | ||
|
|
13d961d41d | ||
|
|
fddc4c2fc0 | ||
|
|
061ec7369f | ||
|
|
8366dbd8e0 | ||
|
|
b02047e4b5 | ||
|
|
e9545c4961 | ||
|
|
966a2b1df5 | ||
|
|
dec6540967 | ||
|
|
3fe1673ce9 | ||
|
|
e9e13474c9 | ||
|
|
aee9093848 | ||
|
|
76822c7c34 | ||
|
|
5c18d34d89 | ||
|
|
970a9c7552 | ||
|
|
37a42dc408 | ||
|
|
a03c9f9457 | ||
|
|
60004ebff1 | ||
|
|
2d9fcf6828 | ||
|
|
e1959afb6b | ||
|
|
c68c78d412 | ||
|
|
c8ac9721d7 | ||
|
|
b72d31f87f | ||
|
|
b1b68b58fe | ||
|
|
95e15c95f2 | ||
|
|
ca21db9481 | ||
|
|
93ad803073 | ||
|
|
6cc7f70a65 | ||
|
|
9f871a3726 | ||
|
|
e19e2c123e | ||
|
|
cbe44e1fff | ||
|
|
2b0c33f74d | ||
|
|
dae1d36a23 | ||
|
|
824fa8f17a | ||
|
|
31cd0b943c | ||
|
|
8e191c8e6b | ||
|
|
070eced2f6 | ||
|
|
986f8dfb2e | ||
|
|
19d742b9e4 | ||
|
|
8c0c03eb38 | ||
|
|
fd9bc20bc5 | ||
|
|
089fca2319 | ||
|
|
e936890927 | ||
|
|
0450d48f89 | ||
|
|
2463819a3d | ||
|
|
2b2cae2d50 | ||
|
|
0f1b40da71 | ||
|
|
f73d5a9ab2 | ||
|
|
4eb0e24c6e | ||
|
|
1d2235abe7 | ||
|
|
d347e54acb | ||
|
|
b5198d8119 | ||
|
|
b8b5c5ff34 | ||
|
|
a738490a3b | ||
|
|
54a8de2059 | ||
|
|
cb2c0e7ac5 | ||
|
|
510d309b8a | ||
|
|
a7ce2a7aa5 | ||
|
|
cfe24ecdd9 | ||
|
|
c5e9cb025c | ||
|
|
c3d07d60ca | ||
|
|
a0897a7456 | ||
|
|
c50ba9267c | ||
|
|
423e69916c | ||
|
|
b56c76f8ad | ||
|
|
cb2d2f000f | ||
|
|
69af77a3bd | ||
|
|
7767746d3e | ||
|
|
7219aaeb89 | ||
|
|
7af1863e81 | ||
|
|
4beb42bf45 | ||
|
|
12a3086a9e | ||
|
|
198725216f | ||
|
|
08647f1267 | ||
|
|
87811efc30 | ||
|
|
82c3e6f87f | ||
|
|
1ac40a3043 | ||
|
|
127b0c3332 | ||
|
|
a6d9150b14 | ||
|
|
7e5197c566 | ||
|
|
2d217e72bd | ||
|
|
12331cc62b | ||
|
|
2449f1e1b6 | ||
|
|
6a6593c656 | ||
|
|
ad220d61f9 | ||
|
|
1e35383b4d | ||
|
|
c8457ab005 | ||
|
|
070a308217 | ||
|
|
c1f4477376 | ||
|
|
d728320ece | ||
|
|
fee0d7168a | ||
|
|
7c23b32de3 | ||
|
|
1437952aee | ||
|
|
a162157301 | ||
|
|
6316bf3582 | ||
|
|
1d856b4723 | ||
|
|
7f56d5c23a | ||
|
|
a778763851 | ||
|
|
983d7ec265 | ||
|
|
297769ef57 | ||
|
|
885d050e5f | ||
|
|
8fb4ce6cad | ||
|
|
42738ab54d | ||
|
|
7d1250620e | ||
|
|
5c49b93c67 | ||
|
|
9a11f81fd3 | ||
|
|
cba2e972fd | ||
|
|
76ad925842 | ||
|
|
ef6f52f688 | ||
|
|
197bfa9f11 | ||
|
|
145f8c7435 | ||
|
|
a8b43ae598 | ||
|
|
567f19bf68 | ||
|
|
5cd4cd2271 | ||
|
|
4180569443 | ||
|
|
5f4a92c8e6 | ||
|
|
ccf3fed950 | ||
|
|
3626003f68 | ||
|
|
11cb040ad1 | ||
|
|
c1761cab49 | ||
|
|
25b25b5434 | ||
|
|
8bdf66d9c0 | ||
|
|
ccebdd142a | ||
|
|
e952da7f91 | ||
|
|
5bd1e4a167 | ||
|
|
6d3de41751 | ||
|
|
f11bac6705 | ||
|
|
c23a601cc6 | ||
|
|
36d4c69fd6 | ||
|
|
ca7e7fa0c4 | ||
|
|
1f6dd5dbb9 | ||
|
|
db52646655 | ||
|
|
8bb18fa988 | ||
|
|
f0edaf2f8c | ||
|
|
d632e3aa1b | ||
|
|
f0e58fa804 | ||
|
|
ceced09d02 | ||
|
|
7d48115b90 | ||
|
|
d2205228fb | ||
|
|
5417fb7287 | ||
|
|
3f59d6daff | ||
|
|
0d659933aa | ||
|
|
3e8eabe833 | ||
|
|
badfc77339 | ||
|
|
c51d3e59ea | ||
|
|
c0d02a65c3 | ||
|
|
3a203b8d83 | ||
|
|
feecdcc7a4 | ||
|
|
51ad533be6 | ||
|
|
29da0bc8f5 | ||
|
|
bccf7fc2a8 | ||
|
|
7b6b5981c4 | ||
|
|
9463192224 | ||
|
|
d1689f0012 | ||
|
|
8ed67fe349 | ||
|
|
90a1d99785 | ||
|
|
e215cf6fb8 | ||
|
|
e827c0bd94 | ||
|
|
8dd7e4e6b5 | ||
|
|
a2b94f4e06 | ||
|
|
8b0037ffab | ||
|
|
4ab03f3bef | ||
|
|
3c3db52f49 | ||
|
|
5b1e884659 | ||
|
|
7ec6740e26 | ||
|
|
f12b8c19be | ||
|
|
76174d31ce | ||
|
|
5042248260 | ||
|
|
1df6589533 | ||
|
|
12f76b448c | ||
|
|
f112ef34f6 | ||
|
|
e4b57a978f | ||
|
|
b51e09e0e8 | ||
|
|
a3ba3f895c | ||
|
|
947a129e12 | ||
|
|
f3fe6a6cbd | ||
|
|
9d150bef9f | ||
|
|
65be18cc93 | ||
|
|
e27ea63900 | ||
|
|
aa96f7b660 | ||
|
|
053690d885 | ||
|
|
b9fc6397a3 | ||
|
|
19b9f15da3 | ||
|
|
0b9441e1a4 | ||
|
|
2324d7420c | ||
|
|
c6b2ca8b19 | ||
|
|
83ea8dc577 | ||
|
|
d898277f62 | ||
|
|
a289cfb986 | ||
|
|
d603998617 | ||
|
|
8bf9f4f5ab | ||
|
|
2c4e6f2926 | ||
|
|
21fbbc50cd | ||
|
|
99512418da | ||
|
|
c2f2d8771f | ||
|
|
179c9ee8cc | ||
|
|
eb8a505287 | ||
|
|
a7482a3644 | ||
|
|
a692348336 | ||
|
|
285dcc30cf | ||
|
|
bcf51aed83 | ||
|
|
1a11ce6211 | ||
|
|
10021c97a6 | ||
|
|
e869e3c534 | ||
|
|
f6416285db | ||
|
|
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 | ||
|
|
e7db264803 | ||
|
|
0cb7ed9e4e | ||
|
|
4b07609458 | ||
|
|
e41e58e781 | ||
|
|
f5030f1c2c | ||
|
|
2a48fb8e87 | ||
|
|
3d8a71fdb2 | ||
|
|
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 | ||
|
|
6277c0595c | ||
|
|
aa9eda1979 | ||
|
|
1d76efcbcd | ||
|
|
34c2c1ec16 | ||
|
|
bf7fea9a0a | ||
|
|
fdf15f3ca3 | ||
|
|
1cb5875b20 | ||
|
|
1a59a5478f | ||
|
|
fc0cb704f2 | ||
|
|
960c0cbddf | ||
|
|
5750443371 | ||
|
|
9f67d86b30 | ||
|
|
66f7d83baa | ||
|
|
b44e87c6e8 | ||
|
|
6da7f17c4a | ||
|
|
b4f45d1e79 | ||
|
|
7d766bf7c7 | ||
|
|
f9132cae85 | ||
|
|
75dc7e6671 | ||
|
|
3fd887fc57 | ||
|
|
128447a681 | ||
|
|
17149741a7 | ||
|
|
23bae932c7 | ||
|
|
d2ec40bb67 | ||
|
|
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 | ||
|
|
1a4398cc55 | ||
|
|
5253368acc | ||
|
|
51cfc3d4be | ||
|
|
80bffd93e7 | ||
|
|
df4f22e899 | ||
|
|
529d91fb9d | ||
|
|
8cc70843a5 | ||
|
|
70c841f23a | ||
|
|
108b4e2e10 | ||
|
|
7b22e09805 | ||
|
|
c5838c143c | ||
|
|
eaf71db7c9 | ||
|
|
b322b527b3 | ||
|
|
cc4b231875 | ||
|
|
05642a3e17 | ||
|
|
7dcc6bb579 | ||
|
|
f15c416e59 | ||
|
|
6fa97eeec7 | ||
|
|
03bbf273b3 | ||
|
|
cd0cce4195 | ||
|
|
9f1a72ec88 | ||
|
|
1e376cd3a6 | ||
|
|
49cf939c04 | ||
|
|
338394f8c3 | ||
|
|
575b62d77b | ||
|
|
57fc0eb5b1 | ||
|
|
3a19fe3663 | ||
|
|
a10c621e33 | ||
|
|
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 | ||
|
|
2e2d479103 | ||
|
|
1f5c124ac4 | ||
|
|
64a5bc038a | ||
|
|
5d9396334c | ||
|
|
35f0e355bf | ||
|
|
ec160f1f0a | ||
|
|
bbaeca96eb | ||
|
|
3ab779895f | ||
|
|
203c7360e7 | ||
|
|
a831f174ef | ||
|
|
153091f52f | ||
|
|
35d3af5039 | ||
|
|
57e8cd6eab | ||
|
|
fc123a71af | ||
|
|
f14836cf02 | ||
|
|
4178feb65f | ||
|
|
2edaf22590 | ||
|
|
acd3dab957 | ||
|
|
6bbd74adcd | ||
|
|
9bb928bb38 | ||
|
|
c87a6c5969 | ||
|
|
c482c13dcb | ||
|
|
b87ed97402 | ||
|
|
ee000dabfd | ||
|
|
4395711d26 | ||
|
|
a73a011ee0 | ||
|
|
2a8e5e2c14 | ||
|
|
5d9a41f712 | ||
|
|
ebcf4b60f6 | ||
|
|
f976b78917 | ||
|
|
078790bd0f | ||
|
|
b88c5a89a8 | ||
|
|
57028e3acc | ||
|
|
c586a17926 | ||
|
|
9d078bac54 | ||
|
|
38eaefcabd | ||
|
|
ba8cadc2f1 | ||
|
|
380d5dfa6d | ||
|
|
32af626630 | ||
|
|
8358fedaf4 | ||
|
|
aba915037f | ||
|
|
ec82b0c648 | ||
|
|
81a87f873f | ||
|
|
d91b8ac444 | ||
|
|
952e51ac75 | ||
|
|
11267cd44f | ||
|
|
0e59e0aebd | ||
|
|
ae1d3b3dd3 | ||
|
|
1a91dbee5f | ||
|
|
fd507e3e41 | ||
|
|
9c1a67cf47 | ||
|
|
69e3824840 | ||
|
|
fcb1a98129 | ||
|
|
6d942635af | ||
|
|
cda2c5d459 | ||
|
|
969bb5a742 | ||
|
|
4bccc611c3 | ||
|
|
d18c4ece0c | ||
|
|
25c664b13a | ||
|
|
7c680c955f | ||
|
|
442e93d3fc | ||
|
|
f037d1b6ca | ||
|
|
2c8b627008 | ||
|
|
221e3eddd5 | ||
|
|
74c39c677b | ||
|
|
e6558832bf | ||
|
|
9c50625c55 | ||
|
|
d372435e92 | ||
|
|
a53facf709 | ||
|
|
ffc39dfbcb | ||
|
|
cba38b15a9 | ||
|
|
3450b5f80c | ||
|
|
5ac7564bfe | ||
|
|
53cd289b90 | ||
|
|
8dc13bcf1a | ||
|
|
261825a89b | ||
|
|
f47a5a309d | ||
|
|
a40f2b9fa0 | ||
|
|
cfcd3892f7 | ||
|
|
703987f61c | ||
|
|
e50a8917ec | ||
|
|
74d7c8e625 | ||
|
|
a5d1383fe8 | ||
|
|
bf2bcf515c | ||
|
|
4c5e94c64b | ||
|
|
b4043216b6 | ||
|
|
4371014667 | ||
|
|
fbb3222d29 | ||
|
|
f76b5d8002 | ||
|
|
7e54868206 | ||
|
|
4ca3889bed | ||
|
|
d84a8e6404 | ||
|
|
eef1aebe8c | ||
|
|
4833b6085c | ||
|
|
2ceaca8828 | ||
|
|
48382c4b59 | ||
|
|
d02158c0ef | ||
|
|
9a45f0b31c | ||
|
|
25c26e2f81 | ||
|
|
6213c4f2cd | ||
|
|
e4837f14b1 | ||
|
|
ce86131d12 | ||
|
|
e6c9baf6ef | ||
|
|
8d6db7be31 | ||
|
|
a2548b1fd0 | ||
|
|
e4658bb99d | ||
|
|
bf2e4a561a | ||
|
|
1816320124 | ||
|
|
f09bfe293d | ||
|
|
7b4e8fda4b | ||
|
|
c95812353f | ||
|
|
cd34eea017 | ||
|
|
b622ec7a28 | ||
|
|
7a0a702ec0 | ||
|
|
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 | ||
|
|
d9ed8e125e | ||
|
|
4c4143d9be | ||
|
|
36708a5067 | ||
|
|
8bc7d259f4 | ||
|
|
2d047fa428 | ||
|
|
735d420d40 | ||
|
|
8c32955da1 | ||
|
|
bc9fc1aece | ||
|
|
c111ed4b20 | ||
|
|
b88e3c99c1 | ||
|
|
a3ea9427d1 | ||
|
|
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 | ||
|
|
09b534b8a3 | ||
|
|
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 | ||
|
|
190c61ba2f | ||
|
|
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 | ||
|
|
dc71ec734d | ||
|
|
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 | ||
|
|
3af96e50bd | ||
|
|
59a85c1d75 | ||
|
|
4427149a38 | ||
|
|
20dee618ea | ||
|
|
37ebbb53be | ||
|
|
ba019efaf1 | ||
|
|
ce948fc512 | ||
|
|
2cd9e7fb55 | ||
|
|
1e2d151684 | ||
|
|
ce5651f5fa | ||
|
|
20ba0bf4ed | ||
|
|
ddc56c8a0d | ||
|
|
c325ffd0f8 | ||
|
|
e0da2764c9 | ||
|
|
6e88d9688b | ||
|
|
bf898f10fb | ||
|
|
c891999e1d | ||
|
|
938e287501 | ||
|
|
edcfc32b1a | ||
|
|
904b211d98 | ||
|
|
af08567f24 | ||
|
|
75ef658962 | ||
|
|
fe2dd79838 | ||
|
|
bbe7e6525d | ||
|
|
ef20df719c | ||
|
|
68399601ce | ||
|
|
aa637fd942 | ||
|
|
601c97c015 | ||
|
|
6b47052491 | ||
|
|
297da94319 | ||
|
|
64f101f534 | ||
|
|
45917f278a | ||
|
|
ddd2759cec | ||
|
|
70d8903d3c | ||
|
|
f66c7dc09c | ||
|
|
ad29093ac1 | ||
|
|
82c6caef85 | ||
|
|
46ec72412a | ||
|
|
ead09395d9 | ||
|
|
7106fc5304 | ||
|
|
d16dcb9f19 | ||
|
|
1aaf34b0ed | ||
|
|
39a3b8922d | ||
|
|
9b78582475 | ||
|
|
3a84224b93 | ||
|
|
2592ba7399 | ||
|
|
1795e0a290 | ||
|
|
c959f59581 | ||
|
|
2449723a1c | ||
|
|
ae0e56e98d | ||
|
|
6efe521e44 | ||
|
|
bccd21ac14 | ||
|
|
8449a65cdf | ||
|
|
fc47562983 | ||
|
|
76900ae291 | ||
|
|
ec55559ff1 | ||
|
|
3daa26e1f7 | ||
|
|
9ea8b6f659 | ||
|
|
387f2f0a94 | ||
|
|
b0d95d02be | ||
|
|
3a98f01d31 | ||
|
|
d305752749 | ||
|
|
2ba4b235fc | ||
|
|
6820c0a5d7 | ||
|
|
048883ad27 | ||
|
|
08e7ada242 | ||
|
|
d3ddfa31f7 | ||
|
|
4b899a813e | ||
|
|
28610a9a42 | ||
|
|
15ee9a5cac | ||
|
|
58945a429f | ||
|
|
9f4111015e | ||
|
|
04b960b415 | ||
|
|
33267f2178 | ||
|
|
970e50d1f1 | ||
|
|
d4199c2d08 | ||
|
|
cf4ca7b6a8 | ||
|
|
d8b335ce65 | ||
|
|
65eb528e2d | ||
|
|
ec9d68960f | ||
|
|
c618eba9a9 | ||
|
|
1ef8378a30 | ||
|
|
43289103cb | ||
|
|
1bc5632771 | ||
|
|
4b488a2d28 | ||
|
|
f28367bcfc |
No files matched your search
@@ -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
-1
@@ -1,12 +1,16 @@
|
|||||||
syncthing
|
syncthing
|
||||||
|
!gui/syncthing
|
||||||
|
!Godeps/_workspace/src/github.com/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
|
||||||
|
syncthing.sig
|
||||||
|
RELEASE
|
||||||
|
deb
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# This is the official list of Syncthing authors for copyright purposes.
|
||||||
|
|
||||||
|
Aaron Bieber <qbit@deftly.net>
|
||||||
|
Adam Piggott <aD@simplypeachy.co.uk> <simplypeachy@users.noreply.github.com>
|
||||||
|
Alexander Graf <register-github@alex-graf.de>
|
||||||
|
Andrew Dunham <andrew@du.nham.ca>
|
||||||
|
Antony Male <antony.male@gmail.com>
|
||||||
|
Arthur Axel fREW Schmidt <frew@afoolishmanifesto.com> <frioux@gmail.com>
|
||||||
|
Audrius Butkevicius <audrius.butkevicius@gmail.com>
|
||||||
|
Bart De Vries <devriesb@gmail.com>
|
||||||
|
Ben Curthoys <ben@bencurthoys.com>
|
||||||
|
Ben Schulz <ueomkail@gmail.com> <uok@users.noreply.github.com>
|
||||||
|
Ben Sidhom <bsidhom@gmail.com>
|
||||||
|
Brandon Philips <brandon@ifup.org>
|
||||||
|
Brendan Long <self@brendanlong.com>
|
||||||
|
Brian R. Becker <brbecker@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 Howie <me@chrishowie.com>
|
||||||
|
Chris Joel <chris@scriptolo.gy>
|
||||||
|
Colin Kennedy <moshen.colin@gmail.com>
|
||||||
|
Daniel Bergmann <dan.arne.bergmann@gmail.com> <brgmnn@users.noreply.github.com>
|
||||||
|
Daniel Martí <mvdan@mvdan.cc>
|
||||||
|
Denis A. <denisva@gmail.com>
|
||||||
|
Dennis Wilson <dw@risu.io>
|
||||||
|
Dominik Heidler <dominik@heidler.eu>
|
||||||
|
Elias Jarlebring <jarlebring@gmail.com>
|
||||||
|
Emil Hessman <emil@hessman.se>
|
||||||
|
Erik Meitner <e.meitner@willystreet.coop>
|
||||||
|
Federico Castagnini <federico.castagnini@gmail.com>
|
||||||
|
Felix Ableitner <me@nutomic.com>
|
||||||
|
Felix Unterpaintner <bigbear2nd@gmail.com>
|
||||||
|
Francois-Xavier Gsell <fxgsell@gmail.com>
|
||||||
|
Frank Isemann <frank@isemann.name>
|
||||||
|
Gilli Sigurdsson <gilli@vx.is>
|
||||||
|
Jacek Szafarkiewicz <szafar@linux.pl>
|
||||||
|
Jakob Borg <jakob@nym.se>
|
||||||
|
Jake Peterson <jake@acogdev.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>
|
||||||
|
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>
|
||||||
|
Lord Landon Agahnim <lordlandon@gmail.com>
|
||||||
|
Marc Laporte <marc@marclaporte.com> <marc@laporte.name>
|
||||||
|
Marc Pujol <kilburn@la3.org>
|
||||||
|
Marcin Dziadus <dziadus.marcin@gmail.com>
|
||||||
|
Mateusz Naściszewski <matin1111@wp.pl>
|
||||||
|
Matt Burke <mburke@amplify.com> <burkemw3@gmail.com>
|
||||||
|
Michael Jephcote <rewt0r@gmx.com> <Rewt0r@users.noreply.github.com>
|
||||||
|
Michael Ploujnikov <ploujj@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>
|
||||||
|
Phill Luby <phill.luby@newredo.com>
|
||||||
|
Piotr Bejda <piotrb10@gmail.com>
|
||||||
|
Ryan Sullivan <kayoticsully@gmail.com>
|
||||||
|
Scott Klupfel <kluppy@going2blue.com>
|
||||||
|
Sergey Mishin <ralder@yandex.ru>
|
||||||
|
Stefan Tatschner <stefan@sevenbyte.org> <rumpelsepp@sevenbyte.org>
|
||||||
|
Stefan Kuntz <stefan.github@gmail.com> <Stefan.github@gmail.com>
|
||||||
|
Tim Abell <tim@timwise.co.uk>
|
||||||
|
Tobias Nygren <tnn@nygren.pp.se>
|
||||||
|
Tomas Cerveny <kozec@kozec.com>
|
||||||
|
Tully Robinson <tully@tojr.org>
|
||||||
|
Tyler Brazier <tyler@tylerbrazier.com>
|
||||||
|
Veeti Paananen <veeti.paananen@rojekti.fi>
|
||||||
|
Vil Brekin <vilbrekin@gmail.com>
|
||||||
|
Yannic A. <eipiminusone+github@gmail.com> <eipiminus1@users.noreply.github.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)*
|
||||||
+10
-63
@@ -31,75 +31,22 @@ latest info on Transifex.
|
|||||||
|
|
||||||
## Contributing Code
|
## Contributing Code
|
||||||
|
|
||||||
Please do contribute! If you want to contribute but are unsure where to
|
Every contribution is welcome. If you want to contribute but are unsure
|
||||||
start, the [Contributions Needed
|
where to start, any open issues are fair game! See the [Contribution
|
||||||
topic](http://discourse.syncthing.net/t/49) lists areas in need of
|
Guidelines](http://docs.syncthing.net/dev/contributing.html) for the full
|
||||||
attention. In general, any open issues are fair game! Be prepared for a
|
story on committing code.
|
||||||
[certain amount of
|
|
||||||
review](https://discourse.syncthing.net/t/733); it's all in the name of
|
|
||||||
quality. :)
|
|
||||||
|
|
||||||
You may be asked to "rebase" or "squash" your pull request before it's
|
## Contributing Documentation
|
||||||
accepted. This means to make sure that the pull request:
|
|
||||||
|
|
||||||
- Is based on the latest commit on `master`.
|
Updates to the [documentation site](http://docs.syncthing.net/) can be
|
||||||
|
made as pull requests on the [documentation
|
||||||
- Only contains a single commit (exceptions to be made where
|
repository](https://github.com/syncthing/docs).
|
||||||
appropriate).
|
|
||||||
|
|
||||||
- Has a descriptive commit message, ending in `(fixes #123)` if it
|
|
||||||
resolves an existing issue.
|
|
||||||
|
|
||||||
## 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
|
|
||||||
will ensure that you are added to the CONTRIBUTORS file. You are welcome
|
|
||||||
to add yourself as a separate commit in your first pull request.
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
[See the documentation](http://discourse.syncthing.net/t/44) on how to
|
|
||||||
get started with a build environment.
|
|
||||||
|
|
||||||
## Branches
|
|
||||||
|
|
||||||
- `master` is the main branch containing good code that will end up in
|
|
||||||
the next release. You should base your work on it. It won't ever be
|
|
||||||
rebased or force-pushed to.
|
|
||||||
|
|
||||||
- `vx.y` branches exist to make patch releases on otherwise obsolete
|
|
||||||
minor releases. Should only contain fixes cherry picked from master.
|
|
||||||
Don't base any work on them.
|
|
||||||
|
|
||||||
- Other branches are probably topic branches and may be subject to
|
|
||||||
rebasing. Don't base any work on them unless you specifically know
|
|
||||||
otherwise.
|
|
||||||
|
|
||||||
## Tags
|
|
||||||
|
|
||||||
All releases are tagged semver style as `vx.y.z`. Release tags are
|
|
||||||
signed by GPG key BCE524C7.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
Yes please!
|
|
||||||
|
|
||||||
## Style
|
|
||||||
|
|
||||||
- `go fmt`
|
|
||||||
|
|
||||||
- Unix line breaks
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
[Over here!](http://discourse.syncthing.net/category/documentation)
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
GPLv3
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
Aaron Bieber <qbit@deftly.net>
|
|
||||||
Alexander Graf <register-github@alex-graf.de>
|
|
||||||
Andrew Dunham <andrew@du.nham.ca>
|
|
||||||
Audrius Butkevicius <audrius.butkevicius@gmail.com>
|
|
||||||
Arthur Axel fREW Schmidt <frew@afoolishmanifesto.com> <frioux@gmail.com>
|
|
||||||
Ben Sidhom <bsidhom@gmail.com>
|
|
||||||
Brandon Philips <brandon@ifup.org>
|
|
||||||
Caleb Callaway <enlightened.despot@gmail.com>
|
|
||||||
Chris Joel <chris@scriptolo.gy>
|
|
||||||
Daniel Martí <mvdan@mvdan.cc>
|
|
||||||
Felix Ableitner <me@nutomic.com>
|
|
||||||
Felix Unterpaintner <bigbear2nd@gmail.com>
|
|
||||||
Gilli Sigurdsson <gilli@vx.is>
|
|
||||||
James Patterson <jamespatterson@operamail.com> <jpjp@users.noreply.github.com>
|
|
||||||
Jens Diemer <github.com@jensdiemer.de> <git@jensdiemer.de>
|
|
||||||
Jochen Voss <voss@seehuhn.de>
|
|
||||||
Lode Hoste <zillode@zillode.be>
|
|
||||||
Marcin Dziadus <dziadus.marcin@gmail.com>
|
|
||||||
Michael Tilli <pyfisch@gmail.com>
|
|
||||||
Philippe Schommers <philippe@schommers.be>
|
|
||||||
Phill Luby <phill.luby@newredo.com>
|
|
||||||
Ryan Sullivan <kayoticsully@gmail.com>
|
|
||||||
Tully Robinson <tully@tojr.org>
|
|
||||||
Veeti Paananen <veeti.paananen@rojekti.fi>
|
|
||||||
Generated
+56
-36
@@ -1,58 +1,50 @@
|
|||||||
{
|
{
|
||||||
"ImportPath": "github.com/syncthing/syncthing",
|
"ImportPath": "github.com/syncthing/syncthing",
|
||||||
"GoVersion": "go1.3.3",
|
"GoVersion": "go1.5.1",
|
||||||
"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": "code.google.com/p/snappy-go/snappy",
|
|
||||||
"Comment": "null-15",
|
|
||||||
"Rev": "12e4b4183793ac4b061921e7980845e750679fd0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/AudriusButkevicius/lfu-go",
|
|
||||||
"Rev": "164bcecceb92fd6037f4d18a8d97b495ec6ef669"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/bkaradzic/go-lz4",
|
"ImportPath": "github.com/bkaradzic/go-lz4",
|
||||||
"Rev": "93a831dcee242be64a9cc9803dda84af25932de7"
|
"Rev": "74ddf82598bc4745b965729e9c6a463bedd33049"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/calmh/osext",
|
"ImportPath": "github.com/calmh/du",
|
||||||
"Rev": "9bf61584e5f1f172e8766ddc9022d9c401faaa5e"
|
"Rev": "3c0690cca16228b97741327b1b6781397afbdb24"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/calmh/luhn",
|
||||||
|
"Rev": "0c8388ff95fa92d4094011e5a04fc99dea3d1632"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/calmh/xdr",
|
"ImportPath": "github.com/calmh/xdr",
|
||||||
"Rev": "ec3d404f43731551258977b38dd72cf557d00398"
|
"Rev": "9eb3e1a622d9364deb39c831f7e5f164393d7e37"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/golang/snappy",
|
||||||
|
"Rev": "723cc1e459b8eea2dea4583200fd60757d40097a"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/juju/ratelimit",
|
"ImportPath": "github.com/juju/ratelimit",
|
||||||
"Rev": "f9f36d11773655c0485207f0ad30dc2655f69d56"
|
"Rev": "772f5c38e468398c4511514f4f6aa9a4185bc0a0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/kardianos/osext",
|
||||||
|
"Rev": "345163ffe35aa66560a4cd7dddf00f3ae21c9fda"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/rcrowley/go-metrics",
|
||||||
|
"Rev": "1ce93efbc8f9c568886b2ef85ce305b2217b3de3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/syndtr/goleveldb/leveldb",
|
"ImportPath": "github.com/syndtr/goleveldb/leveldb",
|
||||||
"Rev": "0d8857b7ec571b0a6c9677d8e6c0a4ceeabd1d71"
|
"Rev": "1a9d62f03ea92815b46fcaab357cfd4df264b1a0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/thejerf/suture",
|
||||||
|
"Comment": "v1.0.1",
|
||||||
|
"Rev": "99c1f2d613756768fc4299acd9dc621e11ed3fd7"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/vitrun/qart/coding",
|
"ImportPath": "github.com/vitrun/qart/coding",
|
||||||
@@ -65,6 +57,34 @@
|
|||||||
{
|
{
|
||||||
"ImportPath": "github.com/vitrun/qart/qr",
|
"ImportPath": "github.com/vitrun/qart/qr",
|
||||||
"Rev": "ccb109cf25f0cd24474da73b9fee4e7a3e8a8ce0"
|
"Rev": "ccb109cf25f0cd24474da73b9fee4e7a3e8a8ce0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/crypto/bcrypt",
|
||||||
|
"Rev": "575fdbe86e5dd89229707ebec0575ce7d088a4a6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/crypto/blowfish",
|
||||||
|
"Rev": "575fdbe86e5dd89229707ebec0575ce7d088a4a6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/net/internal/iana",
|
||||||
|
"Rev": "042ba42fa6633b34205efc66ba5719cd3afd8d38"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/net/ipv6",
|
||||||
|
"Rev": "042ba42fa6633b34205efc66ba5719cd3afd8d38"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/net/proxy",
|
||||||
|
"Rev": "042ba42fa6633b34205efc66ba5719cd3afd8d38"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/text/transform",
|
||||||
|
"Rev": "5eb8d4684c4796dd36c74f6452f2c0fa6c79597e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/text/unicode/norm",
|
||||||
|
"Rev": "5eb8d4684c4796dd36c74f6452f2c0fa6c79597e"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
-226
@@ -1,226 +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 bcrypt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBcryptingIsEasy(t *testing.T) {
|
|
||||||
pass := []byte("mypassword")
|
|
||||||
hp, err := GenerateFromPassword(pass, 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("GenerateFromPassword error: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if CompareHashAndPassword(hp, pass) != nil {
|
|
||||||
t.Errorf("%v should hash %s correctly", hp, pass)
|
|
||||||
}
|
|
||||||
|
|
||||||
notPass := "notthepass"
|
|
||||||
err = CompareHashAndPassword(hp, []byte(notPass))
|
|
||||||
if err != ErrMismatchedHashAndPassword {
|
|
||||||
t.Errorf("%v and %s should be mismatched", hp, notPass)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBcryptingIsCorrect(t *testing.T) {
|
|
||||||
pass := []byte("allmine")
|
|
||||||
salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
|
|
||||||
expectedHash := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga")
|
|
||||||
|
|
||||||
hash, err := bcrypt(pass, 10, salt)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("bcrypt blew up: %v", err)
|
|
||||||
}
|
|
||||||
if !bytes.HasSuffix(expectedHash, hash) {
|
|
||||||
t.Errorf("%v should be the suffix of %v", hash, expectedHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
h, err := newFromHash(expectedHash)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unable to parse %s: %v", string(expectedHash), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is not the safe way to compare these hashes. We do this only for
|
|
||||||
// testing clarity. Use bcrypt.CompareHashAndPassword()
|
|
||||||
if err == nil && !bytes.Equal(expectedHash, h.Hash()) {
|
|
||||||
t.Errorf("Parsed hash %v should equal %v", h.Hash(), expectedHash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestVeryShortPasswords(t *testing.T) {
|
|
||||||
key := []byte("k")
|
|
||||||
salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
|
|
||||||
_, err := bcrypt(key, 10, salt)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("One byte key resulted in error: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTooLongPasswordsWork(t *testing.T) {
|
|
||||||
salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
|
|
||||||
// One byte over the usual 56 byte limit that blowfish has
|
|
||||||
tooLongPass := []byte("012345678901234567890123456789012345678901234567890123456")
|
|
||||||
tooLongExpected := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C")
|
|
||||||
hash, err := bcrypt(tooLongPass, 10, salt)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("bcrypt blew up on long password: %v", err)
|
|
||||||
}
|
|
||||||
if !bytes.HasSuffix(tooLongExpected, hash) {
|
|
||||||
t.Errorf("%v should be the suffix of %v", hash, tooLongExpected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type InvalidHashTest struct {
|
|
||||||
err error
|
|
||||||
hash []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
var invalidTests = []InvalidHashTest{
|
|
||||||
{ErrHashTooShort, []byte("$2a$10$fooo")},
|
|
||||||
{ErrHashTooShort, []byte("$2a")},
|
|
||||||
{HashVersionTooNewError('3'), []byte("$3a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
|
|
||||||
{InvalidHashPrefixError('%'), []byte("%2a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
|
|
||||||
{InvalidCostError(32), []byte("$2a$32$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInvalidHashErrors(t *testing.T) {
|
|
||||||
check := func(name string, expected, err error) {
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("%s: Should have returned an error", name)
|
|
||||||
}
|
|
||||||
if err != nil && err != expected {
|
|
||||||
t.Errorf("%s gave err %v but should have given %v", name, err, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, iht := range invalidTests {
|
|
||||||
_, err := newFromHash(iht.hash)
|
|
||||||
check("newFromHash", iht.err, err)
|
|
||||||
err = CompareHashAndPassword(iht.hash, []byte("anything"))
|
|
||||||
check("CompareHashAndPassword", iht.err, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnpaddedBase64Encoding(t *testing.T) {
|
|
||||||
original := []byte{101, 201, 101, 75, 19, 227, 199, 20, 239, 236, 133, 32, 30, 109, 243, 30}
|
|
||||||
encodedOriginal := []byte("XajjQvNhvvRt5GSeFk1xFe")
|
|
||||||
|
|
||||||
encoded := base64Encode(original)
|
|
||||||
|
|
||||||
if !bytes.Equal(encodedOriginal, encoded) {
|
|
||||||
t.Errorf("Encoded %v should have equaled %v", encoded, encodedOriginal)
|
|
||||||
}
|
|
||||||
|
|
||||||
decoded, err := base64Decode(encodedOriginal)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("base64Decode blew up: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(decoded, original) {
|
|
||||||
t.Errorf("Decoded %v should have equaled %v", decoded, original)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCost(t *testing.T) {
|
|
||||||
suffix := "XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C"
|
|
||||||
for _, vers := range []string{"2a", "2"} {
|
|
||||||
for _, cost := range []int{4, 10} {
|
|
||||||
s := fmt.Sprintf("$%s$%02d$%s", vers, cost, suffix)
|
|
||||||
h := []byte(s)
|
|
||||||
actual, err := Cost(h)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Cost, error: %s", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if actual != cost {
|
|
||||||
t.Errorf("Cost, expected: %d, actual: %d", cost, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, err := Cost([]byte("$a$a$" + suffix))
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Cost, malformed but no error returned")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCostValidationInHash(t *testing.T) {
|
|
||||||
if testing.Short() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
pass := []byte("mypassword")
|
|
||||||
|
|
||||||
for c := 0; c < MinCost; c++ {
|
|
||||||
p, _ := newFromPassword(pass, c)
|
|
||||||
if p.cost != DefaultCost {
|
|
||||||
t.Errorf("newFromPassword should default costs below %d to %d, but was %d", MinCost, DefaultCost, p.cost)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p, _ := newFromPassword(pass, 14)
|
|
||||||
if p.cost != 14 {
|
|
||||||
t.Errorf("newFromPassword should default cost to 14, but was %d", p.cost)
|
|
||||||
}
|
|
||||||
|
|
||||||
hp, _ := newFromHash(p.Hash())
|
|
||||||
if p.cost != hp.cost {
|
|
||||||
t.Errorf("newFromHash should maintain the cost at %d, but was %d", p.cost, hp.cost)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := newFromPassword(pass, 32)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("newFromPassword: should return a cost error")
|
|
||||||
}
|
|
||||||
if err != InvalidCostError(32) {
|
|
||||||
t.Errorf("newFromPassword: should return cost error, got %#v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCostReturnsWithLeadingZeroes(t *testing.T) {
|
|
||||||
hp, _ := newFromPassword([]byte("abcdefgh"), 7)
|
|
||||||
cost := hp.Hash()[4:7]
|
|
||||||
expected := []byte("07$")
|
|
||||||
|
|
||||||
if !bytes.Equal(expected, cost) {
|
|
||||||
t.Errorf("single digit costs in hash should have leading zeros: was %v instead of %v", cost, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMinorNotRequired(t *testing.T) {
|
|
||||||
noMinorHash := []byte("$2$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga")
|
|
||||||
h, err := newFromHash(noMinorHash)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("No minor hash blew up: %s", err)
|
|
||||||
}
|
|
||||||
if h.minor != 0 {
|
|
||||||
t.Errorf("Should leave minor version at 0, but was %d", h.minor)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(noMinorHash, h.Hash()) {
|
|
||||||
t.Errorf("Should generate hash %v, but created %v", noMinorHash, h.Hash())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkEqual(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
passwd := []byte("somepasswordyoulike")
|
|
||||||
hash, _ := GenerateFromPassword(passwd, 10)
|
|
||||||
b.StartTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
CompareHashAndPassword(hash, passwd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkGeneration(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
passwd := []byte("mylongpassword1234")
|
|
||||||
b.StartTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
GenerateFromPassword(passwd, 10)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-274
@@ -1,274 +0,0 @@
|
|||||||
// Copyright 2010 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 blowfish
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
type CryptTest struct {
|
|
||||||
key []byte
|
|
||||||
in []byte
|
|
||||||
out []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test vector values are from http://www.schneier.com/code/vectors.txt.
|
|
||||||
var encryptTests = []CryptTest{
|
|
||||||
{
|
|
||||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
||||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
||||||
[]byte{0x4E, 0xF9, 0x97, 0x45, 0x61, 0x98, 0xDD, 0x78}},
|
|
||||||
{
|
|
||||||
[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
|
||||||
[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
|
||||||
[]byte{0x51, 0x86, 0x6F, 0xD5, 0xB8, 0x5E, 0xCB, 0x8A}},
|
|
||||||
{
|
|
||||||
[]byte{0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
||||||
[]byte{0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
|
|
||||||
[]byte{0x7D, 0x85, 0x6F, 0x9A, 0x61, 0x30, 0x63, 0xF2}},
|
|
||||||
{
|
|
||||||
[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
|
|
||||||
[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
|
|
||||||
[]byte{0x24, 0x66, 0xDD, 0x87, 0x8B, 0x96, 0x3C, 0x9D}},
|
|
||||||
|
|
||||||
{
|
|
||||||
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
|
|
||||||
[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
|
|
||||||
[]byte{0x61, 0xF9, 0xC3, 0x80, 0x22, 0x81, 0xB0, 0x96}},
|
|
||||||
{
|
|
||||||
[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
|
|
||||||
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
|
|
||||||
[]byte{0x7D, 0x0C, 0xC6, 0x30, 0xAF, 0xDA, 0x1E, 0xC7}},
|
|
||||||
{
|
|
||||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
||||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
||||||
[]byte{0x4E, 0xF9, 0x97, 0x45, 0x61, 0x98, 0xDD, 0x78}},
|
|
||||||
{
|
|
||||||
[]byte{0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10},
|
|
||||||
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
|
|
||||||
[]byte{0x0A, 0xCE, 0xAB, 0x0F, 0xC6, 0xA0, 0xA2, 0x8D}},
|
|
||||||
{
|
|
||||||
[]byte{0x7C, 0xA1, 0x10, 0x45, 0x4A, 0x1A, 0x6E, 0x57},
|
|
||||||
[]byte{0x01, 0xA1, 0xD6, 0xD0, 0x39, 0x77, 0x67, 0x42},
|
|
||||||
[]byte{0x59, 0xC6, 0x82, 0x45, 0xEB, 0x05, 0x28, 0x2B}},
|
|
||||||
{
|
|
||||||
[]byte{0x01, 0x31, 0xD9, 0x61, 0x9D, 0xC1, 0x37, 0x6E},
|
|
||||||
[]byte{0x5C, 0xD5, 0x4C, 0xA8, 0x3D, 0xEF, 0x57, 0xDA},
|
|
||||||
[]byte{0xB1, 0xB8, 0xCC, 0x0B, 0x25, 0x0F, 0x09, 0xA0}},
|
|
||||||
{
|
|
||||||
[]byte{0x07, 0xA1, 0x13, 0x3E, 0x4A, 0x0B, 0x26, 0x86},
|
|
||||||
[]byte{0x02, 0x48, 0xD4, 0x38, 0x06, 0xF6, 0x71, 0x72},
|
|
||||||
[]byte{0x17, 0x30, 0xE5, 0x77, 0x8B, 0xEA, 0x1D, 0xA4}},
|
|
||||||
{
|
|
||||||
[]byte{0x38, 0x49, 0x67, 0x4C, 0x26, 0x02, 0x31, 0x9E},
|
|
||||||
[]byte{0x51, 0x45, 0x4B, 0x58, 0x2D, 0xDF, 0x44, 0x0A},
|
|
||||||
[]byte{0xA2, 0x5E, 0x78, 0x56, 0xCF, 0x26, 0x51, 0xEB}},
|
|
||||||
{
|
|
||||||
[]byte{0x04, 0xB9, 0x15, 0xBA, 0x43, 0xFE, 0xB5, 0xB6},
|
|
||||||
[]byte{0x42, 0xFD, 0x44, 0x30, 0x59, 0x57, 0x7F, 0xA2},
|
|
||||||
[]byte{0x35, 0x38, 0x82, 0xB1, 0x09, 0xCE, 0x8F, 0x1A}},
|
|
||||||
{
|
|
||||||
[]byte{0x01, 0x13, 0xB9, 0x70, 0xFD, 0x34, 0xF2, 0xCE},
|
|
||||||
[]byte{0x05, 0x9B, 0x5E, 0x08, 0x51, 0xCF, 0x14, 0x3A},
|
|
||||||
[]byte{0x48, 0xF4, 0xD0, 0x88, 0x4C, 0x37, 0x99, 0x18}},
|
|
||||||
{
|
|
||||||
[]byte{0x01, 0x70, 0xF1, 0x75, 0x46, 0x8F, 0xB5, 0xE6},
|
|
||||||
[]byte{0x07, 0x56, 0xD8, 0xE0, 0x77, 0x47, 0x61, 0xD2},
|
|
||||||
[]byte{0x43, 0x21, 0x93, 0xB7, 0x89, 0x51, 0xFC, 0x98}},
|
|
||||||
{
|
|
||||||
[]byte{0x43, 0x29, 0x7F, 0xAD, 0x38, 0xE3, 0x73, 0xFE},
|
|
||||||
[]byte{0x76, 0x25, 0x14, 0xB8, 0x29, 0xBF, 0x48, 0x6A},
|
|
||||||
[]byte{0x13, 0xF0, 0x41, 0x54, 0xD6, 0x9D, 0x1A, 0xE5}},
|
|
||||||
{
|
|
||||||
[]byte{0x07, 0xA7, 0x13, 0x70, 0x45, 0xDA, 0x2A, 0x16},
|
|
||||||
[]byte{0x3B, 0xDD, 0x11, 0x90, 0x49, 0x37, 0x28, 0x02},
|
|
||||||
[]byte{0x2E, 0xED, 0xDA, 0x93, 0xFF, 0xD3, 0x9C, 0x79}},
|
|
||||||
{
|
|
||||||
[]byte{0x04, 0x68, 0x91, 0x04, 0xC2, 0xFD, 0x3B, 0x2F},
|
|
||||||
[]byte{0x26, 0x95, 0x5F, 0x68, 0x35, 0xAF, 0x60, 0x9A},
|
|
||||||
[]byte{0xD8, 0x87, 0xE0, 0x39, 0x3C, 0x2D, 0xA6, 0xE3}},
|
|
||||||
{
|
|
||||||
[]byte{0x37, 0xD0, 0x6B, 0xB5, 0x16, 0xCB, 0x75, 0x46},
|
|
||||||
[]byte{0x16, 0x4D, 0x5E, 0x40, 0x4F, 0x27, 0x52, 0x32},
|
|
||||||
[]byte{0x5F, 0x99, 0xD0, 0x4F, 0x5B, 0x16, 0x39, 0x69}},
|
|
||||||
{
|
|
||||||
[]byte{0x1F, 0x08, 0x26, 0x0D, 0x1A, 0xC2, 0x46, 0x5E},
|
|
||||||
[]byte{0x6B, 0x05, 0x6E, 0x18, 0x75, 0x9F, 0x5C, 0xCA},
|
|
||||||
[]byte{0x4A, 0x05, 0x7A, 0x3B, 0x24, 0xD3, 0x97, 0x7B}},
|
|
||||||
{
|
|
||||||
[]byte{0x58, 0x40, 0x23, 0x64, 0x1A, 0xBA, 0x61, 0x76},
|
|
||||||
[]byte{0x00, 0x4B, 0xD6, 0xEF, 0x09, 0x17, 0x60, 0x62},
|
|
||||||
[]byte{0x45, 0x20, 0x31, 0xC1, 0xE4, 0xFA, 0xDA, 0x8E}},
|
|
||||||
{
|
|
||||||
[]byte{0x02, 0x58, 0x16, 0x16, 0x46, 0x29, 0xB0, 0x07},
|
|
||||||
[]byte{0x48, 0x0D, 0x39, 0x00, 0x6E, 0xE7, 0x62, 0xF2},
|
|
||||||
[]byte{0x75, 0x55, 0xAE, 0x39, 0xF5, 0x9B, 0x87, 0xBD}},
|
|
||||||
{
|
|
||||||
[]byte{0x49, 0x79, 0x3E, 0xBC, 0x79, 0xB3, 0x25, 0x8F},
|
|
||||||
[]byte{0x43, 0x75, 0x40, 0xC8, 0x69, 0x8F, 0x3C, 0xFA},
|
|
||||||
[]byte{0x53, 0xC5, 0x5F, 0x9C, 0xB4, 0x9F, 0xC0, 0x19}},
|
|
||||||
{
|
|
||||||
[]byte{0x4F, 0xB0, 0x5E, 0x15, 0x15, 0xAB, 0x73, 0xA7},
|
|
||||||
[]byte{0x07, 0x2D, 0x43, 0xA0, 0x77, 0x07, 0x52, 0x92},
|
|
||||||
[]byte{0x7A, 0x8E, 0x7B, 0xFA, 0x93, 0x7E, 0x89, 0xA3}},
|
|
||||||
{
|
|
||||||
[]byte{0x49, 0xE9, 0x5D, 0x6D, 0x4C, 0xA2, 0x29, 0xBF},
|
|
||||||
[]byte{0x02, 0xFE, 0x55, 0x77, 0x81, 0x17, 0xF1, 0x2A},
|
|
||||||
[]byte{0xCF, 0x9C, 0x5D, 0x7A, 0x49, 0x86, 0xAD, 0xB5}},
|
|
||||||
{
|
|
||||||
[]byte{0x01, 0x83, 0x10, 0xDC, 0x40, 0x9B, 0x26, 0xD6},
|
|
||||||
[]byte{0x1D, 0x9D, 0x5C, 0x50, 0x18, 0xF7, 0x28, 0xC2},
|
|
||||||
[]byte{0xD1, 0xAB, 0xB2, 0x90, 0x65, 0x8B, 0xC7, 0x78}},
|
|
||||||
{
|
|
||||||
[]byte{0x1C, 0x58, 0x7F, 0x1C, 0x13, 0x92, 0x4F, 0xEF},
|
|
||||||
[]byte{0x30, 0x55, 0x32, 0x28, 0x6D, 0x6F, 0x29, 0x5A},
|
|
||||||
[]byte{0x55, 0xCB, 0x37, 0x74, 0xD1, 0x3E, 0xF2, 0x01}},
|
|
||||||
{
|
|
||||||
[]byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
|
|
||||||
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
|
|
||||||
[]byte{0xFA, 0x34, 0xEC, 0x48, 0x47, 0xB2, 0x68, 0xB2}},
|
|
||||||
{
|
|
||||||
[]byte{0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E},
|
|
||||||
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
|
|
||||||
[]byte{0xA7, 0x90, 0x79, 0x51, 0x08, 0xEA, 0x3C, 0xAE}},
|
|
||||||
{
|
|
||||||
[]byte{0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE},
|
|
||||||
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
|
|
||||||
[]byte{0xC3, 0x9E, 0x07, 0x2D, 0x9F, 0xAC, 0x63, 0x1D}},
|
|
||||||
{
|
|
||||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
||||||
[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
|
||||||
[]byte{0x01, 0x49, 0x33, 0xE0, 0xCD, 0xAF, 0xF6, 0xE4}},
|
|
||||||
{
|
|
||||||
[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
|
||||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
||||||
[]byte{0xF2, 0x1E, 0x9A, 0x77, 0xB7, 0x1C, 0x49, 0xBC}},
|
|
||||||
{
|
|
||||||
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
|
|
||||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
||||||
[]byte{0x24, 0x59, 0x46, 0x88, 0x57, 0x54, 0x36, 0x9A}},
|
|
||||||
{
|
|
||||||
[]byte{0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10},
|
|
||||||
[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
|
||||||
[]byte{0x6B, 0x5C, 0x5A, 0x9C, 0x5D, 0x9E, 0x0A, 0x5A}},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCipherEncrypt(t *testing.T) {
|
|
||||||
for i, tt := range encryptTests {
|
|
||||||
c, err := NewCipher(tt.key)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("NewCipher(%d bytes) = %s", len(tt.key), err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ct := make([]byte, len(tt.out))
|
|
||||||
c.Encrypt(ct, tt.in)
|
|
||||||
for j, v := range ct {
|
|
||||||
if v != tt.out[j] {
|
|
||||||
t.Errorf("Cipher.Encrypt, test vector #%d: cipher-text[%d] = %#x, expected %#x", i, j, v, tt.out[j])
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCipherDecrypt(t *testing.T) {
|
|
||||||
for i, tt := range encryptTests {
|
|
||||||
c, err := NewCipher(tt.key)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("NewCipher(%d bytes) = %s", len(tt.key), err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
pt := make([]byte, len(tt.in))
|
|
||||||
c.Decrypt(pt, tt.out)
|
|
||||||
for j, v := range pt {
|
|
||||||
if v != tt.in[j] {
|
|
||||||
t.Errorf("Cipher.Decrypt, test vector #%d: plain-text[%d] = %#x, expected %#x", i, j, v, tt.in[j])
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSaltedCipherKeyLength(t *testing.T) {
|
|
||||||
if _, err := NewSaltedCipher(nil, []byte{'a'}); err != KeySizeError(0) {
|
|
||||||
t.Errorf("NewSaltedCipher with short key, gave error %#v, expected %#v", err, KeySizeError(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
// A 57-byte key. One over the typical blowfish restriction.
|
|
||||||
key := []byte("012345678901234567890123456789012345678901234567890123456")
|
|
||||||
if _, err := NewSaltedCipher(key, []byte{'a'}); err != nil {
|
|
||||||
t.Errorf("NewSaltedCipher with long key, gave error %#v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test vectors generated with Blowfish from OpenSSH.
|
|
||||||
var saltedVectors = [][8]byte{
|
|
||||||
{0x0c, 0x82, 0x3b, 0x7b, 0x8d, 0x01, 0x4b, 0x7e},
|
|
||||||
{0xd1, 0xe1, 0x93, 0xf0, 0x70, 0xa6, 0xdb, 0x12},
|
|
||||||
{0xfc, 0x5e, 0xba, 0xde, 0xcb, 0xf8, 0x59, 0xad},
|
|
||||||
{0x8a, 0x0c, 0x76, 0xe7, 0xdd, 0x2c, 0xd3, 0xa8},
|
|
||||||
{0x2c, 0xcb, 0x7b, 0xee, 0xac, 0x7b, 0x7f, 0xf8},
|
|
||||||
{0xbb, 0xf6, 0x30, 0x6f, 0xe1, 0x5d, 0x62, 0xbf},
|
|
||||||
{0x97, 0x1e, 0xc1, 0x3d, 0x3d, 0xe0, 0x11, 0xe9},
|
|
||||||
{0x06, 0xd7, 0x4d, 0xb1, 0x80, 0xa3, 0xb1, 0x38},
|
|
||||||
{0x67, 0xa1, 0xa9, 0x75, 0x0e, 0x5b, 0xc6, 0xb4},
|
|
||||||
{0x51, 0x0f, 0x33, 0x0e, 0x4f, 0x67, 0xd2, 0x0c},
|
|
||||||
{0xf1, 0x73, 0x7e, 0xd8, 0x44, 0xea, 0xdb, 0xe5},
|
|
||||||
{0x14, 0x0e, 0x16, 0xce, 0x7f, 0x4a, 0x9c, 0x7b},
|
|
||||||
{0x4b, 0xfe, 0x43, 0xfd, 0xbf, 0x36, 0x04, 0x47},
|
|
||||||
{0xb1, 0xeb, 0x3e, 0x15, 0x36, 0xa7, 0xbb, 0xe2},
|
|
||||||
{0x6d, 0x0b, 0x41, 0xdd, 0x00, 0x98, 0x0b, 0x19},
|
|
||||||
{0xd3, 0xce, 0x45, 0xce, 0x1d, 0x56, 0xb7, 0xfc},
|
|
||||||
{0xd9, 0xf0, 0xfd, 0xda, 0xc0, 0x23, 0xb7, 0x93},
|
|
||||||
{0x4c, 0x6f, 0xa1, 0xe4, 0x0c, 0xa8, 0xca, 0x57},
|
|
||||||
{0xe6, 0x2f, 0x28, 0xa7, 0x0c, 0x94, 0x0d, 0x08},
|
|
||||||
{0x8f, 0xe3, 0xf0, 0xb6, 0x29, 0xe3, 0x44, 0x03},
|
|
||||||
{0xff, 0x98, 0xdd, 0x04, 0x45, 0xb4, 0x6d, 0x1f},
|
|
||||||
{0x9e, 0x45, 0x4d, 0x18, 0x40, 0x53, 0xdb, 0xef},
|
|
||||||
{0xb7, 0x3b, 0xef, 0x29, 0xbe, 0xa8, 0x13, 0x71},
|
|
||||||
{0x02, 0x54, 0x55, 0x41, 0x8e, 0x04, 0xfc, 0xad},
|
|
||||||
{0x6a, 0x0a, 0xee, 0x7c, 0x10, 0xd9, 0x19, 0xfe},
|
|
||||||
{0x0a, 0x22, 0xd9, 0x41, 0xcc, 0x23, 0x87, 0x13},
|
|
||||||
{0x6e, 0xff, 0x1f, 0xff, 0x36, 0x17, 0x9c, 0xbe},
|
|
||||||
{0x79, 0xad, 0xb7, 0x40, 0xf4, 0x9f, 0x51, 0xa6},
|
|
||||||
{0x97, 0x81, 0x99, 0xa4, 0xde, 0x9e, 0x9f, 0xb6},
|
|
||||||
{0x12, 0x19, 0x7a, 0x28, 0xd0, 0xdc, 0xcc, 0x92},
|
|
||||||
{0x81, 0xda, 0x60, 0x1e, 0x0e, 0xdd, 0x65, 0x56},
|
|
||||||
{0x7d, 0x76, 0x20, 0xb2, 0x73, 0xc9, 0x9e, 0xee},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSaltedCipher(t *testing.T) {
|
|
||||||
var key, salt [32]byte
|
|
||||||
for i := range key {
|
|
||||||
key[i] = byte(i)
|
|
||||||
salt[i] = byte(i + 32)
|
|
||||||
}
|
|
||||||
for i, v := range saltedVectors {
|
|
||||||
c, err := NewSaltedCipher(key[:], salt[:i])
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
var buf [8]byte
|
|
||||||
c.Encrypt(buf[:], buf[:])
|
|
||||||
if v != buf {
|
|
||||||
t.Errorf("%d: expected %x, got %x", i, v, buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkExpandKeyWithSalt(b *testing.B) {
|
|
||||||
key := make([]byte, 32)
|
|
||||||
salt := make([]byte, 16)
|
|
||||||
c, _ := NewCipher(key)
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
expandKeyWithSalt(key, salt, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkExpandKey(b *testing.B) {
|
|
||||||
key := make([]byte, 32)
|
|
||||||
c, _ := NewCipher(key)
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
ExpandKey(key, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-37
@@ -1,37 +0,0 @@
|
|||||||
// Copyright 2013 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 transform_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"unicode"
|
|
||||||
|
|
||||||
"code.google.com/p/go.text/transform"
|
|
||||||
"code.google.com/p/go.text/unicode/norm"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ExampleRemoveFunc() {
|
|
||||||
input := []byte(`tschüß; до свидания`)
|
|
||||||
|
|
||||||
b := make([]byte, len(input))
|
|
||||||
|
|
||||||
t := transform.RemoveFunc(unicode.IsSpace)
|
|
||||||
n, _, _ := t.Transform(b, input, true)
|
|
||||||
fmt.Println(string(b[:n]))
|
|
||||||
|
|
||||||
t = transform.RemoveFunc(func(r rune) bool {
|
|
||||||
return !unicode.Is(unicode.Latin, r)
|
|
||||||
})
|
|
||||||
n, _, _ = t.Transform(b, input, true)
|
|
||||||
fmt.Println(string(b[:n]))
|
|
||||||
|
|
||||||
n, _, _ = t.Transform(b, norm.NFD.Bytes(input), true)
|
|
||||||
fmt.Println(string(b[:n]))
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// tschüß;досвидания
|
|
||||||
// tschüß
|
|
||||||
// tschuß
|
|
||||||
}
|
|
||||||
-1075
File diff suppressed because it is too large.
Load diff
-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
|
|
||||||
-130
@@ -1,130 +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"
|
|
||||||
|
|
||||||
// TestCase is used for most tests.
|
|
||||||
type TestCase struct {
|
|
||||||
in []rune
|
|
||||||
out []rune
|
|
||||||
}
|
|
||||||
|
|
||||||
func runTests(t *testing.T, name string, fm Form, tests []TestCase) {
|
|
||||||
rb := reorderBuffer{}
|
|
||||||
rb.init(fm, nil)
|
|
||||||
for i, test := range tests {
|
|
||||||
rb.setFlusher(nil, appendFlush)
|
|
||||||
for j, rune := range test.in {
|
|
||||||
b := []byte(string(rune))
|
|
||||||
src := inputBytes(b)
|
|
||||||
info := rb.f.info(src, 0)
|
|
||||||
if j == 0 {
|
|
||||||
rb.ss.first(info)
|
|
||||||
} else {
|
|
||||||
rb.ss.next(info)
|
|
||||||
}
|
|
||||||
if rb.insertFlush(src, 0, info) < 0 {
|
|
||||||
t.Errorf("%s:%d: insert failed for rune %d", name, i, j)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rb.doFlush()
|
|
||||||
was := string(rb.out)
|
|
||||||
want := string(test.out)
|
|
||||||
if len(was) != len(want) {
|
|
||||||
t.Errorf("%s:%d: length = %d; want %d", name, i, len(was), len(want))
|
|
||||||
}
|
|
||||||
if was != want {
|
|
||||||
k, pfx := pidx(was, want)
|
|
||||||
t.Errorf("%s:%d: \nwas %s%+q; \nwant %s%+q", name, i, pfx, was[k:], pfx, want[k:])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlush(t *testing.T) {
|
|
||||||
const (
|
|
||||||
hello = "Hello "
|
|
||||||
world = "world!"
|
|
||||||
)
|
|
||||||
buf := make([]byte, maxByteBufferSize)
|
|
||||||
p := copy(buf, hello)
|
|
||||||
out := buf[p:]
|
|
||||||
rb := reorderBuffer{}
|
|
||||||
rb.initString(NFC, world)
|
|
||||||
if i := rb.flushCopy(out); i != 0 {
|
|
||||||
t.Errorf("wrote bytes on flush of empty buffer. (len(out) = %d)", i)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range world {
|
|
||||||
// No need to set streamSafe values for this test.
|
|
||||||
rb.insertFlush(rb.src, i, rb.f.info(rb.src, i))
|
|
||||||
n := rb.flushCopy(out)
|
|
||||||
out = out[n:]
|
|
||||||
p += n
|
|
||||||
}
|
|
||||||
|
|
||||||
was := buf[:p]
|
|
||||||
want := hello + world
|
|
||||||
if string(was) != want {
|
|
||||||
t.Errorf(`output after flush was "%s"; want "%s"`, string(was), want)
|
|
||||||
}
|
|
||||||
if rb.nrune != 0 {
|
|
||||||
t.Errorf("non-null size of info buffer (rb.nrune == %d)", rb.nrune)
|
|
||||||
}
|
|
||||||
if rb.nbyte != 0 {
|
|
||||||
t.Errorf("non-null size of byte buffer (rb.nbyte == %d)", rb.nbyte)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var insertTests = []TestCase{
|
|
||||||
{[]rune{'a'}, []rune{'a'}},
|
|
||||||
{[]rune{0x300}, []rune{0x300}},
|
|
||||||
{[]rune{0x300, 0x316}, []rune{0x316, 0x300}}, // CCC(0x300)==230; CCC(0x316)==220
|
|
||||||
{[]rune{0x316, 0x300}, []rune{0x316, 0x300}},
|
|
||||||
{[]rune{0x41, 0x316, 0x300}, []rune{0x41, 0x316, 0x300}},
|
|
||||||
{[]rune{0x41, 0x300, 0x316}, []rune{0x41, 0x316, 0x300}},
|
|
||||||
{[]rune{0x300, 0x316, 0x41}, []rune{0x316, 0x300, 0x41}},
|
|
||||||
{[]rune{0x41, 0x300, 0x40, 0x316}, []rune{0x41, 0x300, 0x40, 0x316}},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInsert(t *testing.T) {
|
|
||||||
runTests(t, "TestInsert", NFD, insertTests)
|
|
||||||
}
|
|
||||||
|
|
||||||
var decompositionNFDTest = []TestCase{
|
|
||||||
{[]rune{0xC0}, []rune{0x41, 0x300}},
|
|
||||||
{[]rune{0xAC00}, []rune{0x1100, 0x1161}},
|
|
||||||
{[]rune{0x01C4}, []rune{0x01C4}},
|
|
||||||
{[]rune{0x320E}, []rune{0x320E}},
|
|
||||||
{[]rune("음ẻ과"), []rune{0x110B, 0x1173, 0x11B7, 0x65, 0x309, 0x1100, 0x116A}},
|
|
||||||
}
|
|
||||||
|
|
||||||
var decompositionNFKDTest = []TestCase{
|
|
||||||
{[]rune{0xC0}, []rune{0x41, 0x300}},
|
|
||||||
{[]rune{0xAC00}, []rune{0x1100, 0x1161}},
|
|
||||||
{[]rune{0x01C4}, []rune{0x44, 0x5A, 0x030C}},
|
|
||||||
{[]rune{0x320E}, []rune{0x28, 0x1100, 0x1161, 0x29}},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDecomposition(t *testing.T) {
|
|
||||||
runTests(t, "TestDecompositionNFD", NFD, decompositionNFDTest)
|
|
||||||
runTests(t, "TestDecompositionNFKD", NFKD, decompositionNFKDTest)
|
|
||||||
}
|
|
||||||
|
|
||||||
var compositionTest = []TestCase{
|
|
||||||
{[]rune{0x41, 0x300}, []rune{0xC0}},
|
|
||||||
{[]rune{0x41, 0x316}, []rune{0x41, 0x316}},
|
|
||||||
{[]rune{0x41, 0x300, 0x35D}, []rune{0xC0, 0x35D}},
|
|
||||||
{[]rune{0x41, 0x316, 0x300}, []rune{0xC0, 0x316}},
|
|
||||||
// blocking starter
|
|
||||||
{[]rune{0x41, 0x316, 0x40, 0x300}, []rune{0x41, 0x316, 0x40, 0x300}},
|
|
||||||
{[]rune{0x1100, 0x1161}, []rune{0xAC00}},
|
|
||||||
// parenthesized Hangul, alternate between ASCII and Hangul.
|
|
||||||
{[]rune{0x28, 0x1100, 0x1161, 0x29}, []rune{0x28, 0xAC00, 0x29}},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestComposition(t *testing.T) {
|
|
||||||
runTests(t, "TestComposition", NFC, compositionTest)
|
|
||||||
}
|
|
||||||
Generated
Vendored
-82
@@ -1,82 +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.
|
|
||||||
|
|
||||||
package norm_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"unicode/utf8"
|
|
||||||
|
|
||||||
"code.google.com/p/go.text/unicode/norm"
|
|
||||||
)
|
|
||||||
|
|
||||||
// EqualSimple uses a norm.Iter to compare two non-normalized
|
|
||||||
// strings for equivalence.
|
|
||||||
func EqualSimple(a, b string) bool {
|
|
||||||
var ia, ib norm.Iter
|
|
||||||
ia.InitString(norm.NFKD, a)
|
|
||||||
ib.InitString(norm.NFKD, b)
|
|
||||||
for !ia.Done() && !ib.Done() {
|
|
||||||
if !bytes.Equal(ia.Next(), ib.Next()) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ia.Done() && ib.Done()
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindPrefix finds the longest common prefix of ASCII characters
|
|
||||||
// of a and b.
|
|
||||||
func FindPrefix(a, b string) int {
|
|
||||||
i := 0
|
|
||||||
for ; i < len(a) && i < len(b) && a[i] < utf8.RuneSelf && a[i] == b[i]; i++ {
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
// EqualOpt is like EqualSimple, but optimizes the special
|
|
||||||
// case for ASCII characters.
|
|
||||||
func EqualOpt(a, b string) bool {
|
|
||||||
n := FindPrefix(a, b)
|
|
||||||
a, b = a[n:], b[n:]
|
|
||||||
var ia, ib norm.Iter
|
|
||||||
ia.InitString(norm.NFKD, a)
|
|
||||||
ib.InitString(norm.NFKD, b)
|
|
||||||
for !ia.Done() && !ib.Done() {
|
|
||||||
if !bytes.Equal(ia.Next(), ib.Next()) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if n := int64(FindPrefix(a[ia.Pos():], b[ib.Pos():])); n != 0 {
|
|
||||||
ia.Seek(n, 1)
|
|
||||||
ib.Seek(n, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ia.Done() && ib.Done()
|
|
||||||
}
|
|
||||||
|
|
||||||
var compareTests = []struct{ a, b string }{
|
|
||||||
{"aaa", "aaa"},
|
|
||||||
{"aaa", "aab"},
|
|
||||||
{"a\u0300a", "\u00E0a"},
|
|
||||||
{"a\u0300\u0320b", "a\u0320\u0300b"},
|
|
||||||
{"\u1E0A\u0323", "\x44\u0323\u0307"},
|
|
||||||
// A character that decomposes into multiple segments
|
|
||||||
// spans several iterations.
|
|
||||||
{"\u3304", "\u30A4\u30CB\u30F3\u30AF\u3099"},
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleIter() {
|
|
||||||
for i, t := range compareTests {
|
|
||||||
r0 := EqualSimple(t.a, t.b)
|
|
||||||
r1 := EqualOpt(t.a, t.b)
|
|
||||||
fmt.Printf("%d: %v %v\n", i, r0, r1)
|
|
||||||
}
|
|
||||||
// Output:
|
|
||||||
// 0: true true
|
|
||||||
// 1: false false
|
|
||||||
// 2: true true
|
|
||||||
// 3: true true
|
|
||||||
// 4: true true
|
|
||||||
// 5: true true
|
|
||||||
}
|
|
||||||
-54
@@ -1,54 +0,0 @@
|
|||||||
// Copyright 2013 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 test
|
|
||||||
|
|
||||||
package norm
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestProperties(t *testing.T) {
|
|
||||||
var d runeData
|
|
||||||
CK := [2]string{"C", "K"}
|
|
||||||
for k, r := 1, rune(0); r < 0x2ffff; r++ {
|
|
||||||
if k < len(testData) && r == testData[k].r {
|
|
||||||
d = testData[k]
|
|
||||||
k++
|
|
||||||
}
|
|
||||||
s := string(r)
|
|
||||||
for j, p := range []Properties{NFC.PropertiesString(s), NFKC.PropertiesString(s)} {
|
|
||||||
f := d.f[j]
|
|
||||||
if p.CCC() != d.ccc {
|
|
||||||
t.Errorf("%U: ccc(%s): was %d; want %d %X", r, CK[j], p.CCC(), d.ccc, p.index)
|
|
||||||
}
|
|
||||||
if p.isYesC() != (f.qc == Yes) {
|
|
||||||
t.Errorf("%U: YesC(%s): was %v; want %v", r, CK[j], p.isYesC(), f.qc == Yes)
|
|
||||||
}
|
|
||||||
if p.combinesBackward() != (f.qc == Maybe) {
|
|
||||||
t.Errorf("%U: combines backwards(%s): was %v; want %v", r, CK[j], p.combinesBackward(), f.qc == Maybe)
|
|
||||||
}
|
|
||||||
if p.nLeadingNonStarters() != d.nLead {
|
|
||||||
t.Errorf("%U: nLead(%s): was %d; want %d %#v %#v", r, CK[j], p.nLeadingNonStarters(), d.nLead, p, d)
|
|
||||||
}
|
|
||||||
if p.nTrailingNonStarters() != d.nTrail {
|
|
||||||
t.Errorf("%U: nTrail(%s): was %d; want %d %#v %#v", r, CK[j], p.nTrailingNonStarters(), d.nTrail, p, d)
|
|
||||||
}
|
|
||||||
if p.combinesForward() != f.combinesForward {
|
|
||||||
t.Errorf("%U: combines forward(%s): was %v; want %v %#v", r, CK[j], p.combinesForward(), f.combinesForward, p)
|
|
||||||
}
|
|
||||||
// Skip Hangul as it is algorithmically computed.
|
|
||||||
if r >= hangulBase && r < hangulEnd {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if p.hasDecomposition() {
|
|
||||||
if has := f.decomposition != ""; !has {
|
|
||||||
t.Errorf("%U: hasDecomposition(%s): was %v; want %v", r, CK[j], p.hasDecomposition(), has)
|
|
||||||
}
|
|
||||||
if string(p.Decomposition()) != f.decomposition {
|
|
||||||
t.Errorf("%U: decomp(%s): was %+q; want %+q", r, CK[j], p.Decomposition(), f.decomposition)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-98
@@ -1,98 +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 (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func doIterNorm(f Form, s string) []byte {
|
|
||||||
acc := []byte{}
|
|
||||||
i := Iter{}
|
|
||||||
i.InitString(f, s)
|
|
||||||
for !i.Done() {
|
|
||||||
acc = append(acc, i.Next()...)
|
|
||||||
}
|
|
||||||
return acc
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIterNext(t *testing.T) {
|
|
||||||
runNormTests(t, "IterNext", func(f Form, out []byte, s string) []byte {
|
|
||||||
return doIterNorm(f, string(append(out, s...)))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type SegmentTest struct {
|
|
||||||
in string
|
|
||||||
out []string
|
|
||||||
}
|
|
||||||
|
|
||||||
var segmentTests = []SegmentTest{
|
|
||||||
{"\u1E0A\u0323a", []string{"\x44\u0323\u0307", "a", ""}},
|
|
||||||
{rep('a', segSize), append(strings.Split(rep('a', segSize), ""), "")},
|
|
||||||
{rep('a', segSize+2), append(strings.Split(rep('a', segSize+2), ""), "")},
|
|
||||||
{rep('a', segSize) + "\u0300aa",
|
|
||||||
append(strings.Split(rep('a', segSize-1), ""), "a\u0300", "a", "a", "")},
|
|
||||||
|
|
||||||
// U+0f73 is NOT treated as a starter as it is a modifier
|
|
||||||
{"a" + grave(29) + "\u0f73", []string{"a" + grave(29), cgj + "\u0f73"}},
|
|
||||||
{"a\u0f73", []string{"a\u0f73"}},
|
|
||||||
|
|
||||||
// U+ff9e is treated as a non-starter.
|
|
||||||
// TODO: should we? Note that this will only affect iteration, as whether
|
|
||||||
// or not we do so does not affect the normalization output and will either
|
|
||||||
// way result in consistent iteration output.
|
|
||||||
{"a" + grave(30) + "\uff9e", []string{"a" + grave(30), cgj + "\uff9e"}},
|
|
||||||
{"a\uff9e", []string{"a\uff9e"}},
|
|
||||||
}
|
|
||||||
|
|
||||||
var segmentTestsK = []SegmentTest{
|
|
||||||
{"\u3332", []string{"\u30D5", "\u30A1", "\u30E9", "\u30C3", "\u30C8\u3099", ""}},
|
|
||||||
// last segment of multi-segment decomposition needs normalization
|
|
||||||
{"\u3332\u093C", []string{"\u30D5", "\u30A1", "\u30E9", "\u30C3", "\u30C8\u093C\u3099", ""}},
|
|
||||||
{"\u320E", []string{"\x28", "\uAC00", "\x29"}},
|
|
||||||
|
|
||||||
// last segment should be copied to start of buffer.
|
|
||||||
{"\ufdfa", []string{"\u0635", "\u0644", "\u0649", " ", "\u0627", "\u0644", "\u0644", "\u0647", " ", "\u0639", "\u0644", "\u064a", "\u0647", " ", "\u0648", "\u0633", "\u0644", "\u0645", ""}},
|
|
||||||
{"\ufdfa" + grave(30), []string{"\u0635", "\u0644", "\u0649", " ", "\u0627", "\u0644", "\u0644", "\u0647", " ", "\u0639", "\u0644", "\u064a", "\u0647", " ", "\u0648", "\u0633", "\u0644", "\u0645" + grave(30), ""}},
|
|
||||||
{"\uFDFA" + grave(64), []string{"\u0635", "\u0644", "\u0649", " ", "\u0627", "\u0644", "\u0644", "\u0647", " ", "\u0639", "\u0644", "\u064a", "\u0647", " ", "\u0648", "\u0633", "\u0644", "\u0645" + grave(30), cgj + grave(30), cgj + grave(4), ""}},
|
|
||||||
|
|
||||||
// Hangul and Jamo are grouped togeter.
|
|
||||||
{"\uAC00", []string{"\u1100\u1161", ""}},
|
|
||||||
{"\uAC01", []string{"\u1100\u1161\u11A8", ""}},
|
|
||||||
{"\u1100\u1161", []string{"\u1100\u1161", ""}},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note that, by design, segmentation is equal for composing and decomposing forms.
|
|
||||||
func TestIterSegmentation(t *testing.T) {
|
|
||||||
segmentTest(t, "SegmentTestD", NFD, segmentTests)
|
|
||||||
segmentTest(t, "SegmentTestC", NFC, segmentTests)
|
|
||||||
segmentTest(t, "SegmentTestKD", NFKD, segmentTestsK)
|
|
||||||
segmentTest(t, "SegmentTestKC", NFKC, segmentTestsK)
|
|
||||||
}
|
|
||||||
|
|
||||||
func segmentTest(t *testing.T, name string, f Form, tests []SegmentTest) {
|
|
||||||
iter := Iter{}
|
|
||||||
for i, tt := range tests {
|
|
||||||
iter.InitString(f, tt.in)
|
|
||||||
for j, seg := range tt.out {
|
|
||||||
if seg == "" {
|
|
||||||
if !iter.Done() {
|
|
||||||
res := string(iter.Next())
|
|
||||||
t.Errorf(`%s:%d:%d: expected Done()==true, found segment %+q`, name, i, j, res)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if iter.Done() {
|
|
||||||
t.Errorf("%s:%d:%d: Done()==true, want false", name, i, j)
|
|
||||||
}
|
|
||||||
seg = f.String(seg)
|
|
||||||
if res := string(iter.Next()); res != seg {
|
|
||||||
t.Errorf(`%s:%d:%d" segment was %+q (%d); want %+q (%d)`, name, i, j, pc(res), len(res), pc(seg), len(seg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-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")
|
|
||||||
}
|
|
||||||
-14
@@ -1,14 +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_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPlaceHolder(t *testing.T) {
|
|
||||||
// Does nothing, just allows the Makefile to be canonical
|
|
||||||
// while waiting for the package itself to be written.
|
|
||||||
}
|
|
||||||
-1086
File diff suppressed because it is too large.
Load diff
-318
@@ -1,318 +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
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"regexp"
|
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
|
|
||||||
"code.google.com/p/go.text/unicode/norm"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
loadTestData()
|
|
||||||
CharacterByCharacterTests()
|
|
||||||
StandardTests()
|
|
||||||
PerformanceTest()
|
|
||||||
if errorCount == 0 {
|
|
||||||
fmt.Println("PASS")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const file = "NormalizationTest.txt"
|
|
||||||
|
|
||||||
var url = flag.String("url",
|
|
||||||
"http://www.unicode.org/Public/"+unicode.Version+"/ucd/"+file,
|
|
||||||
"URL of Unicode database directory")
|
|
||||||
var localFiles = flag.Bool("local",
|
|
||||||
false,
|
|
||||||
"data files have been copied to the current directory; for debugging only")
|
|
||||||
|
|
||||||
var logger = log.New(os.Stderr, "", log.Lshortfile)
|
|
||||||
|
|
||||||
// This regression test runs the test set in NormalizationTest.txt
|
|
||||||
// (taken from http://www.unicode.org/Public/<unicode.Version>/ucd/).
|
|
||||||
//
|
|
||||||
// NormalizationTest.txt has form:
|
|
||||||
// @Part0 # Specific cases
|
|
||||||
// #
|
|
||||||
// 1E0A;1E0A;0044 0307;1E0A;0044 0307; # (Ḋ; Ḋ; D◌̇; Ḋ; D◌̇; ) LATIN CAPITAL LETTER D WITH DOT ABOVE
|
|
||||||
// 1E0C;1E0C;0044 0323;1E0C;0044 0323; # (Ḍ; Ḍ; D◌̣; Ḍ; D◌̣; ) LATIN CAPITAL LETTER D WITH DOT BELOW
|
|
||||||
//
|
|
||||||
// Each test has 5 columns (c1, c2, c3, c4, c5), where
|
|
||||||
// (c1, c2, c3, c4, c5) == (c1, NFC(c1), NFD(c1), NFKC(c1), NFKD(c1))
|
|
||||||
//
|
|
||||||
// CONFORMANCE:
|
|
||||||
// 1. The following invariants must be true for all conformant implementations
|
|
||||||
//
|
|
||||||
// NFC
|
|
||||||
// c2 == NFC(c1) == NFC(c2) == NFC(c3)
|
|
||||||
// c4 == NFC(c4) == NFC(c5)
|
|
||||||
//
|
|
||||||
// NFD
|
|
||||||
// c3 == NFD(c1) == NFD(c2) == NFD(c3)
|
|
||||||
// c5 == NFD(c4) == NFD(c5)
|
|
||||||
//
|
|
||||||
// NFKC
|
|
||||||
// c4 == NFKC(c1) == NFKC(c2) == NFKC(c3) == NFKC(c4) == NFKC(c5)
|
|
||||||
//
|
|
||||||
// NFKD
|
|
||||||
// c5 == NFKD(c1) == NFKD(c2) == NFKD(c3) == NFKD(c4) == NFKD(c5)
|
|
||||||
//
|
|
||||||
// 2. For every code point X assigned in this version of Unicode that is not
|
|
||||||
// specifically listed in Part 1, the following invariants must be true
|
|
||||||
// for all conformant implementations:
|
|
||||||
//
|
|
||||||
// X == NFC(X) == NFD(X) == NFKC(X) == NFKD(X)
|
|
||||||
//
|
|
||||||
|
|
||||||
// Column types.
|
|
||||||
const (
|
|
||||||
cRaw = iota
|
|
||||||
cNFC
|
|
||||||
cNFD
|
|
||||||
cNFKC
|
|
||||||
cNFKD
|
|
||||||
cMaxColumns
|
|
||||||
)
|
|
||||||
|
|
||||||
// Holds data from NormalizationTest.txt
|
|
||||||
var part []Part
|
|
||||||
|
|
||||||
type Part struct {
|
|
||||||
name string
|
|
||||||
number int
|
|
||||||
tests []Test
|
|
||||||
}
|
|
||||||
|
|
||||||
type Test struct {
|
|
||||||
name string
|
|
||||||
partnr int
|
|
||||||
number int
|
|
||||||
r rune // used for character by character test
|
|
||||||
cols [cMaxColumns]string // Each has 5 entries, see below.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t Test) Name() string {
|
|
||||||
if t.number < 0 {
|
|
||||||
return part[t.partnr].name
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s:%d", part[t.partnr].name, t.number)
|
|
||||||
}
|
|
||||||
|
|
||||||
var partRe = regexp.MustCompile(`@Part(\d) # (.*)$`)
|
|
||||||
var testRe = regexp.MustCompile(`^` + strings.Repeat(`([\dA-F ]+);`, 5) + ` # (.*)$`)
|
|
||||||
|
|
||||||
var counter int
|
|
||||||
|
|
||||||
// Load the data form NormalizationTest.txt
|
|
||||||
func loadTestData() {
|
|
||||||
if *localFiles {
|
|
||||||
pwd, _ := os.Getwd()
|
|
||||||
*url = "file://" + path.Join(pwd, file)
|
|
||||||
}
|
|
||||||
t := &http.Transport{}
|
|
||||||
t.RegisterProtocol("file", http.NewFileTransport(http.Dir("/")))
|
|
||||||
c := &http.Client{Transport: t}
|
|
||||||
resp, err := c.Get(*url)
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal(err)
|
|
||||||
}
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
logger.Fatal("bad GET status for "+file, resp.Status)
|
|
||||||
}
|
|
||||||
f := resp.Body
|
|
||||||
defer f.Close()
|
|
||||||
scanner := bufio.NewScanner(f)
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := scanner.Text()
|
|
||||||
if len(line) == 0 || line[0] == '#' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
m := partRe.FindStringSubmatch(line)
|
|
||||||
if m != nil {
|
|
||||||
if len(m) < 3 {
|
|
||||||
logger.Fatal("Failed to parse Part: ", line)
|
|
||||||
}
|
|
||||||
i, err := strconv.Atoi(m[1])
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal(err)
|
|
||||||
}
|
|
||||||
name := m[2]
|
|
||||||
part = append(part, Part{name: name[:len(name)-1], number: i})
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
m = testRe.FindStringSubmatch(line)
|
|
||||||
if m == nil || len(m) < 7 {
|
|
||||||
logger.Fatalf(`Failed to parse: "%s" result: %#v`, line, m)
|
|
||||||
}
|
|
||||||
test := Test{name: m[6], partnr: len(part) - 1, number: counter}
|
|
||||||
counter++
|
|
||||||
for j := 1; j < len(m)-1; j++ {
|
|
||||||
for _, split := range strings.Split(m[j], " ") {
|
|
||||||
r, err := strconv.ParseUint(split, 16, 64)
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal(err)
|
|
||||||
}
|
|
||||||
if test.r == 0 {
|
|
||||||
// save for CharacterByCharacterTests
|
|
||||||
test.r = rune(r)
|
|
||||||
}
|
|
||||||
var buf [utf8.UTFMax]byte
|
|
||||||
sz := utf8.EncodeRune(buf[:], rune(r))
|
|
||||||
test.cols[j-1] += string(buf[:sz])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
part := &part[len(part)-1]
|
|
||||||
part.tests = append(part.tests, test)
|
|
||||||
}
|
|
||||||
if scanner.Err() != nil {
|
|
||||||
logger.Fatal(scanner.Err())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var fstr = []string{"NFC", "NFD", "NFKC", "NFKD"}
|
|
||||||
|
|
||||||
var errorCount int
|
|
||||||
|
|
||||||
func cmpResult(t *Test, name string, f norm.Form, gold, test, result string) {
|
|
||||||
if gold != result {
|
|
||||||
errorCount++
|
|
||||||
if errorCount > 20 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger.Printf("%s:%s: %s(%+q)=%+q; want %+q: %s",
|
|
||||||
t.Name(), name, fstr[f], test, result, gold, t.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmpIsNormal(t *Test, name string, f norm.Form, test string, result, want bool) {
|
|
||||||
if result != want {
|
|
||||||
errorCount++
|
|
||||||
if errorCount > 20 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger.Printf("%s:%s: %s(%+q)=%v; want %v", t.Name(), name, fstr[f], test, result, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func doTest(t *Test, f norm.Form, gold, test string) {
|
|
||||||
testb := []byte(test)
|
|
||||||
result := f.Bytes(testb)
|
|
||||||
cmpResult(t, "Bytes", f, gold, test, string(result))
|
|
||||||
|
|
||||||
sresult := f.String(test)
|
|
||||||
cmpResult(t, "String", f, gold, test, sresult)
|
|
||||||
|
|
||||||
acc := []byte{}
|
|
||||||
i := norm.Iter{}
|
|
||||||
i.InitString(f, test)
|
|
||||||
for !i.Done() {
|
|
||||||
acc = append(acc, i.Next()...)
|
|
||||||
}
|
|
||||||
cmpResult(t, "Iter.Next", f, gold, test, string(acc))
|
|
||||||
|
|
||||||
buf := make([]byte, 128)
|
|
||||||
acc = nil
|
|
||||||
for p := 0; p < len(testb); {
|
|
||||||
nDst, nSrc, _ := f.Transform(buf, testb[p:], true)
|
|
||||||
acc = append(acc, buf[:nDst]...)
|
|
||||||
p += nSrc
|
|
||||||
}
|
|
||||||
cmpResult(t, "Transform", f, gold, test, string(acc))
|
|
||||||
|
|
||||||
for i := range test {
|
|
||||||
out := f.Append(f.Bytes([]byte(test[:i])), []byte(test[i:])...)
|
|
||||||
cmpResult(t, fmt.Sprintf(":Append:%d", i), f, gold, test, string(out))
|
|
||||||
}
|
|
||||||
cmpIsNormal(t, "IsNormal", f, test, f.IsNormal([]byte(test)), test == gold)
|
|
||||||
cmpIsNormal(t, "IsNormalString", f, test, f.IsNormalString(test), test == gold)
|
|
||||||
}
|
|
||||||
|
|
||||||
func doConformanceTests(t *Test, partn int) {
|
|
||||||
for i := 0; i <= 2; i++ {
|
|
||||||
doTest(t, norm.NFC, t.cols[1], t.cols[i])
|
|
||||||
doTest(t, norm.NFD, t.cols[2], t.cols[i])
|
|
||||||
doTest(t, norm.NFKC, t.cols[3], t.cols[i])
|
|
||||||
doTest(t, norm.NFKD, t.cols[4], t.cols[i])
|
|
||||||
}
|
|
||||||
for i := 3; i <= 4; i++ {
|
|
||||||
doTest(t, norm.NFC, t.cols[3], t.cols[i])
|
|
||||||
doTest(t, norm.NFD, t.cols[4], t.cols[i])
|
|
||||||
doTest(t, norm.NFKC, t.cols[3], t.cols[i])
|
|
||||||
doTest(t, norm.NFKD, t.cols[4], t.cols[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func CharacterByCharacterTests() {
|
|
||||||
tests := part[1].tests
|
|
||||||
var last rune = 0
|
|
||||||
for i := 0; i <= len(tests); i++ { // last one is special case
|
|
||||||
var r rune
|
|
||||||
if i == len(tests) {
|
|
||||||
r = 0x2FA1E // Don't have to go to 0x10FFFF
|
|
||||||
} else {
|
|
||||||
r = tests[i].r
|
|
||||||
}
|
|
||||||
for last++; last < r; last++ {
|
|
||||||
// Check all characters that were not explicitly listed in the test.
|
|
||||||
t := &Test{partnr: 1, number: -1}
|
|
||||||
char := string(last)
|
|
||||||
doTest(t, norm.NFC, char, char)
|
|
||||||
doTest(t, norm.NFD, char, char)
|
|
||||||
doTest(t, norm.NFKC, char, char)
|
|
||||||
doTest(t, norm.NFKD, char, char)
|
|
||||||
}
|
|
||||||
if i < len(tests) {
|
|
||||||
doConformanceTests(&tests[i], 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func StandardTests() {
|
|
||||||
for _, j := range []int{0, 2, 3} {
|
|
||||||
for _, test := range part[j].tests {
|
|
||||||
doConformanceTests(&test, j)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PerformanceTest verifies that normalization is O(n). If any of the
|
|
||||||
// code does not properly check for maxCombiningChars, normalization
|
|
||||||
// may exhibit O(n**2) behavior.
|
|
||||||
func PerformanceTest() {
|
|
||||||
runtime.GOMAXPROCS(2)
|
|
||||||
success := make(chan bool, 1)
|
|
||||||
go func() {
|
|
||||||
buf := bytes.Repeat([]byte("\u035D"), 1024*1024)
|
|
||||||
buf = append(buf, "\u035B"...)
|
|
||||||
norm.NFC.Append(nil, buf...)
|
|
||||||
success <- true
|
|
||||||
}()
|
|
||||||
timeout := time.After(1 * time.Second)
|
|
||||||
select {
|
|
||||||
case <-success:
|
|
||||||
// test completed before the timeout
|
|
||||||
case <-timeout:
|
|
||||||
errorCount++
|
|
||||||
logger.Printf(`unexpectedly long time to complete PerformanceTest`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-56
@@ -1,56 +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 (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var bufSizes = []int{1, 2, 3, 4, 5, 6, 7, 8, 100, 101, 102, 103, 4000, 4001, 4002, 4003}
|
|
||||||
|
|
||||||
func readFunc(size int) appendFunc {
|
|
||||||
return func(f Form, out []byte, s string) []byte {
|
|
||||||
out = append(out, s...)
|
|
||||||
r := f.Reader(bytes.NewBuffer(out))
|
|
||||||
buf := make([]byte, size)
|
|
||||||
result := []byte{}
|
|
||||||
for n, err := 0, error(nil); err == nil; {
|
|
||||||
n, err = r.Read(buf)
|
|
||||||
result = append(result, buf[:n]...)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReader(t *testing.T) {
|
|
||||||
for _, s := range bufSizes {
|
|
||||||
name := fmt.Sprintf("TestReader%d", s)
|
|
||||||
runNormTests(t, name, readFunc(s))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeFunc(size int) appendFunc {
|
|
||||||
return func(f Form, out []byte, s string) []byte {
|
|
||||||
in := append(out, s...)
|
|
||||||
result := new(bytes.Buffer)
|
|
||||||
w := f.Writer(result)
|
|
||||||
buf := make([]byte, size)
|
|
||||||
for n := 0; len(in) > 0; in = in[n:] {
|
|
||||||
n = copy(buf, in)
|
|
||||||
_, _ = w.Write(buf[:n])
|
|
||||||
}
|
|
||||||
w.Close()
|
|
||||||
return result.Bytes()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWriter(t *testing.T) {
|
|
||||||
for _, s := range bufSizes {
|
|
||||||
name := fmt.Sprintf("TestWriter%d", s)
|
|
||||||
runNormTests(t, name, writeFunc(s))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-6989
File diff suppressed because it is too large.
Load diff
-101
@@ -1,101 +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 (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"code.google.com/p/go.text/transform"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTransform(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
f Form
|
|
||||||
in, out string
|
|
||||||
eof bool
|
|
||||||
dstSize int
|
|
||||||
err error
|
|
||||||
}{
|
|
||||||
{NFC, "ab", "ab", true, 2, nil},
|
|
||||||
{NFC, "qx", "qx", true, 2, nil},
|
|
||||||
{NFD, "qx", "qx", true, 2, nil},
|
|
||||||
{NFC, "", "", true, 1, nil},
|
|
||||||
{NFD, "", "", true, 1, nil},
|
|
||||||
{NFC, "", "", false, 1, nil},
|
|
||||||
{NFD, "", "", false, 1, nil},
|
|
||||||
|
|
||||||
// Normalized segment does not fit in destination.
|
|
||||||
{NFD, "ö", "", true, 1, transform.ErrShortDst},
|
|
||||||
{NFD, "ö", "", true, 2, transform.ErrShortDst},
|
|
||||||
|
|
||||||
// As an artifact of the algorithm, only full segments are written.
|
|
||||||
// This is not strictly required, and some bytes could be written.
|
|
||||||
// In practice, for Transform to not block, the destination buffer
|
|
||||||
// should be at least MaxSegmentSize to work anyway and these edge
|
|
||||||
// conditions will be relatively rare.
|
|
||||||
{NFC, "ab", "", true, 1, transform.ErrShortDst},
|
|
||||||
// This is even true for inert runes.
|
|
||||||
{NFC, "qx", "", true, 1, transform.ErrShortDst},
|
|
||||||
{NFC, "a\u0300abc", "\u00e0a", true, 4, transform.ErrShortDst},
|
|
||||||
|
|
||||||
// We cannot write a segment if succesive runes could still change the result.
|
|
||||||
{NFD, "ö", "", false, 3, transform.ErrShortSrc},
|
|
||||||
{NFC, "a\u0300", "", false, 4, transform.ErrShortSrc},
|
|
||||||
{NFD, "a\u0300", "", false, 4, transform.ErrShortSrc},
|
|
||||||
{NFC, "ö", "", false, 3, transform.ErrShortSrc},
|
|
||||||
|
|
||||||
{NFC, "a\u0300", "", true, 1, transform.ErrShortDst},
|
|
||||||
// Theoretically could fit, but won't due to simplified checks.
|
|
||||||
{NFC, "a\u0300", "", true, 2, transform.ErrShortDst},
|
|
||||||
{NFC, "a\u0300", "", true, 3, transform.ErrShortDst},
|
|
||||||
{NFC, "a\u0300", "\u00e0", true, 4, nil},
|
|
||||||
|
|
||||||
{NFD, "öa\u0300", "o\u0308", false, 8, transform.ErrShortSrc},
|
|
||||||
{NFD, "öa\u0300ö", "o\u0308a\u0300", true, 8, transform.ErrShortDst},
|
|
||||||
{NFD, "öa\u0300ö", "o\u0308a\u0300", false, 12, transform.ErrShortSrc},
|
|
||||||
|
|
||||||
// Illegal input is copied verbatim.
|
|
||||||
{NFD, "\xbd\xb2=\xbc ", "\xbd\xb2=\xbc ", true, 8, nil},
|
|
||||||
}
|
|
||||||
b := make([]byte, 100)
|
|
||||||
for i, tt := range tests {
|
|
||||||
nDst, _, err := tt.f.Transform(b[:tt.dstSize], []byte(tt.in), tt.eof)
|
|
||||||
out := string(b[:nDst])
|
|
||||||
if out != tt.out || err != tt.err {
|
|
||||||
t.Errorf("%d: was %+q (%v); want %+q (%v)", i, out, err, tt.out, tt.err)
|
|
||||||
}
|
|
||||||
if want := tt.f.String(tt.in)[:nDst]; want != out {
|
|
||||||
t.Errorf("%d: incorect normalization: was %+q; want %+q", i, out, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var transBufSizes = []int{
|
|
||||||
MaxTransformChunkSize,
|
|
||||||
3 * MaxTransformChunkSize / 2,
|
|
||||||
2 * MaxTransformChunkSize,
|
|
||||||
3 * MaxTransformChunkSize,
|
|
||||||
100 * MaxTransformChunkSize,
|
|
||||||
}
|
|
||||||
|
|
||||||
func doTransNorm(f Form, buf []byte, b []byte) []byte {
|
|
||||||
acc := []byte{}
|
|
||||||
for p := 0; p < len(b); {
|
|
||||||
nd, ns, _ := f.Transform(buf[:], b[p:], true)
|
|
||||||
p += ns
|
|
||||||
acc = append(acc, buf[:nd]...)
|
|
||||||
}
|
|
||||||
return acc
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTransformNorm(t *testing.T) {
|
|
||||||
for _, sz := range transBufSizes {
|
|
||||||
buf := make([]byte, sz)
|
|
||||||
runNormTests(t, fmt.Sprintf("Transform:%d", sz), func(f Form, out []byte, s string) []byte {
|
|
||||||
return doTransNorm(f, buf, append(out, s...))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-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
|
|
||||||
}
|
|
||||||
-124
@@ -1,124 +0,0 @@
|
|||||||
// Copyright 2011 The Snappy-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 snappy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ErrCorrupt reports that the input is invalid.
|
|
||||||
var ErrCorrupt = errors.New("snappy: corrupt input")
|
|
||||||
|
|
||||||
// DecodedLen returns the length of the decoded block.
|
|
||||||
func DecodedLen(src []byte) (int, error) {
|
|
||||||
v, _, err := decodedLen(src)
|
|
||||||
return v, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// decodedLen returns the length of the decoded block and the number of bytes
|
|
||||||
// that the length header occupied.
|
|
||||||
func decodedLen(src []byte) (blockLen, headerLen int, err error) {
|
|
||||||
v, n := binary.Uvarint(src)
|
|
||||||
if n == 0 {
|
|
||||||
return 0, 0, ErrCorrupt
|
|
||||||
}
|
|
||||||
if uint64(int(v)) != v {
|
|
||||||
return 0, 0, errors.New("snappy: decoded block is too large")
|
|
||||||
}
|
|
||||||
return int(v), n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode returns the decoded form of src. The returned slice may be a sub-
|
|
||||||
// slice of dst if dst was large enough to hold the entire decoded block.
|
|
||||||
// Otherwise, a newly allocated slice will be returned.
|
|
||||||
// It is valid to pass a nil dst.
|
|
||||||
func Decode(dst, src []byte) ([]byte, error) {
|
|
||||||
dLen, s, err := decodedLen(src)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(dst) < dLen {
|
|
||||||
dst = make([]byte, dLen)
|
|
||||||
}
|
|
||||||
|
|
||||||
var d, offset, length int
|
|
||||||
for s < len(src) {
|
|
||||||
switch src[s] & 0x03 {
|
|
||||||
case tagLiteral:
|
|
||||||
x := uint(src[s] >> 2)
|
|
||||||
switch {
|
|
||||||
case x < 60:
|
|
||||||
s += 1
|
|
||||||
case x == 60:
|
|
||||||
s += 2
|
|
||||||
if s > len(src) {
|
|
||||||
return nil, ErrCorrupt
|
|
||||||
}
|
|
||||||
x = uint(src[s-1])
|
|
||||||
case x == 61:
|
|
||||||
s += 3
|
|
||||||
if s > len(src) {
|
|
||||||
return nil, ErrCorrupt
|
|
||||||
}
|
|
||||||
x = uint(src[s-2]) | uint(src[s-1])<<8
|
|
||||||
case x == 62:
|
|
||||||
s += 4
|
|
||||||
if s > len(src) {
|
|
||||||
return nil, ErrCorrupt
|
|
||||||
}
|
|
||||||
x = uint(src[s-3]) | uint(src[s-2])<<8 | uint(src[s-1])<<16
|
|
||||||
case x == 63:
|
|
||||||
s += 5
|
|
||||||
if s > len(src) {
|
|
||||||
return nil, ErrCorrupt
|
|
||||||
}
|
|
||||||
x = uint(src[s-4]) | uint(src[s-3])<<8 | uint(src[s-2])<<16 | uint(src[s-1])<<24
|
|
||||||
}
|
|
||||||
length = int(x + 1)
|
|
||||||
if length <= 0 {
|
|
||||||
return nil, errors.New("snappy: unsupported literal length")
|
|
||||||
}
|
|
||||||
if length > len(dst)-d || length > len(src)-s {
|
|
||||||
return nil, ErrCorrupt
|
|
||||||
}
|
|
||||||
copy(dst[d:], src[s:s+length])
|
|
||||||
d += length
|
|
||||||
s += length
|
|
||||||
continue
|
|
||||||
|
|
||||||
case tagCopy1:
|
|
||||||
s += 2
|
|
||||||
if s > len(src) {
|
|
||||||
return nil, ErrCorrupt
|
|
||||||
}
|
|
||||||
length = 4 + int(src[s-2])>>2&0x7
|
|
||||||
offset = int(src[s-2])&0xe0<<3 | int(src[s-1])
|
|
||||||
|
|
||||||
case tagCopy2:
|
|
||||||
s += 3
|
|
||||||
if s > len(src) {
|
|
||||||
return nil, ErrCorrupt
|
|
||||||
}
|
|
||||||
length = 1 + int(src[s-3])>>2
|
|
||||||
offset = int(src[s-2]) | int(src[s-1])<<8
|
|
||||||
|
|
||||||
case tagCopy4:
|
|
||||||
return nil, errors.New("snappy: unsupported COPY_4 tag")
|
|
||||||
}
|
|
||||||
|
|
||||||
end := d + length
|
|
||||||
if offset > d || end > len(dst) {
|
|
||||||
return nil, ErrCorrupt
|
|
||||||
}
|
|
||||||
for ; d < end; d++ {
|
|
||||||
dst[d] = dst[d-offset]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if d != dLen {
|
|
||||||
return nil, ErrCorrupt
|
|
||||||
}
|
|
||||||
return dst[:d], nil
|
|
||||||
}
|
|
||||||
-261
@@ -1,261 +0,0 @@
|
|||||||
// Copyright 2011 The Snappy-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 snappy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"math/rand"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var download = flag.Bool("download", false, "If true, download any missing files before running benchmarks")
|
|
||||||
|
|
||||||
func roundtrip(b, ebuf, dbuf []byte) error {
|
|
||||||
e, err := Encode(ebuf, b)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("encoding error: %v", err)
|
|
||||||
}
|
|
||||||
d, err := Decode(dbuf, e)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("decoding error: %v", err)
|
|
||||||
}
|
|
||||||
if !bytes.Equal(b, d) {
|
|
||||||
return fmt.Errorf("roundtrip mismatch:\n\twant %v\n\tgot %v", b, d)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmpty(t *testing.T) {
|
|
||||||
if err := roundtrip(nil, nil, nil); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSmallCopy(t *testing.T) {
|
|
||||||
for _, ebuf := range [][]byte{nil, make([]byte, 20), make([]byte, 64)} {
|
|
||||||
for _, dbuf := range [][]byte{nil, make([]byte, 20), make([]byte, 64)} {
|
|
||||||
for i := 0; i < 32; i++ {
|
|
||||||
s := "aaaa" + strings.Repeat("b", i) + "aaaabbbb"
|
|
||||||
if err := roundtrip([]byte(s), ebuf, dbuf); err != nil {
|
|
||||||
t.Errorf("len(ebuf)=%d, len(dbuf)=%d, i=%d: %v", len(ebuf), len(dbuf), i, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSmallRand(t *testing.T) {
|
|
||||||
rand.Seed(27354294)
|
|
||||||
for n := 1; n < 20000; n += 23 {
|
|
||||||
b := make([]byte, n)
|
|
||||||
for i, _ := range b {
|
|
||||||
b[i] = uint8(rand.Uint32())
|
|
||||||
}
|
|
||||||
if err := roundtrip(b, nil, nil); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSmallRegular(t *testing.T) {
|
|
||||||
for n := 1; n < 20000; n += 23 {
|
|
||||||
b := make([]byte, n)
|
|
||||||
for i, _ := range b {
|
|
||||||
b[i] = uint8(i%10 + 'a')
|
|
||||||
}
|
|
||||||
if err := roundtrip(b, nil, nil); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func benchDecode(b *testing.B, src []byte) {
|
|
||||||
encoded, err := Encode(nil, src)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
// Bandwidth is in amount of uncompressed data.
|
|
||||||
b.SetBytes(int64(len(src)))
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
Decode(src, encoded)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func benchEncode(b *testing.B, src []byte) {
|
|
||||||
// Bandwidth is in amount of uncompressed data.
|
|
||||||
b.SetBytes(int64(len(src)))
|
|
||||||
dst := make([]byte, MaxEncodedLen(len(src)))
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
Encode(dst, src)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func readFile(b *testing.B, filename string) []byte {
|
|
||||||
src, err := ioutil.ReadFile(filename)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatalf("failed reading %s: %s", filename, err)
|
|
||||||
}
|
|
||||||
if len(src) == 0 {
|
|
||||||
b.Fatalf("%s has zero length", filename)
|
|
||||||
}
|
|
||||||
return src
|
|
||||||
}
|
|
||||||
|
|
||||||
// expand returns a slice of length n containing repeated copies of src.
|
|
||||||
func expand(src []byte, n int) []byte {
|
|
||||||
dst := make([]byte, n)
|
|
||||||
for x := dst; len(x) > 0; {
|
|
||||||
i := copy(x, src)
|
|
||||||
x = x[i:]
|
|
||||||
}
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
|
|
||||||
func benchWords(b *testing.B, n int, decode bool) {
|
|
||||||
// Note: the file is OS-language dependent so the resulting values are not
|
|
||||||
// directly comparable for non-US-English OS installations.
|
|
||||||
data := expand(readFile(b, "/usr/share/dict/words"), n)
|
|
||||||
if decode {
|
|
||||||
benchDecode(b, data)
|
|
||||||
} else {
|
|
||||||
benchEncode(b, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkWordsDecode1e3(b *testing.B) { benchWords(b, 1e3, true) }
|
|
||||||
func BenchmarkWordsDecode1e4(b *testing.B) { benchWords(b, 1e4, true) }
|
|
||||||
func BenchmarkWordsDecode1e5(b *testing.B) { benchWords(b, 1e5, true) }
|
|
||||||
func BenchmarkWordsDecode1e6(b *testing.B) { benchWords(b, 1e6, true) }
|
|
||||||
func BenchmarkWordsEncode1e3(b *testing.B) { benchWords(b, 1e3, false) }
|
|
||||||
func BenchmarkWordsEncode1e4(b *testing.B) { benchWords(b, 1e4, false) }
|
|
||||||
func BenchmarkWordsEncode1e5(b *testing.B) { benchWords(b, 1e5, false) }
|
|
||||||
func BenchmarkWordsEncode1e6(b *testing.B) { benchWords(b, 1e6, false) }
|
|
||||||
|
|
||||||
// testFiles' values are copied directly from
|
|
||||||
// https://code.google.com/p/snappy/source/browse/trunk/snappy_unittest.cc.
|
|
||||||
// The label field is unused in snappy-go.
|
|
||||||
var testFiles = []struct {
|
|
||||||
label string
|
|
||||||
filename string
|
|
||||||
}{
|
|
||||||
{"html", "html"},
|
|
||||||
{"urls", "urls.10K"},
|
|
||||||
{"jpg", "house.jpg"},
|
|
||||||
{"pdf", "mapreduce-osdi-1.pdf"},
|
|
||||||
{"html4", "html_x_4"},
|
|
||||||
{"cp", "cp.html"},
|
|
||||||
{"c", "fields.c"},
|
|
||||||
{"lsp", "grammar.lsp"},
|
|
||||||
{"xls", "kennedy.xls"},
|
|
||||||
{"txt1", "alice29.txt"},
|
|
||||||
{"txt2", "asyoulik.txt"},
|
|
||||||
{"txt3", "lcet10.txt"},
|
|
||||||
{"txt4", "plrabn12.txt"},
|
|
||||||
{"bin", "ptt5"},
|
|
||||||
{"sum", "sum"},
|
|
||||||
{"man", "xargs.1"},
|
|
||||||
{"pb", "geo.protodata"},
|
|
||||||
{"gaviota", "kppkn.gtb"},
|
|
||||||
}
|
|
||||||
|
|
||||||
// The test data files are present at this canonical URL.
|
|
||||||
const baseURL = "https://snappy.googlecode.com/svn/trunk/testdata/"
|
|
||||||
|
|
||||||
func downloadTestdata(basename string) (errRet error) {
|
|
||||||
filename := filepath.Join("testdata", basename)
|
|
||||||
f, err := os.Create(filename)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to create %s: %s", filename, err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
defer func() {
|
|
||||||
if errRet != nil {
|
|
||||||
os.Remove(filename)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
resp, err := http.Get(baseURL + basename)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to download %s: %s", baseURL+basename, err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
_, err = io.Copy(f, resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to write %s: %s", filename, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func benchFile(b *testing.B, n int, decode bool) {
|
|
||||||
filename := filepath.Join("testdata", testFiles[n].filename)
|
|
||||||
if stat, err := os.Stat(filename); err != nil || stat.Size() == 0 {
|
|
||||||
if !*download {
|
|
||||||
b.Fatal("test data not found; skipping benchmark without the -download flag")
|
|
||||||
}
|
|
||||||
// Download the official snappy C++ implementation reference test data
|
|
||||||
// files for benchmarking.
|
|
||||||
if err := os.Mkdir("testdata", 0777); err != nil && !os.IsExist(err) {
|
|
||||||
b.Fatalf("failed to create testdata: %s", err)
|
|
||||||
}
|
|
||||||
for _, tf := range testFiles {
|
|
||||||
if err := downloadTestdata(tf.filename); err != nil {
|
|
||||||
b.Fatalf("failed to download testdata: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data := readFile(b, filename)
|
|
||||||
if decode {
|
|
||||||
benchDecode(b, data)
|
|
||||||
} else {
|
|
||||||
benchEncode(b, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Naming convention is kept similar to what snappy's C++ implementation uses.
|
|
||||||
func Benchmark_UFlat0(b *testing.B) { benchFile(b, 0, true) }
|
|
||||||
func Benchmark_UFlat1(b *testing.B) { benchFile(b, 1, true) }
|
|
||||||
func Benchmark_UFlat2(b *testing.B) { benchFile(b, 2, true) }
|
|
||||||
func Benchmark_UFlat3(b *testing.B) { benchFile(b, 3, true) }
|
|
||||||
func Benchmark_UFlat4(b *testing.B) { benchFile(b, 4, true) }
|
|
||||||
func Benchmark_UFlat5(b *testing.B) { benchFile(b, 5, true) }
|
|
||||||
func Benchmark_UFlat6(b *testing.B) { benchFile(b, 6, true) }
|
|
||||||
func Benchmark_UFlat7(b *testing.B) { benchFile(b, 7, true) }
|
|
||||||
func Benchmark_UFlat8(b *testing.B) { benchFile(b, 8, true) }
|
|
||||||
func Benchmark_UFlat9(b *testing.B) { benchFile(b, 9, true) }
|
|
||||||
func Benchmark_UFlat10(b *testing.B) { benchFile(b, 10, true) }
|
|
||||||
func Benchmark_UFlat11(b *testing.B) { benchFile(b, 11, true) }
|
|
||||||
func Benchmark_UFlat12(b *testing.B) { benchFile(b, 12, true) }
|
|
||||||
func Benchmark_UFlat13(b *testing.B) { benchFile(b, 13, true) }
|
|
||||||
func Benchmark_UFlat14(b *testing.B) { benchFile(b, 14, true) }
|
|
||||||
func Benchmark_UFlat15(b *testing.B) { benchFile(b, 15, true) }
|
|
||||||
func Benchmark_UFlat16(b *testing.B) { benchFile(b, 16, true) }
|
|
||||||
func Benchmark_UFlat17(b *testing.B) { benchFile(b, 17, true) }
|
|
||||||
func Benchmark_ZFlat0(b *testing.B) { benchFile(b, 0, false) }
|
|
||||||
func Benchmark_ZFlat1(b *testing.B) { benchFile(b, 1, false) }
|
|
||||||
func Benchmark_ZFlat2(b *testing.B) { benchFile(b, 2, false) }
|
|
||||||
func Benchmark_ZFlat3(b *testing.B) { benchFile(b, 3, false) }
|
|
||||||
func Benchmark_ZFlat4(b *testing.B) { benchFile(b, 4, false) }
|
|
||||||
func Benchmark_ZFlat5(b *testing.B) { benchFile(b, 5, false) }
|
|
||||||
func Benchmark_ZFlat6(b *testing.B) { benchFile(b, 6, false) }
|
|
||||||
func Benchmark_ZFlat7(b *testing.B) { benchFile(b, 7, false) }
|
|
||||||
func Benchmark_ZFlat8(b *testing.B) { benchFile(b, 8, false) }
|
|
||||||
func Benchmark_ZFlat9(b *testing.B) { benchFile(b, 9, false) }
|
|
||||||
func Benchmark_ZFlat10(b *testing.B) { benchFile(b, 10, false) }
|
|
||||||
func Benchmark_ZFlat11(b *testing.B) { benchFile(b, 11, false) }
|
|
||||||
func Benchmark_ZFlat12(b *testing.B) { benchFile(b, 12, false) }
|
|
||||||
func Benchmark_ZFlat13(b *testing.B) { benchFile(b, 13, false) }
|
|
||||||
func Benchmark_ZFlat14(b *testing.B) { benchFile(b, 14, false) }
|
|
||||||
func Benchmark_ZFlat15(b *testing.B) { benchFile(b, 15, false) }
|
|
||||||
func Benchmark_ZFlat16(b *testing.B) { benchFile(b, 16, false) }
|
|
||||||
func Benchmark_ZFlat17(b *testing.B) { benchFile(b, 17, false) }
|
|
||||||
-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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
@@ -4,4 +4,6 @@ go:
|
|||||||
- 1.1
|
- 1.1
|
||||||
- 1.2
|
- 1.2
|
||||||
- 1.3
|
- 1.3
|
||||||
|
- 1.4
|
||||||
|
- 1.5
|
||||||
- tip
|
- tip
|
||||||
+1
-1
@@ -4,7 +4,7 @@ go-lz4
|
|||||||
go-lz4 is port of LZ4 lossless compression algorithm to Go. The original C code
|
go-lz4 is port of LZ4 lossless compression algorithm to Go. The original C code
|
||||||
is located at:
|
is located at:
|
||||||
|
|
||||||
https://code.google.com/p/lz4/
|
https://github.com/Cyan4973/lz4
|
||||||
|
|
||||||
Status
|
Status
|
||||||
------
|
------
|
||||||
|
|||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
// +build gofuzz
|
||||||
|
|
||||||
|
package lz4
|
||||||
|
|
||||||
|
import "encoding/binary"
|
||||||
|
|
||||||
|
func Fuzz(data []byte) int {
|
||||||
|
|
||||||
|
if len(data) < 4 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
ln := binary.LittleEndian.Uint32(data)
|
||||||
|
if ln > (1 << 21) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := Decode(nil, data); err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
-63
@@ -1,63 +0,0 @@
|
|||||||
package lz4
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io/ioutil"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var testfile, _ = ioutil.ReadFile("testdata/pg1661.txt")
|
|
||||||
|
|
||||||
func roundtrip(t *testing.T, input []byte) {
|
|
||||||
|
|
||||||
dst, err := Encode(nil, input)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("got error during compression: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
output, err := Decode(nil, dst)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("got error during decompress: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(output, input) {
|
|
||||||
t.Errorf("roundtrip failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmpty(t *testing.T) {
|
|
||||||
roundtrip(t, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLengths(t *testing.T) {
|
|
||||||
|
|
||||||
for i := 0; i < 1024; i++ {
|
|
||||||
roundtrip(t, testfile[:i])
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 1024; i < 4096; i += 23 {
|
|
||||||
roundtrip(t, testfile[:i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWords(t *testing.T) {
|
|
||||||
roundtrip(t, testfile)
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkLZ4Encode(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
Encode(nil, testfile)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkLZ4Decode(b *testing.B) {
|
|
||||||
|
|
||||||
var compressed, _ = Encode(nil, testfile)
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
Decode(nil, compressed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+199
-194
@@ -1,194 +1,199 @@
|
|||||||
/*
|
/*
|
||||||
* 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) || int(d.dpos+length) > len(d.dst) {
|
||||||
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 {
|
|
||||||
d.cp(4, decr[literal])
|
if literal < 4 {
|
||||||
} else {
|
if int(d.dpos+4) > len(d.dst) {
|
||||||
length += 4
|
return nil, ErrCorrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.dpos+length > uncompressedLen {
|
d.cp(4, decr[literal])
|
||||||
return nil, ErrCorrupt
|
} else {
|
||||||
}
|
length += 4
|
||||||
|
}
|
||||||
d.cp(length, 0)
|
|
||||||
}
|
if d.dpos+length > uncompressedLen {
|
||||||
}
|
return nil, ErrCorrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
d.cp(length, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
-13052
File diff suppressed because it is too large.
Load diff
+190
-188
@@ -1,188 +1,190 @@
|
|||||||
/*
|
/*
|
||||||
* 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 (
|
||||||
import "errors"
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
const (
|
)
|
||||||
minMatch = 4
|
|
||||||
hashLog = 17
|
const (
|
||||||
hashTableSize = 1 << hashLog
|
minMatch = 4
|
||||||
hashShift = (minMatch * 8) - hashLog
|
hashLog = 17
|
||||||
incompressible uint32 = 128
|
hashTableSize = 1 << hashLog
|
||||||
uninitHash = 0x88888888
|
hashShift = (minMatch * 8) - hashLog
|
||||||
|
incompressible uint32 = 128
|
||||||
// MaxInputSize is the largest buffer than can be compressed in a single block
|
uninitHash = 0x88888888
|
||||||
MaxInputSize = 0x7E000000
|
|
||||||
)
|
// MaxInputSize is the largest buffer than can be compressed in a single block
|
||||||
|
MaxInputSize = 0x7E000000
|
||||||
var (
|
)
|
||||||
// ErrTooLarge indicates the input buffer was too large
|
|
||||||
ErrTooLarge = errors.New("input too large")
|
var (
|
||||||
)
|
// ErrTooLarge indicates the input buffer was too large
|
||||||
|
ErrTooLarge = errors.New("input too large")
|
||||||
type encoder struct {
|
)
|
||||||
src []byte
|
|
||||||
dst []byte
|
type encoder struct {
|
||||||
hashTable []uint32
|
src []byte
|
||||||
pos uint32
|
dst []byte
|
||||||
anchor uint32
|
hashTable []uint32
|
||||||
dpos uint32
|
pos uint32
|
||||||
}
|
anchor uint32
|
||||||
|
dpos uint32
|
||||||
// CompressBound returns the maximum length of a lz4 block, given it's uncompressed length
|
}
|
||||||
func CompressBound(isize int) int {
|
|
||||||
if isize > MaxInputSize {
|
// CompressBound returns the maximum length of a lz4 block, given it's uncompressed length
|
||||||
return 0
|
func CompressBound(isize int) int {
|
||||||
}
|
if isize > MaxInputSize {
|
||||||
return isize + ((isize) / 255) + 16 + 4
|
return 0
|
||||||
}
|
}
|
||||||
|
return isize + ((isize) / 255) + 16 + 4
|
||||||
func (e *encoder) writeLiterals(length, mlLen, pos uint32) {
|
}
|
||||||
|
|
||||||
ln := length
|
func (e *encoder) writeLiterals(length, mlLen, pos uint32) {
|
||||||
|
|
||||||
var code byte
|
ln := length
|
||||||
if ln > runMask-1 {
|
|
||||||
code = runMask
|
var code byte
|
||||||
} else {
|
if ln > runMask-1 {
|
||||||
code = byte(ln)
|
code = runMask
|
||||||
}
|
} else {
|
||||||
|
code = byte(ln)
|
||||||
if mlLen > mlMask-1 {
|
}
|
||||||
e.dst[e.dpos] = (code << mlBits) + byte(mlMask)
|
|
||||||
} else {
|
if mlLen > mlMask-1 {
|
||||||
e.dst[e.dpos] = (code << mlBits) + byte(mlLen)
|
e.dst[e.dpos] = (code << mlBits) + byte(mlMask)
|
||||||
}
|
} else {
|
||||||
e.dpos++
|
e.dst[e.dpos] = (code << mlBits) + byte(mlLen)
|
||||||
|
}
|
||||||
if code == runMask {
|
e.dpos++
|
||||||
ln -= runMask
|
|
||||||
for ; ln > 254; ln -= 255 {
|
if code == runMask {
|
||||||
e.dst[e.dpos] = 255
|
ln -= runMask
|
||||||
e.dpos++
|
for ; ln > 254; ln -= 255 {
|
||||||
}
|
e.dst[e.dpos] = 255
|
||||||
|
e.dpos++
|
||||||
e.dst[e.dpos] = byte(ln)
|
}
|
||||||
e.dpos++
|
|
||||||
}
|
e.dst[e.dpos] = byte(ln)
|
||||||
|
e.dpos++
|
||||||
for ii := uint32(0); ii < length; ii++ {
|
}
|
||||||
e.dst[e.dpos+ii] = e.src[pos+ii]
|
|
||||||
}
|
for ii := uint32(0); ii < length; 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
|
}
|
||||||
// sub-slice of dst if it was large enough to hold the entire output.
|
|
||||||
func Encode(dst, src []byte) ([]byte, error) {
|
// 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.
|
||||||
if len(src) >= MaxInputSize {
|
func Encode(dst, src []byte) ([]byte, error) {
|
||||||
return nil, ErrTooLarge
|
|
||||||
}
|
if len(src) >= MaxInputSize {
|
||||||
|
return nil, ErrTooLarge
|
||||||
if n := CompressBound(len(src)); len(dst) < n {
|
}
|
||||||
dst = make([]byte, n)
|
|
||||||
}
|
if n := CompressBound(len(src)); len(dst) < n {
|
||||||
|
dst = make([]byte, n)
|
||||||
e := encoder{src: src, dst: dst, hashTable: make([]uint32, hashTableSize)}
|
}
|
||||||
|
|
||||||
binary.LittleEndian.PutUint32(dst, uint32(len(src)))
|
e := encoder{src: src, dst: dst, hashTable: make([]uint32, hashTableSize)}
|
||||||
e.dpos = 4
|
|
||||||
|
binary.LittleEndian.PutUint32(dst, uint32(len(src)))
|
||||||
var (
|
e.dpos = 4
|
||||||
step uint32 = 1
|
|
||||||
limit = incompressible
|
var (
|
||||||
)
|
step uint32 = 1
|
||||||
|
limit = incompressible
|
||||||
for {
|
)
|
||||||
if int(e.pos)+12 >= len(e.src) {
|
|
||||||
e.writeLiterals(uint32(len(e.src))-e.anchor, 0, e.anchor)
|
for {
|
||||||
return e.dst[:e.dpos], nil
|
if int(e.pos)+12 >= len(e.src) {
|
||||||
}
|
e.writeLiterals(uint32(len(e.src))-e.anchor, 0, e.anchor)
|
||||||
|
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])
|
}
|
||||||
|
|
||||||
hash := (sequence * 2654435761) >> hashShift
|
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])
|
||||||
ref := e.hashTable[hash] + uninitHash
|
|
||||||
e.hashTable[hash] = e.pos - uninitHash
|
hash := (sequence * 2654435761) >> hashShift
|
||||||
|
ref := e.hashTable[hash] + 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 {
|
e.hashTable[hash] = e.pos - uninitHash
|
||||||
if e.pos-e.anchor > limit {
|
|
||||||
limit <<= 1
|
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 {
|
||||||
step += 1 + (step >> 2)
|
if e.pos-e.anchor > limit {
|
||||||
}
|
limit <<= 1
|
||||||
e.pos += step
|
step += 1 + (step >> 2)
|
||||||
continue
|
}
|
||||||
}
|
e.pos += step
|
||||||
|
continue
|
||||||
if step > 1 {
|
}
|
||||||
e.hashTable[hash] = ref - uninitHash
|
|
||||||
e.pos -= step - 1
|
if step > 1 {
|
||||||
step = 1
|
e.hashTable[hash] = ref - uninitHash
|
||||||
continue
|
e.pos -= step - 1
|
||||||
}
|
step = 1
|
||||||
limit = incompressible
|
continue
|
||||||
|
}
|
||||||
ln := e.pos - e.anchor
|
limit = incompressible
|
||||||
back := e.pos - ref
|
|
||||||
|
ln := e.pos - e.anchor
|
||||||
anchor := e.anchor
|
back := e.pos - ref
|
||||||
|
|
||||||
e.pos += minMatch
|
anchor := e.anchor
|
||||||
ref += minMatch
|
|
||||||
e.anchor = e.pos
|
e.pos += minMatch
|
||||||
|
ref += minMatch
|
||||||
for int(e.pos) < len(e.src)-5 && e.src[e.pos] == e.src[ref] {
|
e.anchor = e.pos
|
||||||
e.pos++
|
|
||||||
ref++
|
for int(e.pos) < len(e.src)-5 && e.src[e.pos] == e.src[ref] {
|
||||||
}
|
e.pos++
|
||||||
|
ref++
|
||||||
mlLen := e.pos - e.anchor
|
}
|
||||||
|
|
||||||
e.writeLiterals(ln, mlLen, anchor)
|
mlLen := e.pos - e.anchor
|
||||||
e.dst[e.dpos] = uint8(back)
|
|
||||||
e.dst[e.dpos+1] = uint8(back >> 8)
|
e.writeLiterals(ln, mlLen, anchor)
|
||||||
e.dpos += 2
|
e.dst[e.dpos] = uint8(back)
|
||||||
|
e.dst[e.dpos+1] = uint8(back >> 8)
|
||||||
if mlLen > mlMask-1 {
|
e.dpos += 2
|
||||||
mlLen -= mlMask
|
|
||||||
for mlLen > 254 {
|
if mlLen > mlMask-1 {
|
||||||
mlLen -= 255
|
mlLen -= mlMask
|
||||||
|
for mlLen > 254 {
|
||||||
e.dst[e.dpos] = 255
|
mlLen -= 255
|
||||||
e.dpos++
|
|
||||||
}
|
e.dst[e.dpos] = 255
|
||||||
|
e.dpos++
|
||||||
e.dst[e.dpos] = byte(mlLen)
|
}
|
||||||
e.dpos++
|
|
||||||
}
|
e.dst[e.dpos] = byte(mlLen)
|
||||||
|
e.dpos++
|
||||||
e.anchor = e.pos
|
}
|
||||||
}
|
|
||||||
}
|
e.anchor = e.pos
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
distribute this software, either in source code form or as a compiled
|
||||||
|
binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
means.
|
||||||
|
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
of this software dedicate any and all copyright interest in the
|
||||||
|
software to the public domain. We make this dedication for the benefit
|
||||||
|
of the public at large and to the detriment of our heirs and
|
||||||
|
successors. We intend this dedication to be an overt act of
|
||||||
|
relinquishment in perpetuity of all present and future rights to this
|
||||||
|
software under copyright law.
|
||||||
|
|
||||||
|
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 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.
|
||||||
|
|
||||||
|
For more information, please refer to <http://unlicense.org>
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
du
|
||||||
|
==
|
||||||
|
|
||||||
|
Get total and available disk space on a given volume.
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
http://godoc.org/github.com/calmh/du
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
Public Domain
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/calmh/du"
|
||||||
|
)
|
||||||
|
|
||||||
|
var KB = int64(1024)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
usage, err := du.Get(os.Args[1])
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Println("Free:", usage.FreeBytes/(KB*KB), "MiB")
|
||||||
|
fmt.Println("Available:", usage.AvailBytes/(KB*KB), "MiB")
|
||||||
|
fmt.Println("Size:", usage.TotalBytes/(KB*KB), "MiB")
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package du
|
||||||
|
|
||||||
|
// Usage holds information about total and available storage on a volume.
|
||||||
|
type Usage struct {
|
||||||
|
TotalBytes int64 // Size of volume
|
||||||
|
FreeBytes int64 // Unused size
|
||||||
|
AvailBytes int64 // Available to a non-privileged user
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
// +build !windows,!netbsd,!openbsd,!solaris
|
||||||
|
|
||||||
|
package du
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get returns the Usage of a given path, or an error if usage data is
|
||||||
|
// unavailable.
|
||||||
|
func Get(path string) (Usage, error) {
|
||||||
|
var stat syscall.Statfs_t
|
||||||
|
err := syscall.Statfs(filepath.Clean(path), &stat)
|
||||||
|
if err != nil {
|
||||||
|
return Usage{}, err
|
||||||
|
}
|
||||||
|
u := Usage{
|
||||||
|
FreeBytes: int64(stat.Bfree) * int64(stat.Bsize),
|
||||||
|
TotalBytes: int64(stat.Blocks) * int64(stat.Bsize),
|
||||||
|
AvailBytes: int64(stat.Bavail) * int64(stat.Bsize),
|
||||||
|
}
|
||||||
|
return u, nil
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
// +build netbsd openbsd solaris
|
||||||
|
|
||||||
|
package du
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var ErrUnsupported = errors.New("unsupported platform")
|
||||||
|
|
||||||
|
// Get returns the Usage of a given path, or an error if usage data is
|
||||||
|
// unavailable.
|
||||||
|
func Get(path string) (Usage, error) {
|
||||||
|
return Usage{}, ErrUnsupported
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
package du
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get returns the Usage of a given path, or an error if usage data is
|
||||||
|
// unavailable.
|
||||||
|
func Get(path string) (Usage, error) {
|
||||||
|
h := syscall.MustLoadDLL("kernel32.dll")
|
||||||
|
c := h.MustFindProc("GetDiskFreeSpaceExW")
|
||||||
|
|
||||||
|
var u Usage
|
||||||
|
|
||||||
|
ret, _, err := c.Call(
|
||||||
|
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(path))),
|
||||||
|
uintptr(unsafe.Pointer(&u.FreeBytes)),
|
||||||
|
uintptr(unsafe.Pointer(&u.TotalBytes)),
|
||||||
|
uintptr(unsafe.Pointer(&u.AvailBytes)))
|
||||||
|
|
||||||
|
if ret == 0 {
|
||||||
|
return u, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return u, nil
|
||||||
|
}
|
||||||
+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 Jakob Borg and Contributors (see the CONTRIBUTORS file).
|
// 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
|
||||||
-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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+1
-1
@@ -4,7 +4,7 @@ go:
|
|||||||
|
|
||||||
install:
|
install:
|
||||||
- export PATH=$PATH:$HOME/gopath/bin
|
- export PATH=$PATH:$HOME/gopath/bin
|
||||||
- go get code.google.com/p/go.tools/cmd/cover
|
- go get golang.org/x/tools/cover
|
||||||
- go get github.com/mattn/goveralls
|
- go get github.com/mattn/goveralls
|
||||||
|
|
||||||
script:
|
script:
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
xdr
|
xdr
|
||||||
===
|
===
|
||||||
|
|
||||||
[](https://travis-ci.org/calmh/xdr)
|
[](https://circleci.com/gh/calmh/xdr)
|
||||||
[](https://coveralls.io/r/calmh/xdr?branch=master)
|
[](https://coveralls.io/r/calmh/xdr?branch=master)
|
||||||
[](http://godoc.org/github.com/calmh/xdr)
|
[](http://godoc.org/github.com/calmh/xdr)
|
||||||
[](http://opensource.org/licenses/MIT)
|
[](http://opensource.org/licenses/MIT)
|
||||||
|
|||||||
-117
@@ -1,117 +0,0 @@
|
|||||||
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
|
||||||
// is governed by an MIT-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package xdr_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/calmh/xdr"
|
|
||||||
)
|
|
||||||
|
|
||||||
type XDRBenchStruct struct {
|
|
||||||
I1 uint64
|
|
||||||
I2 uint32
|
|
||||||
I3 uint16
|
|
||||||
I4 uint8
|
|
||||||
Bs0 []byte // max:128
|
|
||||||
Bs1 []byte
|
|
||||||
S0 string // max:128
|
|
||||||
S1 string
|
|
||||||
}
|
|
||||||
|
|
||||||
var res []byte // no to be optimized away
|
|
||||||
var s = XDRBenchStruct{
|
|
||||||
I1: 42,
|
|
||||||
I2: 43,
|
|
||||||
I3: 44,
|
|
||||||
I4: 45,
|
|
||||||
Bs0: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18},
|
|
||||||
Bs1: []byte{11, 12, 13, 14, 15, 16, 17, 18, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
|
||||||
S0: "Hello World! String one.",
|
|
||||||
S1: "Hello World! String two.",
|
|
||||||
}
|
|
||||||
var e []byte
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
e, _ = s.MarshalXDR()
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkThisMarshal(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
res, _ = s.MarshalXDR()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkThisUnmarshal(b *testing.B) {
|
|
||||||
var t XDRBenchStruct
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
err := t.UnmarshalXDR(e)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkThisEncode(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_, err := s.EncodeXDR(ioutil.Discard)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkThisEncoder(b *testing.B) {
|
|
||||||
w := xdr.NewWriter(ioutil.Discard)
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_, err := s.encodeXDR(w)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type repeatReader struct {
|
|
||||||
data []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *repeatReader) Read(bs []byte) (n int, err error) {
|
|
||||||
if len(bs) > len(r.data) {
|
|
||||||
err = io.EOF
|
|
||||||
}
|
|
||||||
n = copy(bs, r.data)
|
|
||||||
r.data = r.data[n:]
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *repeatReader) Reset(bs []byte) {
|
|
||||||
r.data = bs
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkThisDecode(b *testing.B) {
|
|
||||||
rr := &repeatReader{e}
|
|
||||||
var t XDRBenchStruct
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
err := t.DecodeXDR(rr)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
rr.Reset(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkThisDecoder(b *testing.B) {
|
|
||||||
rr := &repeatReader{e}
|
|
||||||
r := xdr.NewReader(rr)
|
|
||||||
var t XDRBenchStruct
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
err := t.decodeXDR(r)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
rr.Reset(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-199
@@ -1,199 +0,0 @@
|
|||||||
// ************************************************************
|
|
||||||
// This file is automatically generated by genxdr. Do not edit.
|
|
||||||
// ************************************************************
|
|
||||||
|
|
||||||
package xdr_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/calmh/xdr"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
XDRBenchStruct 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
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| |
|
|
||||||
+ I1 (64 bits) +
|
|
||||||
| |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| I2 |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| 0x0000 | I3 |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| uint8 |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| Length of Bs0 |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
/ /
|
|
||||||
\ Bs0 (variable length) \
|
|
||||||
/ /
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| Length of Bs1 |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
/ /
|
|
||||||
\ Bs1 (variable length) \
|
|
||||||
/ /
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| Length of S0 |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
/ /
|
|
||||||
\ S0 (variable length) \
|
|
||||||
/ /
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| Length of S1 |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
/ /
|
|
||||||
\ S1 (variable length) \
|
|
||||||
/ /
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
|
|
||||||
|
|
||||||
struct XDRBenchStruct {
|
|
||||||
unsigned hyper I1;
|
|
||||||
unsigned int I2;
|
|
||||||
unsigned int I3;
|
|
||||||
uint8 I4;
|
|
||||||
opaque Bs0<128>;
|
|
||||||
opaque Bs1<>;
|
|
||||||
string S0<128>;
|
|
||||||
string S1<>;
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
func (o XDRBenchStruct) EncodeXDR(w io.Writer) (int, error) {
|
|
||||||
var xw = xdr.NewWriter(w)
|
|
||||||
return o.encodeXDR(xw)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o XDRBenchStruct) MarshalXDR() ([]byte, error) {
|
|
||||||
return o.AppendXDR(make([]byte, 0, 128))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o XDRBenchStruct) MustMarshalXDR() []byte {
|
|
||||||
bs, err := o.MarshalXDR()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return bs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o XDRBenchStruct) 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 XDRBenchStruct) encodeXDR(xw *xdr.Writer) (int, error) {
|
|
||||||
xw.WriteUint64(o.I1)
|
|
||||||
xw.WriteUint32(o.I2)
|
|
||||||
xw.WriteUint16(o.I3)
|
|
||||||
xw.WriteUint8(o.I4)
|
|
||||||
if l := len(o.Bs0); l > 128 {
|
|
||||||
return xw.Tot(), xdr.ElementSizeExceeded("Bs0", l, 128)
|
|
||||||
}
|
|
||||||
xw.WriteBytes(o.Bs0)
|
|
||||||
xw.WriteBytes(o.Bs1)
|
|
||||||
if l := len(o.S0); l > 128 {
|
|
||||||
return xw.Tot(), xdr.ElementSizeExceeded("S0", l, 128)
|
|
||||||
}
|
|
||||||
xw.WriteString(o.S0)
|
|
||||||
xw.WriteString(o.S1)
|
|
||||||
return xw.Tot(), xw.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *XDRBenchStruct) DecodeXDR(r io.Reader) error {
|
|
||||||
xr := xdr.NewReader(r)
|
|
||||||
return o.decodeXDR(xr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *XDRBenchStruct) UnmarshalXDR(bs []byte) error {
|
|
||||||
var br = bytes.NewReader(bs)
|
|
||||||
var xr = xdr.NewReader(br)
|
|
||||||
return o.decodeXDR(xr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *XDRBenchStruct) decodeXDR(xr *xdr.Reader) error {
|
|
||||||
o.I1 = xr.ReadUint64()
|
|
||||||
o.I2 = xr.ReadUint32()
|
|
||||||
o.I3 = xr.ReadUint16()
|
|
||||||
o.I4 = xr.ReadUint8()
|
|
||||||
o.Bs0 = xr.ReadBytesMax(128)
|
|
||||||
o.Bs1 = xr.ReadBytes()
|
|
||||||
o.S0 = xr.ReadStringMax(128)
|
|
||||||
o.S1 = xr.ReadString()
|
|
||||||
return xr.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
repeatReader 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 data |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
/ /
|
|
||||||
\ data (variable length) \
|
|
||||||
/ /
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
|
|
||||||
|
|
||||||
struct repeatReader {
|
|
||||||
opaque data<>;
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
func (o repeatReader) EncodeXDR(w io.Writer) (int, error) {
|
|
||||||
var xw = xdr.NewWriter(w)
|
|
||||||
return o.encodeXDR(xw)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o repeatReader) MarshalXDR() ([]byte, error) {
|
|
||||||
return o.AppendXDR(make([]byte, 0, 128))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o repeatReader) MustMarshalXDR() []byte {
|
|
||||||
bs, err := o.MarshalXDR()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return bs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o repeatReader) 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 repeatReader) encodeXDR(xw *xdr.Writer) (int, error) {
|
|
||||||
xw.WriteBytes(o.data)
|
|
||||||
return xw.Tot(), xw.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *repeatReader) DecodeXDR(r io.Reader) error {
|
|
||||||
xr := xdr.NewReader(r)
|
|
||||||
return o.decodeXDR(xr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *repeatReader) UnmarshalXDR(bs []byte) error {
|
|
||||||
var br = bytes.NewReader(bs)
|
|
||||||
var xr = xdr.NewReader(br)
|
|
||||||
return o.decodeXDR(xr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *repeatReader) decodeXDR(xr *xdr.Reader) error {
|
|
||||||
o.data = xr.ReadBytes()
|
|
||||||
return xr.Error()
|
|
||||||
}
|
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
dependencies:
|
||||||
|
post:
|
||||||
|
- ./generate.sh
|
||||||
+165
-77
@@ -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"
|
||||||
@@ -26,6 +28,7 @@ type fieldInfo struct {
|
|||||||
Encoder string // the encoder name, i.e. "Uint64" for Read/WriteUint64
|
Encoder string // the encoder name, i.e. "Uint64" for Read/WriteUint64
|
||||||
Convert string // what to convert to when encoding, i.e. "uint64"
|
Convert string // what to convert to when encoding, i.e. "uint64"
|
||||||
Max int // max size for slices and strings
|
Max int // max size for slices and strings
|
||||||
|
Submax int // max size for strings inside slices
|
||||||
}
|
}
|
||||||
|
|
||||||
type structInfo struct {
|
type structInfo struct {
|
||||||
@@ -50,7 +53,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 +71,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 +88,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 +106,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 +119,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 +140,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}})
|
||||||
@@ -151,9 +157,13 @@ func (o *{{.TypeName}}) decodeXDR(xr *xdr.Reader) error {
|
|||||||
{{if ne $fieldInfo.Convert ""}}
|
{{if ne $fieldInfo.Convert ""}}
|
||||||
o.{{$fieldInfo.Name}}[i] = {{$fieldInfo.FieldType}}(xr.Read{{$fieldInfo.Encoder}}())
|
o.{{$fieldInfo.Name}}[i] = {{$fieldInfo.FieldType}}(xr.Read{{$fieldInfo.Encoder}}())
|
||||||
{{else if $fieldInfo.IsBasic}}
|
{{else if $fieldInfo.IsBasic}}
|
||||||
o.{{$fieldInfo.Name}}[i] = xr.Read{{$fieldInfo.Encoder}}()
|
{{if ge $fieldInfo.Submax 1}}
|
||||||
|
o.{{$fieldInfo.Name}}[i] = xr.Read{{$fieldInfo.Encoder}}Max({{$fieldInfo.Submax}})
|
||||||
|
{{else}}
|
||||||
|
o.{{$fieldInfo.Name}}[i] = xr.Read{{$fieldInfo.Encoder}}()
|
||||||
|
{{end}}
|
||||||
{{else}}
|
{{else}}
|
||||||
(&o.{{$fieldInfo.Name}}[i]).decodeXDR(xr)
|
(&o.{{$fieldInfo.Name}}[i]).DecodeXDRFrom(xr)
|
||||||
{{end}}
|
{{end}}
|
||||||
}
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -161,7 +171,40 @@ func (o *{{.TypeName}}) decodeXDR(xr *xdr.Reader) error {
|
|||||||
return xr.Error()
|
return xr.Error()
|
||||||
}`))
|
}`))
|
||||||
|
|
||||||
var maxRe = regexp.MustCompile(`\Wmax:(\d+)`)
|
var emptyTypeTpl = template.Must(template.New("encoder").Parse(`
|
||||||
|
func (o {{.TypeName}}) EncodeXDR(w io.Writer) (int, error) {
|
||||||
|
return 0, nil
|
||||||
|
}//+n
|
||||||
|
|
||||||
|
func (o {{.TypeName}}) MarshalXDR() ([]byte, error) {
|
||||||
|
return nil, nil
|
||||||
|
}//+n
|
||||||
|
|
||||||
|
func (o {{.TypeName}}) MustMarshalXDR() []byte {
|
||||||
|
return nil
|
||||||
|
}//+n
|
||||||
|
|
||||||
|
func (o {{.TypeName}}) AppendXDR(bs []byte) ([]byte, error) {
|
||||||
|
return bs, nil
|
||||||
|
}//+n
|
||||||
|
|
||||||
|
func (o {{.TypeName}}) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||||
|
return xw.Tot(), xw.Error()
|
||||||
|
}//+n
|
||||||
|
|
||||||
|
func (o *{{.TypeName}}) DecodeXDR(r io.Reader) error {
|
||||||
|
return nil
|
||||||
|
}//+n
|
||||||
|
|
||||||
|
func (o *{{.TypeName}}) UnmarshalXDR(bs []byte) error {
|
||||||
|
return nil
|
||||||
|
}//+n
|
||||||
|
|
||||||
|
func (o *{{.TypeName}}) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||||
|
return xr.Error()
|
||||||
|
}`))
|
||||||
|
|
||||||
|
var maxRe = regexp.MustCompile(`(?:\Wmax:)(\d+)(?:\s*,\s*(\d+))?`)
|
||||||
|
|
||||||
type typeSet struct {
|
type typeSet struct {
|
||||||
Type string
|
Type string
|
||||||
@@ -193,11 +236,15 @@ func handleStruct(t *ast.StructType) []fieldInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn := sf.Names[0].Name
|
fn := sf.Names[0].Name
|
||||||
var max = 0
|
var max1, max2 int
|
||||||
if sf.Comment != nil {
|
if sf.Comment != nil {
|
||||||
c := sf.Comment.List[0].Text
|
c := sf.Comment.List[0].Text
|
||||||
if m := maxRe.FindStringSubmatch(c); m != nil {
|
m := maxRe.FindStringSubmatch(c)
|
||||||
max, _ = strconv.Atoi(m[1])
|
if len(m) >= 2 {
|
||||||
|
max1, _ = strconv.Atoi(m[1])
|
||||||
|
}
|
||||||
|
if len(m) >= 3 {
|
||||||
|
max2, _ = strconv.Atoi(m[2])
|
||||||
}
|
}
|
||||||
if strings.Contains(c, "noencode") {
|
if strings.Contains(c, "noencode") {
|
||||||
continue
|
continue
|
||||||
@@ -215,14 +262,16 @@ func handleStruct(t *ast.StructType) []fieldInfo {
|
|||||||
FieldType: tn,
|
FieldType: tn,
|
||||||
Encoder: enc.Encoder,
|
Encoder: enc.Encoder,
|
||||||
Convert: enc.Type,
|
Convert: enc.Type,
|
||||||
Max: max,
|
Max: max1,
|
||||||
|
Submax: max2,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
f = fieldInfo{
|
f = fieldInfo{
|
||||||
Name: fn,
|
Name: fn,
|
||||||
IsBasic: false,
|
IsBasic: false,
|
||||||
FieldType: tn,
|
FieldType: tn,
|
||||||
Max: max,
|
Max: max1,
|
||||||
|
Submax: max2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,7 +289,8 @@ func handleStruct(t *ast.StructType) []fieldInfo {
|
|||||||
FieldType: tn,
|
FieldType: tn,
|
||||||
Encoder: enc.Encoder,
|
Encoder: enc.Encoder,
|
||||||
Convert: enc.Type,
|
Convert: enc.Type,
|
||||||
Max: max,
|
Max: max1,
|
||||||
|
Submax: max2,
|
||||||
}
|
}
|
||||||
} else if enc, ok := xdrEncoders[tn]; ok {
|
} else if enc, ok := xdrEncoders[tn]; ok {
|
||||||
f = fieldInfo{
|
f = fieldInfo{
|
||||||
@@ -250,17 +300,26 @@ func handleStruct(t *ast.StructType) []fieldInfo {
|
|||||||
FieldType: tn,
|
FieldType: tn,
|
||||||
Encoder: enc.Encoder,
|
Encoder: enc.Encoder,
|
||||||
Convert: enc.Type,
|
Convert: enc.Type,
|
||||||
Max: max,
|
Max: max1,
|
||||||
|
Submax: max2,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
f = fieldInfo{
|
f = fieldInfo{
|
||||||
Name: fn,
|
Name: fn,
|
||||||
IsBasic: false,
|
|
||||||
IsSlice: true,
|
IsSlice: true,
|
||||||
FieldType: tn,
|
FieldType: tn,
|
||||||
Max: max,
|
Max: max1,
|
||||||
|
Submax: max2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
f = fieldInfo{
|
||||||
|
Name: fn,
|
||||||
|
FieldType: ft.Sel.Name,
|
||||||
|
Max: max1,
|
||||||
|
Submax: max2,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fs = append(fs, f)
|
fs = append(fs, f)
|
||||||
@@ -269,12 +328,19 @@ 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
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
err := encodeTpl.Execute(&buf, map[string]interface{}{"TypeName": name, "Fields": fs})
|
var err error
|
||||||
|
if len(fs) == 0 {
|
||||||
|
// This is an empty type. We can create a quite simple codec for it.
|
||||||
|
err = emptyTypeTpl.Execute(&buf, map[string]interface{}{"TypeName": name})
|
||||||
|
} else {
|
||||||
|
// Generate with the default template.
|
||||||
|
err = encodeTpl.Execute(&buf, map[string]interface{}{"TypeName": name, "Fields": fs})
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -286,7 +352,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 +361,79 @@ 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.Println(" 0 1 2 3")
|
if len(fs) == 0 {
|
||||||
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, "(contains no fields)")
|
||||||
|
fmt.Fprintln(output)
|
||||||
|
fmt.Fprintln(output)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(output)
|
||||||
|
fmt.Fprintln(output, " 0 1 2 3")
|
||||||
|
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 +448,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 +496,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
-79
@@ -1,79 +0,0 @@
|
|||||||
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
|
||||||
// is governed by an MIT-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package xdr_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"math/rand"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
"testing/quick"
|
|
||||||
|
|
||||||
"github.com/calmh/xdr"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Contains all supported types
|
|
||||||
type TestStruct struct {
|
|
||||||
I int
|
|
||||||
I8 int8
|
|
||||||
UI8 uint8
|
|
||||||
I16 int16
|
|
||||||
UI16 uint16
|
|
||||||
I32 int32
|
|
||||||
UI32 uint32
|
|
||||||
I64 int64
|
|
||||||
UI64 uint64
|
|
||||||
BS []byte // max:1024
|
|
||||||
S string // max:1024
|
|
||||||
C Opaque
|
|
||||||
SS []string // max:1024
|
|
||||||
}
|
|
||||||
|
|
||||||
type Opaque [32]byte
|
|
||||||
|
|
||||||
func (u *Opaque) encodeXDR(w *xdr.Writer) (int, error) {
|
|
||||||
return w.WriteRaw(u[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *Opaque) decodeXDR(r *xdr.Reader) (int, error) {
|
|
||||||
return r.ReadRaw(u[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Opaque) Generate(rand *rand.Rand, size int) reflect.Value {
|
|
||||||
var u Opaque
|
|
||||||
for i := range u[:] {
|
|
||||||
u[i] = byte(rand.Int())
|
|
||||||
}
|
|
||||||
return reflect.ValueOf(u)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncDec(t *testing.T) {
|
|
||||||
fn := func(t0 TestStruct) bool {
|
|
||||||
bs, err := t0.MarshalXDR()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
var t1 TestStruct
|
|
||||||
err = t1.UnmarshalXDR(bs)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not comparing with DeepEqual since we'll unmarshal nil slices as empty
|
|
||||||
if t0.I != t1.I ||
|
|
||||||
t0.I16 != t1.I16 || t0.UI16 != t1.UI16 ||
|
|
||||||
t0.I32 != t1.I32 || t0.UI32 != t1.UI32 ||
|
|
||||||
t0.I64 != t1.I64 || t0.UI64 != t1.UI64 ||
|
|
||||||
bytes.Compare(t0.BS, t1.BS) != 0 ||
|
|
||||||
t0.S != t1.S || t0.C != t1.C {
|
|
||||||
t.Logf("%#v", t0)
|
|
||||||
t.Logf("%#v", t1)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if err := quick.Check(fn, nil); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-174
@@ -1,174 +0,0 @@
|
|||||||
// ************************************************************
|
|
||||||
// This file is automatically generated by genxdr. Do not edit.
|
|
||||||
// ************************************************************
|
|
||||||
|
|
||||||
package xdr_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/calmh/xdr"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
TestStruct 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
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| int |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| int8 |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| uint8 |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| int16 |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| 0x0000 | UI16 |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| int32 |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| UI32 |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| |
|
|
||||||
+ I64 (64 bits) +
|
|
||||||
| |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| |
|
|
||||||
+ UI64 (64 bits) +
|
|
||||||
| |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| Length of BS |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
/ /
|
|
||||||
\ BS (variable length) \
|
|
||||||
/ /
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| Length of S |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
/ /
|
|
||||||
\ S (variable length) \
|
|
||||||
/ /
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| Opaque |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| Number of SS |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| Length of SS |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
/ /
|
|
||||||
\ SS (variable length) \
|
|
||||||
/ /
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
|
|
||||||
|
|
||||||
struct TestStruct {
|
|
||||||
int I;
|
|
||||||
int8 I8;
|
|
||||||
uint8 UI8;
|
|
||||||
int16 I16;
|
|
||||||
unsigned int UI16;
|
|
||||||
int32 I32;
|
|
||||||
unsigned int UI32;
|
|
||||||
hyper I64;
|
|
||||||
unsigned hyper UI64;
|
|
||||||
opaque BS<1024>;
|
|
||||||
string S<1024>;
|
|
||||||
Opaque C;
|
|
||||||
string SS<1024>;
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
func (o TestStruct) EncodeXDR(w io.Writer) (int, error) {
|
|
||||||
var xw = xdr.NewWriter(w)
|
|
||||||
return o.encodeXDR(xw)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o TestStruct) MarshalXDR() ([]byte, error) {
|
|
||||||
return o.AppendXDR(make([]byte, 0, 128))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o TestStruct) MustMarshalXDR() []byte {
|
|
||||||
bs, err := o.MarshalXDR()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return bs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o TestStruct) 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 TestStruct) encodeXDR(xw *xdr.Writer) (int, error) {
|
|
||||||
xw.WriteUint64(uint64(o.I))
|
|
||||||
xw.WriteUint8(uint8(o.I8))
|
|
||||||
xw.WriteUint8(o.UI8)
|
|
||||||
xw.WriteUint16(uint16(o.I16))
|
|
||||||
xw.WriteUint16(o.UI16)
|
|
||||||
xw.WriteUint32(uint32(o.I32))
|
|
||||||
xw.WriteUint32(o.UI32)
|
|
||||||
xw.WriteUint64(uint64(o.I64))
|
|
||||||
xw.WriteUint64(o.UI64)
|
|
||||||
if l := len(o.BS); l > 1024 {
|
|
||||||
return xw.Tot(), xdr.ElementSizeExceeded("BS", l, 1024)
|
|
||||||
}
|
|
||||||
xw.WriteBytes(o.BS)
|
|
||||||
if l := len(o.S); l > 1024 {
|
|
||||||
return xw.Tot(), xdr.ElementSizeExceeded("S", l, 1024)
|
|
||||||
}
|
|
||||||
xw.WriteString(o.S)
|
|
||||||
_, err := o.C.encodeXDR(xw)
|
|
||||||
if err != nil {
|
|
||||||
return xw.Tot(), err
|
|
||||||
}
|
|
||||||
if l := len(o.SS); l > 1024 {
|
|
||||||
return xw.Tot(), xdr.ElementSizeExceeded("SS", l, 1024)
|
|
||||||
}
|
|
||||||
xw.WriteUint32(uint32(len(o.SS)))
|
|
||||||
for i := range o.SS {
|
|
||||||
xw.WriteString(o.SS[i])
|
|
||||||
}
|
|
||||||
return xw.Tot(), xw.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *TestStruct) DecodeXDR(r io.Reader) error {
|
|
||||||
xr := xdr.NewReader(r)
|
|
||||||
return o.decodeXDR(xr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *TestStruct) UnmarshalXDR(bs []byte) error {
|
|
||||||
var br = bytes.NewReader(bs)
|
|
||||||
var xr = xdr.NewReader(br)
|
|
||||||
return o.decodeXDR(xr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *TestStruct) decodeXDR(xr *xdr.Reader) error {
|
|
||||||
o.I = int(xr.ReadUint64())
|
|
||||||
o.I8 = int8(xr.ReadUint8())
|
|
||||||
o.UI8 = xr.ReadUint8()
|
|
||||||
o.I16 = int16(xr.ReadUint16())
|
|
||||||
o.UI16 = xr.ReadUint16()
|
|
||||||
o.I32 = int32(xr.ReadUint32())
|
|
||||||
o.UI32 = xr.ReadUint32()
|
|
||||||
o.I64 = int64(xr.ReadUint64())
|
|
||||||
o.UI64 = xr.ReadUint64()
|
|
||||||
o.BS = xr.ReadBytesMax(1024)
|
|
||||||
o.S = xr.ReadStringMax(1024)
|
|
||||||
(&o.C).decodeXDR(xr)
|
|
||||||
_SSSize := int(xr.ReadUint32())
|
|
||||||
if _SSSize > 1024 {
|
|
||||||
return xdr.ElementSizeExceeded("SS", _SSSize, 1024)
|
|
||||||
}
|
|
||||||
o.SS = make([]string, _SSSize)
|
|
||||||
for i := range o.SS {
|
|
||||||
o.SS[i] = xr.ReadString()
|
|
||||||
}
|
|
||||||
return xr.Error()
|
|
||||||
}
|
|
||||||
+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
|
||||||
|
|||||||
-44
@@ -1,44 +0,0 @@
|
|||||||
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
|
||||||
// is governed by an MIT-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build refl
|
|
||||||
|
|
||||||
package xdr_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
refl "github.com/davecgh/go-xdr/xdr"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCompareMarshals(t *testing.T) {
|
|
||||||
e0 := s.MarshalXDR()
|
|
||||||
e1, err := refl.Marshal(s)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if bytes.Compare(e0, e1) != 0 {
|
|
||||||
t.Fatalf("Encoding mismatch;\n\t%x (this)\n\t%x (refl)", e0, e1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkReflMarshal(b *testing.B) {
|
|
||||||
var err error
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
res, err = refl.Marshal(s)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkReflUnmarshal(b *testing.B) {
|
|
||||||
var t XDRBenchStruct
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_, err := refl.Unmarshal(e, &t)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-93
@@ -1,93 +0,0 @@
|
|||||||
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
|
||||||
// is governed by an MIT-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package xdr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"testing/quick"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBytesNil(t *testing.T) {
|
|
||||||
fn := func(bs []byte) bool {
|
|
||||||
var b = new(bytes.Buffer)
|
|
||||||
var w = NewWriter(b)
|
|
||||||
var r = NewReader(b)
|
|
||||||
w.WriteBytes(bs)
|
|
||||||
w.WriteBytes(bs)
|
|
||||||
r.ReadBytes()
|
|
||||||
res := r.ReadBytes()
|
|
||||||
return bytes.Compare(bs, res) == 0
|
|
||||||
}
|
|
||||||
if err := quick.Check(fn, nil); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBytesGiven(t *testing.T) {
|
|
||||||
fn := func(bs []byte) bool {
|
|
||||||
var b = new(bytes.Buffer)
|
|
||||||
var w = NewWriter(b)
|
|
||||||
var r = NewReader(b)
|
|
||||||
w.WriteBytes(bs)
|
|
||||||
w.WriteBytes(bs)
|
|
||||||
res := make([]byte, 12)
|
|
||||||
res = r.ReadBytesInto(res)
|
|
||||||
res = r.ReadBytesInto(res)
|
|
||||||
return bytes.Compare(bs, res) == 0
|
|
||||||
}
|
|
||||||
if err := quick.Check(fn, nil); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReadBytesMaxInto(t *testing.T) {
|
|
||||||
var max = 64
|
|
||||||
for tot := 32; tot < 128; tot++ {
|
|
||||||
for diff := -32; diff <= 32; diff++ {
|
|
||||||
var b = new(bytes.Buffer)
|
|
||||||
var r = NewReader(b)
|
|
||||||
var w = NewWriter(b)
|
|
||||||
|
|
||||||
var toWrite = make([]byte, tot)
|
|
||||||
w.WriteBytes(toWrite)
|
|
||||||
|
|
||||||
var buf = make([]byte, tot+diff)
|
|
||||||
var bs = r.ReadBytesMaxInto(max, buf)
|
|
||||||
|
|
||||||
if tot <= max {
|
|
||||||
if read := len(bs); read != tot {
|
|
||||||
t.Errorf("Incorrect read bytes, wrote=%d, buf=%d, max=%d, read=%d", tot, tot+diff, max, read)
|
|
||||||
}
|
|
||||||
} else if !strings.Contains(r.err.Error(), "exceeds size") {
|
|
||||||
t.Errorf("Unexpected non-ErrElementSizeExceeded error for wrote=%d, max=%d: %v", tot, max, r.err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReadStringMax(t *testing.T) {
|
|
||||||
for tot := 42; tot < 72; tot++ {
|
|
||||||
for max := 0; max < 128; max++ {
|
|
||||||
var b = new(bytes.Buffer)
|
|
||||||
var r = NewReader(b)
|
|
||||||
var w = NewWriter(b)
|
|
||||||
|
|
||||||
var toWrite = make([]byte, tot)
|
|
||||||
w.WriteBytes(toWrite)
|
|
||||||
|
|
||||||
var str = r.ReadStringMax(max)
|
|
||||||
var read = len(str)
|
|
||||||
|
|
||||||
if max == 0 || tot <= max {
|
|
||||||
if read != tot {
|
|
||||||
t.Errorf("Incorrect read bytes, wrote=%d, max=%d, read=%d", tot, max, read)
|
|
||||||
}
|
|
||||||
} else if !strings.Contains(r.err.Error(), "exceeds size") {
|
|
||||||
t.Errorf("Unexpected non-ErrElementSizeExceeded error for wrote=%d, max=%d, read=%d: %v", tot, max, read, r.err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
# This is the official list of Snappy-Go authors for copyright purposes.
|
||||||
|
# This file is distinct from the CONTRIBUTORS files.
|
||||||
|
# See the latter for an explanation.
|
||||||
|
|
||||||
|
# Names should be added to this file as
|
||||||
|
# Name or Organization <email address>
|
||||||
|
# The email address is not required for organizations.
|
||||||
|
|
||||||
|
# Please keep the list sorted.
|
||||||
|
|
||||||
|
Damian Gryski <dgryski@gmail.com>
|
||||||
|
Google Inc.
|
||||||
|
Jan Mercl <0xjnml@gmail.com>
|
||||||
|
Sebastien Binet <seb.binet@gmail.com>
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
# This is the official list of people who can contribute
|
||||||
|
# (and typically have contributed) code to the Snappy-Go repository.
|
||||||
|
# The AUTHORS file lists the copyright holders; this file
|
||||||
|
# lists people. For example, Google employees are listed here
|
||||||
|
# but not in AUTHORS, because Google holds the copyright.
|
||||||
|
#
|
||||||
|
# The submission process automatically checks to make sure
|
||||||
|
# that people submitting code are listed in this file (by email address).
|
||||||
|
#
|
||||||
|
# Names should be added to this file only after verifying that
|
||||||
|
# the individual or the individual's organization has agreed to
|
||||||
|
# the appropriate Contributor License Agreement, found here:
|
||||||
|
#
|
||||||
|
# http://code.google.com/legal/individual-cla-v1.0.html
|
||||||
|
# http://code.google.com/legal/corporate-cla-v1.0.html
|
||||||
|
#
|
||||||
|
# The agreement for individuals can be filled out on the web.
|
||||||
|
#
|
||||||
|
# When adding J Random Contributor's name to this file,
|
||||||
|
# either J's name or J's organization's name should be
|
||||||
|
# added to the AUTHORS file, depending on whether the
|
||||||
|
# individual or corporate CLA was used.
|
||||||
|
|
||||||
|
# Names should be added to this file like so:
|
||||||
|
# Name <email address>
|
||||||
|
|
||||||
|
# Please keep the list sorted.
|
||||||
|
|
||||||
|
Damian Gryski <dgryski@gmail.com>
|
||||||
|
Jan Mercl <0xjnml@gmail.com>
|
||||||
|
Kai Backman <kaib@golang.org>
|
||||||
|
Marc-Antoine Ruel <maruel@chromium.org>
|
||||||
|
Nigel Tao <nigeltao@golang.org>
|
||||||
|
Rob Pike <r@golang.org>
|
||||||
|
Russ Cox <rsc@golang.org>
|
||||||
|
Sebastien Binet <seb.binet@gmail.com>
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) 2011 The Snappy-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.
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
The Snappy compression format in the Go programming language.
|
||||||
|
|
||||||
|
To download and install from source:
|
||||||
|
$ go get github.com/golang/snappy
|
||||||
|
|
||||||
|
Unless otherwise noted, the Snappy-Go source files are distributed
|
||||||
|
under the BSD-style license found in the LICENSE file.
|
||||||
+294
@@ -0,0 +1,294 @@
|
|||||||
|
// Copyright 2011 The Snappy-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 snappy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrCorrupt reports that the input is invalid.
|
||||||
|
ErrCorrupt = errors.New("snappy: corrupt input")
|
||||||
|
// ErrTooLarge reports that the uncompressed length is too large.
|
||||||
|
ErrTooLarge = errors.New("snappy: decoded block is too large")
|
||||||
|
// ErrUnsupported reports that the input isn't supported.
|
||||||
|
ErrUnsupported = errors.New("snappy: unsupported input")
|
||||||
|
)
|
||||||
|
|
||||||
|
// DecodedLen returns the length of the decoded block.
|
||||||
|
func DecodedLen(src []byte) (int, error) {
|
||||||
|
v, _, err := decodedLen(src)
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodedLen returns the length of the decoded block and the number of bytes
|
||||||
|
// that the length header occupied.
|
||||||
|
func decodedLen(src []byte) (blockLen, headerLen int, err error) {
|
||||||
|
v, n := binary.Uvarint(src)
|
||||||
|
if n <= 0 || v > 0xffffffff {
|
||||||
|
return 0, 0, ErrCorrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
const wordSize = 32 << (^uint(0) >> 32 & 1)
|
||||||
|
if wordSize == 32 && v > 0x7fffffff {
|
||||||
|
return 0, 0, ErrTooLarge
|
||||||
|
}
|
||||||
|
return int(v), n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode returns the decoded form of src. The returned slice may be a sub-
|
||||||
|
// slice of dst if dst was large enough to hold the entire decoded block.
|
||||||
|
// Otherwise, a newly allocated slice will be returned.
|
||||||
|
// It is valid to pass a nil dst.
|
||||||
|
func Decode(dst, src []byte) ([]byte, error) {
|
||||||
|
dLen, s, err := decodedLen(src)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(dst) < dLen {
|
||||||
|
dst = make([]byte, dLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
var d, offset, length int
|
||||||
|
for s < len(src) {
|
||||||
|
switch src[s] & 0x03 {
|
||||||
|
case tagLiteral:
|
||||||
|
x := uint(src[s] >> 2)
|
||||||
|
switch {
|
||||||
|
case x < 60:
|
||||||
|
s++
|
||||||
|
case x == 60:
|
||||||
|
s += 2
|
||||||
|
if s > len(src) {
|
||||||
|
return nil, ErrCorrupt
|
||||||
|
}
|
||||||
|
x = uint(src[s-1])
|
||||||
|
case x == 61:
|
||||||
|
s += 3
|
||||||
|
if s > len(src) {
|
||||||
|
return nil, ErrCorrupt
|
||||||
|
}
|
||||||
|
x = uint(src[s-2]) | uint(src[s-1])<<8
|
||||||
|
case x == 62:
|
||||||
|
s += 4
|
||||||
|
if s > len(src) {
|
||||||
|
return nil, ErrCorrupt
|
||||||
|
}
|
||||||
|
x = uint(src[s-3]) | uint(src[s-2])<<8 | uint(src[s-1])<<16
|
||||||
|
case x == 63:
|
||||||
|
s += 5
|
||||||
|
if s > len(src) {
|
||||||
|
return nil, ErrCorrupt
|
||||||
|
}
|
||||||
|
x = uint(src[s-4]) | uint(src[s-3])<<8 | uint(src[s-2])<<16 | uint(src[s-1])<<24
|
||||||
|
}
|
||||||
|
length = int(x + 1)
|
||||||
|
if length <= 0 {
|
||||||
|
return nil, errors.New("snappy: unsupported literal length")
|
||||||
|
}
|
||||||
|
if length > len(dst)-d || length > len(src)-s {
|
||||||
|
return nil, ErrCorrupt
|
||||||
|
}
|
||||||
|
copy(dst[d:], src[s:s+length])
|
||||||
|
d += length
|
||||||
|
s += length
|
||||||
|
continue
|
||||||
|
|
||||||
|
case tagCopy1:
|
||||||
|
s += 2
|
||||||
|
if s > len(src) {
|
||||||
|
return nil, ErrCorrupt
|
||||||
|
}
|
||||||
|
length = 4 + int(src[s-2])>>2&0x7
|
||||||
|
offset = int(src[s-2])&0xe0<<3 | int(src[s-1])
|
||||||
|
|
||||||
|
case tagCopy2:
|
||||||
|
s += 3
|
||||||
|
if s > len(src) {
|
||||||
|
return nil, ErrCorrupt
|
||||||
|
}
|
||||||
|
length = 1 + int(src[s-3])>>2
|
||||||
|
offset = int(src[s-2]) | int(src[s-1])<<8
|
||||||
|
|
||||||
|
case tagCopy4:
|
||||||
|
return nil, errors.New("snappy: unsupported COPY_4 tag")
|
||||||
|
}
|
||||||
|
|
||||||
|
end := d + length
|
||||||
|
if offset > d || end > len(dst) {
|
||||||
|
return nil, ErrCorrupt
|
||||||
|
}
|
||||||
|
for ; d < end; d++ {
|
||||||
|
dst[d] = dst[d-offset]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if d != dLen {
|
||||||
|
return nil, ErrCorrupt
|
||||||
|
}
|
||||||
|
return dst[:d], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewReader returns a new Reader that decompresses from r, using the framing
|
||||||
|
// format described at
|
||||||
|
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||||
|
func NewReader(r io.Reader) *Reader {
|
||||||
|
return &Reader{
|
||||||
|
r: r,
|
||||||
|
decoded: make([]byte, maxUncompressedChunkLen),
|
||||||
|
buf: make([]byte, MaxEncodedLen(maxUncompressedChunkLen)+checksumSize),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reader is an io.Reader than can read Snappy-compressed bytes.
|
||||||
|
type Reader struct {
|
||||||
|
r io.Reader
|
||||||
|
err error
|
||||||
|
decoded []byte
|
||||||
|
buf []byte
|
||||||
|
// decoded[i:j] contains decoded bytes that have not yet been passed on.
|
||||||
|
i, j int
|
||||||
|
readHeader bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset discards any buffered data, resets all state, and switches the Snappy
|
||||||
|
// reader to read from r. This permits reusing a Reader rather than allocating
|
||||||
|
// a new one.
|
||||||
|
func (r *Reader) Reset(reader io.Reader) {
|
||||||
|
r.r = reader
|
||||||
|
r.err = nil
|
||||||
|
r.i = 0
|
||||||
|
r.j = 0
|
||||||
|
r.readHeader = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) readFull(p []byte) (ok bool) {
|
||||||
|
if _, r.err = io.ReadFull(r.r, p); r.err != nil {
|
||||||
|
if r.err == io.ErrUnexpectedEOF {
|
||||||
|
r.err = ErrCorrupt
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read satisfies the io.Reader interface.
|
||||||
|
func (r *Reader) Read(p []byte) (int, error) {
|
||||||
|
if r.err != nil {
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
if r.i < r.j {
|
||||||
|
n := copy(p, r.decoded[r.i:r.j])
|
||||||
|
r.i += n
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
if !r.readFull(r.buf[:4]) {
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
chunkType := r.buf[0]
|
||||||
|
if !r.readHeader {
|
||||||
|
if chunkType != chunkTypeStreamIdentifier {
|
||||||
|
r.err = ErrCorrupt
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
r.readHeader = true
|
||||||
|
}
|
||||||
|
chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16
|
||||||
|
if chunkLen > len(r.buf) {
|
||||||
|
r.err = ErrUnsupported
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// The chunk types are specified at
|
||||||
|
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||||
|
switch chunkType {
|
||||||
|
case chunkTypeCompressedData:
|
||||||
|
// Section 4.2. Compressed data (chunk type 0x00).
|
||||||
|
if chunkLen < checksumSize {
|
||||||
|
r.err = ErrCorrupt
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
buf := r.buf[:chunkLen]
|
||||||
|
if !r.readFull(buf) {
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
|
||||||
|
buf = buf[checksumSize:]
|
||||||
|
|
||||||
|
n, err := DecodedLen(buf)
|
||||||
|
if err != nil {
|
||||||
|
r.err = err
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
if n > len(r.decoded) {
|
||||||
|
r.err = ErrCorrupt
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
if _, err := Decode(r.decoded, buf); err != nil {
|
||||||
|
r.err = err
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
if crc(r.decoded[:n]) != checksum {
|
||||||
|
r.err = ErrCorrupt
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
r.i, r.j = 0, n
|
||||||
|
continue
|
||||||
|
|
||||||
|
case chunkTypeUncompressedData:
|
||||||
|
// Section 4.3. Uncompressed data (chunk type 0x01).
|
||||||
|
if chunkLen < checksumSize {
|
||||||
|
r.err = ErrCorrupt
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
buf := r.buf[:checksumSize]
|
||||||
|
if !r.readFull(buf) {
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
|
||||||
|
// Read directly into r.decoded instead of via r.buf.
|
||||||
|
n := chunkLen - checksumSize
|
||||||
|
if !r.readFull(r.decoded[:n]) {
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
if crc(r.decoded[:n]) != checksum {
|
||||||
|
r.err = ErrCorrupt
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
r.i, r.j = 0, n
|
||||||
|
continue
|
||||||
|
|
||||||
|
case chunkTypeStreamIdentifier:
|
||||||
|
// Section 4.1. Stream identifier (chunk type 0xff).
|
||||||
|
if chunkLen != len(magicBody) {
|
||||||
|
r.err = ErrCorrupt
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
if !r.readFull(r.buf[:len(magicBody)]) {
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
for i := 0; i < len(magicBody); i++ {
|
||||||
|
if r.buf[i] != magicBody[i] {
|
||||||
|
r.err = ErrCorrupt
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if chunkType <= 0x7f {
|
||||||
|
// Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f).
|
||||||
|
r.err = ErrUnsupported
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
// Section 4.4 Padding (chunk type 0xfe).
|
||||||
|
// Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd).
|
||||||
|
if !r.readFull(r.buf[:chunkLen]) {
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
Vendored
+83
-3
@@ -6,6 +6,7 @@ package snappy
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// We limit how far copy back-references can go, the same as the C++ code.
|
// We limit how far copy back-references can go, the same as the C++ code.
|
||||||
@@ -78,7 +79,7 @@ func emitCopy(dst []byte, offset, length int) int {
|
|||||||
// slice of dst if dst was large enough to hold the entire encoded block.
|
// slice of dst if dst was large enough to hold the entire encoded block.
|
||||||
// Otherwise, a newly allocated slice will be returned.
|
// Otherwise, a newly allocated slice will be returned.
|
||||||
// It is valid to pass a nil dst.
|
// It is valid to pass a nil dst.
|
||||||
func Encode(dst, src []byte) ([]byte, error) {
|
func Encode(dst, src []byte) []byte {
|
||||||
if n := MaxEncodedLen(len(src)); len(dst) < n {
|
if n := MaxEncodedLen(len(src)); len(dst) < n {
|
||||||
dst = make([]byte, n)
|
dst = make([]byte, n)
|
||||||
}
|
}
|
||||||
@@ -91,7 +92,7 @@ func Encode(dst, src []byte) ([]byte, error) {
|
|||||||
if len(src) != 0 {
|
if len(src) != 0 {
|
||||||
d += emitLiteral(dst[d:], src)
|
d += emitLiteral(dst[d:], src)
|
||||||
}
|
}
|
||||||
return dst[:d], nil
|
return dst[:d]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive.
|
// Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive.
|
||||||
@@ -144,7 +145,7 @@ func Encode(dst, src []byte) ([]byte, error) {
|
|||||||
if lit != len(src) {
|
if lit != len(src) {
|
||||||
d += emitLiteral(dst[d:], src[lit:])
|
d += emitLiteral(dst[d:], src[lit:])
|
||||||
}
|
}
|
||||||
return dst[:d], nil
|
return dst[:d]
|
||||||
}
|
}
|
||||||
|
|
||||||
// MaxEncodedLen returns the maximum length of a snappy block, given its
|
// MaxEncodedLen returns the maximum length of a snappy block, given its
|
||||||
@@ -172,3 +173,82 @@ func MaxEncodedLen(srcLen int) int {
|
|||||||
// This last factor dominates the blowup, so the final estimate is:
|
// This last factor dominates the blowup, so the final estimate is:
|
||||||
return 32 + srcLen + srcLen/6
|
return 32 + srcLen + srcLen/6
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewWriter returns a new Writer that compresses to w, using the framing
|
||||||
|
// format described at
|
||||||
|
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||||
|
func NewWriter(w io.Writer) *Writer {
|
||||||
|
return &Writer{
|
||||||
|
w: w,
|
||||||
|
enc: make([]byte, MaxEncodedLen(maxUncompressedChunkLen)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writer is an io.Writer than can write Snappy-compressed bytes.
|
||||||
|
type Writer struct {
|
||||||
|
w io.Writer
|
||||||
|
err error
|
||||||
|
enc []byte
|
||||||
|
buf [checksumSize + chunkHeaderSize]byte
|
||||||
|
wroteHeader bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset discards the writer's state and switches the Snappy writer to write to
|
||||||
|
// w. This permits reusing a Writer rather than allocating a new one.
|
||||||
|
func (w *Writer) Reset(writer io.Writer) {
|
||||||
|
w.w = writer
|
||||||
|
w.err = nil
|
||||||
|
w.wroteHeader = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write satisfies the io.Writer interface.
|
||||||
|
func (w *Writer) Write(p []byte) (n int, errRet error) {
|
||||||
|
if w.err != nil {
|
||||||
|
return 0, w.err
|
||||||
|
}
|
||||||
|
if !w.wroteHeader {
|
||||||
|
copy(w.enc, magicChunk)
|
||||||
|
if _, err := w.w.Write(w.enc[:len(magicChunk)]); err != nil {
|
||||||
|
w.err = err
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
w.wroteHeader = true
|
||||||
|
}
|
||||||
|
for len(p) > 0 {
|
||||||
|
var uncompressed []byte
|
||||||
|
if len(p) > maxUncompressedChunkLen {
|
||||||
|
uncompressed, p = p[:maxUncompressedChunkLen], p[maxUncompressedChunkLen:]
|
||||||
|
} else {
|
||||||
|
uncompressed, p = p, nil
|
||||||
|
}
|
||||||
|
checksum := crc(uncompressed)
|
||||||
|
|
||||||
|
// Compress the buffer, discarding the result if the improvement
|
||||||
|
// isn't at least 12.5%.
|
||||||
|
chunkType := uint8(chunkTypeCompressedData)
|
||||||
|
chunkBody := Encode(w.enc, uncompressed)
|
||||||
|
if len(chunkBody) >= len(uncompressed)-len(uncompressed)/8 {
|
||||||
|
chunkType, chunkBody = chunkTypeUncompressedData, uncompressed
|
||||||
|
}
|
||||||
|
|
||||||
|
chunkLen := 4 + len(chunkBody)
|
||||||
|
w.buf[0] = chunkType
|
||||||
|
w.buf[1] = uint8(chunkLen >> 0)
|
||||||
|
w.buf[2] = uint8(chunkLen >> 8)
|
||||||
|
w.buf[3] = uint8(chunkLen >> 16)
|
||||||
|
w.buf[4] = uint8(checksum >> 0)
|
||||||
|
w.buf[5] = uint8(checksum >> 8)
|
||||||
|
w.buf[6] = uint8(checksum >> 16)
|
||||||
|
w.buf[7] = uint8(checksum >> 24)
|
||||||
|
if _, err := w.w.Write(w.buf[:]); err != nil {
|
||||||
|
w.err = err
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
if _, err := w.w.Write(chunkBody); err != nil {
|
||||||
|
w.err = err
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
n += len(uncompressed)
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
Generated
Vendored
+31
-1
@@ -5,9 +5,13 @@
|
|||||||
// Package snappy implements the snappy block-based compression format.
|
// Package snappy implements the snappy block-based compression format.
|
||||||
// It aims for very high speeds and reasonable compression.
|
// It aims for very high speeds and reasonable compression.
|
||||||
//
|
//
|
||||||
// The C++ snappy implementation is at http://code.google.com/p/snappy/
|
// The C++ snappy implementation is at https://github.com/google/snappy
|
||||||
package snappy
|
package snappy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"hash/crc32"
|
||||||
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Each encoded block begins with the varint-encoded length of the decoded data,
|
Each encoded block begins with the varint-encoded length of the decoded data,
|
||||||
followed by a sequence of chunks. Chunks begin and end on byte boundaries. The
|
followed by a sequence of chunks. Chunks begin and end on byte boundaries. The
|
||||||
@@ -36,3 +40,29 @@ const (
|
|||||||
tagCopy2 = 0x02
|
tagCopy2 = 0x02
|
||||||
tagCopy4 = 0x03
|
tagCopy4 = 0x03
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
checksumSize = 4
|
||||||
|
chunkHeaderSize = 4
|
||||||
|
magicChunk = "\xff\x06\x00\x00" + magicBody
|
||||||
|
magicBody = "sNaPpY"
|
||||||
|
// https://github.com/google/snappy/blob/master/framing_format.txt says
|
||||||
|
// that "the uncompressed data in a chunk must be no longer than 65536 bytes".
|
||||||
|
maxUncompressedChunkLen = 65536
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
chunkTypeCompressedData = 0x00
|
||||||
|
chunkTypeUncompressedData = 0x01
|
||||||
|
chunkTypePadding = 0xfe
|
||||||
|
chunkTypeStreamIdentifier = 0xff
|
||||||
|
)
|
||||||
|
|
||||||
|
var crcTable = crc32.MakeTable(crc32.Castagnoli)
|
||||||
|
|
||||||
|
// crc implements the checksum specified in section 3 of
|
||||||
|
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||||
|
func crc(b []byte) uint32 {
|
||||||
|
c := crc32.Update(0, crcTable, b)
|
||||||
|
return uint32(c>>15|c<<17) + 0xa282ead8
|
||||||
|
}
|
||||||
+6
@@ -1,3 +1,9 @@
|
|||||||
|
All files in this repository are licensed as follows. If you contribute
|
||||||
|
to this repository, it is assumed that you license your contribution
|
||||||
|
under the same license unless you state otherwise.
|
||||||
|
|
||||||
|
All files Copyright (C) 2015 Canonical Ltd. unless otherwise specified in the file.
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@ token in the bucket represents one byte.
|
|||||||
```go
|
```go
|
||||||
func Writer(w io.Writer, bucket *Bucket) io.Writer
|
func Writer(w io.Writer, bucket *Bucket) io.Writer
|
||||||
```
|
```
|
||||||
Writer returns a reader that is rate limited by the given token bucket. Each
|
Writer returns a writer that is rate limited by the given token bucket. Each
|
||||||
token in the bucket represents one byte.
|
token in the bucket represents one byte.
|
||||||
|
|
||||||
#### type Bucket
|
#### type Bucket
|
||||||
|
|||||||
+4
-9
@@ -2,7 +2,8 @@
|
|||||||
// Licensed under the LGPLv3 with static-linking exception.
|
// Licensed under the LGPLv3 with static-linking exception.
|
||||||
// See LICENCE file for details.
|
// See LICENCE file for details.
|
||||||
|
|
||||||
// The ratelimit package provides an efficient token bucket implementation.
|
// The ratelimit package provides an efficient token bucket implementation
|
||||||
|
// that can be used to limit the rate of arbitrary things.
|
||||||
// See http://en.wikipedia.org/wiki/Token_bucket.
|
// See http://en.wikipedia.org/wiki/Token_bucket.
|
||||||
package ratelimit
|
package ratelimit
|
||||||
|
|
||||||
@@ -10,6 +11,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Bucket represents a token bucket that fills at a predetermined rate.
|
// Bucket represents a token bucket that fills at a predetermined rate.
|
||||||
@@ -55,7 +57,7 @@ func NewBucketWithRate(rate float64, capacity int64) *Bucket {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
tb := NewBucketWithQuantum(fillInterval, capacity, quantum)
|
tb := NewBucketWithQuantum(fillInterval, capacity, quantum)
|
||||||
if diff := abs(tb.Rate() - rate); diff/rate <= rateMargin {
|
if diff := math.Abs(tb.Rate() - rate); diff/rate <= rateMargin {
|
||||||
return tb
|
return tb
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,10 +219,3 @@ func (tb *Bucket) adjust(now time.Time) (currentTick int64) {
|
|||||||
tb.availTick = currentTick
|
tb.availTick = currentTick
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func abs(f float64) float64 {
|
|
||||||
if f < 0 {
|
|
||||||
return -f
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
-328
@@ -1,328 +0,0 @@
|
|||||||
// Copyright 2014 Canonical Ltd.
|
|
||||||
// Licensed under the LGPLv3 with static-linking exception.
|
|
||||||
// See LICENCE file for details.
|
|
||||||
|
|
||||||
package ratelimit
|
|
||||||
|
|
||||||
import (
|
|
||||||
gc "launchpad.net/gocheck"
|
|
||||||
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPackage(t *testing.T) {
|
|
||||||
gc.TestingT(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
type rateLimitSuite struct{}
|
|
||||||
|
|
||||||
var _ = gc.Suite(rateLimitSuite{})
|
|
||||||
|
|
||||||
type takeReq struct {
|
|
||||||
time time.Duration
|
|
||||||
count int64
|
|
||||||
expectWait time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
var takeTests = []struct {
|
|
||||||
about string
|
|
||||||
fillInterval time.Duration
|
|
||||||
capacity int64
|
|
||||||
reqs []takeReq
|
|
||||||
}{{
|
|
||||||
about: "serial requests",
|
|
||||||
fillInterval: 250 * time.Millisecond,
|
|
||||||
capacity: 10,
|
|
||||||
reqs: []takeReq{{
|
|
||||||
time: 0,
|
|
||||||
count: 0,
|
|
||||||
expectWait: 0,
|
|
||||||
}, {
|
|
||||||
time: 0,
|
|
||||||
count: 10,
|
|
||||||
expectWait: 0,
|
|
||||||
}, {
|
|
||||||
time: 0,
|
|
||||||
count: 1,
|
|
||||||
expectWait: 250 * time.Millisecond,
|
|
||||||
}, {
|
|
||||||
time: 250 * time.Millisecond,
|
|
||||||
count: 1,
|
|
||||||
expectWait: 250 * time.Millisecond,
|
|
||||||
}},
|
|
||||||
}, {
|
|
||||||
about: "concurrent requests",
|
|
||||||
fillInterval: 250 * time.Millisecond,
|
|
||||||
capacity: 10,
|
|
||||||
reqs: []takeReq{{
|
|
||||||
time: 0,
|
|
||||||
count: 10,
|
|
||||||
expectWait: 0,
|
|
||||||
}, {
|
|
||||||
time: 0,
|
|
||||||
count: 2,
|
|
||||||
expectWait: 500 * time.Millisecond,
|
|
||||||
}, {
|
|
||||||
time: 0,
|
|
||||||
count: 2,
|
|
||||||
expectWait: 1000 * time.Millisecond,
|
|
||||||
}, {
|
|
||||||
time: 0,
|
|
||||||
count: 1,
|
|
||||||
expectWait: 1250 * time.Millisecond,
|
|
||||||
}},
|
|
||||||
}, {
|
|
||||||
about: "more than capacity",
|
|
||||||
fillInterval: 1 * time.Millisecond,
|
|
||||||
capacity: 10,
|
|
||||||
reqs: []takeReq{{
|
|
||||||
time: 0,
|
|
||||||
count: 10,
|
|
||||||
expectWait: 0,
|
|
||||||
}, {
|
|
||||||
time: 20 * time.Millisecond,
|
|
||||||
count: 15,
|
|
||||||
expectWait: 5 * time.Millisecond,
|
|
||||||
}},
|
|
||||||
}, {
|
|
||||||
about: "sub-quantum time",
|
|
||||||
fillInterval: 10 * time.Millisecond,
|
|
||||||
capacity: 10,
|
|
||||||
reqs: []takeReq{{
|
|
||||||
time: 0,
|
|
||||||
count: 10,
|
|
||||||
expectWait: 0,
|
|
||||||
}, {
|
|
||||||
time: 7 * time.Millisecond,
|
|
||||||
count: 1,
|
|
||||||
expectWait: 3 * time.Millisecond,
|
|
||||||
}, {
|
|
||||||
time: 8 * time.Millisecond,
|
|
||||||
count: 1,
|
|
||||||
expectWait: 12 * time.Millisecond,
|
|
||||||
}},
|
|
||||||
}, {
|
|
||||||
about: "within capacity",
|
|
||||||
fillInterval: 10 * time.Millisecond,
|
|
||||||
capacity: 5,
|
|
||||||
reqs: []takeReq{{
|
|
||||||
time: 0,
|
|
||||||
count: 5,
|
|
||||||
expectWait: 0,
|
|
||||||
}, {
|
|
||||||
time: 60 * time.Millisecond,
|
|
||||||
count: 5,
|
|
||||||
expectWait: 0,
|
|
||||||
}, {
|
|
||||||
time: 60 * time.Millisecond,
|
|
||||||
count: 1,
|
|
||||||
expectWait: 10 * time.Millisecond,
|
|
||||||
}, {
|
|
||||||
time: 80 * time.Millisecond,
|
|
||||||
count: 2,
|
|
||||||
expectWait: 10 * time.Millisecond,
|
|
||||||
}},
|
|
||||||
}}
|
|
||||||
|
|
||||||
func (rateLimitSuite) TestTake(c *gc.C) {
|
|
||||||
for i, test := range takeTests {
|
|
||||||
tb := NewBucket(test.fillInterval, test.capacity)
|
|
||||||
for j, req := range test.reqs {
|
|
||||||
d, ok := tb.take(tb.startTime.Add(req.time), req.count, infinityDuration)
|
|
||||||
c.Assert(ok, gc.Equals, true)
|
|
||||||
if d != req.expectWait {
|
|
||||||
c.Fatalf("test %d.%d, %s, got %v want %v", i, j, test.about, d, req.expectWait)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rateLimitSuite) TestTakeMaxDuration(c *gc.C) {
|
|
||||||
for i, test := range takeTests {
|
|
||||||
tb := NewBucket(test.fillInterval, test.capacity)
|
|
||||||
for j, req := range test.reqs {
|
|
||||||
if req.expectWait > 0 {
|
|
||||||
d, ok := tb.take(tb.startTime.Add(req.time), req.count, req.expectWait-1)
|
|
||||||
c.Assert(ok, gc.Equals, false)
|
|
||||||
c.Assert(d, gc.Equals, time.Duration(0))
|
|
||||||
}
|
|
||||||
d, ok := tb.take(tb.startTime.Add(req.time), req.count, req.expectWait)
|
|
||||||
c.Assert(ok, gc.Equals, true)
|
|
||||||
if d != req.expectWait {
|
|
||||||
c.Fatalf("test %d.%d, %s, got %v want %v", i, j, test.about, d, req.expectWait)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type takeAvailableReq struct {
|
|
||||||
time time.Duration
|
|
||||||
count int64
|
|
||||||
expect int64
|
|
||||||
}
|
|
||||||
|
|
||||||
var takeAvailableTests = []struct {
|
|
||||||
about string
|
|
||||||
fillInterval time.Duration
|
|
||||||
capacity int64
|
|
||||||
reqs []takeAvailableReq
|
|
||||||
}{{
|
|
||||||
about: "serial requests",
|
|
||||||
fillInterval: 250 * time.Millisecond,
|
|
||||||
capacity: 10,
|
|
||||||
reqs: []takeAvailableReq{{
|
|
||||||
time: 0,
|
|
||||||
count: 0,
|
|
||||||
expect: 0,
|
|
||||||
}, {
|
|
||||||
time: 0,
|
|
||||||
count: 10,
|
|
||||||
expect: 10,
|
|
||||||
}, {
|
|
||||||
time: 0,
|
|
||||||
count: 1,
|
|
||||||
expect: 0,
|
|
||||||
}, {
|
|
||||||
time: 250 * time.Millisecond,
|
|
||||||
count: 1,
|
|
||||||
expect: 1,
|
|
||||||
}},
|
|
||||||
}, {
|
|
||||||
about: "concurrent requests",
|
|
||||||
fillInterval: 250 * time.Millisecond,
|
|
||||||
capacity: 10,
|
|
||||||
reqs: []takeAvailableReq{{
|
|
||||||
time: 0,
|
|
||||||
count: 5,
|
|
||||||
expect: 5,
|
|
||||||
}, {
|
|
||||||
time: 0,
|
|
||||||
count: 2,
|
|
||||||
expect: 2,
|
|
||||||
}, {
|
|
||||||
time: 0,
|
|
||||||
count: 5,
|
|
||||||
expect: 3,
|
|
||||||
}, {
|
|
||||||
time: 0,
|
|
||||||
count: 1,
|
|
||||||
expect: 0,
|
|
||||||
}},
|
|
||||||
}, {
|
|
||||||
about: "more than capacity",
|
|
||||||
fillInterval: 1 * time.Millisecond,
|
|
||||||
capacity: 10,
|
|
||||||
reqs: []takeAvailableReq{{
|
|
||||||
time: 0,
|
|
||||||
count: 10,
|
|
||||||
expect: 10,
|
|
||||||
}, {
|
|
||||||
time: 20 * time.Millisecond,
|
|
||||||
count: 15,
|
|
||||||
expect: 10,
|
|
||||||
}},
|
|
||||||
}, {
|
|
||||||
about: "within capacity",
|
|
||||||
fillInterval: 10 * time.Millisecond,
|
|
||||||
capacity: 5,
|
|
||||||
reqs: []takeAvailableReq{{
|
|
||||||
time: 0,
|
|
||||||
count: 5,
|
|
||||||
expect: 5,
|
|
||||||
}, {
|
|
||||||
time: 60 * time.Millisecond,
|
|
||||||
count: 5,
|
|
||||||
expect: 5,
|
|
||||||
}, {
|
|
||||||
time: 70 * time.Millisecond,
|
|
||||||
count: 1,
|
|
||||||
expect: 1,
|
|
||||||
}},
|
|
||||||
}}
|
|
||||||
|
|
||||||
func (rateLimitSuite) TestTakeAvailable(c *gc.C) {
|
|
||||||
for i, test := range takeAvailableTests {
|
|
||||||
tb := NewBucket(test.fillInterval, test.capacity)
|
|
||||||
for j, req := range test.reqs {
|
|
||||||
d := tb.takeAvailable(tb.startTime.Add(req.time), req.count)
|
|
||||||
if d != req.expect {
|
|
||||||
c.Fatalf("test %d.%d, %s, got %v want %v", i, j, test.about, d, req.expect)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rateLimitSuite) TestPanics(c *gc.C) {
|
|
||||||
c.Assert(func() { NewBucket(0, 1) }, gc.PanicMatches, "token bucket fill interval is not > 0")
|
|
||||||
c.Assert(func() { NewBucket(-2, 1) }, gc.PanicMatches, "token bucket fill interval is not > 0")
|
|
||||||
c.Assert(func() { NewBucket(1, 0) }, gc.PanicMatches, "token bucket capacity is not > 0")
|
|
||||||
c.Assert(func() { NewBucket(1, -2) }, gc.PanicMatches, "token bucket capacity is not > 0")
|
|
||||||
}
|
|
||||||
|
|
||||||
func isCloseTo(x, y, tolerance float64) bool {
|
|
||||||
return abs(x-y)/y < tolerance
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rateLimitSuite) TestRate(c *gc.C) {
|
|
||||||
tb := NewBucket(1, 1)
|
|
||||||
if !isCloseTo(tb.Rate(), 1e9, 0.00001) {
|
|
||||||
c.Fatalf("got %v want 1e9", tb.Rate())
|
|
||||||
}
|
|
||||||
tb = NewBucket(2*time.Second, 1)
|
|
||||||
if !isCloseTo(tb.Rate(), 0.5, 0.00001) {
|
|
||||||
c.Fatalf("got %v want 0.5", tb.Rate())
|
|
||||||
}
|
|
||||||
tb = NewBucketWithQuantum(100*time.Millisecond, 1, 5)
|
|
||||||
if !isCloseTo(tb.Rate(), 50, 0.00001) {
|
|
||||||
c.Fatalf("got %v want 50", tb.Rate())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkRate(c *gc.C, rate float64) {
|
|
||||||
tb := NewBucketWithRate(rate, 1<<62)
|
|
||||||
if !isCloseTo(tb.Rate(), rate, rateMargin) {
|
|
||||||
c.Fatalf("got %g want %v", tb.Rate(), rate)
|
|
||||||
}
|
|
||||||
d, ok := tb.take(tb.startTime, 1<<62, infinityDuration)
|
|
||||||
c.Assert(ok, gc.Equals, true)
|
|
||||||
c.Assert(d, gc.Equals, time.Duration(0))
|
|
||||||
|
|
||||||
// Check that the actual rate is as expected by
|
|
||||||
// asking for a not-quite multiple of the bucket's
|
|
||||||
// quantum and checking that the wait time
|
|
||||||
// correct.
|
|
||||||
d, ok = tb.take(tb.startTime, tb.quantum*2-tb.quantum/2, infinityDuration)
|
|
||||||
c.Assert(ok, gc.Equals, true)
|
|
||||||
expectTime := 1e9 * float64(tb.quantum) * 2 / rate
|
|
||||||
if !isCloseTo(float64(d), expectTime, rateMargin) {
|
|
||||||
c.Fatalf("rate %g: got %g want %v", rate, float64(d), expectTime)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rateLimitSuite) TestNewWithRate(c *gc.C) {
|
|
||||||
for rate := float64(1); rate < 1e6; rate += 7 {
|
|
||||||
checkRate(c, rate)
|
|
||||||
}
|
|
||||||
for _, rate := range []float64{
|
|
||||||
1024 * 1024 * 1024,
|
|
||||||
1e-5,
|
|
||||||
0.9e-5,
|
|
||||||
0.5,
|
|
||||||
0.9,
|
|
||||||
0.9e8,
|
|
||||||
3e12,
|
|
||||||
4e18,
|
|
||||||
} {
|
|
||||||
checkRate(c, rate)
|
|
||||||
checkRate(c, rate/3)
|
|
||||||
checkRate(c, rate*1.3)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkWait(b *testing.B) {
|
|
||||||
tb := NewBucket(1, 16*1024)
|
|
||||||
for i := b.N - 1; i >= 0; i-- {
|
|
||||||
tb.Wait(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+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.
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
### 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. Both
|
||||||
|
working directory and the os.Args[0] value are arbitrary and cannot
|
||||||
|
be relied on; os.Args[0] can be "faked".
|
||||||
|
|
||||||
|
Multi-platform and supports:
|
||||||
|
* Linux
|
||||||
|
* OS X
|
||||||
|
* Windows
|
||||||
|
* Plan 9
|
||||||
|
* BSDs.
|
||||||
Generated
Vendored
+2
-7
@@ -16,17 +16,12 @@ func Executable() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns same path as Executable, returns just the folder
|
// Returns same path as Executable, returns just the folder
|
||||||
// path. Excludes the executable name.
|
// path. Excludes the executable name and any trailing slash.
|
||||||
func ExecutableFolder() (string, error) {
|
func ExecutableFolder() (string, error) {
|
||||||
p, err := Executable()
|
p, err := Executable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
folder, _ := filepath.Split(p)
|
|
||||||
return folder, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Depricated. Same as Executable().
|
return filepath.Dir(p), nil
|
||||||
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
+11
-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 solaris dragonfly
|
||||||
|
|
||||||
package osext
|
package osext
|
||||||
|
|
||||||
@@ -11,15 +11,23 @@ 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 deletedTag = " (deleted)"
|
||||||
|
execpath, err := os.Readlink("/proc/self/exe")
|
||||||
|
if err != nil {
|
||||||
|
return execpath, err
|
||||||
|
}
|
||||||
|
execpath = strings.TrimSuffix(execpath, deletedTag)
|
||||||
|
execpath = strings.TrimPrefix(execpath, deletedTag)
|
||||||
|
return execpath, nil
|
||||||
case "netbsd":
|
case "netbsd":
|
||||||
return os.Readlink("/proc/curproc/exe")
|
return os.Readlink("/proc/curproc/exe")
|
||||||
case "openbsd":
|
case "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
+53
-6
@@ -2,12 +2,13 @@
|
|||||||
// 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 darwin freebsd
|
// +build darwin freebsd openbsd
|
||||||
|
|
||||||
package osext
|
package osext
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
"syscall"
|
||||||
@@ -23,6 +24,8 @@ func executable() (string, error) {
|
|||||||
mib = [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1}
|
mib = [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1}
|
||||||
case "darwin":
|
case "darwin":
|
||||||
mib = [4]int32{1 /* CTL_KERN */, 38 /* KERN_PROCARGS */, int32(os.Getpid()), -1}
|
mib = [4]int32{1 /* CTL_KERN */, 38 /* KERN_PROCARGS */, int32(os.Getpid()), -1}
|
||||||
|
case "openbsd":
|
||||||
|
mib = [4]int32{1 /* CTL_KERN */, 55 /* KERN_PROC_ARGS */, int32(os.Getpid()), 1 /* KERN_PROC_ARGV */}
|
||||||
}
|
}
|
||||||
|
|
||||||
n := uintptr(0)
|
n := uintptr(0)
|
||||||
@@ -42,14 +45,58 @@ func executable() (string, error) {
|
|||||||
if n == 0 { // This shouldn't happen.
|
if n == 0 { // This shouldn't happen.
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
for i, v := range buf {
|
|
||||||
if v == 0 {
|
var execPath string
|
||||||
buf = buf[:i]
|
switch runtime.GOOS {
|
||||||
break
|
case "openbsd":
|
||||||
|
// buf now contains **argv, with pointers to each of the C-style
|
||||||
|
// NULL terminated arguments.
|
||||||
|
var args []string
|
||||||
|
argv := uintptr(unsafe.Pointer(&buf[0]))
|
||||||
|
Loop:
|
||||||
|
for {
|
||||||
|
argp := *(**[1<<20]byte)(unsafe.Pointer(argv))
|
||||||
|
if argp == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for i := 0; uintptr(i) < n; i++ {
|
||||||
|
// we don't want the full arguments list
|
||||||
|
if string(argp[i]) == " " {
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
if argp[i] != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
args = append(args, string(argp[:i]))
|
||||||
|
n -= uintptr(i)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if n < unsafe.Sizeof(argv) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
argv += unsafe.Sizeof(argv)
|
||||||
|
n -= unsafe.Sizeof(argv)
|
||||||
|
}
|
||||||
|
execPath = args[0]
|
||||||
|
// There is no canonical way to get an executable path on
|
||||||
|
// OpenBSD, so check PATH in case we are called directly
|
||||||
|
if execPath[0] != '/' && execPath[0] != '.' {
|
||||||
|
execIsInPath, err := exec.LookPath(execPath)
|
||||||
|
if err == nil {
|
||||||
|
execPath = execIsInPath
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
for i, v := range buf {
|
||||||
|
if v == 0 {
|
||||||
|
buf = buf[:i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
execPath = string(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
execPath := string(buf)
|
|
||||||
// execPath will not be empty due to above checks.
|
// execPath will not be empty due to above checks.
|
||||||
// Try to get the absolute path if the execPath is not rooted.
|
// Try to get the absolute path if the execPath is not rooted.
|
||||||
if execPath[0] != '/' {
|
if execPath[0] != '/' {
|
||||||
Generated
Vendored
File renamed without changes.
@@ -0,0 +1,9 @@
|
|||||||
|
*.[68]
|
||||||
|
*.a
|
||||||
|
*.out
|
||||||
|
*.swp
|
||||||
|
_obj
|
||||||
|
_testmain.go
|
||||||
|
cmd/metrics-bench/metrics-bench
|
||||||
|
cmd/metrics-example/metrics-example
|
||||||
|
cmd/never-read/never-read
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.2
|
||||||
|
- 1.3
|
||||||
|
- 1.4
|
||||||
|
|
||||||
|
script:
|
||||||
|
- ./validate.sh
|
||||||
|
|
||||||
|
# this should give us faster builds according to
|
||||||
|
# http://docs.travis-ci.com/user/migrating-from-legacy/
|
||||||
|
sudo: false
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
Copyright 2012 Richard Crowley. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following
|
||||||
|
disclaimer in the documentation and/or other materials provided
|
||||||
|
with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY RICHARD CROWLEY ``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 RICHARD CROWLEY 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.
|
||||||
|
|
||||||
|
The views and conclusions contained in the software and documentation
|
||||||
|
are those of the authors and should not be interpreted as representing
|
||||||
|
official policies, either expressed or implied, of Richard Crowley.
|
||||||
+126
@@ -0,0 +1,126 @@
|
|||||||
|
go-metrics
|
||||||
|
==========
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Go port of Coda Hale's Metrics library: <https://github.com/dropwizard/metrics>.
|
||||||
|
|
||||||
|
Documentation: <http://godoc.org/github.com/rcrowley/go-metrics>.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
Create and update metrics:
|
||||||
|
|
||||||
|
```go
|
||||||
|
c := metrics.NewCounter()
|
||||||
|
metrics.Register("foo", c)
|
||||||
|
c.Inc(47)
|
||||||
|
|
||||||
|
g := metrics.NewGauge()
|
||||||
|
metrics.Register("bar", g)
|
||||||
|
g.Update(47)
|
||||||
|
|
||||||
|
s := metrics.NewExpDecaySample(1028, 0.015) // or metrics.NewUniformSample(1028)
|
||||||
|
h := metrics.NewHistogram(s)
|
||||||
|
metrics.Register("baz", h)
|
||||||
|
h.Update(47)
|
||||||
|
|
||||||
|
m := metrics.NewMeter()
|
||||||
|
metrics.Register("quux", m)
|
||||||
|
m.Mark(47)
|
||||||
|
|
||||||
|
t := metrics.NewTimer()
|
||||||
|
metrics.Register("bang", t)
|
||||||
|
t.Time(func() {})
|
||||||
|
t.Update(47)
|
||||||
|
```
|
||||||
|
|
||||||
|
Periodically log every metric in human-readable form to standard error:
|
||||||
|
|
||||||
|
```go
|
||||||
|
go metrics.Log(metrics.DefaultRegistry, 60e9, log.New(os.Stderr, "metrics: ", log.Lmicroseconds))
|
||||||
|
```
|
||||||
|
|
||||||
|
Periodically log every metric in slightly-more-parseable form to syslog:
|
||||||
|
|
||||||
|
```go
|
||||||
|
w, _ := syslog.Dial("unixgram", "/dev/log", syslog.LOG_INFO, "metrics")
|
||||||
|
go metrics.Syslog(metrics.DefaultRegistry, 60e9, w)
|
||||||
|
```
|
||||||
|
|
||||||
|
Periodically emit every metric to Graphite using the [Graphite client](https://github.com/cyberdelia/go-metrics-graphite):
|
||||||
|
|
||||||
|
```go
|
||||||
|
|
||||||
|
import "github.com/cyberdelia/go-metrics-graphite"
|
||||||
|
|
||||||
|
addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:2003")
|
||||||
|
go graphite.Graphite(metrics.DefaultRegistry, 10e9, "metrics", addr)
|
||||||
|
```
|
||||||
|
|
||||||
|
Periodically emit every metric into InfluxDB:
|
||||||
|
|
||||||
|
**NOTE:** this has been pulled out of the library due to constant fluctuations
|
||||||
|
in the InfluxDB API. In fact, all client libraries are on their way out. see
|
||||||
|
issues [#121](https://github.com/rcrowley/go-metrics/issues/121) and
|
||||||
|
[#124](https://github.com/rcrowley/go-metrics/issues/124) for progress and details.
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/rcrowley/go-metrics/influxdb"
|
||||||
|
|
||||||
|
go influxdb.Influxdb(metrics.DefaultRegistry, 10e9, &influxdb.Config{
|
||||||
|
Host: "127.0.0.1:8086",
|
||||||
|
Database: "metrics",
|
||||||
|
Username: "test",
|
||||||
|
Password: "test",
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Periodically upload every metric to Librato using the [Librato client](https://github.com/mihasya/go-metrics-librato):
|
||||||
|
|
||||||
|
**Note**: the client included with this repository under the `librato` package
|
||||||
|
has been deprecated and moved to the repository linked above.
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/mihasya/go-metrics-librato"
|
||||||
|
|
||||||
|
go librato.Librato(metrics.DefaultRegistry,
|
||||||
|
10e9, // interval
|
||||||
|
"example@example.com", // account owner email address
|
||||||
|
"token", // Librato API token
|
||||||
|
"hostname", // source
|
||||||
|
[]float64{0.95}, // percentiles to send
|
||||||
|
time.Millisecond, // time unit
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Periodically emit every metric to StatHat:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/rcrowley/go-metrics/stathat"
|
||||||
|
|
||||||
|
go stathat.Stathat(metrics.DefaultRegistry, 10e9, "example@example.com")
|
||||||
|
```
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
```sh
|
||||||
|
go get github.com/rcrowley/go-metrics
|
||||||
|
```
|
||||||
|
|
||||||
|
StatHat support additionally requires their Go client:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
go get github.com/stathat/go
|
||||||
|
```
|
||||||
|
|
||||||
|
Publishing Metrics
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Clients are available for the following destinations:
|
||||||
|
|
||||||
|
* Librato - [https://github.com/mihasya/go-metrics-librato](https://github.com/mihasya/go-metrics-librato)
|
||||||
|
* Graphite - [https://github.com/cyberdelia/go-metrics-graphite](https://github.com/cyberdelia/go-metrics-graphite)
|
||||||
|
* InfluxDB - [https://github.com/vrischmann/go-metrics-influxdb](https://github.com/vrischmann/go-metrics-influxdb)
|
||||||
Generated
Vendored
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/rcrowley/go-metrics"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
r := metrics.NewRegistry()
|
||||||
|
for i := 0; i < 10000; i++ {
|
||||||
|
r.Register(fmt.Sprintf("counter-%d", i), metrics.NewCounter())
|
||||||
|
r.Register(fmt.Sprintf("gauge-%d", i), metrics.NewGauge())
|
||||||
|
r.Register(fmt.Sprintf("gaugefloat64-%d", i), metrics.NewGaugeFloat64())
|
||||||
|
r.Register(fmt.Sprintf("histogram-uniform-%d", i), metrics.NewHistogram(metrics.NewUniformSample(1028)))
|
||||||
|
r.Register(fmt.Sprintf("histogram-exp-%d", i), metrics.NewHistogram(metrics.NewExpDecaySample(1028, 0.015)))
|
||||||
|
r.Register(fmt.Sprintf("meter-%d", i), metrics.NewMeter())
|
||||||
|
}
|
||||||
|
time.Sleep(600e9)
|
||||||
|
}
|
||||||
Generated
Vendored
+154
@@ -0,0 +1,154 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/rcrowley/go-metrics"
|
||||||
|
// "github.com/rcrowley/go-metrics/stathat"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
// "syslog"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const fanout = 10
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
r := metrics.NewRegistry()
|
||||||
|
|
||||||
|
c := metrics.NewCounter()
|
||||||
|
r.Register("foo", c)
|
||||||
|
for i := 0; i < fanout; i++ {
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
c.Dec(19)
|
||||||
|
time.Sleep(300e6)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
c.Inc(47)
|
||||||
|
time.Sleep(400e6)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
g := metrics.NewGauge()
|
||||||
|
r.Register("bar", g)
|
||||||
|
for i := 0; i < fanout; i++ {
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
g.Update(19)
|
||||||
|
time.Sleep(300e6)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
g.Update(47)
|
||||||
|
time.Sleep(400e6)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
gf := metrics.NewGaugeFloat64()
|
||||||
|
r.Register("barfloat64", gf)
|
||||||
|
for i := 0; i < fanout; i++ {
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
g.Update(19.0)
|
||||||
|
time.Sleep(300e6)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
g.Update(47.0)
|
||||||
|
time.Sleep(400e6)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
hc := metrics.NewHealthcheck(func(h metrics.Healthcheck) {
|
||||||
|
if 0 < rand.Intn(2) {
|
||||||
|
h.Healthy()
|
||||||
|
} else {
|
||||||
|
h.Unhealthy(errors.New("baz"))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
r.Register("baz", hc)
|
||||||
|
|
||||||
|
s := metrics.NewExpDecaySample(1028, 0.015)
|
||||||
|
//s := metrics.NewUniformSample(1028)
|
||||||
|
h := metrics.NewHistogram(s)
|
||||||
|
r.Register("bang", h)
|
||||||
|
for i := 0; i < fanout; i++ {
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
h.Update(19)
|
||||||
|
time.Sleep(300e6)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
h.Update(47)
|
||||||
|
time.Sleep(400e6)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
m := metrics.NewMeter()
|
||||||
|
r.Register("quux", m)
|
||||||
|
for i := 0; i < fanout; i++ {
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
m.Mark(19)
|
||||||
|
time.Sleep(300e6)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
m.Mark(47)
|
||||||
|
time.Sleep(400e6)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
t := metrics.NewTimer()
|
||||||
|
r.Register("hooah", t)
|
||||||
|
for i := 0; i < fanout; i++ {
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
t.Time(func() { time.Sleep(300e6) })
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
t.Time(func() { time.Sleep(400e6) })
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
metrics.RegisterDebugGCStats(r)
|
||||||
|
go metrics.CaptureDebugGCStats(r, 5e9)
|
||||||
|
|
||||||
|
metrics.RegisterRuntimeMemStats(r)
|
||||||
|
go metrics.CaptureRuntimeMemStats(r, 5e9)
|
||||||
|
|
||||||
|
metrics.Log(r, 60e9, log.New(os.Stderr, "metrics: ", log.Lmicroseconds))
|
||||||
|
|
||||||
|
/*
|
||||||
|
w, err := syslog.Dial("unixgram", "/dev/log", syslog.LOG_INFO, "metrics")
|
||||||
|
if nil != err { log.Fatalln(err) }
|
||||||
|
metrics.Syslog(r, 60e9, w)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:2003")
|
||||||
|
metrics.Graphite(r, 10e9, "metrics", addr)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
stathat.Stathat(r, 10e9, "example@example.com")
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
Generated
Vendored
+22
@@ -0,0 +1,22 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:2003")
|
||||||
|
l, err := net.ListenTCP("tcp", addr)
|
||||||
|
if nil != err {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
log.Println("listening", l.Addr())
|
||||||
|
for {
|
||||||
|
c, err := l.AcceptTCP()
|
||||||
|
if nil != err {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
log.Println("accepted", c.RemoteAddr())
|
||||||
|
}
|
||||||
|
}
|
||||||
+112
@@ -0,0 +1,112 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
import "sync/atomic"
|
||||||
|
|
||||||
|
// Counters hold an int64 value that can be incremented and decremented.
|
||||||
|
type Counter interface {
|
||||||
|
Clear()
|
||||||
|
Count() int64
|
||||||
|
Dec(int64)
|
||||||
|
Inc(int64)
|
||||||
|
Snapshot() Counter
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrRegisterCounter returns an existing Counter or constructs and registers
|
||||||
|
// a new StandardCounter.
|
||||||
|
func GetOrRegisterCounter(name string, r Registry) Counter {
|
||||||
|
if nil == r {
|
||||||
|
r = DefaultRegistry
|
||||||
|
}
|
||||||
|
return r.GetOrRegister(name, NewCounter).(Counter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCounter constructs a new StandardCounter.
|
||||||
|
func NewCounter() Counter {
|
||||||
|
if UseNilMetrics {
|
||||||
|
return NilCounter{}
|
||||||
|
}
|
||||||
|
return &StandardCounter{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRegisteredCounter constructs and registers a new StandardCounter.
|
||||||
|
func NewRegisteredCounter(name string, r Registry) Counter {
|
||||||
|
c := NewCounter()
|
||||||
|
if nil == r {
|
||||||
|
r = DefaultRegistry
|
||||||
|
}
|
||||||
|
r.Register(name, c)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// CounterSnapshot is a read-only copy of another Counter.
|
||||||
|
type CounterSnapshot int64
|
||||||
|
|
||||||
|
// Clear panics.
|
||||||
|
func (CounterSnapshot) Clear() {
|
||||||
|
panic("Clear called on a CounterSnapshot")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count returns the count at the time the snapshot was taken.
|
||||||
|
func (c CounterSnapshot) Count() int64 { return int64(c) }
|
||||||
|
|
||||||
|
// Dec panics.
|
||||||
|
func (CounterSnapshot) Dec(int64) {
|
||||||
|
panic("Dec called on a CounterSnapshot")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inc panics.
|
||||||
|
func (CounterSnapshot) Inc(int64) {
|
||||||
|
panic("Inc called on a CounterSnapshot")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Snapshot returns the snapshot.
|
||||||
|
func (c CounterSnapshot) Snapshot() Counter { return c }
|
||||||
|
|
||||||
|
// NilCounter is a no-op Counter.
|
||||||
|
type NilCounter struct{}
|
||||||
|
|
||||||
|
// Clear is a no-op.
|
||||||
|
func (NilCounter) Clear() {}
|
||||||
|
|
||||||
|
// Count is a no-op.
|
||||||
|
func (NilCounter) Count() int64 { return 0 }
|
||||||
|
|
||||||
|
// Dec is a no-op.
|
||||||
|
func (NilCounter) Dec(i int64) {}
|
||||||
|
|
||||||
|
// Inc is a no-op.
|
||||||
|
func (NilCounter) Inc(i int64) {}
|
||||||
|
|
||||||
|
// Snapshot is a no-op.
|
||||||
|
func (NilCounter) Snapshot() Counter { return NilCounter{} }
|
||||||
|
|
||||||
|
// StandardCounter is the standard implementation of a Counter and uses the
|
||||||
|
// sync/atomic package to manage a single int64 value.
|
||||||
|
type StandardCounter struct {
|
||||||
|
count int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear sets the counter to zero.
|
||||||
|
func (c *StandardCounter) Clear() {
|
||||||
|
atomic.StoreInt64(&c.count, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count returns the current count.
|
||||||
|
func (c *StandardCounter) Count() int64 {
|
||||||
|
return atomic.LoadInt64(&c.count)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dec decrements the counter by the given amount.
|
||||||
|
func (c *StandardCounter) Dec(i int64) {
|
||||||
|
atomic.AddInt64(&c.count, -i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inc increments the counter by the given amount.
|
||||||
|
func (c *StandardCounter) Inc(i int64) {
|
||||||
|
atomic.AddInt64(&c.count, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Snapshot returns a read-only copy of the counter.
|
||||||
|
func (c *StandardCounter) Snapshot() Counter {
|
||||||
|
return CounterSnapshot(c.Count())
|
||||||
|
}
|
||||||
+76
@@ -0,0 +1,76 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime/debug"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
debugMetrics struct {
|
||||||
|
GCStats struct {
|
||||||
|
LastGC Gauge
|
||||||
|
NumGC Gauge
|
||||||
|
Pause Histogram
|
||||||
|
//PauseQuantiles Histogram
|
||||||
|
PauseTotal Gauge
|
||||||
|
}
|
||||||
|
ReadGCStats Timer
|
||||||
|
}
|
||||||
|
gcStats debug.GCStats
|
||||||
|
)
|
||||||
|
|
||||||
|
// Capture new values for the Go garbage collector statistics exported in
|
||||||
|
// debug.GCStats. This is designed to be called as a goroutine.
|
||||||
|
func CaptureDebugGCStats(r Registry, d time.Duration) {
|
||||||
|
for _ = range time.Tick(d) {
|
||||||
|
CaptureDebugGCStatsOnce(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture new values for the Go garbage collector statistics exported in
|
||||||
|
// debug.GCStats. This is designed to be called in a background goroutine.
|
||||||
|
// Giving a registry which has not been given to RegisterDebugGCStats will
|
||||||
|
// panic.
|
||||||
|
//
|
||||||
|
// Be careful (but much less so) with this because debug.ReadGCStats calls
|
||||||
|
// the C function runtime·lock(runtime·mheap) which, while not a stop-the-world
|
||||||
|
// operation, isn't something you want to be doing all the time.
|
||||||
|
func CaptureDebugGCStatsOnce(r Registry) {
|
||||||
|
lastGC := gcStats.LastGC
|
||||||
|
t := time.Now()
|
||||||
|
debug.ReadGCStats(&gcStats)
|
||||||
|
debugMetrics.ReadGCStats.UpdateSince(t)
|
||||||
|
|
||||||
|
debugMetrics.GCStats.LastGC.Update(int64(gcStats.LastGC.UnixNano()))
|
||||||
|
debugMetrics.GCStats.NumGC.Update(int64(gcStats.NumGC))
|
||||||
|
if lastGC != gcStats.LastGC && 0 < len(gcStats.Pause) {
|
||||||
|
debugMetrics.GCStats.Pause.Update(int64(gcStats.Pause[0]))
|
||||||
|
}
|
||||||
|
//debugMetrics.GCStats.PauseQuantiles.Update(gcStats.PauseQuantiles)
|
||||||
|
debugMetrics.GCStats.PauseTotal.Update(int64(gcStats.PauseTotal))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register metrics for the Go garbage collector statistics exported in
|
||||||
|
// debug.GCStats. The metrics are named by their fully-qualified Go symbols,
|
||||||
|
// i.e. debug.GCStats.PauseTotal.
|
||||||
|
func RegisterDebugGCStats(r Registry) {
|
||||||
|
debugMetrics.GCStats.LastGC = NewGauge()
|
||||||
|
debugMetrics.GCStats.NumGC = NewGauge()
|
||||||
|
debugMetrics.GCStats.Pause = NewHistogram(NewExpDecaySample(1028, 0.015))
|
||||||
|
//debugMetrics.GCStats.PauseQuantiles = NewHistogram(NewExpDecaySample(1028, 0.015))
|
||||||
|
debugMetrics.GCStats.PauseTotal = NewGauge()
|
||||||
|
debugMetrics.ReadGCStats = NewTimer()
|
||||||
|
|
||||||
|
r.Register("debug.GCStats.LastGC", debugMetrics.GCStats.LastGC)
|
||||||
|
r.Register("debug.GCStats.NumGC", debugMetrics.GCStats.NumGC)
|
||||||
|
r.Register("debug.GCStats.Pause", debugMetrics.GCStats.Pause)
|
||||||
|
//r.Register("debug.GCStats.PauseQuantiles", debugMetrics.GCStats.PauseQuantiles)
|
||||||
|
r.Register("debug.GCStats.PauseTotal", debugMetrics.GCStats.PauseTotal)
|
||||||
|
r.Register("debug.ReadGCStats", debugMetrics.ReadGCStats)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate an initial slice for gcStats.Pause to avoid allocations during
|
||||||
|
// normal operation.
|
||||||
|
func init() {
|
||||||
|
gcStats.Pause = make([]time.Duration, 11)
|
||||||
|
}
|
||||||
+118
@@ -0,0 +1,118 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EWMAs continuously calculate an exponentially-weighted moving average
|
||||||
|
// based on an outside source of clock ticks.
|
||||||
|
type EWMA interface {
|
||||||
|
Rate() float64
|
||||||
|
Snapshot() EWMA
|
||||||
|
Tick()
|
||||||
|
Update(int64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEWMA constructs a new EWMA with the given alpha.
|
||||||
|
func NewEWMA(alpha float64) EWMA {
|
||||||
|
if UseNilMetrics {
|
||||||
|
return NilEWMA{}
|
||||||
|
}
|
||||||
|
return &StandardEWMA{alpha: alpha}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEWMA1 constructs a new EWMA for a one-minute moving average.
|
||||||
|
func NewEWMA1() EWMA {
|
||||||
|
return NewEWMA(1 - math.Exp(-5.0/60.0/1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEWMA5 constructs a new EWMA for a five-minute moving average.
|
||||||
|
func NewEWMA5() EWMA {
|
||||||
|
return NewEWMA(1 - math.Exp(-5.0/60.0/5))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEWMA15 constructs a new EWMA for a fifteen-minute moving average.
|
||||||
|
func NewEWMA15() EWMA {
|
||||||
|
return NewEWMA(1 - math.Exp(-5.0/60.0/15))
|
||||||
|
}
|
||||||
|
|
||||||
|
// EWMASnapshot is a read-only copy of another EWMA.
|
||||||
|
type EWMASnapshot float64
|
||||||
|
|
||||||
|
// Rate returns the rate of events per second at the time the snapshot was
|
||||||
|
// taken.
|
||||||
|
func (a EWMASnapshot) Rate() float64 { return float64(a) }
|
||||||
|
|
||||||
|
// Snapshot returns the snapshot.
|
||||||
|
func (a EWMASnapshot) Snapshot() EWMA { return a }
|
||||||
|
|
||||||
|
// Tick panics.
|
||||||
|
func (EWMASnapshot) Tick() {
|
||||||
|
panic("Tick called on an EWMASnapshot")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update panics.
|
||||||
|
func (EWMASnapshot) Update(int64) {
|
||||||
|
panic("Update called on an EWMASnapshot")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NilEWMA is a no-op EWMA.
|
||||||
|
type NilEWMA struct{}
|
||||||
|
|
||||||
|
// Rate is a no-op.
|
||||||
|
func (NilEWMA) Rate() float64 { return 0.0 }
|
||||||
|
|
||||||
|
// Snapshot is a no-op.
|
||||||
|
func (NilEWMA) Snapshot() EWMA { return NilEWMA{} }
|
||||||
|
|
||||||
|
// Tick is a no-op.
|
||||||
|
func (NilEWMA) Tick() {}
|
||||||
|
|
||||||
|
// Update is a no-op.
|
||||||
|
func (NilEWMA) Update(n int64) {}
|
||||||
|
|
||||||
|
// StandardEWMA is the standard implementation of an EWMA and tracks the number
|
||||||
|
// of uncounted events and processes them on each tick. It uses the
|
||||||
|
// sync/atomic package to manage uncounted events.
|
||||||
|
type StandardEWMA struct {
|
||||||
|
uncounted int64 // /!\ this should be the first member to ensure 64-bit alignment
|
||||||
|
alpha float64
|
||||||
|
rate float64
|
||||||
|
init bool
|
||||||
|
mutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rate returns the moving average rate of events per second.
|
||||||
|
func (a *StandardEWMA) Rate() float64 {
|
||||||
|
a.mutex.Lock()
|
||||||
|
defer a.mutex.Unlock()
|
||||||
|
return a.rate * float64(1e9)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Snapshot returns a read-only copy of the EWMA.
|
||||||
|
func (a *StandardEWMA) Snapshot() EWMA {
|
||||||
|
return EWMASnapshot(a.Rate())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tick ticks the clock to update the moving average. It assumes it is called
|
||||||
|
// every five seconds.
|
||||||
|
func (a *StandardEWMA) Tick() {
|
||||||
|
count := atomic.LoadInt64(&a.uncounted)
|
||||||
|
atomic.AddInt64(&a.uncounted, -count)
|
||||||
|
instantRate := float64(count) / float64(5e9)
|
||||||
|
a.mutex.Lock()
|
||||||
|
defer a.mutex.Unlock()
|
||||||
|
if a.init {
|
||||||
|
a.rate += a.alpha * (instantRate - a.rate)
|
||||||
|
} else {
|
||||||
|
a.init = true
|
||||||
|
a.rate = instantRate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update adds n uncounted events.
|
||||||
|
func (a *StandardEWMA) Update(n int64) {
|
||||||
|
atomic.AddInt64(&a.uncounted, n)
|
||||||
|
}
|
||||||
+84
@@ -0,0 +1,84 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
import "sync/atomic"
|
||||||
|
|
||||||
|
// Gauges hold an int64 value that can be set arbitrarily.
|
||||||
|
type Gauge interface {
|
||||||
|
Snapshot() Gauge
|
||||||
|
Update(int64)
|
||||||
|
Value() int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrRegisterGauge returns an existing Gauge or constructs and registers a
|
||||||
|
// new StandardGauge.
|
||||||
|
func GetOrRegisterGauge(name string, r Registry) Gauge {
|
||||||
|
if nil == r {
|
||||||
|
r = DefaultRegistry
|
||||||
|
}
|
||||||
|
return r.GetOrRegister(name, NewGauge).(Gauge)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGauge constructs a new StandardGauge.
|
||||||
|
func NewGauge() Gauge {
|
||||||
|
if UseNilMetrics {
|
||||||
|
return NilGauge{}
|
||||||
|
}
|
||||||
|
return &StandardGauge{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRegisteredGauge constructs and registers a new StandardGauge.
|
||||||
|
func NewRegisteredGauge(name string, r Registry) Gauge {
|
||||||
|
c := NewGauge()
|
||||||
|
if nil == r {
|
||||||
|
r = DefaultRegistry
|
||||||
|
}
|
||||||
|
r.Register(name, c)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// GaugeSnapshot is a read-only copy of another Gauge.
|
||||||
|
type GaugeSnapshot int64
|
||||||
|
|
||||||
|
// Snapshot returns the snapshot.
|
||||||
|
func (g GaugeSnapshot) Snapshot() Gauge { return g }
|
||||||
|
|
||||||
|
// Update panics.
|
||||||
|
func (GaugeSnapshot) Update(int64) {
|
||||||
|
panic("Update called on a GaugeSnapshot")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the value at the time the snapshot was taken.
|
||||||
|
func (g GaugeSnapshot) Value() int64 { return int64(g) }
|
||||||
|
|
||||||
|
// NilGauge is a no-op Gauge.
|
||||||
|
type NilGauge struct{}
|
||||||
|
|
||||||
|
// Snapshot is a no-op.
|
||||||
|
func (NilGauge) Snapshot() Gauge { return NilGauge{} }
|
||||||
|
|
||||||
|
// Update is a no-op.
|
||||||
|
func (NilGauge) Update(v int64) {}
|
||||||
|
|
||||||
|
// Value is a no-op.
|
||||||
|
func (NilGauge) Value() int64 { return 0 }
|
||||||
|
|
||||||
|
// StandardGauge is the standard implementation of a Gauge and uses the
|
||||||
|
// sync/atomic package to manage a single int64 value.
|
||||||
|
type StandardGauge struct {
|
||||||
|
value int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Snapshot returns a read-only copy of the gauge.
|
||||||
|
func (g *StandardGauge) Snapshot() Gauge {
|
||||||
|
return GaugeSnapshot(g.Value())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update updates the gauge's value.
|
||||||
|
func (g *StandardGauge) Update(v int64) {
|
||||||
|
atomic.StoreInt64(&g.value, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the gauge's current value.
|
||||||
|
func (g *StandardGauge) Value() int64 {
|
||||||
|
return atomic.LoadInt64(&g.value)
|
||||||
|
}
|
||||||
+91
@@ -0,0 +1,91 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
// GaugeFloat64s hold a float64 value that can be set arbitrarily.
|
||||||
|
type GaugeFloat64 interface {
|
||||||
|
Snapshot() GaugeFloat64
|
||||||
|
Update(float64)
|
||||||
|
Value() float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrRegisterGaugeFloat64 returns an existing GaugeFloat64 or constructs and registers a
|
||||||
|
// new StandardGaugeFloat64.
|
||||||
|
func GetOrRegisterGaugeFloat64(name string, r Registry) GaugeFloat64 {
|
||||||
|
if nil == r {
|
||||||
|
r = DefaultRegistry
|
||||||
|
}
|
||||||
|
return r.GetOrRegister(name, NewGaugeFloat64()).(GaugeFloat64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGaugeFloat64 constructs a new StandardGaugeFloat64.
|
||||||
|
func NewGaugeFloat64() GaugeFloat64 {
|
||||||
|
if UseNilMetrics {
|
||||||
|
return NilGaugeFloat64{}
|
||||||
|
}
|
||||||
|
return &StandardGaugeFloat64{
|
||||||
|
value: 0.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRegisteredGaugeFloat64 constructs and registers a new StandardGaugeFloat64.
|
||||||
|
func NewRegisteredGaugeFloat64(name string, r Registry) GaugeFloat64 {
|
||||||
|
c := NewGaugeFloat64()
|
||||||
|
if nil == r {
|
||||||
|
r = DefaultRegistry
|
||||||
|
}
|
||||||
|
r.Register(name, c)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// GaugeFloat64Snapshot is a read-only copy of another GaugeFloat64.
|
||||||
|
type GaugeFloat64Snapshot float64
|
||||||
|
|
||||||
|
// Snapshot returns the snapshot.
|
||||||
|
func (g GaugeFloat64Snapshot) Snapshot() GaugeFloat64 { return g }
|
||||||
|
|
||||||
|
// Update panics.
|
||||||
|
func (GaugeFloat64Snapshot) Update(float64) {
|
||||||
|
panic("Update called on a GaugeFloat64Snapshot")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the value at the time the snapshot was taken.
|
||||||
|
func (g GaugeFloat64Snapshot) Value() float64 { return float64(g) }
|
||||||
|
|
||||||
|
// NilGauge is a no-op Gauge.
|
||||||
|
type NilGaugeFloat64 struct{}
|
||||||
|
|
||||||
|
// Snapshot is a no-op.
|
||||||
|
func (NilGaugeFloat64) Snapshot() GaugeFloat64 { return NilGaugeFloat64{} }
|
||||||
|
|
||||||
|
// Update is a no-op.
|
||||||
|
func (NilGaugeFloat64) Update(v float64) {}
|
||||||
|
|
||||||
|
// Value is a no-op.
|
||||||
|
func (NilGaugeFloat64) Value() float64 { return 0.0 }
|
||||||
|
|
||||||
|
// StandardGaugeFloat64 is the standard implementation of a GaugeFloat64 and uses
|
||||||
|
// sync.Mutex to manage a single float64 value.
|
||||||
|
type StandardGaugeFloat64 struct {
|
||||||
|
mutex sync.Mutex
|
||||||
|
value float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Snapshot returns a read-only copy of the gauge.
|
||||||
|
func (g *StandardGaugeFloat64) Snapshot() GaugeFloat64 {
|
||||||
|
return GaugeFloat64Snapshot(g.Value())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update updates the gauge's value.
|
||||||
|
func (g *StandardGaugeFloat64) Update(v float64) {
|
||||||
|
g.mutex.Lock()
|
||||||
|
defer g.mutex.Unlock()
|
||||||
|
g.value = v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the gauge's current value.
|
||||||
|
func (g *StandardGaugeFloat64) Value() float64 {
|
||||||
|
g.mutex.Lock()
|
||||||
|
defer g.mutex.Unlock()
|
||||||
|
return g.value
|
||||||
|
}
|
||||||
+113
@@ -0,0 +1,113 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GraphiteConfig provides a container with configuration parameters for
|
||||||
|
// the Graphite exporter
|
||||||
|
type GraphiteConfig struct {
|
||||||
|
Addr *net.TCPAddr // Network address to connect to
|
||||||
|
Registry Registry // Registry to be exported
|
||||||
|
FlushInterval time.Duration // Flush interval
|
||||||
|
DurationUnit time.Duration // Time conversion unit for durations
|
||||||
|
Prefix string // Prefix to be prepended to metric names
|
||||||
|
Percentiles []float64 // Percentiles to export from timers and histograms
|
||||||
|
}
|
||||||
|
|
||||||
|
// Graphite is a blocking exporter function which reports metrics in r
|
||||||
|
// to a graphite server located at addr, flushing them every d duration
|
||||||
|
// and prepending metric names with prefix.
|
||||||
|
func Graphite(r Registry, d time.Duration, prefix string, addr *net.TCPAddr) {
|
||||||
|
GraphiteWithConfig(GraphiteConfig{
|
||||||
|
Addr: addr,
|
||||||
|
Registry: r,
|
||||||
|
FlushInterval: d,
|
||||||
|
DurationUnit: time.Nanosecond,
|
||||||
|
Prefix: prefix,
|
||||||
|
Percentiles: []float64{0.5, 0.75, 0.95, 0.99, 0.999},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GraphiteWithConfig is a blocking exporter function just like Graphite,
|
||||||
|
// but it takes a GraphiteConfig instead.
|
||||||
|
func GraphiteWithConfig(c GraphiteConfig) {
|
||||||
|
log.Printf("WARNING: This go-metrics client has been DEPRECATED! It has been moved to https://github.com/cyberdelia/go-metrics-graphite and will be removed from rcrowley/go-metrics on August 12th 2015")
|
||||||
|
for _ = range time.Tick(c.FlushInterval) {
|
||||||
|
if err := graphite(&c); nil != err {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GraphiteOnce performs a single submission to Graphite, returning a
|
||||||
|
// non-nil error on failed connections. This can be used in a loop
|
||||||
|
// similar to GraphiteWithConfig for custom error handling.
|
||||||
|
func GraphiteOnce(c GraphiteConfig) error {
|
||||||
|
log.Printf("WARNING: This go-metrics client has been DEPRECATED! It has been moved to https://github.com/cyberdelia/go-metrics-graphite and will be removed from rcrowley/go-metrics on August 12th 2015")
|
||||||
|
return graphite(&c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func graphite(c *GraphiteConfig) error {
|
||||||
|
now := time.Now().Unix()
|
||||||
|
du := float64(c.DurationUnit)
|
||||||
|
conn, err := net.DialTCP("tcp", nil, c.Addr)
|
||||||
|
if nil != err {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
w := bufio.NewWriter(conn)
|
||||||
|
c.Registry.Each(func(name string, i interface{}) {
|
||||||
|
switch metric := i.(type) {
|
||||||
|
case Counter:
|
||||||
|
fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, metric.Count(), now)
|
||||||
|
case Gauge:
|
||||||
|
fmt.Fprintf(w, "%s.%s.value %d %d\n", c.Prefix, name, metric.Value(), now)
|
||||||
|
case GaugeFloat64:
|
||||||
|
fmt.Fprintf(w, "%s.%s.value %f %d\n", c.Prefix, name, metric.Value(), now)
|
||||||
|
case Histogram:
|
||||||
|
h := metric.Snapshot()
|
||||||
|
ps := h.Percentiles(c.Percentiles)
|
||||||
|
fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, h.Count(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, h.Min(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, h.Max(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, h.Mean(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, h.StdDev(), now)
|
||||||
|
for psIdx, psKey := range c.Percentiles {
|
||||||
|
key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1)
|
||||||
|
fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now)
|
||||||
|
}
|
||||||
|
case Meter:
|
||||||
|
m := metric.Snapshot()
|
||||||
|
fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, m.Count(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, m.Rate1(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, m.Rate5(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, m.Rate15(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, m.RateMean(), now)
|
||||||
|
case Timer:
|
||||||
|
t := metric.Snapshot()
|
||||||
|
ps := t.Percentiles(c.Percentiles)
|
||||||
|
fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, t.Count(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, t.Min()/int64(du), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, t.Max()/int64(du), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, t.Mean()/du, now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, t.StdDev()/du, now)
|
||||||
|
for psIdx, psKey := range c.Percentiles {
|
||||||
|
key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1)
|
||||||
|
fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, t.Rate1(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, t.Rate5(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, t.Rate15(), now)
|
||||||
|
fmt.Fprintf(w, "%s.%s.mean-rate %.2f %d\n", c.Prefix, name, t.RateMean(), now)
|
||||||
|
}
|
||||||
|
w.Flush()
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
// Healthchecks hold an error value describing an arbitrary up/down status.
|
||||||
|
type Healthcheck interface {
|
||||||
|
Check()
|
||||||
|
Error() error
|
||||||
|
Healthy()
|
||||||
|
Unhealthy(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHealthcheck constructs a new Healthcheck which will use the given
|
||||||
|
// function to update its status.
|
||||||
|
func NewHealthcheck(f func(Healthcheck)) Healthcheck {
|
||||||
|
if UseNilMetrics {
|
||||||
|
return NilHealthcheck{}
|
||||||
|
}
|
||||||
|
return &StandardHealthcheck{nil, f}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NilHealthcheck is a no-op.
|
||||||
|
type NilHealthcheck struct{}
|
||||||
|
|
||||||
|
// Check is a no-op.
|
||||||
|
func (NilHealthcheck) Check() {}
|
||||||
|
|
||||||
|
// Error is a no-op.
|
||||||
|
func (NilHealthcheck) Error() error { return nil }
|
||||||
|
|
||||||
|
// Healthy is a no-op.
|
||||||
|
func (NilHealthcheck) Healthy() {}
|
||||||
|
|
||||||
|
// Unhealthy is a no-op.
|
||||||
|
func (NilHealthcheck) Unhealthy(error) {}
|
||||||
|
|
||||||
|
// StandardHealthcheck is the standard implementation of a Healthcheck and
|
||||||
|
// stores the status and a function to call to update the status.
|
||||||
|
type StandardHealthcheck struct {
|
||||||
|
err error
|
||||||
|
f func(Healthcheck)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check runs the healthcheck function to update the healthcheck's status.
|
||||||
|
func (h *StandardHealthcheck) Check() {
|
||||||
|
h.f(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns the healthcheck's status, which will be nil if it is healthy.
|
||||||
|
func (h *StandardHealthcheck) Error() error {
|
||||||
|
return h.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Healthy marks the healthcheck as healthy.
|
||||||
|
func (h *StandardHealthcheck) Healthy() {
|
||||||
|
h.err = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unhealthy marks the healthcheck as unhealthy. The error is stored and
|
||||||
|
// may be retrieved by the Error method.
|
||||||
|
func (h *StandardHealthcheck) Unhealthy(err error) {
|
||||||
|
h.err = err
|
||||||
|
}
|
||||||
+202
@@ -0,0 +1,202 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
// Histograms calculate distribution statistics from a series of int64 values.
|
||||||
|
type Histogram interface {
|
||||||
|
Clear()
|
||||||
|
Count() int64
|
||||||
|
Max() int64
|
||||||
|
Mean() float64
|
||||||
|
Min() int64
|
||||||
|
Percentile(float64) float64
|
||||||
|
Percentiles([]float64) []float64
|
||||||
|
Sample() Sample
|
||||||
|
Snapshot() Histogram
|
||||||
|
StdDev() float64
|
||||||
|
Sum() int64
|
||||||
|
Update(int64)
|
||||||
|
Variance() float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrRegisterHistogram returns an existing Histogram or constructs and
|
||||||
|
// registers a new StandardHistogram.
|
||||||
|
func GetOrRegisterHistogram(name string, r Registry, s Sample) Histogram {
|
||||||
|
if nil == r {
|
||||||
|
r = DefaultRegistry
|
||||||
|
}
|
||||||
|
return r.GetOrRegister(name, func() Histogram { return NewHistogram(s) }).(Histogram)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHistogram constructs a new StandardHistogram from a Sample.
|
||||||
|
func NewHistogram(s Sample) Histogram {
|
||||||
|
if UseNilMetrics {
|
||||||
|
return NilHistogram{}
|
||||||
|
}
|
||||||
|
return &StandardHistogram{sample: s}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRegisteredHistogram constructs and registers a new StandardHistogram from
|
||||||
|
// a Sample.
|
||||||
|
func NewRegisteredHistogram(name string, r Registry, s Sample) Histogram {
|
||||||
|
c := NewHistogram(s)
|
||||||
|
if nil == r {
|
||||||
|
r = DefaultRegistry
|
||||||
|
}
|
||||||
|
r.Register(name, c)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// HistogramSnapshot is a read-only copy of another Histogram.
|
||||||
|
type HistogramSnapshot struct {
|
||||||
|
sample *SampleSnapshot
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear panics.
|
||||||
|
func (*HistogramSnapshot) Clear() {
|
||||||
|
panic("Clear called on a HistogramSnapshot")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count returns the number of samples recorded at the time the snapshot was
|
||||||
|
// taken.
|
||||||
|
func (h *HistogramSnapshot) Count() int64 { return h.sample.Count() }
|
||||||
|
|
||||||
|
// Max returns the maximum value in the sample at the time the snapshot was
|
||||||
|
// taken.
|
||||||
|
func (h *HistogramSnapshot) Max() int64 { return h.sample.Max() }
|
||||||
|
|
||||||
|
// Mean returns the mean of the values in the sample at the time the snapshot
|
||||||
|
// was taken.
|
||||||
|
func (h *HistogramSnapshot) Mean() float64 { return h.sample.Mean() }
|
||||||
|
|
||||||
|
// Min returns the minimum value in the sample at the time the snapshot was
|
||||||
|
// taken.
|
||||||
|
func (h *HistogramSnapshot) Min() int64 { return h.sample.Min() }
|
||||||
|
|
||||||
|
// Percentile returns an arbitrary percentile of values in the sample at the
|
||||||
|
// time the snapshot was taken.
|
||||||
|
func (h *HistogramSnapshot) Percentile(p float64) float64 {
|
||||||
|
return h.sample.Percentile(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Percentiles returns a slice of arbitrary percentiles of values in the sample
|
||||||
|
// at the time the snapshot was taken.
|
||||||
|
func (h *HistogramSnapshot) Percentiles(ps []float64) []float64 {
|
||||||
|
return h.sample.Percentiles(ps)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sample returns the Sample underlying the histogram.
|
||||||
|
func (h *HistogramSnapshot) Sample() Sample { return h.sample }
|
||||||
|
|
||||||
|
// Snapshot returns the snapshot.
|
||||||
|
func (h *HistogramSnapshot) Snapshot() Histogram { return h }
|
||||||
|
|
||||||
|
// StdDev returns the standard deviation of the values in the sample at the
|
||||||
|
// time the snapshot was taken.
|
||||||
|
func (h *HistogramSnapshot) StdDev() float64 { return h.sample.StdDev() }
|
||||||
|
|
||||||
|
// Sum returns the sum in the sample at the time the snapshot was taken.
|
||||||
|
func (h *HistogramSnapshot) Sum() int64 { return h.sample.Sum() }
|
||||||
|
|
||||||
|
// Update panics.
|
||||||
|
func (*HistogramSnapshot) Update(int64) {
|
||||||
|
panic("Update called on a HistogramSnapshot")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variance returns the variance of inputs at the time the snapshot was taken.
|
||||||
|
func (h *HistogramSnapshot) Variance() float64 { return h.sample.Variance() }
|
||||||
|
|
||||||
|
// NilHistogram is a no-op Histogram.
|
||||||
|
type NilHistogram struct{}
|
||||||
|
|
||||||
|
// Clear is a no-op.
|
||||||
|
func (NilHistogram) Clear() {}
|
||||||
|
|
||||||
|
// Count is a no-op.
|
||||||
|
func (NilHistogram) Count() int64 { return 0 }
|
||||||
|
|
||||||
|
// Max is a no-op.
|
||||||
|
func (NilHistogram) Max() int64 { return 0 }
|
||||||
|
|
||||||
|
// Mean is a no-op.
|
||||||
|
func (NilHistogram) Mean() float64 { return 0.0 }
|
||||||
|
|
||||||
|
// Min is a no-op.
|
||||||
|
func (NilHistogram) Min() int64 { return 0 }
|
||||||
|
|
||||||
|
// Percentile is a no-op.
|
||||||
|
func (NilHistogram) Percentile(p float64) float64 { return 0.0 }
|
||||||
|
|
||||||
|
// Percentiles is a no-op.
|
||||||
|
func (NilHistogram) Percentiles(ps []float64) []float64 {
|
||||||
|
return make([]float64, len(ps))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sample is a no-op.
|
||||||
|
func (NilHistogram) Sample() Sample { return NilSample{} }
|
||||||
|
|
||||||
|
// Snapshot is a no-op.
|
||||||
|
func (NilHistogram) Snapshot() Histogram { return NilHistogram{} }
|
||||||
|
|
||||||
|
// StdDev is a no-op.
|
||||||
|
func (NilHistogram) StdDev() float64 { return 0.0 }
|
||||||
|
|
||||||
|
// Sum is a no-op.
|
||||||
|
func (NilHistogram) Sum() int64 { return 0 }
|
||||||
|
|
||||||
|
// Update is a no-op.
|
||||||
|
func (NilHistogram) Update(v int64) {}
|
||||||
|
|
||||||
|
// Variance is a no-op.
|
||||||
|
func (NilHistogram) Variance() float64 { return 0.0 }
|
||||||
|
|
||||||
|
// StandardHistogram is the standard implementation of a Histogram and uses a
|
||||||
|
// Sample to bound its memory use.
|
||||||
|
type StandardHistogram struct {
|
||||||
|
sample Sample
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear clears the histogram and its sample.
|
||||||
|
func (h *StandardHistogram) Clear() { h.sample.Clear() }
|
||||||
|
|
||||||
|
// Count returns the number of samples recorded since the histogram was last
|
||||||
|
// cleared.
|
||||||
|
func (h *StandardHistogram) Count() int64 { return h.sample.Count() }
|
||||||
|
|
||||||
|
// Max returns the maximum value in the sample.
|
||||||
|
func (h *StandardHistogram) Max() int64 { return h.sample.Max() }
|
||||||
|
|
||||||
|
// Mean returns the mean of the values in the sample.
|
||||||
|
func (h *StandardHistogram) Mean() float64 { return h.sample.Mean() }
|
||||||
|
|
||||||
|
// Min returns the minimum value in the sample.
|
||||||
|
func (h *StandardHistogram) Min() int64 { return h.sample.Min() }
|
||||||
|
|
||||||
|
// Percentile returns an arbitrary percentile of the values in the sample.
|
||||||
|
func (h *StandardHistogram) Percentile(p float64) float64 {
|
||||||
|
return h.sample.Percentile(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Percentiles returns a slice of arbitrary percentiles of the values in the
|
||||||
|
// sample.
|
||||||
|
func (h *StandardHistogram) Percentiles(ps []float64) []float64 {
|
||||||
|
return h.sample.Percentiles(ps)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sample returns the Sample underlying the histogram.
|
||||||
|
func (h *StandardHistogram) Sample() Sample { return h.sample }
|
||||||
|
|
||||||
|
// Snapshot returns a read-only copy of the histogram.
|
||||||
|
func (h *StandardHistogram) Snapshot() Histogram {
|
||||||
|
return &HistogramSnapshot{sample: h.sample.Snapshot().(*SampleSnapshot)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StdDev returns the standard deviation of the values in the sample.
|
||||||
|
func (h *StandardHistogram) StdDev() float64 { return h.sample.StdDev() }
|
||||||
|
|
||||||
|
// Sum returns the sum in the sample.
|
||||||
|
func (h *StandardHistogram) Sum() int64 { return h.sample.Sum() }
|
||||||
|
|
||||||
|
// Update samples a new value.
|
||||||
|
func (h *StandardHistogram) Update(v int64) { h.sample.Update(v) }
|
||||||
|
|
||||||
|
// Variance returns the variance of the values in the sample.
|
||||||
|
func (h *StandardHistogram) Variance() float64 { return h.sample.Variance() }
|
||||||
+83
@@ -0,0 +1,83 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MarshalJSON returns a byte slice containing a JSON representation of all
|
||||||
|
// the metrics in the Registry.
|
||||||
|
func (r *StandardRegistry) MarshalJSON() ([]byte, error) {
|
||||||
|
data := make(map[string]map[string]interface{})
|
||||||
|
r.Each(func(name string, i interface{}) {
|
||||||
|
values := make(map[string]interface{})
|
||||||
|
switch metric := i.(type) {
|
||||||
|
case Counter:
|
||||||
|
values["count"] = metric.Count()
|
||||||
|
case Gauge:
|
||||||
|
values["value"] = metric.Value()
|
||||||
|
case GaugeFloat64:
|
||||||
|
values["value"] = metric.Value()
|
||||||
|
case Healthcheck:
|
||||||
|
values["error"] = nil
|
||||||
|
metric.Check()
|
||||||
|
if err := metric.Error(); nil != err {
|
||||||
|
values["error"] = metric.Error().Error()
|
||||||
|
}
|
||||||
|
case Histogram:
|
||||||
|
h := metric.Snapshot()
|
||||||
|
ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
|
||||||
|
values["count"] = h.Count()
|
||||||
|
values["min"] = h.Min()
|
||||||
|
values["max"] = h.Max()
|
||||||
|
values["mean"] = h.Mean()
|
||||||
|
values["stddev"] = h.StdDev()
|
||||||
|
values["median"] = ps[0]
|
||||||
|
values["75%"] = ps[1]
|
||||||
|
values["95%"] = ps[2]
|
||||||
|
values["99%"] = ps[3]
|
||||||
|
values["99.9%"] = ps[4]
|
||||||
|
case Meter:
|
||||||
|
m := metric.Snapshot()
|
||||||
|
values["count"] = m.Count()
|
||||||
|
values["1m.rate"] = m.Rate1()
|
||||||
|
values["5m.rate"] = m.Rate5()
|
||||||
|
values["15m.rate"] = m.Rate15()
|
||||||
|
values["mean.rate"] = m.RateMean()
|
||||||
|
case Timer:
|
||||||
|
t := metric.Snapshot()
|
||||||
|
ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
|
||||||
|
values["count"] = t.Count()
|
||||||
|
values["min"] = t.Min()
|
||||||
|
values["max"] = t.Max()
|
||||||
|
values["mean"] = t.Mean()
|
||||||
|
values["stddev"] = t.StdDev()
|
||||||
|
values["median"] = ps[0]
|
||||||
|
values["75%"] = ps[1]
|
||||||
|
values["95%"] = ps[2]
|
||||||
|
values["99%"] = ps[3]
|
||||||
|
values["99.9%"] = ps[4]
|
||||||
|
values["1m.rate"] = t.Rate1()
|
||||||
|
values["5m.rate"] = t.Rate5()
|
||||||
|
values["15m.rate"] = t.Rate15()
|
||||||
|
values["mean.rate"] = t.RateMean()
|
||||||
|
}
|
||||||
|
data[name] = values
|
||||||
|
})
|
||||||
|
return json.Marshal(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteJSON writes metrics from the given registry periodically to the
|
||||||
|
// specified io.Writer as JSON.
|
||||||
|
func WriteJSON(r Registry, d time.Duration, w io.Writer) {
|
||||||
|
for _ = range time.Tick(d) {
|
||||||
|
WriteJSONOnce(r, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteJSONOnce writes metrics from the given registry to the specified
|
||||||
|
// io.Writer as JSON.
|
||||||
|
func WriteJSONOnce(r Registry, w io.Writer) {
|
||||||
|
json.NewEncoder(w).Encode(r)
|
||||||
|
}
|
||||||
+102
@@ -0,0 +1,102 @@
|
|||||||
|
package librato
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const Operations = "operations"
|
||||||
|
const OperationsShort = "ops"
|
||||||
|
|
||||||
|
type LibratoClient struct {
|
||||||
|
Email, Token string
|
||||||
|
}
|
||||||
|
|
||||||
|
// property strings
|
||||||
|
const (
|
||||||
|
// display attributes
|
||||||
|
Color = "color"
|
||||||
|
DisplayMax = "display_max"
|
||||||
|
DisplayMin = "display_min"
|
||||||
|
DisplayUnitsLong = "display_units_long"
|
||||||
|
DisplayUnitsShort = "display_units_short"
|
||||||
|
DisplayStacked = "display_stacked"
|
||||||
|
DisplayTransform = "display_transform"
|
||||||
|
// special gauge display attributes
|
||||||
|
SummarizeFunction = "summarize_function"
|
||||||
|
Aggregate = "aggregate"
|
||||||
|
|
||||||
|
// metric keys
|
||||||
|
Name = "name"
|
||||||
|
Period = "period"
|
||||||
|
Description = "description"
|
||||||
|
DisplayName = "display_name"
|
||||||
|
Attributes = "attributes"
|
||||||
|
|
||||||
|
// measurement keys
|
||||||
|
MeasureTime = "measure_time"
|
||||||
|
Source = "source"
|
||||||
|
Value = "value"
|
||||||
|
|
||||||
|
// special gauge keys
|
||||||
|
Count = "count"
|
||||||
|
Sum = "sum"
|
||||||
|
Max = "max"
|
||||||
|
Min = "min"
|
||||||
|
SumSquares = "sum_squares"
|
||||||
|
|
||||||
|
// batch keys
|
||||||
|
Counters = "counters"
|
||||||
|
Gauges = "gauges"
|
||||||
|
|
||||||
|
MetricsPostUrl = "https://metrics-api.librato.com/v1/metrics"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Measurement map[string]interface{}
|
||||||
|
type Metric map[string]interface{}
|
||||||
|
|
||||||
|
type Batch struct {
|
||||||
|
Gauges []Measurement `json:"gauges,omitempty"`
|
||||||
|
Counters []Measurement `json:"counters,omitempty"`
|
||||||
|
MeasureTime int64 `json:"measure_time"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *LibratoClient) PostMetrics(batch Batch) (err error) {
|
||||||
|
var (
|
||||||
|
js []byte
|
||||||
|
req *http.Request
|
||||||
|
resp *http.Response
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(batch.Counters) == 0 && len(batch.Gauges) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if js, err = json.Marshal(batch); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if req, err = http.NewRequest("POST", MetricsPostUrl, bytes.NewBuffer(js)); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.SetBasicAuth(self.Email, self.Token)
|
||||||
|
|
||||||
|
if resp, err = http.DefaultClient.Do(req); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
var body []byte
|
||||||
|
if body, err = ioutil.ReadAll(resp.Body); err != nil {
|
||||||
|
body = []byte(fmt.Sprintf("(could not fetch response body for error: %s)", err))
|
||||||
|
}
|
||||||
|
err = fmt.Errorf("Unable to post to Librato: %d %s %s", resp.StatusCode, resp.Status, string(body))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
+231
@@ -0,0 +1,231 @@
|
|||||||
|
package librato
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
"regexp"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rcrowley/go-metrics"
|
||||||
|
)
|
||||||
|
|
||||||
|
// a regexp for extracting the unit from time.Duration.String
|
||||||
|
var unitRegexp = regexp.MustCompile("[^\\d]+$")
|
||||||
|
|
||||||
|
// a helper that turns a time.Duration into librato display attributes for timer metrics
|
||||||
|
func translateTimerAttributes(d time.Duration) (attrs map[string]interface{}) {
|
||||||
|
attrs = make(map[string]interface{})
|
||||||
|
attrs[DisplayTransform] = fmt.Sprintf("x/%d", int64(d))
|
||||||
|
attrs[DisplayUnitsShort] = string(unitRegexp.Find([]byte(d.String())))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type Reporter struct {
|
||||||
|
Email, Token string
|
||||||
|
Source string
|
||||||
|
Interval time.Duration
|
||||||
|
Registry metrics.Registry
|
||||||
|
Percentiles []float64 // percentiles to report on histogram metrics
|
||||||
|
TimerAttributes map[string]interface{} // units in which timers will be displayed
|
||||||
|
intervalSec int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReporter(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) *Reporter {
|
||||||
|
return &Reporter{e, t, s, d, r, p, translateTimerAttributes(u), int64(d / time.Second)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Librato(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) {
|
||||||
|
NewReporter(r, d, e, t, s, p, u).Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Reporter) Run() {
|
||||||
|
log.Printf("WARNING: This client has been DEPRECATED! It has been moved to https://github.com/mihasya/go-metrics-librato and will be removed from rcrowley/go-metrics on August 5th 2015")
|
||||||
|
ticker := time.Tick(self.Interval)
|
||||||
|
metricsApi := &LibratoClient{self.Email, self.Token}
|
||||||
|
for now := range ticker {
|
||||||
|
var metrics Batch
|
||||||
|
var err error
|
||||||
|
if metrics, err = self.BuildRequest(now, self.Registry); err != nil {
|
||||||
|
log.Printf("ERROR constructing librato request body %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := metricsApi.PostMetrics(metrics); err != nil {
|
||||||
|
log.Printf("ERROR sending metrics to librato %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate sum of squares from data provided by metrics.Histogram
|
||||||
|
// see http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods
|
||||||
|
func sumSquares(s metrics.Sample) float64 {
|
||||||
|
count := float64(s.Count())
|
||||||
|
sumSquared := math.Pow(count*s.Mean(), 2)
|
||||||
|
sumSquares := math.Pow(count*s.StdDev(), 2) + sumSquared/count
|
||||||
|
if math.IsNaN(sumSquares) {
|
||||||
|
return 0.0
|
||||||
|
}
|
||||||
|
return sumSquares
|
||||||
|
}
|
||||||
|
func sumSquaresTimer(t metrics.Timer) float64 {
|
||||||
|
count := float64(t.Count())
|
||||||
|
sumSquared := math.Pow(count*t.Mean(), 2)
|
||||||
|
sumSquares := math.Pow(count*t.StdDev(), 2) + sumSquared/count
|
||||||
|
if math.IsNaN(sumSquares) {
|
||||||
|
return 0.0
|
||||||
|
}
|
||||||
|
return sumSquares
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Batch, err error) {
|
||||||
|
snapshot = Batch{
|
||||||
|
// coerce timestamps to a stepping fn so that they line up in Librato graphs
|
||||||
|
MeasureTime: (now.Unix() / self.intervalSec) * self.intervalSec,
|
||||||
|
Source: self.Source,
|
||||||
|
}
|
||||||
|
snapshot.Gauges = make([]Measurement, 0)
|
||||||
|
snapshot.Counters = make([]Measurement, 0)
|
||||||
|
histogramGaugeCount := 1 + len(self.Percentiles)
|
||||||
|
r.Each(func(name string, metric interface{}) {
|
||||||
|
measurement := Measurement{}
|
||||||
|
measurement[Period] = self.Interval.Seconds()
|
||||||
|
switch m := metric.(type) {
|
||||||
|
case metrics.Counter:
|
||||||
|
if m.Count() > 0 {
|
||||||
|
measurement[Name] = fmt.Sprintf("%s.%s", name, "count")
|
||||||
|
measurement[Value] = float64(m.Count())
|
||||||
|
measurement[Attributes] = map[string]interface{}{
|
||||||
|
DisplayUnitsLong: Operations,
|
||||||
|
DisplayUnitsShort: OperationsShort,
|
||||||
|
DisplayMin: "0",
|
||||||
|
}
|
||||||
|
snapshot.Counters = append(snapshot.Counters, measurement)
|
||||||
|
}
|
||||||
|
case metrics.Gauge:
|
||||||
|
measurement[Name] = name
|
||||||
|
measurement[Value] = float64(m.Value())
|
||||||
|
snapshot.Gauges = append(snapshot.Gauges, measurement)
|
||||||
|
case metrics.GaugeFloat64:
|
||||||
|
measurement[Name] = name
|
||||||
|
measurement[Value] = float64(m.Value())
|
||||||
|
snapshot.Gauges = append(snapshot.Gauges, measurement)
|
||||||
|
case metrics.Histogram:
|
||||||
|
if m.Count() > 0 {
|
||||||
|
gauges := make([]Measurement, histogramGaugeCount, histogramGaugeCount)
|
||||||
|
s := m.Sample()
|
||||||
|
measurement[Name] = fmt.Sprintf("%s.%s", name, "hist")
|
||||||
|
measurement[Count] = uint64(s.Count())
|
||||||
|
measurement[Max] = float64(s.Max())
|
||||||
|
measurement[Min] = float64(s.Min())
|
||||||
|
measurement[Sum] = float64(s.Sum())
|
||||||
|
measurement[SumSquares] = sumSquares(s)
|
||||||
|
gauges[0] = measurement
|
||||||
|
for i, p := range self.Percentiles {
|
||||||
|
gauges[i+1] = Measurement{
|
||||||
|
Name: fmt.Sprintf("%s.%.2f", measurement[Name], p),
|
||||||
|
Value: s.Percentile(p),
|
||||||
|
Period: measurement[Period],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
snapshot.Gauges = append(snapshot.Gauges, gauges...)
|
||||||
|
}
|
||||||
|
case metrics.Meter:
|
||||||
|
measurement[Name] = name
|
||||||
|
measurement[Value] = float64(m.Count())
|
||||||
|
snapshot.Counters = append(snapshot.Counters, measurement)
|
||||||
|
snapshot.Gauges = append(snapshot.Gauges,
|
||||||
|
Measurement{
|
||||||
|
Name: fmt.Sprintf("%s.%s", name, "1min"),
|
||||||
|
Value: m.Rate1(),
|
||||||
|
Period: int64(self.Interval.Seconds()),
|
||||||
|
Attributes: map[string]interface{}{
|
||||||
|
DisplayUnitsLong: Operations,
|
||||||
|
DisplayUnitsShort: OperationsShort,
|
||||||
|
DisplayMin: "0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Measurement{
|
||||||
|
Name: fmt.Sprintf("%s.%s", name, "5min"),
|
||||||
|
Value: m.Rate5(),
|
||||||
|
Period: int64(self.Interval.Seconds()),
|
||||||
|
Attributes: map[string]interface{}{
|
||||||
|
DisplayUnitsLong: Operations,
|
||||||
|
DisplayUnitsShort: OperationsShort,
|
||||||
|
DisplayMin: "0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Measurement{
|
||||||
|
Name: fmt.Sprintf("%s.%s", name, "15min"),
|
||||||
|
Value: m.Rate15(),
|
||||||
|
Period: int64(self.Interval.Seconds()),
|
||||||
|
Attributes: map[string]interface{}{
|
||||||
|
DisplayUnitsLong: Operations,
|
||||||
|
DisplayUnitsShort: OperationsShort,
|
||||||
|
DisplayMin: "0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
case metrics.Timer:
|
||||||
|
measurement[Name] = name
|
||||||
|
measurement[Value] = float64(m.Count())
|
||||||
|
snapshot.Counters = append(snapshot.Counters, measurement)
|
||||||
|
if m.Count() > 0 {
|
||||||
|
libratoName := fmt.Sprintf("%s.%s", name, "timer.mean")
|
||||||
|
gauges := make([]Measurement, histogramGaugeCount, histogramGaugeCount)
|
||||||
|
gauges[0] = Measurement{
|
||||||
|
Name: libratoName,
|
||||||
|
Count: uint64(m.Count()),
|
||||||
|
Sum: m.Mean() * float64(m.Count()),
|
||||||
|
Max: float64(m.Max()),
|
||||||
|
Min: float64(m.Min()),
|
||||||
|
SumSquares: sumSquaresTimer(m),
|
||||||
|
Period: int64(self.Interval.Seconds()),
|
||||||
|
Attributes: self.TimerAttributes,
|
||||||
|
}
|
||||||
|
for i, p := range self.Percentiles {
|
||||||
|
gauges[i+1] = Measurement{
|
||||||
|
Name: fmt.Sprintf("%s.timer.%2.0f", name, p*100),
|
||||||
|
Value: m.Percentile(p),
|
||||||
|
Period: int64(self.Interval.Seconds()),
|
||||||
|
Attributes: self.TimerAttributes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
snapshot.Gauges = append(snapshot.Gauges, gauges...)
|
||||||
|
snapshot.Gauges = append(snapshot.Gauges,
|
||||||
|
Measurement{
|
||||||
|
Name: fmt.Sprintf("%s.%s", name, "rate.1min"),
|
||||||
|
Value: m.Rate1(),
|
||||||
|
Period: int64(self.Interval.Seconds()),
|
||||||
|
Attributes: map[string]interface{}{
|
||||||
|
DisplayUnitsLong: Operations,
|
||||||
|
DisplayUnitsShort: OperationsShort,
|
||||||
|
DisplayMin: "0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Measurement{
|
||||||
|
Name: fmt.Sprintf("%s.%s", name, "rate.5min"),
|
||||||
|
Value: m.Rate5(),
|
||||||
|
Period: int64(self.Interval.Seconds()),
|
||||||
|
Attributes: map[string]interface{}{
|
||||||
|
DisplayUnitsLong: Operations,
|
||||||
|
DisplayUnitsShort: OperationsShort,
|
||||||
|
DisplayMin: "0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Measurement{
|
||||||
|
Name: fmt.Sprintf("%s.%s", name, "rate.15min"),
|
||||||
|
Value: m.Rate15(),
|
||||||
|
Period: int64(self.Interval.Seconds()),
|
||||||
|
Attributes: map[string]interface{}{
|
||||||
|
DisplayUnitsLong: Operations,
|
||||||
|
DisplayUnitsShort: OperationsShort,
|
||||||
|
DisplayMin: "0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
+70
@@ -0,0 +1,70 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Output each metric in the given registry periodically using the given
|
||||||
|
// logger.
|
||||||
|
func Log(r Registry, d time.Duration, l *log.Logger) {
|
||||||
|
for _ = range time.Tick(d) {
|
||||||
|
r.Each(func(name string, i interface{}) {
|
||||||
|
switch metric := i.(type) {
|
||||||
|
case Counter:
|
||||||
|
l.Printf("counter %s\n", name)
|
||||||
|
l.Printf(" count: %9d\n", metric.Count())
|
||||||
|
case Gauge:
|
||||||
|
l.Printf("gauge %s\n", name)
|
||||||
|
l.Printf(" value: %9d\n", metric.Value())
|
||||||
|
case GaugeFloat64:
|
||||||
|
l.Printf("gauge %s\n", name)
|
||||||
|
l.Printf(" value: %f\n", metric.Value())
|
||||||
|
case Healthcheck:
|
||||||
|
metric.Check()
|
||||||
|
l.Printf("healthcheck %s\n", name)
|
||||||
|
l.Printf(" error: %v\n", metric.Error())
|
||||||
|
case Histogram:
|
||||||
|
h := metric.Snapshot()
|
||||||
|
ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
|
||||||
|
l.Printf("histogram %s\n", name)
|
||||||
|
l.Printf(" count: %9d\n", h.Count())
|
||||||
|
l.Printf(" min: %9d\n", h.Min())
|
||||||
|
l.Printf(" max: %9d\n", h.Max())
|
||||||
|
l.Printf(" mean: %12.2f\n", h.Mean())
|
||||||
|
l.Printf(" stddev: %12.2f\n", h.StdDev())
|
||||||
|
l.Printf(" median: %12.2f\n", ps[0])
|
||||||
|
l.Printf(" 75%%: %12.2f\n", ps[1])
|
||||||
|
l.Printf(" 95%%: %12.2f\n", ps[2])
|
||||||
|
l.Printf(" 99%%: %12.2f\n", ps[3])
|
||||||
|
l.Printf(" 99.9%%: %12.2f\n", ps[4])
|
||||||
|
case Meter:
|
||||||
|
m := metric.Snapshot()
|
||||||
|
l.Printf("meter %s\n", name)
|
||||||
|
l.Printf(" count: %9d\n", m.Count())
|
||||||
|
l.Printf(" 1-min rate: %12.2f\n", m.Rate1())
|
||||||
|
l.Printf(" 5-min rate: %12.2f\n", m.Rate5())
|
||||||
|
l.Printf(" 15-min rate: %12.2f\n", m.Rate15())
|
||||||
|
l.Printf(" mean rate: %12.2f\n", m.RateMean())
|
||||||
|
case Timer:
|
||||||
|
t := metric.Snapshot()
|
||||||
|
ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
|
||||||
|
l.Printf("timer %s\n", name)
|
||||||
|
l.Printf(" count: %9d\n", t.Count())
|
||||||
|
l.Printf(" min: %9d\n", t.Min())
|
||||||
|
l.Printf(" max: %9d\n", t.Max())
|
||||||
|
l.Printf(" mean: %12.2f\n", t.Mean())
|
||||||
|
l.Printf(" stddev: %12.2f\n", t.StdDev())
|
||||||
|
l.Printf(" median: %12.2f\n", ps[0])
|
||||||
|
l.Printf(" 75%%: %12.2f\n", ps[1])
|
||||||
|
l.Printf(" 95%%: %12.2f\n", ps[2])
|
||||||
|
l.Printf(" 99%%: %12.2f\n", ps[3])
|
||||||
|
l.Printf(" 99.9%%: %12.2f\n", ps[4])
|
||||||
|
l.Printf(" 1-min rate: %12.2f\n", t.Rate1())
|
||||||
|
l.Printf(" 5-min rate: %12.2f\n", t.Rate5())
|
||||||
|
l.Printf(" 15-min rate: %12.2f\n", t.Rate15())
|
||||||
|
l.Printf(" mean rate: %12.2f\n", t.RateMean())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Loaded 100 of 888 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user