Compare commits
757
Commits
v0.11.0
...
v0.12.0-beta3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
240e7b0835 | ||
|
|
baf5191433 | ||
|
|
2645e87766 | ||
|
|
ec8bc02d33 | ||
|
|
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 | ||
|
|
e7db264803 | ||
|
|
3d8a71fdb2 | ||
|
|
6277c0595c | ||
|
|
aa9eda1979 | ||
|
|
1d76efcbcd | ||
|
|
34c2c1ec16 | ||
|
|
bf7fea9a0a | ||
|
|
fdf15f3ca3 | ||
|
|
1cb5875b20 | ||
|
|
1a59a5478f | ||
|
|
5750443371 | ||
|
|
f9132cae85 | ||
|
|
17149741a7 | ||
|
|
d2ec40bb67 | ||
|
|
1a4398cc55 | ||
|
|
529d91fb9d | ||
|
|
108b4e2e10 | ||
|
|
cd0cce4195 | ||
|
|
9f1a72ec88 | ||
|
|
a10c621e33 | ||
|
|
2e2d479103 | ||
|
|
35f0e355bf | ||
|
|
4395711d26 | ||
|
|
aba915037f | ||
|
|
442e93d3fc | ||
|
|
3450b5f80c | ||
|
|
f76b5d8002 | ||
|
|
7e54868206 | ||
|
|
d84a8e6404 | ||
|
|
4833b6085c | ||
|
|
2ceaca8828 | ||
|
|
d02158c0ef | ||
|
|
6213c4f2cd | ||
|
|
cd34eea017 | ||
|
|
7a0a702ec0 | ||
|
|
d9ed8e125e | ||
|
|
36708a5067 | ||
|
|
8c32955da1 | ||
|
|
c111ed4b20 | ||
|
|
a3ea9427d1 | ||
|
|
09b534b8a3 | ||
|
|
190c61ba2f | ||
|
|
dc71ec734d | ||
|
|
3af96e50bd | ||
|
|
ddc56c8a0d | ||
|
|
e0da2764c9 | ||
|
|
ad29093ac1 | ||
|
|
28610a9a42 | ||
|
|
65eb528e2d | ||
|
|
ec9d68960f | ||
|
|
c618eba9a9 | ||
|
|
1ef8378a30 | ||
|
|
43289103cb | ||
|
|
1bc5632771 | ||
|
|
4b488a2d28 | ||
|
|
f28367bcfc |
@@ -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
-7
@@ -1,18 +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*
|
|
||||||
.idea/
|
|
||||||
.jshintrc
|
.jshintrc
|
||||||
coverage.out
|
coverage.out
|
||||||
files/pidx
|
files/pidx
|
||||||
bin
|
bin
|
||||||
perfstats*.csv
|
perfstats*.csv
|
||||||
coverage.xml
|
coverage.xml
|
||||||
!gui/scripts/syncthing
|
syncthing.sig
|
||||||
.DS_Store
|
|
||||||
syncthing.md5
|
|
||||||
syncthing.exe.md5
|
|
||||||
RELEASE
|
RELEASE
|
||||||
|
deb
|
||||||
|
|||||||
@@ -1,29 +1,40 @@
|
|||||||
# This is the official list of Syncthing authors for copyright purposes.
|
# This is the official list of Syncthing authors for copyright purposes.
|
||||||
|
|
||||||
Aaron Bieber <qbit@deftly.net>
|
Aaron Bieber <qbit@deftly.net>
|
||||||
|
Adam Piggott <aD@simplypeachy.co.uk> <simplypeachy@users.noreply.github.com>
|
||||||
Alexander Graf <register-github@alex-graf.de>
|
Alexander Graf <register-github@alex-graf.de>
|
||||||
Andrew Dunham <andrew@du.nham.ca>
|
Andrew Dunham <andrew@du.nham.ca>
|
||||||
Audrius Butkevicius <audrius.butkevicius@gmail.com>
|
Antony Male <antony.male@gmail.com>
|
||||||
Arthur Axel fREW Schmidt <frew@afoolishmanifesto.com> <frioux@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 Curthoys <ben@bencurthoys.com>
|
||||||
Ben Schulz <ueomkail@gmail.com> <uok@users.noreply.github.com>
|
Ben Schulz <ueomkail@gmail.com> <uok@users.noreply.github.com>
|
||||||
Ben Sidhom <bsidhom@gmail.com>
|
Ben Sidhom <bsidhom@gmail.com>
|
||||||
Brandon Philips <brandon@ifup.org>
|
Brandon Philips <brandon@ifup.org>
|
||||||
Brendan Long <self@brendanlong.com>
|
Brendan Long <self@brendanlong.com>
|
||||||
|
Brian R. Becker <brbecker@gmail.com>
|
||||||
Caleb Callaway <enlightened.despot@gmail.com>
|
Caleb Callaway <enlightened.despot@gmail.com>
|
||||||
Carsten Hagemann <moter8@gmail.com>
|
Carsten Hagemann <moter8@gmail.com>
|
||||||
Cathryne Linenweaver <cathryne.linenweaver@gmail.com> <Cathryne@users.noreply.github.com>
|
Cathryne Linenweaver <cathryne.linenweaver@gmail.com> <Cathryne@users.noreply.github.com>
|
||||||
|
Chris Howie <me@chrishowie.com>
|
||||||
Chris Joel <chris@scriptolo.gy>
|
Chris Joel <chris@scriptolo.gy>
|
||||||
Colin Kennedy <moshen.colin@gmail.com>
|
Colin Kennedy <moshen.colin@gmail.com>
|
||||||
|
Daniel Bergmann <dan.arne.bergmann@gmail.com> <brgmnn@users.noreply.github.com>
|
||||||
Daniel Martí <mvdan@mvdan.cc>
|
Daniel Martí <mvdan@mvdan.cc>
|
||||||
|
Denis A. <denisva@gmail.com>
|
||||||
Dennis Wilson <dw@risu.io>
|
Dennis Wilson <dw@risu.io>
|
||||||
Dominik Heidler <dominik@heidler.eu>
|
Dominik Heidler <dominik@heidler.eu>
|
||||||
|
Elias Jarlebring <jarlebring@gmail.com>
|
||||||
Emil Hessman <emil@hessman.se>
|
Emil Hessman <emil@hessman.se>
|
||||||
|
Erik Meitner <e.meitner@willystreet.coop>
|
||||||
Federico Castagnini <federico.castagnini@gmail.com>
|
Federico Castagnini <federico.castagnini@gmail.com>
|
||||||
Felix Ableitner <me@nutomic.com>
|
Felix Ableitner <me@nutomic.com>
|
||||||
Felix Unterpaintner <bigbear2nd@gmail.com>
|
Felix Unterpaintner <bigbear2nd@gmail.com>
|
||||||
Francois-Xavier Gsell <fxgsell@gmail.com>
|
Francois-Xavier Gsell <fxgsell@gmail.com>
|
||||||
|
Frank Isemann <frank@isemann.name>
|
||||||
Gilli Sigurdsson <gilli@vx.is>
|
Gilli Sigurdsson <gilli@vx.is>
|
||||||
|
Jacek Szafarkiewicz <szafar@linux.pl>
|
||||||
Jakob Borg <jakob@nym.se>
|
Jakob Borg <jakob@nym.se>
|
||||||
James Patterson <jamespatterson@operamail.com> <jpjp@users.noreply.github.com>
|
James Patterson <jamespatterson@operamail.com> <jpjp@users.noreply.github.com>
|
||||||
Jaroslav Malec <dzardacz@gmail.com>
|
Jaroslav Malec <dzardacz@gmail.com>
|
||||||
@@ -33,9 +44,12 @@ Johan Vromans <jvromans@squirrel.nl>
|
|||||||
Karol Różycki <rozycki.karol@gmail.com>
|
Karol Różycki <rozycki.karol@gmail.com>
|
||||||
Ken'ichi Kamada <kamada@nanohz.org>
|
Ken'ichi Kamada <kamada@nanohz.org>
|
||||||
Lode Hoste <zillode@zillode.be>
|
Lode Hoste <zillode@zillode.be>
|
||||||
Marcin Dziadus <dziadus.marcin@gmail.com>
|
Lord Landon Agahnim <lordlandon@gmail.com>
|
||||||
Marc Laporte <marc@marclaporte.com> <marc@laporte.name>
|
Marc Laporte <marc@marclaporte.com> <marc@laporte.name>
|
||||||
Marc Pujol <kilburn@la3.org>
|
Marc Pujol <kilburn@la3.org>
|
||||||
|
Marcin Dziadus <dziadus.marcin@gmail.com>
|
||||||
|
Mateon1 <matin1111@wp.pl>
|
||||||
|
Matt Burke <mburke@amplify.com>
|
||||||
Michael Jephcote <rewt0r@gmx.com> <Rewt0r@users.noreply.github.com>
|
Michael Jephcote <rewt0r@gmx.com> <Rewt0r@users.noreply.github.com>
|
||||||
Michael Tilli <pyfisch@gmail.com>
|
Michael Tilli <pyfisch@gmail.com>
|
||||||
Pascal Jungblut <github@pascalj.com> <mail@pascal-jungblut.com>
|
Pascal Jungblut <github@pascalj.com> <mail@pascal-jungblut.com>
|
||||||
@@ -45,7 +59,7 @@ Phill Luby <phill.luby@newredo.com>
|
|||||||
Piotr Bejda <piotrb10@gmail.com>
|
Piotr Bejda <piotrb10@gmail.com>
|
||||||
Ryan Sullivan <kayoticsully@gmail.com>
|
Ryan Sullivan <kayoticsully@gmail.com>
|
||||||
Sergey Mishin <ralder@yandex.ru>
|
Sergey Mishin <ralder@yandex.ru>
|
||||||
Stefan Tatschner <stefan@sevenbyte.org>
|
Stefan Tatschner <stefan@sevenbyte.org> <rumpelsepp@sevenbyte.org>
|
||||||
Tim Abell <tim@timwise.co.uk>
|
Tim Abell <tim@timwise.co.uk>
|
||||||
Tobias Nygren <tnn@nygren.pp.se>
|
Tobias Nygren <tnn@nygren.pp.se>
|
||||||
Tomas Cerveny <kozec@kozec.com>
|
Tomas Cerveny <kozec@kozec.com>
|
||||||
|
|||||||
+7
-95
@@ -32,64 +32,15 @@ latest info on Transifex.
|
|||||||
## Contributing Code
|
## Contributing Code
|
||||||
|
|
||||||
Every contribution is welcome. If you want to contribute but are unsure
|
Every contribution is welcome. If you want to contribute but are unsure
|
||||||
where to start, any open issues are fair game! Be prepared for a
|
where to start, any open issues are fair game! See the [Contribution
|
||||||
[certain amount of review](https://github.com/syncthing/syncthing/wiki/FAQ#why-are-you-being-so-hard-on-my-pull-request);
|
Guidelines](http://docs.syncthing.net/dev/contributing.html) for the full
|
||||||
it's all in the name of quality. :) Following the points below will make this
|
story on committing code.
|
||||||
a smoother process.
|
|
||||||
|
|
||||||
Individuals making significant and valuable contributions are given
|
## Contributing Documentation
|
||||||
commit-access to the project. If you make a significant contribution and
|
|
||||||
are not considered for commit-access, please contact any of the
|
|
||||||
Syncthing core team members.
|
|
||||||
|
|
||||||
All nontrivial contributions should go through the pull request
|
Updates to the [documentation site](http://docs.syncthing.net/) can be
|
||||||
mechanism for internal review. Determining what is "nontrivial" is left
|
made as pull requests on the [documentation
|
||||||
at the discretion of the contributor.
|
repository](https://github.com/syncthing/docs).
|
||||||
|
|
||||||
### Authorship
|
|
||||||
|
|
||||||
All code authors are listed in the AUTHORS file. Commits must be made
|
|
||||||
with the same name and email as listed in the AUTHORS file. To
|
|
||||||
accomplish this, ensure that your git configuration is set correctly
|
|
||||||
prior to making your first commit;
|
|
||||||
|
|
||||||
$ git config --global user.name "Jane Doe"
|
|
||||||
$ git config --global user.email janedoe@example.com
|
|
||||||
|
|
||||||
You must be reachable on the given email address. If you do not wish to
|
|
||||||
use your real name for whatever reason, using a nickname or pseudonym is
|
|
||||||
perfectly acceptable.
|
|
||||||
|
|
||||||
### Core Team
|
|
||||||
|
|
||||||
The Syncthing core team currently consists of the following members;
|
|
||||||
|
|
||||||
- Jakob Borg (@calmh)
|
|
||||||
- Audrius Butkevicius (@AudriusButkevicius)
|
|
||||||
|
|
||||||
## Coding Style
|
|
||||||
|
|
||||||
- Follow the conventions laid out in [Effective Go](https://golang.org/doc/effective_go.html)
|
|
||||||
as much as makes sense.
|
|
||||||
|
|
||||||
- All text files use Unix line endings.
|
|
||||||
|
|
||||||
- Each commit should be `go fmt` clean.
|
|
||||||
|
|
||||||
- The commit message subject should be a single short sentence
|
|
||||||
describing the change, starting with a capital letter.
|
|
||||||
|
|
||||||
- Commits that resolve an existing issue must include the issue number
|
|
||||||
as `(fixes #123)` at the end of the commit message subject.
|
|
||||||
|
|
||||||
- Imports are grouped per `goimports` standard; that is, standard
|
|
||||||
library first, then third party libraries after a blank line.
|
|
||||||
|
|
||||||
- A contribution solving a single issue or introducing a single new
|
|
||||||
feature should probably be a single commit based on the current
|
|
||||||
`master` branch. You may be asked to "rebase" or "squash" your pull
|
|
||||||
request to make sure this is the case, especially if there have been
|
|
||||||
amendments during review.
|
|
||||||
|
|
||||||
## Licensing
|
## Licensing
|
||||||
|
|
||||||
@@ -99,42 +50,3 @@ 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 AUTHORS file. You are welcome to
|
|
||||||
add yourself as a separate commit in your first pull request.
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
[See the documentation](https://github.com/syncthing/syncthing/wiki/Building)
|
|
||||||
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!
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
[Over here!](https://github.com/syncthing/syncthing/wiki)
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
MPLv2
|
|
||||||
|
|||||||
Generated
+25
-20
@@ -1,17 +1,17 @@
|
|||||||
{
|
{
|
||||||
"ImportPath": "github.com/syncthing/syncthing",
|
"ImportPath": "github.com/syncthing/syncthing",
|
||||||
"GoVersion": "go1.4",
|
"GoVersion": "go1.5.1",
|
||||||
"Packages": [
|
"Packages": [
|
||||||
"./cmd/..."
|
"./cmd/..."
|
||||||
],
|
],
|
||||||
"Deps": [
|
"Deps": [
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/bkaradzic/go-lz4",
|
"ImportPath": "github.com/bkaradzic/go-lz4",
|
||||||
"Rev": "93a831dcee242be64a9cc9803dda84af25932de7"
|
"Rev": "74ddf82598bc4745b965729e9c6a463bedd33049"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/calmh/logger",
|
"ImportPath": "github.com/calmh/du",
|
||||||
"Rev": "f50d32b313bec2933a3e1049f7416a29f3413d29"
|
"Rev": "3c0690cca16228b97741327b1b6781397afbdb24"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/calmh/luhn",
|
"ImportPath": "github.com/calmh/luhn",
|
||||||
@@ -21,29 +21,26 @@
|
|||||||
"ImportPath": "github.com/calmh/xdr",
|
"ImportPath": "github.com/calmh/xdr",
|
||||||
"Rev": "5f7208e86762911861c94f1849eddbfc0a60cbf0"
|
"Rev": "5f7208e86762911861c94f1849eddbfc0a60cbf0"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/golang/snappy",
|
||||||
|
"Rev": "723cc1e459b8eea2dea4583200fd60757d40097a"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/juju/ratelimit",
|
"ImportPath": "github.com/juju/ratelimit",
|
||||||
"Rev": "c5abe513796336ee2869745bff0638508450e9c5"
|
"Rev": "772f5c38e468398c4511514f4f6aa9a4185bc0a0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/kardianos/osext",
|
"ImportPath": "github.com/kardianos/osext",
|
||||||
"Rev": "efacde03154693404c65e7aa7d461ac9014acd0c"
|
"Rev": "6e7f843663477789fac7c02def0d0909e969b4e5"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/syncthing/protocol",
|
|
||||||
"Rev": "e7db2648034fb71b051902a02bc25d4468ed492e"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/syndtr/goleveldb/leveldb",
|
"ImportPath": "github.com/syndtr/goleveldb/leveldb",
|
||||||
"Rev": "87e4e645d80ae9c537e8f2dee52b28036a5dd75e"
|
"Rev": "1a9d62f03ea92815b46fcaab357cfd4df264b1a0"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/syndtr/gosnappy/snappy",
|
|
||||||
"Rev": "156a073208e131d7d2e212cb749feae7c339e846"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/thejerf/suture",
|
"ImportPath": "github.com/thejerf/suture",
|
||||||
"Rev": "ff19fb384c3fe30f42717967eaa69da91e5f317c"
|
"Comment": "v1.0.1",
|
||||||
|
"Rev": "99c1f2d613756768fc4299acd9dc621e11ed3fd7"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/vitrun/qart/coding",
|
"ImportPath": "github.com/vitrun/qart/coding",
|
||||||
@@ -59,19 +56,27 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/crypto/bcrypt",
|
"ImportPath": "golang.org/x/crypto/bcrypt",
|
||||||
"Rev": "c57d4a71915a248dbad846d60825145062b4c18e"
|
"Rev": "81bf7719a6b7ce9b665598222362b50122dfc13b"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/crypto/blowfish",
|
"ImportPath": "golang.org/x/crypto/blowfish",
|
||||||
"Rev": "c57d4a71915a248dbad846d60825145062b4c18e"
|
"Rev": "81bf7719a6b7ce9b665598222362b50122dfc13b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/net/internal/iana",
|
||||||
|
"Rev": "db8e4de5b2d6653f66aea53094624468caad15d2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/net/ipv6",
|
||||||
|
"Rev": "db8e4de5b2d6653f66aea53094624468caad15d2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/text/transform",
|
"ImportPath": "golang.org/x/text/transform",
|
||||||
"Rev": "2076e9cab4147459c82bc81169e46c139d358547"
|
"Rev": "723492b65e225eafcba054e76ba18bb9c5ac1ea2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/text/unicode/norm",
|
"ImportPath": "golang.org/x/text/unicode/norm",
|
||||||
"Rev": "2076e9cab4147459c82bc81169e46c139d358547"
|
"Rev": "723492b65e225eafcba054e76ba18bb9c5ac1ea2"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+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
|
||||||
|
}
|
||||||
+6
-1
@@ -141,7 +141,7 @@ func Decode(dst, src []byte) ([]byte, error) {
|
|||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,7 +179,12 @@ func Decode(dst, src []byte) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
literal := d.dpos - d.ref
|
literal := d.dpos - d.ref
|
||||||
|
|
||||||
if literal < 4 {
|
if literal < 4 {
|
||||||
|
if int(d.dpos+4) > len(d.dst) {
|
||||||
|
return nil, ErrCorrupt
|
||||||
|
}
|
||||||
|
|
||||||
d.cp(4, decr[literal])
|
d.cp(4, decr[literal])
|
||||||
} else {
|
} else {
|
||||||
length += 4
|
length += 4
|
||||||
|
|||||||
+4
-2
@@ -25,8 +25,10 @@
|
|||||||
|
|
||||||
package lz4
|
package lz4
|
||||||
|
|
||||||
import "encoding/binary"
|
import (
|
||||||
import "errors"
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
minMatch = 4
|
minMatch = 4
|
||||||
|
|||||||
+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
|
||||||
|
}
|
||||||
-15
@@ -1,15 +0,0 @@
|
|||||||
logger
|
|
||||||
======
|
|
||||||
|
|
||||||
A small wrapper around `log` to provide log levels.
|
|
||||||
|
|
||||||
Documentation
|
|
||||||
-------------
|
|
||||||
|
|
||||||
http://godoc.org/github.com/calmh/logger
|
|
||||||
|
|
||||||
License
|
|
||||||
-------
|
|
||||||
|
|
||||||
MIT
|
|
||||||
|
|
||||||
-160
@@ -1,160 +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 logger implements a standardized logger with callback functionality
|
|
||||||
package logger
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
type LogLevel int
|
|
||||||
|
|
||||||
const (
|
|
||||||
LevelDebug LogLevel = iota
|
|
||||||
LevelInfo
|
|
||||||
LevelOK
|
|
||||||
LevelWarn
|
|
||||||
LevelFatal
|
|
||||||
NumLevels
|
|
||||||
)
|
|
||||||
|
|
||||||
// A MessageHandler is called with the log level and message text.
|
|
||||||
type MessageHandler func(l LogLevel, msg string)
|
|
||||||
|
|
||||||
type Logger struct {
|
|
||||||
logger *log.Logger
|
|
||||||
handlers [NumLevels][]MessageHandler
|
|
||||||
mut sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// The default logger logs to standard output with a time prefix.
|
|
||||||
var DefaultLogger = New()
|
|
||||||
|
|
||||||
func New() *Logger {
|
|
||||||
return &Logger{
|
|
||||||
logger: log.New(os.Stdout, "", log.Ltime),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddHandler registers a new MessageHandler to receive messages with the
|
|
||||||
// specified log level or above.
|
|
||||||
func (l *Logger) AddHandler(level LogLevel, h MessageHandler) {
|
|
||||||
l.mut.Lock()
|
|
||||||
defer l.mut.Unlock()
|
|
||||||
l.handlers[level] = append(l.handlers[level], h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// See log.SetFlags
|
|
||||||
func (l *Logger) SetFlags(flag int) {
|
|
||||||
l.logger.SetFlags(flag)
|
|
||||||
}
|
|
||||||
|
|
||||||
// See log.SetPrefix
|
|
||||||
func (l *Logger) SetPrefix(prefix string) {
|
|
||||||
l.logger.SetPrefix(prefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) callHandlers(level LogLevel, s string) {
|
|
||||||
for _, h := range l.handlers[level] {
|
|
||||||
h(level, strings.TrimSpace(s))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debugln logs a line with a DEBUG prefix.
|
|
||||||
func (l *Logger) Debugln(vals ...interface{}) {
|
|
||||||
l.mut.Lock()
|
|
||||||
defer l.mut.Unlock()
|
|
||||||
s := fmt.Sprintln(vals...)
|
|
||||||
l.logger.Output(2, "DEBUG: "+s)
|
|
||||||
l.callHandlers(LevelDebug, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debugf logs a formatted line with a DEBUG prefix.
|
|
||||||
func (l *Logger) Debugf(format string, vals ...interface{}) {
|
|
||||||
l.mut.Lock()
|
|
||||||
defer l.mut.Unlock()
|
|
||||||
s := fmt.Sprintf(format, vals...)
|
|
||||||
l.logger.Output(2, "DEBUG: "+s)
|
|
||||||
l.callHandlers(LevelDebug, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Infoln logs a line with an INFO prefix.
|
|
||||||
func (l *Logger) Infoln(vals ...interface{}) {
|
|
||||||
l.mut.Lock()
|
|
||||||
defer l.mut.Unlock()
|
|
||||||
s := fmt.Sprintln(vals...)
|
|
||||||
l.logger.Output(2, "INFO: "+s)
|
|
||||||
l.callHandlers(LevelInfo, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Infof logs a formatted line with an INFO prefix.
|
|
||||||
func (l *Logger) Infof(format string, vals ...interface{}) {
|
|
||||||
l.mut.Lock()
|
|
||||||
defer l.mut.Unlock()
|
|
||||||
s := fmt.Sprintf(format, vals...)
|
|
||||||
l.logger.Output(2, "INFO: "+s)
|
|
||||||
l.callHandlers(LevelInfo, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Okln logs a line with an OK prefix.
|
|
||||||
func (l *Logger) Okln(vals ...interface{}) {
|
|
||||||
l.mut.Lock()
|
|
||||||
defer l.mut.Unlock()
|
|
||||||
s := fmt.Sprintln(vals...)
|
|
||||||
l.logger.Output(2, "OK: "+s)
|
|
||||||
l.callHandlers(LevelOK, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Okf logs a formatted line with an OK prefix.
|
|
||||||
func (l *Logger) Okf(format string, vals ...interface{}) {
|
|
||||||
l.mut.Lock()
|
|
||||||
defer l.mut.Unlock()
|
|
||||||
s := fmt.Sprintf(format, vals...)
|
|
||||||
l.logger.Output(2, "OK: "+s)
|
|
||||||
l.callHandlers(LevelOK, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warnln logs a formatted line with a WARNING prefix.
|
|
||||||
func (l *Logger) Warnln(vals ...interface{}) {
|
|
||||||
l.mut.Lock()
|
|
||||||
defer l.mut.Unlock()
|
|
||||||
s := fmt.Sprintln(vals...)
|
|
||||||
l.logger.Output(2, "WARNING: "+s)
|
|
||||||
l.callHandlers(LevelWarn, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warnf logs a formatted line with a WARNING prefix.
|
|
||||||
func (l *Logger) Warnf(format string, vals ...interface{}) {
|
|
||||||
l.mut.Lock()
|
|
||||||
defer l.mut.Unlock()
|
|
||||||
s := fmt.Sprintf(format, vals...)
|
|
||||||
l.logger.Output(2, "WARNING: "+s)
|
|
||||||
l.callHandlers(LevelWarn, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fatalln logs a line with a FATAL prefix and exits the process with exit
|
|
||||||
// code 1.
|
|
||||||
func (l *Logger) Fatalln(vals ...interface{}) {
|
|
||||||
l.mut.Lock()
|
|
||||||
defer l.mut.Unlock()
|
|
||||||
s := fmt.Sprintln(vals...)
|
|
||||||
l.logger.Output(2, "FATAL: "+s)
|
|
||||||
l.callHandlers(LevelFatal, s)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fatalf logs a formatted line with a FATAL prefix and exits the process with
|
|
||||||
// exit code 1.
|
|
||||||
func (l *Logger) Fatalf(format string, vals ...interface{}) {
|
|
||||||
l.mut.Lock()
|
|
||||||
defer l.mut.Unlock()
|
|
||||||
s := fmt.Sprintf(format, vals...)
|
|
||||||
l.logger.Output(2, "FATAL: "+s)
|
|
||||||
l.callHandlers(LevelFatal, s)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
-58
@@ -1,58 +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 logger
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAPI(t *testing.T) {
|
|
||||||
l := New()
|
|
||||||
l.SetFlags(0)
|
|
||||||
l.SetPrefix("testing")
|
|
||||||
|
|
||||||
debug := 0
|
|
||||||
l.AddHandler(LevelDebug, checkFunc(t, LevelDebug, "test 0", &debug))
|
|
||||||
info := 0
|
|
||||||
l.AddHandler(LevelInfo, checkFunc(t, LevelInfo, "test 1", &info))
|
|
||||||
warn := 0
|
|
||||||
l.AddHandler(LevelWarn, checkFunc(t, LevelWarn, "test 2", &warn))
|
|
||||||
ok := 0
|
|
||||||
l.AddHandler(LevelOK, checkFunc(t, LevelOK, "test 3", &ok))
|
|
||||||
|
|
||||||
l.Debugf("test %d", 0)
|
|
||||||
l.Debugln("test", 0)
|
|
||||||
l.Infof("test %d", 1)
|
|
||||||
l.Infoln("test", 1)
|
|
||||||
l.Warnf("test %d", 2)
|
|
||||||
l.Warnln("test", 2)
|
|
||||||
l.Okf("test %d", 3)
|
|
||||||
l.Okln("test", 3)
|
|
||||||
|
|
||||||
if debug != 2 {
|
|
||||||
t.Errorf("Debug handler called %d != 2 times", debug)
|
|
||||||
}
|
|
||||||
if info != 2 {
|
|
||||||
t.Errorf("Info handler called %d != 2 times", info)
|
|
||||||
}
|
|
||||||
if warn != 2 {
|
|
||||||
t.Errorf("Warn handler called %d != 2 times", warn)
|
|
||||||
}
|
|
||||||
if ok != 2 {
|
|
||||||
t.Errorf("Ok handler called %d != 2 times", ok)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkFunc(t *testing.T, expectl LogLevel, expectmsg string, counter *int) func(LogLevel, string) {
|
|
||||||
return func(l LogLevel, msg string) {
|
|
||||||
*counter++
|
|
||||||
if l != expectl {
|
|
||||||
t.Errorf("Incorrect message level %d != %d", l, expectl)
|
|
||||||
}
|
|
||||||
if !strings.HasSuffix(msg, expectmsg) {
|
|
||||||
t.Errorf("%q does not end with %q", msg, expectmsg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+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.
|
||||||
Generated
Vendored
+11
-9
@@ -13,6 +13,8 @@ import (
|
|||||||
var (
|
var (
|
||||||
// ErrCorrupt reports that the input is invalid.
|
// ErrCorrupt reports that the input is invalid.
|
||||||
ErrCorrupt = errors.New("snappy: corrupt input")
|
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 reports that the input isn't supported.
|
||||||
ErrUnsupported = errors.New("snappy: unsupported input")
|
ErrUnsupported = errors.New("snappy: unsupported input")
|
||||||
)
|
)
|
||||||
@@ -27,11 +29,13 @@ func DecodedLen(src []byte) (int, error) {
|
|||||||
// that the length header occupied.
|
// that the length header occupied.
|
||||||
func decodedLen(src []byte) (blockLen, headerLen int, err error) {
|
func decodedLen(src []byte) (blockLen, headerLen int, err error) {
|
||||||
v, n := binary.Uvarint(src)
|
v, n := binary.Uvarint(src)
|
||||||
if n == 0 {
|
if n <= 0 || v > 0xffffffff {
|
||||||
return 0, 0, ErrCorrupt
|
return 0, 0, ErrCorrupt
|
||||||
}
|
}
|
||||||
if uint64(int(v)) != v {
|
|
||||||
return 0, 0, errors.New("snappy: decoded block is too large")
|
const wordSize = 32 << (^uint(0) >> 32 & 1)
|
||||||
|
if wordSize == 32 && v > 0x7fffffff {
|
||||||
|
return 0, 0, ErrTooLarge
|
||||||
}
|
}
|
||||||
return int(v), n, nil
|
return int(v), n, nil
|
||||||
}
|
}
|
||||||
@@ -56,7 +60,7 @@ func Decode(dst, src []byte) ([]byte, error) {
|
|||||||
x := uint(src[s] >> 2)
|
x := uint(src[s] >> 2)
|
||||||
switch {
|
switch {
|
||||||
case x < 60:
|
case x < 60:
|
||||||
s += 1
|
s++
|
||||||
case x == 60:
|
case x == 60:
|
||||||
s += 2
|
s += 2
|
||||||
if s > len(src) {
|
if s > len(src) {
|
||||||
@@ -130,7 +134,7 @@ func Decode(dst, src []byte) ([]byte, error) {
|
|||||||
|
|
||||||
// NewReader returns a new Reader that decompresses from r, using the framing
|
// NewReader returns a new Reader that decompresses from r, using the framing
|
||||||
// format described at
|
// format described at
|
||||||
// https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt
|
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||||
func NewReader(r io.Reader) *Reader {
|
func NewReader(r io.Reader) *Reader {
|
||||||
return &Reader{
|
return &Reader{
|
||||||
r: r,
|
r: r,
|
||||||
@@ -200,7 +204,7 @@ func (r *Reader) Read(p []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The chunk types are specified at
|
// The chunk types are specified at
|
||||||
// https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt
|
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||||
switch chunkType {
|
switch chunkType {
|
||||||
case chunkTypeCompressedData:
|
case chunkTypeCompressedData:
|
||||||
// Section 4.2. Compressed data (chunk type 0x00).
|
// Section 4.2. Compressed data (chunk type 0x00).
|
||||||
@@ -280,13 +284,11 @@ func (r *Reader) Read(p []byte) (int, error) {
|
|||||||
// Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f).
|
// Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f).
|
||||||
r.err = ErrUnsupported
|
r.err = ErrUnsupported
|
||||||
return 0, r.err
|
return 0, r.err
|
||||||
|
}
|
||||||
} else {
|
|
||||||
// Section 4.4 Padding (chunk type 0xfe).
|
// Section 4.4 Padding (chunk type 0xfe).
|
||||||
// Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd).
|
// Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd).
|
||||||
if !r.readFull(r.buf[:chunkLen]) {
|
if !r.readFull(r.buf[:chunkLen]) {
|
||||||
return 0, r.err
|
return 0, r.err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Generated
Vendored
+7
-11
@@ -79,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)
|
||||||
}
|
}
|
||||||
@@ -92,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.
|
||||||
@@ -145,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
|
||||||
@@ -176,7 +176,7 @@ func MaxEncodedLen(srcLen int) int {
|
|||||||
|
|
||||||
// NewWriter returns a new Writer that compresses to w, using the framing
|
// NewWriter returns a new Writer that compresses to w, using the framing
|
||||||
// format described at
|
// format described at
|
||||||
// https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt
|
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||||
func NewWriter(w io.Writer) *Writer {
|
func NewWriter(w io.Writer) *Writer {
|
||||||
return &Writer{
|
return &Writer{
|
||||||
w: w,
|
w: w,
|
||||||
@@ -226,11 +226,7 @@ func (w *Writer) Write(p []byte) (n int, errRet error) {
|
|||||||
// Compress the buffer, discarding the result if the improvement
|
// Compress the buffer, discarding the result if the improvement
|
||||||
// isn't at least 12.5%.
|
// isn't at least 12.5%.
|
||||||
chunkType := uint8(chunkTypeCompressedData)
|
chunkType := uint8(chunkTypeCompressedData)
|
||||||
chunkBody, err := Encode(w.enc, uncompressed)
|
chunkBody := Encode(w.enc, uncompressed)
|
||||||
if err != nil {
|
|
||||||
w.err = err
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
if len(chunkBody) >= len(uncompressed)-len(uncompressed)/8 {
|
if len(chunkBody) >= len(uncompressed)-len(uncompressed)/8 {
|
||||||
chunkType, chunkBody = chunkTypeUncompressedData, uncompressed
|
chunkType, chunkBody = chunkTypeUncompressedData, uncompressed
|
||||||
}
|
}
|
||||||
@@ -244,11 +240,11 @@ func (w *Writer) Write(p []byte) (n int, errRet error) {
|
|||||||
w.buf[5] = uint8(checksum >> 8)
|
w.buf[5] = uint8(checksum >> 8)
|
||||||
w.buf[6] = uint8(checksum >> 16)
|
w.buf[6] = uint8(checksum >> 16)
|
||||||
w.buf[7] = uint8(checksum >> 24)
|
w.buf[7] = uint8(checksum >> 24)
|
||||||
if _, err = w.w.Write(w.buf[:]); err != nil {
|
if _, err := w.w.Write(w.buf[:]); err != nil {
|
||||||
w.err = err
|
w.err = err
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
if _, err = w.w.Write(chunkBody); err != nil {
|
if _, err := w.w.Write(chunkBody); err != nil {
|
||||||
w.err = err
|
w.err = err
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
Generated
Vendored
+3
-3
@@ -5,7 +5,7 @@
|
|||||||
// 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 (
|
import (
|
||||||
@@ -46,7 +46,7 @@ const (
|
|||||||
chunkHeaderSize = 4
|
chunkHeaderSize = 4
|
||||||
magicChunk = "\xff\x06\x00\x00" + magicBody
|
magicChunk = "\xff\x06\x00\x00" + magicBody
|
||||||
magicBody = "sNaPpY"
|
magicBody = "sNaPpY"
|
||||||
// https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt says
|
// 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".
|
// that "the uncompressed data in a chunk must be no longer than 65536 bytes".
|
||||||
maxUncompressedChunkLen = 65536
|
maxUncompressedChunkLen = 65536
|
||||||
)
|
)
|
||||||
@@ -61,7 +61,7 @@ const (
|
|||||||
var crcTable = crc32.MakeTable(crc32.Castagnoli)
|
var crcTable = crc32.MakeTable(crc32.Castagnoli)
|
||||||
|
|
||||||
// crc implements the checksum specified in section 3 of
|
// crc implements the checksum specified in section 3 of
|
||||||
// https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt
|
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||||
func crc(b []byte) uint32 {
|
func crc(b []byte) uint32 {
|
||||||
c := crc32.Update(0, crcTable, b)
|
c := crc32.Update(0, crcTable, b)
|
||||||
return uint32(c>>15|c<<17) + 0xa282ead8
|
return uint32(c>>15|c<<17) + 0xa282ead8
|
||||||
Generated
Vendored
+26
-13
@@ -24,11 +24,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func roundtrip(b, ebuf, dbuf []byte) error {
|
func roundtrip(b, ebuf, dbuf []byte) error {
|
||||||
e, err := Encode(ebuf, b)
|
d, err := Decode(dbuf, Encode(ebuf, b))
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("encoding error: %v", err)
|
|
||||||
}
|
|
||||||
d, err := Decode(dbuf, e)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("decoding error: %v", err)
|
return fmt.Errorf("decoding error: %v", err)
|
||||||
}
|
}
|
||||||
@@ -82,6 +78,26 @@ func TestSmallRegular(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInvalidVarint(t *testing.T) {
|
||||||
|
data := []byte("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00")
|
||||||
|
if _, err := DecodedLen(data); err != ErrCorrupt {
|
||||||
|
t.Errorf("DecodedLen: got %v, want ErrCorrupt", err)
|
||||||
|
}
|
||||||
|
if _, err := Decode(nil, data); err != ErrCorrupt {
|
||||||
|
t.Errorf("Decode: got %v, want ErrCorrupt", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The encoded varint overflows 32 bits
|
||||||
|
data = []byte("\xff\xff\xff\xff\xff\x00")
|
||||||
|
|
||||||
|
if _, err := DecodedLen(data); err != ErrCorrupt {
|
||||||
|
t.Errorf("DecodedLen: got %v, want ErrCorrupt", err)
|
||||||
|
}
|
||||||
|
if _, err := Decode(nil, data); err != ErrCorrupt {
|
||||||
|
t.Errorf("Decode: got %v, want ErrCorrupt", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func cmp(a, b []byte) error {
|
func cmp(a, b []byte) error {
|
||||||
if len(a) != len(b) {
|
if len(a) != len(b) {
|
||||||
return fmt.Errorf("got %d bytes, want %d", len(a), len(b))
|
return fmt.Errorf("got %d bytes, want %d", len(a), len(b))
|
||||||
@@ -197,10 +213,7 @@ func TestWriterReset(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func benchDecode(b *testing.B, src []byte) {
|
func benchDecode(b *testing.B, src []byte) {
|
||||||
encoded, err := Encode(nil, src)
|
encoded := Encode(nil, src)
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
// Bandwidth is in amount of uncompressed data.
|
// Bandwidth is in amount of uncompressed data.
|
||||||
b.SetBytes(int64(len(src)))
|
b.SetBytes(int64(len(src)))
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
@@ -222,7 +235,7 @@ func benchEncode(b *testing.B, src []byte) {
|
|||||||
func readFile(b testing.TB, filename string) []byte {
|
func readFile(b testing.TB, filename string) []byte {
|
||||||
src, err := ioutil.ReadFile(filename)
|
src, err := ioutil.ReadFile(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("failed reading %s: %s", filename, err)
|
b.Skipf("skipping benchmark: %v", err)
|
||||||
}
|
}
|
||||||
if len(src) == 0 {
|
if len(src) == 0 {
|
||||||
b.Fatalf("%s has zero length", filename)
|
b.Fatalf("%s has zero length", filename)
|
||||||
@@ -284,14 +297,14 @@ var testFiles = []struct {
|
|||||||
// The test data files are present at this canonical URL.
|
// The test data files are present at this canonical URL.
|
||||||
const baseURL = "https://raw.githubusercontent.com/google/snappy/master/testdata/"
|
const baseURL = "https://raw.githubusercontent.com/google/snappy/master/testdata/"
|
||||||
|
|
||||||
func downloadTestdata(basename string) (errRet error) {
|
func downloadTestdata(b *testing.B, basename string) (errRet error) {
|
||||||
filename := filepath.Join(*testdata, basename)
|
filename := filepath.Join(*testdata, basename)
|
||||||
if stat, err := os.Stat(filename); err == nil && stat.Size() != 0 {
|
if stat, err := os.Stat(filename); err == nil && stat.Size() != 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !*download {
|
if !*download {
|
||||||
return fmt.Errorf("test data not found; skipping benchmark without the -download flag")
|
b.Skipf("test data not found; skipping benchmark without the -download flag")
|
||||||
}
|
}
|
||||||
// Download the official snappy C++ implementation reference test data
|
// Download the official snappy C++ implementation reference test data
|
||||||
// files for benchmarking.
|
// files for benchmarking.
|
||||||
@@ -326,7 +339,7 @@ func downloadTestdata(basename string) (errRet error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func benchFile(b *testing.B, n int, decode bool) {
|
func benchFile(b *testing.B, n int, decode bool) {
|
||||||
if err := downloadTestdata(testFiles[n].filename); err != nil {
|
if err := downloadTestdata(b, testFiles[n].filename); err != nil {
|
||||||
b.Fatalf("failed to download testdata: %s", err)
|
b.Fatalf("failed to download testdata: %s", err)
|
||||||
}
|
}
|
||||||
data := readFile(b, filepath.Join(*testdata, testFiles[n].filename))
|
data := readFile(b, filepath.Join(*testdata, testFiles[n].filename))
|
||||||
+5
-2
@@ -1,5 +1,8 @@
|
|||||||
This package contains an efficient token-bucket-based rate limiter.
|
All files in this repository are licensed as follows. If you contribute
|
||||||
Copyright (C) 2015 Canonical Ltd.
|
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.
|
||||||
|
|
||||||
|
|||||||
+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
|
|
||||||
}
|
|
||||||
|
|||||||
+3
-1
@@ -4,7 +4,9 @@
|
|||||||
|
|
||||||
There is sometimes utility in finding the current executable file
|
There is sometimes utility in finding the current executable file
|
||||||
that is running. This can be used for upgrading the current executable
|
that is running. This can be used for upgrading the current executable
|
||||||
or finding resources located relative to the executable file.
|
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:
|
Multi-platform and supports:
|
||||||
* Linux
|
* Linux
|
||||||
|
|||||||
+3
-3
@@ -16,12 +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
|
return filepath.Dir(p), nil
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -17,12 +17,14 @@ import (
|
|||||||
func executable() (string, error) {
|
func executable() (string, error) {
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "linux":
|
case "linux":
|
||||||
const deletedSuffix = " (deleted)"
|
const deletedTag = " (deleted)"
|
||||||
execpath, err := os.Readlink("/proc/self/exe")
|
execpath, err := os.Readlink("/proc/self/exe")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return execpath, err
|
return execpath, err
|
||||||
}
|
}
|
||||||
return strings.TrimSuffix(execpath, deletedSuffix), nil
|
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", "dragonfly":
|
case "openbsd", "dragonfly":
|
||||||
|
|||||||
+23
@@ -24,6 +24,29 @@ const (
|
|||||||
executableEnvValueDelete = "delete"
|
executableEnvValueDelete = "delete"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestPrintExecutable(t *testing.T) {
|
||||||
|
ef, err := Executable()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Executable failed: %v", err)
|
||||||
|
}
|
||||||
|
t.Log("Executable:", ef)
|
||||||
|
}
|
||||||
|
func TestPrintExecutableFolder(t *testing.T) {
|
||||||
|
ef, err := ExecutableFolder()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ExecutableFolder failed: %v", err)
|
||||||
|
}
|
||||||
|
t.Log("Executable Folder:", ef)
|
||||||
|
}
|
||||||
|
func TestExecutableFolder(t *testing.T) {
|
||||||
|
ef, err := ExecutableFolder()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ExecutableFolder failed: %v", err)
|
||||||
|
}
|
||||||
|
if ef[len(ef)-1] == filepath.Separator {
|
||||||
|
t.Fatal("ExecutableFolder ends with a trailing slash.")
|
||||||
|
}
|
||||||
|
}
|
||||||
func TestExecutableMatch(t *testing.T) {
|
func TestExecutableMatch(t *testing.T) {
|
||||||
ep, err := Executable()
|
ep, err := Executable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
-15
@@ -1,15 +0,0 @@
|
|||||||
// Copyright (C) 2014 The Protocol Authors.
|
|
||||||
|
|
||||||
package protocol
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/calmh/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
debug = strings.Contains(os.Getenv("STTRACE"), "protocol") || os.Getenv("STTRACE") == "all"
|
|
||||||
l = logger.DefaultLogger
|
|
||||||
)
|
|
||||||
+200
-74
@@ -69,6 +69,7 @@ type DB struct {
|
|||||||
compErrC chan error
|
compErrC chan error
|
||||||
compPerErrC chan error
|
compPerErrC chan error
|
||||||
compErrSetC chan error
|
compErrSetC chan error
|
||||||
|
compWriteLocking bool
|
||||||
compStats []cStats
|
compStats []cStats
|
||||||
|
|
||||||
// Close.
|
// Close.
|
||||||
@@ -108,6 +109,16 @@ func openDB(s *session) (*DB, error) {
|
|||||||
closeC: make(chan struct{}),
|
closeC: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read-only mode.
|
||||||
|
readOnly := s.o.GetReadOnly()
|
||||||
|
|
||||||
|
if readOnly {
|
||||||
|
// Recover journals (read-only mode).
|
||||||
|
if err := db.recoverJournalRO(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Recover journals.
|
||||||
if err := db.recoverJournal(); err != nil {
|
if err := db.recoverJournal(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -122,14 +133,20 @@ func openDB(s *session) (*DB, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Doesn't need to be included in the wait group.
|
// Doesn't need to be included in the wait group.
|
||||||
go db.compactionError()
|
go db.compactionError()
|
||||||
go db.mpoolDrain()
|
go db.mpoolDrain()
|
||||||
|
|
||||||
|
if readOnly {
|
||||||
|
db.SetReadOnly()
|
||||||
|
} else {
|
||||||
db.closeW.Add(3)
|
db.closeW.Add(3)
|
||||||
go db.tCompaction()
|
go db.tCompaction()
|
||||||
go db.mCompaction()
|
go db.mCompaction()
|
||||||
go db.jWriter()
|
go db.jWriter()
|
||||||
|
}
|
||||||
|
|
||||||
s.logf("db@open done T·%v", time.Since(start))
|
s.logf("db@open done T·%v", time.Since(start))
|
||||||
|
|
||||||
@@ -274,8 +291,9 @@ func recoverTable(s *session, o *opt.Options) error {
|
|||||||
|
|
||||||
// We will drop corrupted table.
|
// We will drop corrupted table.
|
||||||
strict = o.GetStrict(opt.StrictRecovery)
|
strict = o.GetStrict(opt.StrictRecovery)
|
||||||
|
noSync = o.GetNoSync()
|
||||||
|
|
||||||
rec = &sessionRecord{numLevel: o.GetNumLevel()}
|
rec = &sessionRecord{}
|
||||||
bpool = util.NewBufferPool(o.GetBlockSize() + 5)
|
bpool = util.NewBufferPool(o.GetBlockSize() + 5)
|
||||||
)
|
)
|
||||||
buildTable := func(iter iterator.Iterator) (tmp storage.File, size int64, err error) {
|
buildTable := func(iter iterator.Iterator) (tmp storage.File, size int64, err error) {
|
||||||
@@ -311,10 +329,12 @@ func recoverTable(s *session, o *opt.Options) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !noSync {
|
||||||
err = writer.Sync()
|
err = writer.Sync()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
size = int64(tw.BytesLen())
|
size = int64(tw.BytesLen())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -450,78 +470,92 @@ func recoverTable(s *session, o *opt.Options) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) recoverJournal() error {
|
func (db *DB) recoverJournal() error {
|
||||||
// Get all tables and sort it by file number.
|
// Get all journals and sort it by file number.
|
||||||
journalFiles_, err := db.s.getFiles(storage.TypeJournal)
|
allJournalFiles, err := db.s.getFiles(storage.TypeJournal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
journalFiles := files(journalFiles_)
|
files(allJournalFiles).sort()
|
||||||
journalFiles.sort()
|
|
||||||
|
|
||||||
// Discard older journal.
|
// Journals that will be recovered.
|
||||||
prev := -1
|
var recJournalFiles []storage.File
|
||||||
for i, file := range journalFiles {
|
for _, jf := range allJournalFiles {
|
||||||
if file.Num() >= db.s.stJournalNum {
|
if jf.Num() >= db.s.stJournalNum || jf.Num() == db.s.stPrevJournalNum {
|
||||||
if prev >= 0 {
|
recJournalFiles = append(recJournalFiles, jf)
|
||||||
i--
|
|
||||||
journalFiles[i] = journalFiles[prev]
|
|
||||||
}
|
|
||||||
journalFiles = journalFiles[i:]
|
|
||||||
break
|
|
||||||
} else if file.Num() == db.s.stPrevJournalNum {
|
|
||||||
prev = i
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var jr *journal.Reader
|
var (
|
||||||
var of storage.File
|
of storage.File // Obsolete file.
|
||||||
var mem *memdb.DB
|
rec = &sessionRecord{}
|
||||||
batch := new(Batch)
|
)
|
||||||
cm := newCMem(db.s)
|
|
||||||
buf := new(util.Buffer)
|
// Recover journals.
|
||||||
|
if len(recJournalFiles) > 0 {
|
||||||
|
db.logf("journal@recovery F·%d", len(recJournalFiles))
|
||||||
|
|
||||||
|
// Mark file number as used.
|
||||||
|
db.s.markFileNum(recJournalFiles[len(recJournalFiles)-1].Num())
|
||||||
|
|
||||||
|
var (
|
||||||
// Options.
|
// Options.
|
||||||
strict := db.s.o.GetStrict(opt.StrictJournal)
|
strict = db.s.o.GetStrict(opt.StrictJournal)
|
||||||
checksum := db.s.o.GetStrict(opt.StrictJournalChecksum)
|
checksum = db.s.o.GetStrict(opt.StrictJournalChecksum)
|
||||||
writeBuffer := db.s.o.GetWriteBuffer()
|
writeBuffer = db.s.o.GetWriteBuffer()
|
||||||
recoverJournal := func(file storage.File) error {
|
|
||||||
db.logf("journal@recovery recovering @%d", file.Num())
|
jr *journal.Reader
|
||||||
reader, err := file.Open()
|
mdb = memdb.New(db.s.icmp, writeBuffer)
|
||||||
|
buf = &util.Buffer{}
|
||||||
|
batch = &Batch{}
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, jf := range recJournalFiles {
|
||||||
|
db.logf("journal@recovery recovering @%d", jf.Num())
|
||||||
|
|
||||||
|
fr, err := jf.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer reader.Close()
|
|
||||||
|
|
||||||
// Create/reset journal reader instance.
|
// Create or reset journal reader instance.
|
||||||
if jr == nil {
|
if jr == nil {
|
||||||
jr = journal.NewReader(reader, dropper{db.s, file}, strict, checksum)
|
jr = journal.NewReader(fr, dropper{db.s, jf}, strict, checksum)
|
||||||
} else {
|
} else {
|
||||||
jr.Reset(reader, dropper{db.s, file}, strict, checksum)
|
jr.Reset(fr, dropper{db.s, jf}, strict, checksum)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush memdb and remove obsolete journal file.
|
// Flush memdb and remove obsolete journal file.
|
||||||
if of != nil {
|
if of != nil {
|
||||||
if mem.Len() > 0 {
|
if mdb.Len() > 0 {
|
||||||
if err := cm.flush(mem, 0); err != nil {
|
if _, err := db.s.flushMemdb(rec, mdb, -1); err != nil {
|
||||||
|
fr.Close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := cm.commit(file.Num(), db.seq); err != nil {
|
|
||||||
|
rec.setJournalNum(jf.Num())
|
||||||
|
rec.setSeqNum(db.seq)
|
||||||
|
if err := db.s.commit(rec); err != nil {
|
||||||
|
fr.Close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cm.reset()
|
rec.resetAddedTables()
|
||||||
|
|
||||||
of.Remove()
|
of.Remove()
|
||||||
of = nil
|
of = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replay journal to memdb.
|
// Replay journal to memdb.
|
||||||
mem.Reset()
|
mdb.Reset()
|
||||||
for {
|
for {
|
||||||
r, err := jr.Next()
|
r, err := jr.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return errors.SetFile(err, file)
|
|
||||||
|
fr.Close()
|
||||||
|
return errors.SetFile(err, jf)
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
@@ -529,53 +563,43 @@ func (db *DB) recoverJournal() error {
|
|||||||
if err == io.ErrUnexpectedEOF {
|
if err == io.ErrUnexpectedEOF {
|
||||||
// This is error returned due to corruption, with strict == false.
|
// This is error returned due to corruption, with strict == false.
|
||||||
continue
|
continue
|
||||||
} else {
|
|
||||||
return errors.SetFile(err, file)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fr.Close()
|
||||||
|
return errors.SetFile(err, jf)
|
||||||
}
|
}
|
||||||
if err := batch.memDecodeAndReplay(db.seq, buf.Bytes(), mem); err != nil {
|
if err := batch.memDecodeAndReplay(db.seq, buf.Bytes(), mdb); err != nil {
|
||||||
if strict || !errors.IsCorrupted(err) {
|
if !strict && errors.IsCorrupted(err) {
|
||||||
return errors.SetFile(err, file)
|
|
||||||
} else {
|
|
||||||
db.s.logf("journal error: %v (skipped)", err)
|
db.s.logf("journal error: %v (skipped)", err)
|
||||||
// We won't apply sequence number as it might be corrupted.
|
// We won't apply sequence number as it might be corrupted.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fr.Close()
|
||||||
|
return errors.SetFile(err, jf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save sequence number.
|
// Save sequence number.
|
||||||
db.seq = batch.seq + uint64(batch.Len())
|
db.seq = batch.seq + uint64(batch.Len())
|
||||||
|
|
||||||
// Flush it if large enough.
|
// Flush it if large enough.
|
||||||
if mem.Size() >= writeBuffer {
|
if mdb.Size() >= writeBuffer {
|
||||||
if err := cm.flush(mem, 0); err != nil {
|
if _, err := db.s.flushMemdb(rec, mdb, 0); err != nil {
|
||||||
|
fr.Close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
mem.Reset()
|
|
||||||
|
mdb.Reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
of = file
|
fr.Close()
|
||||||
return nil
|
of = jf
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recover all journals.
|
// Flush the last memdb.
|
||||||
if len(journalFiles) > 0 {
|
if mdb.Len() > 0 {
|
||||||
db.logf("journal@recovery F·%d", len(journalFiles))
|
if _, err := db.s.flushMemdb(rec, mdb, 0); err != nil {
|
||||||
|
|
||||||
// Mark file number as used.
|
|
||||||
db.s.markFileNum(journalFiles[len(journalFiles)-1].Num())
|
|
||||||
|
|
||||||
mem = memdb.New(db.s.icmp, writeBuffer)
|
|
||||||
for _, file := range journalFiles {
|
|
||||||
if err := recoverJournal(file); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush the last journal.
|
|
||||||
if mem.Len() > 0 {
|
|
||||||
if err := cm.flush(mem, 0); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -587,8 +611,10 @@ func (db *DB) recoverJournal() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Commit.
|
// Commit.
|
||||||
if err := cm.commit(db.journalFile.Num(), db.seq); err != nil {
|
rec.setJournalNum(db.journalFile.Num())
|
||||||
// Close journal.
|
rec.setSeqNum(db.seq)
|
||||||
|
if err := db.s.commit(rec); err != nil {
|
||||||
|
// Close journal on error.
|
||||||
if db.journal != nil {
|
if db.journal != nil {
|
||||||
db.journal.Close()
|
db.journal.Close()
|
||||||
db.journalWriter.Close()
|
db.journalWriter.Close()
|
||||||
@@ -604,6 +630,103 @@ func (db *DB) recoverJournal() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *DB) recoverJournalRO() error {
|
||||||
|
// Get all journals and sort it by file number.
|
||||||
|
allJournalFiles, err := db.s.getFiles(storage.TypeJournal)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
files(allJournalFiles).sort()
|
||||||
|
|
||||||
|
// Journals that will be recovered.
|
||||||
|
var recJournalFiles []storage.File
|
||||||
|
for _, jf := range allJournalFiles {
|
||||||
|
if jf.Num() >= db.s.stJournalNum || jf.Num() == db.s.stPrevJournalNum {
|
||||||
|
recJournalFiles = append(recJournalFiles, jf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Options.
|
||||||
|
strict = db.s.o.GetStrict(opt.StrictJournal)
|
||||||
|
checksum = db.s.o.GetStrict(opt.StrictJournalChecksum)
|
||||||
|
writeBuffer = db.s.o.GetWriteBuffer()
|
||||||
|
|
||||||
|
mdb = memdb.New(db.s.icmp, writeBuffer)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Recover journals.
|
||||||
|
if len(recJournalFiles) > 0 {
|
||||||
|
db.logf("journal@recovery RO·Mode F·%d", len(recJournalFiles))
|
||||||
|
|
||||||
|
var (
|
||||||
|
jr *journal.Reader
|
||||||
|
buf = &util.Buffer{}
|
||||||
|
batch = &Batch{}
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, jf := range recJournalFiles {
|
||||||
|
db.logf("journal@recovery recovering @%d", jf.Num())
|
||||||
|
|
||||||
|
fr, err := jf.Open()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create or reset journal reader instance.
|
||||||
|
if jr == nil {
|
||||||
|
jr = journal.NewReader(fr, dropper{db.s, jf}, strict, checksum)
|
||||||
|
} else {
|
||||||
|
jr.Reset(fr, dropper{db.s, jf}, strict, checksum)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replay journal to memdb.
|
||||||
|
for {
|
||||||
|
r, err := jr.Next()
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
fr.Close()
|
||||||
|
return errors.SetFile(err, jf)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.Reset()
|
||||||
|
if _, err := buf.ReadFrom(r); err != nil {
|
||||||
|
if err == io.ErrUnexpectedEOF {
|
||||||
|
// This is error returned due to corruption, with strict == false.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fr.Close()
|
||||||
|
return errors.SetFile(err, jf)
|
||||||
|
}
|
||||||
|
if err := batch.memDecodeAndReplay(db.seq, buf.Bytes(), mdb); err != nil {
|
||||||
|
if !strict && errors.IsCorrupted(err) {
|
||||||
|
db.s.logf("journal error: %v (skipped)", err)
|
||||||
|
// We won't apply sequence number as it might be corrupted.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fr.Close()
|
||||||
|
return errors.SetFile(err, jf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save sequence number.
|
||||||
|
db.seq = batch.seq + uint64(batch.Len())
|
||||||
|
}
|
||||||
|
|
||||||
|
fr.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set memDB.
|
||||||
|
db.mem = &memDB{db: db, DB: mdb, ref: 1}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (db *DB) get(key []byte, seq uint64, ro *opt.ReadOptions) (value []byte, err error) {
|
func (db *DB) get(key []byte, seq uint64, ro *opt.ReadOptions) (value []byte, err error) {
|
||||||
ikey := newIkey(key, seq, ktSeek)
|
ikey := newIkey(key, seq, ktSeek)
|
||||||
|
|
||||||
@@ -614,7 +737,7 @@ func (db *DB) get(key []byte, seq uint64, ro *opt.ReadOptions) (value []byte, er
|
|||||||
}
|
}
|
||||||
defer m.decref()
|
defer m.decref()
|
||||||
|
|
||||||
mk, mv, me := m.mdb.Find(ikey)
|
mk, mv, me := m.Find(ikey)
|
||||||
if me == nil {
|
if me == nil {
|
||||||
ukey, _, kt, kerr := parseIkey(mk)
|
ukey, _, kt, kerr := parseIkey(mk)
|
||||||
if kerr != nil {
|
if kerr != nil {
|
||||||
@@ -652,7 +775,7 @@ func (db *DB) has(key []byte, seq uint64, ro *opt.ReadOptions) (ret bool, err er
|
|||||||
}
|
}
|
||||||
defer m.decref()
|
defer m.decref()
|
||||||
|
|
||||||
mk, _, me := m.mdb.Find(ikey)
|
mk, _, me := m.Find(ikey)
|
||||||
if me == nil {
|
if me == nil {
|
||||||
ukey, _, kt, kerr := parseIkey(mk)
|
ukey, _, kt, kerr := parseIkey(mk)
|
||||||
if kerr != nil {
|
if kerr != nil {
|
||||||
@@ -784,7 +907,7 @@ func (db *DB) GetProperty(name string) (value string, err error) {
|
|||||||
|
|
||||||
const prefix = "leveldb."
|
const prefix = "leveldb."
|
||||||
if !strings.HasPrefix(name, prefix) {
|
if !strings.HasPrefix(name, prefix) {
|
||||||
return "", errors.New("leveldb: GetProperty: unknown property: " + name)
|
return "", ErrNotFound
|
||||||
}
|
}
|
||||||
p := name[len(prefix):]
|
p := name[len(prefix):]
|
||||||
|
|
||||||
@@ -798,7 +921,7 @@ func (db *DB) GetProperty(name string) (value string, err error) {
|
|||||||
var rest string
|
var rest string
|
||||||
n, _ := fmt.Sscanf(p[len(numFilesPrefix):], "%d%s", &level, &rest)
|
n, _ := fmt.Sscanf(p[len(numFilesPrefix):], "%d%s", &level, &rest)
|
||||||
if n != 1 || int(level) >= db.s.o.GetNumLevel() {
|
if n != 1 || int(level) >= db.s.o.GetNumLevel() {
|
||||||
err = errors.New("leveldb: GetProperty: invalid property: " + name)
|
err = ErrNotFound
|
||||||
} else {
|
} else {
|
||||||
value = fmt.Sprint(v.tLen(int(level)))
|
value = fmt.Sprint(v.tLen(int(level)))
|
||||||
}
|
}
|
||||||
@@ -837,7 +960,7 @@ func (db *DB) GetProperty(name string) (value string, err error) {
|
|||||||
case p == "aliveiters":
|
case p == "aliveiters":
|
||||||
value = fmt.Sprintf("%d", atomic.LoadInt32(&db.aliveIters))
|
value = fmt.Sprintf("%d", atomic.LoadInt32(&db.aliveIters))
|
||||||
default:
|
default:
|
||||||
err = errors.New("leveldb: GetProperty: unknown property: " + name)
|
err = ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -900,6 +1023,9 @@ func (db *DB) Close() error {
|
|||||||
var err error
|
var err error
|
||||||
select {
|
select {
|
||||||
case err = <-db.compErrC:
|
case err = <-db.compErrC:
|
||||||
|
if err == ErrReadOnly {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+35
-79
@@ -11,7 +11,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/syndtr/goleveldb/leveldb/errors"
|
"github.com/syndtr/goleveldb/leveldb/errors"
|
||||||
"github.com/syndtr/goleveldb/leveldb/memdb"
|
|
||||||
"github.com/syndtr/goleveldb/leveldb/opt"
|
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -62,58 +61,8 @@ func (p *cStatsStaging) stopTimer() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type cMem struct {
|
|
||||||
s *session
|
|
||||||
level int
|
|
||||||
rec *sessionRecord
|
|
||||||
}
|
|
||||||
|
|
||||||
func newCMem(s *session) *cMem {
|
|
||||||
return &cMem{s: s, rec: &sessionRecord{numLevel: s.o.GetNumLevel()}}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cMem) flush(mem *memdb.DB, level int) error {
|
|
||||||
s := c.s
|
|
||||||
|
|
||||||
// Write memdb to table.
|
|
||||||
iter := mem.NewIterator(nil)
|
|
||||||
defer iter.Release()
|
|
||||||
t, n, err := s.tops.createFrom(iter)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pick level.
|
|
||||||
if level < 0 {
|
|
||||||
v := s.version()
|
|
||||||
level = v.pickLevel(t.imin.ukey(), t.imax.ukey())
|
|
||||||
v.release()
|
|
||||||
}
|
|
||||||
c.rec.addTableFile(level, t)
|
|
||||||
|
|
||||||
s.logf("mem@flush created L%d@%d N·%d S·%s %q:%q", level, t.file.Num(), n, shortenb(int(t.size)), t.imin, t.imax)
|
|
||||||
|
|
||||||
c.level = level
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cMem) reset() {
|
|
||||||
c.rec = &sessionRecord{numLevel: c.s.o.GetNumLevel()}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cMem) commit(journal, seq uint64) error {
|
|
||||||
c.rec.setJournalNum(journal)
|
|
||||||
c.rec.setSeqNum(seq)
|
|
||||||
|
|
||||||
// Commit changes.
|
|
||||||
return c.s.commit(c.rec)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) compactionError() {
|
func (db *DB) compactionError() {
|
||||||
var (
|
var err error
|
||||||
err error
|
|
||||||
wlocked bool
|
|
||||||
)
|
|
||||||
noerr:
|
noerr:
|
||||||
// No error.
|
// No error.
|
||||||
for {
|
for {
|
||||||
@@ -121,7 +70,7 @@ noerr:
|
|||||||
case err = <-db.compErrSetC:
|
case err = <-db.compErrSetC:
|
||||||
switch {
|
switch {
|
||||||
case err == nil:
|
case err == nil:
|
||||||
case errors.IsCorrupted(err):
|
case err == ErrReadOnly, errors.IsCorrupted(err):
|
||||||
goto hasperr
|
goto hasperr
|
||||||
default:
|
default:
|
||||||
goto haserr
|
goto haserr
|
||||||
@@ -139,7 +88,7 @@ haserr:
|
|||||||
switch {
|
switch {
|
||||||
case err == nil:
|
case err == nil:
|
||||||
goto noerr
|
goto noerr
|
||||||
case errors.IsCorrupted(err):
|
case err == ErrReadOnly, errors.IsCorrupted(err):
|
||||||
goto hasperr
|
goto hasperr
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
@@ -155,9 +104,9 @@ hasperr:
|
|||||||
case db.compPerErrC <- err:
|
case db.compPerErrC <- err:
|
||||||
case db.writeLockC <- struct{}{}:
|
case db.writeLockC <- struct{}{}:
|
||||||
// Hold write lock, so that write won't pass-through.
|
// Hold write lock, so that write won't pass-through.
|
||||||
wlocked = true
|
db.compWriteLocking = true
|
||||||
case _, _ = <-db.closeC:
|
case _, _ = <-db.closeC:
|
||||||
if wlocked {
|
if db.compWriteLocking {
|
||||||
// We should release the lock or Close will hang.
|
// We should release the lock or Close will hang.
|
||||||
<-db.writeLockC
|
<-db.writeLockC
|
||||||
}
|
}
|
||||||
@@ -287,21 +236,18 @@ func (db *DB) compactionExitTransact() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) memCompaction() {
|
func (db *DB) memCompaction() {
|
||||||
mem := db.getFrozenMem()
|
mdb := db.getFrozenMem()
|
||||||
if mem == nil {
|
if mdb == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer mem.decref()
|
defer mdb.decref()
|
||||||
|
|
||||||
c := newCMem(db.s)
|
db.logf("memdb@flush N·%d S·%s", mdb.Len(), shortenb(mdb.Size()))
|
||||||
stats := new(cStatsStaging)
|
|
||||||
|
|
||||||
db.logf("mem@flush N·%d S·%s", mem.mdb.Len(), shortenb(mem.mdb.Size()))
|
|
||||||
|
|
||||||
// Don't compact empty memdb.
|
// Don't compact empty memdb.
|
||||||
if mem.mdb.Len() == 0 {
|
if mdb.Len() == 0 {
|
||||||
db.logf("mem@flush skipping")
|
db.logf("memdb@flush skipping")
|
||||||
// drop frozen mem
|
// drop frozen memdb
|
||||||
db.dropFrozenMem()
|
db.dropFrozenMem()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -317,13 +263,20 @@ func (db *DB) memCompaction() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
db.compactionTransactFunc("mem@flush", func(cnt *compactionTransactCounter) (err error) {
|
var (
|
||||||
|
rec = &sessionRecord{}
|
||||||
|
stats = &cStatsStaging{}
|
||||||
|
flushLevel int
|
||||||
|
)
|
||||||
|
|
||||||
|
db.compactionTransactFunc("memdb@flush", func(cnt *compactionTransactCounter) (err error) {
|
||||||
stats.startTimer()
|
stats.startTimer()
|
||||||
defer stats.stopTimer()
|
flushLevel, err = db.s.flushMemdb(rec, mdb.DB, -1)
|
||||||
return c.flush(mem.mdb, -1)
|
stats.stopTimer()
|
||||||
|
return
|
||||||
}, func() error {
|
}, func() error {
|
||||||
for _, r := range c.rec.addedTables {
|
for _, r := range rec.addedTables {
|
||||||
db.logf("mem@flush revert @%d", r.num)
|
db.logf("memdb@flush revert @%d", r.num)
|
||||||
f := db.s.getTableFile(r.num)
|
f := db.s.getTableFile(r.num)
|
||||||
if err := f.Remove(); err != nil {
|
if err := f.Remove(); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -332,20 +285,23 @@ func (db *DB) memCompaction() {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
db.compactionTransactFunc("mem@commit", func(cnt *compactionTransactCounter) (err error) {
|
db.compactionTransactFunc("memdb@commit", func(cnt *compactionTransactCounter) (err error) {
|
||||||
stats.startTimer()
|
stats.startTimer()
|
||||||
defer stats.stopTimer()
|
rec.setJournalNum(db.journalFile.Num())
|
||||||
return c.commit(db.journalFile.Num(), db.frozenSeq)
|
rec.setSeqNum(db.frozenSeq)
|
||||||
|
err = db.s.commit(rec)
|
||||||
|
stats.stopTimer()
|
||||||
|
return
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
db.logf("mem@flush committed F·%d T·%v", len(c.rec.addedTables), stats.duration)
|
db.logf("memdb@flush committed F·%d T·%v", len(rec.addedTables), stats.duration)
|
||||||
|
|
||||||
for _, r := range c.rec.addedTables {
|
for _, r := range rec.addedTables {
|
||||||
stats.write += r.size
|
stats.write += r.size
|
||||||
}
|
}
|
||||||
db.compStats[c.level].add(stats)
|
db.compStats[flushLevel].add(stats)
|
||||||
|
|
||||||
// Drop frozen mem.
|
// Drop frozen memdb.
|
||||||
db.dropFrozenMem()
|
db.dropFrozenMem()
|
||||||
|
|
||||||
// Resume table compaction.
|
// Resume table compaction.
|
||||||
@@ -557,7 +513,7 @@ func (b *tableCompactionBuilder) revert() error {
|
|||||||
func (db *DB) tableCompaction(c *compaction, noTrivial bool) {
|
func (db *DB) tableCompaction(c *compaction, noTrivial bool) {
|
||||||
defer c.release()
|
defer c.release()
|
||||||
|
|
||||||
rec := &sessionRecord{numLevel: db.s.o.GetNumLevel()}
|
rec := &sessionRecord{}
|
||||||
rec.addCompPtr(c.level, c.imax)
|
rec.addCompPtr(c.level, c.imax)
|
||||||
|
|
||||||
if !noTrivial && c.trivial() {
|
if !noTrivial && c.trivial() {
|
||||||
|
|||||||
+20
-2
@@ -8,6 +8,7 @@ package leveldb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"math/rand"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
@@ -39,11 +40,11 @@ func (db *DB) newRawIterator(slice *util.Range, ro *opt.ReadOptions) iterator.It
|
|||||||
ti := v.getIterators(slice, ro)
|
ti := v.getIterators(slice, ro)
|
||||||
n := len(ti) + 2
|
n := len(ti) + 2
|
||||||
i := make([]iterator.Iterator, 0, n)
|
i := make([]iterator.Iterator, 0, n)
|
||||||
emi := em.mdb.NewIterator(slice)
|
emi := em.NewIterator(slice)
|
||||||
emi.SetReleaser(&memdbReleaser{m: em})
|
emi.SetReleaser(&memdbReleaser{m: em})
|
||||||
i = append(i, emi)
|
i = append(i, emi)
|
||||||
if fm != nil {
|
if fm != nil {
|
||||||
fmi := fm.mdb.NewIterator(slice)
|
fmi := fm.NewIterator(slice)
|
||||||
fmi.SetReleaser(&memdbReleaser{m: fm})
|
fmi.SetReleaser(&memdbReleaser{m: fm})
|
||||||
i = append(i, fmi)
|
i = append(i, fmi)
|
||||||
}
|
}
|
||||||
@@ -80,6 +81,10 @@ func (db *DB) newIterator(seq uint64, slice *util.Range, ro *opt.ReadOptions) *d
|
|||||||
return iter
|
return iter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *DB) iterSamplingRate() int {
|
||||||
|
return rand.Intn(2 * db.s.o.GetIteratorSamplingRate())
|
||||||
|
}
|
||||||
|
|
||||||
type dir int
|
type dir int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -98,6 +103,7 @@ type dbIter struct {
|
|||||||
seq uint64
|
seq uint64
|
||||||
strict bool
|
strict bool
|
||||||
|
|
||||||
|
smaplingGap int
|
||||||
dir dir
|
dir dir
|
||||||
key []byte
|
key []byte
|
||||||
value []byte
|
value []byte
|
||||||
@@ -105,6 +111,15 @@ type dbIter struct {
|
|||||||
releaser util.Releaser
|
releaser util.Releaser
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *dbIter) sampleSeek() {
|
||||||
|
ikey := i.iter.Key()
|
||||||
|
i.smaplingGap -= len(ikey) + len(i.iter.Value())
|
||||||
|
for i.smaplingGap < 0 {
|
||||||
|
i.smaplingGap += i.db.iterSamplingRate()
|
||||||
|
i.db.sampleSeek(ikey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (i *dbIter) setErr(err error) {
|
func (i *dbIter) setErr(err error) {
|
||||||
i.err = err
|
i.err = err
|
||||||
i.key = nil
|
i.key = nil
|
||||||
@@ -175,6 +190,7 @@ func (i *dbIter) Seek(key []byte) bool {
|
|||||||
func (i *dbIter) next() bool {
|
func (i *dbIter) next() bool {
|
||||||
for {
|
for {
|
||||||
if ukey, seq, kt, kerr := parseIkey(i.iter.Key()); kerr == nil {
|
if ukey, seq, kt, kerr := parseIkey(i.iter.Key()); kerr == nil {
|
||||||
|
i.sampleSeek()
|
||||||
if seq <= i.seq {
|
if seq <= i.seq {
|
||||||
switch kt {
|
switch kt {
|
||||||
case ktDel:
|
case ktDel:
|
||||||
@@ -225,6 +241,7 @@ func (i *dbIter) prev() bool {
|
|||||||
if i.iter.Valid() {
|
if i.iter.Valid() {
|
||||||
for {
|
for {
|
||||||
if ukey, seq, kt, kerr := parseIkey(i.iter.Key()); kerr == nil {
|
if ukey, seq, kt, kerr := parseIkey(i.iter.Key()); kerr == nil {
|
||||||
|
i.sampleSeek()
|
||||||
if seq <= i.seq {
|
if seq <= i.seq {
|
||||||
if !del && i.icmp.uCompare(ukey, i.key) < 0 {
|
if !del && i.icmp.uCompare(ukey, i.key) < 0 {
|
||||||
return true
|
return true
|
||||||
@@ -266,6 +283,7 @@ func (i *dbIter) Prev() bool {
|
|||||||
case dirForward:
|
case dirForward:
|
||||||
for i.iter.Prev() {
|
for i.iter.Prev() {
|
||||||
if ukey, _, _, kerr := parseIkey(i.iter.Key()); kerr == nil {
|
if ukey, _, _, kerr := parseIkey(i.iter.Key()); kerr == nil {
|
||||||
|
i.sampleSeek()
|
||||||
if i.icmp.uCompare(ukey, i.key) < 0 {
|
if i.icmp.uCompare(ukey, i.key) < 0 {
|
||||||
goto cont
|
goto cont
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-6
@@ -16,7 +16,7 @@ import (
|
|||||||
|
|
||||||
type memDB struct {
|
type memDB struct {
|
||||||
db *DB
|
db *DB
|
||||||
mdb *memdb.DB
|
*memdb.DB
|
||||||
ref int32
|
ref int32
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,12 +27,12 @@ func (m *memDB) incref() {
|
|||||||
func (m *memDB) decref() {
|
func (m *memDB) decref() {
|
||||||
if ref := atomic.AddInt32(&m.ref, -1); ref == 0 {
|
if ref := atomic.AddInt32(&m.ref, -1); ref == 0 {
|
||||||
// Only put back memdb with std capacity.
|
// Only put back memdb with std capacity.
|
||||||
if m.mdb.Capacity() == m.db.s.o.GetWriteBuffer() {
|
if m.Capacity() == m.db.s.o.GetWriteBuffer() {
|
||||||
m.mdb.Reset()
|
m.Reset()
|
||||||
m.db.mpoolPut(m.mdb)
|
m.db.mpoolPut(m.DB)
|
||||||
}
|
}
|
||||||
m.db = nil
|
m.db = nil
|
||||||
m.mdb = nil
|
m.DB = nil
|
||||||
} else if ref < 0 {
|
} else if ref < 0 {
|
||||||
panic("negative memdb ref")
|
panic("negative memdb ref")
|
||||||
}
|
}
|
||||||
@@ -48,6 +48,15 @@ func (db *DB) addSeq(delta uint64) {
|
|||||||
atomic.AddUint64(&db.seq, delta)
|
atomic.AddUint64(&db.seq, delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *DB) sampleSeek(ikey iKey) {
|
||||||
|
v := db.s.version()
|
||||||
|
if v.sampleSeek(ikey) {
|
||||||
|
// Trigger table compaction.
|
||||||
|
db.compSendTrigger(db.tcompCmdC)
|
||||||
|
}
|
||||||
|
v.release()
|
||||||
|
}
|
||||||
|
|
||||||
func (db *DB) mpoolPut(mem *memdb.DB) {
|
func (db *DB) mpoolPut(mem *memdb.DB) {
|
||||||
defer func() {
|
defer func() {
|
||||||
recover()
|
recover()
|
||||||
@@ -117,7 +126,7 @@ func (db *DB) newMem(n int) (mem *memDB, err error) {
|
|||||||
}
|
}
|
||||||
mem = &memDB{
|
mem = &memDB{
|
||||||
db: db,
|
db: db,
|
||||||
mdb: mdb,
|
DB: mdb,
|
||||||
ref: 2,
|
ref: 2,
|
||||||
}
|
}
|
||||||
db.mem = mem
|
db.mem = mem
|
||||||
|
|||||||
+135
-13
@@ -405,19 +405,21 @@ func (h *dbHarness) compactRange(min, max string) {
|
|||||||
t.Log("DB range compaction done")
|
t.Log("DB range compaction done")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *dbHarness) sizeAssert(start, limit string, low, hi uint64) {
|
func (h *dbHarness) sizeOf(start, limit string) uint64 {
|
||||||
t := h.t
|
sz, err := h.db.SizeOf([]util.Range{
|
||||||
db := h.db
|
|
||||||
|
|
||||||
s, err := db.SizeOf([]util.Range{
|
|
||||||
{[]byte(start), []byte(limit)},
|
{[]byte(start), []byte(limit)},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("SizeOf: got error: ", err)
|
h.t.Error("SizeOf: got error: ", err)
|
||||||
}
|
}
|
||||||
if s.Sum() < low || s.Sum() > hi {
|
return sz.Sum()
|
||||||
t.Errorf("sizeof %q to %q not in range, want %d - %d, got %d",
|
}
|
||||||
shorten(start), shorten(limit), low, hi, s.Sum())
|
|
||||||
|
func (h *dbHarness) sizeAssert(start, limit string, low, hi uint64) {
|
||||||
|
sz := h.sizeOf(start, limit)
|
||||||
|
if sz < low || sz > hi {
|
||||||
|
h.t.Errorf("sizeOf %q to %q not in range, want %d - %d, got %d",
|
||||||
|
shorten(start), shorten(limit), low, hi, sz)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2443,7 +2445,7 @@ func TestDB_TableCompactionBuilder(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
rec := &sessionRecord{numLevel: s.o.GetNumLevel()}
|
rec := &sessionRecord{}
|
||||||
rec.addTableFile(i, tf)
|
rec.addTableFile(i, tf)
|
||||||
if err := s.commit(rec); err != nil {
|
if err := s.commit(rec); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -2453,7 +2455,7 @@ func TestDB_TableCompactionBuilder(t *testing.T) {
|
|||||||
// Build grandparent.
|
// Build grandparent.
|
||||||
v := s.version()
|
v := s.version()
|
||||||
c := newCompaction(s, v, 1, append(tFiles{}, v.tables[1]...))
|
c := newCompaction(s, v, 1, append(tFiles{}, v.tables[1]...))
|
||||||
rec := &sessionRecord{numLevel: s.o.GetNumLevel()}
|
rec := &sessionRecord{}
|
||||||
b := &tableCompactionBuilder{
|
b := &tableCompactionBuilder{
|
||||||
s: s,
|
s: s,
|
||||||
c: c,
|
c: c,
|
||||||
@@ -2477,7 +2479,7 @@ func TestDB_TableCompactionBuilder(t *testing.T) {
|
|||||||
// Build level-1.
|
// Build level-1.
|
||||||
v = s.version()
|
v = s.version()
|
||||||
c = newCompaction(s, v, 0, append(tFiles{}, v.tables[0]...))
|
c = newCompaction(s, v, 0, append(tFiles{}, v.tables[0]...))
|
||||||
rec = &sessionRecord{numLevel: s.o.GetNumLevel()}
|
rec = &sessionRecord{}
|
||||||
b = &tableCompactionBuilder{
|
b = &tableCompactionBuilder{
|
||||||
s: s,
|
s: s,
|
||||||
c: c,
|
c: c,
|
||||||
@@ -2521,7 +2523,7 @@ func TestDB_TableCompactionBuilder(t *testing.T) {
|
|||||||
// Compaction with transient error.
|
// Compaction with transient error.
|
||||||
v = s.version()
|
v = s.version()
|
||||||
c = newCompaction(s, v, 1, append(tFiles{}, v.tables[1]...))
|
c = newCompaction(s, v, 1, append(tFiles{}, v.tables[1]...))
|
||||||
rec = &sessionRecord{numLevel: s.o.GetNumLevel()}
|
rec = &sessionRecord{}
|
||||||
b = &tableCompactionBuilder{
|
b = &tableCompactionBuilder{
|
||||||
s: s,
|
s: s,
|
||||||
c: c,
|
c: c,
|
||||||
@@ -2577,3 +2579,123 @@ func TestDB_TableCompactionBuilder(t *testing.T) {
|
|||||||
}
|
}
|
||||||
v.release()
|
v.release()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testDB_IterTriggeredCompaction(t *testing.T, limitDiv int) {
|
||||||
|
const (
|
||||||
|
vSize = 200 * opt.KiB
|
||||||
|
tSize = 100 * opt.MiB
|
||||||
|
mIter = 100
|
||||||
|
n = tSize / vSize
|
||||||
|
)
|
||||||
|
|
||||||
|
h := newDbHarnessWopt(t, &opt.Options{
|
||||||
|
Compression: opt.NoCompression,
|
||||||
|
DisableBlockCache: true,
|
||||||
|
})
|
||||||
|
defer h.close()
|
||||||
|
|
||||||
|
key := func(x int) string {
|
||||||
|
return fmt.Sprintf("v%06d", x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill.
|
||||||
|
value := strings.Repeat("x", vSize)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
h.put(key(i), value)
|
||||||
|
}
|
||||||
|
h.compactMem()
|
||||||
|
|
||||||
|
// Delete all.
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
h.delete(key(i))
|
||||||
|
}
|
||||||
|
h.compactMem()
|
||||||
|
|
||||||
|
var (
|
||||||
|
limit = n / limitDiv
|
||||||
|
|
||||||
|
startKey = key(0)
|
||||||
|
limitKey = key(limit)
|
||||||
|
maxKey = key(n)
|
||||||
|
slice = &util.Range{Limit: []byte(limitKey)}
|
||||||
|
|
||||||
|
initialSize0 = h.sizeOf(startKey, limitKey)
|
||||||
|
initialSize1 = h.sizeOf(limitKey, maxKey)
|
||||||
|
)
|
||||||
|
|
||||||
|
t.Logf("inital size %s [rest %s]", shortenb(int(initialSize0)), shortenb(int(initialSize1)))
|
||||||
|
|
||||||
|
for r := 0; true; r++ {
|
||||||
|
if r >= mIter {
|
||||||
|
t.Fatal("taking too long to compact")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterates.
|
||||||
|
iter := h.db.NewIterator(slice, h.ro)
|
||||||
|
for iter.Next() {
|
||||||
|
}
|
||||||
|
if err := iter.Error(); err != nil {
|
||||||
|
t.Fatalf("Iter err: %v", err)
|
||||||
|
}
|
||||||
|
iter.Release()
|
||||||
|
|
||||||
|
// Wait compaction.
|
||||||
|
h.waitCompaction()
|
||||||
|
|
||||||
|
// Check size.
|
||||||
|
size0 := h.sizeOf(startKey, limitKey)
|
||||||
|
size1 := h.sizeOf(limitKey, maxKey)
|
||||||
|
t.Logf("#%03d size %s [rest %s]", r, shortenb(int(size0)), shortenb(int(size1)))
|
||||||
|
if size0 < initialSize0/10 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if initialSize1 > 0 {
|
||||||
|
h.sizeAssert(limitKey, maxKey, initialSize1/4-opt.MiB, initialSize1+opt.MiB)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDB_IterTriggeredCompaction(t *testing.T) {
|
||||||
|
testDB_IterTriggeredCompaction(t, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDB_IterTriggeredCompactionHalf(t *testing.T) {
|
||||||
|
testDB_IterTriggeredCompaction(t, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDB_ReadOnly(t *testing.T) {
|
||||||
|
h := newDbHarness(t)
|
||||||
|
defer h.close()
|
||||||
|
|
||||||
|
h.put("foo", "v1")
|
||||||
|
h.put("bar", "v2")
|
||||||
|
h.compactMem()
|
||||||
|
|
||||||
|
h.put("xfoo", "v1")
|
||||||
|
h.put("xbar", "v2")
|
||||||
|
|
||||||
|
t.Log("Trigger read-only")
|
||||||
|
if err := h.db.SetReadOnly(); err != nil {
|
||||||
|
h.close()
|
||||||
|
t.Fatalf("SetReadOnly error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
h.stor.SetEmuErr(storage.TypeAll, tsOpCreate, tsOpReplace, tsOpRemove, tsOpWrite, tsOpWrite, tsOpSync)
|
||||||
|
|
||||||
|
ro := func(key, value, wantValue string) {
|
||||||
|
if err := h.db.Put([]byte(key), []byte(value), h.wo); err != ErrReadOnly {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
h.getVal(key, wantValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
ro("foo", "vx", "v1")
|
||||||
|
|
||||||
|
h.o.ReadOnly = true
|
||||||
|
h.reopenDB()
|
||||||
|
|
||||||
|
ro("foo", "vx", "v1")
|
||||||
|
ro("bar", "vx", "v2")
|
||||||
|
h.assertNumKeys(4)
|
||||||
|
}
|
||||||
|
|||||||
+52
-25
@@ -63,24 +63,24 @@ func (db *DB) rotateMem(n int) (mem *memDB, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) flush(n int) (mem *memDB, nn int, err error) {
|
func (db *DB) flush(n int) (mdb *memDB, mdbFree int, err error) {
|
||||||
delayed := false
|
delayed := false
|
||||||
flush := func() (retry bool) {
|
flush := func() (retry bool) {
|
||||||
v := db.s.version()
|
v := db.s.version()
|
||||||
defer v.release()
|
defer v.release()
|
||||||
mem = db.getEffectiveMem()
|
mdb = db.getEffectiveMem()
|
||||||
defer func() {
|
defer func() {
|
||||||
if retry {
|
if retry {
|
||||||
mem.decref()
|
mdb.decref()
|
||||||
mem = nil
|
mdb = nil
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
nn = mem.mdb.Free()
|
mdbFree = mdb.Free()
|
||||||
switch {
|
switch {
|
||||||
case v.tLen(0) >= db.s.o.GetWriteL0SlowdownTrigger() && !delayed:
|
case v.tLen(0) >= db.s.o.GetWriteL0SlowdownTrigger() && !delayed:
|
||||||
delayed = true
|
delayed = true
|
||||||
time.Sleep(time.Millisecond)
|
time.Sleep(time.Millisecond)
|
||||||
case nn >= n:
|
case mdbFree >= n:
|
||||||
return false
|
return false
|
||||||
case v.tLen(0) >= db.s.o.GetWriteL0PauseTrigger():
|
case v.tLen(0) >= db.s.o.GetWriteL0PauseTrigger():
|
||||||
delayed = true
|
delayed = true
|
||||||
@@ -90,15 +90,15 @@ func (db *DB) flush(n int) (mem *memDB, nn int, err error) {
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
// Allow memdb to grow if it has no entry.
|
// Allow memdb to grow if it has no entry.
|
||||||
if mem.mdb.Len() == 0 {
|
if mdb.Len() == 0 {
|
||||||
nn = n
|
mdbFree = n
|
||||||
} else {
|
} else {
|
||||||
mem.decref()
|
mdb.decref()
|
||||||
mem, err = db.rotateMem(n)
|
mdb, err = db.rotateMem(n)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
nn = mem.mdb.Free()
|
mdbFree = mdb.Free()
|
||||||
} else {
|
} else {
|
||||||
nn = 0
|
mdbFree = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -129,7 +129,7 @@ func (db *DB) Write(b *Batch, wo *opt.WriteOptions) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
b.init(wo.GetSync())
|
b.init(wo.GetSync() && !db.s.o.GetNoSync())
|
||||||
|
|
||||||
// The write happen synchronously.
|
// The write happen synchronously.
|
||||||
select {
|
select {
|
||||||
@@ -157,18 +157,18 @@ func (db *DB) Write(b *Batch, wo *opt.WriteOptions) (err error) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
mem, memFree, err := db.flush(b.size())
|
mdb, mdbFree, err := db.flush(b.size())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer mem.decref()
|
defer mdb.decref()
|
||||||
|
|
||||||
// Calculate maximum size of the batch.
|
// Calculate maximum size of the batch.
|
||||||
m := 1 << 20
|
m := 1 << 20
|
||||||
if x := b.size(); x <= 128<<10 {
|
if x := b.size(); x <= 128<<10 {
|
||||||
m = x + (128 << 10)
|
m = x + (128 << 10)
|
||||||
}
|
}
|
||||||
m = minInt(m, memFree)
|
m = minInt(m, mdbFree)
|
||||||
|
|
||||||
// Merge with other batch.
|
// Merge with other batch.
|
||||||
drain:
|
drain:
|
||||||
@@ -197,7 +197,7 @@ drain:
|
|||||||
select {
|
select {
|
||||||
case db.journalC <- b:
|
case db.journalC <- b:
|
||||||
// Write into memdb
|
// Write into memdb
|
||||||
if berr := b.memReplay(mem.mdb); berr != nil {
|
if berr := b.memReplay(mdb.DB); berr != nil {
|
||||||
panic(berr)
|
panic(berr)
|
||||||
}
|
}
|
||||||
case err = <-db.compPerErrC:
|
case err = <-db.compPerErrC:
|
||||||
@@ -211,7 +211,7 @@ drain:
|
|||||||
case err = <-db.journalAckC:
|
case err = <-db.journalAckC:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Revert memdb if error detected
|
// Revert memdb if error detected
|
||||||
if berr := b.revertMemReplay(mem.mdb); berr != nil {
|
if berr := b.revertMemReplay(mdb.DB); berr != nil {
|
||||||
panic(berr)
|
panic(berr)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -225,7 +225,7 @@ drain:
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if berr := b.memReplay(mem.mdb); berr != nil {
|
if berr := b.memReplay(mdb.DB); berr != nil {
|
||||||
panic(berr)
|
panic(berr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,7 +233,7 @@ drain:
|
|||||||
// Set last seq number.
|
// Set last seq number.
|
||||||
db.addSeq(uint64(b.Len()))
|
db.addSeq(uint64(b.Len()))
|
||||||
|
|
||||||
if b.size() >= memFree {
|
if b.size() >= mdbFree {
|
||||||
db.rotateMem(0)
|
db.rotateMem(0)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -249,8 +249,7 @@ func (db *DB) Put(key, value []byte, wo *opt.WriteOptions) error {
|
|||||||
return db.Write(b, wo)
|
return db.Write(b, wo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete deletes the value for the given key. It returns ErrNotFound if
|
// Delete deletes the value for the given key.
|
||||||
// the DB does not contain the key.
|
|
||||||
//
|
//
|
||||||
// It is safe to modify the contents of the arguments after Delete returns.
|
// It is safe to modify the contents of the arguments after Delete returns.
|
||||||
func (db *DB) Delete(key []byte, wo *opt.WriteOptions) error {
|
func (db *DB) Delete(key []byte, wo *opt.WriteOptions) error {
|
||||||
@@ -290,9 +289,9 @@ func (db *DB) CompactRange(r util.Range) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for overlaps in memdb.
|
// Check for overlaps in memdb.
|
||||||
mem := db.getEffectiveMem()
|
mdb := db.getEffectiveMem()
|
||||||
defer mem.decref()
|
defer mdb.decref()
|
||||||
if isMemOverlaps(db.s.icmp, mem.mdb, r.Start, r.Limit) {
|
if isMemOverlaps(db.s.icmp, mdb.DB, r.Start, r.Limit) {
|
||||||
// Memdb compaction.
|
// Memdb compaction.
|
||||||
if _, err := db.rotateMem(0); err != nil {
|
if _, err := db.rotateMem(0); err != nil {
|
||||||
<-db.writeLockC
|
<-db.writeLockC
|
||||||
@@ -309,3 +308,31 @@ func (db *DB) CompactRange(r util.Range) error {
|
|||||||
// Table compaction.
|
// Table compaction.
|
||||||
return db.compSendRange(db.tcompCmdC, -1, r.Start, r.Limit)
|
return db.compSendRange(db.tcompCmdC, -1, r.Start, r.Limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetReadOnly makes DB read-only. It will stay read-only until reopened.
|
||||||
|
func (db *DB) SetReadOnly() error {
|
||||||
|
if err := db.ok(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock writer.
|
||||||
|
select {
|
||||||
|
case db.writeLockC <- struct{}{}:
|
||||||
|
db.compWriteLocking = true
|
||||||
|
case err := <-db.compPerErrC:
|
||||||
|
return err
|
||||||
|
case _, _ = <-db.closeC:
|
||||||
|
return ErrClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set compaction read-only.
|
||||||
|
select {
|
||||||
|
case db.compErrSetC <- ErrReadOnly:
|
||||||
|
case perr := <-db.compPerErrC:
|
||||||
|
return perr
|
||||||
|
case _, _ = <-db.closeC:
|
||||||
|
return ErrClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
+1
@@ -12,6 +12,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
ErrNotFound = errors.ErrNotFound
|
ErrNotFound = errors.ErrNotFound
|
||||||
|
ErrReadOnly = errors.New("leveldb: read-only mode")
|
||||||
ErrSnapshotReleased = errors.New("leveldb: snapshot released")
|
ErrSnapshotReleased = errors.New("leveldb: snapshot released")
|
||||||
ErrIterReleased = errors.New("leveldb: iterator released")
|
ErrIterReleased = errors.New("leveldb: iterator released")
|
||||||
ErrClosed = errors.New("leveldb: closed")
|
ErrClosed = errors.New("leveldb: closed")
|
||||||
|
|||||||
+3
-1
@@ -52,12 +52,14 @@ func IsCorrupted(err error) bool {
|
|||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
case *ErrCorrupted:
|
case *ErrCorrupted:
|
||||||
return true
|
return true
|
||||||
|
case *storage.ErrCorrupted:
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrMissingFiles is the type that indicating a corruption due to missing
|
// ErrMissingFiles is the type that indicating a corruption due to missing
|
||||||
// files.
|
// files. ErrMissingFiles always wrapped with ErrCorrupted.
|
||||||
type ErrMissingFiles struct {
|
type ErrMissingFiles struct {
|
||||||
Files []*storage.FileInfo
|
Files []*storage.FileInfo
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-3
@@ -206,6 +206,7 @@ func (p *DB) randHeight() (h int) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Must hold RW-lock if prev == true, as it use shared prevNode slice.
|
||||||
func (p *DB) findGE(key []byte, prev bool) (int, bool) {
|
func (p *DB) findGE(key []byte, prev bool) (int, bool) {
|
||||||
node := 0
|
node := 0
|
||||||
h := p.maxHeight - 1
|
h := p.maxHeight - 1
|
||||||
@@ -302,7 +303,7 @@ func (p *DB) Put(key []byte, value []byte) error {
|
|||||||
node := len(p.nodeData)
|
node := len(p.nodeData)
|
||||||
p.nodeData = append(p.nodeData, kvOffset, len(key), len(value), h)
|
p.nodeData = append(p.nodeData, kvOffset, len(key), len(value), h)
|
||||||
for i, n := range p.prevNode[:h] {
|
for i, n := range p.prevNode[:h] {
|
||||||
m := n + 4 + i
|
m := n + nNext + i
|
||||||
p.nodeData = append(p.nodeData, p.nodeData[m])
|
p.nodeData = append(p.nodeData, p.nodeData[m])
|
||||||
p.nodeData[m] = node
|
p.nodeData[m] = node
|
||||||
}
|
}
|
||||||
@@ -434,20 +435,22 @@ func (p *DB) Len() int {
|
|||||||
|
|
||||||
// Reset resets the DB to initial empty state. Allows reuse the buffer.
|
// Reset resets the DB to initial empty state. Allows reuse the buffer.
|
||||||
func (p *DB) Reset() {
|
func (p *DB) Reset() {
|
||||||
|
p.mu.Lock()
|
||||||
p.rnd = rand.New(rand.NewSource(0xdeadbeef))
|
p.rnd = rand.New(rand.NewSource(0xdeadbeef))
|
||||||
p.maxHeight = 1
|
p.maxHeight = 1
|
||||||
p.n = 0
|
p.n = 0
|
||||||
p.kvSize = 0
|
p.kvSize = 0
|
||||||
p.kvData = p.kvData[:0]
|
p.kvData = p.kvData[:0]
|
||||||
p.nodeData = p.nodeData[:4+tMaxHeight]
|
p.nodeData = p.nodeData[:nNext+tMaxHeight]
|
||||||
p.nodeData[nKV] = 0
|
p.nodeData[nKV] = 0
|
||||||
p.nodeData[nKey] = 0
|
p.nodeData[nKey] = 0
|
||||||
p.nodeData[nVal] = 0
|
p.nodeData[nVal] = 0
|
||||||
p.nodeData[nHeight] = tMaxHeight
|
p.nodeData[nHeight] = tMaxHeight
|
||||||
for n := 0; n < tMaxHeight; n++ {
|
for n := 0; n < tMaxHeight; n++ {
|
||||||
p.nodeData[4+n] = 0
|
p.nodeData[nNext+n] = 0
|
||||||
p.prevNode[n] = 0
|
p.prevNode[n] = 0
|
||||||
}
|
}
|
||||||
|
p.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new initalized in-memory key/value DB. The capacity
|
// New creates a new initalized in-memory key/value DB. The capacity
|
||||||
|
|||||||
+60
-2
@@ -34,10 +34,11 @@ var (
|
|||||||
DefaultCompactionTotalSize = 10 * MiB
|
DefaultCompactionTotalSize = 10 * MiB
|
||||||
DefaultCompactionTotalSizeMultiplier = 10.0
|
DefaultCompactionTotalSizeMultiplier = 10.0
|
||||||
DefaultCompressionType = SnappyCompression
|
DefaultCompressionType = SnappyCompression
|
||||||
DefaultOpenFilesCacher = LRUCacher
|
DefaultIteratorSamplingRate = 1 * MiB
|
||||||
DefaultOpenFilesCacheCapacity = 500
|
|
||||||
DefaultMaxMemCompationLevel = 2
|
DefaultMaxMemCompationLevel = 2
|
||||||
DefaultNumLevel = 7
|
DefaultNumLevel = 7
|
||||||
|
DefaultOpenFilesCacher = LRUCacher
|
||||||
|
DefaultOpenFilesCacheCapacity = 500
|
||||||
DefaultWriteBuffer = 4 * MiB
|
DefaultWriteBuffer = 4 * MiB
|
||||||
DefaultWriteL0PauseTrigger = 12
|
DefaultWriteL0PauseTrigger = 12
|
||||||
DefaultWriteL0SlowdownTrigger = 8
|
DefaultWriteL0SlowdownTrigger = 8
|
||||||
@@ -249,6 +250,11 @@ type Options struct {
|
|||||||
// The default value (DefaultCompression) uses snappy compression.
|
// The default value (DefaultCompression) uses snappy compression.
|
||||||
Compression Compression
|
Compression Compression
|
||||||
|
|
||||||
|
// DisableBufferPool allows disable use of util.BufferPool functionality.
|
||||||
|
//
|
||||||
|
// The default value is false.
|
||||||
|
DisableBufferPool bool
|
||||||
|
|
||||||
// DisableBlockCache allows disable use of cache.Cache functionality on
|
// DisableBlockCache allows disable use of cache.Cache functionality on
|
||||||
// 'sorted table' block.
|
// 'sorted table' block.
|
||||||
//
|
//
|
||||||
@@ -288,6 +294,13 @@ type Options struct {
|
|||||||
// The default value is nil.
|
// The default value is nil.
|
||||||
Filter filter.Filter
|
Filter filter.Filter
|
||||||
|
|
||||||
|
// IteratorSamplingRate defines approximate gap (in bytes) between read
|
||||||
|
// sampling of an iterator. The samples will be used to determine when
|
||||||
|
// compaction should be triggered.
|
||||||
|
//
|
||||||
|
// The default is 1MiB.
|
||||||
|
IteratorSamplingRate int
|
||||||
|
|
||||||
// MaxMemCompationLevel defines maximum level a newly compacted 'memdb'
|
// MaxMemCompationLevel defines maximum level a newly compacted 'memdb'
|
||||||
// will be pushed into if doesn't creates overlap. This should less than
|
// will be pushed into if doesn't creates overlap. This should less than
|
||||||
// NumLevel. Use -1 for level-0.
|
// NumLevel. Use -1 for level-0.
|
||||||
@@ -295,6 +308,11 @@ type Options struct {
|
|||||||
// The default is 2.
|
// The default is 2.
|
||||||
MaxMemCompationLevel int
|
MaxMemCompationLevel int
|
||||||
|
|
||||||
|
// NoSync allows completely disable fsync.
|
||||||
|
//
|
||||||
|
// The default is false.
|
||||||
|
NoSync bool
|
||||||
|
|
||||||
// NumLevel defines number of database level. The level shouldn't changed
|
// NumLevel defines number of database level. The level shouldn't changed
|
||||||
// between opens, or the database will panic.
|
// between opens, or the database will panic.
|
||||||
//
|
//
|
||||||
@@ -313,6 +331,11 @@ type Options struct {
|
|||||||
// The default value is 500.
|
// The default value is 500.
|
||||||
OpenFilesCacheCapacity int
|
OpenFilesCacheCapacity int
|
||||||
|
|
||||||
|
// If true then opens DB in read-only mode.
|
||||||
|
//
|
||||||
|
// The default value is false.
|
||||||
|
ReadOnly bool
|
||||||
|
|
||||||
// Strict defines the DB strict level.
|
// Strict defines the DB strict level.
|
||||||
Strict Strict
|
Strict Strict
|
||||||
|
|
||||||
@@ -464,6 +487,20 @@ func (o *Options) GetCompression() Compression {
|
|||||||
return o.Compression
|
return o.Compression
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *Options) GetDisableBufferPool() bool {
|
||||||
|
if o == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return o.DisableBufferPool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) GetDisableBlockCache() bool {
|
||||||
|
if o == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return o.DisableBlockCache
|
||||||
|
}
|
||||||
|
|
||||||
func (o *Options) GetDisableCompactionBackoff() bool {
|
func (o *Options) GetDisableCompactionBackoff() bool {
|
||||||
if o == nil {
|
if o == nil {
|
||||||
return false
|
return false
|
||||||
@@ -492,6 +529,13 @@ func (o *Options) GetFilter() filter.Filter {
|
|||||||
return o.Filter
|
return o.Filter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *Options) GetIteratorSamplingRate() int {
|
||||||
|
if o == nil || o.IteratorSamplingRate <= 0 {
|
||||||
|
return DefaultIteratorSamplingRate
|
||||||
|
}
|
||||||
|
return o.IteratorSamplingRate
|
||||||
|
}
|
||||||
|
|
||||||
func (o *Options) GetMaxMemCompationLevel() int {
|
func (o *Options) GetMaxMemCompationLevel() int {
|
||||||
level := DefaultMaxMemCompationLevel
|
level := DefaultMaxMemCompationLevel
|
||||||
if o != nil {
|
if o != nil {
|
||||||
@@ -507,6 +551,13 @@ func (o *Options) GetMaxMemCompationLevel() int {
|
|||||||
return level
|
return level
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *Options) GetNoSync() bool {
|
||||||
|
if o == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return o.NoSync
|
||||||
|
}
|
||||||
|
|
||||||
func (o *Options) GetNumLevel() int {
|
func (o *Options) GetNumLevel() int {
|
||||||
if o == nil || o.NumLevel <= 0 {
|
if o == nil || o.NumLevel <= 0 {
|
||||||
return DefaultNumLevel
|
return DefaultNumLevel
|
||||||
@@ -533,6 +584,13 @@ func (o *Options) GetOpenFilesCacheCapacity() int {
|
|||||||
return o.OpenFilesCacheCapacity
|
return o.OpenFilesCacheCapacity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *Options) GetReadOnly() bool {
|
||||||
|
if o == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return o.ReadOnly
|
||||||
|
}
|
||||||
|
|
||||||
func (o *Options) GetStrict(strict Strict) bool {
|
func (o *Options) GetStrict(strict Strict) bool {
|
||||||
if o == nil || o.Strict == 0 {
|
if o == nil || o.Strict == 0 {
|
||||||
return DefaultStrict&strict != 0
|
return DefaultStrict&strict != 0
|
||||||
|
|||||||
+10
-254
@@ -11,10 +11,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
|
||||||
|
|
||||||
"github.com/syndtr/goleveldb/leveldb/errors"
|
"github.com/syndtr/goleveldb/leveldb/errors"
|
||||||
"github.com/syndtr/goleveldb/leveldb/iterator"
|
|
||||||
"github.com/syndtr/goleveldb/leveldb/journal"
|
"github.com/syndtr/goleveldb/leveldb/journal"
|
||||||
"github.com/syndtr/goleveldb/leveldb/opt"
|
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||||
"github.com/syndtr/goleveldb/leveldb/storage"
|
"github.com/syndtr/goleveldb/leveldb/storage"
|
||||||
@@ -127,11 +125,16 @@ func (s *session) recover() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer reader.Close()
|
defer reader.Close()
|
||||||
strict := s.o.GetStrict(opt.StrictManifest)
|
|
||||||
jr := journal.NewReader(reader, dropper{s, m}, strict, true)
|
|
||||||
|
|
||||||
staging := s.stVersion.newStaging()
|
var (
|
||||||
rec := &sessionRecord{numLevel: s.o.GetNumLevel()}
|
// Options.
|
||||||
|
numLevel = s.o.GetNumLevel()
|
||||||
|
strict = s.o.GetStrict(opt.StrictManifest)
|
||||||
|
|
||||||
|
jr = journal.NewReader(reader, dropper{s, m}, strict, true)
|
||||||
|
rec = &sessionRecord{}
|
||||||
|
staging = s.stVersion.newStaging()
|
||||||
|
)
|
||||||
for {
|
for {
|
||||||
var r io.Reader
|
var r io.Reader
|
||||||
r, err = jr.Next()
|
r, err = jr.Next()
|
||||||
@@ -143,7 +146,7 @@ func (s *session) recover() (err error) {
|
|||||||
return errors.SetFile(err, m)
|
return errors.SetFile(err, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = rec.decode(r)
|
err = rec.decode(r, numLevel)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// save compact pointers
|
// save compact pointers
|
||||||
for _, r := range rec.compPtrs {
|
for _, r := range rec.compPtrs {
|
||||||
@@ -206,250 +209,3 @@ func (s *session) commit(r *sessionRecord) (err error) {
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pick a compaction based on current state; need external synchronization.
|
|
||||||
func (s *session) pickCompaction() *compaction {
|
|
||||||
v := s.version()
|
|
||||||
|
|
||||||
var level int
|
|
||||||
var t0 tFiles
|
|
||||||
if v.cScore >= 1 {
|
|
||||||
level = v.cLevel
|
|
||||||
cptr := s.stCompPtrs[level]
|
|
||||||
tables := v.tables[level]
|
|
||||||
for _, t := range tables {
|
|
||||||
if cptr == nil || s.icmp.Compare(t.imax, cptr) > 0 {
|
|
||||||
t0 = append(t0, t)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(t0) == 0 {
|
|
||||||
t0 = append(t0, tables[0])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if p := atomic.LoadPointer(&v.cSeek); p != nil {
|
|
||||||
ts := (*tSet)(p)
|
|
||||||
level = ts.level
|
|
||||||
t0 = append(t0, ts.table)
|
|
||||||
} else {
|
|
||||||
v.release()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return newCompaction(s, v, level, t0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create compaction from given level and range; need external synchronization.
|
|
||||||
func (s *session) getCompactionRange(level int, umin, umax []byte) *compaction {
|
|
||||||
v := s.version()
|
|
||||||
|
|
||||||
t0 := v.tables[level].getOverlaps(nil, s.icmp, umin, umax, level == 0)
|
|
||||||
if len(t0) == 0 {
|
|
||||||
v.release()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid compacting too much in one shot in case the range is large.
|
|
||||||
// But we cannot do this for level-0 since level-0 files can overlap
|
|
||||||
// and we must not pick one file and drop another older file if the
|
|
||||||
// two files overlap.
|
|
||||||
if level > 0 {
|
|
||||||
limit := uint64(v.s.o.GetCompactionSourceLimit(level))
|
|
||||||
total := uint64(0)
|
|
||||||
for i, t := range t0 {
|
|
||||||
total += t.size
|
|
||||||
if total >= limit {
|
|
||||||
s.logf("table@compaction limiting F·%d -> F·%d", len(t0), i+1)
|
|
||||||
t0 = t0[:i+1]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return newCompaction(s, v, level, t0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newCompaction(s *session, v *version, level int, t0 tFiles) *compaction {
|
|
||||||
c := &compaction{
|
|
||||||
s: s,
|
|
||||||
v: v,
|
|
||||||
level: level,
|
|
||||||
tables: [2]tFiles{t0, nil},
|
|
||||||
maxGPOverlaps: uint64(s.o.GetCompactionGPOverlaps(level)),
|
|
||||||
tPtrs: make([]int, s.o.GetNumLevel()),
|
|
||||||
}
|
|
||||||
c.expand()
|
|
||||||
c.save()
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// compaction represent a compaction state.
|
|
||||||
type compaction struct {
|
|
||||||
s *session
|
|
||||||
v *version
|
|
||||||
|
|
||||||
level int
|
|
||||||
tables [2]tFiles
|
|
||||||
maxGPOverlaps uint64
|
|
||||||
|
|
||||||
gp tFiles
|
|
||||||
gpi int
|
|
||||||
seenKey bool
|
|
||||||
gpOverlappedBytes uint64
|
|
||||||
imin, imax iKey
|
|
||||||
tPtrs []int
|
|
||||||
released bool
|
|
||||||
|
|
||||||
snapGPI int
|
|
||||||
snapSeenKey bool
|
|
||||||
snapGPOverlappedBytes uint64
|
|
||||||
snapTPtrs []int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *compaction) save() {
|
|
||||||
c.snapGPI = c.gpi
|
|
||||||
c.snapSeenKey = c.seenKey
|
|
||||||
c.snapGPOverlappedBytes = c.gpOverlappedBytes
|
|
||||||
c.snapTPtrs = append(c.snapTPtrs[:0], c.tPtrs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *compaction) restore() {
|
|
||||||
c.gpi = c.snapGPI
|
|
||||||
c.seenKey = c.snapSeenKey
|
|
||||||
c.gpOverlappedBytes = c.snapGPOverlappedBytes
|
|
||||||
c.tPtrs = append(c.tPtrs[:0], c.snapTPtrs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *compaction) release() {
|
|
||||||
if !c.released {
|
|
||||||
c.released = true
|
|
||||||
c.v.release()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expand compacted tables; need external synchronization.
|
|
||||||
func (c *compaction) expand() {
|
|
||||||
limit := uint64(c.s.o.GetCompactionExpandLimit(c.level))
|
|
||||||
vt0, vt1 := c.v.tables[c.level], c.v.tables[c.level+1]
|
|
||||||
|
|
||||||
t0, t1 := c.tables[0], c.tables[1]
|
|
||||||
imin, imax := t0.getRange(c.s.icmp)
|
|
||||||
// We expand t0 here just incase ukey hop across tables.
|
|
||||||
t0 = vt0.getOverlaps(t0, c.s.icmp, imin.ukey(), imax.ukey(), c.level == 0)
|
|
||||||
if len(t0) != len(c.tables[0]) {
|
|
||||||
imin, imax = t0.getRange(c.s.icmp)
|
|
||||||
}
|
|
||||||
t1 = vt1.getOverlaps(t1, c.s.icmp, imin.ukey(), imax.ukey(), false)
|
|
||||||
// Get entire range covered by compaction.
|
|
||||||
amin, amax := append(t0, t1...).getRange(c.s.icmp)
|
|
||||||
|
|
||||||
// See if we can grow the number of inputs in "level" without
|
|
||||||
// changing the number of "level+1" files we pick up.
|
|
||||||
if len(t1) > 0 {
|
|
||||||
exp0 := vt0.getOverlaps(nil, c.s.icmp, amin.ukey(), amax.ukey(), c.level == 0)
|
|
||||||
if len(exp0) > len(t0) && t1.size()+exp0.size() < limit {
|
|
||||||
xmin, xmax := exp0.getRange(c.s.icmp)
|
|
||||||
exp1 := vt1.getOverlaps(nil, c.s.icmp, xmin.ukey(), xmax.ukey(), false)
|
|
||||||
if len(exp1) == len(t1) {
|
|
||||||
c.s.logf("table@compaction expanding L%d+L%d (F·%d S·%s)+(F·%d S·%s) -> (F·%d S·%s)+(F·%d S·%s)",
|
|
||||||
c.level, c.level+1, len(t0), shortenb(int(t0.size())), len(t1), shortenb(int(t1.size())),
|
|
||||||
len(exp0), shortenb(int(exp0.size())), len(exp1), shortenb(int(exp1.size())))
|
|
||||||
imin, imax = xmin, xmax
|
|
||||||
t0, t1 = exp0, exp1
|
|
||||||
amin, amax = append(t0, t1...).getRange(c.s.icmp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute the set of grandparent files that overlap this compaction
|
|
||||||
// (parent == level+1; grandparent == level+2)
|
|
||||||
if c.level+2 < c.s.o.GetNumLevel() {
|
|
||||||
c.gp = c.v.tables[c.level+2].getOverlaps(c.gp, c.s.icmp, amin.ukey(), amax.ukey(), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.tables[0], c.tables[1] = t0, t1
|
|
||||||
c.imin, c.imax = imin, imax
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether compaction is trivial.
|
|
||||||
func (c *compaction) trivial() bool {
|
|
||||||
return len(c.tables[0]) == 1 && len(c.tables[1]) == 0 && c.gp.size() <= c.maxGPOverlaps
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *compaction) baseLevelForKey(ukey []byte) bool {
|
|
||||||
for level, tables := range c.v.tables[c.level+2:] {
|
|
||||||
for c.tPtrs[level] < len(tables) {
|
|
||||||
t := tables[c.tPtrs[level]]
|
|
||||||
if c.s.icmp.uCompare(ukey, t.imax.ukey()) <= 0 {
|
|
||||||
// We've advanced far enough.
|
|
||||||
if c.s.icmp.uCompare(ukey, t.imin.ukey()) >= 0 {
|
|
||||||
// Key falls in this file's range, so definitely not base level.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
c.tPtrs[level]++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *compaction) shouldStopBefore(ikey iKey) bool {
|
|
||||||
for ; c.gpi < len(c.gp); c.gpi++ {
|
|
||||||
gp := c.gp[c.gpi]
|
|
||||||
if c.s.icmp.Compare(ikey, gp.imax) <= 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if c.seenKey {
|
|
||||||
c.gpOverlappedBytes += gp.size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.seenKey = true
|
|
||||||
|
|
||||||
if c.gpOverlappedBytes > c.maxGPOverlaps {
|
|
||||||
// Too much overlap for current output; start new output.
|
|
||||||
c.gpOverlappedBytes = 0
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates an iterator.
|
|
||||||
func (c *compaction) newIterator() iterator.Iterator {
|
|
||||||
// Creates iterator slice.
|
|
||||||
icap := len(c.tables)
|
|
||||||
if c.level == 0 {
|
|
||||||
// Special case for level-0
|
|
||||||
icap = len(c.tables[0]) + 1
|
|
||||||
}
|
|
||||||
its := make([]iterator.Iterator, 0, icap)
|
|
||||||
|
|
||||||
// Options.
|
|
||||||
ro := &opt.ReadOptions{
|
|
||||||
DontFillCache: true,
|
|
||||||
Strict: opt.StrictOverride,
|
|
||||||
}
|
|
||||||
strict := c.s.o.GetStrict(opt.StrictCompaction)
|
|
||||||
if strict {
|
|
||||||
ro.Strict |= opt.StrictReader
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tables := range c.tables {
|
|
||||||
if len(tables) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Level-0 is not sorted and may overlaps each other.
|
|
||||||
if c.level+i == 0 {
|
|
||||||
for _, t := range tables {
|
|
||||||
its = append(its, c.s.tops.newIterator(t, nil, ro))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
it := iterator.NewIndexedIterator(tables.newIndexIterator(c.s.tops, c.s.icmp, nil, ro), strict)
|
|
||||||
its = append(its, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return iterator.NewMergedIterator(its, c.s.icmp, strict)
|
|
||||||
}
|
|
||||||
|
|||||||
+287
@@ -0,0 +1,287 @@
|
|||||||
|
// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
package leveldb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/syndtr/goleveldb/leveldb/iterator"
|
||||||
|
"github.com/syndtr/goleveldb/leveldb/memdb"
|
||||||
|
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *session) pickMemdbLevel(umin, umax []byte) int {
|
||||||
|
v := s.version()
|
||||||
|
defer v.release()
|
||||||
|
return v.pickMemdbLevel(umin, umax)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *session) flushMemdb(rec *sessionRecord, mdb *memdb.DB, level int) (level_ int, err error) {
|
||||||
|
// Create sorted table.
|
||||||
|
iter := mdb.NewIterator(nil)
|
||||||
|
defer iter.Release()
|
||||||
|
t, n, err := s.tops.createFrom(iter)
|
||||||
|
if err != nil {
|
||||||
|
return level, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick level and add to record.
|
||||||
|
if level < 0 {
|
||||||
|
level = s.pickMemdbLevel(t.imin.ukey(), t.imax.ukey())
|
||||||
|
}
|
||||||
|
rec.addTableFile(level, t)
|
||||||
|
|
||||||
|
s.logf("memdb@flush created L%d@%d N·%d S·%s %q:%q", level, t.file.Num(), n, shortenb(int(t.size)), t.imin, t.imax)
|
||||||
|
return level, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick a compaction based on current state; need external synchronization.
|
||||||
|
func (s *session) pickCompaction() *compaction {
|
||||||
|
v := s.version()
|
||||||
|
|
||||||
|
var level int
|
||||||
|
var t0 tFiles
|
||||||
|
if v.cScore >= 1 {
|
||||||
|
level = v.cLevel
|
||||||
|
cptr := s.stCompPtrs[level]
|
||||||
|
tables := v.tables[level]
|
||||||
|
for _, t := range tables {
|
||||||
|
if cptr == nil || s.icmp.Compare(t.imax, cptr) > 0 {
|
||||||
|
t0 = append(t0, t)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(t0) == 0 {
|
||||||
|
t0 = append(t0, tables[0])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if p := atomic.LoadPointer(&v.cSeek); p != nil {
|
||||||
|
ts := (*tSet)(p)
|
||||||
|
level = ts.level
|
||||||
|
t0 = append(t0, ts.table)
|
||||||
|
} else {
|
||||||
|
v.release()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newCompaction(s, v, level, t0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create compaction from given level and range; need external synchronization.
|
||||||
|
func (s *session) getCompactionRange(level int, umin, umax []byte) *compaction {
|
||||||
|
v := s.version()
|
||||||
|
|
||||||
|
t0 := v.tables[level].getOverlaps(nil, s.icmp, umin, umax, level == 0)
|
||||||
|
if len(t0) == 0 {
|
||||||
|
v.release()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid compacting too much in one shot in case the range is large.
|
||||||
|
// But we cannot do this for level-0 since level-0 files can overlap
|
||||||
|
// and we must not pick one file and drop another older file if the
|
||||||
|
// two files overlap.
|
||||||
|
if level > 0 {
|
||||||
|
limit := uint64(v.s.o.GetCompactionSourceLimit(level))
|
||||||
|
total := uint64(0)
|
||||||
|
for i, t := range t0 {
|
||||||
|
total += t.size
|
||||||
|
if total >= limit {
|
||||||
|
s.logf("table@compaction limiting F·%d -> F·%d", len(t0), i+1)
|
||||||
|
t0 = t0[:i+1]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newCompaction(s, v, level, t0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCompaction(s *session, v *version, level int, t0 tFiles) *compaction {
|
||||||
|
c := &compaction{
|
||||||
|
s: s,
|
||||||
|
v: v,
|
||||||
|
level: level,
|
||||||
|
tables: [2]tFiles{t0, nil},
|
||||||
|
maxGPOverlaps: uint64(s.o.GetCompactionGPOverlaps(level)),
|
||||||
|
tPtrs: make([]int, s.o.GetNumLevel()),
|
||||||
|
}
|
||||||
|
c.expand()
|
||||||
|
c.save()
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// compaction represent a compaction state.
|
||||||
|
type compaction struct {
|
||||||
|
s *session
|
||||||
|
v *version
|
||||||
|
|
||||||
|
level int
|
||||||
|
tables [2]tFiles
|
||||||
|
maxGPOverlaps uint64
|
||||||
|
|
||||||
|
gp tFiles
|
||||||
|
gpi int
|
||||||
|
seenKey bool
|
||||||
|
gpOverlappedBytes uint64
|
||||||
|
imin, imax iKey
|
||||||
|
tPtrs []int
|
||||||
|
released bool
|
||||||
|
|
||||||
|
snapGPI int
|
||||||
|
snapSeenKey bool
|
||||||
|
snapGPOverlappedBytes uint64
|
||||||
|
snapTPtrs []int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compaction) save() {
|
||||||
|
c.snapGPI = c.gpi
|
||||||
|
c.snapSeenKey = c.seenKey
|
||||||
|
c.snapGPOverlappedBytes = c.gpOverlappedBytes
|
||||||
|
c.snapTPtrs = append(c.snapTPtrs[:0], c.tPtrs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compaction) restore() {
|
||||||
|
c.gpi = c.snapGPI
|
||||||
|
c.seenKey = c.snapSeenKey
|
||||||
|
c.gpOverlappedBytes = c.snapGPOverlappedBytes
|
||||||
|
c.tPtrs = append(c.tPtrs[:0], c.snapTPtrs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compaction) release() {
|
||||||
|
if !c.released {
|
||||||
|
c.released = true
|
||||||
|
c.v.release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expand compacted tables; need external synchronization.
|
||||||
|
func (c *compaction) expand() {
|
||||||
|
limit := uint64(c.s.o.GetCompactionExpandLimit(c.level))
|
||||||
|
vt0, vt1 := c.v.tables[c.level], c.v.tables[c.level+1]
|
||||||
|
|
||||||
|
t0, t1 := c.tables[0], c.tables[1]
|
||||||
|
imin, imax := t0.getRange(c.s.icmp)
|
||||||
|
// We expand t0 here just incase ukey hop across tables.
|
||||||
|
t0 = vt0.getOverlaps(t0, c.s.icmp, imin.ukey(), imax.ukey(), c.level == 0)
|
||||||
|
if len(t0) != len(c.tables[0]) {
|
||||||
|
imin, imax = t0.getRange(c.s.icmp)
|
||||||
|
}
|
||||||
|
t1 = vt1.getOverlaps(t1, c.s.icmp, imin.ukey(), imax.ukey(), false)
|
||||||
|
// Get entire range covered by compaction.
|
||||||
|
amin, amax := append(t0, t1...).getRange(c.s.icmp)
|
||||||
|
|
||||||
|
// See if we can grow the number of inputs in "level" without
|
||||||
|
// changing the number of "level+1" files we pick up.
|
||||||
|
if len(t1) > 0 {
|
||||||
|
exp0 := vt0.getOverlaps(nil, c.s.icmp, amin.ukey(), amax.ukey(), c.level == 0)
|
||||||
|
if len(exp0) > len(t0) && t1.size()+exp0.size() < limit {
|
||||||
|
xmin, xmax := exp0.getRange(c.s.icmp)
|
||||||
|
exp1 := vt1.getOverlaps(nil, c.s.icmp, xmin.ukey(), xmax.ukey(), false)
|
||||||
|
if len(exp1) == len(t1) {
|
||||||
|
c.s.logf("table@compaction expanding L%d+L%d (F·%d S·%s)+(F·%d S·%s) -> (F·%d S·%s)+(F·%d S·%s)",
|
||||||
|
c.level, c.level+1, len(t0), shortenb(int(t0.size())), len(t1), shortenb(int(t1.size())),
|
||||||
|
len(exp0), shortenb(int(exp0.size())), len(exp1), shortenb(int(exp1.size())))
|
||||||
|
imin, imax = xmin, xmax
|
||||||
|
t0, t1 = exp0, exp1
|
||||||
|
amin, amax = append(t0, t1...).getRange(c.s.icmp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the set of grandparent files that overlap this compaction
|
||||||
|
// (parent == level+1; grandparent == level+2)
|
||||||
|
if c.level+2 < c.s.o.GetNumLevel() {
|
||||||
|
c.gp = c.v.tables[c.level+2].getOverlaps(c.gp, c.s.icmp, amin.ukey(), amax.ukey(), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.tables[0], c.tables[1] = t0, t1
|
||||||
|
c.imin, c.imax = imin, imax
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether compaction is trivial.
|
||||||
|
func (c *compaction) trivial() bool {
|
||||||
|
return len(c.tables[0]) == 1 && len(c.tables[1]) == 0 && c.gp.size() <= c.maxGPOverlaps
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compaction) baseLevelForKey(ukey []byte) bool {
|
||||||
|
for level, tables := range c.v.tables[c.level+2:] {
|
||||||
|
for c.tPtrs[level] < len(tables) {
|
||||||
|
t := tables[c.tPtrs[level]]
|
||||||
|
if c.s.icmp.uCompare(ukey, t.imax.ukey()) <= 0 {
|
||||||
|
// We've advanced far enough.
|
||||||
|
if c.s.icmp.uCompare(ukey, t.imin.ukey()) >= 0 {
|
||||||
|
// Key falls in this file's range, so definitely not base level.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
c.tPtrs[level]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compaction) shouldStopBefore(ikey iKey) bool {
|
||||||
|
for ; c.gpi < len(c.gp); c.gpi++ {
|
||||||
|
gp := c.gp[c.gpi]
|
||||||
|
if c.s.icmp.Compare(ikey, gp.imax) <= 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if c.seenKey {
|
||||||
|
c.gpOverlappedBytes += gp.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.seenKey = true
|
||||||
|
|
||||||
|
if c.gpOverlappedBytes > c.maxGPOverlaps {
|
||||||
|
// Too much overlap for current output; start new output.
|
||||||
|
c.gpOverlappedBytes = 0
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates an iterator.
|
||||||
|
func (c *compaction) newIterator() iterator.Iterator {
|
||||||
|
// Creates iterator slice.
|
||||||
|
icap := len(c.tables)
|
||||||
|
if c.level == 0 {
|
||||||
|
// Special case for level-0.
|
||||||
|
icap = len(c.tables[0]) + 1
|
||||||
|
}
|
||||||
|
its := make([]iterator.Iterator, 0, icap)
|
||||||
|
|
||||||
|
// Options.
|
||||||
|
ro := &opt.ReadOptions{
|
||||||
|
DontFillCache: true,
|
||||||
|
Strict: opt.StrictOverride,
|
||||||
|
}
|
||||||
|
strict := c.s.o.GetStrict(opt.StrictCompaction)
|
||||||
|
if strict {
|
||||||
|
ro.Strict |= opt.StrictReader
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tables := range c.tables {
|
||||||
|
if len(tables) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Level-0 is not sorted and may overlaps each other.
|
||||||
|
if c.level+i == 0 {
|
||||||
|
for _, t := range tables {
|
||||||
|
its = append(its, c.s.tops.newIterator(t, nil, ro))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
it := iterator.NewIndexedIterator(tables.newIndexIterator(c.s.tops, c.s.icmp, nil, ro), strict)
|
||||||
|
its = append(its, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return iterator.NewMergedIterator(its, c.s.icmp, strict)
|
||||||
|
}
|
||||||
+6
-8
@@ -52,8 +52,6 @@ type dtRecord struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type sessionRecord struct {
|
type sessionRecord struct {
|
||||||
numLevel int
|
|
||||||
|
|
||||||
hasRec int
|
hasRec int
|
||||||
comparer string
|
comparer string
|
||||||
journalNum uint64
|
journalNum uint64
|
||||||
@@ -230,7 +228,7 @@ func (p *sessionRecord) readBytes(field string, r byteReader) []byte {
|
|||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *sessionRecord) readLevel(field string, r io.ByteReader) int {
|
func (p *sessionRecord) readLevel(field string, r io.ByteReader, numLevel int) int {
|
||||||
if p.err != nil {
|
if p.err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -238,14 +236,14 @@ func (p *sessionRecord) readLevel(field string, r io.ByteReader) int {
|
|||||||
if p.err != nil {
|
if p.err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
if x >= uint64(p.numLevel) {
|
if x >= uint64(numLevel) {
|
||||||
p.err = errors.NewErrCorrupted(nil, &ErrManifestCorrupted{field, "invalid level number"})
|
p.err = errors.NewErrCorrupted(nil, &ErrManifestCorrupted{field, "invalid level number"})
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return int(x)
|
return int(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *sessionRecord) decode(r io.Reader) error {
|
func (p *sessionRecord) decode(r io.Reader, numLevel int) error {
|
||||||
br, ok := r.(byteReader)
|
br, ok := r.(byteReader)
|
||||||
if !ok {
|
if !ok {
|
||||||
br = bufio.NewReader(r)
|
br = bufio.NewReader(r)
|
||||||
@@ -286,13 +284,13 @@ func (p *sessionRecord) decode(r io.Reader) error {
|
|||||||
p.setSeqNum(x)
|
p.setSeqNum(x)
|
||||||
}
|
}
|
||||||
case recCompPtr:
|
case recCompPtr:
|
||||||
level := p.readLevel("comp-ptr.level", br)
|
level := p.readLevel("comp-ptr.level", br, numLevel)
|
||||||
ikey := p.readBytes("comp-ptr.ikey", br)
|
ikey := p.readBytes("comp-ptr.ikey", br)
|
||||||
if p.err == nil {
|
if p.err == nil {
|
||||||
p.addCompPtr(level, iKey(ikey))
|
p.addCompPtr(level, iKey(ikey))
|
||||||
}
|
}
|
||||||
case recAddTable:
|
case recAddTable:
|
||||||
level := p.readLevel("add-table.level", br)
|
level := p.readLevel("add-table.level", br, numLevel)
|
||||||
num := p.readUvarint("add-table.num", br)
|
num := p.readUvarint("add-table.num", br)
|
||||||
size := p.readUvarint("add-table.size", br)
|
size := p.readUvarint("add-table.size", br)
|
||||||
imin := p.readBytes("add-table.imin", br)
|
imin := p.readBytes("add-table.imin", br)
|
||||||
@@ -301,7 +299,7 @@ func (p *sessionRecord) decode(r io.Reader) error {
|
|||||||
p.addTable(level, num, size, imin, imax)
|
p.addTable(level, num, size, imin, imax)
|
||||||
}
|
}
|
||||||
case recDelTable:
|
case recDelTable:
|
||||||
level := p.readLevel("del-table.level", br)
|
level := p.readLevel("del-table.level", br, numLevel)
|
||||||
num := p.readUvarint("del-table.num", br)
|
num := p.readUvarint("del-table.num", br)
|
||||||
if p.err == nil {
|
if p.err == nil {
|
||||||
p.delTable(level, num)
|
p.delTable(level, num)
|
||||||
|
|||||||
+3
-3
@@ -19,8 +19,8 @@ func decodeEncode(v *sessionRecord) (res bool, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
v2 := &sessionRecord{numLevel: opt.DefaultNumLevel}
|
v2 := &sessionRecord{}
|
||||||
err = v.decode(b)
|
err = v.decode(b, opt.DefaultNumLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,7 @@ func decodeEncode(v *sessionRecord) (res bool, err error) {
|
|||||||
|
|
||||||
func TestSessionRecord_EncodeDecode(t *testing.T) {
|
func TestSessionRecord_EncodeDecode(t *testing.T) {
|
||||||
big := uint64(1) << 50
|
big := uint64(1) << 50
|
||||||
v := &sessionRecord{numLevel: opt.DefaultNumLevel}
|
v := &sessionRecord{}
|
||||||
i := uint64(0)
|
i := uint64(0)
|
||||||
test := func() {
|
test := func() {
|
||||||
res, err := decodeEncode(v)
|
res, err := decodeEncode(v)
|
||||||
|
|||||||
+3
-1
@@ -182,7 +182,7 @@ func (s *session) newManifest(rec *sessionRecord, v *version) (err error) {
|
|||||||
defer v.release()
|
defer v.release()
|
||||||
}
|
}
|
||||||
if rec == nil {
|
if rec == nil {
|
||||||
rec = &sessionRecord{numLevel: s.o.GetNumLevel()}
|
rec = &sessionRecord{}
|
||||||
}
|
}
|
||||||
s.fillRecord(rec, true)
|
s.fillRecord(rec, true)
|
||||||
v.fillRecord(rec)
|
v.fillRecord(rec)
|
||||||
@@ -240,10 +240,12 @@ func (s *session) flushManifest(rec *sessionRecord) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !s.o.GetNoSync() {
|
||||||
err = s.manifestWriter.Sync()
|
err = s.manifestWriter.Sync()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
s.recordCommited(rec)
|
s.recordCommited(rec)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
Vendored
+25
-16
@@ -243,7 +243,10 @@ func (fs *fileStorage) GetManifest() (f File, err error) {
|
|||||||
rem = append(rem, fn)
|
rem = append(rem, fn)
|
||||||
}
|
}
|
||||||
if !pend1 || cerr == nil {
|
if !pend1 || cerr == nil {
|
||||||
cerr = fmt.Errorf("leveldb/storage: corrupted or incomplete %s file", fn)
|
cerr = &ErrCorrupted{
|
||||||
|
File: fsParseName(filepath.Base(fn)),
|
||||||
|
Err: errors.New("leveldb/storage: corrupted or incomplete manifest file"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if f != nil && f1.Num() < f.Num() {
|
} else if f != nil && f1.Num() < f.Num() {
|
||||||
fs.log(fmt.Sprintf("skipping %s: obsolete", fn))
|
fs.log(fmt.Sprintf("skipping %s: obsolete", fn))
|
||||||
@@ -326,8 +329,7 @@ func (fs *fileStorage) Close() error {
|
|||||||
runtime.SetFinalizer(fs, nil)
|
runtime.SetFinalizer(fs, nil)
|
||||||
|
|
||||||
if fs.open > 0 {
|
if fs.open > 0 {
|
||||||
fs.log(fmt.Sprintf("refuse to close, %d files still open", fs.open))
|
fs.log(fmt.Sprintf("close: warning, %d files still open", fs.open))
|
||||||
return fmt.Errorf("leveldb/storage: cannot close, %d files still open", fs.open)
|
|
||||||
}
|
}
|
||||||
fs.open = -1
|
fs.open = -1
|
||||||
e1 := fs.logw.Close()
|
e1 := fs.logw.Close()
|
||||||
@@ -505,30 +507,37 @@ func (f *file) path() string {
|
|||||||
return filepath.Join(f.fs.path, f.name())
|
return filepath.Join(f.fs.path, f.name())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *file) parse(name string) bool {
|
func fsParseName(name string) *FileInfo {
|
||||||
var num uint64
|
fi := &FileInfo{}
|
||||||
var tail string
|
var tail string
|
||||||
_, err := fmt.Sscanf(name, "%d.%s", &num, &tail)
|
_, err := fmt.Sscanf(name, "%d.%s", &fi.Num, &tail)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
switch tail {
|
switch tail {
|
||||||
case "log":
|
case "log":
|
||||||
f.t = TypeJournal
|
fi.Type = TypeJournal
|
||||||
case "ldb", "sst":
|
case "ldb", "sst":
|
||||||
f.t = TypeTable
|
fi.Type = TypeTable
|
||||||
case "tmp":
|
case "tmp":
|
||||||
f.t = TypeTemp
|
fi.Type = TypeTemp
|
||||||
default:
|
default:
|
||||||
return false
|
return nil
|
||||||
}
|
}
|
||||||
f.num = num
|
return fi
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
n, _ := fmt.Sscanf(name, "MANIFEST-%d%s", &num, &tail)
|
n, _ := fmt.Sscanf(name, "MANIFEST-%d%s", &fi.Num, &tail)
|
||||||
if n == 1 {
|
if n == 1 {
|
||||||
f.t = TypeManifest
|
fi.Type = TypeManifest
|
||||||
f.num = num
|
return fi
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *file) parse(name string) bool {
|
||||||
|
fi := fsParseName(name)
|
||||||
|
if fi == nil {
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
|
f.t = fi.Type
|
||||||
|
f.num = fi.Num
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
Vendored
+11
-1
@@ -50,13 +50,23 @@ func rename(oldpath, newpath string) error {
|
|||||||
return os.Rename(oldpath, newpath)
|
return os.Rename(oldpath, newpath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isErrInvalid(err error) bool {
|
||||||
|
if err == os.ErrInvalid {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if syserr, ok := err.(*os.SyscallError); ok && syserr.Err == syscall.EINVAL {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func syncDir(name string) error {
|
func syncDir(name string) error {
|
||||||
f, err := os.Open(name)
|
f, err := os.Open(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
if err := f.Sync(); err != nil {
|
if err := f.Sync(); err != nil && !isErrInvalid(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
+16
@@ -46,6 +46,22 @@ var (
|
|||||||
ErrClosed = errors.New("leveldb/storage: closed")
|
ErrClosed = errors.New("leveldb/storage: closed")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrCorrupted is the type that wraps errors that indicate corruption of
|
||||||
|
// a file. Package storage has its own type instead of using
|
||||||
|
// errors.ErrCorrupted to prevent circular import.
|
||||||
|
type ErrCorrupted struct {
|
||||||
|
File *FileInfo
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrCorrupted) Error() string {
|
||||||
|
if e.File != nil {
|
||||||
|
return fmt.Sprintf("%v [file=%v]", e.Err, e.File)
|
||||||
|
} else {
|
||||||
|
return e.Err.Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Syncer is the interface that wraps basic Sync method.
|
// Syncer is the interface that wraps basic Sync method.
|
||||||
type Syncer interface {
|
type Syncer interface {
|
||||||
// Sync commits the current contents of the file to stable storage.
|
// Sync commits the current contents of the file to stable storage.
|
||||||
|
|||||||
+10
@@ -42,6 +42,8 @@ type tsOp uint
|
|||||||
const (
|
const (
|
||||||
tsOpOpen tsOp = iota
|
tsOpOpen tsOp = iota
|
||||||
tsOpCreate
|
tsOpCreate
|
||||||
|
tsOpReplace
|
||||||
|
tsOpRemove
|
||||||
tsOpRead
|
tsOpRead
|
||||||
tsOpReadAt
|
tsOpReadAt
|
||||||
tsOpWrite
|
tsOpWrite
|
||||||
@@ -241,6 +243,10 @@ func (tf tsFile) Replace(newfile storage.File) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if tf.shouldErr(tsOpReplace) {
|
||||||
|
err = errors.New("leveldb.testStorage: emulated create error")
|
||||||
|
return
|
||||||
|
}
|
||||||
err = tf.File.Replace(newfile.(tsFile).File)
|
err = tf.File.Replace(newfile.(tsFile).File)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ts.t.Errorf("E: cannot replace file, num=%d type=%v: %v", tf.Num(), tf.Type(), err)
|
ts.t.Errorf("E: cannot replace file, num=%d type=%v: %v", tf.Num(), tf.Type(), err)
|
||||||
@@ -258,6 +264,10 @@ func (tf tsFile) Remove() (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if tf.shouldErr(tsOpRemove) {
|
||||||
|
err = errors.New("leveldb.testStorage: emulated create error")
|
||||||
|
return
|
||||||
|
}
|
||||||
err = tf.File.Remove()
|
err = tf.File.Remove()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ts.t.Errorf("E: cannot remove file, num=%d type=%v: %v", tf.Num(), tf.Type(), err)
|
ts.t.Errorf("E: cannot remove file, num=%d type=%v: %v", tf.Num(), tf.Type(), err)
|
||||||
|
|||||||
+10
-2
@@ -287,6 +287,7 @@ func (x *tFilesSortByNum) Less(i, j int) bool {
|
|||||||
// Table operations.
|
// Table operations.
|
||||||
type tOps struct {
|
type tOps struct {
|
||||||
s *session
|
s *session
|
||||||
|
noSync bool
|
||||||
cache *cache.Cache
|
cache *cache.Cache
|
||||||
bcache *cache.Cache
|
bcache *cache.Cache
|
||||||
bpool *util.BufferPool
|
bpool *util.BufferPool
|
||||||
@@ -441,22 +442,27 @@ func newTableOps(s *session) *tOps {
|
|||||||
var (
|
var (
|
||||||
cacher cache.Cacher
|
cacher cache.Cacher
|
||||||
bcache *cache.Cache
|
bcache *cache.Cache
|
||||||
|
bpool *util.BufferPool
|
||||||
)
|
)
|
||||||
if s.o.GetOpenFilesCacheCapacity() > 0 {
|
if s.o.GetOpenFilesCacheCapacity() > 0 {
|
||||||
cacher = cache.NewLRU(s.o.GetOpenFilesCacheCapacity())
|
cacher = cache.NewLRU(s.o.GetOpenFilesCacheCapacity())
|
||||||
}
|
}
|
||||||
if !s.o.DisableBlockCache {
|
if !s.o.GetDisableBlockCache() {
|
||||||
var bcacher cache.Cacher
|
var bcacher cache.Cacher
|
||||||
if s.o.GetBlockCacheCapacity() > 0 {
|
if s.o.GetBlockCacheCapacity() > 0 {
|
||||||
bcacher = cache.NewLRU(s.o.GetBlockCacheCapacity())
|
bcacher = cache.NewLRU(s.o.GetBlockCacheCapacity())
|
||||||
}
|
}
|
||||||
bcache = cache.NewCache(bcacher)
|
bcache = cache.NewCache(bcacher)
|
||||||
}
|
}
|
||||||
|
if !s.o.GetDisableBufferPool() {
|
||||||
|
bpool = util.NewBufferPool(s.o.GetBlockSize() + 5)
|
||||||
|
}
|
||||||
return &tOps{
|
return &tOps{
|
||||||
s: s,
|
s: s,
|
||||||
|
noSync: s.o.GetNoSync(),
|
||||||
cache: cache.NewCache(cacher),
|
cache: cache.NewCache(cacher),
|
||||||
bcache: bcache,
|
bcache: bcache,
|
||||||
bpool: util.NewBufferPool(s.o.GetBlockSize() + 5),
|
bpool: bpool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,10 +507,12 @@ func (w *tWriter) finish() (f *tFile, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !w.t.noSync {
|
||||||
err = w.w.Sync()
|
err = w.w.Sync()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
f = newTableFile(w.file, uint64(w.tw.BytesLen()), iKey(w.first), iKey(w.last))
|
f = newTableFile(w.file, uint64(w.tw.BytesLen()), iKey(w.first), iKey(w.last))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/syndtr/gosnappy/snappy"
|
"github.com/golang/snappy"
|
||||||
|
|
||||||
"github.com/syndtr/goleveldb/leveldb/cache"
|
"github.com/syndtr/goleveldb/leveldb/cache"
|
||||||
"github.com/syndtr/goleveldb/leveldb/comparer"
|
"github.com/syndtr/goleveldb/leveldb/comparer"
|
||||||
|
|||||||
+2
-6
@@ -12,7 +12,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/syndtr/gosnappy/snappy"
|
"github.com/golang/snappy"
|
||||||
|
|
||||||
"github.com/syndtr/goleveldb/leveldb/comparer"
|
"github.com/syndtr/goleveldb/leveldb/comparer"
|
||||||
"github.com/syndtr/goleveldb/leveldb/filter"
|
"github.com/syndtr/goleveldb/leveldb/filter"
|
||||||
@@ -167,11 +167,7 @@ func (w *Writer) writeBlock(buf *util.Buffer, compression opt.Compression) (bh b
|
|||||||
if n := snappy.MaxEncodedLen(buf.Len()) + blockTrailerLen; len(w.compressionScratch) < n {
|
if n := snappy.MaxEncodedLen(buf.Len()) + blockTrailerLen; len(w.compressionScratch) < n {
|
||||||
w.compressionScratch = make([]byte, n)
|
w.compressionScratch = make([]byte, n)
|
||||||
}
|
}
|
||||||
var compressed []byte
|
compressed := snappy.Encode(w.compressionScratch, buf.Bytes())
|
||||||
compressed, err = snappy.Encode(w.compressionScratch, buf.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
n := len(compressed)
|
n := len(compressed)
|
||||||
b = compressed[:n+blockTrailerLen]
|
b = compressed[:n+blockTrailerLen]
|
||||||
b[n] = blockTypeSnappyCompression
|
b[n] = blockTypeSnappyCompression
|
||||||
|
|||||||
+1
@@ -201,6 +201,7 @@ func (p *BufferPool) String() string {
|
|||||||
|
|
||||||
func (p *BufferPool) drain() {
|
func (p *BufferPool) drain() {
|
||||||
ticker := time.NewTicker(2 * time.Second)
|
ticker := time.NewTicker(2 * time.Second)
|
||||||
|
defer ticker.Stop()
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
|
|||||||
+24
-3
@@ -136,9 +136,8 @@ func (v *version) get(ikey iKey, ro *opt.ReadOptions, noValue bool) (value []byt
|
|||||||
if !tseek {
|
if !tseek {
|
||||||
if tset == nil {
|
if tset == nil {
|
||||||
tset = &tSet{level, t}
|
tset = &tSet{level, t}
|
||||||
} else if tset.table.consumeSeek() <= 0 {
|
} else {
|
||||||
tseek = true
|
tseek = true
|
||||||
tcomp = atomic.CompareAndSwapPointer(&v.cSeek, nil, unsafe.Pointer(tset))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,6 +202,28 @@ func (v *version) get(ikey iKey, ro *opt.ReadOptions, noValue bool) (value []byt
|
|||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if tseek && tset.table.consumeSeek() <= 0 {
|
||||||
|
tcomp = atomic.CompareAndSwapPointer(&v.cSeek, nil, unsafe.Pointer(tset))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *version) sampleSeek(ikey iKey) (tcomp bool) {
|
||||||
|
var tset *tSet
|
||||||
|
|
||||||
|
v.walkOverlapping(ikey, func(level int, t *tFile) bool {
|
||||||
|
if tset == nil {
|
||||||
|
tset = &tSet{level, t}
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
if tset.table.consumeSeek() <= 0 {
|
||||||
|
tcomp = atomic.CompareAndSwapPointer(&v.cSeek, nil, unsafe.Pointer(tset))
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}, nil)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,7 +300,7 @@ func (v *version) offsetOf(ikey iKey) (n uint64, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *version) pickLevel(umin, umax []byte) (level int) {
|
func (v *version) pickMemdbLevel(umin, umax []byte) (level int) {
|
||||||
if !v.tables[0].overlaps(v.s.icmp, umin, umax, true) {
|
if !v.tables[0].overlaps(v.s.icmp, umin, umax, true) {
|
||||||
var overlaps tFiles
|
var overlaps tFiles
|
||||||
maxLevel := v.s.o.GetMaxMemCompationLevel()
|
maxLevel := v.s.o.GetMaxMemCompationLevel()
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2014 Barracuda Networks, Inc.
|
Copyright (c) 2014-2015 Barracuda Networks, Inc.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
+13
-8
@@ -6,10 +6,8 @@ Suture
|
|||||||
Suture provides Erlang-ish supervisor trees for Go. "Supervisor trees" ->
|
Suture provides Erlang-ish supervisor trees for Go. "Supervisor trees" ->
|
||||||
"sutree" -> "suture" -> holds your code together when it's trying to die.
|
"sutree" -> "suture" -> holds your code together when it's trying to die.
|
||||||
|
|
||||||
This is intended to be a production-quality library going into code that I
|
This library has hit maturity, and isn't expected to be changed
|
||||||
will be very early on the phone tree to support when it goes down. However,
|
radically. This can also be imported via gopkg.in/thejerf/suture.v1 .
|
||||||
it has not been deployed into something quite that serious yet. (I will
|
|
||||||
update this statement when that changes.)
|
|
||||||
|
|
||||||
It is intended to deal gracefully with the real failure cases that can
|
It is intended to deal gracefully with the real failure cases that can
|
||||||
occur with supervision trees (such as burning all your CPU time endlessly
|
occur with supervision trees (such as burning all your CPU time endlessly
|
||||||
@@ -24,10 +22,6 @@ This module is fully covered with [godoc](http://godoc.org/github.com/thejerf/su
|
|||||||
including an example, usage, and everything else you might expect from a
|
including an example, usage, and everything else you might expect from a
|
||||||
README.md on GitHub. (DRY.)
|
README.md on GitHub. (DRY.)
|
||||||
|
|
||||||
This is not currently tagged with particular git tags for Go as this is
|
|
||||||
currently considered to be alpha code. As I move this into production and
|
|
||||||
feel more confident about it, I'll give it relevant tags.
|
|
||||||
|
|
||||||
Code Signing
|
Code Signing
|
||||||
------------
|
------------
|
||||||
|
|
||||||
@@ -43,3 +37,14 @@ easily fit into the OTP paradigm. It ought to someday be considered a good
|
|||||||
idea to distribute libraries that provide some sort of supervisor tree
|
idea to distribute libraries that provide some sort of supervisor tree
|
||||||
functionality out of the box. It is possible to provide this functionality
|
functionality out of the box. It is possible to provide this functionality
|
||||||
without explicitly depending on the Suture library.
|
without explicitly depending on the Suture library.
|
||||||
|
|
||||||
|
Changelog
|
||||||
|
---------
|
||||||
|
|
||||||
|
suture uses semantic versioning.
|
||||||
|
|
||||||
|
1. 1.0.0
|
||||||
|
* Initial release.
|
||||||
|
2. 1.0.1
|
||||||
|
* Fixed data race on the .state variable.
|
||||||
|
|
||||||
|
|||||||
+53
-13
@@ -36,6 +36,13 @@ to your Supervisor. Supervisors are also services, so you can create a
|
|||||||
tree structure here, depending on the exact combination of restarts
|
tree structure here, depending on the exact combination of restarts
|
||||||
you want to create.
|
you want to create.
|
||||||
|
|
||||||
|
As a special case, when adding Supervisors to Supervisors, the "sub"
|
||||||
|
supervisor will have the "super" supervisor's Log function copied.
|
||||||
|
This allows you to set one log function on the "top" supervisor, and
|
||||||
|
have it propagate down to all the sub-supervisors. This also allows
|
||||||
|
libraries or modules to provide Supervisors without having to commit
|
||||||
|
their users to a particular logging method.
|
||||||
|
|
||||||
Finally, as what is probably the last line of your main() function, call
|
Finally, as what is probably the last line of your main() function, call
|
||||||
.Serve() on your top level supervisor. This will start all the services
|
.Serve() on your top level supervisor. This will start all the services
|
||||||
you've defined.
|
you've defined.
|
||||||
@@ -52,6 +59,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -116,7 +124,6 @@ type Supervisor struct {
|
|||||||
lastFail time.Time
|
lastFail time.Time
|
||||||
failures float64
|
failures float64
|
||||||
restartQueue []serviceID
|
restartQueue []serviceID
|
||||||
state uint8
|
|
||||||
serviceCounter serviceID
|
serviceCounter serviceID
|
||||||
control chan supervisorMessage
|
control chan supervisorMessage
|
||||||
resumeTimer <-chan time.Time
|
resumeTimer <-chan time.Time
|
||||||
@@ -126,14 +133,19 @@ type Supervisor struct {
|
|||||||
// If you ever come up with some need to get into these, submit a pull
|
// If you ever come up with some need to get into these, submit a pull
|
||||||
// request to make them public and some smidge of justification, and
|
// request to make them public and some smidge of justification, and
|
||||||
// I'll happily do it.
|
// I'll happily do it.
|
||||||
logBadStop func(Service)
|
// But since I've now changed the signature on these once, I'm glad I
|
||||||
logFailure func(service Service, currentFailures float64, failureThreshold float64, restarting bool, error interface{}, stacktrace []byte)
|
// didn't start with them public... :)
|
||||||
|
logBadStop func(*Supervisor, Service)
|
||||||
|
logFailure func(supervisor *Supervisor, service Service, currentFailures float64, failureThreshold float64, restarting bool, error interface{}, stacktrace []byte)
|
||||||
logBackoff func(*Supervisor, bool)
|
logBackoff func(*Supervisor, bool)
|
||||||
|
|
||||||
// avoid a dependency on github.com/thejerf/abtime by just implementing
|
// avoid a dependency on github.com/thejerf/abtime by just implementing
|
||||||
// a minimal chunk.
|
// a minimal chunk.
|
||||||
getNow func() time.Time
|
getNow func() time.Time
|
||||||
getResume func(time.Duration) <-chan time.Time
|
getResume func(time.Duration) <-chan time.Time
|
||||||
|
|
||||||
|
sync.Mutex
|
||||||
|
state uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spec is used to pass arguments to the New function to create a
|
// Spec is used to pass arguments to the New function to create a
|
||||||
@@ -233,10 +245,10 @@ func New(name string, spec Spec) (s *Supervisor) {
|
|||||||
s.resumeTimer = make(chan time.Time)
|
s.resumeTimer = make(chan time.Time)
|
||||||
|
|
||||||
// set up the default logging handlers
|
// set up the default logging handlers
|
||||||
s.logBadStop = func(service Service) {
|
s.logBadStop = func(supervisor *Supervisor, service Service) {
|
||||||
s.log(fmt.Sprintf("Service %s failed to terminate in a timely manner", serviceName(service)))
|
s.log(fmt.Sprintf("%s: Service %s failed to terminate in a timely manner", serviceName(supervisor), serviceName(service)))
|
||||||
}
|
}
|
||||||
s.logFailure = func(service Service, failures float64, threshold float64, restarting bool, err interface{}, st []byte) {
|
s.logFailure = func(supervisor *Supervisor, service Service, failures float64, threshold float64, restarting bool, err interface{}, st []byte) {
|
||||||
var errString string
|
var errString string
|
||||||
|
|
||||||
e, canError := err.(error)
|
e, canError := err.(error)
|
||||||
@@ -246,7 +258,7 @@ func New(name string, spec Spec) (s *Supervisor) {
|
|||||||
errString = fmt.Sprintf("%#v", err)
|
errString = fmt.Sprintf("%#v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.log(fmt.Sprintf("Failed service '%s' (%f failures of %f), restarting: %#v, error: %s, stacktrace: %s", serviceName(service), failures, threshold, restarting, errString, string(st)))
|
s.log(fmt.Sprintf("%s: Failed service '%s' (%f failures of %f), restarting: %#v, error: %s, stacktrace: %s", serviceName(supervisor), serviceName(service), failures, threshold, restarting, errString, string(st)))
|
||||||
}
|
}
|
||||||
s.logBackoff = func(s *Supervisor, entering bool) {
|
s.logBackoff = func(s *Supervisor, entering bool) {
|
||||||
if entering {
|
if entering {
|
||||||
@@ -346,12 +358,25 @@ will be started when the supervisor is.
|
|||||||
|
|
||||||
The returned ServiceID may be passed to the Remove method of the Supervisor
|
The returned ServiceID may be passed to the Remove method of the Supervisor
|
||||||
to terminate the service.
|
to terminate the service.
|
||||||
|
|
||||||
|
As a special behavior, if the service added is itself a supervisor, the
|
||||||
|
supervisor being added will copy the Log function from the Supervisor it
|
||||||
|
is being added to. This allows factoring out providing a Supervisor
|
||||||
|
from its logging.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
func (s *Supervisor) Add(service Service) ServiceToken {
|
func (s *Supervisor) Add(service Service) ServiceToken {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
panic("can't add service to nil *suture.Supervisor")
|
panic("can't add service to nil *suture.Supervisor")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if supervisor, isSupervisor := service.(*Supervisor); isSupervisor {
|
||||||
|
supervisor.logBadStop = s.logBadStop
|
||||||
|
supervisor.logFailure = s.logFailure
|
||||||
|
supervisor.logBackoff = s.logBackoff
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
if s.state == notRunning {
|
if s.state == notRunning {
|
||||||
id := s.serviceCounter
|
id := s.serviceCounter
|
||||||
s.serviceCounter++
|
s.serviceCounter++
|
||||||
@@ -359,8 +384,10 @@ func (s *Supervisor) Add(service Service) ServiceToken {
|
|||||||
s.services[id] = service
|
s.services[id] = service
|
||||||
s.restartQueue = append(s.restartQueue, id)
|
s.restartQueue = append(s.restartQueue, id)
|
||||||
|
|
||||||
|
s.Unlock()
|
||||||
return ServiceToken{uint64(s.id)<<32 | uint64(id)}
|
return ServiceToken{uint64(s.id)<<32 | uint64(id)}
|
||||||
}
|
}
|
||||||
|
s.Unlock()
|
||||||
|
|
||||||
response := make(chan serviceID)
|
response := make(chan serviceID)
|
||||||
s.control <- addService{service, response}
|
s.control <- addService{service, response}
|
||||||
@@ -387,16 +414,19 @@ func (s *Supervisor) Serve() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
s.Lock()
|
||||||
s.state = notRunning
|
s.state = notRunning
|
||||||
|
s.Unlock()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
if s.state != notRunning {
|
if s.state != notRunning {
|
||||||
// FIXME: Don't explain why I don't need a semaphore, just use one
|
s.Unlock()
|
||||||
// This doesn't use a semaphore because it's just a sanity check.
|
|
||||||
panic("Running a supervisor while it is already running?")
|
panic("Running a supervisor while it is already running?")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.state = normal
|
s.state = normal
|
||||||
|
s.Unlock()
|
||||||
|
|
||||||
// for all the services I currently know about, start them
|
// for all the services I currently know about, start them
|
||||||
for _, id := range s.restartQueue {
|
for _, id := range s.restartQueue {
|
||||||
@@ -451,7 +481,9 @@ func (s *Supervisor) Serve() {
|
|||||||
// excessive thrashing
|
// excessive thrashing
|
||||||
// FIXME: Ought to permit some spacing of these functions, rather
|
// FIXME: Ought to permit some spacing of these functions, rather
|
||||||
// than simply hammering through them
|
// than simply hammering through them
|
||||||
|
s.Lock()
|
||||||
s.state = normal
|
s.state = normal
|
||||||
|
s.Unlock()
|
||||||
s.failures = 0
|
s.failures = 0
|
||||||
s.logBackoff(s, false)
|
s.logBackoff(s, false)
|
||||||
for _, id := range s.restartQueue {
|
for _, id := range s.restartQueue {
|
||||||
@@ -478,7 +510,9 @@ func (s *Supervisor) handleFailedService(id serviceID, err interface{}, stacktra
|
|||||||
}
|
}
|
||||||
|
|
||||||
if s.failures > s.failureThreshold {
|
if s.failures > s.failureThreshold {
|
||||||
|
s.Lock()
|
||||||
s.state = paused
|
s.state = paused
|
||||||
|
s.Unlock()
|
||||||
s.logBackoff(s, true)
|
s.logBackoff(s, true)
|
||||||
s.resumeTimer = s.getResume(s.failureBackoff)
|
s.resumeTimer = s.getResume(s.failureBackoff)
|
||||||
}
|
}
|
||||||
@@ -490,14 +524,20 @@ func (s *Supervisor) handleFailedService(id serviceID, err interface{}, stacktra
|
|||||||
// It is possible for a service to be no longer monitored
|
// It is possible for a service to be no longer monitored
|
||||||
// by the time we get here. In that case, just ignore it.
|
// by the time we get here. In that case, just ignore it.
|
||||||
if monitored {
|
if monitored {
|
||||||
if s.state == normal {
|
// this may look dangerous because the state could change, but this
|
||||||
|
// code is only ever run in the one goroutine that is permitted to
|
||||||
|
// change the state, so nothing else will.
|
||||||
|
s.Lock()
|
||||||
|
curState := s.state
|
||||||
|
s.Unlock()
|
||||||
|
if curState == normal {
|
||||||
s.runService(failedService, id)
|
s.runService(failedService, id)
|
||||||
s.logFailure(failedService, s.failures, s.failureThreshold, true, err, stacktrace)
|
s.logFailure(s, failedService, s.failures, s.failureThreshold, true, err, stacktrace)
|
||||||
} else {
|
} else {
|
||||||
// FIXME: When restarting, check that the service still
|
// FIXME: When restarting, check that the service still
|
||||||
// exists (it may have been stopped in the meantime)
|
// exists (it may have been stopped in the meantime)
|
||||||
s.restartQueue = append(s.restartQueue, id)
|
s.restartQueue = append(s.restartQueue, id)
|
||||||
s.logFailure(failedService, s.failures, s.failureThreshold, false, err, stacktrace)
|
s.logFailure(s, failedService, s.failures, s.failureThreshold, false, err, stacktrace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -536,7 +576,7 @@ func (s *Supervisor) removeService(id serviceID) {
|
|||||||
case <-successChan:
|
case <-successChan:
|
||||||
// Life is good!
|
// Life is good!
|
||||||
case <-failChan:
|
case <-failChan:
|
||||||
s.logBadStop(service)
|
s.logBadStop(s, service)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@ func (i *Incrementor) Serve() {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case i.next <- i.current:
|
case i.next <- i.current:
|
||||||
i.current += 1
|
i.current++
|
||||||
case <-i.stop:
|
case <-i.stop:
|
||||||
// We sync here just to guarantee the output of "Stopping the service",
|
// We sync here just to guarantee the output of "Stopping the service",
|
||||||
// so this passes the test reliably.
|
// so this passes the test reliably.
|
||||||
|
|||||||
+34
-10
@@ -77,7 +77,7 @@ func TestFailures(t *testing.T) {
|
|||||||
// to avoid deadlocks during shutdown, we have to not try to send
|
// to avoid deadlocks during shutdown, we have to not try to send
|
||||||
// things out on channels while we're shutting down (this undoes the
|
// things out on channels while we're shutting down (this undoes the
|
||||||
// logFailure overide about 25 lines down)
|
// logFailure overide about 25 lines down)
|
||||||
s.logFailure = func(Service, float64, float64, bool, interface{}, []byte) {}
|
s.logFailure = func(*Supervisor, Service, float64, float64, bool, interface{}, []byte) {}
|
||||||
s.Stop()
|
s.Stop()
|
||||||
}()
|
}()
|
||||||
s.sync()
|
s.sync()
|
||||||
@@ -102,7 +102,7 @@ func TestFailures(t *testing.T) {
|
|||||||
|
|
||||||
failNotify := make(chan bool)
|
failNotify := make(chan bool)
|
||||||
// use this to synchronize on here
|
// use this to synchronize on here
|
||||||
s.logFailure = func(s Service, cf float64, ft float64, r bool, error interface{}, stacktrace []byte) {
|
s.logFailure = func(supervisor *Supervisor, s Service, cf float64, ft float64, r bool, error interface{}, stacktrace []byte) {
|
||||||
failNotify <- r
|
failNotify <- r
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,8 +276,8 @@ func TestDefaultLogging(t *testing.T) {
|
|||||||
|
|
||||||
serviceName(&BarelyService{})
|
serviceName(&BarelyService{})
|
||||||
|
|
||||||
s.logBadStop(service)
|
s.logBadStop(s, service)
|
||||||
s.logFailure(service, 1, 1, true, errors.New("test error"), []byte{})
|
s.logFailure(s, service, 1, 1, true, errors.New("test error"), []byte{})
|
||||||
|
|
||||||
s.Stop()
|
s.Stop()
|
||||||
}
|
}
|
||||||
@@ -289,9 +289,17 @@ func TestNestedSupervisors(t *testing.T) {
|
|||||||
super2 := NewSimple("Nested5")
|
super2 := NewSimple("Nested5")
|
||||||
service := NewService("Service5")
|
service := NewService("Service5")
|
||||||
|
|
||||||
|
super2.logBadStop = func(*Supervisor, Service) {
|
||||||
|
panic("Failed to copy logBadStop")
|
||||||
|
}
|
||||||
|
|
||||||
super1.Add(super2)
|
super1.Add(super2)
|
||||||
super2.Add(service)
|
super2.Add(service)
|
||||||
|
|
||||||
|
// test the functions got copied from super1; if this panics, it didn't
|
||||||
|
// get copied
|
||||||
|
super2.logBadStop(super2, service)
|
||||||
|
|
||||||
go super1.Serve()
|
go super1.Serve()
|
||||||
super1.sync()
|
super1.sync()
|
||||||
|
|
||||||
@@ -340,7 +348,7 @@ func TestStoppingStillWorksWithHungServices(t *testing.T) {
|
|||||||
return resumeChan
|
return resumeChan
|
||||||
}
|
}
|
||||||
failNotify := make(chan struct{})
|
failNotify := make(chan struct{})
|
||||||
s.logBadStop = func(s Service) {
|
s.logBadStop = func(supervisor *Supervisor, s Service) {
|
||||||
failNotify <- struct{}{}
|
failNotify <- struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,7 +446,7 @@ func TestFailingSupervisors(t *testing.T) {
|
|||||||
}
|
}
|
||||||
failNotify := make(chan string)
|
failNotify := make(chan string)
|
||||||
// use this to synchronize on here
|
// use this to synchronize on here
|
||||||
s1.logFailure = func(s Service, cf float64, ft float64, r bool, error interface{}, stacktrace []byte) {
|
s1.logFailure = func(supervisor *Supervisor, s Service, cf float64, ft float64, r bool, error interface{}, stacktrace []byte) {
|
||||||
failNotify <- fmt.Sprintf("%s", s)
|
failNotify <- fmt.Sprintf("%s", s)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -470,6 +478,22 @@ func TestNilSupervisorAdd(t *testing.T) {
|
|||||||
s.Add(s)
|
s.Add(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://github.com/thejerf/suture/issues/11
|
||||||
|
//
|
||||||
|
// The purpose of this test is to verify that it does not cause data races,
|
||||||
|
// so there are no obvious assertions.
|
||||||
|
func TestIssue11(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
s := NewSimple("main")
|
||||||
|
s.ServeBackground()
|
||||||
|
|
||||||
|
subsuper := NewSimple("sub")
|
||||||
|
s.Add(subsuper)
|
||||||
|
|
||||||
|
subsuper.Add(NewService("may cause data race"))
|
||||||
|
}
|
||||||
|
|
||||||
// http://golangtutorials.blogspot.com/2011/10/gotest-unit-testing-and-benchmarking-go.html
|
// http://golangtutorials.blogspot.com/2011/10/gotest-unit-testing-and-benchmarking-go.html
|
||||||
// claims test function are run in the same order as the source file...
|
// claims test function are run in the same order as the source file...
|
||||||
// I'm not sure if this is part of the contract, though. Especially in the
|
// I'm not sure if this is part of the contract, though. Especially in the
|
||||||
@@ -501,7 +525,7 @@ func (s *FailableService) Serve() {
|
|||||||
everMultistarted = true
|
everMultistarted = true
|
||||||
panic("Multi-started the same service! " + s.name)
|
panic("Multi-started the same service! " + s.name)
|
||||||
}
|
}
|
||||||
s.existing += 1
|
s.existing++
|
||||||
|
|
||||||
s.started <- true
|
s.started <- true
|
||||||
|
|
||||||
@@ -514,13 +538,13 @@ func (s *FailableService) Serve() {
|
|||||||
case Happy:
|
case Happy:
|
||||||
// Do nothing on purpose. Life is good!
|
// Do nothing on purpose. Life is good!
|
||||||
case Fail:
|
case Fail:
|
||||||
s.existing -= 1
|
s.existing--
|
||||||
if useStopChan {
|
if useStopChan {
|
||||||
s.stop <- true
|
s.stop <- true
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
case Panic:
|
case Panic:
|
||||||
s.existing -= 1
|
s.existing--
|
||||||
panic("Panic!")
|
panic("Panic!")
|
||||||
case Hang:
|
case Hang:
|
||||||
// or more specifically, "hang until I release you"
|
// or more specifically, "hang until I release you"
|
||||||
@@ -529,7 +553,7 @@ func (s *FailableService) Serve() {
|
|||||||
useStopChan = true
|
useStopChan = true
|
||||||
}
|
}
|
||||||
case <-s.shutdown:
|
case <-s.shutdown:
|
||||||
s.existing -= 1
|
s.existing--
|
||||||
if useStopChan {
|
if useStopChan {
|
||||||
s.stop <- true
|
s.stop <- true
|
||||||
}
|
}
|
||||||
|
|||||||
+181
@@ -0,0 +1,181 @@
|
|||||||
|
// go generate gen.go
|
||||||
|
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||||
|
|
||||||
|
// Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA).
|
||||||
|
package iana
|
||||||
|
|
||||||
|
// Differentiated Services Field Codepoints (DSCP), Updated: 2013-06-25
|
||||||
|
const (
|
||||||
|
DiffServCS0 = 0x0 // CS0
|
||||||
|
DiffServCS1 = 0x20 // CS1
|
||||||
|
DiffServCS2 = 0x40 // CS2
|
||||||
|
DiffServCS3 = 0x60 // CS3
|
||||||
|
DiffServCS4 = 0x80 // CS4
|
||||||
|
DiffServCS5 = 0xa0 // CS5
|
||||||
|
DiffServCS6 = 0xc0 // CS6
|
||||||
|
DiffServCS7 = 0xe0 // CS7
|
||||||
|
DiffServAF11 = 0x28 // AF11
|
||||||
|
DiffServAF12 = 0x30 // AF12
|
||||||
|
DiffServAF13 = 0x38 // AF13
|
||||||
|
DiffServAF21 = 0x48 // AF21
|
||||||
|
DiffServAF22 = 0x50 // AF22
|
||||||
|
DiffServAF23 = 0x58 // AF23
|
||||||
|
DiffServAF31 = 0x68 // AF31
|
||||||
|
DiffServAF32 = 0x70 // AF32
|
||||||
|
DiffServAF33 = 0x78 // AF33
|
||||||
|
DiffServAF41 = 0x88 // AF41
|
||||||
|
DiffServAF42 = 0x90 // AF42
|
||||||
|
DiffServAF43 = 0x98 // AF43
|
||||||
|
DiffServEFPHB = 0xb8 // EF PHB
|
||||||
|
DiffServVOICEADMIT = 0xb0 // VOICE-ADMIT
|
||||||
|
)
|
||||||
|
|
||||||
|
// IPv4 TOS Byte and IPv6 Traffic Class Octet, Updated: 2001-09-06
|
||||||
|
const (
|
||||||
|
NotECNTransport = 0x0 // Not-ECT (Not ECN-Capable Transport)
|
||||||
|
ECNTransport1 = 0x1 // ECT(1) (ECN-Capable Transport(1))
|
||||||
|
ECNTransport0 = 0x2 // ECT(0) (ECN-Capable Transport(0))
|
||||||
|
CongestionExperienced = 0x3 // CE (Congestion Experienced)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Protocol Numbers, Updated: 2015-06-23
|
||||||
|
const (
|
||||||
|
ProtocolIP = 0 // IPv4 encapsulation, pseudo protocol number
|
||||||
|
ProtocolHOPOPT = 0 // IPv6 Hop-by-Hop Option
|
||||||
|
ProtocolICMP = 1 // Internet Control Message
|
||||||
|
ProtocolIGMP = 2 // Internet Group Management
|
||||||
|
ProtocolGGP = 3 // Gateway-to-Gateway
|
||||||
|
ProtocolIPv4 = 4 // IPv4 encapsulation
|
||||||
|
ProtocolST = 5 // Stream
|
||||||
|
ProtocolTCP = 6 // Transmission Control
|
||||||
|
ProtocolCBT = 7 // CBT
|
||||||
|
ProtocolEGP = 8 // Exterior Gateway Protocol
|
||||||
|
ProtocolIGP = 9 // any private interior gateway (used by Cisco for their IGRP)
|
||||||
|
ProtocolBBNRCCMON = 10 // BBN RCC Monitoring
|
||||||
|
ProtocolNVPII = 11 // Network Voice Protocol
|
||||||
|
ProtocolPUP = 12 // PUP
|
||||||
|
ProtocolARGUS = 13 // ARGUS
|
||||||
|
ProtocolEMCON = 14 // EMCON
|
||||||
|
ProtocolXNET = 15 // Cross Net Debugger
|
||||||
|
ProtocolCHAOS = 16 // Chaos
|
||||||
|
ProtocolUDP = 17 // User Datagram
|
||||||
|
ProtocolMUX = 18 // Multiplexing
|
||||||
|
ProtocolDCNMEAS = 19 // DCN Measurement Subsystems
|
||||||
|
ProtocolHMP = 20 // Host Monitoring
|
||||||
|
ProtocolPRM = 21 // Packet Radio Measurement
|
||||||
|
ProtocolXNSIDP = 22 // XEROX NS IDP
|
||||||
|
ProtocolTRUNK1 = 23 // Trunk-1
|
||||||
|
ProtocolTRUNK2 = 24 // Trunk-2
|
||||||
|
ProtocolLEAF1 = 25 // Leaf-1
|
||||||
|
ProtocolLEAF2 = 26 // Leaf-2
|
||||||
|
ProtocolRDP = 27 // Reliable Data Protocol
|
||||||
|
ProtocolIRTP = 28 // Internet Reliable Transaction
|
||||||
|
ProtocolISOTP4 = 29 // ISO Transport Protocol Class 4
|
||||||
|
ProtocolNETBLT = 30 // Bulk Data Transfer Protocol
|
||||||
|
ProtocolMFENSP = 31 // MFE Network Services Protocol
|
||||||
|
ProtocolMERITINP = 32 // MERIT Internodal Protocol
|
||||||
|
ProtocolDCCP = 33 // Datagram Congestion Control Protocol
|
||||||
|
Protocol3PC = 34 // Third Party Connect Protocol
|
||||||
|
ProtocolIDPR = 35 // Inter-Domain Policy Routing Protocol
|
||||||
|
ProtocolXTP = 36 // XTP
|
||||||
|
ProtocolDDP = 37 // Datagram Delivery Protocol
|
||||||
|
ProtocolIDPRCMTP = 38 // IDPR Control Message Transport Proto
|
||||||
|
ProtocolTPPP = 39 // TP++ Transport Protocol
|
||||||
|
ProtocolIL = 40 // IL Transport Protocol
|
||||||
|
ProtocolIPv6 = 41 // IPv6 encapsulation
|
||||||
|
ProtocolSDRP = 42 // Source Demand Routing Protocol
|
||||||
|
ProtocolIPv6Route = 43 // Routing Header for IPv6
|
||||||
|
ProtocolIPv6Frag = 44 // Fragment Header for IPv6
|
||||||
|
ProtocolIDRP = 45 // Inter-Domain Routing Protocol
|
||||||
|
ProtocolRSVP = 46 // Reservation Protocol
|
||||||
|
ProtocolGRE = 47 // Generic Routing Encapsulation
|
||||||
|
ProtocolDSR = 48 // Dynamic Source Routing Protocol
|
||||||
|
ProtocolBNA = 49 // BNA
|
||||||
|
ProtocolESP = 50 // Encap Security Payload
|
||||||
|
ProtocolAH = 51 // Authentication Header
|
||||||
|
ProtocolINLSP = 52 // Integrated Net Layer Security TUBA
|
||||||
|
ProtocolNARP = 54 // NBMA Address Resolution Protocol
|
||||||
|
ProtocolMOBILE = 55 // IP Mobility
|
||||||
|
ProtocolTLSP = 56 // Transport Layer Security Protocol using Kryptonet key management
|
||||||
|
ProtocolSKIP = 57 // SKIP
|
||||||
|
ProtocolIPv6ICMP = 58 // ICMP for IPv6
|
||||||
|
ProtocolIPv6NoNxt = 59 // No Next Header for IPv6
|
||||||
|
ProtocolIPv6Opts = 60 // Destination Options for IPv6
|
||||||
|
ProtocolCFTP = 62 // CFTP
|
||||||
|
ProtocolSATEXPAK = 64 // SATNET and Backroom EXPAK
|
||||||
|
ProtocolKRYPTOLAN = 65 // Kryptolan
|
||||||
|
ProtocolRVD = 66 // MIT Remote Virtual Disk Protocol
|
||||||
|
ProtocolIPPC = 67 // Internet Pluribus Packet Core
|
||||||
|
ProtocolSATMON = 69 // SATNET Monitoring
|
||||||
|
ProtocolVISA = 70 // VISA Protocol
|
||||||
|
ProtocolIPCV = 71 // Internet Packet Core Utility
|
||||||
|
ProtocolCPNX = 72 // Computer Protocol Network Executive
|
||||||
|
ProtocolCPHB = 73 // Computer Protocol Heart Beat
|
||||||
|
ProtocolWSN = 74 // Wang Span Network
|
||||||
|
ProtocolPVP = 75 // Packet Video Protocol
|
||||||
|
ProtocolBRSATMON = 76 // Backroom SATNET Monitoring
|
||||||
|
ProtocolSUNND = 77 // SUN ND PROTOCOL-Temporary
|
||||||
|
ProtocolWBMON = 78 // WIDEBAND Monitoring
|
||||||
|
ProtocolWBEXPAK = 79 // WIDEBAND EXPAK
|
||||||
|
ProtocolISOIP = 80 // ISO Internet Protocol
|
||||||
|
ProtocolVMTP = 81 // VMTP
|
||||||
|
ProtocolSECUREVMTP = 82 // SECURE-VMTP
|
||||||
|
ProtocolVINES = 83 // VINES
|
||||||
|
ProtocolTTP = 84 // Transaction Transport Protocol
|
||||||
|
ProtocolIPTM = 84 // Internet Protocol Traffic Manager
|
||||||
|
ProtocolNSFNETIGP = 85 // NSFNET-IGP
|
||||||
|
ProtocolDGP = 86 // Dissimilar Gateway Protocol
|
||||||
|
ProtocolTCF = 87 // TCF
|
||||||
|
ProtocolEIGRP = 88 // EIGRP
|
||||||
|
ProtocolOSPFIGP = 89 // OSPFIGP
|
||||||
|
ProtocolSpriteRPC = 90 // Sprite RPC Protocol
|
||||||
|
ProtocolLARP = 91 // Locus Address Resolution Protocol
|
||||||
|
ProtocolMTP = 92 // Multicast Transport Protocol
|
||||||
|
ProtocolAX25 = 93 // AX.25 Frames
|
||||||
|
ProtocolIPIP = 94 // IP-within-IP Encapsulation Protocol
|
||||||
|
ProtocolSCCSP = 96 // Semaphore Communications Sec. Pro.
|
||||||
|
ProtocolETHERIP = 97 // Ethernet-within-IP Encapsulation
|
||||||
|
ProtocolENCAP = 98 // Encapsulation Header
|
||||||
|
ProtocolGMTP = 100 // GMTP
|
||||||
|
ProtocolIFMP = 101 // Ipsilon Flow Management Protocol
|
||||||
|
ProtocolPNNI = 102 // PNNI over IP
|
||||||
|
ProtocolPIM = 103 // Protocol Independent Multicast
|
||||||
|
ProtocolARIS = 104 // ARIS
|
||||||
|
ProtocolSCPS = 105 // SCPS
|
||||||
|
ProtocolQNX = 106 // QNX
|
||||||
|
ProtocolAN = 107 // Active Networks
|
||||||
|
ProtocolIPComp = 108 // IP Payload Compression Protocol
|
||||||
|
ProtocolSNP = 109 // Sitara Networks Protocol
|
||||||
|
ProtocolCompaqPeer = 110 // Compaq Peer Protocol
|
||||||
|
ProtocolIPXinIP = 111 // IPX in IP
|
||||||
|
ProtocolVRRP = 112 // Virtual Router Redundancy Protocol
|
||||||
|
ProtocolPGM = 113 // PGM Reliable Transport Protocol
|
||||||
|
ProtocolL2TP = 115 // Layer Two Tunneling Protocol
|
||||||
|
ProtocolDDX = 116 // D-II Data Exchange (DDX)
|
||||||
|
ProtocolIATP = 117 // Interactive Agent Transfer Protocol
|
||||||
|
ProtocolSTP = 118 // Schedule Transfer Protocol
|
||||||
|
ProtocolSRP = 119 // SpectraLink Radio Protocol
|
||||||
|
ProtocolUTI = 120 // UTI
|
||||||
|
ProtocolSMP = 121 // Simple Message Protocol
|
||||||
|
ProtocolPTP = 123 // Performance Transparency Protocol
|
||||||
|
ProtocolISIS = 124 // ISIS over IPv4
|
||||||
|
ProtocolFIRE = 125 // FIRE
|
||||||
|
ProtocolCRTP = 126 // Combat Radio Transport Protocol
|
||||||
|
ProtocolCRUDP = 127 // Combat Radio User Datagram
|
||||||
|
ProtocolSSCOPMCE = 128 // SSCOPMCE
|
||||||
|
ProtocolIPLT = 129 // IPLT
|
||||||
|
ProtocolSPS = 130 // Secure Packet Shield
|
||||||
|
ProtocolPIPE = 131 // Private IP Encapsulation within IP
|
||||||
|
ProtocolSCTP = 132 // Stream Control Transmission Protocol
|
||||||
|
ProtocolFC = 133 // Fibre Channel
|
||||||
|
ProtocolRSVPE2EIGNORE = 134 // RSVP-E2E-IGNORE
|
||||||
|
ProtocolMobilityHeader = 135 // Mobility Header
|
||||||
|
ProtocolUDPLite = 136 // UDPLite
|
||||||
|
ProtocolMPLSinIP = 137 // MPLS-in-IP
|
||||||
|
ProtocolMANET = 138 // MANET Protocols
|
||||||
|
ProtocolHIP = 139 // Host Identity Protocol
|
||||||
|
ProtocolShim6 = 140 // Shim6 Protocol
|
||||||
|
ProtocolWESP = 141 // Wrapped Encapsulating Security Payload
|
||||||
|
ProtocolROHC = 142 // Robust Header Compression
|
||||||
|
ProtocolReserved = 255 // Reserved
|
||||||
|
)
|
||||||
+293
@@ -0,0 +1,293 @@
|
|||||||
|
// 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 ignore
|
||||||
|
|
||||||
|
//go:generate go run gen.go
|
||||||
|
|
||||||
|
// This program generates internet protocol constants and tables by
|
||||||
|
// reading IANA protocol registries.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"go/format"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var registries = []struct {
|
||||||
|
url string
|
||||||
|
parse func(io.Writer, io.Reader) error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"http://www.iana.org/assignments/dscp-registry/dscp-registry.xml",
|
||||||
|
parseDSCPRegistry,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"http://www.iana.org/assignments/ipv4-tos-byte/ipv4-tos-byte.xml",
|
||||||
|
parseTOSTCByte,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml",
|
||||||
|
parseProtocolNumbers,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var bb bytes.Buffer
|
||||||
|
fmt.Fprintf(&bb, "// go generate gen.go\n")
|
||||||
|
fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n")
|
||||||
|
fmt.Fprintf(&bb, "// Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA).\n")
|
||||||
|
fmt.Fprintf(&bb, `package iana // import "golang.org/x/net/internal/iana"`+"\n\n")
|
||||||
|
for _, r := range registries {
|
||||||
|
resp, err := http.Get(r.url)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
fmt.Fprintf(os.Stderr, "got HTTP status code %v for %v\n", resp.StatusCode, r.url)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if err := r.parse(&bb, resp.Body); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&bb, "\n")
|
||||||
|
}
|
||||||
|
b, err := format.Source(bb.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile("const.go", b, 0644); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDSCPRegistry(w io.Writer, r io.Reader) error {
|
||||||
|
dec := xml.NewDecoder(r)
|
||||||
|
var dr dscpRegistry
|
||||||
|
if err := dec.Decode(&dr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
drs := dr.escape()
|
||||||
|
fmt.Fprintf(w, "// %s, Updated: %s\n", dr.Title, dr.Updated)
|
||||||
|
fmt.Fprintf(w, "const (\n")
|
||||||
|
for _, dr := range drs {
|
||||||
|
fmt.Fprintf(w, "DiffServ%s = %#x", dr.Name, dr.Value)
|
||||||
|
fmt.Fprintf(w, "// %s\n", dr.OrigName)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, ")\n")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type dscpRegistry struct {
|
||||||
|
XMLName xml.Name `xml:"registry"`
|
||||||
|
Title string `xml:"title"`
|
||||||
|
Updated string `xml:"updated"`
|
||||||
|
Note string `xml:"note"`
|
||||||
|
RegTitle string `xml:"registry>title"`
|
||||||
|
PoolRecords []struct {
|
||||||
|
Name string `xml:"name"`
|
||||||
|
Space string `xml:"space"`
|
||||||
|
} `xml:"registry>record"`
|
||||||
|
Records []struct {
|
||||||
|
Name string `xml:"name"`
|
||||||
|
Space string `xml:"space"`
|
||||||
|
} `xml:"registry>registry>record"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type canonDSCPRecord struct {
|
||||||
|
OrigName string
|
||||||
|
Name string
|
||||||
|
Value int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (drr *dscpRegistry) escape() []canonDSCPRecord {
|
||||||
|
drs := make([]canonDSCPRecord, len(drr.Records))
|
||||||
|
sr := strings.NewReplacer(
|
||||||
|
"+", "",
|
||||||
|
"-", "",
|
||||||
|
"/", "",
|
||||||
|
".", "",
|
||||||
|
" ", "",
|
||||||
|
)
|
||||||
|
for i, dr := range drr.Records {
|
||||||
|
s := strings.TrimSpace(dr.Name)
|
||||||
|
drs[i].OrigName = s
|
||||||
|
drs[i].Name = sr.Replace(s)
|
||||||
|
n, err := strconv.ParseUint(dr.Space, 2, 8)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
drs[i].Value = int(n) << 2
|
||||||
|
}
|
||||||
|
return drs
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTOSTCByte(w io.Writer, r io.Reader) error {
|
||||||
|
dec := xml.NewDecoder(r)
|
||||||
|
var ttb tosTCByte
|
||||||
|
if err := dec.Decode(&ttb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
trs := ttb.escape()
|
||||||
|
fmt.Fprintf(w, "// %s, Updated: %s\n", ttb.Title, ttb.Updated)
|
||||||
|
fmt.Fprintf(w, "const (\n")
|
||||||
|
for _, tr := range trs {
|
||||||
|
fmt.Fprintf(w, "%s = %#x", tr.Keyword, tr.Value)
|
||||||
|
fmt.Fprintf(w, "// %s\n", tr.OrigKeyword)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, ")\n")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type tosTCByte struct {
|
||||||
|
XMLName xml.Name `xml:"registry"`
|
||||||
|
Title string `xml:"title"`
|
||||||
|
Updated string `xml:"updated"`
|
||||||
|
Note string `xml:"note"`
|
||||||
|
RegTitle string `xml:"registry>title"`
|
||||||
|
Records []struct {
|
||||||
|
Binary string `xml:"binary"`
|
||||||
|
Keyword string `xml:"keyword"`
|
||||||
|
} `xml:"registry>record"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type canonTOSTCByteRecord struct {
|
||||||
|
OrigKeyword string
|
||||||
|
Keyword string
|
||||||
|
Value int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ttb *tosTCByte) escape() []canonTOSTCByteRecord {
|
||||||
|
trs := make([]canonTOSTCByteRecord, len(ttb.Records))
|
||||||
|
sr := strings.NewReplacer(
|
||||||
|
"Capable", "",
|
||||||
|
"(", "",
|
||||||
|
")", "",
|
||||||
|
"+", "",
|
||||||
|
"-", "",
|
||||||
|
"/", "",
|
||||||
|
".", "",
|
||||||
|
" ", "",
|
||||||
|
)
|
||||||
|
for i, tr := range ttb.Records {
|
||||||
|
s := strings.TrimSpace(tr.Keyword)
|
||||||
|
trs[i].OrigKeyword = s
|
||||||
|
ss := strings.Split(s, " ")
|
||||||
|
if len(ss) > 1 {
|
||||||
|
trs[i].Keyword = strings.Join(ss[1:], " ")
|
||||||
|
} else {
|
||||||
|
trs[i].Keyword = ss[0]
|
||||||
|
}
|
||||||
|
trs[i].Keyword = sr.Replace(trs[i].Keyword)
|
||||||
|
n, err := strconv.ParseUint(tr.Binary, 2, 8)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
trs[i].Value = int(n)
|
||||||
|
}
|
||||||
|
return trs
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseProtocolNumbers(w io.Writer, r io.Reader) error {
|
||||||
|
dec := xml.NewDecoder(r)
|
||||||
|
var pn protocolNumbers
|
||||||
|
if err := dec.Decode(&pn); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
prs := pn.escape()
|
||||||
|
prs = append([]canonProtocolRecord{{
|
||||||
|
Name: "IP",
|
||||||
|
Descr: "IPv4 encapsulation, pseudo protocol number",
|
||||||
|
Value: 0,
|
||||||
|
}}, prs...)
|
||||||
|
fmt.Fprintf(w, "// %s, Updated: %s\n", pn.Title, pn.Updated)
|
||||||
|
fmt.Fprintf(w, "const (\n")
|
||||||
|
for _, pr := range prs {
|
||||||
|
if pr.Name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "Protocol%s = %d", pr.Name, pr.Value)
|
||||||
|
s := pr.Descr
|
||||||
|
if s == "" {
|
||||||
|
s = pr.OrigName
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "// %s\n", s)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, ")\n")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type protocolNumbers struct {
|
||||||
|
XMLName xml.Name `xml:"registry"`
|
||||||
|
Title string `xml:"title"`
|
||||||
|
Updated string `xml:"updated"`
|
||||||
|
RegTitle string `xml:"registry>title"`
|
||||||
|
Note string `xml:"registry>note"`
|
||||||
|
Records []struct {
|
||||||
|
Value string `xml:"value"`
|
||||||
|
Name string `xml:"name"`
|
||||||
|
Descr string `xml:"description"`
|
||||||
|
} `xml:"registry>record"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type canonProtocolRecord struct {
|
||||||
|
OrigName string
|
||||||
|
Name string
|
||||||
|
Descr string
|
||||||
|
Value int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pn *protocolNumbers) escape() []canonProtocolRecord {
|
||||||
|
prs := make([]canonProtocolRecord, len(pn.Records))
|
||||||
|
sr := strings.NewReplacer(
|
||||||
|
"-in-", "in",
|
||||||
|
"-within-", "within",
|
||||||
|
"-over-", "over",
|
||||||
|
"+", "P",
|
||||||
|
"-", "",
|
||||||
|
"/", "",
|
||||||
|
".", "",
|
||||||
|
" ", "",
|
||||||
|
)
|
||||||
|
for i, pr := range pn.Records {
|
||||||
|
if strings.Contains(pr.Name, "Deprecated") ||
|
||||||
|
strings.Contains(pr.Name, "deprecated") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
prs[i].OrigName = pr.Name
|
||||||
|
s := strings.TrimSpace(pr.Name)
|
||||||
|
switch pr.Name {
|
||||||
|
case "ISIS over IPv4":
|
||||||
|
prs[i].Name = "ISIS"
|
||||||
|
case "manet":
|
||||||
|
prs[i].Name = "MANET"
|
||||||
|
default:
|
||||||
|
prs[i].Name = sr.Replace(s)
|
||||||
|
}
|
||||||
|
ss := strings.Split(pr.Descr, "\n")
|
||||||
|
for i := range ss {
|
||||||
|
ss[i] = strings.TrimSpace(ss[i])
|
||||||
|
}
|
||||||
|
if len(ss) > 1 {
|
||||||
|
prs[i].Descr = strings.Join(ss, " ")
|
||||||
|
} else {
|
||||||
|
prs[i].Descr = ss[0]
|
||||||
|
}
|
||||||
|
prs[i].Value, _ = strconv.Atoi(pr.Value)
|
||||||
|
}
|
||||||
|
return prs
|
||||||
|
}
|
||||||
+92
@@ -0,0 +1,92 @@
|
|||||||
|
// 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 ipv6
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errMissingAddress = errors.New("missing address")
|
||||||
|
errInvalidConnType = errors.New("invalid conn type")
|
||||||
|
errNoSuchInterface = errors.New("no such interface")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Note that RFC 3542 obsoletes RFC 2292 but OS X Snow Leopard and the
|
||||||
|
// former still support RFC 2292 only. Please be aware that almost
|
||||||
|
// all protocol implementations prohibit using a combination of RFC
|
||||||
|
// 2292 and RFC 3542 for some practical reasons.
|
||||||
|
|
||||||
|
type rawOpt struct {
|
||||||
|
sync.RWMutex
|
||||||
|
cflags ControlFlags
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *rawOpt) set(f ControlFlags) { c.cflags |= f }
|
||||||
|
func (c *rawOpt) clear(f ControlFlags) { c.cflags &^= f }
|
||||||
|
func (c *rawOpt) isset(f ControlFlags) bool { return c.cflags&f != 0 }
|
||||||
|
|
||||||
|
// A ControlFlags represents per packet basis IP-level socket option
|
||||||
|
// control flags.
|
||||||
|
type ControlFlags uint
|
||||||
|
|
||||||
|
const (
|
||||||
|
FlagTrafficClass ControlFlags = 1 << iota // pass the traffic class on the received packet
|
||||||
|
FlagHopLimit // pass the hop limit on the received packet
|
||||||
|
FlagSrc // pass the source address on the received packet
|
||||||
|
FlagDst // pass the destination address on the received packet
|
||||||
|
FlagInterface // pass the interface index on the received packet
|
||||||
|
FlagPathMTU // pass the path MTU on the received packet path
|
||||||
|
)
|
||||||
|
|
||||||
|
const flagPacketInfo = FlagDst | FlagInterface
|
||||||
|
|
||||||
|
// A ControlMessage represents per packet basis IP-level socket
|
||||||
|
// options.
|
||||||
|
type ControlMessage struct {
|
||||||
|
// Receiving socket options: SetControlMessage allows to
|
||||||
|
// receive the options from the protocol stack using ReadFrom
|
||||||
|
// method of PacketConn.
|
||||||
|
//
|
||||||
|
// Specifying socket options: ControlMessage for WriteTo
|
||||||
|
// method of PacketConn allows to send the options to the
|
||||||
|
// protocol stack.
|
||||||
|
//
|
||||||
|
TrafficClass int // traffic class, must be 1 <= value <= 255 when specifying
|
||||||
|
HopLimit int // hop limit, must be 1 <= value <= 255 when specifying
|
||||||
|
Src net.IP // source address, specifying only
|
||||||
|
Dst net.IP // destination address, receiving only
|
||||||
|
IfIndex int // interface index, must be 1 <= value when specifying
|
||||||
|
NextHop net.IP // next hop address, specifying only
|
||||||
|
MTU int // path MTU, receiving only
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *ControlMessage) String() string {
|
||||||
|
if cm == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("tclass: %#x, hoplim: %v, src: %v, dst: %v, ifindex: %v, nexthop: %v, mtu: %v", cm.TrafficClass, cm.HopLimit, cm.Src, cm.Dst, cm.IfIndex, cm.NextHop, cm.MTU)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ancillary data socket options
|
||||||
|
const (
|
||||||
|
ctlTrafficClass = iota // header field
|
||||||
|
ctlHopLimit // header field
|
||||||
|
ctlPacketInfo // inbound or outbound packet path
|
||||||
|
ctlNextHop // nexthop
|
||||||
|
ctlPathMTU // path mtu
|
||||||
|
ctlMax
|
||||||
|
)
|
||||||
|
|
||||||
|
// A ctlOpt represents a binding for ancillary data socket option.
|
||||||
|
type ctlOpt struct {
|
||||||
|
name int // option name, must be equal or greater than 1
|
||||||
|
length int // option length
|
||||||
|
marshal func([]byte, *ControlMessage) []byte
|
||||||
|
parse func(*ControlMessage, []byte)
|
||||||
|
}
|
||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
// 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 darwin
|
||||||
|
|
||||||
|
package ipv6
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/net/internal/iana"
|
||||||
|
)
|
||||||
|
|
||||||
|
func marshal2292HopLimit(b []byte, cm *ControlMessage) []byte {
|
||||||
|
m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||||
|
m.Level = iana.ProtocolIPv6
|
||||||
|
m.Type = sysIPV6_2292HOPLIMIT
|
||||||
|
m.SetLen(syscall.CmsgLen(4))
|
||||||
|
if cm != nil {
|
||||||
|
data := b[syscall.CmsgLen(0):]
|
||||||
|
// TODO(mikio): fix potential misaligned memory access
|
||||||
|
*(*int32)(unsafe.Pointer(&data[:4][0])) = int32(cm.HopLimit)
|
||||||
|
}
|
||||||
|
return b[syscall.CmsgSpace(4):]
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshal2292PacketInfo(b []byte, cm *ControlMessage) []byte {
|
||||||
|
m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||||
|
m.Level = iana.ProtocolIPv6
|
||||||
|
m.Type = sysIPV6_2292PKTINFO
|
||||||
|
m.SetLen(syscall.CmsgLen(sysSizeofInet6Pktinfo))
|
||||||
|
if cm != nil {
|
||||||
|
pi := (*sysInet6Pktinfo)(unsafe.Pointer(&b[syscall.CmsgLen(0)]))
|
||||||
|
if ip := cm.Src.To16(); ip != nil && ip.To4() == nil {
|
||||||
|
copy(pi.Addr[:], ip)
|
||||||
|
}
|
||||||
|
if cm.IfIndex > 0 {
|
||||||
|
pi.setIfindex(cm.IfIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b[syscall.CmsgSpace(sysSizeofInet6Pktinfo):]
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshal2292NextHop(b []byte, cm *ControlMessage) []byte {
|
||||||
|
m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||||
|
m.Level = iana.ProtocolIPv6
|
||||||
|
m.Type = sysIPV6_2292NEXTHOP
|
||||||
|
m.SetLen(syscall.CmsgLen(sysSizeofSockaddrInet6))
|
||||||
|
if cm != nil {
|
||||||
|
sa := (*sysSockaddrInet6)(unsafe.Pointer(&b[syscall.CmsgLen(0)]))
|
||||||
|
sa.setSockaddr(cm.NextHop, cm.IfIndex)
|
||||||
|
}
|
||||||
|
return b[syscall.CmsgSpace(sysSizeofSockaddrInet6):]
|
||||||
|
}
|
||||||
+103
@@ -0,0 +1,103 @@
|
|||||||
|
// 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 darwin dragonfly freebsd linux netbsd openbsd
|
||||||
|
|
||||||
|
package ipv6
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/net/internal/iana"
|
||||||
|
)
|
||||||
|
|
||||||
|
func marshalTrafficClass(b []byte, cm *ControlMessage) []byte {
|
||||||
|
m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||||
|
m.Level = iana.ProtocolIPv6
|
||||||
|
m.Type = sysIPV6_TCLASS
|
||||||
|
m.SetLen(syscall.CmsgLen(4))
|
||||||
|
if cm != nil {
|
||||||
|
data := b[syscall.CmsgLen(0):]
|
||||||
|
// TODO(mikio): fix potential misaligned memory access
|
||||||
|
*(*int32)(unsafe.Pointer(&data[:4][0])) = int32(cm.TrafficClass)
|
||||||
|
}
|
||||||
|
return b[syscall.CmsgSpace(4):]
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTrafficClass(cm *ControlMessage, b []byte) {
|
||||||
|
// TODO(mikio): fix potential misaligned memory access
|
||||||
|
cm.TrafficClass = int(*(*int32)(unsafe.Pointer(&b[:4][0])))
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalHopLimit(b []byte, cm *ControlMessage) []byte {
|
||||||
|
m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||||
|
m.Level = iana.ProtocolIPv6
|
||||||
|
m.Type = sysIPV6_HOPLIMIT
|
||||||
|
m.SetLen(syscall.CmsgLen(4))
|
||||||
|
if cm != nil {
|
||||||
|
data := b[syscall.CmsgLen(0):]
|
||||||
|
// TODO(mikio): fix potential misaligned memory access
|
||||||
|
*(*int32)(unsafe.Pointer(&data[:4][0])) = int32(cm.HopLimit)
|
||||||
|
}
|
||||||
|
return b[syscall.CmsgSpace(4):]
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseHopLimit(cm *ControlMessage, b []byte) {
|
||||||
|
// TODO(mikio): fix potential misaligned memory access
|
||||||
|
cm.HopLimit = int(*(*int32)(unsafe.Pointer(&b[:4][0])))
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalPacketInfo(b []byte, cm *ControlMessage) []byte {
|
||||||
|
m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||||
|
m.Level = iana.ProtocolIPv6
|
||||||
|
m.Type = sysIPV6_PKTINFO
|
||||||
|
m.SetLen(syscall.CmsgLen(sysSizeofInet6Pktinfo))
|
||||||
|
if cm != nil {
|
||||||
|
pi := (*sysInet6Pktinfo)(unsafe.Pointer(&b[syscall.CmsgLen(0)]))
|
||||||
|
if ip := cm.Src.To16(); ip != nil && ip.To4() == nil {
|
||||||
|
copy(pi.Addr[:], ip)
|
||||||
|
}
|
||||||
|
if cm.IfIndex > 0 {
|
||||||
|
pi.setIfindex(cm.IfIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b[syscall.CmsgSpace(sysSizeofInet6Pktinfo):]
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePacketInfo(cm *ControlMessage, b []byte) {
|
||||||
|
pi := (*sysInet6Pktinfo)(unsafe.Pointer(&b[0]))
|
||||||
|
cm.Dst = pi.Addr[:]
|
||||||
|
cm.IfIndex = int(pi.Ifindex)
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalNextHop(b []byte, cm *ControlMessage) []byte {
|
||||||
|
m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||||
|
m.Level = iana.ProtocolIPv6
|
||||||
|
m.Type = sysIPV6_NEXTHOP
|
||||||
|
m.SetLen(syscall.CmsgLen(sysSizeofSockaddrInet6))
|
||||||
|
if cm != nil {
|
||||||
|
sa := (*sysSockaddrInet6)(unsafe.Pointer(&b[syscall.CmsgLen(0)]))
|
||||||
|
sa.setSockaddr(cm.NextHop, cm.IfIndex)
|
||||||
|
}
|
||||||
|
return b[syscall.CmsgSpace(sysSizeofSockaddrInet6):]
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseNextHop(cm *ControlMessage, b []byte) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalPathMTU(b []byte, cm *ControlMessage) []byte {
|
||||||
|
m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||||
|
m.Level = iana.ProtocolIPv6
|
||||||
|
m.Type = sysIPV6_PATHMTU
|
||||||
|
m.SetLen(syscall.CmsgLen(sysSizeofIPv6Mtuinfo))
|
||||||
|
return b[syscall.CmsgSpace(sysSizeofIPv6Mtuinfo):]
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePathMTU(cm *ControlMessage, b []byte) {
|
||||||
|
mi := (*sysIPv6Mtuinfo)(unsafe.Pointer(&b[0]))
|
||||||
|
cm.Dst = mi.Addr.Addr[:]
|
||||||
|
cm.IfIndex = int(mi.Addr.Scope_id)
|
||||||
|
cm.MTU = int(mi.Mtu)
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
// 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 nacl plan9 solaris
|
||||||
|
|
||||||
|
package ipv6
|
||||||
|
|
||||||
|
func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
|
||||||
|
return errOpNoSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func newControlMessage(opt *rawOpt) (oob []byte) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseControlMessage(b []byte) (*ControlMessage, error) {
|
||||||
|
return nil, errOpNoSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalControlMessage(cm *ControlMessage) (oob []byte) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+166
@@ -0,0 +1,166 @@
|
|||||||
|
// 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 darwin dragonfly freebsd linux netbsd openbsd
|
||||||
|
|
||||||
|
package ipv6
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/net/internal/iana"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
|
||||||
|
opt.Lock()
|
||||||
|
defer opt.Unlock()
|
||||||
|
if cf&FlagTrafficClass != 0 && sockOpts[ssoReceiveTrafficClass].name > 0 {
|
||||||
|
if err := setInt(fd, &sockOpts[ssoReceiveTrafficClass], boolint(on)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if on {
|
||||||
|
opt.set(FlagTrafficClass)
|
||||||
|
} else {
|
||||||
|
opt.clear(FlagTrafficClass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cf&FlagHopLimit != 0 && sockOpts[ssoReceiveHopLimit].name > 0 {
|
||||||
|
if err := setInt(fd, &sockOpts[ssoReceiveHopLimit], boolint(on)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if on {
|
||||||
|
opt.set(FlagHopLimit)
|
||||||
|
} else {
|
||||||
|
opt.clear(FlagHopLimit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cf&flagPacketInfo != 0 && sockOpts[ssoReceivePacketInfo].name > 0 {
|
||||||
|
if err := setInt(fd, &sockOpts[ssoReceivePacketInfo], boolint(on)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if on {
|
||||||
|
opt.set(cf & flagPacketInfo)
|
||||||
|
} else {
|
||||||
|
opt.clear(cf & flagPacketInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cf&FlagPathMTU != 0 && sockOpts[ssoReceivePathMTU].name > 0 {
|
||||||
|
if err := setInt(fd, &sockOpts[ssoReceivePathMTU], boolint(on)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if on {
|
||||||
|
opt.set(FlagPathMTU)
|
||||||
|
} else {
|
||||||
|
opt.clear(FlagPathMTU)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newControlMessage(opt *rawOpt) (oob []byte) {
|
||||||
|
opt.RLock()
|
||||||
|
var l int
|
||||||
|
if opt.isset(FlagTrafficClass) && ctlOpts[ctlTrafficClass].name > 0 {
|
||||||
|
l += syscall.CmsgSpace(ctlOpts[ctlTrafficClass].length)
|
||||||
|
}
|
||||||
|
if opt.isset(FlagHopLimit) && ctlOpts[ctlHopLimit].name > 0 {
|
||||||
|
l += syscall.CmsgSpace(ctlOpts[ctlHopLimit].length)
|
||||||
|
}
|
||||||
|
if opt.isset(flagPacketInfo) && ctlOpts[ctlPacketInfo].name > 0 {
|
||||||
|
l += syscall.CmsgSpace(ctlOpts[ctlPacketInfo].length)
|
||||||
|
}
|
||||||
|
if opt.isset(FlagPathMTU) && ctlOpts[ctlPathMTU].name > 0 {
|
||||||
|
l += syscall.CmsgSpace(ctlOpts[ctlPathMTU].length)
|
||||||
|
}
|
||||||
|
if l > 0 {
|
||||||
|
oob = make([]byte, l)
|
||||||
|
b := oob
|
||||||
|
if opt.isset(FlagTrafficClass) && ctlOpts[ctlTrafficClass].name > 0 {
|
||||||
|
b = ctlOpts[ctlTrafficClass].marshal(b, nil)
|
||||||
|
}
|
||||||
|
if opt.isset(FlagHopLimit) && ctlOpts[ctlHopLimit].name > 0 {
|
||||||
|
b = ctlOpts[ctlHopLimit].marshal(b, nil)
|
||||||
|
}
|
||||||
|
if opt.isset(flagPacketInfo) && ctlOpts[ctlPacketInfo].name > 0 {
|
||||||
|
b = ctlOpts[ctlPacketInfo].marshal(b, nil)
|
||||||
|
}
|
||||||
|
if opt.isset(FlagPathMTU) && ctlOpts[ctlPathMTU].name > 0 {
|
||||||
|
b = ctlOpts[ctlPathMTU].marshal(b, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
opt.RUnlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseControlMessage(b []byte) (*ControlMessage, error) {
|
||||||
|
if len(b) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
cmsgs, err := syscall.ParseSocketControlMessage(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, os.NewSyscallError("parse socket control message", err)
|
||||||
|
}
|
||||||
|
cm := &ControlMessage{}
|
||||||
|
for _, m := range cmsgs {
|
||||||
|
if m.Header.Level != iana.ProtocolIPv6 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch int(m.Header.Type) {
|
||||||
|
case ctlOpts[ctlTrafficClass].name:
|
||||||
|
ctlOpts[ctlTrafficClass].parse(cm, m.Data[:])
|
||||||
|
case ctlOpts[ctlHopLimit].name:
|
||||||
|
ctlOpts[ctlHopLimit].parse(cm, m.Data[:])
|
||||||
|
case ctlOpts[ctlPacketInfo].name:
|
||||||
|
ctlOpts[ctlPacketInfo].parse(cm, m.Data[:])
|
||||||
|
case ctlOpts[ctlPathMTU].name:
|
||||||
|
ctlOpts[ctlPathMTU].parse(cm, m.Data[:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalControlMessage(cm *ControlMessage) (oob []byte) {
|
||||||
|
if cm == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var l int
|
||||||
|
tclass := false
|
||||||
|
if ctlOpts[ctlTrafficClass].name > 0 && cm.TrafficClass > 0 {
|
||||||
|
tclass = true
|
||||||
|
l += syscall.CmsgSpace(ctlOpts[ctlTrafficClass].length)
|
||||||
|
}
|
||||||
|
hoplimit := false
|
||||||
|
if ctlOpts[ctlHopLimit].name > 0 && cm.HopLimit > 0 {
|
||||||
|
hoplimit = true
|
||||||
|
l += syscall.CmsgSpace(ctlOpts[ctlHopLimit].length)
|
||||||
|
}
|
||||||
|
pktinfo := false
|
||||||
|
if ctlOpts[ctlPacketInfo].name > 0 && (cm.Src.To16() != nil && cm.Src.To4() == nil || cm.IfIndex > 0) {
|
||||||
|
pktinfo = true
|
||||||
|
l += syscall.CmsgSpace(ctlOpts[ctlPacketInfo].length)
|
||||||
|
}
|
||||||
|
nexthop := false
|
||||||
|
if ctlOpts[ctlNextHop].name > 0 && cm.NextHop.To16() != nil && cm.NextHop.To4() == nil {
|
||||||
|
nexthop = true
|
||||||
|
l += syscall.CmsgSpace(ctlOpts[ctlNextHop].length)
|
||||||
|
}
|
||||||
|
if l > 0 {
|
||||||
|
oob = make([]byte, l)
|
||||||
|
b := oob
|
||||||
|
if tclass {
|
||||||
|
b = ctlOpts[ctlTrafficClass].marshal(b, cm)
|
||||||
|
}
|
||||||
|
if hoplimit {
|
||||||
|
b = ctlOpts[ctlHopLimit].marshal(b, cm)
|
||||||
|
}
|
||||||
|
if pktinfo {
|
||||||
|
b = ctlOpts[ctlPacketInfo].marshal(b, cm)
|
||||||
|
}
|
||||||
|
if nexthop {
|
||||||
|
b = ctlOpts[ctlNextHop].marshal(b, cm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
// 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 ipv6
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
func setControlMessage(fd syscall.Handle, opt *rawOpt, cf ControlFlags, on bool) error {
|
||||||
|
// TODO(mikio): implement this
|
||||||
|
return syscall.EWINDOWS
|
||||||
|
}
|
||||||
|
|
||||||
|
func newControlMessage(opt *rawOpt) (oob []byte) {
|
||||||
|
// TODO(mikio): implement this
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseControlMessage(b []byte) (*ControlMessage, error) {
|
||||||
|
// TODO(mikio): implement this
|
||||||
|
return nil, syscall.EWINDOWS
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalControlMessage(cm *ControlMessage) (oob []byte) {
|
||||||
|
// TODO(mikio): implement this
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+112
@@ -0,0 +1,112 @@
|
|||||||
|
// Copyright 2014 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
|
||||||
|
|
||||||
|
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
||||||
|
|
||||||
|
package ipv6
|
||||||
|
|
||||||
|
/*
|
||||||
|
#define __APPLE_USE_RFC_3542
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/icmp6.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
const (
|
||||||
|
sysIPV6_UNICAST_HOPS = C.IPV6_UNICAST_HOPS
|
||||||
|
sysIPV6_MULTICAST_IF = C.IPV6_MULTICAST_IF
|
||||||
|
sysIPV6_MULTICAST_HOPS = C.IPV6_MULTICAST_HOPS
|
||||||
|
sysIPV6_MULTICAST_LOOP = C.IPV6_MULTICAST_LOOP
|
||||||
|
sysIPV6_JOIN_GROUP = C.IPV6_JOIN_GROUP
|
||||||
|
sysIPV6_LEAVE_GROUP = C.IPV6_LEAVE_GROUP
|
||||||
|
|
||||||
|
sysIPV6_PORTRANGE = C.IPV6_PORTRANGE
|
||||||
|
sysICMP6_FILTER = C.ICMP6_FILTER
|
||||||
|
sysIPV6_2292PKTINFO = C.IPV6_2292PKTINFO
|
||||||
|
sysIPV6_2292HOPLIMIT = C.IPV6_2292HOPLIMIT
|
||||||
|
sysIPV6_2292NEXTHOP = C.IPV6_2292NEXTHOP
|
||||||
|
sysIPV6_2292HOPOPTS = C.IPV6_2292HOPOPTS
|
||||||
|
sysIPV6_2292DSTOPTS = C.IPV6_2292DSTOPTS
|
||||||
|
sysIPV6_2292RTHDR = C.IPV6_2292RTHDR
|
||||||
|
|
||||||
|
sysIPV6_2292PKTOPTIONS = C.IPV6_2292PKTOPTIONS
|
||||||
|
|
||||||
|
sysIPV6_CHECKSUM = C.IPV6_CHECKSUM
|
||||||
|
sysIPV6_V6ONLY = C.IPV6_V6ONLY
|
||||||
|
|
||||||
|
sysIPV6_IPSEC_POLICY = C.IPV6_IPSEC_POLICY
|
||||||
|
|
||||||
|
sysIPV6_RECVTCLASS = C.IPV6_RECVTCLASS
|
||||||
|
sysIPV6_TCLASS = C.IPV6_TCLASS
|
||||||
|
|
||||||
|
sysIPV6_RTHDRDSTOPTS = C.IPV6_RTHDRDSTOPTS
|
||||||
|
|
||||||
|
sysIPV6_RECVPKTINFO = C.IPV6_RECVPKTINFO
|
||||||
|
|
||||||
|
sysIPV6_RECVHOPLIMIT = C.IPV6_RECVHOPLIMIT
|
||||||
|
sysIPV6_RECVRTHDR = C.IPV6_RECVRTHDR
|
||||||
|
sysIPV6_RECVHOPOPTS = C.IPV6_RECVHOPOPTS
|
||||||
|
sysIPV6_RECVDSTOPTS = C.IPV6_RECVDSTOPTS
|
||||||
|
|
||||||
|
sysIPV6_USE_MIN_MTU = C.IPV6_USE_MIN_MTU
|
||||||
|
sysIPV6_RECVPATHMTU = C.IPV6_RECVPATHMTU
|
||||||
|
|
||||||
|
sysIPV6_PATHMTU = C.IPV6_PATHMTU
|
||||||
|
|
||||||
|
sysIPV6_PKTINFO = C.IPV6_PKTINFO
|
||||||
|
sysIPV6_HOPLIMIT = C.IPV6_HOPLIMIT
|
||||||
|
sysIPV6_NEXTHOP = C.IPV6_NEXTHOP
|
||||||
|
sysIPV6_HOPOPTS = C.IPV6_HOPOPTS
|
||||||
|
sysIPV6_DSTOPTS = C.IPV6_DSTOPTS
|
||||||
|
sysIPV6_RTHDR = C.IPV6_RTHDR
|
||||||
|
|
||||||
|
sysIPV6_AUTOFLOWLABEL = C.IPV6_AUTOFLOWLABEL
|
||||||
|
|
||||||
|
sysIPV6_DONTFRAG = C.IPV6_DONTFRAG
|
||||||
|
|
||||||
|
sysIPV6_PREFER_TEMPADDR = C.IPV6_PREFER_TEMPADDR
|
||||||
|
|
||||||
|
sysIPV6_MSFILTER = C.IPV6_MSFILTER
|
||||||
|
sysMCAST_JOIN_GROUP = C.MCAST_JOIN_GROUP
|
||||||
|
sysMCAST_LEAVE_GROUP = C.MCAST_LEAVE_GROUP
|
||||||
|
sysMCAST_JOIN_SOURCE_GROUP = C.MCAST_JOIN_SOURCE_GROUP
|
||||||
|
sysMCAST_LEAVE_SOURCE_GROUP = C.MCAST_LEAVE_SOURCE_GROUP
|
||||||
|
sysMCAST_BLOCK_SOURCE = C.MCAST_BLOCK_SOURCE
|
||||||
|
sysMCAST_UNBLOCK_SOURCE = C.MCAST_UNBLOCK_SOURCE
|
||||||
|
|
||||||
|
sysIPV6_BOUND_IF = C.IPV6_BOUND_IF
|
||||||
|
|
||||||
|
sysIPV6_PORTRANGE_DEFAULT = C.IPV6_PORTRANGE_DEFAULT
|
||||||
|
sysIPV6_PORTRANGE_HIGH = C.IPV6_PORTRANGE_HIGH
|
||||||
|
sysIPV6_PORTRANGE_LOW = C.IPV6_PORTRANGE_LOW
|
||||||
|
|
||||||
|
sysSizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage
|
||||||
|
sysSizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
||||||
|
sysSizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
|
||||||
|
sysSizeofIPv6Mtuinfo = C.sizeof_struct_ip6_mtuinfo
|
||||||
|
|
||||||
|
sysSizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq
|
||||||
|
sysSizeofGroupReq = C.sizeof_struct_group_req
|
||||||
|
sysSizeofGroupSourceReq = C.sizeof_struct_group_source_req
|
||||||
|
|
||||||
|
sysSizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
|
||||||
|
)
|
||||||
|
|
||||||
|
type sysSockaddrStorage C.struct_sockaddr_storage
|
||||||
|
|
||||||
|
type sysSockaddrInet6 C.struct_sockaddr_in6
|
||||||
|
|
||||||
|
type sysInet6Pktinfo C.struct_in6_pktinfo
|
||||||
|
|
||||||
|
type sysIPv6Mtuinfo C.struct_ip6_mtuinfo
|
||||||
|
|
||||||
|
type sysIPv6Mreq C.struct_ipv6_mreq
|
||||||
|
|
||||||
|
type sysICMPv6Filter C.struct_icmp6_filter
|
||||||
|
|
||||||
|
type sysGroupReq C.struct_group_req
|
||||||
|
|
||||||
|
type sysGroupSourceReq C.struct_group_source_req
|
||||||
+84
@@ -0,0 +1,84 @@
|
|||||||
|
// Copyright 2014 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
|
||||||
|
|
||||||
|
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
||||||
|
|
||||||
|
package ipv6
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/icmp6.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
const (
|
||||||
|
sysIPV6_UNICAST_HOPS = C.IPV6_UNICAST_HOPS
|
||||||
|
sysIPV6_MULTICAST_IF = C.IPV6_MULTICAST_IF
|
||||||
|
sysIPV6_MULTICAST_HOPS = C.IPV6_MULTICAST_HOPS
|
||||||
|
sysIPV6_MULTICAST_LOOP = C.IPV6_MULTICAST_LOOP
|
||||||
|
sysIPV6_JOIN_GROUP = C.IPV6_JOIN_GROUP
|
||||||
|
sysIPV6_LEAVE_GROUP = C.IPV6_LEAVE_GROUP
|
||||||
|
sysIPV6_PORTRANGE = C.IPV6_PORTRANGE
|
||||||
|
sysICMP6_FILTER = C.ICMP6_FILTER
|
||||||
|
|
||||||
|
sysIPV6_CHECKSUM = C.IPV6_CHECKSUM
|
||||||
|
sysIPV6_V6ONLY = C.IPV6_V6ONLY
|
||||||
|
|
||||||
|
sysIPV6_IPSEC_POLICY = C.IPV6_IPSEC_POLICY
|
||||||
|
|
||||||
|
sysIPV6_RTHDRDSTOPTS = C.IPV6_RTHDRDSTOPTS
|
||||||
|
sysIPV6_RECVPKTINFO = C.IPV6_RECVPKTINFO
|
||||||
|
sysIPV6_RECVHOPLIMIT = C.IPV6_RECVHOPLIMIT
|
||||||
|
sysIPV6_RECVRTHDR = C.IPV6_RECVRTHDR
|
||||||
|
sysIPV6_RECVHOPOPTS = C.IPV6_RECVHOPOPTS
|
||||||
|
sysIPV6_RECVDSTOPTS = C.IPV6_RECVDSTOPTS
|
||||||
|
|
||||||
|
sysIPV6_USE_MIN_MTU = C.IPV6_USE_MIN_MTU
|
||||||
|
sysIPV6_RECVPATHMTU = C.IPV6_RECVPATHMTU
|
||||||
|
|
||||||
|
sysIPV6_PATHMTU = C.IPV6_PATHMTU
|
||||||
|
|
||||||
|
sysIPV6_PKTINFO = C.IPV6_PKTINFO
|
||||||
|
sysIPV6_HOPLIMIT = C.IPV6_HOPLIMIT
|
||||||
|
sysIPV6_NEXTHOP = C.IPV6_NEXTHOP
|
||||||
|
sysIPV6_HOPOPTS = C.IPV6_HOPOPTS
|
||||||
|
sysIPV6_DSTOPTS = C.IPV6_DSTOPTS
|
||||||
|
sysIPV6_RTHDR = C.IPV6_RTHDR
|
||||||
|
|
||||||
|
sysIPV6_RECVTCLASS = C.IPV6_RECVTCLASS
|
||||||
|
|
||||||
|
sysIPV6_AUTOFLOWLABEL = C.IPV6_AUTOFLOWLABEL
|
||||||
|
|
||||||
|
sysIPV6_TCLASS = C.IPV6_TCLASS
|
||||||
|
sysIPV6_DONTFRAG = C.IPV6_DONTFRAG
|
||||||
|
|
||||||
|
sysIPV6_PREFER_TEMPADDR = C.IPV6_PREFER_TEMPADDR
|
||||||
|
|
||||||
|
sysIPV6_PORTRANGE_DEFAULT = C.IPV6_PORTRANGE_DEFAULT
|
||||||
|
sysIPV6_PORTRANGE_HIGH = C.IPV6_PORTRANGE_HIGH
|
||||||
|
sysIPV6_PORTRANGE_LOW = C.IPV6_PORTRANGE_LOW
|
||||||
|
|
||||||
|
sysSizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
||||||
|
sysSizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
|
||||||
|
sysSizeofIPv6Mtuinfo = C.sizeof_struct_ip6_mtuinfo
|
||||||
|
|
||||||
|
sysSizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq
|
||||||
|
|
||||||
|
sysSizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
|
||||||
|
)
|
||||||
|
|
||||||
|
type sysSockaddrInet6 C.struct_sockaddr_in6
|
||||||
|
|
||||||
|
type sysInet6Pktinfo C.struct_in6_pktinfo
|
||||||
|
|
||||||
|
type sysIPv6Mtuinfo C.struct_ip6_mtuinfo
|
||||||
|
|
||||||
|
type sysIPv6Mreq C.struct_ipv6_mreq
|
||||||
|
|
||||||
|
type sysICMPv6Filter C.struct_icmp6_filter
|
||||||
+105
@@ -0,0 +1,105 @@
|
|||||||
|
// Copyright 2014 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
|
||||||
|
|
||||||
|
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
||||||
|
|
||||||
|
package ipv6
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/icmp6.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
const (
|
||||||
|
sysIPV6_UNICAST_HOPS = C.IPV6_UNICAST_HOPS
|
||||||
|
sysIPV6_MULTICAST_IF = C.IPV6_MULTICAST_IF
|
||||||
|
sysIPV6_MULTICAST_HOPS = C.IPV6_MULTICAST_HOPS
|
||||||
|
sysIPV6_MULTICAST_LOOP = C.IPV6_MULTICAST_LOOP
|
||||||
|
sysIPV6_JOIN_GROUP = C.IPV6_JOIN_GROUP
|
||||||
|
sysIPV6_LEAVE_GROUP = C.IPV6_LEAVE_GROUP
|
||||||
|
sysIPV6_PORTRANGE = C.IPV6_PORTRANGE
|
||||||
|
sysICMP6_FILTER = C.ICMP6_FILTER
|
||||||
|
|
||||||
|
sysIPV6_CHECKSUM = C.IPV6_CHECKSUM
|
||||||
|
sysIPV6_V6ONLY = C.IPV6_V6ONLY
|
||||||
|
|
||||||
|
sysIPV6_IPSEC_POLICY = C.IPV6_IPSEC_POLICY
|
||||||
|
|
||||||
|
sysIPV6_RTHDRDSTOPTS = C.IPV6_RTHDRDSTOPTS
|
||||||
|
|
||||||
|
sysIPV6_RECVPKTINFO = C.IPV6_RECVPKTINFO
|
||||||
|
sysIPV6_RECVHOPLIMIT = C.IPV6_RECVHOPLIMIT
|
||||||
|
sysIPV6_RECVRTHDR = C.IPV6_RECVRTHDR
|
||||||
|
sysIPV6_RECVHOPOPTS = C.IPV6_RECVHOPOPTS
|
||||||
|
sysIPV6_RECVDSTOPTS = C.IPV6_RECVDSTOPTS
|
||||||
|
|
||||||
|
sysIPV6_USE_MIN_MTU = C.IPV6_USE_MIN_MTU
|
||||||
|
sysIPV6_RECVPATHMTU = C.IPV6_RECVPATHMTU
|
||||||
|
|
||||||
|
sysIPV6_PATHMTU = C.IPV6_PATHMTU
|
||||||
|
|
||||||
|
sysIPV6_PKTINFO = C.IPV6_PKTINFO
|
||||||
|
sysIPV6_HOPLIMIT = C.IPV6_HOPLIMIT
|
||||||
|
sysIPV6_NEXTHOP = C.IPV6_NEXTHOP
|
||||||
|
sysIPV6_HOPOPTS = C.IPV6_HOPOPTS
|
||||||
|
sysIPV6_DSTOPTS = C.IPV6_DSTOPTS
|
||||||
|
sysIPV6_RTHDR = C.IPV6_RTHDR
|
||||||
|
|
||||||
|
sysIPV6_RECVTCLASS = C.IPV6_RECVTCLASS
|
||||||
|
|
||||||
|
sysIPV6_AUTOFLOWLABEL = C.IPV6_AUTOFLOWLABEL
|
||||||
|
|
||||||
|
sysIPV6_TCLASS = C.IPV6_TCLASS
|
||||||
|
sysIPV6_DONTFRAG = C.IPV6_DONTFRAG
|
||||||
|
|
||||||
|
sysIPV6_PREFER_TEMPADDR = C.IPV6_PREFER_TEMPADDR
|
||||||
|
|
||||||
|
sysIPV6_BINDANY = C.IPV6_BINDANY
|
||||||
|
|
||||||
|
sysIPV6_MSFILTER = C.IPV6_MSFILTER
|
||||||
|
|
||||||
|
sysMCAST_JOIN_GROUP = C.MCAST_JOIN_GROUP
|
||||||
|
sysMCAST_LEAVE_GROUP = C.MCAST_LEAVE_GROUP
|
||||||
|
sysMCAST_JOIN_SOURCE_GROUP = C.MCAST_JOIN_SOURCE_GROUP
|
||||||
|
sysMCAST_LEAVE_SOURCE_GROUP = C.MCAST_LEAVE_SOURCE_GROUP
|
||||||
|
sysMCAST_BLOCK_SOURCE = C.MCAST_BLOCK_SOURCE
|
||||||
|
sysMCAST_UNBLOCK_SOURCE = C.MCAST_UNBLOCK_SOURCE
|
||||||
|
|
||||||
|
sysIPV6_PORTRANGE_DEFAULT = C.IPV6_PORTRANGE_DEFAULT
|
||||||
|
sysIPV6_PORTRANGE_HIGH = C.IPV6_PORTRANGE_HIGH
|
||||||
|
sysIPV6_PORTRANGE_LOW = C.IPV6_PORTRANGE_LOW
|
||||||
|
|
||||||
|
sysSizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage
|
||||||
|
sysSizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
||||||
|
sysSizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
|
||||||
|
sysSizeofIPv6Mtuinfo = C.sizeof_struct_ip6_mtuinfo
|
||||||
|
|
||||||
|
sysSizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq
|
||||||
|
sysSizeofGroupReq = C.sizeof_struct_group_req
|
||||||
|
sysSizeofGroupSourceReq = C.sizeof_struct_group_source_req
|
||||||
|
|
||||||
|
sysSizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
|
||||||
|
)
|
||||||
|
|
||||||
|
type sysSockaddrStorage C.struct_sockaddr_storage
|
||||||
|
|
||||||
|
type sysSockaddrInet6 C.struct_sockaddr_in6
|
||||||
|
|
||||||
|
type sysInet6Pktinfo C.struct_in6_pktinfo
|
||||||
|
|
||||||
|
type sysIPv6Mtuinfo C.struct_ip6_mtuinfo
|
||||||
|
|
||||||
|
type sysIPv6Mreq C.struct_ipv6_mreq
|
||||||
|
|
||||||
|
type sysGroupReq C.struct_group_req
|
||||||
|
|
||||||
|
type sysGroupSourceReq C.struct_group_source_req
|
||||||
|
|
||||||
|
type sysICMPv6Filter C.struct_icmp6_filter
|
||||||
+136
@@ -0,0 +1,136 @@
|
|||||||
|
// Copyright 2014 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
|
||||||
|
|
||||||
|
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
||||||
|
|
||||||
|
package ipv6
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <linux/in.h>
|
||||||
|
#include <linux/in6.h>
|
||||||
|
#include <linux/ipv6.h>
|
||||||
|
#include <linux/icmpv6.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
const (
|
||||||
|
sysIPV6_ADDRFORM = C.IPV6_ADDRFORM
|
||||||
|
sysIPV6_2292PKTINFO = C.IPV6_2292PKTINFO
|
||||||
|
sysIPV6_2292HOPOPTS = C.IPV6_2292HOPOPTS
|
||||||
|
sysIPV6_2292DSTOPTS = C.IPV6_2292DSTOPTS
|
||||||
|
sysIPV6_2292RTHDR = C.IPV6_2292RTHDR
|
||||||
|
sysIPV6_2292PKTOPTIONS = C.IPV6_2292PKTOPTIONS
|
||||||
|
sysIPV6_CHECKSUM = C.IPV6_CHECKSUM
|
||||||
|
sysIPV6_2292HOPLIMIT = C.IPV6_2292HOPLIMIT
|
||||||
|
sysIPV6_NEXTHOP = C.IPV6_NEXTHOP
|
||||||
|
sysIPV6_FLOWINFO = C.IPV6_FLOWINFO
|
||||||
|
|
||||||
|
sysIPV6_UNICAST_HOPS = C.IPV6_UNICAST_HOPS
|
||||||
|
sysIPV6_MULTICAST_IF = C.IPV6_MULTICAST_IF
|
||||||
|
sysIPV6_MULTICAST_HOPS = C.IPV6_MULTICAST_HOPS
|
||||||
|
sysIPV6_MULTICAST_LOOP = C.IPV6_MULTICAST_LOOP
|
||||||
|
sysIPV6_ADD_MEMBERSHIP = C.IPV6_ADD_MEMBERSHIP
|
||||||
|
sysIPV6_DROP_MEMBERSHIP = C.IPV6_DROP_MEMBERSHIP
|
||||||
|
sysMCAST_JOIN_GROUP = C.MCAST_JOIN_GROUP
|
||||||
|
sysMCAST_LEAVE_GROUP = C.MCAST_LEAVE_GROUP
|
||||||
|
sysMCAST_JOIN_SOURCE_GROUP = C.MCAST_JOIN_SOURCE_GROUP
|
||||||
|
sysMCAST_LEAVE_SOURCE_GROUP = C.MCAST_LEAVE_SOURCE_GROUP
|
||||||
|
sysMCAST_BLOCK_SOURCE = C.MCAST_BLOCK_SOURCE
|
||||||
|
sysMCAST_UNBLOCK_SOURCE = C.MCAST_UNBLOCK_SOURCE
|
||||||
|
sysMCAST_MSFILTER = C.MCAST_MSFILTER
|
||||||
|
sysIPV6_ROUTER_ALERT = C.IPV6_ROUTER_ALERT
|
||||||
|
sysIPV6_MTU_DISCOVER = C.IPV6_MTU_DISCOVER
|
||||||
|
sysIPV6_MTU = C.IPV6_MTU
|
||||||
|
sysIPV6_RECVERR = C.IPV6_RECVERR
|
||||||
|
sysIPV6_V6ONLY = C.IPV6_V6ONLY
|
||||||
|
sysIPV6_JOIN_ANYCAST = C.IPV6_JOIN_ANYCAST
|
||||||
|
sysIPV6_LEAVE_ANYCAST = C.IPV6_LEAVE_ANYCAST
|
||||||
|
|
||||||
|
//sysIPV6_PMTUDISC_DONT = C.IPV6_PMTUDISC_DONT
|
||||||
|
//sysIPV6_PMTUDISC_WANT = C.IPV6_PMTUDISC_WANT
|
||||||
|
//sysIPV6_PMTUDISC_DO = C.IPV6_PMTUDISC_DO
|
||||||
|
//sysIPV6_PMTUDISC_PROBE = C.IPV6_PMTUDISC_PROBE
|
||||||
|
//sysIPV6_PMTUDISC_INTERFACE = C.IPV6_PMTUDISC_INTERFACE
|
||||||
|
//sysIPV6_PMTUDISC_OMIT = C.IPV6_PMTUDISC_OMIT
|
||||||
|
|
||||||
|
sysIPV6_FLOWLABEL_MGR = C.IPV6_FLOWLABEL_MGR
|
||||||
|
sysIPV6_FLOWINFO_SEND = C.IPV6_FLOWINFO_SEND
|
||||||
|
|
||||||
|
sysIPV6_IPSEC_POLICY = C.IPV6_IPSEC_POLICY
|
||||||
|
sysIPV6_XFRM_POLICY = C.IPV6_XFRM_POLICY
|
||||||
|
|
||||||
|
sysIPV6_RECVPKTINFO = C.IPV6_RECVPKTINFO
|
||||||
|
sysIPV6_PKTINFO = C.IPV6_PKTINFO
|
||||||
|
sysIPV6_RECVHOPLIMIT = C.IPV6_RECVHOPLIMIT
|
||||||
|
sysIPV6_HOPLIMIT = C.IPV6_HOPLIMIT
|
||||||
|
sysIPV6_RECVHOPOPTS = C.IPV6_RECVHOPOPTS
|
||||||
|
sysIPV6_HOPOPTS = C.IPV6_HOPOPTS
|
||||||
|
sysIPV6_RTHDRDSTOPTS = C.IPV6_RTHDRDSTOPTS
|
||||||
|
sysIPV6_RECVRTHDR = C.IPV6_RECVRTHDR
|
||||||
|
sysIPV6_RTHDR = C.IPV6_RTHDR
|
||||||
|
sysIPV6_RECVDSTOPTS = C.IPV6_RECVDSTOPTS
|
||||||
|
sysIPV6_DSTOPTS = C.IPV6_DSTOPTS
|
||||||
|
sysIPV6_RECVPATHMTU = C.IPV6_RECVPATHMTU
|
||||||
|
sysIPV6_PATHMTU = C.IPV6_PATHMTU
|
||||||
|
sysIPV6_DONTFRAG = C.IPV6_DONTFRAG
|
||||||
|
|
||||||
|
sysIPV6_RECVTCLASS = C.IPV6_RECVTCLASS
|
||||||
|
sysIPV6_TCLASS = C.IPV6_TCLASS
|
||||||
|
|
||||||
|
sysIPV6_ADDR_PREFERENCES = C.IPV6_ADDR_PREFERENCES
|
||||||
|
|
||||||
|
sysIPV6_PREFER_SRC_TMP = C.IPV6_PREFER_SRC_TMP
|
||||||
|
sysIPV6_PREFER_SRC_PUBLIC = C.IPV6_PREFER_SRC_PUBLIC
|
||||||
|
sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = C.IPV6_PREFER_SRC_PUBTMP_DEFAULT
|
||||||
|
sysIPV6_PREFER_SRC_COA = C.IPV6_PREFER_SRC_COA
|
||||||
|
sysIPV6_PREFER_SRC_HOME = C.IPV6_PREFER_SRC_HOME
|
||||||
|
sysIPV6_PREFER_SRC_CGA = C.IPV6_PREFER_SRC_CGA
|
||||||
|
sysIPV6_PREFER_SRC_NONCGA = C.IPV6_PREFER_SRC_NONCGA
|
||||||
|
|
||||||
|
sysIPV6_MINHOPCOUNT = C.IPV6_MINHOPCOUNT
|
||||||
|
|
||||||
|
sysIPV6_ORIGDSTADDR = C.IPV6_ORIGDSTADDR
|
||||||
|
sysIPV6_RECVORIGDSTADDR = C.IPV6_RECVORIGDSTADDR
|
||||||
|
sysIPV6_TRANSPARENT = C.IPV6_TRANSPARENT
|
||||||
|
sysIPV6_UNICAST_IF = C.IPV6_UNICAST_IF
|
||||||
|
|
||||||
|
sysICMPV6_FILTER = C.ICMPV6_FILTER
|
||||||
|
|
||||||
|
sysICMPV6_FILTER_BLOCK = C.ICMPV6_FILTER_BLOCK
|
||||||
|
sysICMPV6_FILTER_PASS = C.ICMPV6_FILTER_PASS
|
||||||
|
sysICMPV6_FILTER_BLOCKOTHERS = C.ICMPV6_FILTER_BLOCKOTHERS
|
||||||
|
sysICMPV6_FILTER_PASSONLY = C.ICMPV6_FILTER_PASSONLY
|
||||||
|
|
||||||
|
sysSizeofKernelSockaddrStorage = C.sizeof_struct___kernel_sockaddr_storage
|
||||||
|
sysSizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
||||||
|
sysSizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
|
||||||
|
sysSizeofIPv6Mtuinfo = C.sizeof_struct_ip6_mtuinfo
|
||||||
|
sysSizeofIPv6FlowlabelReq = C.sizeof_struct_in6_flowlabel_req
|
||||||
|
|
||||||
|
sysSizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq
|
||||||
|
sysSizeofGroupReq = C.sizeof_struct_group_req
|
||||||
|
sysSizeofGroupSourceReq = C.sizeof_struct_group_source_req
|
||||||
|
|
||||||
|
sysSizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
|
||||||
|
)
|
||||||
|
|
||||||
|
type sysKernelSockaddrStorage C.struct___kernel_sockaddr_storage
|
||||||
|
|
||||||
|
type sysSockaddrInet6 C.struct_sockaddr_in6
|
||||||
|
|
||||||
|
type sysInet6Pktinfo C.struct_in6_pktinfo
|
||||||
|
|
||||||
|
type sysIPv6Mtuinfo C.struct_ip6_mtuinfo
|
||||||
|
|
||||||
|
type sysIPv6FlowlabelReq C.struct_in6_flowlabel_req
|
||||||
|
|
||||||
|
type sysIPv6Mreq C.struct_ipv6_mreq
|
||||||
|
|
||||||
|
type sysGroupReq C.struct_group_req
|
||||||
|
|
||||||
|
type sysGroupSourceReq C.struct_group_source_req
|
||||||
|
|
||||||
|
type sysICMPv6Filter C.struct_icmp6_filter
|
||||||
+80
@@ -0,0 +1,80 @@
|
|||||||
|
// Copyright 2014 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
|
||||||
|
|
||||||
|
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
||||||
|
|
||||||
|
package ipv6
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/icmp6.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
const (
|
||||||
|
sysIPV6_UNICAST_HOPS = C.IPV6_UNICAST_HOPS
|
||||||
|
sysIPV6_MULTICAST_IF = C.IPV6_MULTICAST_IF
|
||||||
|
sysIPV6_MULTICAST_HOPS = C.IPV6_MULTICAST_HOPS
|
||||||
|
sysIPV6_MULTICAST_LOOP = C.IPV6_MULTICAST_LOOP
|
||||||
|
sysIPV6_JOIN_GROUP = C.IPV6_JOIN_GROUP
|
||||||
|
sysIPV6_LEAVE_GROUP = C.IPV6_LEAVE_GROUP
|
||||||
|
sysIPV6_PORTRANGE = C.IPV6_PORTRANGE
|
||||||
|
sysICMP6_FILTER = C.ICMP6_FILTER
|
||||||
|
|
||||||
|
sysIPV6_CHECKSUM = C.IPV6_CHECKSUM
|
||||||
|
sysIPV6_V6ONLY = C.IPV6_V6ONLY
|
||||||
|
|
||||||
|
sysIPV6_IPSEC_POLICY = C.IPV6_IPSEC_POLICY
|
||||||
|
|
||||||
|
sysIPV6_RTHDRDSTOPTS = C.IPV6_RTHDRDSTOPTS
|
||||||
|
|
||||||
|
sysIPV6_RECVPKTINFO = C.IPV6_RECVPKTINFO
|
||||||
|
sysIPV6_RECVHOPLIMIT = C.IPV6_RECVHOPLIMIT
|
||||||
|
sysIPV6_RECVRTHDR = C.IPV6_RECVRTHDR
|
||||||
|
sysIPV6_RECVHOPOPTS = C.IPV6_RECVHOPOPTS
|
||||||
|
sysIPV6_RECVDSTOPTS = C.IPV6_RECVDSTOPTS
|
||||||
|
|
||||||
|
sysIPV6_USE_MIN_MTU = C.IPV6_USE_MIN_MTU
|
||||||
|
sysIPV6_RECVPATHMTU = C.IPV6_RECVPATHMTU
|
||||||
|
sysIPV6_PATHMTU = C.IPV6_PATHMTU
|
||||||
|
|
||||||
|
sysIPV6_PKTINFO = C.IPV6_PKTINFO
|
||||||
|
sysIPV6_HOPLIMIT = C.IPV6_HOPLIMIT
|
||||||
|
sysIPV6_NEXTHOP = C.IPV6_NEXTHOP
|
||||||
|
sysIPV6_HOPOPTS = C.IPV6_HOPOPTS
|
||||||
|
sysIPV6_DSTOPTS = C.IPV6_DSTOPTS
|
||||||
|
sysIPV6_RTHDR = C.IPV6_RTHDR
|
||||||
|
|
||||||
|
sysIPV6_RECVTCLASS = C.IPV6_RECVTCLASS
|
||||||
|
|
||||||
|
sysIPV6_TCLASS = C.IPV6_TCLASS
|
||||||
|
sysIPV6_DONTFRAG = C.IPV6_DONTFRAG
|
||||||
|
|
||||||
|
sysIPV6_PORTRANGE_DEFAULT = C.IPV6_PORTRANGE_DEFAULT
|
||||||
|
sysIPV6_PORTRANGE_HIGH = C.IPV6_PORTRANGE_HIGH
|
||||||
|
sysIPV6_PORTRANGE_LOW = C.IPV6_PORTRANGE_LOW
|
||||||
|
|
||||||
|
sysSizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
||||||
|
sysSizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
|
||||||
|
sysSizeofIPv6Mtuinfo = C.sizeof_struct_ip6_mtuinfo
|
||||||
|
|
||||||
|
sysSizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq
|
||||||
|
|
||||||
|
sysSizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
|
||||||
|
)
|
||||||
|
|
||||||
|
type sysSockaddrInet6 C.struct_sockaddr_in6
|
||||||
|
|
||||||
|
type sysInet6Pktinfo C.struct_in6_pktinfo
|
||||||
|
|
||||||
|
type sysIPv6Mtuinfo C.struct_ip6_mtuinfo
|
||||||
|
|
||||||
|
type sysIPv6Mreq C.struct_ipv6_mreq
|
||||||
|
|
||||||
|
type sysICMPv6Filter C.struct_icmp6_filter
|
||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
// Copyright 2014 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
|
||||||
|
|
||||||
|
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
||||||
|
|
||||||
|
package ipv6
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/icmp6.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
const (
|
||||||
|
sysIPV6_UNICAST_HOPS = C.IPV6_UNICAST_HOPS
|
||||||
|
sysIPV6_MULTICAST_IF = C.IPV6_MULTICAST_IF
|
||||||
|
sysIPV6_MULTICAST_HOPS = C.IPV6_MULTICAST_HOPS
|
||||||
|
sysIPV6_MULTICAST_LOOP = C.IPV6_MULTICAST_LOOP
|
||||||
|
sysIPV6_JOIN_GROUP = C.IPV6_JOIN_GROUP
|
||||||
|
sysIPV6_LEAVE_GROUP = C.IPV6_LEAVE_GROUP
|
||||||
|
sysIPV6_PORTRANGE = C.IPV6_PORTRANGE
|
||||||
|
sysICMP6_FILTER = C.ICMP6_FILTER
|
||||||
|
|
||||||
|
sysIPV6_CHECKSUM = C.IPV6_CHECKSUM
|
||||||
|
sysIPV6_V6ONLY = C.IPV6_V6ONLY
|
||||||
|
|
||||||
|
sysIPV6_RTHDRDSTOPTS = C.IPV6_RTHDRDSTOPTS
|
||||||
|
|
||||||
|
sysIPV6_RECVPKTINFO = C.IPV6_RECVPKTINFO
|
||||||
|
sysIPV6_RECVHOPLIMIT = C.IPV6_RECVHOPLIMIT
|
||||||
|
sysIPV6_RECVRTHDR = C.IPV6_RECVRTHDR
|
||||||
|
sysIPV6_RECVHOPOPTS = C.IPV6_RECVHOPOPTS
|
||||||
|
sysIPV6_RECVDSTOPTS = C.IPV6_RECVDSTOPTS
|
||||||
|
|
||||||
|
sysIPV6_USE_MIN_MTU = C.IPV6_USE_MIN_MTU
|
||||||
|
sysIPV6_RECVPATHMTU = C.IPV6_RECVPATHMTU
|
||||||
|
|
||||||
|
sysIPV6_PATHMTU = C.IPV6_PATHMTU
|
||||||
|
|
||||||
|
sysIPV6_PKTINFO = C.IPV6_PKTINFO
|
||||||
|
sysIPV6_HOPLIMIT = C.IPV6_HOPLIMIT
|
||||||
|
sysIPV6_NEXTHOP = C.IPV6_NEXTHOP
|
||||||
|
sysIPV6_HOPOPTS = C.IPV6_HOPOPTS
|
||||||
|
sysIPV6_DSTOPTS = C.IPV6_DSTOPTS
|
||||||
|
sysIPV6_RTHDR = C.IPV6_RTHDR
|
||||||
|
|
||||||
|
sysIPV6_AUTH_LEVEL = C.IPV6_AUTH_LEVEL
|
||||||
|
sysIPV6_ESP_TRANS_LEVEL = C.IPV6_ESP_TRANS_LEVEL
|
||||||
|
sysIPV6_ESP_NETWORK_LEVEL = C.IPV6_ESP_NETWORK_LEVEL
|
||||||
|
sysIPSEC6_OUTSA = C.IPSEC6_OUTSA
|
||||||
|
sysIPV6_RECVTCLASS = C.IPV6_RECVTCLASS
|
||||||
|
|
||||||
|
sysIPV6_AUTOFLOWLABEL = C.IPV6_AUTOFLOWLABEL
|
||||||
|
sysIPV6_IPCOMP_LEVEL = C.IPV6_IPCOMP_LEVEL
|
||||||
|
|
||||||
|
sysIPV6_TCLASS = C.IPV6_TCLASS
|
||||||
|
sysIPV6_DONTFRAG = C.IPV6_DONTFRAG
|
||||||
|
sysIPV6_PIPEX = C.IPV6_PIPEX
|
||||||
|
|
||||||
|
sysIPV6_RTABLE = C.IPV6_RTABLE
|
||||||
|
|
||||||
|
sysIPV6_PORTRANGE_DEFAULT = C.IPV6_PORTRANGE_DEFAULT
|
||||||
|
sysIPV6_PORTRANGE_HIGH = C.IPV6_PORTRANGE_HIGH
|
||||||
|
sysIPV6_PORTRANGE_LOW = C.IPV6_PORTRANGE_LOW
|
||||||
|
|
||||||
|
sysSizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
||||||
|
sysSizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
|
||||||
|
sysSizeofIPv6Mtuinfo = C.sizeof_struct_ip6_mtuinfo
|
||||||
|
|
||||||
|
sysSizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq
|
||||||
|
|
||||||
|
sysSizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
|
||||||
|
)
|
||||||
|
|
||||||
|
type sysSockaddrInet6 C.struct_sockaddr_in6
|
||||||
|
|
||||||
|
type sysInet6Pktinfo C.struct_in6_pktinfo
|
||||||
|
|
||||||
|
type sysIPv6Mtuinfo C.struct_ip6_mtuinfo
|
||||||
|
|
||||||
|
type sysIPv6Mreq C.struct_ipv6_mreq
|
||||||
|
|
||||||
|
type sysICMPv6Filter C.struct_icmp6_filter
|
||||||
+96
@@ -0,0 +1,96 @@
|
|||||||
|
// Copyright 2014 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
|
||||||
|
|
||||||
|
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
||||||
|
|
||||||
|
package ipv6
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/icmp6.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
const (
|
||||||
|
sysIPV6_UNICAST_HOPS = C.IPV6_UNICAST_HOPS
|
||||||
|
sysIPV6_MULTICAST_IF = C.IPV6_MULTICAST_IF
|
||||||
|
sysIPV6_MULTICAST_HOPS = C.IPV6_MULTICAST_HOPS
|
||||||
|
sysIPV6_MULTICAST_LOOP = C.IPV6_MULTICAST_LOOP
|
||||||
|
sysIPV6_JOIN_GROUP = C.IPV6_JOIN_GROUP
|
||||||
|
sysIPV6_LEAVE_GROUP = C.IPV6_LEAVE_GROUP
|
||||||
|
|
||||||
|
sysIPV6_PKTINFO = C.IPV6_PKTINFO
|
||||||
|
|
||||||
|
sysIPV6_HOPLIMIT = C.IPV6_HOPLIMIT
|
||||||
|
sysIPV6_NEXTHOP = C.IPV6_NEXTHOP
|
||||||
|
sysIPV6_HOPOPTS = C.IPV6_HOPOPTS
|
||||||
|
sysIPV6_DSTOPTS = C.IPV6_DSTOPTS
|
||||||
|
|
||||||
|
sysIPV6_RTHDR = C.IPV6_RTHDR
|
||||||
|
sysIPV6_RTHDRDSTOPTS = C.IPV6_RTHDRDSTOPTS
|
||||||
|
|
||||||
|
sysIPV6_RECVPKTINFO = C.IPV6_RECVPKTINFO
|
||||||
|
sysIPV6_RECVHOPLIMIT = C.IPV6_RECVHOPLIMIT
|
||||||
|
sysIPV6_RECVHOPOPTS = C.IPV6_RECVHOPOPTS
|
||||||
|
|
||||||
|
sysIPV6_RECVRTHDR = C.IPV6_RECVRTHDR
|
||||||
|
|
||||||
|
sysIPV6_RECVRTHDRDSTOPTS = C.IPV6_RECVRTHDRDSTOPTS
|
||||||
|
|
||||||
|
sysIPV6_CHECKSUM = C.IPV6_CHECKSUM
|
||||||
|
sysIPV6_RECVTCLASS = C.IPV6_RECVTCLASS
|
||||||
|
sysIPV6_USE_MIN_MTU = C.IPV6_USE_MIN_MTU
|
||||||
|
sysIPV6_DONTFRAG = C.IPV6_DONTFRAG
|
||||||
|
sysIPV6_SEC_OPT = C.IPV6_SEC_OPT
|
||||||
|
sysIPV6_SRC_PREFERENCES = C.IPV6_SRC_PREFERENCES
|
||||||
|
sysIPV6_RECVPATHMTU = C.IPV6_RECVPATHMTU
|
||||||
|
sysIPV6_PATHMTU = C.IPV6_PATHMTU
|
||||||
|
sysIPV6_TCLASS = C.IPV6_TCLASS
|
||||||
|
sysIPV6_V6ONLY = C.IPV6_V6ONLY
|
||||||
|
|
||||||
|
sysIPV6_RECVDSTOPTS = C.IPV6_RECVDSTOPTS
|
||||||
|
|
||||||
|
sysIPV6_PREFER_SRC_HOME = C.IPV6_PREFER_SRC_HOME
|
||||||
|
sysIPV6_PREFER_SRC_COA = C.IPV6_PREFER_SRC_COA
|
||||||
|
sysIPV6_PREFER_SRC_PUBLIC = C.IPV6_PREFER_SRC_PUBLIC
|
||||||
|
sysIPV6_PREFER_SRC_TMP = C.IPV6_PREFER_SRC_TMP
|
||||||
|
sysIPV6_PREFER_SRC_NONCGA = C.IPV6_PREFER_SRC_NONCGA
|
||||||
|
sysIPV6_PREFER_SRC_CGA = C.IPV6_PREFER_SRC_CGA
|
||||||
|
|
||||||
|
sysIPV6_PREFER_SRC_MIPMASK = C.IPV6_PREFER_SRC_MIPMASK
|
||||||
|
sysIPV6_PREFER_SRC_MIPDEFAULT = C.IPV6_PREFER_SRC_MIPDEFAULT
|
||||||
|
sysIPV6_PREFER_SRC_TMPMASK = C.IPV6_PREFER_SRC_TMPMASK
|
||||||
|
sysIPV6_PREFER_SRC_TMPDEFAULT = C.IPV6_PREFER_SRC_TMPDEFAULT
|
||||||
|
sysIPV6_PREFER_SRC_CGAMASK = C.IPV6_PREFER_SRC_CGAMASK
|
||||||
|
sysIPV6_PREFER_SRC_CGADEFAULT = C.IPV6_PREFER_SRC_CGADEFAULT
|
||||||
|
|
||||||
|
sysIPV6_PREFER_SRC_MASK = C.IPV6_PREFER_SRC_MASK
|
||||||
|
|
||||||
|
sysIPV6_PREFER_SRC_DEFAULT = C.IPV6_PREFER_SRC_DEFAULT
|
||||||
|
|
||||||
|
sysIPV6_BOUND_IF = C.IPV6_BOUND_IF
|
||||||
|
sysIPV6_UNSPEC_SRC = C.IPV6_UNSPEC_SRC
|
||||||
|
|
||||||
|
sysICMP6_FILTER = C.ICMP6_FILTER
|
||||||
|
|
||||||
|
sysSizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
||||||
|
sysSizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
|
||||||
|
sysSizeofIPv6Mtuinfo = C.sizeof_struct_ip6_mtuinfo
|
||||||
|
|
||||||
|
sysSizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq
|
||||||
|
|
||||||
|
sysSizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
|
||||||
|
)
|
||||||
|
|
||||||
|
type sysSockaddrInet6 C.struct_sockaddr_in6
|
||||||
|
|
||||||
|
type sysInet6Pktinfo C.struct_in6_pktinfo
|
||||||
|
|
||||||
|
type sysIPv6Mtuinfo C.struct_ip6_mtuinfo
|
||||||
|
|
||||||
|
type sysIPv6Mreq C.struct_ipv6_mreq
|
||||||
|
|
||||||
|
type sysICMPv6Filter C.struct_icmp6_filter
|
||||||
+288
@@ -0,0 +1,288 @@
|
|||||||
|
// 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 darwin dragonfly freebsd linux netbsd openbsd windows
|
||||||
|
|
||||||
|
package ipv6
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MulticastHopLimit returns the hop limit field value for outgoing
|
||||||
|
// multicast packets.
|
||||||
|
func (c *dgramOpt) MulticastHopLimit() (int, error) {
|
||||||
|
if !c.ok() {
|
||||||
|
return 0, syscall.EINVAL
|
||||||
|
}
|
||||||
|
fd, err := c.sysfd()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return getInt(fd, &sockOpts[ssoMulticastHopLimit])
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMulticastHopLimit sets the hop limit field value for future
|
||||||
|
// outgoing multicast packets.
|
||||||
|
func (c *dgramOpt) SetMulticastHopLimit(hoplim int) error {
|
||||||
|
if !c.ok() {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
fd, err := c.sysfd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return setInt(fd, &sockOpts[ssoMulticastHopLimit], hoplim)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MulticastInterface returns the default interface for multicast
|
||||||
|
// packet transmissions.
|
||||||
|
func (c *dgramOpt) MulticastInterface() (*net.Interface, error) {
|
||||||
|
if !c.ok() {
|
||||||
|
return nil, syscall.EINVAL
|
||||||
|
}
|
||||||
|
fd, err := c.sysfd()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return getInterface(fd, &sockOpts[ssoMulticastInterface])
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMulticastInterface sets the default interface for future
|
||||||
|
// multicast packet transmissions.
|
||||||
|
func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error {
|
||||||
|
if !c.ok() {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
fd, err := c.sysfd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return setInterface(fd, &sockOpts[ssoMulticastInterface], ifi)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MulticastLoopback reports whether transmitted multicast packets
|
||||||
|
// should be copied and send back to the originator.
|
||||||
|
func (c *dgramOpt) MulticastLoopback() (bool, error) {
|
||||||
|
if !c.ok() {
|
||||||
|
return false, syscall.EINVAL
|
||||||
|
}
|
||||||
|
fd, err := c.sysfd()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
on, err := getInt(fd, &sockOpts[ssoMulticastLoopback])
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return on == 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMulticastLoopback sets whether transmitted multicast packets
|
||||||
|
// should be copied and send back to the originator.
|
||||||
|
func (c *dgramOpt) SetMulticastLoopback(on bool) error {
|
||||||
|
if !c.ok() {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
fd, err := c.sysfd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return setInt(fd, &sockOpts[ssoMulticastLoopback], boolint(on))
|
||||||
|
}
|
||||||
|
|
||||||
|
// JoinGroup joins the group address group on the interface ifi.
|
||||||
|
// By default all sources that can cast data to group are accepted.
|
||||||
|
// It's possible to mute and unmute data transmission from a specific
|
||||||
|
// source by using ExcludeSourceSpecificGroup and
|
||||||
|
// IncludeSourceSpecificGroup.
|
||||||
|
// JoinGroup uses the system assigned multicast interface when ifi is
|
||||||
|
// nil, although this is not recommended because the assignment
|
||||||
|
// depends on platforms and sometimes it might require routing
|
||||||
|
// configuration.
|
||||||
|
func (c *dgramOpt) JoinGroup(ifi *net.Interface, group net.Addr) error {
|
||||||
|
if !c.ok() {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
fd, err := c.sysfd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
grp := netAddrToIP16(group)
|
||||||
|
if grp == nil {
|
||||||
|
return errMissingAddress
|
||||||
|
}
|
||||||
|
return setGroup(fd, &sockOpts[ssoJoinGroup], ifi, grp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeaveGroup leaves the group address group on the interface ifi
|
||||||
|
// regardless of whether the group is any-source group or
|
||||||
|
// source-specific group.
|
||||||
|
func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error {
|
||||||
|
if !c.ok() {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
fd, err := c.sysfd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
grp := netAddrToIP16(group)
|
||||||
|
if grp == nil {
|
||||||
|
return errMissingAddress
|
||||||
|
}
|
||||||
|
return setGroup(fd, &sockOpts[ssoLeaveGroup], ifi, grp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// JoinSourceSpecificGroup joins the source-specific group comprising
|
||||||
|
// group and source on the interface ifi.
|
||||||
|
// JoinSourceSpecificGroup uses the system assigned multicast
|
||||||
|
// interface when ifi is nil, although this is not recommended because
|
||||||
|
// the assignment depends on platforms and sometimes it might require
|
||||||
|
// routing configuration.
|
||||||
|
func (c *dgramOpt) JoinSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
|
||||||
|
if !c.ok() {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
fd, err := c.sysfd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
grp := netAddrToIP16(group)
|
||||||
|
if grp == nil {
|
||||||
|
return errMissingAddress
|
||||||
|
}
|
||||||
|
src := netAddrToIP16(source)
|
||||||
|
if src == nil {
|
||||||
|
return errMissingAddress
|
||||||
|
}
|
||||||
|
return setSourceGroup(fd, &sockOpts[ssoJoinSourceGroup], ifi, grp, src)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeaveSourceSpecificGroup leaves the source-specific group on the
|
||||||
|
// interface ifi.
|
||||||
|
func (c *dgramOpt) LeaveSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
|
||||||
|
if !c.ok() {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
fd, err := c.sysfd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
grp := netAddrToIP16(group)
|
||||||
|
if grp == nil {
|
||||||
|
return errMissingAddress
|
||||||
|
}
|
||||||
|
src := netAddrToIP16(source)
|
||||||
|
if src == nil {
|
||||||
|
return errMissingAddress
|
||||||
|
}
|
||||||
|
return setSourceGroup(fd, &sockOpts[ssoLeaveSourceGroup], ifi, grp, src)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExcludeSourceSpecificGroup excludes the source-specific group from
|
||||||
|
// the already joined any-source groups by JoinGroup on the interface
|
||||||
|
// ifi.
|
||||||
|
func (c *dgramOpt) ExcludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
|
||||||
|
if !c.ok() {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
fd, err := c.sysfd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
grp := netAddrToIP16(group)
|
||||||
|
if grp == nil {
|
||||||
|
return errMissingAddress
|
||||||
|
}
|
||||||
|
src := netAddrToIP16(source)
|
||||||
|
if src == nil {
|
||||||
|
return errMissingAddress
|
||||||
|
}
|
||||||
|
return setSourceGroup(fd, &sockOpts[ssoBlockSourceGroup], ifi, grp, src)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncludeSourceSpecificGroup includes the excluded source-specific
|
||||||
|
// group by ExcludeSourceSpecificGroup again on the interface ifi.
|
||||||
|
func (c *dgramOpt) IncludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
|
||||||
|
if !c.ok() {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
fd, err := c.sysfd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
grp := netAddrToIP16(group)
|
||||||
|
if grp == nil {
|
||||||
|
return errMissingAddress
|
||||||
|
}
|
||||||
|
src := netAddrToIP16(source)
|
||||||
|
if src == nil {
|
||||||
|
return errMissingAddress
|
||||||
|
}
|
||||||
|
return setSourceGroup(fd, &sockOpts[ssoUnblockSourceGroup], ifi, grp, src)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checksum reports whether the kernel will compute, store or verify a
|
||||||
|
// checksum for both incoming and outgoing packets. If on is true, it
|
||||||
|
// returns an offset in bytes into the data of where the checksum
|
||||||
|
// field is located.
|
||||||
|
func (c *dgramOpt) Checksum() (on bool, offset int, err error) {
|
||||||
|
if !c.ok() {
|
||||||
|
return false, 0, syscall.EINVAL
|
||||||
|
}
|
||||||
|
fd, err := c.sysfd()
|
||||||
|
if err != nil {
|
||||||
|
return false, 0, err
|
||||||
|
}
|
||||||
|
offset, err = getInt(fd, &sockOpts[ssoChecksum])
|
||||||
|
if err != nil {
|
||||||
|
return false, 0, err
|
||||||
|
}
|
||||||
|
if offset < 0 {
|
||||||
|
return false, 0, nil
|
||||||
|
}
|
||||||
|
return true, offset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetChecksum enables the kernel checksum processing. If on is ture,
|
||||||
|
// the offset should be an offset in bytes into the data of where the
|
||||||
|
// checksum field is located.
|
||||||
|
func (c *dgramOpt) SetChecksum(on bool, offset int) error {
|
||||||
|
if !c.ok() {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
fd, err := c.sysfd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !on {
|
||||||
|
offset = -1
|
||||||
|
}
|
||||||
|
return setInt(fd, &sockOpts[ssoChecksum], offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ICMPFilter returns an ICMP filter.
|
||||||
|
func (c *dgramOpt) ICMPFilter() (*ICMPFilter, error) {
|
||||||
|
if !c.ok() {
|
||||||
|
return nil, syscall.EINVAL
|
||||||
|
}
|
||||||
|
fd, err := c.sysfd()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return getICMPFilter(fd, &sockOpts[ssoICMPFilter])
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetICMPFilter deploys the ICMP filter.
|
||||||
|
func (c *dgramOpt) SetICMPFilter(f *ICMPFilter) error {
|
||||||
|
if !c.ok() {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
fd, err := c.sysfd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return setICMPFilter(fd, &sockOpts[ssoICMPFilter], f)
|
||||||
|
}
|
||||||
+119
@@ -0,0 +1,119 @@
|
|||||||
|
// 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 nacl plan9 solaris
|
||||||
|
|
||||||
|
package ipv6
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
// MulticastHopLimit returns the hop limit field value for outgoing
|
||||||
|
// multicast packets.
|
||||||
|
func (c *dgramOpt) MulticastHopLimit() (int, error) {
|
||||||
|
return 0, errOpNoSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMulticastHopLimit sets the hop limit field value for future
|
||||||
|
// outgoing multicast packets.
|
||||||
|
func (c *dgramOpt) SetMulticastHopLimit(hoplim int) error {
|
||||||
|
return errOpNoSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
// MulticastInterface returns the default interface for multicast
|
||||||
|
// packet transmissions.
|
||||||
|
func (c *dgramOpt) MulticastInterface() (*net.Interface, error) {
|
||||||
|
return nil, errOpNoSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMulticastInterface sets the default interface for future
|
||||||
|
// multicast packet transmissions.
|
||||||
|
func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error {
|
||||||
|
return errOpNoSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
// MulticastLoopback reports whether transmitted multicast packets
|
||||||
|
// should be copied and send back to the originator.
|
||||||
|
func (c *dgramOpt) MulticastLoopback() (bool, error) {
|
||||||
|
return false, errOpNoSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMulticastLoopback sets whether transmitted multicast packets
|
||||||
|
// should be copied and send back to the originator.
|
||||||
|
func (c *dgramOpt) SetMulticastLoopback(on bool) error {
|
||||||
|
return errOpNoSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
// JoinGroup joins the group address group on the interface ifi.
|
||||||
|
// By default all sources that can cast data to group are accepted.
|
||||||
|
// It's possible to mute and unmute data transmission from a specific
|
||||||
|
// source by using ExcludeSourceSpecificGroup and
|
||||||
|
// IncludeSourceSpecificGroup.
|
||||||
|
// JoinGroup uses the system assigned multicast interface when ifi is
|
||||||
|
// nil, although this is not recommended because the assignment
|
||||||
|
// depends on platforms and sometimes it might require routing
|
||||||
|
// configuration.
|
||||||
|
func (c *dgramOpt) JoinGroup(ifi *net.Interface, group net.Addr) error {
|
||||||
|
return errOpNoSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeaveGroup leaves the group address group on the interface ifi
|
||||||
|
// regardless of whether the group is any-source group or
|
||||||
|
// source-specific group.
|
||||||
|
func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error {
|
||||||
|
return errOpNoSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
// JoinSourceSpecificGroup joins the source-specific group comprising
|
||||||
|
// group and source on the interface ifi.
|
||||||
|
// JoinSourceSpecificGroup uses the system assigned multicast
|
||||||
|
// interface when ifi is nil, although this is not recommended because
|
||||||
|
// the assignment depends on platforms and sometimes it might require
|
||||||
|
// routing configuration.
|
||||||
|
func (c *dgramOpt) JoinSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
|
||||||
|
return errOpNoSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeaveSourceSpecificGroup leaves the source-specific group on the
|
||||||
|
// interface ifi.
|
||||||
|
func (c *dgramOpt) LeaveSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
|
||||||
|
return errOpNoSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExcludeSourceSpecificGroup excludes the source-specific group from
|
||||||
|
// the already joined any-source groups by JoinGroup on the interface
|
||||||
|
// ifi.
|
||||||
|
func (c *dgramOpt) ExcludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
|
||||||
|
return errOpNoSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncludeSourceSpecificGroup includes the excluded source-specific
|
||||||
|
// group by ExcludeSourceSpecificGroup again on the interface ifi.
|
||||||
|
func (c *dgramOpt) IncludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
|
||||||
|
return errOpNoSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checksum reports whether the kernel will compute, store or verify a
|
||||||
|
// checksum for both incoming and outgoing packets. If on is true, it
|
||||||
|
// returns an offset in bytes into the data of where the checksum
|
||||||
|
// field is located.
|
||||||
|
func (c *dgramOpt) Checksum() (on bool, offset int, err error) {
|
||||||
|
return false, 0, errOpNoSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetChecksum enables the kernel checksum processing. If on is ture,
|
||||||
|
// the offset should be an offset in bytes into the data of where the
|
||||||
|
// checksum field is located.
|
||||||
|
func (c *dgramOpt) SetChecksum(on bool, offset int) error {
|
||||||
|
return errOpNoSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
// ICMPFilter returns an ICMP filter.
|
||||||
|
func (c *dgramOpt) ICMPFilter() (*ICMPFilter, error) {
|
||||||
|
return nil, errOpNoSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetICMPFilter deploys the ICMP filter.
|
||||||
|
func (c *dgramOpt) SetICMPFilter(f *ICMPFilter) error {
|
||||||
|
return errOpNoSupport
|
||||||
|
}
|
||||||
+240
@@ -0,0 +1,240 @@
|
|||||||
|
// 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 ipv6 implements IP-level socket options for the Internet
|
||||||
|
// Protocol version 6.
|
||||||
|
//
|
||||||
|
// The package provides IP-level socket options that allow
|
||||||
|
// manipulation of IPv6 facilities.
|
||||||
|
//
|
||||||
|
// The IPv6 protocol is defined in RFC 2460.
|
||||||
|
// Basic and advanced socket interface extensions are defined in RFC
|
||||||
|
// 3493 and RFC 3542.
|
||||||
|
// Socket interface extensions for multicast source filters are
|
||||||
|
// defined in RFC 3678.
|
||||||
|
// MLDv1 and MLDv2 are defined in RFC 2710 and RFC 3810.
|
||||||
|
// Source-specific multicast is defined in RFC 4607.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Unicasting
|
||||||
|
//
|
||||||
|
// The options for unicasting are available for net.TCPConn,
|
||||||
|
// net.UDPConn and net.IPConn which are created as network connections
|
||||||
|
// that use the IPv6 transport. When a single TCP connection carrying
|
||||||
|
// a data flow of multiple packets needs to indicate the flow is
|
||||||
|
// important, ipv6.Conn is used to set the traffic class field on the
|
||||||
|
// IPv6 header for each packet.
|
||||||
|
//
|
||||||
|
// ln, err := net.Listen("tcp6", "[::]:1024")
|
||||||
|
// if err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
// defer ln.Close()
|
||||||
|
// for {
|
||||||
|
// c, err := ln.Accept()
|
||||||
|
// if err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
// go func(c net.Conn) {
|
||||||
|
// defer c.Close()
|
||||||
|
//
|
||||||
|
// The outgoing packets will be labeled DiffServ assured forwarding
|
||||||
|
// class 1 low drop precedence, known as AF11 packets.
|
||||||
|
//
|
||||||
|
// if err := ipv6.NewConn(c).SetTrafficClass(DiffServAF11); err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
// if _, err := c.Write(data); err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
// }(c)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Multicasting
|
||||||
|
//
|
||||||
|
// The options for multicasting are available for net.UDPConn and
|
||||||
|
// net.IPconn which are created as network connections that use the
|
||||||
|
// IPv6 transport. A few network facilities must be prepared before
|
||||||
|
// you begin multicasting, at a minimum joining network interfaces and
|
||||||
|
// multicast groups.
|
||||||
|
//
|
||||||
|
// en0, err := net.InterfaceByName("en0")
|
||||||
|
// if err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
// en1, err := net.InterfaceByIndex(911)
|
||||||
|
// if err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
// group := net.ParseIP("ff02::114")
|
||||||
|
//
|
||||||
|
// First, an application listens to an appropriate address with an
|
||||||
|
// appropriate service port.
|
||||||
|
//
|
||||||
|
// c, err := net.ListenPacket("udp6", "[::]:1024")
|
||||||
|
// if err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
// defer c.Close()
|
||||||
|
//
|
||||||
|
// Second, the application joins multicast groups, starts listening to
|
||||||
|
// the groups on the specified network interfaces. Note that the
|
||||||
|
// service port for transport layer protocol does not matter with this
|
||||||
|
// operation as joining groups affects only network and link layer
|
||||||
|
// protocols, such as IPv6 and Ethernet.
|
||||||
|
//
|
||||||
|
// p := ipv6.NewPacketConn(c)
|
||||||
|
// if err := p.JoinGroup(en0, &net.UDPAddr{IP: group}); err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
// if err := p.JoinGroup(en1, &net.UDPAddr{IP: group}); err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// The application might set per packet control message transmissions
|
||||||
|
// between the protocol stack within the kernel. When the application
|
||||||
|
// needs a destination address on an incoming packet,
|
||||||
|
// SetControlMessage of ipv6.PacketConn is used to enable control
|
||||||
|
// message transmissons.
|
||||||
|
//
|
||||||
|
// if err := p.SetControlMessage(ipv6.FlagDst, true); err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// The application could identify whether the received packets are
|
||||||
|
// of interest by using the control message that contains the
|
||||||
|
// destination address of the received packet.
|
||||||
|
//
|
||||||
|
// b := make([]byte, 1500)
|
||||||
|
// for {
|
||||||
|
// n, rcm, src, err := p.ReadFrom(b)
|
||||||
|
// if err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
// if rcm.Dst.IsMulticast() {
|
||||||
|
// if rcm.Dst.Equal(group) {
|
||||||
|
// // joined group, do something
|
||||||
|
// } else {
|
||||||
|
// // unknown group, discard
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// The application can also send both unicast and multicast packets.
|
||||||
|
//
|
||||||
|
// p.SetTrafficClass(DiffServCS0)
|
||||||
|
// p.SetHopLimit(16)
|
||||||
|
// if _, err := p.WriteTo(data[:n], nil, src); err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
// dst := &net.UDPAddr{IP: group, Port: 1024}
|
||||||
|
// wcm := ipv6.ControlMessage{TrafficClass: DiffServCS7, HopLimit: 1}
|
||||||
|
// for _, ifi := range []*net.Interface{en0, en1} {
|
||||||
|
// wcm.IfIndex = ifi.Index
|
||||||
|
// if _, err := p.WriteTo(data[:n], &wcm, dst); err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// More multicasting
|
||||||
|
//
|
||||||
|
// An application that uses PacketConn may join multiple multicast
|
||||||
|
// groups. For example, a UDP listener with port 1024 might join two
|
||||||
|
// different groups across over two different network interfaces by
|
||||||
|
// using:
|
||||||
|
//
|
||||||
|
// c, err := net.ListenPacket("udp6", "[::]:1024")
|
||||||
|
// if err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
// defer c.Close()
|
||||||
|
// p := ipv6.NewPacketConn(c)
|
||||||
|
// if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::1:114")}); err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
// if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::2:114")}); err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
// if err := p.JoinGroup(en1, &net.UDPAddr{IP: net.ParseIP("ff02::2:114")}); err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// It is possible for multiple UDP listeners that listen on the same
|
||||||
|
// UDP port to join the same multicast group. The net package will
|
||||||
|
// provide a socket that listens to a wildcard address with reusable
|
||||||
|
// UDP port when an appropriate multicast address prefix is passed to
|
||||||
|
// the net.ListenPacket or net.ListenUDP.
|
||||||
|
//
|
||||||
|
// c1, err := net.ListenPacket("udp6", "[ff02::]:1024")
|
||||||
|
// if err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
// defer c1.Close()
|
||||||
|
// c2, err := net.ListenPacket("udp6", "[ff02::]:1024")
|
||||||
|
// if err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
// defer c2.Close()
|
||||||
|
// p1 := ipv6.NewPacketConn(c1)
|
||||||
|
// if err := p1.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
// p2 := ipv6.NewPacketConn(c2)
|
||||||
|
// if err := p2.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Also it is possible for the application to leave or rejoin a
|
||||||
|
// multicast group on the network interface.
|
||||||
|
//
|
||||||
|
// if err := p.LeaveGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
// if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff01::114")}); err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Source-specific multicasting
|
||||||
|
//
|
||||||
|
// An application that uses PacketConn on MLDv2 supported platform is
|
||||||
|
// able to join source-specific multicast groups.
|
||||||
|
// The application may use JoinSourceSpecificGroup and
|
||||||
|
// LeaveSourceSpecificGroup for the operation known as "include" mode,
|
||||||
|
//
|
||||||
|
// ssmgroup := net.UDPAddr{IP: net.ParseIP("ff32::8000:9")}
|
||||||
|
// ssmsource := net.UDPAddr{IP: net.ParseIP("fe80::cafe")}
|
||||||
|
// if err := p.JoinSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
// if err := p.LeaveSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// or JoinGroup, ExcludeSourceSpecificGroup,
|
||||||
|
// IncludeSourceSpecificGroup and LeaveGroup for the operation known
|
||||||
|
// as "exclude" mode.
|
||||||
|
//
|
||||||
|
// exclsource := net.UDPAddr{IP: net.ParseIP("fe80::dead")}
|
||||||
|
// if err := p.JoinGroup(en0, &ssmgroup); err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
// if err := p.ExcludeSourceSpecificGroup(en0, &ssmgroup, &exclsource); err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
// if err := p.LeaveGroup(en0, &ssmgroup); err != nil {
|
||||||
|
// // error handling
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Note that it depends on each platform implementation what happens
|
||||||
|
// when an application which runs on MLDv2 unsupported platform uses
|
||||||
|
// JoinSourceSpecificGroup and LeaveSourceSpecificGroup.
|
||||||
|
// In general the platform tries to fall back to conversations using
|
||||||
|
// MLDv1 and starts to listen to multicast traffic.
|
||||||
|
// In the fallback case, ExcludeSourceSpecificGroup and
|
||||||
|
// IncludeSourceSpecificGroup may return an error.
|
||||||
|
package ipv6
|
||||||
+123
@@ -0,0 +1,123 @@
|
|||||||
|
// 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 ipv6
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Conn represents a network endpoint that uses IPv6 transport.
|
||||||
|
// It allows to set basic IP-level socket options such as traffic
|
||||||
|
// class and hop limit.
|
||||||
|
type Conn struct {
|
||||||
|
genericOpt
|
||||||
|
}
|
||||||
|
|
||||||
|
type genericOpt struct {
|
||||||
|
net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *genericOpt) ok() bool { return c != nil && c.Conn != nil }
|
||||||
|
|
||||||
|
// PathMTU returns a path MTU value for the destination associated
|
||||||
|
// with the endpoint.
|
||||||
|
func (c *Conn) PathMTU() (int, error) {
|
||||||
|
if !c.genericOpt.ok() {
|
||||||
|
return 0, syscall.EINVAL
|
||||||
|
}
|
||||||
|
fd, err := c.genericOpt.sysfd()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
_, mtu, err := getMTUInfo(fd, &sockOpts[ssoPathMTU])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return mtu, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConn returns a new Conn.
|
||||||
|
func NewConn(c net.Conn) *Conn {
|
||||||
|
return &Conn{
|
||||||
|
genericOpt: genericOpt{Conn: c},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A PacketConn represents a packet network endpoint that uses IPv6
|
||||||
|
// transport. It is used to control several IP-level socket options
|
||||||
|
// including IPv6 header manipulation. It also provides datagram
|
||||||
|
// based network I/O methods specific to the IPv6 and higher layer
|
||||||
|
// protocols such as OSPF, GRE, and UDP.
|
||||||
|
type PacketConn struct {
|
||||||
|
genericOpt
|
||||||
|
dgramOpt
|
||||||
|
payloadHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
type dgramOpt struct {
|
||||||
|
net.PacketConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dgramOpt) ok() bool { return c != nil && c.PacketConn != nil }
|
||||||
|
|
||||||
|
// SetControlMessage allows to receive the per packet basis IP-level
|
||||||
|
// socket options.
|
||||||
|
func (c *PacketConn) SetControlMessage(cf ControlFlags, on bool) error {
|
||||||
|
if !c.payloadHandler.ok() {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
fd, err := c.payloadHandler.sysfd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return setControlMessage(fd, &c.payloadHandler.rawOpt, cf, on)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDeadline sets the read and write deadlines associated with the
|
||||||
|
// endpoint.
|
||||||
|
func (c *PacketConn) SetDeadline(t time.Time) error {
|
||||||
|
if !c.payloadHandler.ok() {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
return c.payloadHandler.SetDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReadDeadline sets the read deadline associated with the
|
||||||
|
// endpoint.
|
||||||
|
func (c *PacketConn) SetReadDeadline(t time.Time) error {
|
||||||
|
if !c.payloadHandler.ok() {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
return c.payloadHandler.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWriteDeadline sets the write deadline associated with the
|
||||||
|
// endpoint.
|
||||||
|
func (c *PacketConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
if !c.payloadHandler.ok() {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
return c.payloadHandler.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the endpoint.
|
||||||
|
func (c *PacketConn) Close() error {
|
||||||
|
if !c.payloadHandler.ok() {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
return c.payloadHandler.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPacketConn returns a new PacketConn using c as its underlying
|
||||||
|
// transport.
|
||||||
|
func NewPacketConn(c net.PacketConn) *PacketConn {
|
||||||
|
return &PacketConn{
|
||||||
|
genericOpt: genericOpt{Conn: c.(net.Conn)},
|
||||||
|
dgramOpt: dgramOpt{PacketConn: c},
|
||||||
|
payloadHandler: payloadHandler{PacketConn: c},
|
||||||
|
}
|
||||||
|
}
|
||||||
+214
@@ -0,0 +1,214 @@
|
|||||||
|
// Copyright 2014 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 ipv6_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/icmp"
|
||||||
|
"golang.org/x/net/ipv6"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleConn_markingTCP() {
|
||||||
|
ln, err := net.Listen("tcp6", "[::]:1024")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer ln.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
c, err := ln.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
go func(c net.Conn) {
|
||||||
|
defer c.Close()
|
||||||
|
p := ipv6.NewConn(c)
|
||||||
|
if err := p.SetTrafficClass(0x28); err != nil { // DSCP AF11
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := p.SetHopLimit(128); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err := c.Write([]byte("HELLO-R-U-THERE-ACK")); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExamplePacketConn_servingOneShotMulticastDNS() {
|
||||||
|
c, err := net.ListenPacket("udp6", "[::]:5353") // mDNS over UDP
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
p := ipv6.NewPacketConn(c)
|
||||||
|
|
||||||
|
en0, err := net.InterfaceByName("en0")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
mDNSLinkLocal := net.UDPAddr{IP: net.ParseIP("ff02::fb")}
|
||||||
|
if err := p.JoinGroup(en0, &mDNSLinkLocal); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer p.LeaveGroup(en0, &mDNSLinkLocal)
|
||||||
|
if err := p.SetControlMessage(ipv6.FlagDst|ipv6.FlagInterface, true); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var wcm ipv6.ControlMessage
|
||||||
|
b := make([]byte, 1500)
|
||||||
|
for {
|
||||||
|
_, rcm, peer, err := p.ReadFrom(b)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if !rcm.Dst.IsMulticast() || !rcm.Dst.Equal(mDNSLinkLocal.IP) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
wcm.IfIndex = rcm.IfIndex
|
||||||
|
answers := []byte("FAKE-MDNS-ANSWERS") // fake mDNS answers, you need to implement this
|
||||||
|
if _, err := p.WriteTo(answers, &wcm, peer); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExamplePacketConn_tracingIPPacketRoute() {
|
||||||
|
// Tracing an IP packet route to www.google.com.
|
||||||
|
|
||||||
|
const host = "www.google.com"
|
||||||
|
ips, err := net.LookupIP(host)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
var dst net.IPAddr
|
||||||
|
for _, ip := range ips {
|
||||||
|
if ip.To16() != nil && ip.To4() == nil {
|
||||||
|
dst.IP = ip
|
||||||
|
fmt.Printf("using %v for tracing an IP packet route to %s\n", dst.IP, host)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dst.IP == nil {
|
||||||
|
log.Fatal("no AAAA record found")
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := net.ListenPacket("ip6:58", "::") // ICMP for IPv6
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
p := ipv6.NewPacketConn(c)
|
||||||
|
|
||||||
|
if err := p.SetControlMessage(ipv6.FlagHopLimit|ipv6.FlagSrc|ipv6.FlagDst|ipv6.FlagInterface, true); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
wm := icmp.Message{
|
||||||
|
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
|
||||||
|
Body: &icmp.Echo{
|
||||||
|
ID: os.Getpid() & 0xffff,
|
||||||
|
Data: []byte("HELLO-R-U-THERE"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var f ipv6.ICMPFilter
|
||||||
|
f.SetAll(true)
|
||||||
|
f.Accept(ipv6.ICMPTypeTimeExceeded)
|
||||||
|
f.Accept(ipv6.ICMPTypeEchoReply)
|
||||||
|
if err := p.SetICMPFilter(&f); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var wcm ipv6.ControlMessage
|
||||||
|
rb := make([]byte, 1500)
|
||||||
|
for i := 1; i <= 64; i++ { // up to 64 hops
|
||||||
|
wm.Body.(*icmp.Echo).Seq = i
|
||||||
|
wb, err := wm.Marshal(nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the real world usually there are several
|
||||||
|
// multiple traffic-engineered paths for each hop.
|
||||||
|
// You may need to probe a few times to each hop.
|
||||||
|
begin := time.Now()
|
||||||
|
wcm.HopLimit = i
|
||||||
|
if _, err := p.WriteTo(wb, &wcm, &dst); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := p.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
n, rcm, peer, err := p.ReadFrom(rb)
|
||||||
|
if err != nil {
|
||||||
|
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||||
|
fmt.Printf("%v\t*\n", i)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
rm, err := icmp.ParseMessage(58, rb[:n])
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
rtt := time.Since(begin)
|
||||||
|
|
||||||
|
// In the real world you need to determine whether the
|
||||||
|
// received message is yours using ControlMessage.Src,
|
||||||
|
// ControlMesage.Dst, icmp.Echo.ID and icmp.Echo.Seq.
|
||||||
|
switch rm.Type {
|
||||||
|
case ipv6.ICMPTypeTimeExceeded:
|
||||||
|
names, _ := net.LookupAddr(peer.String())
|
||||||
|
fmt.Printf("%d\t%v %+v %v\n\t%+v\n", i, peer, names, rtt, rcm)
|
||||||
|
case ipv6.ICMPTypeEchoReply:
|
||||||
|
names, _ := net.LookupAddr(peer.String())
|
||||||
|
fmt.Printf("%d\t%v %+v %v\n\t%+v\n", i, peer, names, rtt, rcm)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExamplePacketConn_advertisingOSPFHello() {
|
||||||
|
c, err := net.ListenPacket("ip6:89", "::") // OSPF for IPv6
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
p := ipv6.NewPacketConn(c)
|
||||||
|
|
||||||
|
en0, err := net.InterfaceByName("en0")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
allSPFRouters := net.IPAddr{IP: net.ParseIP("ff02::5")}
|
||||||
|
if err := p.JoinGroup(en0, &allSPFRouters); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer p.LeaveGroup(en0, &allSPFRouters)
|
||||||
|
|
||||||
|
hello := make([]byte, 24) // fake hello data, you need to implement this
|
||||||
|
ospf := make([]byte, 16) // fake ospf header, you need to implement this
|
||||||
|
ospf[0] = 3 // version 3
|
||||||
|
ospf[1] = 1 // hello packet
|
||||||
|
ospf = append(ospf, hello...)
|
||||||
|
if err := p.SetChecksum(true, 12); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cm := ipv6.ControlMessage{
|
||||||
|
TrafficClass: 0xc0, // DSCP CS6
|
||||||
|
HopLimit: 1,
|
||||||
|
IfIndex: en0.Index,
|
||||||
|
}
|
||||||
|
if _, err := p.WriteTo(ospf, &cm, &allSPFRouters); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
+208
@@ -0,0 +1,208 @@
|
|||||||
|
// 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 ignore
|
||||||
|
|
||||||
|
//go:generate go run gen.go
|
||||||
|
|
||||||
|
// This program generates system adaptation constants and types,
|
||||||
|
// internet protocol constants and tables by reading template files
|
||||||
|
// and IANA protocol registries.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"go/format"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if err := genzsys(); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if err := geniana(); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func genzsys() error {
|
||||||
|
defs := "defs_" + runtime.GOOS + ".go"
|
||||||
|
f, err := os.Open(defs)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
cmd := exec.Command("go", "tool", "cgo", "-godefs", defs)
|
||||||
|
b, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// The ipv6 pacakge still supports go1.2, and so we need to
|
||||||
|
// take care of additional platforms in go1.3 and above for
|
||||||
|
// working with go1.2.
|
||||||
|
switch {
|
||||||
|
case runtime.GOOS == "dragonfly" || runtime.GOOS == "solaris":
|
||||||
|
b = bytes.Replace(b, []byte("package ipv6\n"), []byte("// +build "+runtime.GOOS+"\n\npackage ipv6\n"), 1)
|
||||||
|
case runtime.GOOS == "linux" && (runtime.GOARCH == "arm64" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le"):
|
||||||
|
b = bytes.Replace(b, []byte("package ipv6\n"), []byte("// +build "+runtime.GOOS+","+runtime.GOARCH+"\n\npackage ipv6\n"), 1)
|
||||||
|
}
|
||||||
|
b, err = format.Source(b)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
zsys := "zsys_" + runtime.GOOS + ".go"
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "freebsd", "linux":
|
||||||
|
zsys = "zsys_" + runtime.GOOS + "_" + runtime.GOARCH + ".go"
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(zsys, b, 0644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var registries = []struct {
|
||||||
|
url string
|
||||||
|
parse func(io.Writer, io.Reader) error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"http://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xml",
|
||||||
|
parseICMPv6Parameters,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func geniana() error {
|
||||||
|
var bb bytes.Buffer
|
||||||
|
fmt.Fprintf(&bb, "// go generate gen.go\n")
|
||||||
|
fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n")
|
||||||
|
fmt.Fprintf(&bb, "package ipv6\n\n")
|
||||||
|
for _, r := range registries {
|
||||||
|
resp, err := http.Get(r.url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return fmt.Errorf("got HTTP status code %v for %v\n", resp.StatusCode, r.url)
|
||||||
|
}
|
||||||
|
if err := r.parse(&bb, resp.Body); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&bb, "\n")
|
||||||
|
}
|
||||||
|
b, err := format.Source(bb.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile("iana.go", b, 0644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseICMPv6Parameters(w io.Writer, r io.Reader) error {
|
||||||
|
dec := xml.NewDecoder(r)
|
||||||
|
var icp icmpv6Parameters
|
||||||
|
if err := dec.Decode(&icp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
prs := icp.escape()
|
||||||
|
fmt.Fprintf(w, "// %s, Updated: %s\n", icp.Title, icp.Updated)
|
||||||
|
fmt.Fprintf(w, "const (\n")
|
||||||
|
for _, pr := range prs {
|
||||||
|
if pr.Name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "ICMPType%s ICMPType = %d", pr.Name, pr.Value)
|
||||||
|
fmt.Fprintf(w, "// %s\n", pr.OrigName)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, ")\n\n")
|
||||||
|
fmt.Fprintf(w, "// %s, Updated: %s\n", icp.Title, icp.Updated)
|
||||||
|
fmt.Fprintf(w, "var icmpTypes = map[ICMPType]string{\n")
|
||||||
|
for _, pr := range prs {
|
||||||
|
if pr.Name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "%d: %q,\n", pr.Value, strings.ToLower(pr.OrigName))
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "}\n")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type icmpv6Parameters struct {
|
||||||
|
XMLName xml.Name `xml:"registry"`
|
||||||
|
Title string `xml:"title"`
|
||||||
|
Updated string `xml:"updated"`
|
||||||
|
Registries []struct {
|
||||||
|
Title string `xml:"title"`
|
||||||
|
Records []struct {
|
||||||
|
Value string `xml:"value"`
|
||||||
|
Name string `xml:"name"`
|
||||||
|
} `xml:"record"`
|
||||||
|
} `xml:"registry"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type canonICMPv6ParamRecord struct {
|
||||||
|
OrigName string
|
||||||
|
Name string
|
||||||
|
Value int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (icp *icmpv6Parameters) escape() []canonICMPv6ParamRecord {
|
||||||
|
id := -1
|
||||||
|
for i, r := range icp.Registries {
|
||||||
|
if strings.Contains(r.Title, "Type") || strings.Contains(r.Title, "type") {
|
||||||
|
id = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if id < 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
prs := make([]canonICMPv6ParamRecord, len(icp.Registries[id].Records))
|
||||||
|
sr := strings.NewReplacer(
|
||||||
|
"Messages", "",
|
||||||
|
"Message", "",
|
||||||
|
"ICMP", "",
|
||||||
|
"+", "P",
|
||||||
|
"-", "",
|
||||||
|
"/", "",
|
||||||
|
".", "",
|
||||||
|
" ", "",
|
||||||
|
)
|
||||||
|
for i, pr := range icp.Registries[id].Records {
|
||||||
|
if strings.Contains(pr.Name, "Reserved") ||
|
||||||
|
strings.Contains(pr.Name, "Unassigned") ||
|
||||||
|
strings.Contains(pr.Name, "Deprecated") ||
|
||||||
|
strings.Contains(pr.Name, "Experiment") ||
|
||||||
|
strings.Contains(pr.Name, "experiment") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ss := strings.Split(pr.Name, "\n")
|
||||||
|
if len(ss) > 1 {
|
||||||
|
prs[i].Name = strings.Join(ss, " ")
|
||||||
|
} else {
|
||||||
|
prs[i].Name = ss[0]
|
||||||
|
}
|
||||||
|
s := strings.TrimSpace(prs[i].Name)
|
||||||
|
prs[i].OrigName = s
|
||||||
|
prs[i].Name = sr.Replace(s)
|
||||||
|
prs[i].Value, _ = strconv.Atoi(pr.Value)
|
||||||
|
}
|
||||||
|
return prs
|
||||||
|
}
|
||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
// 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 darwin dragonfly freebsd linux netbsd openbsd windows
|
||||||
|
|
||||||
|
package ipv6
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
// TrafficClass returns the traffic class field value for outgoing
|
||||||
|
// packets.
|
||||||
|
func (c *genericOpt) TrafficClass() (int, error) {
|
||||||
|
if !c.ok() {
|
||||||
|
return 0, syscall.EINVAL
|
||||||
|
}
|
||||||
|
fd, err := c.sysfd()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return getInt(fd, &sockOpts[ssoTrafficClass])
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTrafficClass sets the traffic class field value for future
|
||||||
|
// outgoing packets.
|
||||||
|
func (c *genericOpt) SetTrafficClass(tclass int) error {
|
||||||
|
if !c.ok() {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
fd, err := c.sysfd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return setInt(fd, &sockOpts[ssoTrafficClass], tclass)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HopLimit returns the hop limit field value for outgoing packets.
|
||||||
|
func (c *genericOpt) HopLimit() (int, error) {
|
||||||
|
if !c.ok() {
|
||||||
|
return 0, syscall.EINVAL
|
||||||
|
}
|
||||||
|
fd, err := c.sysfd()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return getInt(fd, &sockOpts[ssoHopLimit])
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHopLimit sets the hop limit field value for future outgoing
|
||||||
|
// packets.
|
||||||
|
func (c *genericOpt) SetHopLimit(hoplim int) error {
|
||||||
|
if !c.ok() {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
fd, err := c.sysfd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return setInt(fd, &sockOpts[ssoHopLimit], hoplim)
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
// 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 nacl plan9 solaris
|
||||||
|
|
||||||
|
package ipv6
|
||||||
|
|
||||||
|
// TrafficClass returns the traffic class field value for outgoing
|
||||||
|
// packets.
|
||||||
|
func (c *genericOpt) TrafficClass() (int, error) {
|
||||||
|
return 0, errOpNoSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTrafficClass sets the traffic class field value for future
|
||||||
|
// outgoing packets.
|
||||||
|
func (c *genericOpt) SetTrafficClass(tclass int) error {
|
||||||
|
return errOpNoSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
// HopLimit returns the hop limit field value for outgoing packets.
|
||||||
|
func (c *genericOpt) HopLimit() (int, error) {
|
||||||
|
return 0, errOpNoSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHopLimit sets the hop limit field value for future outgoing
|
||||||
|
// packets.
|
||||||
|
func (c *genericOpt) SetHopLimit(hoplim int) error {
|
||||||
|
return errOpNoSupport
|
||||||
|
}
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
// Copyright 2014 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 ipv6
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Version = 6 // protocol version
|
||||||
|
HeaderLen = 40 // header length
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Header represents an IPv6 base header.
|
||||||
|
type Header struct {
|
||||||
|
Version int // protocol version
|
||||||
|
TrafficClass int // traffic class
|
||||||
|
FlowLabel int // flow label
|
||||||
|
PayloadLen int // payload length
|
||||||
|
NextHeader int // next header
|
||||||
|
HopLimit int // hop limit
|
||||||
|
Src net.IP // source address
|
||||||
|
Dst net.IP // destination address
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Header) String() string {
|
||||||
|
if h == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("ver: %v, tclass: %#x, flowlbl: %#x, payloadlen: %v, nxthdr: %v, hoplim: %v, src: %v, dst: %v", h.Version, h.TrafficClass, h.FlowLabel, h.PayloadLen, h.NextHeader, h.HopLimit, h.Src, h.Dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseHeader parses b as an IPv6 base header.
|
||||||
|
func ParseHeader(b []byte) (*Header, error) {
|
||||||
|
if len(b) < HeaderLen {
|
||||||
|
return nil, errors.New("header too short")
|
||||||
|
}
|
||||||
|
h := &Header{
|
||||||
|
Version: int(b[0]) >> 4,
|
||||||
|
TrafficClass: int(b[0]&0x0f)<<4 | int(b[1])>>4,
|
||||||
|
FlowLabel: int(b[1]&0x0f)<<16 | int(b[2])<<8 | int(b[3]),
|
||||||
|
PayloadLen: int(b[4])<<8 | int(b[5]),
|
||||||
|
NextHeader: int(b[6]),
|
||||||
|
HopLimit: int(b[7]),
|
||||||
|
}
|
||||||
|
h.Src = make(net.IP, net.IPv6len)
|
||||||
|
copy(h.Src, b[8:24])
|
||||||
|
h.Dst = make(net.IP, net.IPv6len)
|
||||||
|
copy(h.Dst, b[24:40])
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
// Copyright 2014 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 ipv6_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/net/internal/iana"
|
||||||
|
"golang.org/x/net/ipv6"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
wireHeaderFromKernel = [ipv6.HeaderLen]byte{
|
||||||
|
0x69, 0x8b, 0xee, 0xf1,
|
||||||
|
0xca, 0xfe, 0x2c, 0x01,
|
||||||
|
0x20, 0x01, 0x0d, 0xb8,
|
||||||
|
0x00, 0x01, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x01,
|
||||||
|
0x20, 0x01, 0x0d, 0xb8,
|
||||||
|
0x00, 0x02, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x01,
|
||||||
|
}
|
||||||
|
|
||||||
|
testHeader = &ipv6.Header{
|
||||||
|
Version: ipv6.Version,
|
||||||
|
TrafficClass: iana.DiffServAF43,
|
||||||
|
FlowLabel: 0xbeef1,
|
||||||
|
PayloadLen: 0xcafe,
|
||||||
|
NextHeader: iana.ProtocolIPv6Frag,
|
||||||
|
HopLimit: 1,
|
||||||
|
Src: net.ParseIP("2001:db8:1::1"),
|
||||||
|
Dst: net.ParseIP("2001:db8:2::1"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseHeader(t *testing.T) {
|
||||||
|
h, err := ipv6.ParseHeader(wireHeaderFromKernel[:])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(h, testHeader) {
|
||||||
|
t.Fatalf("got %#v; want %#v", h, testHeader)
|
||||||
|
}
|
||||||
|
}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
// 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 ipv6
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errOpNoSupport = errors.New("operation not supported")
|
||||||
|
|
||||||
|
func boolint(b bool) int {
|
||||||
|
if b {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func netAddrToIP16(a net.Addr) net.IP {
|
||||||
|
switch v := a.(type) {
|
||||||
|
case *net.UDPAddr:
|
||||||
|
if ip := v.IP.To16(); ip != nil && ip.To4() == nil {
|
||||||
|
return ip
|
||||||
|
}
|
||||||
|
case *net.IPAddr:
|
||||||
|
if ip := v.IP.To16(); ip != nil && ip.To4() == nil {
|
||||||
|
return ip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
// 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 nacl plan9 solaris
|
||||||
|
|
||||||
|
package ipv6
|
||||||
|
|
||||||
|
func (c *genericOpt) sysfd() (int, error) {
|
||||||
|
return 0, errOpNoSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dgramOpt) sysfd() (int, error) {
|
||||||
|
return 0, errOpNoSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *payloadHandler) sysfd() (int, error) {
|
||||||
|
return 0, errOpNoSupport
|
||||||
|
}
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
// 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 darwin dragonfly freebsd linux netbsd openbsd
|
||||||
|
|
||||||
|
package ipv6
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *genericOpt) sysfd() (int, error) {
|
||||||
|
switch p := c.Conn.(type) {
|
||||||
|
case *net.TCPConn, *net.UDPConn, *net.IPConn:
|
||||||
|
return sysfd(p)
|
||||||
|
}
|
||||||
|
return 0, errInvalidConnType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dgramOpt) sysfd() (int, error) {
|
||||||
|
switch p := c.PacketConn.(type) {
|
||||||
|
case *net.UDPConn, *net.IPConn:
|
||||||
|
return sysfd(p.(net.Conn))
|
||||||
|
}
|
||||||
|
return 0, errInvalidConnType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *payloadHandler) sysfd() (int, error) {
|
||||||
|
return sysfd(c.PacketConn.(net.Conn))
|
||||||
|
}
|
||||||
|
|
||||||
|
func sysfd(c net.Conn) (int, error) {
|
||||||
|
cv := reflect.ValueOf(c)
|
||||||
|
switch ce := cv.Elem(); ce.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
nfd := ce.FieldByName("conn").FieldByName("fd")
|
||||||
|
switch fe := nfd.Elem(); fe.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
fd := fe.FieldByName("sysfd")
|
||||||
|
return int(fd.Int()), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, errInvalidConnType
|
||||||
|
}
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
// 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 ipv6
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"reflect"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *genericOpt) sysfd() (syscall.Handle, error) {
|
||||||
|
switch p := c.Conn.(type) {
|
||||||
|
case *net.TCPConn, *net.UDPConn, *net.IPConn:
|
||||||
|
return sysfd(p)
|
||||||
|
}
|
||||||
|
return syscall.InvalidHandle, errInvalidConnType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dgramOpt) sysfd() (syscall.Handle, error) {
|
||||||
|
switch p := c.PacketConn.(type) {
|
||||||
|
case *net.UDPConn, *net.IPConn:
|
||||||
|
return sysfd(p.(net.Conn))
|
||||||
|
}
|
||||||
|
return syscall.InvalidHandle, errInvalidConnType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *payloadHandler) sysfd() (syscall.Handle, error) {
|
||||||
|
return sysfd(c.PacketConn.(net.Conn))
|
||||||
|
}
|
||||||
|
|
||||||
|
func sysfd(c net.Conn) (syscall.Handle, error) {
|
||||||
|
cv := reflect.ValueOf(c)
|
||||||
|
switch ce := cv.Elem(); ce.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
netfd := ce.FieldByName("conn").FieldByName("fd")
|
||||||
|
switch fe := netfd.Elem(); fe.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
fd := fe.FieldByName("sysfd")
|
||||||
|
return syscall.Handle(fd.Uint()), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return syscall.InvalidHandle, errInvalidConnType
|
||||||
|
}
|
||||||
+82
@@ -0,0 +1,82 @@
|
|||||||
|
// go generate gen.go
|
||||||
|
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||||
|
|
||||||
|
package ipv6
|
||||||
|
|
||||||
|
// Internet Control Message Protocol version 6 (ICMPv6) Parameters, Updated: 2015-07-07
|
||||||
|
const (
|
||||||
|
ICMPTypeDestinationUnreachable ICMPType = 1 // Destination Unreachable
|
||||||
|
ICMPTypePacketTooBig ICMPType = 2 // Packet Too Big
|
||||||
|
ICMPTypeTimeExceeded ICMPType = 3 // Time Exceeded
|
||||||
|
ICMPTypeParameterProblem ICMPType = 4 // Parameter Problem
|
||||||
|
ICMPTypeEchoRequest ICMPType = 128 // Echo Request
|
||||||
|
ICMPTypeEchoReply ICMPType = 129 // Echo Reply
|
||||||
|
ICMPTypeMulticastListenerQuery ICMPType = 130 // Multicast Listener Query
|
||||||
|
ICMPTypeMulticastListenerReport ICMPType = 131 // Multicast Listener Report
|
||||||
|
ICMPTypeMulticastListenerDone ICMPType = 132 // Multicast Listener Done
|
||||||
|
ICMPTypeRouterSolicitation ICMPType = 133 // Router Solicitation
|
||||||
|
ICMPTypeRouterAdvertisement ICMPType = 134 // Router Advertisement
|
||||||
|
ICMPTypeNeighborSolicitation ICMPType = 135 // Neighbor Solicitation
|
||||||
|
ICMPTypeNeighborAdvertisement ICMPType = 136 // Neighbor Advertisement
|
||||||
|
ICMPTypeRedirect ICMPType = 137 // Redirect Message
|
||||||
|
ICMPTypeRouterRenumbering ICMPType = 138 // Router Renumbering
|
||||||
|
ICMPTypeNodeInformationQuery ICMPType = 139 // ICMP Node Information Query
|
||||||
|
ICMPTypeNodeInformationResponse ICMPType = 140 // ICMP Node Information Response
|
||||||
|
ICMPTypeInverseNeighborDiscoverySolicitation ICMPType = 141 // Inverse Neighbor Discovery Solicitation Message
|
||||||
|
ICMPTypeInverseNeighborDiscoveryAdvertisement ICMPType = 142 // Inverse Neighbor Discovery Advertisement Message
|
||||||
|
ICMPTypeVersion2MulticastListenerReport ICMPType = 143 // Version 2 Multicast Listener Report
|
||||||
|
ICMPTypeHomeAgentAddressDiscoveryRequest ICMPType = 144 // Home Agent Address Discovery Request Message
|
||||||
|
ICMPTypeHomeAgentAddressDiscoveryReply ICMPType = 145 // Home Agent Address Discovery Reply Message
|
||||||
|
ICMPTypeMobilePrefixSolicitation ICMPType = 146 // Mobile Prefix Solicitation
|
||||||
|
ICMPTypeMobilePrefixAdvertisement ICMPType = 147 // Mobile Prefix Advertisement
|
||||||
|
ICMPTypeCertificationPathSolicitation ICMPType = 148 // Certification Path Solicitation Message
|
||||||
|
ICMPTypeCertificationPathAdvertisement ICMPType = 149 // Certification Path Advertisement Message
|
||||||
|
ICMPTypeMulticastRouterAdvertisement ICMPType = 151 // Multicast Router Advertisement
|
||||||
|
ICMPTypeMulticastRouterSolicitation ICMPType = 152 // Multicast Router Solicitation
|
||||||
|
ICMPTypeMulticastRouterTermination ICMPType = 153 // Multicast Router Termination
|
||||||
|
ICMPTypeFMIPv6 ICMPType = 154 // FMIPv6 Messages
|
||||||
|
ICMPTypeRPLControl ICMPType = 155 // RPL Control Message
|
||||||
|
ICMPTypeILNPv6LocatorUpdate ICMPType = 156 // ILNPv6 Locator Update Message
|
||||||
|
ICMPTypeDuplicateAddressRequest ICMPType = 157 // Duplicate Address Request
|
||||||
|
ICMPTypeDuplicateAddressConfirmation ICMPType = 158 // Duplicate Address Confirmation
|
||||||
|
ICMPTypeMPLControl ICMPType = 159 // MPL Control Message
|
||||||
|
)
|
||||||
|
|
||||||
|
// Internet Control Message Protocol version 6 (ICMPv6) Parameters, Updated: 2015-07-07
|
||||||
|
var icmpTypes = map[ICMPType]string{
|
||||||
|
1: "destination unreachable",
|
||||||
|
2: "packet too big",
|
||||||
|
3: "time exceeded",
|
||||||
|
4: "parameter problem",
|
||||||
|
128: "echo request",
|
||||||
|
129: "echo reply",
|
||||||
|
130: "multicast listener query",
|
||||||
|
131: "multicast listener report",
|
||||||
|
132: "multicast listener done",
|
||||||
|
133: "router solicitation",
|
||||||
|
134: "router advertisement",
|
||||||
|
135: "neighbor solicitation",
|
||||||
|
136: "neighbor advertisement",
|
||||||
|
137: "redirect message",
|
||||||
|
138: "router renumbering",
|
||||||
|
139: "icmp node information query",
|
||||||
|
140: "icmp node information response",
|
||||||
|
141: "inverse neighbor discovery solicitation message",
|
||||||
|
142: "inverse neighbor discovery advertisement message",
|
||||||
|
143: "version 2 multicast listener report",
|
||||||
|
144: "home agent address discovery request message",
|
||||||
|
145: "home agent address discovery reply message",
|
||||||
|
146: "mobile prefix solicitation",
|
||||||
|
147: "mobile prefix advertisement",
|
||||||
|
148: "certification path solicitation message",
|
||||||
|
149: "certification path advertisement message",
|
||||||
|
151: "multicast router advertisement",
|
||||||
|
152: "multicast router solicitation",
|
||||||
|
153: "multicast router termination",
|
||||||
|
154: "fmipv6 messages",
|
||||||
|
155: "rpl control message",
|
||||||
|
156: "ilnpv6 locator update message",
|
||||||
|
157: "duplicate address request",
|
||||||
|
158: "duplicate address confirmation",
|
||||||
|
159: "mpl control message",
|
||||||
|
}
|
||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
// 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 ipv6
|
||||||
|
|
||||||
|
import "golang.org/x/net/internal/iana"
|
||||||
|
|
||||||
|
// An ICMPType represents a type of ICMP message.
|
||||||
|
type ICMPType int
|
||||||
|
|
||||||
|
func (typ ICMPType) String() string {
|
||||||
|
s, ok := icmpTypes[typ]
|
||||||
|
if !ok {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protocol returns the ICMPv6 protocol number.
|
||||||
|
func (typ ICMPType) Protocol() int {
|
||||||
|
return iana.ProtocolIPv6ICMP
|
||||||
|
}
|
||||||
|
|
||||||
|
// An ICMPFilter represents an ICMP message filter for incoming
|
||||||
|
// packets. The filter belongs to a packet delivery path on a host and
|
||||||
|
// it cannot interact with forwarding packets or tunnel-outer packets.
|
||||||
|
//
|
||||||
|
// Note: RFC 2460 defines a reasonable role model. A node means a
|
||||||
|
// device that implements IP. A router means a node that forwards IP
|
||||||
|
// packets not explicitly addressed to itself, and a host means a node
|
||||||
|
// that is not a router.
|
||||||
|
type ICMPFilter struct {
|
||||||
|
sysICMPv6Filter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept accepts incoming ICMP packets including the type field value
|
||||||
|
// typ.
|
||||||
|
func (f *ICMPFilter) Accept(typ ICMPType) {
|
||||||
|
f.accept(typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block blocks incoming ICMP packets including the type field value
|
||||||
|
// typ.
|
||||||
|
func (f *ICMPFilter) Block(typ ICMPType) {
|
||||||
|
f.block(typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAll sets the filter action to the filter.
|
||||||
|
func (f *ICMPFilter) SetAll(block bool) {
|
||||||
|
f.setAll(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WillBlock reports whether the ICMP type will be blocked.
|
||||||
|
func (f *ICMPFilter) WillBlock(typ ICMPType) bool {
|
||||||
|
return f.willBlock(typ)
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
// 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 darwin dragonfly freebsd netbsd openbsd
|
||||||
|
|
||||||
|
package ipv6
|
||||||
|
|
||||||
|
func (f *sysICMPv6Filter) accept(typ ICMPType) {
|
||||||
|
f.Filt[typ>>5] |= 1 << (uint32(typ) & 31)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *sysICMPv6Filter) block(typ ICMPType) {
|
||||||
|
f.Filt[typ>>5] &^= 1 << (uint32(typ) & 31)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *sysICMPv6Filter) setAll(block bool) {
|
||||||
|
for i := range f.Filt {
|
||||||
|
if block {
|
||||||
|
f.Filt[i] = 0
|
||||||
|
} else {
|
||||||
|
f.Filt[i] = 1<<32 - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *sysICMPv6Filter) willBlock(typ ICMPType) bool {
|
||||||
|
return f.Filt[typ>>5]&(1<<(uint32(typ)&31)) == 0
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
// 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 ipv6
|
||||||
|
|
||||||
|
func (f *sysICMPv6Filter) accept(typ ICMPType) {
|
||||||
|
f.Data[typ>>5] &^= 1 << (uint32(typ) & 31)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *sysICMPv6Filter) block(typ ICMPType) {
|
||||||
|
f.Data[typ>>5] |= 1 << (uint32(typ) & 31)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *sysICMPv6Filter) setAll(block bool) {
|
||||||
|
for i := range f.Data {
|
||||||
|
if block {
|
||||||
|
f.Data[i] = 1<<32 - 1
|
||||||
|
} else {
|
||||||
|
f.Data[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *sysICMPv6Filter) willBlock(typ ICMPType) bool {
|
||||||
|
return f.Data[typ>>5]&(1<<(uint32(typ)&31)) != 0
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
// 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 solaris
|
||||||
|
|
||||||
|
package ipv6
|
||||||
|
|
||||||
|
func (f *sysICMPv6Filter) accept(typ ICMPType) {
|
||||||
|
// TODO(mikio): implement this
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *sysICMPv6Filter) block(typ ICMPType) {
|
||||||
|
// TODO(mikio): implement this
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *sysICMPv6Filter) setAll(block bool) {
|
||||||
|
// TODO(mikio): implement this
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *sysICMPv6Filter) willBlock(typ ICMPType) bool {
|
||||||
|
// TODO(mikio): implement this
|
||||||
|
return false
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
// 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 nacl plan9
|
||||||
|
|
||||||
|
package ipv6
|
||||||
|
|
||||||
|
type sysICMPv6Filter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *sysICMPv6Filter) accept(typ ICMPType) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *sysICMPv6Filter) block(typ ICMPType) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *sysICMPv6Filter) setAll(block bool) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *sysICMPv6Filter) willBlock(typ ICMPType) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user