Compare commits
4201 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 952ab7db1c | |||
| abb3fb8a31 | |||
| fc860df514 | |||
| c934918347 | |||
| c98a34a5d4 | |||
| bdcffe703b | |||
| a09079ed25 | |||
| 1b59960ff9 | |||
| f04d054b5a | |||
| 132789785d | |||
| 002de7b6a0 | |||
| 9e725026d1 | |||
| da39dfada3 | |||
| ff2cde469e | |||
| 0fe4c01a28 | |||
| 351e855464 | |||
| 7203ccb73d | |||
| e89c4c053a | |||
| 0391c57a37 | |||
| 2f9840ddae | |||
| 513d3bc374 | |||
| c0a26c918a | |||
| d1704d5304 | |||
| d3d0161fb9 | |||
| db8777c29e | |||
| ba4554f053 | |||
| 33bed5b1ec | |||
| 4f27bdfc27 | |||
| 9212303906 | |||
| 603da2dce2 | |||
| d510e3cca3 | |||
| f51514d0e7 | |||
| e67be59c5f | |||
| add12b43aa | |||
| aec91d8f32 | |||
| 53b0f36be6 | |||
| 830bde2c83 | |||
| be1744a481 | |||
| 01ade9c8ae | |||
| b1acc37c16 | |||
| 64a591610b | |||
| 9a07b22d4a | |||
| 406bedf1e3 | |||
| 7f55fbbe84 | |||
| 75f9ea623c | |||
| 9745679c63 | |||
| 8519a24ba6 | |||
| c0be9987d0 | |||
| c1f1fd71fe | |||
| 3c657d1749 | |||
| 53f80fdf73 | |||
| 6325ae070c | |||
| 089c283ca6 | |||
| 8d7ea0424d | |||
| f12d5771dc | |||
| 7b0c49a1b6 | |||
| 0690fe7585 | |||
| 3bc918ff78 | |||
| 3e50edf46f | |||
| 1b10607def | |||
| 9d7a811e72 | |||
| 4f7d43a6dd | |||
| 83675e7a1d | |||
| 34fee05a1d | |||
| d10773c311 | |||
| caa2356409 | |||
| 523ac45456 | |||
| b50d57b7fd | |||
| 666314a9d9 | |||
| 8e645ab782 | |||
| dfc1101307 | |||
| f12ca95af2 | |||
| f528923d1e | |||
| c9d6366d75 | |||
| 714a47ffb0 | |||
| 86a22b8086 | |||
| a4d27282ae | |||
| 2e425c4386 | |||
| d27463268d | |||
| 3d74ff97af | |||
| 675846ac1e | |||
| c2b0d309fb | |||
| cb0950b3fd | |||
| 03d0f0dc34 | |||
| 91c3218a0c | |||
| 2ced65b9e7 | |||
| 03821d8bd3 | |||
| 92405ad1a6 | |||
| 5a69e85e80 | |||
| 4f034a01ed | |||
| d7bc0659e4 | |||
| 4f4781d254 | |||
| 6a87aac84f | |||
| 797a999585 | |||
| 272fb3b444 | |||
| 60eb9088ff | |||
| c8652222ef | |||
| 84494edab4 | |||
| b19885fdb3 | |||
| e39dafb584 | |||
| 710dba7f84 | |||
| a57fa9cfab | |||
| 49d5eae66a | |||
| d9d2cf74a0 | |||
| 1b1741de64 | |||
| 50ba0fd079 | |||
| 60a6a40175 | |||
| 323195be0e | |||
| ae76f1e999 | |||
| 0a29fa65ab | |||
| 37cd5a0bec | |||
| db0c85318b | |||
| 9e00b619ab | |||
| 8aa2d8d92c | |||
| 22d8a379c7 | |||
| 09aff7bb14 | |||
| b068c8f346 | |||
| c2ff49ed43 | |||
| 4cb6bb6f64 | |||
| b80da29b23 | |||
| 836ca50570 | |||
| e384c822b6 | |||
| 916182bc73 | |||
| c62ce007ea | |||
| aec66045ef | |||
| 03c0537340 | |||
| 7dde6c7e3c | |||
| cb0f4ce55a | |||
| d86d5e8bb2 | |||
| d2c68fd163 | |||
| a02db70a63 | |||
| 165417c462 | |||
| ff3cbdc90d | |||
| 9028969617 | |||
| f6da436f4b | |||
| 166a33037f | |||
| 7c0798b622 | |||
| ee42c46bd3 | |||
| 885f6fcf28 | |||
| ada5ab74d2 | |||
| 0bc3ae15ae | |||
| dc57bab107 | |||
| 48795dba07 | |||
| c55c0c8c28 | |||
| 2c9d96375b | |||
| e20679afe1 | |||
| b37c05c6b8 | |||
| dfe4008607 | |||
| 54d610878d | |||
| 229b777f30 | |||
| 7812c2c937 | |||
| 500e02cdbb | |||
| 82c9e23206 | |||
| 705b7d18e8 | |||
| f4bde023aa | |||
| af48b069cc | |||
| 5cb4a9acf6 | |||
| adc5bf6604 | |||
| 24d307531d | |||
| 93fdd1c012 | |||
| 5161f03f02 | |||
| 682ffcb8ed | |||
| 524ffe3fa5 | |||
| d8366e4a88 | |||
| b83c5b32bf | |||
| 3102e36a45 | |||
| 3d8344003e | |||
| a4415bce10 | |||
| 9f87fd1fcf | |||
| 5592b8b190 | |||
| f822b10550 | |||
| 1a6c7587c2 | |||
| 6b82538e62 | |||
| 3f17bda786 | |||
| e10d7260c2 | |||
| 409cb2beb8 | |||
| 742ab17a2a | |||
| 9f254df091 | |||
| 290dcf0610 | |||
| 0f0290d574 | |||
| 5bb72dfe5d | |||
| 0b73a66516 | |||
| 0631fd2440 | |||
| 1e2e8650e1 | |||
| 21035b0c1a | |||
| c884955ff7 | |||
| b91ff430db | |||
| b09c50bf9c | |||
| 7f0603effa | |||
| ff441d3b3e | |||
| 950f4a8672 | |||
| 95eb81467c | |||
| 55ed883648 | |||
| 0c2cebb775 | |||
| 645588902b | |||
| 881e923105 | |||
| ef5ca0c218 | |||
| 406b394704 | |||
| 7b0d8c2e77 | |||
| b1b68ceedb | |||
| 6df3940c26 | |||
| 238476bfcd | |||
| d69a9d52a9 | |||
| b3007c03bd | |||
| c2784d76e4 | |||
| 8ff7ceeddc | |||
| 84c8964865 | |||
| 7d3f94911f | |||
| 54e17c8bf4 | |||
| 793b86e604 | |||
| 7b47692ec0 | |||
| 35a75a95dc | |||
| c7c7fbe9d9 | |||
| 9e0e04f4de | |||
| 1e2732aa21 | |||
| b7234785f8 | |||
| 30056cd1ae | |||
| 4781e1d86f | |||
| a467f6908c | |||
| 9fc20b41c6 | |||
| e2c44f519c | |||
| 53a029a796 | |||
| 02da7414ab | |||
| d1f953b0dd | |||
| 47cefb2ab2 | |||
| 4de8920642 | |||
| 76f9e5c5db | |||
| 27d675a793 | |||
| dd9e1d61eb | |||
| b670b5550a | |||
| ee6516aa31 | |||
| 8b15624f7d | |||
| 5baa432906 | |||
| d3a02a1663 | |||
| e187a5f5cb | |||
| e73631c5f0 | |||
| b87e5ed13d | |||
| c51365c634 | |||
| d60f0e734c | |||
| a83176c77a | |||
| eb31be0432 | |||
| 07bf24a3b4 | |||
| ef1633ac76 | |||
| 9f305f674a | |||
| 9d2b744c12 | |||
| 91f1f3067a | |||
| 119335930c | |||
| 370a3549e7 | |||
| d64d954721 | |||
| 81f9a81c7d | |||
| 127c891526 | |||
| c08024ebb8 | |||
| 677fde50b3 | |||
| eabce48761 | |||
| d59aecba31 | |||
| 22b0bd8e13 | |||
| 9260248543 | |||
| c4a348db67 | |||
| 20aa53486a | |||
| ed4807d9a4 | |||
| 7d07b19734 | |||
| a7e30c925f | |||
| 0c81fa4191 | |||
| 9e696a154b | |||
| 344e52e311 | |||
| a2f51c85c2 | |||
| 00f4900ba7 | |||
| e125f8b05b | |||
| fb198a0645 | |||
| 506181599c | |||
| 15d0bdcba9 | |||
| b6b0d0664d | |||
| baa3aa4bad | |||
| a48a31e3f5 | |||
| 2343c82c33 | |||
| 616883304e | |||
| 1cb09b7d2d | |||
| 3b5e1fa0fc | |||
| a94aceb22f | |||
| f5d8243f15 | |||
| c9c2e69f49 | |||
| ef0dcea6a4 | |||
| 678c80ffe4 | |||
| 0f1d0380dc | |||
| 58bc722a1f | |||
| 53dc346583 | |||
| c2f498fc82 | |||
| a548014755 | |||
| 2c18640386 | |||
| 78094fa0cb | |||
| f6458d1b8f | |||
| 56cf2db68b | |||
| a1b5a3d5c0 | |||
| 4d3b5348ae | |||
| 3d02fcd473 | |||
| 25bf406f0e | |||
| 071910b255 | |||
| bed6fb8baf | |||
| d903bf79c5 | |||
| 49cfe57edc | |||
| 1f70f7e37c | |||
| eca076cf7d | |||
| dbcf7a02a0 | |||
| 7be53bbb88 | |||
| 17e3608865 | |||
| 19c7cd99f5 | |||
| 01aef75c96 | |||
| 8f6d587ecb | |||
| 3ef19411f9 | |||
| ea43e089d4 | |||
| 5fa9237a62 | |||
| fa367e92b0 | |||
| 4072ae4d05 | |||
| e9c6795ef8 | |||
| afb27f7f02 | |||
| cf4d7ff50f | |||
| 29e7e54bb4 | |||
| 6982c06261 | |||
| 26d87ec3bb | |||
| c51591b308 | |||
| 8b052a950d | |||
| 1ff5fc9620 | |||
| 4aaf8d4ceb | |||
| 720e8dedbc | |||
| 8ac11f19bd | |||
| bea6ecaf35 | |||
| 69f2c26d50 | |||
| 59802c3981 | |||
| bdbaa84989 | |||
| 8208bfa2b9 | |||
| c49d864f14 | |||
| 2621c6fd2f | |||
| 3c920c61e9 | |||
| a5e40563de | |||
| a557d62c4a | |||
| d6bb8e6e06 | |||
| 69d05a3637 | |||
| da3b38ccce | |||
| e7dc2f9190 | |||
| ac2e9b0e09 | |||
| 6cb4ac9ec2 | |||
| 862eec34db | |||
| 7da829a86f | |||
| 81bd428b25 | |||
| ae74ac8329 | |||
| 5520022766 | |||
| 7872bfa173 | |||
| bea3c01772 | |||
| 55a7830ff9 | |||
| 470ef87dd5 | |||
| b31bad1c4d | |||
| 8b4346c3ec | |||
| 2bdb37d412 | |||
| 1471c15b29 | |||
| 4b1782cf6d | |||
| 71fab4d250 | |||
| 22ebc80329 | |||
| 74b820f287 | |||
| 3949b750f5 | |||
| a26778fa9b | |||
| d4b7be009c | |||
| c126831108 | |||
| 2751be57dc | |||
| 8df90bb475 | |||
| 36251b86f7 | |||
| 42cc64e2ed | |||
| 158859a1e2 | |||
| 5822222c74 | |||
| e99be02055 | |||
| 1901a5a9f4 | |||
| a27032f09e | |||
| 5e041dca9f | |||
| c9ec6159e8 | |||
| 5b17aae1b2 | |||
| 5931231a32 | |||
| d7dc37b3c4 | |||
| 6802505dda | |||
| 7a92f6c6b1 | |||
| 68c1b2dd47 | |||
| b57c9b6af5 | |||
| c120c3a403 | |||
| 2bbd2d6ed1 | |||
| 4955297bf6 | |||
| 6d3f9d5154 | |||
| b97d5bcca8 | |||
| 97068c10f3 | |||
| 8cdab7231a | |||
| bc7639b0ff | |||
| 3f4f6d5787 | |||
| c17547159e | |||
| 8a3e584c19 | |||
| 043b04d8a6 | |||
| f87f13081b | |||
| 649d4cf7b0 | |||
| c7cf361a96 | |||
| 98f2875b22 | |||
| 2fdf9bc55d | |||
| c0ab669142 | |||
| 14a5561e43 | |||
| 0fe3ae7c22 | |||
| 5d0eb80204 | |||
| 441230ff77 | |||
| a0514bb1a7 | |||
| 80079e8322 | |||
| ae760798e1 | |||
| 364f61bda6 | |||
| 050f9f8091 | |||
| e0931e201e | |||
| 20e05cdcd0 | |||
| 4afe0f407a | |||
| 232c1172e5 | |||
| a505231774 | |||
| 885e3f19bd | |||
| 93b5180e62 | |||
| 27d5b17096 | |||
| c2119c9e62 | |||
| 91210cbb49 | |||
| 8e9c9b9553 | |||
| fae2ca8458 | |||
| 3af4164c8b | |||
| a1761795fe | |||
| e147db5233 | |||
| 582539a1e6 | |||
| 5cfb9783b3 | |||
| 8de21be274 | |||
| c554ffccc9 | |||
| 838c182b5b | |||
| fcc6a677a5 | |||
| bd63fd73b1 | |||
| fecb21cdb1 | |||
| 89a021609b | |||
| bbc178ccc4 | |||
| f1c73999be | |||
| 916ec63af6 | |||
| 341b9691a7 | |||
| 6e0f64017a | |||
| 14df5211af | |||
| 5611173366 | |||
| 1afe8ab736 | |||
| 1c8803402e | |||
| db03562d43 | |||
| d87287c0d0 | |||
| 992bb0a98c | |||
| e6551c8485 | |||
| 53f4dfe83c | |||
| 9410634c2b | |||
| b0e2050cdb | |||
| c7f136c2b8 | |||
| a9f0659f2f | |||
| 9988044bbe | |||
| c24bf7ea55 | |||
| 1296a22069 | |||
| 72172d853c | |||
| 5a05d9b867 | |||
| 841205dbfe | |||
| c58b383b6d | |||
| 2547a29dd7 | |||
| 8fa2b7765a | |||
| c7522063b3 | |||
| d1d967f0cf | |||
| 8c91ced784 | |||
| a4147d9019 | |||
| 673bd5fcc0 | |||
| 24c721cb5d | |||
| 230fb083fc | |||
| 509ae5e2d9 | |||
| 136b3f25f6 | |||
| 57eb1710e9 | |||
| ece1defb2f | |||
| 8fd2937a58 | |||
| cce634f340 | |||
| 47429d01e8 | |||
| 445c4edeca | |||
| c005b8dcb0 | |||
| b9ed6c4c2c | |||
| 3153e36a3d | |||
| 60bceb0f09 | |||
| 257c3f5e82 | |||
| 7d0723da68 | |||
| 205426a9c6 | |||
| bd12e38b56 | |||
| 95a65bf0d0 | |||
| 429b3a0429 | |||
| cc14563b62 | |||
| 99b00b6a5e | |||
| b2af8f135b | |||
| 67c39b2512 | |||
| d62820acf9 | |||
| ce29d3a574 | |||
| 1e9769cdd7 | |||
| 6daa766fde | |||
| ed95e80088 | |||
| 0dd7934405 | |||
| 8606b4dd8d | |||
| 4922b46fbd | |||
| b99e92bad7 | |||
| a17d953334 | |||
| 7817d092cb | |||
| 075a699aae | |||
| 44a542391e | |||
| e589e6c19d | |||
| 0a50f374db | |||
| 4a58196959 | |||
| 4949721c0b | |||
| 0901350087 | |||
| 8078babf0a | |||
| 00ce889a8b | |||
| 7279644372 | |||
| cd29e3c524 | |||
| 3312a29dde | |||
| 72d645865e | |||
| 9471b9f6af | |||
| 0518a92cdb | |||
| 6cf01c1d30 | |||
| 5f4ed66aa1 | |||
| ee5d0dd43f | |||
| 7ebf58f1bc | |||
| aecd7c64ce | |||
| 783dd612f7 | |||
| 4efff736b3 | |||
| fa12a18190 | |||
| 2b65e1062e | |||
| 80031c59da | |||
| 6e35592e9e | |||
| 9e11d7b201 | |||
| fd5f9f8968 | |||
| d8a0a477ca | |||
| 6dd6ecde95 | |||
| 6e148e20cd | |||
| 72c5f2e5c6 | |||
| d7d45d8092 | |||
| 57d5dfa80c | |||
| c080f677cb | |||
| 725baf0971 | |||
| ec4c3bae0d | |||
| 8d95c82d7c | |||
| 386cb274bd | |||
| a9d422e008 | |||
| 75f64733f9 | |||
| ad0a205116 | |||
| 40c952c0ae | |||
| 0ee1146e1c | |||
| 88180904f2 | |||
| f6ea2a7f8e | |||
| c5f90d64bc | |||
| 941c9f1531 | |||
| 62a4106a79 | |||
| 9c855ab22e | |||
| 166273b357 | |||
| d304498027 | |||
| 7cbd92e1b1 | |||
| 9245d22f16 | |||
| 3f85f719de | |||
| 7ba05c18ee | |||
| 617bf173a4 | |||
| 5f8c0ca932 | |||
| 93a66deb7e | |||
| 997bed569e | |||
| b999b58049 | |||
| c3c6ff0093 | |||
| 2953fe40d1 | |||
| 4b69d0e093 | |||
| ff0a83fe5b | |||
| 9c60bb8336 | |||
| 34903c4201 | |||
| 9ecf9a3f48 | |||
| 7ba9e7c322 | |||
| dc42db444b | |||
| a9c221189b | |||
| b2966957e0 | |||
| 0d30166357 | |||
| d65f1fb08a | |||
| 622b614f31 | |||
| 335b08e3a8 | |||
| b1ade6d0c0 | |||
| 46becc5338 | |||
| 20fac4bb80 | |||
| d84b6bb822 | |||
| 55b63941b8 | |||
| c9bb3ba2e4 | |||
| e70003737b | |||
| f98c21b68e | |||
| c704ba9ef9 | |||
| dfad6a2aa9 | |||
| fb7264a663 | |||
| 889814a1af | |||
| 694a7de59d | |||
| b312f81e21 | |||
| 059185b325 | |||
| f8eea13406 | |||
| 1e9e9cbebb | |||
| cdbb32d0f0 | |||
| becbb3b123 | |||
| 06da67e6fc | |||
| 2f08f8021f | |||
| 9d3f3847ed | |||
| 74c8d34805 | |||
| 5ec1490be0 | |||
| 2760d032ca | |||
| 813e6ddf83 | |||
| 6ffb95f6c8 | |||
| 0b5c11bf93 | |||
| 5aade9a4a5 | |||
| 9717c3d292 | |||
| a365ae51c4 | |||
| 97222797a0 | |||
| e588bb29b9 | |||
| 2dd9450793 | |||
| 3ee12464b4 | |||
| 59ebcea356 | |||
| 27d4896a13 | |||
| 1088eb12ea | |||
| f40d219370 | |||
| 429cc20eb7 | |||
| e85ce7c94e | |||
| 283c8d95e2 | |||
| a9aa375109 | |||
| f7d2c58783 | |||
| 4d3e0de4ba | |||
| 9dbc509996 | |||
| baec3f909c | |||
| c41aaad3bb | |||
| 9682bbfbda | |||
| 9e6a1fdcd4 | |||
| 49bddfbe53 | |||
| 55e0ac3e24 | |||
| cbcc3ea132 | |||
| ab132ff6fe | |||
| 19e52a10df | |||
| a8145e187f | |||
| e33fa10115 | |||
| 4b6e7e7867 | |||
| 70d121a94b | |||
| 33ffb07d31 | |||
| 7aaa92ac47 | |||
| 5883eb9a25 | |||
| e60efa2b3d | |||
| ddf6d64faa | |||
| c7221b035d | |||
| a69ba18f62 | |||
| b31611a8d1 | |||
| 0ca0e3e9bd | |||
| 0a96a1150b | |||
| b8c249cddc | |||
| e8ba6d4771 | |||
| 606fce09ca | |||
| 3d8b4a42b7 | |||
| ab8c2fb5c7 | |||
| 77578e8aac | |||
| 1fc2ab444b | |||
| fa5c890ff6 | |||
| a3c17f8f81 | |||
| f1f21bf220 | |||
| 54155cb42d | |||
| 414c58174b | |||
| 94acc20dd6 | |||
| 8e9119eedf | |||
| a04b92332f | |||
| 0ad10b0fee | |||
| 0ca2ed7ad7 | |||
| fa4226cae4 | |||
| cc63236a2e | |||
| 6623657ef3 | |||
| 4405117bea | |||
| e371800878 | |||
| 7a47646534 | |||
| d475ad7ce1 | |||
| 7c8418f493 | |||
| 200a7fc844 | |||
| 5a38e0ba3f | |||
| c77490c32d | |||
| b75c9f2bbb | |||
| 322bedbb04 | |||
| 487655b365 | |||
| 03c678a810 | |||
| b79f8aceb8 | |||
| 92e8c4303a | |||
| e735a3a25c | |||
| 8d13e01342 | |||
| 34f8cc8620 | |||
| add10c98fa | |||
| 6503326073 | |||
| db1dc9985a | |||
| 3641c97667 | |||
| 8f214fe4a9 | |||
| 98cfc204ca | |||
| d0061c172c | |||
| 93a04158fd | |||
| d862e79133 | |||
| 68c1a0b9b4 | |||
| 2a38d2a3d2 | |||
| 9c4175715a | |||
| d637148cca | |||
| 3395992abd | |||
| 7a15fef3b8 | |||
| 9667a0a618 | |||
| 7346113742 | |||
| 3f1fa04725 | |||
| 719c313b23 | |||
| 3d11efc9e0 | |||
| 3dca7cd694 | |||
| 803da92ca9 | |||
| b49bbe82dd | |||
| 3959eb26fb | |||
| 1235cead35 | |||
| dd6bb6d5fd | |||
| 91d37f35bc | |||
| 51518490c6 | |||
| 2c10beed0b | |||
| 8f3f787a34 | |||
| 3522d451df | |||
| cf3114b56d | |||
| 3d95135638 | |||
| 4db662e576 | |||
| 1d15b8be9b | |||
| d25b15263a | |||
| 7931d956f7 | |||
| c9afabf09f | |||
| c262f48bfe | |||
| 8c108b4d20 | |||
| ec137c9522 | |||
| b17d7d8126 | |||
| 43569d8d36 | |||
| c4b527e5e9 | |||
| 6386d079b0 | |||
| d2699a20fc | |||
| 0b854dff9d | |||
| 9de6cdddfd | |||
| 5045842f4f | |||
| e0405de5bf | |||
| d6fbfc3545 | |||
| bdaef44765 | |||
| 488444354b | |||
| 26654df48c | |||
| 7f5e236dd7 | |||
| 914b09fd1f | |||
| e9f05d138f | |||
| 10894695c6 | |||
| 6b188ebcf3 | |||
| 57e3f9e64b | |||
| b5694ca788 | |||
| bcfd18ceb1 | |||
| f689512a3f | |||
| dd1f7a5ab7 | |||
| d48e46a29c | |||
| 75460be98d | |||
| ce0456b5ac | |||
| e3e028c988 | |||
| da34f27546 | |||
| c205fdd77e | |||
| ae4206f362 | |||
| 391665e322 | |||
| 5521759b23 | |||
| f0492c4eb3 | |||
| b1edf12257 | |||
| 2579e8f715 | |||
| a1bcc15458 | |||
| 49c1527724 | |||
| da35820fd5 | |||
| 79eac61b09 | |||
| 2ff08e6c84 | |||
| 25b314f5f1 | |||
| c5e0c47989 | |||
| 4253f22680 | |||
| bdb56d91b9 | |||
| c3820fbbf2 | |||
| cbdb036b69 | |||
| b75b4190c8 | |||
| 1ad547fb65 | |||
| ac46db78d7 | |||
| 53a7c7bd49 | |||
| e667fcb472 | |||
| ee92ee0190 | |||
| 3e49b73f70 | |||
| 236e206764 | |||
| ed771f5c64 | |||
| c6dd777fd6 | |||
| 1caa683ec1 | |||
| 88dfd634e5 | |||
| 5c27796471 | |||
| a54365424e | |||
| ef35a7a4cb | |||
| 601a4fac1a | |||
| f4ccc69422 | |||
| ceea5ebeb3 | |||
| f35e1ac0c5 | |||
| 0e76f9d93b | |||
| 1b08176583 | |||
| b3e2665a79 | |||
| 81af29e3e2 | |||
| 0da0774ce4 | |||
| 151004d645 | |||
| 4e3fdfaeef | |||
| 1c29a93013 | |||
| 689f13f4f1 | |||
| 0558565a95 | |||
| b84c4e1417 | |||
| 9c099f1337 | |||
| 416811a2a9 | |||
| c20d612736 | |||
| 3475a9ab0a | |||
| d7adee05a8 | |||
| 73cbd17e17 | |||
| 7260629bc0 | |||
| 566c348b00 | |||
| 190f153b92 | |||
| c56c48a777 | |||
| 98d22b88a0 | |||
| 28449f9f5b | |||
| a0e2e7a962 | |||
| fb6d453c74 | |||
| 89f8be40c6 | |||
| 96fba1b322 | |||
| 0c68e1e510 | |||
| 22903df2c1 | |||
| 10618e80a3 | |||
| 161326c548 | |||
| f7fc0c1d3e | |||
| 120e6eab2c | |||
| 72de47df00 | |||
| dfe23e8d53 | |||
| 2b1c942d56 | |||
| 204f125ab3 | |||
| 73f9c7d174 | |||
| c4ba580cbb | |||
| 9fda9642d3 | |||
| dfd2c464b6 | |||
| 5a1ee7f0b0 | |||
| fdcbd54cd7 | |||
| e14741a58c | |||
| 67acef1794 | |||
| 237893ead3 | |||
| 2590536ef3 | |||
| dc91995475 | |||
| 3655c97850 | |||
| c0f3f06cfb | |||
| 05450ca034 | |||
| c005e61151 | |||
| 63e0b53e8b | |||
| 1f586c0fdd | |||
| f1a073501f | |||
| a72f5379fb | |||
| 8cccecceba | |||
| 81418d724a | |||
| 3eb7a9373a | |||
| ac510b26e2 | |||
| 20f8b4fd57 | |||
| 1c9361a818 | |||
| 35e87e23fd | |||
| e03be9158b | |||
| 9833d13762 | |||
| 6ec7d711d8 | |||
| 22a4d49ed0 | |||
| ddca8d91fa | |||
| ee36e2d46d | |||
| de49ea594a | |||
| 6d4fa27ea7 | |||
| 9587b89d9d | |||
| 79c7f7193b | |||
| 2c4b92d410 | |||
| dd78177ae0 | |||
| bd55ec79d2 | |||
| 1313ba8c0a | |||
| 842e873a94 | |||
| d4c4b1fb4c | |||
| 68f1c6ccab | |||
| 4c8aa14e07 | |||
| bd1c29ee32 | |||
| 9b1c592fb7 | |||
| f36f00e87b | |||
| dbb3a34887 | |||
| 929a4d0c0c | |||
| c953cdc375 | |||
| c20c17e3c6 | |||
| 1a1e35d998 | |||
| 8d2a31e38e | |||
| fe377a166a | |||
| 5770d80bf9 | |||
| 7a16dbd31d | |||
| 926c88cfc4 | |||
| 29d010ec0e | |||
| 920274bce4 | |||
| 2ebd6ad77f | |||
| 79dd6918f2 | |||
| 79f7f50c4d | |||
| 987718baf8 | |||
| 4fb9c143ac | |||
| ec62888539 | |||
| 8c34a76f7a | |||
| 6809d38cde | |||
| 69ae4aa024 | |||
| 8e8b867fba | |||
| 0a118d2979 | |||
| 8daaa5d0d2 | |||
| eb14f85a57 | |||
| c69c3c7c36 | |||
| 54911d44c5 | |||
| c765f7be8d | |||
| 44bdaf3ac2 | |||
| fc16e49cb0 | |||
| f5a310ad64 | |||
| 01e50eb3fa | |||
| 722b81c6f0 | |||
| f0efa2b974 | |||
| bab7c8ebbf | |||
| 0725e3af38 | |||
| dd7bb6c4b8 | |||
| d41c131364 | |||
| 47f22ff3e5 | |||
| 744c2e82b5 | |||
| ead7281c20 | |||
| aa3ef49dd7 | |||
| 5c067661f4 | |||
| 226da976dc | |||
| ba17cc0a11 | |||
| 9e0afb7d8a | |||
| 9e7d50bc76 | |||
| d7d5687faa | |||
| 21eb098dd2 | |||
| 2f770f8bfb | |||
| f09698d845 | |||
| 3fbcb024a7 | |||
| b8c1c0e048 | |||
| 2d47242d54 | |||
| 66a7829eee | |||
| 9c67bd2550 | |||
| f67c5a2fd6 | |||
| 0437f6dd66 | |||
| 11b35d650d | |||
| b279e261a1 | |||
| 263402f80a | |||
| 920a83ec7a | |||
| 3c2ac3522c | |||
| 9fdaa637a8 | |||
| 81a9d7f2b9 | |||
| d8d3f05164 | |||
| 653be136ee | |||
| 398c356f22 | |||
| 542b76f687 | |||
| abb8a1914a | |||
| 163d335078 | |||
| 0582836820 | |||
| bb15776ae6 | |||
| dde9d4c9eb | |||
| 1ef75be1c6 | |||
| 3582783972 | |||
| 5070d52f2f | |||
| 7b07ed6580 | |||
| f6a2b6252a | |||
| a9b03de99a | |||
| f1e3b979a7 | |||
| a7f7058636 | |||
| 8ce9b026e9 | |||
| 0dcf2f1bc8 | |||
| 99922feb3b | |||
| 2fd1dca905 | |||
| e3cf718998 | |||
| 7157917a16 | |||
| 3266aae1c3 | |||
| 63194a37f6 | |||
| cabe94552a | |||
| 48a229a0cd | |||
| e4db86836b | |||
| 913a85c571 | |||
| ed4f6fc4b3 | |||
| 9da422f1c5 | |||
| ab1739ba34 | |||
| fc1430aa92 | |||
| 3cde608eda | |||
| 2898552f4b | |||
| 911c148c71 | |||
| 91568a173a | |||
| e57f5499a1 | |||
| 9abb7b71a9 | |||
| 724c354d62 | |||
| c44779094d | |||
| eeedab4091 | |||
| 8559e20237 | |||
| 26730eb083 | |||
| 4160ce674d | |||
| 2dbeea21c4 | |||
| a2b8485a89 | |||
| 5bb74ee61c | |||
| 462fde5e7d | |||
| f1e83a57cd | |||
| cc9a9fb390 | |||
| 8fbcceb742 | |||
| d3a251e6d9 | |||
| be80b26c18 | |||
| 1574b7d834 | |||
| 51e10e344d | |||
| 0d55d8c5b0 | |||
| 1392589d36 | |||
| 548a324256 | |||
| a8a0bc356a | |||
| faee1d5a8d | |||
| 3088dac33b | |||
| 2641062c17 | |||
| 95c738ea28 | |||
| 562d2f67a6 | |||
| ba6aff4a1b | |||
| bb23e3940e | |||
| 94e4370c7e | |||
| 38d28c3f4a | |||
| f60b424d70 | |||
| a1a91d5ef4 | |||
| bfb48b5dde | |||
| 2860813a8e | |||
| 72538e350d | |||
| 59f3d1445f | |||
| 0b88cf1d03 | |||
| 56e2ba29d0 | |||
| 6ec9b84674 | |||
| afd15392b1 | |||
| ae4cc94a9d | |||
| 3f9b75b7b3 | |||
| ec2b097313 | |||
| caaab462bc | |||
| da413b823b | |||
| 14937e7dd2 | |||
| 3418497f3d | |||
| e408f1061a | |||
| c08fe4e2c5 | |||
| b7e21984a1 | |||
| 7fba8cf759 | |||
| 0296c23685 | |||
| 1cdfef4d6a | |||
| cead20ec91 | |||
| 74dd051d51 | |||
| 5473285010 | |||
| 4b3adfa21c | |||
| 7c37301c91 | |||
| d9040f8038 | |||
| f41606c0b0 | |||
| 31d9750579 | |||
| 173fb97832 | |||
| 81248c3f56 | |||
| 2914a0a0a5 | |||
| 815588daba | |||
| 6152eb6d6d | |||
| ff0ebc196c | |||
| 4e8c8d7e2c | |||
| b8a90b7eaa | |||
| 60e7ca4a4c | |||
| 3a3c8ec6b8 | |||
| 05c37e58c1 | |||
| d203dd4770 | |||
| 29ccf10d0b | |||
| b49df09fec | |||
| ce3e117976 | |||
| 309795198d | |||
| 7db00132b2 | |||
| 76a2862b7a | |||
| 215503b4f7 | |||
| 54d4010f1a | |||
| cb1b53cfb5 | |||
| 96e8f94833 | |||
| 1e54a3e801 | |||
| fe9c2b9857 | |||
| 2a2177e7fa | |||
| d1d565e58b | |||
| 891ff383ec | |||
| d322ebd0b9 | |||
| 50190236bb | |||
| d5a0f91cb4 | |||
| 467c1b26fb | |||
| 3cabecda04 | |||
| 6d3160b0ab | |||
| d328e0fb75 | |||
| 5f01afb7ea | |||
| 6fe2fa5ff0 | |||
| b371b1fe34 | |||
| 90c0a39df8 | |||
| 70c5a5dff1 | |||
| da0b7cc7f2 | |||
| 139e9b144e | |||
| 77c0a19451 | |||
| 58cbd19742 | |||
| 9bf6917ae8 | |||
| 897cca0a82 | |||
| 6af09c61be | |||
| c3c7798446 | |||
| 06dc91fadf | |||
| 526cab538a | |||
| 81d19a00aa | |||
| ca755ec9e0 | |||
| 4f6206cb2d | |||
| 7fb53ec954 | |||
| d8b5070ca8 | |||
| 5e99d38412 | |||
| f8a84d4436 | |||
| 3990014073 | |||
| 2b5a735091 | |||
| 3e51206a6b | |||
| 7569b75d61 | |||
| 8fcabac518 | |||
| abb0cfde72 | |||
| 7990ffcc60 | |||
| 49910a1d85 | |||
| 46a143e80e | |||
| 69b7f26e4c | |||
| 5b37d0356c | |||
| 1188ebbb7b | |||
| 76b903b2e0 | |||
| be38c2111f | |||
| 1de787fab8 | |||
| 81f683a61c | |||
| db6f68d031 | |||
| d0a1c805e9 | |||
| 00a654845f | |||
| 04dad8485a | |||
| 0b1475169f | |||
| 6ec4fbc82b | |||
| 18cc7a663b | |||
| cf5febad47 | |||
| 42849af5a8 | |||
| e6364407a9 | |||
| 480b78f2c8 | |||
| fa8f339478 | |||
| 7776839c82 | |||
| 7114cacb85 | |||
| e52be3d83e | |||
| c9cf01e0b6 | |||
| dcbf68e104 | |||
| c2d8c07137 | |||
| a4ed50ca85 | |||
| b3788c8ea0 | |||
| 946c074a41 | |||
| 19f79afb0f | |||
| af3b6f9c83 | |||
| fbe42c156d | |||
| a1f6cbd354 | |||
| a4f052ad31 | |||
| ea87bcefd6 | |||
| 0655991a19 | |||
| f368d2278f | |||
| 1eb6db6ca8 | |||
| a25b63e2df | |||
| ffe7a2fcd7 | |||
| 08b5a7908f | |||
| a8cd9d0154 | |||
| 297240facf | |||
| a022b0cfff | |||
| 72026db599 | |||
| aafc96f58f | |||
| 7c7e8648ff | |||
| 24e2ce0764 | |||
| d7cb4d407b | |||
| 66a506e72b | |||
| 25a7b0a6f8 | |||
| 7aaa1dd8a3 | |||
| 2a6f164923 | |||
| 0f28626bb4 | |||
| 6ed22d0885 | |||
| 6715b91a6c | |||
| 694da60659 | |||
| 47fa4b0a2c | |||
| 8ab6b60778 | |||
| e1a4f81e50 | |||
| 7b7e35d339 | |||
| 3176629410 | |||
| e3ccc45d19 | |||
| beec9e834e | |||
| f6f0486ff9 | |||
| dd175c5431 | |||
| 518f446d31 | |||
| fbbd510088 | |||
| e440d30028 | |||
| 44d30c83bf | |||
| 7ff7b55732 | |||
| 44346b3a5a | |||
| 23a538d61a | |||
| dcb5026f33 | |||
| 778ff9daa9 | |||
| ce9dc809bc | |||
| 59370588dd | |||
| 7d434aa9c4 | |||
| 59ce7c0424 | |||
| 9a0e5a7c18 | |||
| 8d0019595f | |||
| 6ff74cfcab | |||
| aa50ef4069 | |||
| fa0101bd60 | |||
| 21f5b16e47 | |||
| 223a835f33 | |||
| 223e14b0d0 | |||
| a58f69be04 | |||
| e194eb1f69 | |||
| 672824641b | |||
| 6d357211b2 | |||
| 8e39e2889d | |||
| a9ee4bb9f1 | |||
| 80fd6c2400 | |||
| 3cbe7d40d1 | |||
| af0bc95de5 | |||
| 4bf3e7485b | |||
| b701de60ce | |||
| 7ef2743964 | |||
| a165838cbd | |||
| 3c77b8388c | |||
| 9d16f4545d | |||
| d57e6808cc | |||
| b71cc8a580 | |||
| ac3b03881a | |||
| b0d03d1f1c | |||
| a2dcffcca2 | |||
| 9323f0faf8 | |||
| f343c8ba36 | |||
| 502bee9a09 | |||
| 379e2119a8 | |||
| 89a29946f9 | |||
| 20a94fafa7 | |||
| 99ddf1e4ab | |||
| fb778218f5 | |||
| 55fc3cb2c5 | |||
| b779e22205 | |||
| bb5b1f8f01 | |||
| c1a96d4900 | |||
| de298da532 | |||
| 6f5ca53f99 | |||
| d507126101 | |||
| 9a25df01fe | |||
| 6e960f1972 | |||
| 11b9212948 | |||
| b4e2914b70 | |||
| 09b7348595 | |||
| d2bb6e0c0a | |||
| 8632a03662 | |||
| e71c78ae84 | |||
| 03a8027efc | |||
| b7e186b370 | |||
| 4a69f3987f | |||
| 343dc486e0 | |||
| f5b7e8addd | |||
| 5aacfd1639 | |||
| 06e63aedea | |||
| 0320194757 | |||
| 1753771356 | |||
| bc794e7c15 | |||
| eefcecc7ce | |||
| 3795a786c9 | |||
| 855a1bef89 | |||
| 6a67921e40 | |||
| 8709fec517 | |||
| 48245effdf | |||
| 16063933d1 | |||
| d317f197be | |||
| 8ac862f50a | |||
| 0e996c4664 | |||
| 287cfee73c | |||
| a6c465e929 | |||
| becb5ab1dc | |||
| 49170bf2d8 | |||
| b1205db7ac | |||
| ff0cd413e6 | |||
| 7a56e4a0e5 | |||
| d17608d0a0 | |||
| 0af216fea0 | |||
| 1287433a99 | |||
| 56a9964101 | |||
| 532b4383bf | |||
| f9e2623fdc | |||
| eacae83886 | |||
| 5fc53f59c7 | |||
| 7035ea3ab7 | |||
| d67c0a1eda | |||
| 36c6a1955f | |||
| f792989d9b | |||
| ee398f17e1 | |||
| 8c4723ff43 | |||
| 01ae866d58 | |||
| 3b8ae33fe3 | |||
| 6f63909c65 | |||
| 1612baca92 | |||
| 4970bd7f65 | |||
| a775dd2b79 | |||
| 137894348b | |||
| ac40b27c79 | |||
| 9d756525ce | |||
| 6361172bea | |||
| 56b6383407 | |||
| 46fa5a374b | |||
| 82621f250c | |||
| 7373d2eb3c | |||
| 4453236949 | |||
| c2dc4a8e06 | |||
| 92a23da3ec | |||
| 242db26343 | |||
| 87701339fe | |||
| 4669ce0766 | |||
| 9bb5988b4e | |||
| c513171014 | |||
| da5010d37a | |||
| e6b78e5d56 | |||
| 410d700ae3 | |||
| fc173bf679 | |||
| 72154aa668 | |||
| 31b5156191 | |||
| ebce5d07ac | |||
| 915e1ac7de | |||
| b78bfc0a43 | |||
| 30436741a7 | |||
| 98734375f2 | |||
| 37816e3818 | |||
| 4bc2b3f369 | |||
| 00be2bf18d | |||
| 44290a66b7 | |||
| f6cc344623 | |||
| a89d487510 | |||
| a0ec4467fd | |||
| 7dddc0de9e | |||
| e7280f1eb5 | |||
| bf7fcc612d | |||
| cff9bbc9c5 | |||
| fddca3d2d6 | |||
| 9db49fb45e | |||
| 891409aedf | |||
| 77e47066ed | |||
| 852759f904 | |||
| 1dbc310c9b | |||
| ed45c43033 | |||
| 86ca58e2a9 | |||
| 22280db5db | |||
| 8e060e23e3 | |||
| 6e07742fe9 | |||
| 04d5032055 | |||
| 73ae87fad1 | |||
| cd05282369 | |||
| ee94d53bda | |||
| 922e1407c2 | |||
| 2ea22b1850 | |||
| 2c1323ece6 | |||
| adb7fb43cb | |||
| d59fd9c22d | |||
| 6f743f3138 | |||
| 5a7fad0bcd | |||
| 5d2414dfa9 | |||
| bef2425025 | |||
| e8b4286c93 | |||
| 2e9bf0b67c | |||
| 935c273c8f | |||
| b993b41847 | |||
| 1be40cc4fa | |||
| d628b731d1 | |||
| 21e116aa45 | |||
| d77d8ff803 | |||
| 31f64186ae | |||
| 1a703efa78 | |||
| 8b7b0a03eb | |||
| 0761d804a4 | |||
| 3ad42d9279 | |||
| bd41e21c26 | |||
| 10fe23b8f2 | |||
| 39899e40bf | |||
| 5d337bb24f | |||
| dd5909568f | |||
| 38166e976f | |||
| d6a7ffe0d4 | |||
| 2ebc6996a2 | |||
| 2e840134d2 | |||
| 66e1be33cf | |||
| 591959261c | |||
| 459930df09 | |||
| 674fc566bb | |||
| 09832abe50 | |||
| eabd2fc936 | |||
| 6720906ee5 | |||
| abb96802cb | |||
| 29fa05ae05 | |||
| 7b43ba809b | |||
| 49387f9494 | |||
| 953482de53 | |||
| 8cf3a7aeda | |||
| 175f65aabc | |||
| b8c5cf1142 | |||
| 236f121c4e | |||
| 94a392144b | |||
| 2467678bd4 | |||
| e87c1abd4e | |||
| dffc34559b | |||
| 80f2a9a6bf | |||
| 4aa6ecb122 | |||
| ccfcdf7f48 | |||
| 4eb23a38b1 | |||
| cb38213444 | |||
| 842b6111db | |||
| ea54525a33 | |||
| 893cc025f9 | |||
| b81c8d2e1b | |||
| 4b07535e86 | |||
| 0d2fe320a7 | |||
| f294113d01 | |||
| e9063c639a | |||
| 8d6dedc15b | |||
| 1bc4c1a8ac | |||
| 1c7af1a72e | |||
| e61f424ade | |||
| 6d3aae32bc | |||
| fa1cfd94d0 | |||
| 0155b6f841 | |||
| f6953624dd | |||
| 1a5f524ae4 | |||
| a4cd4cc253 | |||
| 1a35c440e8 | |||
| 2c6c84ac61 | |||
| bd666daf82 | |||
| ca3831c4f5 | |||
| bbe0d34f43 | |||
| dd364c962f | |||
| 50068b0b0f | |||
| c49453c519 | |||
| 52c7804f32 | |||
| 19b4f3bfb4 | |||
| f3ac421266 | |||
| 7533a61203 | |||
| 13f9702a41 | |||
| 6355a7019b | |||
| 490464e170 | |||
| 467d338fe4 | |||
| 6130578d18 | |||
| 4389bb037d | |||
| 2eb8a9ef56 | |||
| 393798098c | |||
| 668eb7c398 | |||
| 0937f85534 | |||
| cf64376dca | |||
| 5a98af622d | |||
| 4f5d0b46f7 | |||
| 492e92d65d | |||
| 181939c841 | |||
| b678b4e048 | |||
| 1934b3a5b6 | |||
| cc1d122352 | |||
| a4f0b85462 | |||
| 7b4e1e9055 | |||
| 4c3cd4c9e3 | |||
| 46e913dc23 | |||
| 8f580b13df | |||
| a551686d37 | |||
| 432c78079b | |||
| f5f0e46016 | |||
| b6f32b6e45 | |||
| 66f480519b | |||
| 8044522691 | |||
| c6a67bd203 | |||
| c6881b6d02 | |||
| 4489bec6ef | |||
| 3d71e68696 | |||
| 783d2da4a8 | |||
| 6be4b49999 | |||
| 68185dd93c | |||
| d01ea9d6fb | |||
| d91e6023eb | |||
| 17ed01a0c9 | |||
| 4b6c2d0d3d | |||
| 46c07bb207 | |||
| eaa805b9f0 | |||
| 436fd0b88e | |||
| f706d3c393 | |||
| c58eb1d47a | |||
| b4f9a55e6e | |||
| 1d17891286 | |||
| 6a3f3f5577 | |||
| 29913dd1e4 | |||
| 690837dbe5 | |||
| 82e80a479a | |||
| bc508aee7b | |||
| 95247f7740 | |||
| e5731229c7 | |||
| 52c74ad866 | |||
| a28f890e83 | |||
| 31362dfc17 | |||
| a492cfba13 | |||
| 96afcd90e3 | |||
| ea61f8f597 | |||
| 894ccd18ff | |||
| 9dec6f1324 | |||
| 2e44473ce4 | |||
| 26d6969384 | |||
| 2dbde224d9 | |||
| 8d7ed9f8bf | |||
| aba2cc4db2 | |||
| 2df001fe5c | |||
| a49b8a2608 | |||
| bea272c40b | |||
| a455e32adf | |||
| 9d522bd626 | |||
| 0427396f50 | |||
| c952468e13 | |||
| 94b3ce44e6 | |||
| c439c543d0 | |||
| 78120bd989 | |||
| f66c1c3c9c | |||
| 6f82d83bd6 | |||
| 3e218b146e | |||
| 17517bcc3d | |||
| d8fba47870 | |||
| e9c5261a49 | |||
| 8d53175c20 | |||
| ba5231dc89 | |||
| 032365d57c | |||
| e9aed494f8 | |||
| 16c3d39fd2 | |||
| 1875f7287e | |||
| d619031f68 | |||
| 4ef759dba8 | |||
| 0d16c8eab4 | |||
| de7d176edf | |||
| d37ed65f42 | |||
| 710ddf7906 | |||
| 3abb80885e | |||
| fd962c5e99 | |||
| 07f944bf48 | |||
| 012423338e | |||
| 64cfebc63c | |||
| 28d74f5d9b | |||
| 8418fae82b | |||
| 9b1bebc9b2 | |||
| 8d888bb756 | |||
| 83c29e1945 | |||
| 09ebc33b30 | |||
| ff9bfae722 | |||
| 3b146eda0d | |||
| a8ffde6f21 | |||
| dd9a4e044a | |||
| b8c72ade4c | |||
| 5dd55d3811 | |||
| f00b133eee | |||
| a117b0c723 | |||
| 65aaa607ab | |||
| 9259425a9a | |||
| 6816e2436b | |||
| c8b6e6fd9b | |||
| ac2343ea57 | |||
| a6a9af4f02 | |||
| 35dc173c80 | |||
| a686be8ba4 | |||
| d61b03701c | |||
| 81d9857888 | |||
| cd9e142db3 | |||
| 8682a33ab1 | |||
| 0631e4395a | |||
| d78425eab4 | |||
| 0b03a640fb | |||
| 9d277ac2ac | |||
| d884768344 | |||
| 54c1ffe5f3 | |||
| e11302172e | |||
| bf353a42cd | |||
| d8e19b776e | |||
| cf96bb464f | |||
| 3c7164846d | |||
| 4fa4668ed6 | |||
| 0ce21aea08 | |||
| 6f2de31146 | |||
| e1ac740ac4 | |||
| 4feeaf1641 | |||
| a08bbabd4d | |||
| a7a9d7d85c | |||
| e93c766c42 | |||
| 5d4bfdabd6 | |||
| 39c16d1cc4 | |||
| eb55d19786 | |||
| ae36fada6b | |||
| 60ca7784ba | |||
| 7e8db13854 | |||
| 3e7d0ec14f | |||
| 5971c00a4f | |||
| 8ff7531f89 | |||
| f59e1ad854 | |||
| 1a0a8a1655 | |||
| 24023ff9e8 | |||
| 016f799983 | |||
| fae68a5396 | |||
| 79680b1d5e | |||
| 0ce45c20b8 | |||
| fed374fcb6 | |||
| 374202ac45 | |||
| 56db1d3dfa | |||
| d4796261d7 | |||
| 452c5d5e91 | |||
| cc5f93e717 | |||
| 49601a63c8 | |||
| 6c33188af3 | |||
| 1353e3916f | |||
| 11d4986517 | |||
| e267bf3e09 | |||
| 39c5c8c1d1 | |||
| 6cce073da5 | |||
| 99372c69e5 | |||
| 042b703fe4 | |||
| 1880284bde | |||
| 6c1faa4bdb | |||
| 33f97d7d8f | |||
| 82e033942e | |||
| 693e1c93f1 | |||
| 2919b76947 | |||
| 42b94561a2 | |||
| acaf134dfe | |||
| ab9109e0dc | |||
| f88b2c11fe | |||
| d5d330413b | |||
| 61d7e11001 | |||
| c4c6df179b | |||
| b04b7bf357 | |||
| 97b1c66d4a | |||
| 74a210f198 | |||
| 31861052e5 | |||
| 7c42b5cb17 | |||
| df7fea4412 | |||
| 5fa8b42fac | |||
| d3fa67fe2e | |||
| 357089a438 | |||
| 8b3d75b339 | |||
| f741066466 | |||
| 1e45111bde | |||
| 9595687bce | |||
| 7427b9de35 | |||
| acdddc0b79 | |||
| 01c70caa8f | |||
| ff4bab4c07 | |||
| 5c36029274 | |||
| cd54186113 | |||
| 353689857e | |||
| d74377350b | |||
| 6c0a973ac3 | |||
| 3ca46c29c3 | |||
| 837fde70ae | |||
| 1e52cc474f | |||
| 76807006be | |||
| 0d35fe0f21 | |||
| 370b0fc5da | |||
| 25b3c09f6a | |||
| 576c365753 | |||
| bc2ed60b92 | |||
| a4385100c4 | |||
| 4c01709cdf | |||
| 1df924f4f8 | |||
| 400bfe9251 | |||
| 6e1d364d60 | |||
| 504ad86648 | |||
| 096b2d73cd | |||
| 5754d31d0f | |||
| ed3ed1f90a | |||
| adb1227b2e | |||
| ac190b2e39 | |||
| 18ae87962d | |||
| 4673862981 | |||
| 212d7257e9 | |||
| 9df8d0848a | |||
| ded7abb1f6 | |||
| 44d5a61cfe | |||
| 0db80710aa | |||
| 7c47eff112 | |||
| d81849ab3b | |||
| dbb3f80995 | |||
| 80b9a3e00b | |||
| be5e5d837b | |||
| ffc4a60bc6 | |||
| 1edfa4474f | |||
| 18e70f4e79 | |||
| 62a81cfdd1 | |||
| 6365a026c1 | |||
| 7c89193398 | |||
| 4a6f1718b8 | |||
| 7b3fa8da60 | |||
| 2fae7ccf5c | |||
| f36f48c2cf | |||
| 8b726c7e8b | |||
| 8eb0687407 | |||
| 9b9912ba9e | |||
| 22d0ed8225 | |||
| b7a58d2f87 | |||
| 219ece22fc | |||
| 4874301615 | |||
| 1827dda0c6 | |||
| d088b01f75 | |||
| 00c5062eab | |||
| 84eacde63a | |||
| d98290c17f | |||
| 6d94a3be05 | |||
| a2833d18ed | |||
| 6f95afdc59 | |||
| aaa75a32a5 | |||
| c300015ac2 | |||
| eb4f5e9faa | |||
| c7aec839ae | |||
| 37aa89b19d | |||
| 3f94e70488 | |||
| 435afa0eea | |||
| fb82a5e086 | |||
| 71d98c2f26 | |||
| 4a97aa12d6 | |||
| c9e67fb460 | |||
| cec87be4e3 | |||
| eb1a234a77 | |||
| 394c2b67d6 | |||
| aeb3af7105 | |||
| e5cf99e31c | |||
| 8120535a35 | |||
| ba01433381 | |||
| 6b99cdb83a | |||
| e7b71f8743 | |||
| 03935b2d64 | |||
| 2b89f33765 | |||
| 9c82a4ca60 | |||
| 7eb9b9b1c6 | |||
| 69b35b2ede | |||
| e8016abd97 | |||
| e54036be25 | |||
| 1dc894087c | |||
| b014967550 | |||
| 490962ccdb | |||
| d10e81fb3d | |||
| 33a87f54bb | |||
| 2cdfa59fbe | |||
| bebe74fa4a | |||
| 4098f97735 | |||
| a0b7ac402d | |||
| 0ca4482977 | |||
| 5cf15db6e4 | |||
| 6379d50f07 | |||
| 5a2792ae24 | |||
| 9b26f3cd84 | |||
| 316be5ee34 | |||
| f208e6f0b6 | |||
| 7c8c131e1a | |||
| 81e71d7275 | |||
| fc6c4b8e16 | |||
| 2280566bca | |||
| 33173e76a0 | |||
| cc0b9e5088 | |||
| ecc72d7693 | |||
| 4ab4aeacb0 | |||
| 348d12bd60 | |||
| 7b686c1103 | |||
| 403583cfbb | |||
| 29bff06cd6 | |||
| 2b80057ac9 | |||
| c3625e16d7 | |||
| f4642e9e66 | |||
| 5bdf4c6143 | |||
| 1250850492 | |||
| 80aaf6a065 | |||
| 3025caf932 | |||
| 95cfc50fbd | |||
| ded0925155 | |||
| ebfef15fb0 | |||
| 931408037c | |||
| ad418abf91 | |||
| c7d51a26f6 | |||
| 2c01cc000e | |||
| 3318651565 | |||
| 2ab07f3aac | |||
| 1340e54327 | |||
| 1b6e4645b1 | |||
| a6a573f5dc | |||
| 415415b5b2 | |||
| 175769b53e | |||
| 22f193f042 | |||
| 38e9b92c42 | |||
| 345d727936 | |||
| 86e8e5199e | |||
| 739979a116 | |||
| 7cbc81adca | |||
| 737d0fa23c | |||
| ab1962934d | |||
| 192455702d | |||
| cb0d739daf | |||
| a937fcc477 | |||
| 9503e60444 | |||
| 9f2dc4554d | |||
| 55da600433 | |||
| 96b5c2ae00 | |||
| 3008dc76d3 | |||
| 596c4b77e8 | |||
| 05a31c2686 | |||
| 45535c0f5a | |||
| 4bd0dd2123 | |||
| 9b2a643626 | |||
| 1ebc9a9a88 | |||
| f0c8b7ce40 | |||
| ec54550f21 | |||
| 4474d200b0 | |||
| b24a9e57fd | |||
| 07722dc33d | |||
| f062e35641 | |||
| 321ef9816c | |||
| be01e925c7 | |||
| 6d11006b54 | |||
| ed792b97c0 | |||
| a4b8c2298a | |||
| e5b33ce9f6 | |||
| 30374d46c9 | |||
| 9edf8233f7 | |||
| bd4a14519c | |||
| f12bf8c09a | |||
| 53cc45a0d5 | |||
| fa4b4dece1 | |||
| 02f044a2a1 | |||
| f39f816a98 | |||
| bc5b95be8a | |||
| 431d51f5c4 | |||
| 45c1357bab | |||
| 845f31b98f | |||
| 5136675fae | |||
| 89b6c32cee | |||
| 6ee36fe361 | |||
| db4f23f377 | |||
| 77572d0aee | |||
| 6ff02761a9 | |||
| d46f267663 | |||
| 6a98534d5d | |||
| eeb5d99942 | |||
| dcc5f333c1 | |||
| ff8a66d22f | |||
| f7ad97918a | |||
| 37b79735bf | |||
| 9d9ad6de88 | |||
| 20b925abec | |||
| 9047d56aa0 | |||
| 945ddc2403 | |||
| 92158b0611 | |||
| bed6155c79 | |||
| f2459e61dd | |||
| 69dcdafb3d | |||
| 6dbb072d11 | |||
| dc96849718 | |||
| d7a3cc505c | |||
| c6936ced6c | |||
| 91048f655c | |||
| 66bf524394 | |||
| ba9448bdd7 | |||
| c5cb5cba18 | |||
| aa853ac833 | |||
| 8b759d0e1e | |||
| a8a2192cf9 | |||
| 37f866b47f | |||
| 9cf653d673 | |||
| 0167b4b4a3 | |||
| dcb773e446 | |||
| b1a86fbc98 | |||
| cdd0cf7f18 | |||
| 9ae419201d | |||
| e37cefdbee | |||
| ad8c266f76 | |||
| 807c3bdcc7 | |||
| 62efbd17de | |||
| d99350fd61 | |||
| 40c70a9a2a | |||
| 8fb7f40a6a | |||
| 2f12d41d9d | |||
| 9fbdb6b305 | |||
| 73285cadb6 | |||
| 7d00722bbf | |||
| 4ea600d34e | |||
| 56f1c295b6 | |||
| 7f76ed8413 | |||
| f7edd36931 | |||
| c39f2b7c05 | |||
| 342036408e | |||
| f4904fce17 | |||
| 2abb2de753 | |||
| b61d7c2428 | |||
| bcc5d7c00f | |||
| ef0a0db07e | |||
| a45795efec | |||
| 88ae353aef | |||
| 97b9690711 | |||
| 48ce356d5c | |||
| 92451f94bc | |||
| 593632045d | |||
| e3c55ef307 | |||
| edf1730fd2 | |||
| 34cd8e3f95 | |||
| 242cce022a | |||
| 4a36cca703 | |||
| 19bf51cefb | |||
| 74a2e80142 | |||
| b9b630e3b6 | |||
| f0bdf833d1 | |||
| 81bc6bf34b | |||
| 7de736e8d0 | |||
| cd097e0fce | |||
| cc81a7ccfe | |||
| f83ae630c1 | |||
| 58d320c270 | |||
| 5894f35364 | |||
| c5acbf7e22 | |||
| 59565fd1d1 | |||
| 55592137a2 | |||
| 58523060f0 | |||
| 567aaf87c6 | |||
| e660d683a0 | |||
| 685306c386 | |||
| 5e04274d84 | |||
| 07d53be9fc | |||
| d4b0235a8b | |||
| 34aa41e17b | |||
| 3357fded14 | |||
| 618fc54ac2 | |||
| 339e058b64 | |||
| 102027a343 | |||
| 36f6a9347c | |||
| d49d386ef2 | |||
| 00c363829c | |||
| a9691dbdf4 | |||
| 9df701906f | |||
| 283671fa9d | |||
| 435c29755d | |||
| 686f91777c | |||
| 0d1df6bec3 | |||
| 925f60d9c3 | |||
| 8b3f5fda07 | |||
| 2aa028facb | |||
| b4bbd050c2 | |||
| 2a4fc28318 | |||
| 313485e406 | |||
| faf4267c73 | |||
| e6277d799f | |||
| cdbc8004fb | |||
| 1fac2f686d | |||
| 08c8d679ac | |||
| 48c34b7234 | |||
| ac17b2c584 | |||
| c67c861dc6 | |||
| 28603f0d2c | |||
| b2855f02fe | |||
| bef3d88076 | |||
| e1a8ea7dec | |||
| c4ad97136f | |||
| eab1d6782b | |||
| fd7b8ec77e | |||
| e28c991331 | |||
| a52811dfa3 | |||
| 9e210d705d | |||
| c42f1b53ab | |||
| d171173e90 | |||
| 679f0f9363 | |||
| 724c1e297f | |||
| 83154569b1 | |||
| e3c0fba34b | |||
| 2b6a6b91f3 | |||
| 09a555fdd2 | |||
| dc32f7f0a3 | |||
| 1efd8d6c75 | |||
| 898fc72313 | |||
| 21c5806cbf | |||
| 464e6bec95 | |||
| 2ae832d919 | |||
| 5b03c2d949 | |||
| f629a998a0 | |||
| fe88781bc8 | |||
| e725c97967 | |||
| 63caf22671 | |||
| 44790b1333 | |||
| b40bb64612 | |||
| 09ba9e6259 | |||
| 7775166477 | |||
| 7b5ab29a6d | |||
| 4fd614be09 | |||
| 73236e58c5 | |||
| 0e167f5c24 | |||
| a310a32371 | |||
| c00e26be81 | |||
| 32414853c6 | |||
| f3dc78d457 | |||
| a32ac62208 | |||
| d7a934cf0e | |||
| 503491392d | |||
| b3a2bf367b | |||
| c19eff4872 | |||
| 2941a813c2 | |||
| 0a022d38fa | |||
| 7ed3b3dd3a | |||
| ce52963d2b | |||
| 9a1922fdc6 | |||
| 967424a538 | |||
| 83131103cf | |||
| 9f4a0d3216 | |||
| c1591a5efd | |||
| 918ef4dff8 | |||
| 0d9a04c713 | |||
| 0c0c69f0cf | |||
| 943e80e26c | |||
| 058a327584 | |||
| 1eca4170f7 | |||
| c268e4ad1b | |||
| d4f81e8791 | |||
| 4f0680c3c8 | |||
| 8c26fe44c3 | |||
| 8c7d9f3dd2 | |||
| f241b7e79a | |||
| dc1f3503be | |||
| c2a5e180b8 | |||
| 7351217489 | |||
| aa42aafe33 | |||
| b2da0120d6 | |||
| 9e84e09c26 | |||
| 32c1a9bc45 | |||
| a7e95922c1 | |||
| 1392d0bc14 | |||
| 0f9fa9507e | |||
| 1087535d8f | |||
| 0e51f51979 | |||
| 90e0141ac5 | |||
| 8435a8678e | |||
| bd2888fc3b | |||
| ce1a5cd2ce | |||
| 5c8a28d717 | |||
| 6578ffe2c9 | |||
| 175340522f | |||
| afc917b582 | |||
| 9f4cd7716e | |||
| 29b0017445 | |||
| 910a7c619a | |||
| 273fac2028 | |||
| 59c5d984af | |||
| c885903ff2 | |||
| a323d85d32 | |||
| 491a33de0b | |||
| d6a0a44432 | |||
| 752533489a | |||
| e4e3c19e96 | |||
| 4ddb066728 | |||
| 958bbbc8cb | |||
| e15be5c2bf | |||
| cc436dc8cb | |||
| abbcd1f436 | |||
| db494f2afc | |||
| 985ea29940 | |||
| 76359da58e | |||
| 368cd44558 | |||
| cc1387ec0c | |||
| 7c79985a29 | |||
| 2b56961b54 | |||
| ff9920cbdc | |||
| 953a67bc3a | |||
| 29343aec3a | |||
| 2972472179 | |||
| 240e7b0835 | |||
| baf5191433 | |||
| 2645e87766 | |||
| ec8bc02d33 | |||
| 054bc970e2 | |||
| e4403ca396 | |||
| 04912ea888 | |||
| 103238066d | |||
| 7e4f08c033 | |||
| d47d82d8e1 | |||
| c1c41242bb | |||
| 9b9b44dd65 | |||
| dc5627a2ef | |||
| c1dfae1a6e | |||
| 7b5e4ab426 | |||
| 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 | |||
| 1ded36fcff | |||
| 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 | |||
| 26a44068d8 | |||
| 602b12dcf5 | |||
| a91a836224 | |||
| 969d7c802d | |||
| 4e196d408a | |||
| 8450ab8dab | |||
| 24c499d282 | |||
| 4581c57478 | |||
| f177924629 | |||
| 633e888ba7 | |||
| 4316992d95 | |||
| 5ecb8bdd8a | |||
| 168889d999 | |||
| e1339628d9 | |||
| 1ee190e844 | |||
| aadcfed17d | |||
| 8f99f6eb66 | |||
| a51b948f45 | |||
| 425f61cf34 | |||
| 87cc2d2313 | |||
| 0e2132ad3e | |||
| 7d9df5abc6 | |||
| 118cba4d9b | |||
| 3b81d4b8a5 | |||
| 6e3b3dc4e7 | |||
| 3b2adc9a3e | |||
| 8d421a62d2 | |||
| 185b0690c8 | |||
| bd0e97023e | |||
| 34ff0706a3 | |||
| acba61babb | |||
| f91191218b | |||
| 009b5bc72b | |||
| 05c79ac8c2 | |||
| 24d2a93c0d | |||
| 9b541a28e6 | |||
| de43080228 | |||
| b0cd7be39b | |||
| a7169a6348 | |||
| 6e126fb97e | |||
| 3533429563 | |||
| 22783d8f6c | |||
| 500230af51 | |||
| 1d710bdcd9 | |||
| 5feffba1ff | |||
| 6c56586a6b | |||
| a32b66c7c3 | |||
| 7e3c06191e | |||
| 372c96c6b2 | |||
| 4a2cbc1715 | |||
| 7073b8721a | |||
| 61f8fdd9e8 | |||
| fccb9c0bf4 | |||
| cfdca9f702 | |||
| 3d09090c4e | |||
| 596a49c112 | |||
| 95fc253d6b | |||
| e6d5372029 | |||
| 8e5c692244 | |||
| e694c664e5 | |||
| 8779f93746 | |||
| 1f0f5c1e23 | |||
| cbe24d0c61 | |||
| f9f12131ae | |||
| 50f0da6793 | |||
| 7e0106da0c | |||
| 0b7ab0a095 | |||
| aaf6bf3cd2 | |||
| fa95c82daf | |||
| 0da7142a59 | |||
| 446a938b06 | |||
| c8c4b090ef | |||
| 21c3994650 | |||
| f90f3a5ca6 | |||
| a34d2b72c0 | |||
| 9d617dcfec | |||
| 55506a0fc3 | |||
| 19c03f504f | |||
| e55ff6cccc | |||
| 30d0e5d298 | |||
| 985a3436e2 | |||
| 9dae87c80c | |||
| 46364a38c6 | |||
| 201ff39b80 | |||
| 148b2b9d02 | |||
| 64354b51c9 | |||
| 3cacb48f3c | |||
| f6a58151cb | |||
| d180bc794b | |||
| 3404393974 | |||
| eab5fd5bdd | |||
| 6965812d79 | |||
| 78fb7fe9f9 | |||
| 24bcf6a088 | |||
| e3ca797dad | |||
| f2db1c8ab2 | |||
| 36b8a75ede | |||
| 25d0a363a8 | |||
| 11b2815b88 | |||
| d7c8075862 | |||
| 0a42b85c06 | |||
| baf231e3b6 | |||
| e26e85b6d6 | |||
| b2193b23e5 | |||
| 2af3a92833 | |||
| d2af6dcf38 | |||
| dd6b66167e | |||
| 51a4a88a81 | |||
| 516efb21cf | |||
| 2d4397af53 | |||
| 06f319a380 | |||
| 37ed5a01e0 | |||
| 4a9997e449 | |||
| 54269553c8 | |||
| 041b97dd25 | |||
| 541d05df1b | |||
| 3d6ea23511 | |||
| 9b85a6fb7c | |||
| 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 | |||
| f407ff8861 | |||
| 7fe1fdd8c7 | |||
| a413b83c01 | |||
| 37cbe68204 | |||
| 81f4de965f | |||
| f76a66fc55 | |||
| 030b1f3467 | |||
| d7949aa58e | |||
| b7a180114e | |||
| f0c0c5483f | |||
| 4c9a26dbca | |||
| e611828249 | |||
| 7d444021bb | |||
| 388a29bbe2 | |||
| a03dd1bd41 | |||
| 4b366f2857 | |||
| c87faace6b | |||
| e2be051558 | |||
| 1e8b185377 | |||
| 031804827f | |||
| 6cccd9b6fc | |||
| 687fbb0a7e | |||
| 8f2db99c86 | |||
| 2c0f8dc546 | |||
| a388fb0bb7 | |||
| 27465353c1 | |||
| c2ccab4361 | |||
| bb876eac82 | |||
| 34c04babbe | |||
| e80a9b0075 | |||
| 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 | |||
| 9370f9cae4 | |||
| 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 | |||
| 604f2c9161 | |||
| eb29989dff | |||
| 4d9ca822a7 | |||
| de89d7a976 | |||
| d1f3d95c96 | |||
| 78ef42daa1 | |||
| 319abebd70 | |||
| 76480adda5 | |||
| e205f8afbb | |||
| 42dc51784e | |||
| a4a46f480d | |||
| e34be16237 | |||
| dfcc166918 | |||
| 895d56ed04 | |||
| 12eab4a8ba | |||
| 3eb2b1f7a2 | |||
| 6ecc9bf93a | |||
| 22e24fc387 | |||
| 516d88b072 | |||
| da4ebb6535 | |||
| efa0a06947 | |||
| 77457e91e9 | |||
| 6e4d33c741 | |||
| d3387e2a28 | |||
| 491452a19d | |||
| 7d3257b222 | |||
| 1836ef2884 | |||
| 43d6322d0f | |||
| f0684d83e9 | |||
| 3f3170818d | |||
| 7683096fe1 | |||
| bb438bfb17 | |||
| a11aa295de | |||
| 11eb241c8f | |||
| 049d92b525 | |||
| ebef239a06 | |||
| f9bd59f031 | |||
| 3d5507451b | |||
| 6d0d5bd566 | |||
| 98a13204b2 | |||
| 35d20a19bc | |||
| c318fdc94b | |||
| dab1c4cfc9 | |||
| 9a50f4ac1f | |||
| 59e829e595 | |||
| d0229b62da | |||
| f86946c6df | |||
| 37ad20a71b | |||
| e97f75cad5 | |||
| fcd6ebb06e | |||
| 2505f82ce5 | |||
| 78dca5fe8b | |||
| 8c816f64e4 | |||
| 22c525e3fe | |||
| f3f6b03d85 | |||
| 00bebc317e | |||
| 8f38e83aaf | |||
| ca9d3b597e | |||
| 0890b49e78 | |||
| 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 | |||
| dc9c86e3a1 | |||
| e1959afb6b | |||
| 6bc6ae2d28 | |||
| c68c78d412 | |||
| c8ac9721d7 | |||
| f8bedc55e5 | |||
| b72d31f87f | |||
| b1b68b58fe | |||
| 95e15c95f2 | |||
| ca21db9481 | |||
| 93ad803073 | |||
| 6cc7f70a65 | |||
| 9f871a3726 | |||
| e19e2c123e | |||
| cbe44e1fff | |||
| 2b0c33f74d | |||
| dae1d36a23 | |||
| 824fa8f17a | |||
| 31cd0b943c | |||
| f376c79f7f | |||
| 8e191c8e6b | |||
| 070eced2f6 | |||
| 986f8dfb2e | |||
| a98824b4cf | |||
| 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 | |||
| ca96b13233 | |||
| 525aac74aa | |||
| 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 | |||
| cf227509bf | |||
| c23a601cc6 | |||
| 860fbe48dd | |||
| 36d4c69fd6 | |||
| ca7e7fa0c4 | |||
| 1f6dd5dbb9 | |||
| db52646655 | |||
| 8bb18fa988 | |||
| f0edaf2f8c | |||
| d632e3aa1b | |||
| cc4f32f259 | |||
| 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 | |||
| 6d9ed8ad92 | |||
| 5042248260 | |||
| 1df6589533 | |||
| 3e1a562466 | |||
| f3bf4cf722 | |||
| 12f76b448c | |||
| f112ef34f6 | |||
| e4b57a978f | |||
| b51e09e0e8 | |||
| a3ba3f895c | |||
| 947a129e12 | |||
| f3fe6a6cbd | |||
| 9d150bef9f | |||
| 65be18cc93 | |||
| e27ea63900 | |||
| aa96f7b660 | |||
| 053690d885 | |||
| b9fc6397a3 | |||
| 19b9f15da3 | |||
| 0b9441e1a4 | |||
| 2324d7420c | |||
| c6b2ca8b19 | |||
| 83ea8dc577 | |||
| d898277f62 | |||
| a289cfb986 | |||
| d603998617 | |||
| 8bf9f4f5ab | |||
| 2c4e6f2926 | |||
| 21fbbc50cd | |||
| 99512418da | |||
| c2f2d8771f | |||
| 179c9ee8cc | |||
| eb8a505287 | |||
| a7482a3644 | |||
| a692348336 | |||
| 285dcc30cf | |||
| bcf51aed83 | |||
| 1a11ce6211 | |||
| 10021c97a6 | |||
| e869e3c534 | |||
| f6416285db | |||
| 7f0593cd2d | |||
| e4c41718d8 | |||
| 7234553990 | |||
| 5761efdb73 | |||
| 1133192a86 | |||
| af3288043a | |||
| 24a348f6a8 | |||
| 5528b6c231 | |||
| 245bd1eb17 | |||
| 03506db76c | |||
| cb5ef26020 | |||
| 2493ae4c2c | |||
| 2bd88344ad | |||
| 4950980d69 | |||
| 2361a0dd6e | |||
| 152cdd1750 | |||
| 31797a5831 | |||
| 9308c42cff | |||
| c096cd34b9 | |||
| 5fc0808f28 | |||
| e6866ee980 | |||
| 45631d30b0 | |||
| 0f432a0844 | |||
| df59bc7194 | |||
| ff4706e450 | |||
| fc013bd04c | |||
| 7e29a8b927 | |||
| 67ae7a0b6c | |||
| bd5a64bac0 | |||
| 1bd85d8baf | |||
| fe34b08ece | |||
| 33048f88b8 | |||
| 0ec01f4e78 | |||
| d0ebf06ff8 | |||
| 687b249034 | |||
| d1528dcff0 | |||
| 1c31cf6319 | |||
| 67f0c9bef0 | |||
| d54c366150 | |||
| 4ff535f883 | |||
| 58b15f9452 | |||
| dedca59ac9 | |||
| 3cbddfe545 | |||
| e3cae69495 | |||
| aee40316f8 | |||
| d754f9ae89 | |||
| 3c50b3a9e0 | |||
| fb312a71f7 | |||
| 136d79eaa3 | |||
| 834336499a | |||
| c9da8237df | |||
| 9638dcda0a | |||
| 756c5a2604 | |||
| a9c31652b6 | |||
| 32a76901a9 | |||
| a5e11c7489 | |||
| f2b12014e1 | |||
| 60fcaebfdb | |||
| 32fe2cb659 | |||
| 1207d54fdd | |||
| 3932884688 | |||
| 19a2042746 | |||
| 4c6eb137da | |||
| 57ec2ff915 | |||
| 77a161a087 | |||
| 0642402449 | |||
| 50d377d9fe | |||
| f5211b0697 | |||
| fd4ea46fd7 | |||
| 8d8546868d | |||
| 8ce547edeb | |||
| a17c48aed6 | |||
| d12db3e7b8 | |||
| 15b87ae297 | |||
| 02fdf59839 | |||
| d9da02b7a8 | |||
| 8f2ad6418d | |||
| ff984425a3 | |||
| ac1058359f | |||
| 9afbca3001 | |||
| ec3f17cb9c | |||
| 73b9d5c5f9 | |||
| ecc8591c95 | |||
| 696b67e4b1 | |||
| 266a5116a1 | |||
| 131f2be857 | |||
| be7b3a9952 | |||
| bb31b1785b | |||
| 2a60f4b1e9 | |||
| 33a4fb5a1a | |||
| aece6e8b6c | |||
| 7bf55dd14f | |||
| e158f17c2b | |||
| c5027d9478 | |||
| 36c1d82146 | |||
| bd4f404d45 | |||
| 43d39844f7 | |||
| e041a4d212 | |||
| 433b923ea7 | |||
| f8f1c72b44 | |||
| 542716e216 | |||
| b35958d024 | |||
| 9ee3541655 | |||
| bf7d84c12a | |||
| 34c691087e | |||
| 08c383012f | |||
| e2420495f3 | |||
| d530c5eda7 | |||
| ef7420ecf6 | |||
| c905a41e2a | |||
| 42ff4b5bf0 | |||
| 4fb74a32cc | |||
| c741465328 | |||
| fbca537a40 | |||
| 83420b0199 | |||
| 33d3ba1b45 | |||
| 497f85a236 | |||
| a624c302ab | |||
| cebe21a3af | |||
| 9eb679d70a | |||
| 6d84443db8 | |||
| da8a1f242c | |||
| 946d98b71f | |||
| dff51fc707 | |||
| 7d954dd5d1 | |||
| c6300a5da8 | |||
| 9359daa0d9 | |||
| 2322e9cff7 | |||
| a876e1e348 | |||
| 6a863c8f71 | |||
| 392b006b06 | |||
| 96289f42b7 | |||
| 1b69c2441c | |||
| 8ca85a4918 | |||
| 2a31031cbc | |||
| d148cd8ccc | |||
| d1cc1828b8 | |||
| 069e8cf122 | |||
| 45cbcaca6d | |||
| 102a2db1f3 | |||
| 9f81c85ca7 | |||
| ba4a6fc0c5 | |||
| aa803ce2ff | |||
| a027a60f5d | |||
| 270649535e | |||
| cf80ba71f4 | |||
| b74df18a4a | |||
| 5cd2906a39 | |||
| bc37b69d17 | |||
| 94f6e400ad | |||
| b95a6ccf80 | |||
| 7df9c1b6e4 | |||
| 75348c0158 | |||
| 75fb14acaf | |||
| 5350315b68 | |||
| 658e39c270 | |||
| ef7ce6c7e1 | |||
| 509e2411bf | |||
| 65c906f951 | |||
| 1f159e8233 | |||
| 936c76119d | |||
| f45865606a | |||
| cfc9776bae | |||
| e7db264803 | |||
| 0cb7ed9e4e | |||
| 4b07609458 | |||
| e41e58e781 | |||
| f5030f1c2c | |||
| 2a48fb8e87 | |||
| 3d8a71fdb2 | |||
| df6dbc5fa4 | |||
| 4b1d2839e8 | |||
| a892f80e86 | |||
| b2a79855ae | |||
| ff4974178a | |||
| d7100fd9bc | |||
| 0bfb40ae51 | |||
| 11c83670d6 | |||
| 68ff4f3842 | |||
| ab25cd09ed | |||
| 8f05b8f982 | |||
| 63ae2f64cf | |||
| 105103fae0 | |||
| 70f4792ab1 | |||
| defd9fa322 | |||
| e884d0fda6 | |||
| 5f6a8fdc20 | |||
| 196a9ddbb0 | |||
| 746140bd11 | |||
| 7b99a5fbac | |||
| b74c31e520 | |||
| 221f43e4bd | |||
| a17333d73e | |||
| 207b43499c | |||
| 0c0de17b38 | |||
| 77882e6086 | |||
| 19a9834843 | |||
| 23dab30ca5 | |||
| b5d7ce8ebe | |||
| bf4eb4b269 | |||
| a5edb6807e | |||
| ecadf30fe7 | |||
| 515f0db5b4 | |||
| c2f367cf70 | |||
| f21dfea965 | |||
| b84cad4db0 | |||
| 16ae019c8c | |||
| bd2051febd | |||
| 6fb1e03ed4 | |||
| 8d41a762b6 | |||
| 6d3003716c | |||
| 2c87c3bac3 | |||
| 739c525a98 | |||
| ab287ebf40 | |||
| ab6bcab78a | |||
| 6e317896e9 | |||
| b08ee3ff81 | |||
| 17fd09102e | |||
| a598cd2b18 | |||
| 55e434d67a | |||
| 04d4b5d8a0 | |||
| 7ea00bcb78 | |||
| 0ab56ffde8 | |||
| e7e945533e | |||
| 7fd1047832 | |||
| 19dfa88258 | |||
| 65923b5c20 | |||
| ac731aa50c | |||
| 2aa3182476 | |||
| 529c386943 | |||
| b659da8a4b | |||
| eba98717c9 | |||
| e4dba99cc0 | |||
| 454e688c3d | |||
| 54752deaa1 | |||
| a3cf37cb2e | |||
| 6459d11d32 | |||
| 15f2fabaaf | |||
| d6030b8d68 | |||
| e1757ee726 | |||
| 9fed75d59c | |||
| 5fe15475a4 | |||
| c7f6f4f48d | |||
| 747c6c2714 | |||
| 2951f128f6 | |||
| 53cb66eeaf | |||
| bcf8f798e2 | |||
| 7406176fad | |||
| 34ba5678c3 | |||
| da0b78c67a | |||
| 47e64ae503 | |||
| 520bb74626 | |||
| 4beef5cc66 | |||
| ba575f55ec | |||
| 2012ce02e8 | |||
| c67e2c2a5a | |||
| 4b1ce250c1 | |||
| 0401a07507 | |||
| c12265499a | |||
| 0e341832e0 | |||
| 489e2e6ad5 | |||
| 941f637bca | |||
| 6277c0595c | |||
| aa9eda1979 | |||
| 1d76efcbcd | |||
| 34c2c1ec16 | |||
| bf7fea9a0a | |||
| fdf15f3ca3 | |||
| 1cb5875b20 | |||
| 1a59a5478f | |||
| fc0cb704f2 | |||
| 960c0cbddf | |||
| 5750443371 | |||
| 9f67d86b30 | |||
| 66f7d83baa | |||
| b44e87c6e8 | |||
| 6da7f17c4a | |||
| b4f45d1e79 | |||
| 7d766bf7c7 | |||
| f9132cae85 | |||
| 75dc7e6671 | |||
| 3fd887fc57 | |||
| 128447a681 | |||
| 17149741a7 | |||
| 23bae932c7 | |||
| d2ec40bb67 | |||
| 9701998f82 | |||
| 9d06132743 | |||
| 0289c50ad9 | |||
| 51eea3f90b | |||
| 50490f5b26 | |||
| d12f802027 | |||
| ac7097b4d0 | |||
| 66087e4332 | |||
| 3706f9bcb8 | |||
| c505218896 | |||
| 6186a746e0 | |||
| 3e98bae5ec | |||
| 2e1e8f764e | |||
| a7492f8612 | |||
| 123b1f01e4 | |||
| 865f62e3eb | |||
| 3ea93f52ee | |||
| 27c70bdf07 | |||
| b53e545ebc | |||
| 157a4c891c | |||
| ad9ea07309 | |||
| fc483cdfc6 | |||
| 9033838cf2 | |||
| 8e5d2d5905 | |||
| 18aa66dabb | |||
| a2f7b78453 | |||
| 5c026cbe1d | |||
| dc51476897 | |||
| 39eaa577e0 | |||
| 1f006481ee | |||
| 60faabcbe2 | |||
| d3f1eaf1a3 | |||
| f568e76fd4 | |||
| e947223aaa | |||
| 8311162be3 | |||
| 75523556e8 | |||
| c82b5d4982 | |||
| 1c3158099c | |||
| bdbca75dfa | |||
| 124b189cc0 | |||
| de38b46392 | |||
| e1975644d6 | |||
| d9fd27a9e8 | |||
| 32425c5561 | |||
| 8d20923881 | |||
| 3a35b8b26c | |||
| 36c93b755a | |||
| ea8c3debea | |||
| b2425b2a25 | |||
| 49bc74e7a0 | |||
| 51c932164f | |||
| 19e82e93b1 | |||
| 3f785eaecf | |||
| e59c0f38d9 | |||
| 64004c6bc0 | |||
| 422332de7e | |||
| 5a15ba7451 | |||
| d3686bb1e2 | |||
| 3a6eeef580 | |||
| 1dc5c6b8a8 | |||
| 3532a560d8 | |||
| d2d894d808 | |||
| 2aa38bfc4b | |||
| 9c3cee9ae4 | |||
| fc521b5f9d | |||
| 1a4398cc55 | |||
| 5253368acc | |||
| 51cfc3d4be | |||
| 80bffd93e7 | |||
| df4f22e899 | |||
| 529d91fb9d | |||
| 8cc70843a5 | |||
| 70c841f23a | |||
| 108b4e2e10 | |||
| 7b22e09805 | |||
| c5838c143c | |||
| eaf71db7c9 | |||
| b322b527b3 | |||
| cc4b231875 | |||
| 05642a3e17 | |||
| 7dcc6bb579 | |||
| f15c416e59 | |||
| 6fa97eeec7 | |||
| 03bbf273b3 | |||
| cd0cce4195 | |||
| 9f1a72ec88 | |||
| 1e376cd3a6 | |||
| 49cf939c04 | |||
| 338394f8c3 | |||
| 575b62d77b | |||
| 57fc0eb5b1 | |||
| 3a19fe3663 | |||
| a10c621e33 | |||
| 0c049179b4 | |||
| e22c873ec4 | |||
| d644ebab09 | |||
| 2fdc578a88 | |||
| aeb3a3f7b5 | |||
| 044b7ce070 | |||
| 815e538f10 | |||
| 758233f001 | |||
| f4f4fda520 | |||
| 1d77aeb69c | |||
| 29dbfc647d | |||
| 55d9514e83 | |||
| 46bd7956a3 | |||
| e1ee394c26 | |||
| 19884ade99 | |||
| f0a88061db | |||
| 6057138466 | |||
| aaaa6556f3 | |||
| 4745431cda | |||
| 0455a948a9 | |||
| bf3e249237 | |||
| fb649e9525 | |||
| 9d1e2d9f46 | |||
| 9876d93b60 | |||
| e38fcbb566 | |||
| ed240145c7 | |||
| b3dd05580b | |||
| 32847f33fd | |||
| bff9723fe3 | |||
| d114648c16 | |||
| 44d0da02d0 | |||
| c25107eff3 | |||
| af5c36d2a8 | |||
| 0828a67145 | |||
| 617fb84983 | |||
| 6f8ac2b61c | |||
| 6f2b4b96cf | |||
| 3cc288a169 | |||
| 0bbbf3eb3b | |||
| 4b1b56fee8 | |||
| 154fc59e93 | |||
| 218c4c128c | |||
| 53f1af0cab | |||
| f9577a38dc | |||
| fadc7d9ba5 | |||
| 1e4b2133f6 | |||
| bfefa6d016 | |||
| 8b66472949 | |||
| 3b3aa94c4e | |||
| dc05275670 | |||
| 7921082ece | |||
| 13f82a06ee | |||
| efd6a29909 | |||
| 88c44b303d | |||
| 349223b3b8 | |||
| e7dbb8ccdc | |||
| fe2a743c8d | |||
| 2e2d479103 | |||
| 1f5c124ac4 | |||
| 64a5bc038a | |||
| 5d9396334c | |||
| 35f0e355bf | |||
| ec160f1f0a | |||
| bbaeca96eb | |||
| 3ab779895f | |||
| 203c7360e7 | |||
| a831f174ef | |||
| 153091f52f | |||
| f8abb8e541 | |||
| c8346d0581 | |||
| 35d3af5039 | |||
| 57e8cd6eab | |||
| fc123a71af | |||
| f14836cf02 | |||
| 4178feb65f | |||
| 2edaf22590 | |||
| acd3dab957 | |||
| 6bbd74adcd | |||
| 9bb928bb38 | |||
| c87a6c5969 | |||
| c482c13dcb | |||
| b87ed97402 | |||
| ee000dabfd | |||
| 4395711d26 | |||
| a73a011ee0 | |||
| 2a8e5e2c14 | |||
| 5d9a41f712 | |||
| ebcf4b60f6 | |||
| f976b78917 | |||
| 078790bd0f | |||
| b88c5a89a8 | |||
| 57028e3acc | |||
| c586a17926 | |||
| 9d078bac54 | |||
| 38eaefcabd | |||
| ba8cadc2f1 | |||
| 380d5dfa6d | |||
| 32af626630 | |||
| 8358fedaf4 | |||
| aba915037f | |||
| ec82b0c648 | |||
| 81a87f873f | |||
| d91b8ac444 | |||
| 952e51ac75 | |||
| 11267cd44f | |||
| 0e59e0aebd | |||
| ae1d3b3dd3 | |||
| 1a91dbee5f | |||
| e5bea35515 | |||
| fd507e3e41 | |||
| 9c1a67cf47 | |||
| 69e3824840 | |||
| fcb1a98129 | |||
| 6d942635af | |||
| cda2c5d459 | |||
| 969bb5a742 | |||
| 4bccc611c3 | |||
| d18c4ece0c | |||
| dfbb245b60 | |||
| 25c664b13a | |||
| 7c680c955f | |||
| 442e93d3fc | |||
| f037d1b6ca | |||
| 2c8b627008 | |||
| 221e3eddd5 | |||
| 74c39c677b | |||
| e6558832bf | |||
| 9c50625c55 | |||
| d372435e92 | |||
| a53facf709 | |||
| ffc39dfbcb | |||
| cba38b15a9 | |||
| 3450b5f80c | |||
| 5ac7564bfe | |||
| 53cd289b90 | |||
| 8dc13bcf1a | |||
| 261825a89b | |||
| f47a5a309d | |||
| a40f2b9fa0 | |||
| cfcd3892f7 | |||
| 7aaea6d005 | |||
| 703987f61c | |||
| e50a8917ec | |||
| 74d7c8e625 | |||
| a5d1383fe8 | |||
| bf2bcf515c | |||
| 4c5e94c64b | |||
| b4043216b6 | |||
| 4371014667 | |||
| fbb3222d29 | |||
| f76b5d8002 | |||
| 7e54868206 | |||
| 4ca3889bed | |||
| d84a8e6404 | |||
| eef1aebe8c | |||
| 4833b6085c | |||
| 2ceaca8828 | |||
| 48382c4b59 | |||
| d02158c0ef | |||
| 9a45f0b31c | |||
| 25c26e2f81 | |||
| 6213c4f2cd | |||
| e4837f14b1 | |||
| ce86131d12 | |||
| e6c9baf6ef | |||
| 8d6db7be31 | |||
| a2548b1fd0 | |||
| e4658bb99d | |||
| bf2e4a561a | |||
| 1816320124 | |||
| f09bfe293d | |||
| 7b4e8fda4b | |||
| c95812353f | |||
| cd34eea017 | |||
| b622ec7a28 | |||
| 7a0a702ec0 | |||
| d8fbe7b77f | |||
| dbcac37d91 | |||
| d4d391b34f | |||
| 571cf7d490 | |||
| e18b19ca5a | |||
| 48651bf482 | |||
| 5034a41c08 | |||
| 6795173e77 | |||
| 219ef996f5 | |||
| 00af1db275 | |||
| 5935ea896f | |||
| 459983c05e | |||
| ebf4f029ac | |||
| 0eec945df1 | |||
| 8824b9d68f | |||
| d2862814c5 | |||
| 25fece2d50 | |||
| beb4239d1b | |||
| 2b78e37d92 | |||
| a2070d9ce4 | |||
| 5827a686b8 | |||
| dec479532e | |||
| 5d173168cc | |||
| 2aac1cde04 | |||
| 3676f0268f | |||
| a7b75a54bb | |||
| 961a87b743 | |||
| e03d59e381 | |||
| d46ce5003c | |||
| d9ed8e125e | |||
| 4c4143d9be | |||
| 36708a5067 | |||
| 8bc7d259f4 | |||
| 2d047fa428 | |||
| 735d420d40 | |||
| 8c32955da1 | |||
| bc9fc1aece | |||
| c111ed4b20 | |||
| b88e3c99c1 | |||
| a3ea9427d1 | |||
| ce3e6e084c | |||
| 2a58ca7697 | |||
| af96f7a0cd | |||
| 7d39d1a925 | |||
| 1b6c700e18 | |||
| 6304bd60ee | |||
| 4ad4417740 | |||
| 6a4c259a73 | |||
| 12eabb220d | |||
| d68ce2d68c | |||
| 8e02c040eb | |||
| a7a317c284 | |||
| 9d6ef24660 | |||
| 14014408fb | |||
| b933e9666a | |||
| 7aff59bcce | |||
| 8e2760cb3d | |||
| 7a9fc6dbd3 | |||
| 75d0dc251e | |||
| 9a50c4d93f | |||
| 010d5a0192 | |||
| cf1594829a | |||
| 854d720ce0 | |||
| 2f43c74ece | |||
| b9817ac6b4 | |||
| 1e8da0d494 | |||
| c47be7b415 | |||
| d3f6cb860f | |||
| 83d25f09a3 | |||
| ed747a2d3d | |||
| 3a8ee4ce2e | |||
| 5ac01a3af4 | |||
| 46343f2f9e | |||
| 56ccb5b2ab | |||
| 9a946eed80 | |||
| 9c6cb0f630 | |||
| 1b066d6965 | |||
| 54c3caad53 | |||
| 9b5e8aaf83 | |||
| 5143c09bcf | |||
| 09b534b8a3 | |||
| 2496185629 | |||
| 34deb82aea | |||
| 8f72ae9da2 | |||
| b753f01ac1 | |||
| fd0a147ae6 | |||
| e94bd90782 | |||
| ce4b897d0e | |||
| a7694029e2 | |||
| 1e9110b763 | |||
| 6f3fbbbe49 | |||
| d346ec7bfe | |||
| 26a3613397 | |||
| e6318bddf3 | |||
| 514bb0beda | |||
| 41b1bd2f05 | |||
| bf40dadf04 | |||
| cb1678ebec | |||
| 0c1ac568b5 | |||
| 0f9550c747 | |||
| b13ae17a47 | |||
| f762a12d18 | |||
| 20d30a80be | |||
| 229b218203 | |||
| 4b668aaca8 | |||
| 8c7f1421c6 | |||
| d90b2c1d52 | |||
| 22f39be197 | |||
| 2fa45436c2 | |||
| cadbb6bbce | |||
| 2c89f04be7 | |||
| 597011e3a9 | |||
| 0d433b58ba | |||
| cde8ef56e5 | |||
| 110816c7aa | |||
| fbb1e168f7 | |||
| 23085eb5ae | |||
| 7344a6205f | |||
| 4b76ec40c0 | |||
| 90101d0269 | |||
| 7ac84c0660 | |||
| 2090530bbb | |||
| b6cb7ddbaf | |||
| 3422d9335c | |||
| e91f9a944e | |||
| e7ddc7cf0f | |||
| 40dfa48756 | |||
| 579f92cf5f | |||
| 4565125da9 | |||
| ce13a01e65 | |||
| 618a8682b7 | |||
| 963077f918 | |||
| 3704d2d86b | |||
| fc6a029311 | |||
| 7c7b1e6c2d | |||
| 892920039d | |||
| 51cdd38c3e | |||
| 80977bd4c0 | |||
| d8022f94ef | |||
| 1c43587d7d | |||
| b2ed32b118 | |||
| 0cc815d816 | |||
| d452b7593f | |||
| 5346bdc683 | |||
| e3911bacde | |||
| dc5c1e2002 | |||
| 2e48e298a2 | |||
| 7a1aaaf5c4 | |||
| bde92d5cfe | |||
| cd5d546078 | |||
| 97a9ca24f3 | |||
| 691f0f4845 | |||
| fdd458d2fe | |||
| d2c0b8374a | |||
| c96c78892d | |||
| 957643f523 | |||
| 749bbec566 | |||
| 25e363c5fb | |||
| febeed3277 | |||
| 9d07aa006d | |||
| 12d69e25dd | |||
| 0c9f1efc75 | |||
| 665b4506e7 | |||
| cb5548ceb8 | |||
| c9492e54f7 | |||
| 12490eafff | |||
| 6e83d11d5f | |||
| 9c6aedc91b | |||
| a9339d0627 | |||
| 4d9aa10532 | |||
| b00264b594 | |||
| e329c7015e | |||
| 1392cfc72d | |||
| 9c51cf50ad | |||
| c6688d8f89 | |||
| 87abea0ba3 | |||
| f9fcb44f3c | |||
| 996cbbca38 | |||
| 581f4b89bd | |||
| 88a347dce0 | |||
| 3e7b197a1d | |||
| 94ab06e92f | |||
| d38c81fcff | |||
| 3e26fdfb67 | |||
| e1be73232d | |||
| 190c61ba2f | |||
| 06fd2268d9 | |||
| 15251dfae1 | |||
| f62812a8dc | |||
| c6041d2590 | |||
| c7e779107c | |||
| 7cd25c919f | |||
| 47d67d3985 | |||
| 1a7921b46c | |||
| 43d569741b | |||
| 52c6869eab | |||
| 6dff9097a2 | |||
| 4ff211662a | |||
| 05eab51a0d | |||
| 604a4e7dbc | |||
| 80dca96ee8 | |||
| 7f97037190 | |||
| b658afd857 | |||
| 992ad97ad5 | |||
| 5af6cbae2c | |||
| 2abe792f36 | |||
| 1ff9bb8fdc | |||
| 5cb1039daf | |||
| 591c5dabf4 | |||
| 770fff287e | |||
| cea7a179ae | |||
| d80c40cfbf | |||
| 12e83374e9 | |||
| 98344d2e5e | |||
| 99dc1eec50 | |||
| 2a886576a6 | |||
| 919d005550 | |||
| 97abdaca5a | |||
| 9cc8b7c858 | |||
| 0726472b91 | |||
| 3cbe92d797 | |||
| 72a278c9ed | |||
| e567c8adce | |||
| dde8045109 | |||
| c922c4c383 | |||
| 5dab0e50aa | |||
| bc8907e90d | |||
| a8ba7786ae | |||
| c734e48ad0 | |||
| d30d0b29a9 | |||
| 2912defb97 | |||
| 69f8ac6b56 | |||
| e7441ff6e8 | |||
| 8a34158fa4 | |||
| 8d2a6d96f2 | |||
| bb50b677c7 | |||
| 0fde4b3b2e | |||
| ee9c109f07 | |||
| 1219423091 | |||
| cf00ab854f | |||
| c417dcb7e2 | |||
| 7ad711f554 | |||
| 2d7b0cf94d | |||
| d669c07e8a | |||
| 8bd52946b4 | |||
| 27e81637be | |||
| 5c67e27a30 | |||
| 59af9809fe | |||
| a564510c49 | |||
| 285b614927 | |||
| fd2d2c035e | |||
| dc71ec734d | |||
| 78981862be | |||
| 7f1253ff83 | |||
| e0265aed05 | |||
| 9d36d88a65 | |||
| 5dd5602229 | |||
| 126c4e9a06 | |||
| 5dbaf6ceb0 | |||
| 90de5659ea | |||
| 367e50edab | |||
| 42b8dafafe | |||
| 577aaf8ad6 | |||
| 07cdf0364c | |||
| 7f829f0159 | |||
| a918aa97d9 | |||
| 4fdecc9b85 | |||
| 7af25c785d | |||
| 4de39b205d | |||
| 2748a2e97f | |||
| 2926bbfe15 | |||
| 254c63763a | |||
| 2de834f1f4 | |||
| 7273eab80e | |||
| 13e79c777a | |||
| 8aa7d4b463 | |||
| 5251f1c9db | |||
| 82e923dfc8 | |||
| decf16b92c | |||
| b84d960a81 | |||
| 34cb305755 | |||
| ed85bfa915 | |||
| 06ef33ff5e | |||
| 57f121178c | |||
| 3b88ee623b | |||
| 8588625937 | |||
| 3417839726 | |||
| c1069052ae | |||
| ea17542e4b | |||
| c7d779fe88 | |||
| a70f3f12c5 | |||
| 90a31589bb | |||
| b48d9a3a82 | |||
| 0255311bbe | |||
| bd91519df9 | |||
| 58fe8b0cf1 | |||
| 064aa64f20 | |||
| d9f79853fb | |||
| 2e68ee5c8b | |||
| a9544ca890 | |||
| 9a549a853b | |||
| 2dad769a00 | |||
| 0ceb14dbf6 | |||
| bab1e26d9b | |||
| 9a91cc232c | |||
| 5a46cf1d48 | |||
| f1e241940b | |||
| 47b344ba12 | |||
| afbb06a72f | |||
| e336cd463f | |||
| 3a8315971e | |||
| 4ccfa98771 | |||
| 1db120bf06 | |||
| 262cf63956 | |||
| fe2ae4c6c3 | |||
| 16d9944dbb | |||
| e9956cc71e | |||
| 3af96e50bd | |||
| 59a85c1d75 | |||
| 4427149a38 | |||
| 20dee618ea | |||
| 37ebbb53be | |||
| ba019efaf1 | |||
| ce948fc512 | |||
| 2cd9e7fb55 | |||
| 1e2d151684 | |||
| ce5651f5fa | |||
| 20ba0bf4ed | |||
| ddc56c8a0d | |||
| c325ffd0f8 | |||
| e0da2764c9 | |||
| 6e88d9688b | |||
| bf898f10fb | |||
| c891999e1d | |||
| 938e287501 | |||
| edcfc32b1a | |||
| 904b211d98 | |||
| af08567f24 | |||
| 75ef658962 | |||
| fe2dd79838 | |||
| bbe7e6525d | |||
| ef20df719c | |||
| 68399601ce | |||
| aa637fd942 | |||
| 601c97c015 | |||
| 6b47052491 | |||
| 297da94319 | |||
| 64f101f534 | |||
| 45917f278a | |||
| ddd2759cec | |||
| 70d8903d3c | |||
| f66c7dc09c | |||
| ad29093ac1 | |||
| 82c6caef85 | |||
| 46ec72412a | |||
| ead09395d9 | |||
| 7106fc5304 | |||
| d16dcb9f19 | |||
| 1aaf34b0ed | |||
| 39a3b8922d | |||
| 9b78582475 | |||
| 3a84224b93 | |||
| 2592ba7399 | |||
| 1795e0a290 | |||
| c959f59581 | |||
| 2449723a1c | |||
| ae0e56e98d | |||
| 6efe521e44 | |||
| bccd21ac14 | |||
| 8449a65cdf | |||
| fc47562983 | |||
| 76900ae291 | |||
| ec55559ff1 | |||
| 3daa26e1f7 | |||
| 9ea8b6f659 | |||
| 387f2f0a94 | |||
| b0d95d02be | |||
| 3a98f01d31 | |||
| d305752749 | |||
| 2ba4b235fc | |||
| 6820c0a5d7 | |||
| 048883ad27 | |||
| 08e7ada242 | |||
| d3ddfa31f7 | |||
| 4b899a813e | |||
| 28610a9a42 | |||
| 15ee9a5cac | |||
| 58945a429f | |||
| 9f4111015e | |||
| 04b960b415 | |||
| 33267f2178 | |||
| 970e50d1f1 | |||
| d4199c2d08 | |||
| cf4ca7b6a8 | |||
| d8b335ce65 | |||
| 39a2934b05 | |||
| 7d1c720b84 | |||
| 51743461ee | |||
| 53cf5ca762 | |||
| b5ef42b0a1 | |||
| 0521ddd858 | |||
| b7bb3bfee2 | |||
| 4183044e96 | |||
| 27448bde20 | |||
| 87b9e8fbaf | |||
| 9d79859ba6 | |||
| 25bb55491a | |||
| 198cbacc3e | |||
| 3f842221f7 | |||
| 5d0183a9ed | |||
| 99df4d660b | |||
| 65eb528e2d | |||
| f2adfde1a8 | |||
| 1e915a2903 | |||
| 962eaa8a4b | |||
| e2dc3e9ff3 | |||
| f1bb8daaab | |||
| ec9d68960f | |||
| b0fcbebdae | |||
| 34f72ecf8f | |||
| 9d348319fd | |||
| c55fee69de | |||
| ce31cb072b | |||
| 6b91fc9c91 | |||
| e34f77ba0e | |||
| bc3b7401a1 | |||
| 85677eaf1a | |||
| 75d5e74059 | |||
| c4d15b3b95 | |||
| aa168ec2d6 | |||
| 4ae0efe887 | |||
| 86a57d8b56 | |||
| 9dda7485eb | |||
| 8b9670add9 | |||
| 978aebd79c | |||
| ac079f0f83 | |||
| e82e912151 | |||
| 5488ae5b89 | |||
| 15b875b116 | |||
| dedf835aa6 | |||
| e62b9c6009 | |||
| 53da778506 | |||
| 4360b2c815 | |||
| 1e15b1e0be | |||
| c618eba9a9 | |||
| 0bc50f7284 | |||
| 435f9113e8 | |||
| 8818c4785b | |||
| 2e003e5404 | |||
| e791a8ea07 | |||
| 2fb8eb755b | |||
| 1ef8378a30 | |||
| d031f958a9 | |||
| 9bbadac9dc | |||
| b012f77475 | |||
| 3cf36b1773 | |||
| 8f93c046a9 | |||
| 90af68901a | |||
| c17507b216 | |||
| b110b7c3f7 | |||
| 36431b3dcd | |||
| 609294deee | |||
| fffae9a741 | |||
| 598ce4bb5f | |||
| 212f6dc9e0 | |||
| cd05f1c3d7 | |||
| d7a0691c99 | |||
| 86346aa332 | |||
| b162b1fa34 | |||
| ea9f8b0ceb | |||
| 6210b9e746 | |||
| a778b410b9 | |||
| 8a674c8bc3 | |||
| aaf625c624 | |||
| d4079a3273 | |||
| 8d94fe3346 | |||
| ce510e55ae | |||
| 5419ff9a71 | |||
| ade437d625 | |||
| 87780a5b7e | |||
| f6f6f261ed | |||
| 65acc7c9ad | |||
| 31d95ac9e6 | |||
| 964d17d05a | |||
| 665c5992f0 | |||
| 5f52e0581d | |||
| 870e3a45ef | |||
| a5fe4a3694 | |||
| 838670ccbc | |||
| baf4cc225e | |||
| 93ac1605bd | |||
| c8a68001c1 | |||
| 244a22755c | |||
| 79c3ea82c7 | |||
| 4b92960975 | |||
| ef616ff25b | |||
| 7fb1a470ce | |||
| 48c40c87bc | |||
| fc6b2d9193 | |||
| e5dc66e7e5 | |||
| bb01b76582 | |||
| f1aff0fd96 | |||
| 5656be5206 | |||
| 484ce8e488 | |||
| dcadefd133 | |||
| 28366677b0 | |||
| d65302742c | |||
| b2cf28efdd | |||
| 41e20bb6b7 | |||
| 6e670a2499 | |||
| 481b2186cb | |||
| 1bc1c0b14f | |||
| e9d27b9d2b | |||
| 828bbc407f | |||
| e50469d84e | |||
| d3a9b126a6 | |||
| 9eb185ec39 | |||
| fcf60e7f7c | |||
| 0ebee92f7d | |||
| 64b42bba2e | |||
| d297f9e032 | |||
| 30aabf1da9 | |||
| bfba18fdcb | |||
| eebdaa2f27 | |||
| c3c9c4cde5 | |||
| 640d5135df | |||
| cbbd20a687 | |||
| 1a2a27b988 | |||
| d819151020 | |||
| d089436546 | |||
| 289d604690 | |||
| 2979e0e964 | |||
| 5338f1cfbd | |||
| 214f18cbfd | |||
| 9b11609b63 | |||
| d476c2b613 | |||
| 590afebc0a | |||
| 02bd1af293 | |||
| 2fde82528d | |||
| 6c383e279f | |||
| 5c07477de4 | |||
| 146a284315 | |||
| a8faeeac73 | |||
| 69e385e4cd | |||
| 41b8dd2863 | |||
| 493dc8fcd5 | |||
| 87764445e8 | |||
| 0bb31e16c9 | |||
| 72c90abe36 | |||
| c4d8d33a60 | |||
| a267bca8fb | |||
| 32d2e78e3c | |||
| 555e70ebec | |||
| cd1b2aab46 | |||
| 43289103cb | |||
| 9edce23e76 | |||
| 756a8a35e3 | |||
| f3057c61a7 | |||
| 25345f08e7 | |||
| 2091e12e82 | |||
| 3eb000fa60 | |||
| 175669c61e | |||
| 3059b36118 | |||
| 35b1887e17 | |||
| 8f9b8a8550 | |||
| 174befe729 | |||
| e212b64823 | |||
| 3599b98dca | |||
| 991dc32a0b | |||
| a76efd4166 | |||
| 8a768baaaa | |||
| 997692b494 | |||
| 59ffec4e39 | |||
| 56d0ecc253 | |||
| e863746bd7 | |||
| d4dc7911eb | |||
| f561d3261a | |||
| 1bc5632771 | |||
| fdf8ee7015 | |||
| 5ec95086f2 | |||
| 26e4669316 | |||
| 4b488a2d28 | |||
| 6c352dca74 | |||
| 9d816694ba | |||
| 39ef35db0c | |||
| b8ed135183 | |||
| 6f750582dd | |||
| 5f93fbd471 | |||
| 0e2653b7dd | |||
| 47554b562d | |||
| 99427d649e | |||
| 7bc4589d4d | |||
| 9af586d4ac | |||
| d1c3be3251 | |||
| 87e68cac6c | |||
| 7d5a98409b | |||
| f28367bcfc | |||
| 14817e31f6 | |||
| fbdbd722b1 | |||
| b34102cd11 | |||
| 3c51cd6626 | |||
| b0b34236e3 | |||
| a502836002 | |||
| 09417d4b83 | |||
| 83ef2fa84c | |||
| e596a45e9f | |||
| 24e5000c37 | |||
| e3bcfa17f8 | |||
| 3b512676b7 | |||
| 928198bbfe | |||
| 1fb56f0ad2 | |||
| 9797f62cb8 | |||
| 4ddd87e773 | |||
| 7fd2e4d2db | |||
| 55c7d86205 | |||
| 737a28050c | |||
| 434ecdac6b | |||
| 709570afcc | |||
| b084b4faaf | |||
| d96ce23451 | |||
| 760a9d6d35 | |||
| 8e624cedb1 | |||
| 39cf269d6b | |||
| b9f83c7780 | |||
| 6a00b5a79e | |||
| 2ce674e3fd | |||
| 0fcc25d7c9 | |||
| 63bd0136fb | |||
| 80a2a934dd | |||
| e13976a3b3 | |||
| 5144330807 | |||
| bb29639183 | |||
| 4667cb9de9 | |||
| c34f3defe1 | |||
| eb0d742672 | |||
| d9b0a73787 | |||
| 4810879b2f | |||
| f4b6704aad | |||
| b1a31d3b30 | |||
| bf909db3f9 | |||
| 9c68be4d5e | |||
| d7956dd495 | |||
| 37a473e7d6 | |||
| 5a1c885e8f | |||
| 0b1136ad82 | |||
| 45af549897 | |||
| 97844603fc | |||
| c07b39e58b | |||
| 384c543ab9 | |||
| 592b13d7db | |||
| 6fdba3c02e | |||
| cbf758ead9 | |||
| d1ad778a64 | |||
| ce5ad296ae | |||
| 797e105786 | |||
| d17d80747e | |||
| 55ea207a55 | |||
| 6384d1e5a3 | |||
| aba01cdace | |||
| 517b7a14b4 | |||
| 2927de7cf9 | |||
| 6471ba70e4 | |||
| 9f9de01c51 | |||
| 3662decb8b | |||
| 583bcfb3c7 | |||
| c45e3fa4d5 | |||
| 24cbcef620 | |||
| e2a520ff49 | |||
| a5e3317e28 | |||
| 5638c4ba87 | |||
| bf7a128142 | |||
| c5243cd4d5 | |||
| db868ed29d | |||
| 450c7d80f8 | |||
| abbb001975 | |||
| f35d83ae48 | |||
| a2315dc95e | |||
| 4e2feb6fbc | |||
| 13602b6769 | |||
| 85dba25246 | |||
| 66432672b3 | |||
| e6d96e4c18 | |||
| 9812305bb9 | |||
| f680a63a1f | |||
| 781d63cb2a | |||
| 9ff04ee3d8 | |||
| 5d85a24977 | |||
| 1e51fca0b0 | |||
| 5537d53f9a | |||
| 50a4170541 | |||
| 3a8255bda1 | |||
| a617846f0f | |||
| 5772588c29 | |||
| 9d0dc45f74 | |||
| c6aefbc9a0 | |||
| dbbafb0cc9 | |||
| 6e8272f78f | |||
| cbf73ef29e | |||
| db6d3b495b | |||
| baf8a63121 | |||
| fc4a76ee50 | |||
| 2117d1d035 | |||
| 0a70e0b7b6 | |||
| 64ffac5671 | |||
| ac384e8a9c | |||
| f97c8222c7 | |||
| 728289ee3a | |||
| 5faa16f9ee | |||
| 4e608b116a | |||
| 521b49166e | |||
| 8f32decf2d | |||
| 0d51f83d2d | |||
| 78c6a68db9 | |||
| 2949ab73e2 | |||
| 223741820d | |||
| 4b57821f52 | |||
| 74271a479f | |||
| c377177108 | |||
| 84eb729bd4 | |||
| 14aea365c5 | |||
| 97cb3fa5a5 | |||
| b5368db704 | |||
| 8c442b72f3 | |||
| f8f6791d39 | |||
| 0c09f077aa | |||
| af2831d7b6 | |||
| 64d5d4aec7 | |||
| 619a6b2adb | |||
| 33a26bc0cf | |||
| b445a7c4d3 | |||
| e6892d0c3e | |||
| 33e9a88b56 | |||
| df00a2251e | |||
| 92c44c8abe | |||
| 8e4f7bbd3e | |||
| a40217cf07 | |||
| e586fda5f2 | |||
| a58564ff88 | |||
| 89885b9fb9 | |||
| 5c7d977ae0 | |||
| 2cd3ee9698 | |||
| dd3080e018 | |||
| 5915e8e86a | |||
| 3c67c06654 | |||
| 76232ca573 | |||
| 5235e82bda | |||
| 10f0713257 | |||
| e9c7970ea4 | |||
| 1a6ac4aeb1 | |||
| f633bdddf0 | |||
| de0b91d157 | |||
| 2e77e498f5 | |||
| 4ac67eb1f9 | |||
| 2b536de37f | |||
| 2ffa92ba1b | |||
| 6ecddd8388 | |||
| bd2772ea4c | |||
| 92bf79d53b | |||
| eebe0eeb71 | |||
| 1068eaa0b9 | |||
| faac3e7d7c | |||
| dab4340207 | |||
| fd2567748f | |||
| c2daedbd11 | |||
| 7c604beb73 | |||
| 8c42aea827 | |||
| cf1bfdfb61 | |||
| 75b26513e1 | |||
| 6c09a77a97 | |||
| 67389c39fb | |||
| c326103e6e | |||
| c2120a16da | |||
| 258ad4352e | |||
| 435d3958f4 | |||
| b0408ef5c6 | |||
| 1c41b0bc2f | |||
| aa827f3042 | |||
| f44f5964bb | |||
| 91ba93bd7a | |||
| 0abe4cefb4 | |||
| bccd460f3b | |||
| d1023004e1 | |||
| 04a5f9cb04 | |||
| 9818e2b550 | |||
| fe43e3b89d | |||
| e1f1ae041f | |||
| 5bcf26e324 | |||
| 5f47a8149f | |||
| 00b662b53a | |||
| faf519ab1b | |||
| fce73f6f17 | |||
| 887890baf5 | |||
| c66b24feeb | |||
| 84c6f147ad | |||
| 0cdb0daa8c | |||
| eee702f299 | |||
| df65247325 | |||
| 1a174e75d3 | |||
| 9e1fd3454f | |||
| 3b1603cadf | |||
| 8803bac708 | |||
| 3a01eaa4a6 | |||
| 9f84c1c448 | |||
| dda0390156 | |||
| c74509dd5f | |||
| f61bbb2ff4 | |||
| e7f60161a3 | |||
| ebec4fbc24 | |||
| 1d4105ae3d | |||
| 586d49f0c3 | |||
| 5b0fab0697 | |||
| 2b3359dff3 | |||
| 63203aa14c | |||
| 716a8329c2 | |||
| dab0aec85e | |||
| 6ea8e2525a | |||
| 1f1ab017c0 | |||
| b6912ef95e | |||
| db54dca694 | |||
| 0e751b983c | |||
| 997b20a975 | |||
| 386f9c42c2 | |||
| cfae06db65 | |||
| 44260b7b5c | |||
| 13063b957f | |||
| ee05e12480 | |||
| 5538545fb0 | |||
| bc1167c2c5 | |||
| c57656e4c3 | |||
| 264400a984 | |||
| 408db4eb1d | |||
| 9347f223ef | |||
| 518aa30c9c | |||
| 6bbf1f9355 | |||
| b221e4d445 | |||
| 580fccbfca | |||
| 045916efcc | |||
| 4f92482294 | |||
| 2f055a75a0 | |||
| f0621207e3 | |||
| d657bc4e3d | |||
| a1fd07b27c | |||
| 52219c5f3f | |||
| 1a66461e07 | |||
| d20df12168 | |||
| 668b429615 | |||
| 7db528be39 | |||
| 60f760ee49 | |||
| 884aaab751 | |||
| e968560ea4 | |||
| 07caaa96e4 | |||
| e8a679c280 | |||
| bc885f1d08 | |||
| f2f051d6de | |||
| 49a0bfccba | |||
| 0c1e60894f | |||
| ace87ad7bb | |||
| 50f0097843 | |||
| 32a9466277 | |||
| 1ee3407946 | |||
| f1120d7aa9 | |||
| 2e7d6b2f99 | |||
| dfef929187 | |||
| e78d9ad592 | |||
| 9f2948f595 | |||
| 198da910ed | |||
| 5f1bf9d9d6 | |||
| 798c4aef9a | |||
| f80f5b3bda | |||
| cbb07b0d67 | |||
| 7cc9921615 | |||
| 7555fe065e | |||
| d977f4278e | |||
| 870e3ca893 | |||
| 213acaee3b | |||
| 29296ec998 | |||
| 58381496a2 | |||
| 5981e42aed | |||
| 3c9165d295 | |||
| 60d0ef93ac | |||
| f45d5b0066 | |||
| b71306480f | |||
| dc9df0a79a | |||
| 8976e53998 | |||
| b37b19f344 | |||
| bdd265a1b1 | |||
| 2c9df7aad1 | |||
| 1fca248d4c | |||
| 0080ee6e86 | |||
| 99081ea2a0 | |||
| 1f62247c7e | |||
| 6415d1a6a5 | |||
| 926b08c197 | |||
| caad69a312 | |||
| 518a4eb6ee | |||
| aff41d0b08 | |||
| 113b110ab4 | |||
| ec85c9fdfe | |||
| d71a782179 | |||
| d0aef07a38 | |||
| 8ad0a10c61 | |||
| 85bb725eb6 | |||
| d8f9c096f3 | |||
| e3e9f89861 | |||
| 2faecfa403 | |||
| 1589942fc2 | |||
| 5d9c968614 | |||
| c020cf05e1 | |||
| 09e8d85b1e | |||
| 4d3eb134a2 | |||
| b92df85893 | |||
| 545025ed2b | |||
| 3158962506 | |||
| c314f74de6 | |||
| 65615385e7 | |||
| 727f35b35b | |||
| 07ddf7e87b |
@@ -0,0 +1,8 @@
|
||||
# Text files use LF line endings in this repository
|
||||
* text=auto
|
||||
|
||||
# Except the dependencies, which we leave alone
|
||||
vendor/** -text=auto
|
||||
|
||||
# Diffs on these files are meaningless
|
||||
*.svg -diff
|
||||
@@ -0,0 +1,2 @@
|
||||
/AUTHORS @calmh
|
||||
/*.md @calmh
|
||||
@@ -0,0 +1,42 @@
|
||||
### DO NOT REPORT SECURITY ISSUES IN THIS ISSUE TRACKER
|
||||
|
||||
Instead, contact security@syncthing.net directly - see
|
||||
https://syncthing.net/security.html for more information.
|
||||
|
||||
### DO NOT POST SUPPORT REQUESTS OR GENERAL QUESTIONS IN THIS ISSUE TRACKER
|
||||
|
||||
Please use the forum at https://forum.syncthing.net/ where a large number of
|
||||
helpful people hang out. This issue tracker is for reporting bugs or feature
|
||||
requests directly to the developers. Worst case you might get a short
|
||||
"that's a bug, please report it on GitHub" response on the forum, in which
|
||||
case we thank you for your patience and following our advice. :)
|
||||
|
||||
### Please use the correct issue tracker
|
||||
|
||||
If your problem relates to a Syncthing wrapper or [sub-project](https://github.com/syncthing) such as [Syncthing for Android](https://github.com/syncthing/syncthing-android/issues), [SyncTrayzor](https://github.com/canton7/synctrayzor) or the [documentation](https://github.com/syncthing/docs/issues), please use their respective issue trackers.
|
||||
|
||||
### Does your log mention database corruption?
|
||||
|
||||
If your Syncthing log reports panics because of database corruption it is most likely a fault with your system's storage or memory. Affected log entries will contain lines starting with `panic: leveldb`. You will need to delete the index database to clear this, by running `syncthing -reset-database`.
|
||||
|
||||
### Please do post actual bug reports and feature requests.
|
||||
|
||||
If your issue is a bug report, replace this boilerplate with a description
|
||||
of the problem, being sure to include at least:
|
||||
|
||||
- what happened,
|
||||
- what you expected to happen instead, and
|
||||
- any steps to reproduce the problem.
|
||||
|
||||
Also fill out the version information below and add log output or
|
||||
screenshots as appropriate.
|
||||
|
||||
If your issue is a feature request, simply replace this template text in
|
||||
its entirety.
|
||||
|
||||
### Version Information
|
||||
|
||||
Syncthing Version: v0.x.y
|
||||
OS Version: Windows 7 / Ubuntu 14.04 / ...
|
||||
Browser Version: (if applicable, for GUI issues)
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
### Purpose
|
||||
|
||||
Describe the purpose of this change. If there is an existing issue that is
|
||||
resolved by this pull request, ensure that the commit subject is on the form
|
||||
`Some short description (fixes #1234)` where 1234 is the issue number.
|
||||
|
||||
### Testing
|
||||
|
||||
Describe what testing has been done, and how the reviewer can test the change
|
||||
if new tests are not included.
|
||||
|
||||
### Screenshots
|
||||
|
||||
If this is a GUI change, include screenshots of the change. If not, please
|
||||
feel free to just delete this section.
|
||||
|
||||
### Documentation
|
||||
|
||||
If this is a user visible change (including API and protocol changes), add a link here
|
||||
to the corresponding pull request on https://github.com/syncthing/docs or describe
|
||||
the documentation changes necessary.
|
||||
|
||||
## Authorship
|
||||
|
||||
Your name and email will be added automatically to the AUTHORS file
|
||||
based on the commit metadata.
|
||||
|
||||
+16
-2
@@ -1,11 +1,25 @@
|
||||
syncthing
|
||||
/syncthing
|
||||
/stdiscosrv
|
||||
syncthing.exe
|
||||
stdiscosrv.exe
|
||||
*.tar.gz
|
||||
*.zip
|
||||
*.asc
|
||||
*.sublime*
|
||||
*.deb
|
||||
.jshintrc
|
||||
coverage.out
|
||||
files/pidx
|
||||
bin
|
||||
perfstats*.csv
|
||||
coverage.xml
|
||||
syncthing.sig
|
||||
RELEASE
|
||||
deb
|
||||
lib/auto/gui.files.go
|
||||
snapcraft.yaml
|
||||
prime/
|
||||
snap/
|
||||
parts/
|
||||
stage/
|
||||
*.snap
|
||||
*.bz2
|
||||
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- tip
|
||||
|
||||
install:
|
||||
- export PATH=$PATH:$HOME/gopath/bin
|
||||
- ./build.sh setup
|
||||
|
||||
script:
|
||||
- ./build.sh test-cov
|
||||
|
||||
after_success:
|
||||
- goveralls -coverprofile=coverage.out -service=travis-ci -package=syncthing/syncthing -repotoken="$COVERALLS_TOKEN"
|
||||
|
||||
env:
|
||||
global:
|
||||
secure: "TSPJDsokGCQhKLjgG3c58qHn8Qxhh4zEkWFf0XIOOY2nlDVzdgXDsC+Nq0YaP4106Ee4FgkSefsUTQV5lq/IyYW8elgqlgghjOtOi6RJa14eIS9Yy5Bkx6MXn0QfZX/lG+sy42pKSNk43y9GWx/qrt4nkfTtTvI5cXgwDGYdmX8="
|
||||
@@ -0,0 +1,193 @@
|
||||
# This is the official list of Syncthing authors for copyright purposes.
|
||||
#
|
||||
# THIS FILE IS MOSTLY AUTO GENERATED. IF YOU'VE MADE A COMMIT TO THE
|
||||
# REPOSITORY YOU WILL BE ADDED HERE AUTOMATICALLY WITHOUT THE NEED FOR
|
||||
# ANY MANUAL ACTION.
|
||||
#
|
||||
# That said, you are welcome to correct your name or add a nickname / GitHub
|
||||
# user name as appropriate. The format is:
|
||||
#
|
||||
# Name Name Name (nickname) <email1@example.com> <email2@example.com>
|
||||
#
|
||||
# The in-GUI authors list is periodically automatically updated from the
|
||||
# contents of this file.
|
||||
#
|
||||
|
||||
Aaron Bieber (qbit) <qbit@deftly.net>
|
||||
Adam Piggott (ProactiveServices) <aD@simplypeachy.co.uk> <simplypeachy@users.noreply.github.com> <ProactiveServices@users.noreply.github.com> <adam@proactiveservices.co.uk>
|
||||
Adel Qalieh (adelq) <aqalieh95@gmail.com> <adelq@users.noreply.github.com>
|
||||
Alessandro G. (alessandro.g89) <alessandro.g89@gmail.com>
|
||||
Alexander Graf (alex2108) <register-github@alex-graf.de>
|
||||
Alexandre Viau (aviau) <alexandre@alexandreviau.net> <aviau@debian.org>
|
||||
Anderson Mesquita (andersonvom) <andersonvom@gmail.com>
|
||||
andresvia <andres.via@gmail.com>
|
||||
Andrew Dunham (andrew-d) <andrew@du.nham.ca>
|
||||
Andrew Rabert (nvllsvm) <ar@nullsum.net> <6550543+nvllsvm@users.noreply.github.com>
|
||||
Andrey D (scienmind) <scintertech@cryptolab.net> <scienmind@users.noreply.github.com>
|
||||
andyleap <andyleap@gmail.com>
|
||||
Antoine Lamielle (0x010C) <antoine.lamielle@0x010c.fr> <gh@0x010c.fr>
|
||||
Antony Male (canton7) <antony.male@gmail.com>
|
||||
Aranjedeath <Aranjedeath@users.noreply.github.com>
|
||||
Arthur Axel fREW Schmidt (frioux) <frew@afoolishmanifesto.com> <frioux@gmail.com>
|
||||
Audrius Butkevicius (AudriusButkevicius) <audrius.butkevicius@gmail.com>
|
||||
BAHADIR YILMAZ <bahadiryilmaz32@gmail.com>
|
||||
Bart De Vries (mogwa1) <devriesb@gmail.com>
|
||||
Ben Curthoys (bencurthoys) <ben@bencurthoys.com>
|
||||
Ben Schulz (uok) <ueomkail@gmail.com> <uok@users.noreply.github.com>
|
||||
Ben Shepherd (benshep) <bjashepherd@gmail.com>
|
||||
Ben Sidhom (bsidhom) <bsidhom@gmail.com>
|
||||
Benedikt Heine (bebehei) <bebe@bebehei.de>
|
||||
Benedikt Morbach <benedikt.morbach@googlemail.com>
|
||||
Benno Fünfstück <benno.fuenfstueck@gmail.com>
|
||||
Benny Ng (tpng) <benny.tpng@gmail.com>
|
||||
Boris Rybalkin <ribalkin@gmail.com>
|
||||
Brandon Philips (philips) <brandon@ifup.org>
|
||||
Brendan Long (brendanlong) <self@brendanlong.com>
|
||||
Brian R. Becker (brbecker) <brbecker@gmail.com>
|
||||
Caleb Callaway (cqcallaw) <enlightened.despot@gmail.com>
|
||||
Carsten Hagemann (Moter8) <moter8@gmail.com>
|
||||
Cathryne Linenweaver (Cathryne) <cathryne.linenweaver@gmail.com> <Cathryne@users.noreply.github.com> <katrinleinweber@MAC.local>
|
||||
Cedric Staniewski (xduugu) <cedric@gmx.ca>
|
||||
Chris Howie (cdhowie) <me@chrishowie.com>
|
||||
Chris Joel (cdata) <chris@scriptolo.gy>
|
||||
Chris Tonkinson <chris@masterbran.ch>
|
||||
chucic <chucic@seznam.cz>
|
||||
Colin Kennedy (moshen) <moshen.colin@gmail.com>
|
||||
Cromefire_ <tim.l@nghorst.net>
|
||||
Dale Visser <dale.visser@live.com>
|
||||
Daniel Bergmann (brgmnn) <dan.arne.bergmann@gmail.com> <brgmnn@users.noreply.github.com>
|
||||
Daniel Harte (norgeous) <daniel@harte.me> <daniel@danielharte.co.uk> <norgeous@users.noreply.github.com>
|
||||
Daniel Martí (mvdan) <mvdan@mvdan.cc>
|
||||
Darshil Chanpura (dtchanpura) <dtchanpura@gmail.com> <dcprime314@gmail.com>
|
||||
David Rimmer (dinosore) <dinosore@dbrsoftware.co.uk>
|
||||
Denis A. (dva) <denisva@gmail.com>
|
||||
Dennis Wilson (snnd) <dw@risu.io>
|
||||
derekriemer <derek.riemer@colorado.edu>
|
||||
desbma <desbma@users.noreply.github.com>
|
||||
Dmitry Saveliev (dsaveliev) <d.e.saveliev@gmail.com>
|
||||
Dominik Heidler (asdil12) <dominik@heidler.eu>
|
||||
Elias Jarlebring (jarlebring) <jarlebring@gmail.com>
|
||||
Elliot Huffman <thelich2@gmail.com>
|
||||
Emil Hessman (ceh) <emil@hessman.se>
|
||||
Erik Meitner (WSGCSysadmin) <e.meitner@willystreet.coop>
|
||||
Federico Castagnini (facastagnini) <federico.castagnini@gmail.com>
|
||||
Felix Ableitner (Nutomic) <me@nutomic.com>
|
||||
Felix Unterpaintner (bigbear2nd) <bigbear2nd@gmail.com>
|
||||
Francois-Xavier Gsell (zukoo) <fxgsell@gmail.com>
|
||||
Frank Isemann (fti7) <frank@isemann.name>
|
||||
Gilli Sigurdsson (gillisig) <gilli@vx.is>
|
||||
Graham Miln (grahammiln) <graham.miln@dssw.co.uk> <graham.miln@miln.eu>
|
||||
Han Boetes <han@boetes.org>
|
||||
Harrison Jones (harrisonhjones) <harrisonhjones@users.noreply.github.com>
|
||||
Heiko Zuerker (Smiley73) <heiko@zuerker.org>
|
||||
Iain Barnett <iainspeed@gmail.com>
|
||||
Ian Johnson (anonymouse64) <ian.johnson@canonical.com> <person.uwsome@gmail.com>
|
||||
Jaakko Hannikainen (jgke) <jgke@jgke.fi>
|
||||
Jacek Szafarkiewicz (hadogenes) <szafar@linux.pl>
|
||||
Jake Peterson (acogdev) <jake@acogdev.com>
|
||||
Jakob Borg (calmh) <jakob@nym.se> <jakob@kastelo.net>
|
||||
James Patterson (jpjp) <jamespatterson@operamail.com> <jpjp@users.noreply.github.com>
|
||||
janost <janost@tuta.io>
|
||||
Jaroslav Malec (dzarda) <dzardacz@gmail.com>
|
||||
jaseg <githubaccount@jaseg.net>
|
||||
Jaya Chithra (jayachithra) <s.k.jayachithra@gmail.com>
|
||||
Jens Diemer (jedie) <github.com@jensdiemer.de> <git@jensdiemer.de>
|
||||
Jerry Jacobs (xor-gate) <jerry.jacobs@xor-gate.org> <xor-gate@users.noreply.github.com>
|
||||
Jochen Voss (seehuhn) <voss@seehuhn.de>
|
||||
Johan Andersson <j@i19.se>
|
||||
Johan Vromans (sciurius) <jvromans@squirrel.nl>
|
||||
John Rinehart (fuzzybear3965) <johnrichardrinehart@gmail.com>
|
||||
Jonathan Cross <jcross@gmail.com>
|
||||
Jose Manuel Delicado (jmdaweb) <jmdaweb@hotmail.com> <jmdaweb@users.noreply.github.com>
|
||||
Jörg Thalheim <Mic92@users.noreply.github.com>
|
||||
Karol Różycki (krozycki) <rozycki.karol@gmail.com>
|
||||
Keith Turner <kturner@apache.org>
|
||||
Kelong Cong (kc1212) <kc04bc@gmx.com> <kc1212@users.noreply.github.com>
|
||||
Ken'ichi Kamada (kamadak) <kamada@nanohz.org>
|
||||
Kevin Allen (ironmig) <kma1660@gmail.com>
|
||||
Kevin White, Jr. (kwhite17) <kevinwhite1710@gmail.com>
|
||||
klemens <ka7@github.com>
|
||||
Kurt Fitzner (Kudalufi) <kurt@va1der.ca> <kurt.fitzner@gmail.com>
|
||||
Lars K.W. Gohlke (lkwg82) <lkwg82@gmx.de>
|
||||
Laurent Arnoud <laurent@spkdev.net>
|
||||
Laurent Etiemble (letiemble) <laurent.etiemble@gmail.com> <laurent.etiemble@monobjc.net>
|
||||
Leo Arias (elopio) <yo@elopio.net>
|
||||
Liu Siyuan (liusy182) <liusy182@gmail.com> <liusy182@hotmail.com>
|
||||
Lode Hoste (Zillode) <zillode@zillode.be>
|
||||
Lord Landon Agahnim (LordLandon) <lordlandon@gmail.com>
|
||||
Majed Abdulaziz (majedev) <majed.alhajry@gmail.com>
|
||||
Marc Laporte (marclaporte) <marc@marclaporte.com> <marc@laporte.name>
|
||||
Marc Pujol (kilburn) <kilburn@la3.org>
|
||||
Marcin Dziadus (marcindziadus) <dziadus.marcin@gmail.com>
|
||||
marco-m <marco.molteni@laposte.net>
|
||||
Mark Pulford (mpx) <mark@kyne.com.au>
|
||||
Mateusz Naściszewski (mateon1) <matin1111@wp.pl>
|
||||
Matic Potočnik <hairyfotr@gmail.com>
|
||||
Matt Burke (burkemw3) <mburke@amplify.com> <burkemw3@gmail.com>
|
||||
Matteo Ruina <matteo.ruina@gmail.com>
|
||||
Max Schulze (kralo) <max.schulze@online.de> <kralo@users.noreply.github.com>
|
||||
MaximAL <almaximal@ya.ru>
|
||||
Maxime Thirouin <m@moox.io>
|
||||
Michael Jephcote (Rewt0r) <rewt0r@gmx.com> <Rewt0r@users.noreply.github.com>
|
||||
Michael Ploujnikov (plouj) <ploujj@gmail.com>
|
||||
Michael Tilli (pyfisch) <pyfisch@gmail.com>
|
||||
Mike Boone <mike@boonedocks.net>
|
||||
MikeLund <MikeLund@users.noreply.github.com>
|
||||
Nate Morrison (nrm21) <natemorrison@gmail.com>
|
||||
Nicholas Rishel (PrototypeNM1) <rishel.nick@gmail.com> <PrototypeNM1@users.noreply.github.com>
|
||||
Nico Stapelbroek <3368018+nstapelbroek@users.noreply.github.com>
|
||||
Nicolas Braud-Santoni <nicolas@braud-santoni.eu>
|
||||
Niels Peter Roest (Niller303) <nielsproest@hotmail.com> <seje.niels@hotmail.com>
|
||||
Nils Jakobi (thunderstorm99) <jakobi.nils@gmail.com>
|
||||
NoLooseEnds <jon.koslung@gmail.com>
|
||||
Oyebanji Jacob Mayowa <oyebanji05@gmail.com>
|
||||
Pascal Jungblut (pascalj) <github@pascalj.com> <mail@pascal-jungblut.com>
|
||||
Pawel Palenica (qepasa) <pawelpalenica11@gmail.com>
|
||||
Paweł Rozlach <vespian@users.noreply.github.com>
|
||||
perewa <cavalcante.ten@gmail.com>
|
||||
Peter Badida <KeyWeeUsr@users.noreply.github.com>
|
||||
Peter Dave Hello <hsu@peterdavehello.org>
|
||||
Peter Hoeg (peterhoeg) <peter@speartail.com>
|
||||
Peter Marquardt (wwwutz) <wwwutz@gmail.com> <wwwutz@googlemail.com>
|
||||
Phil Davis <phil.davis@inf.org>
|
||||
Philippe Schommers (filoozoom) <philippe@schommers.be>
|
||||
Phill Luby (pluby) <phill.luby@newredo.com>
|
||||
Pier Paolo Ramon <ramonpierre@gmail.com>
|
||||
Piotr Bejda (piobpl) <piotrb10@gmail.com>
|
||||
Pramodh KP (pramodhkp) <pramodh.p@directi.com> <1507241+pramodhkp@users.noreply.github.com>
|
||||
Richard Hartmann <RichiH@users.noreply.github.com>
|
||||
Robert Carosi (nov1n) <robert@carosi.nl>
|
||||
Roman Zaynetdinov (zaynetro) <romanznet@gmail.com>
|
||||
Ross Smith II (rasa) <ross@smithii.com>
|
||||
rubenbe <github-com-00ff86@vandamme.email>
|
||||
Ryan Sullivan (KayoticSully) <kayoticsully@gmail.com>
|
||||
Sacheendra Talluri (sacheendra) <sacheendra.t@gmail.com>
|
||||
Scott Klupfel (kluppy) <kluppy@going2blue.com>
|
||||
Sergey Mishin (ralder) <ralder@yandex.ru>
|
||||
Simon Frei (imsodin) <freisim93@gmail.com>
|
||||
Sly_tom_cat <slytomcat@mail.ru>
|
||||
Stefan Kuntz (Stefan-Code) <stefan.github@gmail.com> <Stefan.github@gmail.com>
|
||||
Stefan Tatschner (rumpelsepp) <stefan@sevenbyte.org> <rumpelsepp@sevenbyte.org>
|
||||
Suhas Gundimeda (snugghash) <suhas.gundimeda@gmail.com> <snugghash@gmail.com>
|
||||
Taylor Khan (nelsonkhan) <nelsonkhan@gmail.com>
|
||||
Thomas Hipp <thomashipp@gmail.com>
|
||||
Tim Abell (timabell) <tim@timwise.co.uk>
|
||||
Tim Howes (timhowes) <timhowes@berkeley.edu>
|
||||
Tobias Nygren (tnn2) <tnn@nygren.pp.se>
|
||||
Tobias Tom (tobiastom) <t.tom@succont.de>
|
||||
Tomas Cerveny (kozec) <kozec@kozec.com>
|
||||
Tommy Thorn <tommy-github-email@thorn.ws>
|
||||
Tully Robinson (tojrobinson) <tully@tojr.org>
|
||||
Tyler Brazier (tylerbrazier) <tyler@tylerbrazier.com>
|
||||
Unrud (Unrud) <unrud@openaliasbox.org> <Unrud@users.noreply.github.com>
|
||||
Veeti Paananen (veeti) <veeti.paananen@rojekti.fi>
|
||||
Victor Buinsky (buinsky) <vix_booja@tut.by>
|
||||
Vil Brekin (Vilbrekin) <vilbrekin@gmail.com>
|
||||
Vladimir Rusinov <vrusinov@google.com>
|
||||
wangguoliang <liangcszzu@163.com>
|
||||
William A. Kennington III (wkennington) <william@wkennington.com>
|
||||
Wulf Weich (wweich) <wweich@users.noreply.github.com> <wweich@gmx.de> <wulf@weich-kr.de>
|
||||
Xavier O. (damajor) <damajor@gmail.com>
|
||||
xjtdy888 (xjtdy888) <xjtdy888@163.com>
|
||||
Yannic A. (eipiminus1) <eipiminusone+github@gmail.com> <eipiminus1@users.noreply.github.com>
|
||||
佛跳墙 <daoquan@qq.com>
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
education, socio-economic status, nationality, personal appearance, race,
|
||||
religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at security@syncthing.net. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
+23
-50
@@ -12,7 +12,7 @@ least the following:
|
||||
- What operating system, operating system version and version of
|
||||
Syncthing you are running
|
||||
|
||||
- The same for other connected nodes, where relevant
|
||||
- The same for other connected devices, where relevant
|
||||
|
||||
- Screenshot if the issue concerns something visible in the GUI
|
||||
|
||||
@@ -31,60 +31,33 @@ latest info on Transifex.
|
||||
|
||||
## Contributing Code
|
||||
|
||||
Please do contribute! If you want to contribute but are unsure where to
|
||||
start, the [Contributions Needed
|
||||
topic](http://discourse.syncthing.net/t/49) lists areas in need of
|
||||
attention. In general, any open issues are fair game!
|
||||
Every contribution is welcome. If you want to contribute but are unsure
|
||||
where to start, any open issues are fair game! See the [Contribution
|
||||
Guidelines](https://docs.syncthing.net/dev/contributing.html) for the full
|
||||
story on committing code.
|
||||
|
||||
## Contributing Documentation
|
||||
|
||||
Updates to the [documentation site](https://docs.syncthing.net/) can be
|
||||
made as pull requests on the [documentation
|
||||
repository](https://github.com/syncthing/docs).
|
||||
|
||||
## Licensing
|
||||
|
||||
All contributions are made under the same MIT License as the rest of the
|
||||
project, except documentation which is licensed under the Creative
|
||||
Commons Attribution 4.0 International License. You retain the copyright
|
||||
to code you have written.
|
||||
All contributions are made available under the same license as the already
|
||||
existing material being contributed to. For most of the project and unless
|
||||
otherwise stated this means MPLv2, but there are exceptions:
|
||||
|
||||
When accepting your first contribution, the maintainer of the project
|
||||
will ensure that you are added to the CONTRIBUTORS file. You are welcome
|
||||
to add yourself as a separate commit in your first pull request.
|
||||
- Certain commands (under cmd/...) may have a separate license, indicated by
|
||||
the presence of a LICENSE file in the corresponding directory.
|
||||
|
||||
## Building
|
||||
- The documentation (man/...) is licensed under the Creative Commons
|
||||
Attribution 4.0 International License.
|
||||
|
||||
[See the documentation](http://discourse.syncthing.net/t/44) on how to
|
||||
get started with a build environment.
|
||||
- Projects under vendor/... are copyright by and licensed from their
|
||||
respective original authors. Contributions should be made to the original
|
||||
project, not here.
|
||||
|
||||
## Branches
|
||||
Regardless of the license in effect, you retain the copyright to your
|
||||
contribution.
|
||||
|
||||
- `master` is the main branch containing good code that will end up in
|
||||
the next release. You should base your work on it. It won't ever be
|
||||
rebased or force-pushed to.
|
||||
|
||||
- `vx.y` branches exist to make patch releases on otherwise obsolete
|
||||
minor releases. Should only contain fixes cherry picked from master.
|
||||
Don't base any work on them.
|
||||
|
||||
- Other branches are probably topic branches and may be subject to
|
||||
rebasing. Don't base any work on them unless you specifically know
|
||||
otherwise.
|
||||
|
||||
## Tags
|
||||
|
||||
All releases are tagged semver style as `vx.y.z`. Release tags are
|
||||
signed by GPG key BCE524C7.
|
||||
|
||||
## Tests
|
||||
|
||||
Yes please!
|
||||
|
||||
## Style
|
||||
|
||||
- `go fmt`
|
||||
|
||||
- Unix line breaks
|
||||
|
||||
## Documentation
|
||||
|
||||
[Over here!](http://discourse.syncthing.net/category/documentation)
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
Aaron Bieber <qbit@deftly.net>
|
||||
Andrew Dunham <andrew@du.nham.ca>
|
||||
Audrius Butkevicius <audrius.butkevicius@gmail.com>
|
||||
Arthur Axel fREW Schmidt <frew@afoolishmanifesto.com>
|
||||
Ben Sidhom <bsidhom@gmail.com>
|
||||
Brandon Philips <brandon@ifup.org>
|
||||
Gilli Sigurdsson <gilli@vx.is>
|
||||
James Patterson <jamespatterson@operamail.com>
|
||||
Jens Diemer <github.com@jensdiemer.de>
|
||||
Philippe Schommers <philippe@schommers.be>
|
||||
Ryan Sullivan <kayoticsully@gmail.com>
|
||||
Tully Robinson <tully@tojr.org>
|
||||
Veeti Paananen <veeti.paananen@rojekti.fi>
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
FROM golang:1.11 AS builder
|
||||
|
||||
WORKDIR /go/src/github.com/syncthing/syncthing
|
||||
COPY . .
|
||||
|
||||
ENV CGO_ENABLED=0
|
||||
ENV BUILD_HOST=syncthing.net
|
||||
ENV BUILD_USER=docker
|
||||
RUN rm -f syncthing && go run build.go -no-upgrade build syncthing
|
||||
|
||||
FROM alpine
|
||||
|
||||
EXPOSE 8384 22000 21027/udp
|
||||
|
||||
VOLUME ["/var/syncthing"]
|
||||
|
||||
RUN apk add --no-cache ca-certificates su-exec
|
||||
|
||||
COPY --from=builder /go/src/github.com/syncthing/syncthing/syncthing /bin/syncthing
|
||||
|
||||
ENV PUID=1000 PGID=1000
|
||||
|
||||
HEALTHCHECK --interval=1m --timeout=10s \
|
||||
CMD nc -z localhost 8384 || exit 1
|
||||
|
||||
ENTRYPOINT \
|
||||
chown "${PUID}:${PGID}" /var/syncthing \
|
||||
&& su-exec "${PUID}:${PGID}" \
|
||||
env HOME=/var/syncthing \
|
||||
/bin/syncthing \
|
||||
-home /var/syncthing/config \
|
||||
-gui-address 0.0.0.0:8384
|
||||
@@ -0,0 +1,83 @@
|
||||
# The Syncthing Goals
|
||||
|
||||
Syncthing is a **continuous file synchronization program**. It synchronizes
|
||||
files between two or more computers. We strive to fulfill the goals below.
|
||||
The goals are listed in order of importance, the most important one being
|
||||
the first.
|
||||
|
||||
> "Syncing files" here is precise. It means we specifically exclude things
|
||||
> that are not files - calendar items, instant messages, and so on. If those
|
||||
> are in fact stored as files on disk, they can of course be synced as
|
||||
> files.
|
||||
|
||||
Syncthing should be:
|
||||
|
||||
### 1. Safe From Data Loss
|
||||
|
||||
Protecting the user's data is paramount. We take every reasonable precaution
|
||||
to avoid corrupting the user's files.
|
||||
|
||||
> This is the overriding goal, without which synchronizing files becomes
|
||||
> pointless. This means that we do not make unsafe trade offs for the sake
|
||||
> of performance or, in some cases, even usability.
|
||||
|
||||
### 2. Secure Against Attackers
|
||||
|
||||
Again, protecting the user's data is paramount. Regardless of our other
|
||||
goals we must never allow the user's data to be susceptible to eavesdropping
|
||||
or modification by unauthorized parties.
|
||||
|
||||
> This should be understood in context. It is not necessarily reasonable to
|
||||
> expect Syncthing to be resistant against well equipped state level
|
||||
> attackers. We will however do our best. Note also that this is different
|
||||
> from anonymity which is not, currently, a goal.
|
||||
|
||||
### 3. Easy to Use
|
||||
|
||||
Syncthing should be approachable, understandable and inclusive.
|
||||
|
||||
> Complex concepts and maths form the base of Syncthing's functionality.
|
||||
> This should nonetheless be abstracted or hidden to a degree where
|
||||
> Syncthing is usable by the general public.
|
||||
|
||||
### 4. Automatic
|
||||
|
||||
User interaction should be required only when absolutely necessary.
|
||||
|
||||
> Specifically this means that changes to files are picked up without
|
||||
> prompting, conflicts are resolved without prompting and connections are
|
||||
> maintained without prompting. We only prompt the user when it is required
|
||||
> to fulfill one of the (overriding) Secure, Safe or Easy goals.
|
||||
|
||||
### 5. Universally Available
|
||||
|
||||
Syncthing should run on every common computer. We are mindful that the
|
||||
latest technology is not always available to any given individual.
|
||||
|
||||
> Computers include desktops, laptops, servers, virtual machines, small
|
||||
> general purpose computers such as Raspberry Pis and, *where possible*,
|
||||
> tablets and phones. NAS appliances, toasters, cars, firearms, thermostats
|
||||
> and so on may include computing capabitilies but it is not our goal for
|
||||
> Syncthing to run smoothly on these devices.
|
||||
|
||||
### 6. For Individuals
|
||||
|
||||
Syncthing is primarily about empowering the individual user with safe,
|
||||
secure and easy to use file synchronization.
|
||||
|
||||
> We acknowledge that it's also useful in an enterprise setting and include
|
||||
> functionality to support that. If this is in conflict with the
|
||||
> requirements of the individual, those will however take priority.
|
||||
|
||||
### 7. Everything Else
|
||||
|
||||
There are many things we care about that don't make it on to the list. It is
|
||||
fine to optimize for these values as well, as long as they are not in
|
||||
conflict with the stated goals above.
|
||||
|
||||
> For example, performance is a thing we care about. We just don't care more
|
||||
> about it than safety, security, etc. Maintainability of the code base and
|
||||
> providing entertainment value for the maintainers are also things that
|
||||
> matter. It is understood that there are aspects of Syncthing that are
|
||||
> suboptimal or even in opposition with the goals above. However, we
|
||||
> continuously strive to align Syncthing more and more with these goals.
|
||||
Generated
-67
@@ -1,67 +0,0 @@
|
||||
{
|
||||
"ImportPath": "github.com/syncthing/syncthing",
|
||||
"GoVersion": "go1.3",
|
||||
"Packages": [
|
||||
"./cmd/..."
|
||||
],
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "bitbucket.org/kardianos/osext",
|
||||
"Comment": "null-13",
|
||||
"Rev": "5d3ddcf53a508cc2f7404eaebf546ef2cb5cdb6e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "code.google.com/p/go.crypto/bcrypt",
|
||||
"Comment": "null-216",
|
||||
"Rev": "41cd4647fccc72b0b79ef1bd1fe6735e718257cd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "code.google.com/p/go.crypto/blowfish",
|
||||
"Comment": "null-216",
|
||||
"Rev": "41cd4647fccc72b0b79ef1bd1fe6735e718257cd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "code.google.com/p/go.text/transform",
|
||||
"Comment": "null-90",
|
||||
"Rev": "d65bffbc88a153d23a6d2a864531e6e7c2cde59b"
|
||||
},
|
||||
{
|
||||
"ImportPath": "code.google.com/p/go.text/unicode/norm",
|
||||
"Comment": "null-90",
|
||||
"Rev": "d65bffbc88a153d23a6d2a864531e6e7c2cde59b"
|
||||
},
|
||||
{
|
||||
"ImportPath": "code.google.com/p/snappy-go/snappy",
|
||||
"Comment": "null-15",
|
||||
"Rev": "12e4b4183793ac4b061921e7980845e750679fd0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/bkaradzic/go-lz4",
|
||||
"Rev": "77e2ba877bde9da31213bec75dbbe197fa507c21"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/calmh/xdr",
|
||||
"Rev": "e1714bbe4764b15490fcc8ebd25d4bd9ea50a4b9"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/juju/ratelimit",
|
||||
"Rev": "f9f36d11773655c0485207f0ad30dc2655f69d56"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/syndtr/goleveldb/leveldb",
|
||||
"Rev": "c9d6b7be1428942d4cf4f54055b991a8513392eb"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vitrun/qart/coding",
|
||||
"Rev": "ccb109cf25f0cd24474da73b9fee4e7a3e8a8ce0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vitrun/qart/gf256",
|
||||
"Rev": "ccb109cf25f0cd24474da73b9fee4e7a3e8a8ce0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vitrun/qart/qr",
|
||||
"Rev": "ccb109cf25f0cd24474da73b9fee4e7a3e8a8ce0"
|
||||
}
|
||||
]
|
||||
}
|
||||
Generated
-5
@@ -1,5 +0,0 @@
|
||||
This directory tree is generated automatically by godep.
|
||||
|
||||
Please do not edit.
|
||||
|
||||
See https://github.com/tools/godep for more information.
|
||||
Generated
-2
@@ -1,2 +0,0 @@
|
||||
/pkg
|
||||
/bin
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
Copyright (c) 2012 Daniel Theophanes
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Extensions to the standard "os" package.
|
||||
package osext
|
||||
|
||||
import "path/filepath"
|
||||
|
||||
// Executable returns an absolute path that can be used to
|
||||
// re-invoke the current program.
|
||||
// It may not be valid after the current program exits.
|
||||
func Executable() (string, error) {
|
||||
p, err := executable()
|
||||
return filepath.Clean(p), err
|
||||
}
|
||||
|
||||
// Returns same path as Executable, returns just the folder
|
||||
// path. Excludes the executable name.
|
||||
func ExecutableFolder() (string, error) {
|
||||
p, err := Executable()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
folder, _ := filepath.Split(p)
|
||||
return folder, nil
|
||||
}
|
||||
|
||||
// Depricated. Same as Executable().
|
||||
func GetExePath() (exePath string, err error) {
|
||||
return Executable()
|
||||
}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package osext
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func executable() (string, error) {
|
||||
f, err := os.Open("/proc/" + strconv.Itoa(os.Getpid()) + "/text")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
return syscall.Fd2path(int(f.Fd()))
|
||||
}
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux netbsd openbsd
|
||||
|
||||
package osext
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func executable() (string, error) {
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
return os.Readlink("/proc/self/exe")
|
||||
case "netbsd":
|
||||
return os.Readlink("/proc/curproc/exe")
|
||||
case "openbsd":
|
||||
return os.Readlink("/proc/curproc/file")
|
||||
}
|
||||
return "", errors.New("ExecPath not implemented for " + runtime.GOOS)
|
||||
}
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build darwin freebsd
|
||||
|
||||
package osext
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var initCwd, initCwdErr = os.Getwd()
|
||||
|
||||
func executable() (string, error) {
|
||||
var mib [4]int32
|
||||
switch runtime.GOOS {
|
||||
case "freebsd":
|
||||
mib = [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1}
|
||||
case "darwin":
|
||||
mib = [4]int32{1 /* CTL_KERN */, 38 /* KERN_PROCARGS */, int32(os.Getpid()), -1}
|
||||
}
|
||||
|
||||
n := uintptr(0)
|
||||
// Get length.
|
||||
_, _, errNum := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
if errNum != 0 {
|
||||
return "", errNum
|
||||
}
|
||||
if n == 0 { // This shouldn't happen.
|
||||
return "", nil
|
||||
}
|
||||
buf := make([]byte, n)
|
||||
_, _, errNum = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
if errNum != 0 {
|
||||
return "", errNum
|
||||
}
|
||||
if n == 0 { // This shouldn't happen.
|
||||
return "", nil
|
||||
}
|
||||
for i, v := range buf {
|
||||
if v == 0 {
|
||||
buf = buf[:i]
|
||||
break
|
||||
}
|
||||
}
|
||||
var err error
|
||||
execPath := string(buf)
|
||||
// execPath will not be empty due to above checks.
|
||||
// Try to get the absolute path if the execPath is not rooted.
|
||||
if execPath[0] != '/' {
|
||||
execPath, err = getAbs(execPath)
|
||||
if err != nil {
|
||||
return execPath, err
|
||||
}
|
||||
}
|
||||
// For darwin KERN_PROCARGS may return the path to a symlink rather than the
|
||||
// actual executable.
|
||||
if runtime.GOOS == "darwin" {
|
||||
if execPath, err = filepath.EvalSymlinks(execPath); err != nil {
|
||||
return execPath, err
|
||||
}
|
||||
}
|
||||
return execPath, nil
|
||||
}
|
||||
|
||||
func getAbs(execPath string) (string, error) {
|
||||
if initCwdErr != nil {
|
||||
return execPath, initCwdErr
|
||||
}
|
||||
// The execPath may begin with a "../" or a "./" so clean it first.
|
||||
// Join the two paths, trailing and starting slashes undetermined, so use
|
||||
// the generic Join function.
|
||||
return filepath.Join(initCwd, filepath.Clean(execPath)), nil
|
||||
}
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build darwin linux freebsd netbsd windows
|
||||
|
||||
package osext
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
oexec "os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const execPath_EnvVar = "OSTEST_OUTPUT_EXECPATH"
|
||||
|
||||
func TestExecPath(t *testing.T) {
|
||||
ep, err := Executable()
|
||||
if err != nil {
|
||||
t.Fatalf("ExecPath failed: %v", err)
|
||||
}
|
||||
// we want fn to be of the form "dir/prog"
|
||||
dir := filepath.Dir(filepath.Dir(ep))
|
||||
fn, err := filepath.Rel(dir, ep)
|
||||
if err != nil {
|
||||
t.Fatalf("filepath.Rel: %v", err)
|
||||
}
|
||||
cmd := &oexec.Cmd{}
|
||||
// make child start with a relative program path
|
||||
cmd.Dir = dir
|
||||
cmd.Path = fn
|
||||
// forge argv[0] for child, so that we can verify we could correctly
|
||||
// get real path of the executable without influenced by argv[0].
|
||||
cmd.Args = []string{"-", "-test.run=XXXX"}
|
||||
cmd.Env = []string{fmt.Sprintf("%s=1", execPath_EnvVar)}
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("exec(self) failed: %v", err)
|
||||
}
|
||||
outs := string(out)
|
||||
if !filepath.IsAbs(outs) {
|
||||
t.Fatalf("Child returned %q, want an absolute path", out)
|
||||
}
|
||||
if !sameFile(outs, ep) {
|
||||
t.Fatalf("Child returned %q, not the same file as %q", out, ep)
|
||||
}
|
||||
}
|
||||
|
||||
func sameFile(fn1, fn2 string) bool {
|
||||
fi1, err := os.Stat(fn1)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
fi2, err := os.Stat(fn2)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return os.SameFile(fi1, fi2)
|
||||
}
|
||||
|
||||
func init() {
|
||||
if e := os.Getenv(execPath_EnvVar); e != "" {
|
||||
// first chdir to another path
|
||||
dir := "/"
|
||||
if runtime.GOOS == "windows" {
|
||||
dir = filepath.VolumeName(".")
|
||||
}
|
||||
os.Chdir(dir)
|
||||
if ep, err := Executable(); err != nil {
|
||||
fmt.Fprint(os.Stderr, "ERROR: ", err)
|
||||
} else {
|
||||
fmt.Fprint(os.Stderr, ep)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package osext
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
kernel = syscall.MustLoadDLL("kernel32.dll")
|
||||
getModuleFileNameProc = kernel.MustFindProc("GetModuleFileNameW")
|
||||
)
|
||||
|
||||
// GetModuleFileName() with hModule = NULL
|
||||
func executable() (exePath string, err error) {
|
||||
return getModuleFileName()
|
||||
}
|
||||
|
||||
func getModuleFileName() (string, error) {
|
||||
var n uint32
|
||||
b := make([]uint16, syscall.MAX_PATH)
|
||||
size := uint32(len(b))
|
||||
|
||||
r0, _, e1 := getModuleFileNameProc.Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(size))
|
||||
n = uint32(r0)
|
||||
if n == 0 {
|
||||
return "", e1
|
||||
}
|
||||
return string(utf16.Decode(b[0:n])), nil
|
||||
}
|
||||
-226
@@ -1,226 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bcrypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBcryptingIsEasy(t *testing.T) {
|
||||
pass := []byte("mypassword")
|
||||
hp, err := GenerateFromPassword(pass, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateFromPassword error: %s", err)
|
||||
}
|
||||
|
||||
if CompareHashAndPassword(hp, pass) != nil {
|
||||
t.Errorf("%v should hash %s correctly", hp, pass)
|
||||
}
|
||||
|
||||
notPass := "notthepass"
|
||||
err = CompareHashAndPassword(hp, []byte(notPass))
|
||||
if err != ErrMismatchedHashAndPassword {
|
||||
t.Errorf("%v and %s should be mismatched", hp, notPass)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcryptingIsCorrect(t *testing.T) {
|
||||
pass := []byte("allmine")
|
||||
salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
|
||||
expectedHash := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga")
|
||||
|
||||
hash, err := bcrypt(pass, 10, salt)
|
||||
if err != nil {
|
||||
t.Fatalf("bcrypt blew up: %v", err)
|
||||
}
|
||||
if !bytes.HasSuffix(expectedHash, hash) {
|
||||
t.Errorf("%v should be the suffix of %v", hash, expectedHash)
|
||||
}
|
||||
|
||||
h, err := newFromHash(expectedHash)
|
||||
if err != nil {
|
||||
t.Errorf("Unable to parse %s: %v", string(expectedHash), err)
|
||||
}
|
||||
|
||||
// This is not the safe way to compare these hashes. We do this only for
|
||||
// testing clarity. Use bcrypt.CompareHashAndPassword()
|
||||
if err == nil && !bytes.Equal(expectedHash, h.Hash()) {
|
||||
t.Errorf("Parsed hash %v should equal %v", h.Hash(), expectedHash)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVeryShortPasswords(t *testing.T) {
|
||||
key := []byte("k")
|
||||
salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
|
||||
_, err := bcrypt(key, 10, salt)
|
||||
if err != nil {
|
||||
t.Errorf("One byte key resulted in error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTooLongPasswordsWork(t *testing.T) {
|
||||
salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
|
||||
// One byte over the usual 56 byte limit that blowfish has
|
||||
tooLongPass := []byte("012345678901234567890123456789012345678901234567890123456")
|
||||
tooLongExpected := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C")
|
||||
hash, err := bcrypt(tooLongPass, 10, salt)
|
||||
if err != nil {
|
||||
t.Fatalf("bcrypt blew up on long password: %v", err)
|
||||
}
|
||||
if !bytes.HasSuffix(tooLongExpected, hash) {
|
||||
t.Errorf("%v should be the suffix of %v", hash, tooLongExpected)
|
||||
}
|
||||
}
|
||||
|
||||
type InvalidHashTest struct {
|
||||
err error
|
||||
hash []byte
|
||||
}
|
||||
|
||||
var invalidTests = []InvalidHashTest{
|
||||
{ErrHashTooShort, []byte("$2a$10$fooo")},
|
||||
{ErrHashTooShort, []byte("$2a")},
|
||||
{HashVersionTooNewError('3'), []byte("$3a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
|
||||
{InvalidHashPrefixError('%'), []byte("%2a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
|
||||
{InvalidCostError(32), []byte("$2a$32$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
|
||||
}
|
||||
|
||||
func TestInvalidHashErrors(t *testing.T) {
|
||||
check := func(name string, expected, err error) {
|
||||
if err == nil {
|
||||
t.Errorf("%s: Should have returned an error", name)
|
||||
}
|
||||
if err != nil && err != expected {
|
||||
t.Errorf("%s gave err %v but should have given %v", name, err, expected)
|
||||
}
|
||||
}
|
||||
for _, iht := range invalidTests {
|
||||
_, err := newFromHash(iht.hash)
|
||||
check("newFromHash", iht.err, err)
|
||||
err = CompareHashAndPassword(iht.hash, []byte("anything"))
|
||||
check("CompareHashAndPassword", iht.err, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnpaddedBase64Encoding(t *testing.T) {
|
||||
original := []byte{101, 201, 101, 75, 19, 227, 199, 20, 239, 236, 133, 32, 30, 109, 243, 30}
|
||||
encodedOriginal := []byte("XajjQvNhvvRt5GSeFk1xFe")
|
||||
|
||||
encoded := base64Encode(original)
|
||||
|
||||
if !bytes.Equal(encodedOriginal, encoded) {
|
||||
t.Errorf("Encoded %v should have equaled %v", encoded, encodedOriginal)
|
||||
}
|
||||
|
||||
decoded, err := base64Decode(encodedOriginal)
|
||||
if err != nil {
|
||||
t.Fatalf("base64Decode blew up: %s", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(decoded, original) {
|
||||
t.Errorf("Decoded %v should have equaled %v", decoded, original)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCost(t *testing.T) {
|
||||
suffix := "XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C"
|
||||
for _, vers := range []string{"2a", "2"} {
|
||||
for _, cost := range []int{4, 10} {
|
||||
s := fmt.Sprintf("$%s$%02d$%s", vers, cost, suffix)
|
||||
h := []byte(s)
|
||||
actual, err := Cost(h)
|
||||
if err != nil {
|
||||
t.Errorf("Cost, error: %s", err)
|
||||
continue
|
||||
}
|
||||
if actual != cost {
|
||||
t.Errorf("Cost, expected: %d, actual: %d", cost, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
_, err := Cost([]byte("$a$a$" + suffix))
|
||||
if err == nil {
|
||||
t.Errorf("Cost, malformed but no error returned")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCostValidationInHash(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
pass := []byte("mypassword")
|
||||
|
||||
for c := 0; c < MinCost; c++ {
|
||||
p, _ := newFromPassword(pass, c)
|
||||
if p.cost != DefaultCost {
|
||||
t.Errorf("newFromPassword should default costs below %d to %d, but was %d", MinCost, DefaultCost, p.cost)
|
||||
}
|
||||
}
|
||||
|
||||
p, _ := newFromPassword(pass, 14)
|
||||
if p.cost != 14 {
|
||||
t.Errorf("newFromPassword should default cost to 14, but was %d", p.cost)
|
||||
}
|
||||
|
||||
hp, _ := newFromHash(p.Hash())
|
||||
if p.cost != hp.cost {
|
||||
t.Errorf("newFromHash should maintain the cost at %d, but was %d", p.cost, hp.cost)
|
||||
}
|
||||
|
||||
_, err := newFromPassword(pass, 32)
|
||||
if err == nil {
|
||||
t.Fatalf("newFromPassword: should return a cost error")
|
||||
}
|
||||
if err != InvalidCostError(32) {
|
||||
t.Errorf("newFromPassword: should return cost error, got %#v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCostReturnsWithLeadingZeroes(t *testing.T) {
|
||||
hp, _ := newFromPassword([]byte("abcdefgh"), 7)
|
||||
cost := hp.Hash()[4:7]
|
||||
expected := []byte("07$")
|
||||
|
||||
if !bytes.Equal(expected, cost) {
|
||||
t.Errorf("single digit costs in hash should have leading zeros: was %v instead of %v", cost, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinorNotRequired(t *testing.T) {
|
||||
noMinorHash := []byte("$2$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga")
|
||||
h, err := newFromHash(noMinorHash)
|
||||
if err != nil {
|
||||
t.Fatalf("No minor hash blew up: %s", err)
|
||||
}
|
||||
if h.minor != 0 {
|
||||
t.Errorf("Should leave minor version at 0, but was %d", h.minor)
|
||||
}
|
||||
|
||||
if !bytes.Equal(noMinorHash, h.Hash()) {
|
||||
t.Errorf("Should generate hash %v, but created %v", noMinorHash, h.Hash())
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEqual(b *testing.B) {
|
||||
b.StopTimer()
|
||||
passwd := []byte("somepasswordyoulike")
|
||||
hash, _ := GenerateFromPassword(passwd, 10)
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
CompareHashAndPassword(hash, passwd)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGeneration(b *testing.B) {
|
||||
b.StopTimer()
|
||||
passwd := []byte("mylongpassword1234")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
GenerateFromPassword(passwd, 10)
|
||||
}
|
||||
}
|
||||
-274
@@ -1,274 +0,0 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blowfish
|
||||
|
||||
import "testing"
|
||||
|
||||
type CryptTest struct {
|
||||
key []byte
|
||||
in []byte
|
||||
out []byte
|
||||
}
|
||||
|
||||
// Test vector values are from http://www.schneier.com/code/vectors.txt.
|
||||
var encryptTests = []CryptTest{
|
||||
{
|
||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
[]byte{0x4E, 0xF9, 0x97, 0x45, 0x61, 0x98, 0xDD, 0x78}},
|
||||
{
|
||||
[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
||||
[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
||||
[]byte{0x51, 0x86, 0x6F, 0xD5, 0xB8, 0x5E, 0xCB, 0x8A}},
|
||||
{
|
||||
[]byte{0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
[]byte{0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
|
||||
[]byte{0x7D, 0x85, 0x6F, 0x9A, 0x61, 0x30, 0x63, 0xF2}},
|
||||
{
|
||||
[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
|
||||
[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
|
||||
[]byte{0x24, 0x66, 0xDD, 0x87, 0x8B, 0x96, 0x3C, 0x9D}},
|
||||
|
||||
{
|
||||
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
|
||||
[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
|
||||
[]byte{0x61, 0xF9, 0xC3, 0x80, 0x22, 0x81, 0xB0, 0x96}},
|
||||
{
|
||||
[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
|
||||
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
|
||||
[]byte{0x7D, 0x0C, 0xC6, 0x30, 0xAF, 0xDA, 0x1E, 0xC7}},
|
||||
{
|
||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
[]byte{0x4E, 0xF9, 0x97, 0x45, 0x61, 0x98, 0xDD, 0x78}},
|
||||
{
|
||||
[]byte{0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10},
|
||||
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
|
||||
[]byte{0x0A, 0xCE, 0xAB, 0x0F, 0xC6, 0xA0, 0xA2, 0x8D}},
|
||||
{
|
||||
[]byte{0x7C, 0xA1, 0x10, 0x45, 0x4A, 0x1A, 0x6E, 0x57},
|
||||
[]byte{0x01, 0xA1, 0xD6, 0xD0, 0x39, 0x77, 0x67, 0x42},
|
||||
[]byte{0x59, 0xC6, 0x82, 0x45, 0xEB, 0x05, 0x28, 0x2B}},
|
||||
{
|
||||
[]byte{0x01, 0x31, 0xD9, 0x61, 0x9D, 0xC1, 0x37, 0x6E},
|
||||
[]byte{0x5C, 0xD5, 0x4C, 0xA8, 0x3D, 0xEF, 0x57, 0xDA},
|
||||
[]byte{0xB1, 0xB8, 0xCC, 0x0B, 0x25, 0x0F, 0x09, 0xA0}},
|
||||
{
|
||||
[]byte{0x07, 0xA1, 0x13, 0x3E, 0x4A, 0x0B, 0x26, 0x86},
|
||||
[]byte{0x02, 0x48, 0xD4, 0x38, 0x06, 0xF6, 0x71, 0x72},
|
||||
[]byte{0x17, 0x30, 0xE5, 0x77, 0x8B, 0xEA, 0x1D, 0xA4}},
|
||||
{
|
||||
[]byte{0x38, 0x49, 0x67, 0x4C, 0x26, 0x02, 0x31, 0x9E},
|
||||
[]byte{0x51, 0x45, 0x4B, 0x58, 0x2D, 0xDF, 0x44, 0x0A},
|
||||
[]byte{0xA2, 0x5E, 0x78, 0x56, 0xCF, 0x26, 0x51, 0xEB}},
|
||||
{
|
||||
[]byte{0x04, 0xB9, 0x15, 0xBA, 0x43, 0xFE, 0xB5, 0xB6},
|
||||
[]byte{0x42, 0xFD, 0x44, 0x30, 0x59, 0x57, 0x7F, 0xA2},
|
||||
[]byte{0x35, 0x38, 0x82, 0xB1, 0x09, 0xCE, 0x8F, 0x1A}},
|
||||
{
|
||||
[]byte{0x01, 0x13, 0xB9, 0x70, 0xFD, 0x34, 0xF2, 0xCE},
|
||||
[]byte{0x05, 0x9B, 0x5E, 0x08, 0x51, 0xCF, 0x14, 0x3A},
|
||||
[]byte{0x48, 0xF4, 0xD0, 0x88, 0x4C, 0x37, 0x99, 0x18}},
|
||||
{
|
||||
[]byte{0x01, 0x70, 0xF1, 0x75, 0x46, 0x8F, 0xB5, 0xE6},
|
||||
[]byte{0x07, 0x56, 0xD8, 0xE0, 0x77, 0x47, 0x61, 0xD2},
|
||||
[]byte{0x43, 0x21, 0x93, 0xB7, 0x89, 0x51, 0xFC, 0x98}},
|
||||
{
|
||||
[]byte{0x43, 0x29, 0x7F, 0xAD, 0x38, 0xE3, 0x73, 0xFE},
|
||||
[]byte{0x76, 0x25, 0x14, 0xB8, 0x29, 0xBF, 0x48, 0x6A},
|
||||
[]byte{0x13, 0xF0, 0x41, 0x54, 0xD6, 0x9D, 0x1A, 0xE5}},
|
||||
{
|
||||
[]byte{0x07, 0xA7, 0x13, 0x70, 0x45, 0xDA, 0x2A, 0x16},
|
||||
[]byte{0x3B, 0xDD, 0x11, 0x90, 0x49, 0x37, 0x28, 0x02},
|
||||
[]byte{0x2E, 0xED, 0xDA, 0x93, 0xFF, 0xD3, 0x9C, 0x79}},
|
||||
{
|
||||
[]byte{0x04, 0x68, 0x91, 0x04, 0xC2, 0xFD, 0x3B, 0x2F},
|
||||
[]byte{0x26, 0x95, 0x5F, 0x68, 0x35, 0xAF, 0x60, 0x9A},
|
||||
[]byte{0xD8, 0x87, 0xE0, 0x39, 0x3C, 0x2D, 0xA6, 0xE3}},
|
||||
{
|
||||
[]byte{0x37, 0xD0, 0x6B, 0xB5, 0x16, 0xCB, 0x75, 0x46},
|
||||
[]byte{0x16, 0x4D, 0x5E, 0x40, 0x4F, 0x27, 0x52, 0x32},
|
||||
[]byte{0x5F, 0x99, 0xD0, 0x4F, 0x5B, 0x16, 0x39, 0x69}},
|
||||
{
|
||||
[]byte{0x1F, 0x08, 0x26, 0x0D, 0x1A, 0xC2, 0x46, 0x5E},
|
||||
[]byte{0x6B, 0x05, 0x6E, 0x18, 0x75, 0x9F, 0x5C, 0xCA},
|
||||
[]byte{0x4A, 0x05, 0x7A, 0x3B, 0x24, 0xD3, 0x97, 0x7B}},
|
||||
{
|
||||
[]byte{0x58, 0x40, 0x23, 0x64, 0x1A, 0xBA, 0x61, 0x76},
|
||||
[]byte{0x00, 0x4B, 0xD6, 0xEF, 0x09, 0x17, 0x60, 0x62},
|
||||
[]byte{0x45, 0x20, 0x31, 0xC1, 0xE4, 0xFA, 0xDA, 0x8E}},
|
||||
{
|
||||
[]byte{0x02, 0x58, 0x16, 0x16, 0x46, 0x29, 0xB0, 0x07},
|
||||
[]byte{0x48, 0x0D, 0x39, 0x00, 0x6E, 0xE7, 0x62, 0xF2},
|
||||
[]byte{0x75, 0x55, 0xAE, 0x39, 0xF5, 0x9B, 0x87, 0xBD}},
|
||||
{
|
||||
[]byte{0x49, 0x79, 0x3E, 0xBC, 0x79, 0xB3, 0x25, 0x8F},
|
||||
[]byte{0x43, 0x75, 0x40, 0xC8, 0x69, 0x8F, 0x3C, 0xFA},
|
||||
[]byte{0x53, 0xC5, 0x5F, 0x9C, 0xB4, 0x9F, 0xC0, 0x19}},
|
||||
{
|
||||
[]byte{0x4F, 0xB0, 0x5E, 0x15, 0x15, 0xAB, 0x73, 0xA7},
|
||||
[]byte{0x07, 0x2D, 0x43, 0xA0, 0x77, 0x07, 0x52, 0x92},
|
||||
[]byte{0x7A, 0x8E, 0x7B, 0xFA, 0x93, 0x7E, 0x89, 0xA3}},
|
||||
{
|
||||
[]byte{0x49, 0xE9, 0x5D, 0x6D, 0x4C, 0xA2, 0x29, 0xBF},
|
||||
[]byte{0x02, 0xFE, 0x55, 0x77, 0x81, 0x17, 0xF1, 0x2A},
|
||||
[]byte{0xCF, 0x9C, 0x5D, 0x7A, 0x49, 0x86, 0xAD, 0xB5}},
|
||||
{
|
||||
[]byte{0x01, 0x83, 0x10, 0xDC, 0x40, 0x9B, 0x26, 0xD6},
|
||||
[]byte{0x1D, 0x9D, 0x5C, 0x50, 0x18, 0xF7, 0x28, 0xC2},
|
||||
[]byte{0xD1, 0xAB, 0xB2, 0x90, 0x65, 0x8B, 0xC7, 0x78}},
|
||||
{
|
||||
[]byte{0x1C, 0x58, 0x7F, 0x1C, 0x13, 0x92, 0x4F, 0xEF},
|
||||
[]byte{0x30, 0x55, 0x32, 0x28, 0x6D, 0x6F, 0x29, 0x5A},
|
||||
[]byte{0x55, 0xCB, 0x37, 0x74, 0xD1, 0x3E, 0xF2, 0x01}},
|
||||
{
|
||||
[]byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
|
||||
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
|
||||
[]byte{0xFA, 0x34, 0xEC, 0x48, 0x47, 0xB2, 0x68, 0xB2}},
|
||||
{
|
||||
[]byte{0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E},
|
||||
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
|
||||
[]byte{0xA7, 0x90, 0x79, 0x51, 0x08, 0xEA, 0x3C, 0xAE}},
|
||||
{
|
||||
[]byte{0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE},
|
||||
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
|
||||
[]byte{0xC3, 0x9E, 0x07, 0x2D, 0x9F, 0xAC, 0x63, 0x1D}},
|
||||
{
|
||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
||||
[]byte{0x01, 0x49, 0x33, 0xE0, 0xCD, 0xAF, 0xF6, 0xE4}},
|
||||
{
|
||||
[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
[]byte{0xF2, 0x1E, 0x9A, 0x77, 0xB7, 0x1C, 0x49, 0xBC}},
|
||||
{
|
||||
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
|
||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
[]byte{0x24, 0x59, 0x46, 0x88, 0x57, 0x54, 0x36, 0x9A}},
|
||||
{
|
||||
[]byte{0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10},
|
||||
[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
||||
[]byte{0x6B, 0x5C, 0x5A, 0x9C, 0x5D, 0x9E, 0x0A, 0x5A}},
|
||||
}
|
||||
|
||||
func TestCipherEncrypt(t *testing.T) {
|
||||
for i, tt := range encryptTests {
|
||||
c, err := NewCipher(tt.key)
|
||||
if err != nil {
|
||||
t.Errorf("NewCipher(%d bytes) = %s", len(tt.key), err)
|
||||
continue
|
||||
}
|
||||
ct := make([]byte, len(tt.out))
|
||||
c.Encrypt(ct, tt.in)
|
||||
for j, v := range ct {
|
||||
if v != tt.out[j] {
|
||||
t.Errorf("Cipher.Encrypt, test vector #%d: cipher-text[%d] = %#x, expected %#x", i, j, v, tt.out[j])
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCipherDecrypt(t *testing.T) {
|
||||
for i, tt := range encryptTests {
|
||||
c, err := NewCipher(tt.key)
|
||||
if err != nil {
|
||||
t.Errorf("NewCipher(%d bytes) = %s", len(tt.key), err)
|
||||
continue
|
||||
}
|
||||
pt := make([]byte, len(tt.in))
|
||||
c.Decrypt(pt, tt.out)
|
||||
for j, v := range pt {
|
||||
if v != tt.in[j] {
|
||||
t.Errorf("Cipher.Decrypt, test vector #%d: plain-text[%d] = %#x, expected %#x", i, j, v, tt.in[j])
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaltedCipherKeyLength(t *testing.T) {
|
||||
if _, err := NewSaltedCipher(nil, []byte{'a'}); err != KeySizeError(0) {
|
||||
t.Errorf("NewSaltedCipher with short key, gave error %#v, expected %#v", err, KeySizeError(0))
|
||||
}
|
||||
|
||||
// A 57-byte key. One over the typical blowfish restriction.
|
||||
key := []byte("012345678901234567890123456789012345678901234567890123456")
|
||||
if _, err := NewSaltedCipher(key, []byte{'a'}); err != nil {
|
||||
t.Errorf("NewSaltedCipher with long key, gave error %#v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test vectors generated with Blowfish from OpenSSH.
|
||||
var saltedVectors = [][8]byte{
|
||||
{0x0c, 0x82, 0x3b, 0x7b, 0x8d, 0x01, 0x4b, 0x7e},
|
||||
{0xd1, 0xe1, 0x93, 0xf0, 0x70, 0xa6, 0xdb, 0x12},
|
||||
{0xfc, 0x5e, 0xba, 0xde, 0xcb, 0xf8, 0x59, 0xad},
|
||||
{0x8a, 0x0c, 0x76, 0xe7, 0xdd, 0x2c, 0xd3, 0xa8},
|
||||
{0x2c, 0xcb, 0x7b, 0xee, 0xac, 0x7b, 0x7f, 0xf8},
|
||||
{0xbb, 0xf6, 0x30, 0x6f, 0xe1, 0x5d, 0x62, 0xbf},
|
||||
{0x97, 0x1e, 0xc1, 0x3d, 0x3d, 0xe0, 0x11, 0xe9},
|
||||
{0x06, 0xd7, 0x4d, 0xb1, 0x80, 0xa3, 0xb1, 0x38},
|
||||
{0x67, 0xa1, 0xa9, 0x75, 0x0e, 0x5b, 0xc6, 0xb4},
|
||||
{0x51, 0x0f, 0x33, 0x0e, 0x4f, 0x67, 0xd2, 0x0c},
|
||||
{0xf1, 0x73, 0x7e, 0xd8, 0x44, 0xea, 0xdb, 0xe5},
|
||||
{0x14, 0x0e, 0x16, 0xce, 0x7f, 0x4a, 0x9c, 0x7b},
|
||||
{0x4b, 0xfe, 0x43, 0xfd, 0xbf, 0x36, 0x04, 0x47},
|
||||
{0xb1, 0xeb, 0x3e, 0x15, 0x36, 0xa7, 0xbb, 0xe2},
|
||||
{0x6d, 0x0b, 0x41, 0xdd, 0x00, 0x98, 0x0b, 0x19},
|
||||
{0xd3, 0xce, 0x45, 0xce, 0x1d, 0x56, 0xb7, 0xfc},
|
||||
{0xd9, 0xf0, 0xfd, 0xda, 0xc0, 0x23, 0xb7, 0x93},
|
||||
{0x4c, 0x6f, 0xa1, 0xe4, 0x0c, 0xa8, 0xca, 0x57},
|
||||
{0xe6, 0x2f, 0x28, 0xa7, 0x0c, 0x94, 0x0d, 0x08},
|
||||
{0x8f, 0xe3, 0xf0, 0xb6, 0x29, 0xe3, 0x44, 0x03},
|
||||
{0xff, 0x98, 0xdd, 0x04, 0x45, 0xb4, 0x6d, 0x1f},
|
||||
{0x9e, 0x45, 0x4d, 0x18, 0x40, 0x53, 0xdb, 0xef},
|
||||
{0xb7, 0x3b, 0xef, 0x29, 0xbe, 0xa8, 0x13, 0x71},
|
||||
{0x02, 0x54, 0x55, 0x41, 0x8e, 0x04, 0xfc, 0xad},
|
||||
{0x6a, 0x0a, 0xee, 0x7c, 0x10, 0xd9, 0x19, 0xfe},
|
||||
{0x0a, 0x22, 0xd9, 0x41, 0xcc, 0x23, 0x87, 0x13},
|
||||
{0x6e, 0xff, 0x1f, 0xff, 0x36, 0x17, 0x9c, 0xbe},
|
||||
{0x79, 0xad, 0xb7, 0x40, 0xf4, 0x9f, 0x51, 0xa6},
|
||||
{0x97, 0x81, 0x99, 0xa4, 0xde, 0x9e, 0x9f, 0xb6},
|
||||
{0x12, 0x19, 0x7a, 0x28, 0xd0, 0xdc, 0xcc, 0x92},
|
||||
{0x81, 0xda, 0x60, 0x1e, 0x0e, 0xdd, 0x65, 0x56},
|
||||
{0x7d, 0x76, 0x20, 0xb2, 0x73, 0xc9, 0x9e, 0xee},
|
||||
}
|
||||
|
||||
func TestSaltedCipher(t *testing.T) {
|
||||
var key, salt [32]byte
|
||||
for i := range key {
|
||||
key[i] = byte(i)
|
||||
salt[i] = byte(i + 32)
|
||||
}
|
||||
for i, v := range saltedVectors {
|
||||
c, err := NewSaltedCipher(key[:], salt[:i])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var buf [8]byte
|
||||
c.Encrypt(buf[:], buf[:])
|
||||
if v != buf {
|
||||
t.Errorf("%d: expected %x, got %x", i, v, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkExpandKeyWithSalt(b *testing.B) {
|
||||
key := make([]byte, 32)
|
||||
salt := make([]byte, 16)
|
||||
c, _ := NewCipher(key)
|
||||
for i := 0; i < b.N; i++ {
|
||||
expandKeyWithSalt(key, salt, c)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkExpandKey(b *testing.B) {
|
||||
key := make([]byte, 32)
|
||||
c, _ := NewCipher(key)
|
||||
for i := 0; i < b.N; i++ {
|
||||
ExpandKey(key, c)
|
||||
}
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package transform_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode"
|
||||
|
||||
"code.google.com/p/go.text/transform"
|
||||
"code.google.com/p/go.text/unicode/norm"
|
||||
)
|
||||
|
||||
func ExampleRemoveFunc() {
|
||||
input := []byte(`tschüß; до свидания`)
|
||||
|
||||
b := make([]byte, len(input))
|
||||
|
||||
t := transform.RemoveFunc(unicode.IsSpace)
|
||||
n, _, _ := t.Transform(b, input, true)
|
||||
fmt.Println(string(b[:n]))
|
||||
|
||||
t = transform.RemoveFunc(func(r rune) bool {
|
||||
return !unicode.Is(unicode.Latin, r)
|
||||
})
|
||||
n, _, _ = t.Transform(b, input, true)
|
||||
fmt.Println(string(b[:n]))
|
||||
|
||||
n, _, _ = t.Transform(b, norm.NFD.Bytes(input), true)
|
||||
fmt.Println(string(b[:n]))
|
||||
|
||||
// Output:
|
||||
// tschüß;досвидания
|
||||
// tschüß
|
||||
// tschuß
|
||||
}
|
||||
-1075
File diff suppressed because it is too large
Load Diff
-30
@@ -1,30 +0,0 @@
|
||||
# Copyright 2011 The Go Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
maketables: maketables.go triegen.go
|
||||
go build $^
|
||||
|
||||
maketesttables: maketesttables.go triegen.go
|
||||
go build $^
|
||||
|
||||
normregtest: normregtest.go
|
||||
go build $^
|
||||
|
||||
tables: maketables
|
||||
./maketables > tables.go
|
||||
gofmt -w tables.go
|
||||
|
||||
trietesttables: maketesttables
|
||||
./maketesttables > triedata_test.go
|
||||
gofmt -w triedata_test.go
|
||||
|
||||
# Downloads from www.unicode.org, so not part
|
||||
# of standard test scripts.
|
||||
test: testtables regtest
|
||||
|
||||
testtables: maketables
|
||||
./maketables -test > data_test.go && go test -tags=test
|
||||
|
||||
regtest: normregtest
|
||||
./normregtest
|
||||
-130
@@ -1,130 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package norm
|
||||
|
||||
import "testing"
|
||||
|
||||
// TestCase is used for most tests.
|
||||
type TestCase struct {
|
||||
in []rune
|
||||
out []rune
|
||||
}
|
||||
|
||||
func runTests(t *testing.T, name string, fm Form, tests []TestCase) {
|
||||
rb := reorderBuffer{}
|
||||
rb.init(fm, nil)
|
||||
for i, test := range tests {
|
||||
rb.setFlusher(nil, appendFlush)
|
||||
for j, rune := range test.in {
|
||||
b := []byte(string(rune))
|
||||
src := inputBytes(b)
|
||||
info := rb.f.info(src, 0)
|
||||
if j == 0 {
|
||||
rb.ss.first(info)
|
||||
} else {
|
||||
rb.ss.next(info)
|
||||
}
|
||||
if rb.insertFlush(src, 0, info) < 0 {
|
||||
t.Errorf("%s:%d: insert failed for rune %d", name, i, j)
|
||||
}
|
||||
}
|
||||
rb.doFlush()
|
||||
was := string(rb.out)
|
||||
want := string(test.out)
|
||||
if len(was) != len(want) {
|
||||
t.Errorf("%s:%d: length = %d; want %d", name, i, len(was), len(want))
|
||||
}
|
||||
if was != want {
|
||||
k, pfx := pidx(was, want)
|
||||
t.Errorf("%s:%d: \nwas %s%+q; \nwant %s%+q", name, i, pfx, was[k:], pfx, want[k:])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlush(t *testing.T) {
|
||||
const (
|
||||
hello = "Hello "
|
||||
world = "world!"
|
||||
)
|
||||
buf := make([]byte, maxByteBufferSize)
|
||||
p := copy(buf, hello)
|
||||
out := buf[p:]
|
||||
rb := reorderBuffer{}
|
||||
rb.initString(NFC, world)
|
||||
if i := rb.flushCopy(out); i != 0 {
|
||||
t.Errorf("wrote bytes on flush of empty buffer. (len(out) = %d)", i)
|
||||
}
|
||||
|
||||
for i := range world {
|
||||
// No need to set streamSafe values for this test.
|
||||
rb.insertFlush(rb.src, i, rb.f.info(rb.src, i))
|
||||
n := rb.flushCopy(out)
|
||||
out = out[n:]
|
||||
p += n
|
||||
}
|
||||
|
||||
was := buf[:p]
|
||||
want := hello + world
|
||||
if string(was) != want {
|
||||
t.Errorf(`output after flush was "%s"; want "%s"`, string(was), want)
|
||||
}
|
||||
if rb.nrune != 0 {
|
||||
t.Errorf("non-null size of info buffer (rb.nrune == %d)", rb.nrune)
|
||||
}
|
||||
if rb.nbyte != 0 {
|
||||
t.Errorf("non-null size of byte buffer (rb.nbyte == %d)", rb.nbyte)
|
||||
}
|
||||
}
|
||||
|
||||
var insertTests = []TestCase{
|
||||
{[]rune{'a'}, []rune{'a'}},
|
||||
{[]rune{0x300}, []rune{0x300}},
|
||||
{[]rune{0x300, 0x316}, []rune{0x316, 0x300}}, // CCC(0x300)==230; CCC(0x316)==220
|
||||
{[]rune{0x316, 0x300}, []rune{0x316, 0x300}},
|
||||
{[]rune{0x41, 0x316, 0x300}, []rune{0x41, 0x316, 0x300}},
|
||||
{[]rune{0x41, 0x300, 0x316}, []rune{0x41, 0x316, 0x300}},
|
||||
{[]rune{0x300, 0x316, 0x41}, []rune{0x316, 0x300, 0x41}},
|
||||
{[]rune{0x41, 0x300, 0x40, 0x316}, []rune{0x41, 0x300, 0x40, 0x316}},
|
||||
}
|
||||
|
||||
func TestInsert(t *testing.T) {
|
||||
runTests(t, "TestInsert", NFD, insertTests)
|
||||
}
|
||||
|
||||
var decompositionNFDTest = []TestCase{
|
||||
{[]rune{0xC0}, []rune{0x41, 0x300}},
|
||||
{[]rune{0xAC00}, []rune{0x1100, 0x1161}},
|
||||
{[]rune{0x01C4}, []rune{0x01C4}},
|
||||
{[]rune{0x320E}, []rune{0x320E}},
|
||||
{[]rune("음ẻ과"), []rune{0x110B, 0x1173, 0x11B7, 0x65, 0x309, 0x1100, 0x116A}},
|
||||
}
|
||||
|
||||
var decompositionNFKDTest = []TestCase{
|
||||
{[]rune{0xC0}, []rune{0x41, 0x300}},
|
||||
{[]rune{0xAC00}, []rune{0x1100, 0x1161}},
|
||||
{[]rune{0x01C4}, []rune{0x44, 0x5A, 0x030C}},
|
||||
{[]rune{0x320E}, []rune{0x28, 0x1100, 0x1161, 0x29}},
|
||||
}
|
||||
|
||||
func TestDecomposition(t *testing.T) {
|
||||
runTests(t, "TestDecompositionNFD", NFD, decompositionNFDTest)
|
||||
runTests(t, "TestDecompositionNFKD", NFKD, decompositionNFKDTest)
|
||||
}
|
||||
|
||||
var compositionTest = []TestCase{
|
||||
{[]rune{0x41, 0x300}, []rune{0xC0}},
|
||||
{[]rune{0x41, 0x316}, []rune{0x41, 0x316}},
|
||||
{[]rune{0x41, 0x300, 0x35D}, []rune{0xC0, 0x35D}},
|
||||
{[]rune{0x41, 0x316, 0x300}, []rune{0xC0, 0x316}},
|
||||
// blocking starter
|
||||
{[]rune{0x41, 0x316, 0x40, 0x300}, []rune{0x41, 0x316, 0x40, 0x300}},
|
||||
{[]rune{0x1100, 0x1161}, []rune{0xAC00}},
|
||||
// parenthesized Hangul, alternate between ASCII and Hangul.
|
||||
{[]rune{0x28, 0x1100, 0x1161, 0x29}, []rune{0x28, 0xAC00, 0x29}},
|
||||
}
|
||||
|
||||
func TestComposition(t *testing.T) {
|
||||
runTests(t, "TestComposition", NFC, compositionTest)
|
||||
}
|
||||
Generated
Vendored
-82
@@ -1,82 +0,0 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package norm_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"unicode/utf8"
|
||||
|
||||
"code.google.com/p/go.text/unicode/norm"
|
||||
)
|
||||
|
||||
// EqualSimple uses a norm.Iter to compare two non-normalized
|
||||
// strings for equivalence.
|
||||
func EqualSimple(a, b string) bool {
|
||||
var ia, ib norm.Iter
|
||||
ia.InitString(norm.NFKD, a)
|
||||
ib.InitString(norm.NFKD, b)
|
||||
for !ia.Done() && !ib.Done() {
|
||||
if !bytes.Equal(ia.Next(), ib.Next()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return ia.Done() && ib.Done()
|
||||
}
|
||||
|
||||
// FindPrefix finds the longest common prefix of ASCII characters
|
||||
// of a and b.
|
||||
func FindPrefix(a, b string) int {
|
||||
i := 0
|
||||
for ; i < len(a) && i < len(b) && a[i] < utf8.RuneSelf && a[i] == b[i]; i++ {
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// EqualOpt is like EqualSimple, but optimizes the special
|
||||
// case for ASCII characters.
|
||||
func EqualOpt(a, b string) bool {
|
||||
n := FindPrefix(a, b)
|
||||
a, b = a[n:], b[n:]
|
||||
var ia, ib norm.Iter
|
||||
ia.InitString(norm.NFKD, a)
|
||||
ib.InitString(norm.NFKD, b)
|
||||
for !ia.Done() && !ib.Done() {
|
||||
if !bytes.Equal(ia.Next(), ib.Next()) {
|
||||
return false
|
||||
}
|
||||
if n := int64(FindPrefix(a[ia.Pos():], b[ib.Pos():])); n != 0 {
|
||||
ia.Seek(n, 1)
|
||||
ib.Seek(n, 1)
|
||||
}
|
||||
}
|
||||
return ia.Done() && ib.Done()
|
||||
}
|
||||
|
||||
var compareTests = []struct{ a, b string }{
|
||||
{"aaa", "aaa"},
|
||||
{"aaa", "aab"},
|
||||
{"a\u0300a", "\u00E0a"},
|
||||
{"a\u0300\u0320b", "a\u0320\u0300b"},
|
||||
{"\u1E0A\u0323", "\x44\u0323\u0307"},
|
||||
// A character that decomposes into multiple segments
|
||||
// spans several iterations.
|
||||
{"\u3304", "\u30A4\u30CB\u30F3\u30AF\u3099"},
|
||||
}
|
||||
|
||||
func ExampleIter() {
|
||||
for i, t := range compareTests {
|
||||
r0 := EqualSimple(t.a, t.b)
|
||||
r1 := EqualOpt(t.a, t.b)
|
||||
fmt.Printf("%d: %v %v\n", i, r0, r1)
|
||||
}
|
||||
// Output:
|
||||
// 0: true true
|
||||
// 1: false false
|
||||
// 2: true true
|
||||
// 3: true true
|
||||
// 4: true true
|
||||
// 5: true true
|
||||
}
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build test
|
||||
|
||||
package norm
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestProperties(t *testing.T) {
|
||||
var d runeData
|
||||
CK := [2]string{"C", "K"}
|
||||
for k, r := 1, rune(0); r < 0x2ffff; r++ {
|
||||
if k < len(testData) && r == testData[k].r {
|
||||
d = testData[k]
|
||||
k++
|
||||
}
|
||||
s := string(r)
|
||||
for j, p := range []Properties{NFC.PropertiesString(s), NFKC.PropertiesString(s)} {
|
||||
f := d.f[j]
|
||||
if p.CCC() != d.ccc {
|
||||
t.Errorf("%U: ccc(%s): was %d; want %d %X", r, CK[j], p.CCC(), d.ccc, p.index)
|
||||
}
|
||||
if p.isYesC() != (f.qc == Yes) {
|
||||
t.Errorf("%U: YesC(%s): was %v; want %v", r, CK[j], p.isYesC(), f.qc == Yes)
|
||||
}
|
||||
if p.combinesBackward() != (f.qc == Maybe) {
|
||||
t.Errorf("%U: combines backwards(%s): was %v; want %v", r, CK[j], p.combinesBackward(), f.qc == Maybe)
|
||||
}
|
||||
if p.nLeadingNonStarters() != d.nLead {
|
||||
t.Errorf("%U: nLead(%s): was %d; want %d %#v %#v", r, CK[j], p.nLeadingNonStarters(), d.nLead, p, d)
|
||||
}
|
||||
if p.nTrailingNonStarters() != d.nTrail {
|
||||
t.Errorf("%U: nTrail(%s): was %d; want %d %#v %#v", r, CK[j], p.nTrailingNonStarters(), d.nTrail, p, d)
|
||||
}
|
||||
if p.combinesForward() != f.combinesForward {
|
||||
t.Errorf("%U: combines forward(%s): was %v; want %v %#v", r, CK[j], p.combinesForward(), f.combinesForward, p)
|
||||
}
|
||||
// Skip Hangul as it is algorithmically computed.
|
||||
if r >= hangulBase && r < hangulEnd {
|
||||
continue
|
||||
}
|
||||
if p.hasDecomposition() {
|
||||
if has := f.decomposition != ""; !has {
|
||||
t.Errorf("%U: hasDecomposition(%s): was %v; want %v", r, CK[j], p.hasDecomposition(), has)
|
||||
}
|
||||
if string(p.Decomposition()) != f.decomposition {
|
||||
t.Errorf("%U: decomp(%s): was %+q; want %+q", r, CK[j], p.Decomposition(), f.decomposition)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package norm
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func doIterNorm(f Form, s string) []byte {
|
||||
acc := []byte{}
|
||||
i := Iter{}
|
||||
i.InitString(f, s)
|
||||
for !i.Done() {
|
||||
acc = append(acc, i.Next()...)
|
||||
}
|
||||
return acc
|
||||
}
|
||||
|
||||
func TestIterNext(t *testing.T) {
|
||||
runNormTests(t, "IterNext", func(f Form, out []byte, s string) []byte {
|
||||
return doIterNorm(f, string(append(out, s...)))
|
||||
})
|
||||
}
|
||||
|
||||
type SegmentTest struct {
|
||||
in string
|
||||
out []string
|
||||
}
|
||||
|
||||
var segmentTests = []SegmentTest{
|
||||
{"\u1E0A\u0323a", []string{"\x44\u0323\u0307", "a", ""}},
|
||||
{rep('a', segSize), append(strings.Split(rep('a', segSize), ""), "")},
|
||||
{rep('a', segSize+2), append(strings.Split(rep('a', segSize+2), ""), "")},
|
||||
{rep('a', segSize) + "\u0300aa",
|
||||
append(strings.Split(rep('a', segSize-1), ""), "a\u0300", "a", "a", "")},
|
||||
|
||||
// U+0f73 is NOT treated as a starter as it is a modifier
|
||||
{"a" + grave(29) + "\u0f73", []string{"a" + grave(29), cgj + "\u0f73"}},
|
||||
{"a\u0f73", []string{"a\u0f73"}},
|
||||
|
||||
// U+ff9e is treated as a non-starter.
|
||||
// TODO: should we? Note that this will only affect iteration, as whether
|
||||
// or not we do so does not affect the normalization output and will either
|
||||
// way result in consistent iteration output.
|
||||
{"a" + grave(30) + "\uff9e", []string{"a" + grave(30), cgj + "\uff9e"}},
|
||||
{"a\uff9e", []string{"a\uff9e"}},
|
||||
}
|
||||
|
||||
var segmentTestsK = []SegmentTest{
|
||||
{"\u3332", []string{"\u30D5", "\u30A1", "\u30E9", "\u30C3", "\u30C8\u3099", ""}},
|
||||
// last segment of multi-segment decomposition needs normalization
|
||||
{"\u3332\u093C", []string{"\u30D5", "\u30A1", "\u30E9", "\u30C3", "\u30C8\u093C\u3099", ""}},
|
||||
{"\u320E", []string{"\x28", "\uAC00", "\x29"}},
|
||||
|
||||
// last segment should be copied to start of buffer.
|
||||
{"\ufdfa", []string{"\u0635", "\u0644", "\u0649", " ", "\u0627", "\u0644", "\u0644", "\u0647", " ", "\u0639", "\u0644", "\u064a", "\u0647", " ", "\u0648", "\u0633", "\u0644", "\u0645", ""}},
|
||||
{"\ufdfa" + grave(30), []string{"\u0635", "\u0644", "\u0649", " ", "\u0627", "\u0644", "\u0644", "\u0647", " ", "\u0639", "\u0644", "\u064a", "\u0647", " ", "\u0648", "\u0633", "\u0644", "\u0645" + grave(30), ""}},
|
||||
{"\uFDFA" + grave(64), []string{"\u0635", "\u0644", "\u0649", " ", "\u0627", "\u0644", "\u0644", "\u0647", " ", "\u0639", "\u0644", "\u064a", "\u0647", " ", "\u0648", "\u0633", "\u0644", "\u0645" + grave(30), cgj + grave(30), cgj + grave(4), ""}},
|
||||
|
||||
// Hangul and Jamo are grouped togeter.
|
||||
{"\uAC00", []string{"\u1100\u1161", ""}},
|
||||
{"\uAC01", []string{"\u1100\u1161\u11A8", ""}},
|
||||
{"\u1100\u1161", []string{"\u1100\u1161", ""}},
|
||||
}
|
||||
|
||||
// Note that, by design, segmentation is equal for composing and decomposing forms.
|
||||
func TestIterSegmentation(t *testing.T) {
|
||||
segmentTest(t, "SegmentTestD", NFD, segmentTests)
|
||||
segmentTest(t, "SegmentTestC", NFC, segmentTests)
|
||||
segmentTest(t, "SegmentTestKD", NFKD, segmentTestsK)
|
||||
segmentTest(t, "SegmentTestKC", NFKC, segmentTestsK)
|
||||
}
|
||||
|
||||
func segmentTest(t *testing.T, name string, f Form, tests []SegmentTest) {
|
||||
iter := Iter{}
|
||||
for i, tt := range tests {
|
||||
iter.InitString(f, tt.in)
|
||||
for j, seg := range tt.out {
|
||||
if seg == "" {
|
||||
if !iter.Done() {
|
||||
res := string(iter.Next())
|
||||
t.Errorf(`%s:%d:%d: expected Done()==true, found segment %+q`, name, i, j, res)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if iter.Done() {
|
||||
t.Errorf("%s:%d:%d: Done()==true, want false", name, i, j)
|
||||
}
|
||||
seg = f.String(seg)
|
||||
if res := string(iter.Next()); res != seg {
|
||||
t.Errorf(`%s:%d:%d" segment was %+q (%d); want %+q (%d)`, name, i, j, pc(res), len(res), pc(seg), len(seg))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// Generate test data for trie code.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
printTestTables()
|
||||
}
|
||||
|
||||
// We take the smallest, largest and an arbitrary value for each
|
||||
// of the UTF-8 sequence lengths.
|
||||
var testRunes = []rune{
|
||||
0x01, 0x0C, 0x7F, // 1-byte sequences
|
||||
0x80, 0x100, 0x7FF, // 2-byte sequences
|
||||
0x800, 0x999, 0xFFFF, // 3-byte sequences
|
||||
0x10000, 0x10101, 0x10FFFF, // 4-byte sequences
|
||||
0x200, 0x201, 0x202, 0x210, 0x215, // five entries in one sparse block
|
||||
}
|
||||
|
||||
const fileHeader = `// Generated by running
|
||||
// maketesttables
|
||||
// DO NOT EDIT
|
||||
|
||||
package norm
|
||||
|
||||
`
|
||||
|
||||
func printTestTables() {
|
||||
fmt.Print(fileHeader)
|
||||
fmt.Printf("var testRunes = %#v\n\n", testRunes)
|
||||
t := newNode()
|
||||
for i, r := range testRunes {
|
||||
t.insert(r, uint16(i))
|
||||
}
|
||||
t.printTables("testdata")
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package norm_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPlaceHolder(t *testing.T) {
|
||||
// Does nothing, just allows the Makefile to be canonical
|
||||
// while waiting for the package itself to be written.
|
||||
}
|
||||
-1086
File diff suppressed because it is too large
Load Diff
-318
@@ -1,318 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"code.google.com/p/go.text/unicode/norm"
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
loadTestData()
|
||||
CharacterByCharacterTests()
|
||||
StandardTests()
|
||||
PerformanceTest()
|
||||
if errorCount == 0 {
|
||||
fmt.Println("PASS")
|
||||
}
|
||||
}
|
||||
|
||||
const file = "NormalizationTest.txt"
|
||||
|
||||
var url = flag.String("url",
|
||||
"http://www.unicode.org/Public/"+unicode.Version+"/ucd/"+file,
|
||||
"URL of Unicode database directory")
|
||||
var localFiles = flag.Bool("local",
|
||||
false,
|
||||
"data files have been copied to the current directory; for debugging only")
|
||||
|
||||
var logger = log.New(os.Stderr, "", log.Lshortfile)
|
||||
|
||||
// This regression test runs the test set in NormalizationTest.txt
|
||||
// (taken from http://www.unicode.org/Public/<unicode.Version>/ucd/).
|
||||
//
|
||||
// NormalizationTest.txt has form:
|
||||
// @Part0 # Specific cases
|
||||
// #
|
||||
// 1E0A;1E0A;0044 0307;1E0A;0044 0307; # (Ḋ; Ḋ; D◌̇; Ḋ; D◌̇; ) LATIN CAPITAL LETTER D WITH DOT ABOVE
|
||||
// 1E0C;1E0C;0044 0323;1E0C;0044 0323; # (Ḍ; Ḍ; D◌̣; Ḍ; D◌̣; ) LATIN CAPITAL LETTER D WITH DOT BELOW
|
||||
//
|
||||
// Each test has 5 columns (c1, c2, c3, c4, c5), where
|
||||
// (c1, c2, c3, c4, c5) == (c1, NFC(c1), NFD(c1), NFKC(c1), NFKD(c1))
|
||||
//
|
||||
// CONFORMANCE:
|
||||
// 1. The following invariants must be true for all conformant implementations
|
||||
//
|
||||
// NFC
|
||||
// c2 == NFC(c1) == NFC(c2) == NFC(c3)
|
||||
// c4 == NFC(c4) == NFC(c5)
|
||||
//
|
||||
// NFD
|
||||
// c3 == NFD(c1) == NFD(c2) == NFD(c3)
|
||||
// c5 == NFD(c4) == NFD(c5)
|
||||
//
|
||||
// NFKC
|
||||
// c4 == NFKC(c1) == NFKC(c2) == NFKC(c3) == NFKC(c4) == NFKC(c5)
|
||||
//
|
||||
// NFKD
|
||||
// c5 == NFKD(c1) == NFKD(c2) == NFKD(c3) == NFKD(c4) == NFKD(c5)
|
||||
//
|
||||
// 2. For every code point X assigned in this version of Unicode that is not
|
||||
// specifically listed in Part 1, the following invariants must be true
|
||||
// for all conformant implementations:
|
||||
//
|
||||
// X == NFC(X) == NFD(X) == NFKC(X) == NFKD(X)
|
||||
//
|
||||
|
||||
// Column types.
|
||||
const (
|
||||
cRaw = iota
|
||||
cNFC
|
||||
cNFD
|
||||
cNFKC
|
||||
cNFKD
|
||||
cMaxColumns
|
||||
)
|
||||
|
||||
// Holds data from NormalizationTest.txt
|
||||
var part []Part
|
||||
|
||||
type Part struct {
|
||||
name string
|
||||
number int
|
||||
tests []Test
|
||||
}
|
||||
|
||||
type Test struct {
|
||||
name string
|
||||
partnr int
|
||||
number int
|
||||
r rune // used for character by character test
|
||||
cols [cMaxColumns]string // Each has 5 entries, see below.
|
||||
}
|
||||
|
||||
func (t Test) Name() string {
|
||||
if t.number < 0 {
|
||||
return part[t.partnr].name
|
||||
}
|
||||
return fmt.Sprintf("%s:%d", part[t.partnr].name, t.number)
|
||||
}
|
||||
|
||||
var partRe = regexp.MustCompile(`@Part(\d) # (.*)$`)
|
||||
var testRe = regexp.MustCompile(`^` + strings.Repeat(`([\dA-F ]+);`, 5) + ` # (.*)$`)
|
||||
|
||||
var counter int
|
||||
|
||||
// Load the data form NormalizationTest.txt
|
||||
func loadTestData() {
|
||||
if *localFiles {
|
||||
pwd, _ := os.Getwd()
|
||||
*url = "file://" + path.Join(pwd, file)
|
||||
}
|
||||
t := &http.Transport{}
|
||||
t.RegisterProtocol("file", http.NewFileTransport(http.Dir("/")))
|
||||
c := &http.Client{Transport: t}
|
||||
resp, err := c.Get(*url)
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
logger.Fatal("bad GET status for "+file, resp.Status)
|
||||
}
|
||||
f := resp.Body
|
||||
defer f.Close()
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if len(line) == 0 || line[0] == '#' {
|
||||
continue
|
||||
}
|
||||
m := partRe.FindStringSubmatch(line)
|
||||
if m != nil {
|
||||
if len(m) < 3 {
|
||||
logger.Fatal("Failed to parse Part: ", line)
|
||||
}
|
||||
i, err := strconv.Atoi(m[1])
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
name := m[2]
|
||||
part = append(part, Part{name: name[:len(name)-1], number: i})
|
||||
continue
|
||||
}
|
||||
m = testRe.FindStringSubmatch(line)
|
||||
if m == nil || len(m) < 7 {
|
||||
logger.Fatalf(`Failed to parse: "%s" result: %#v`, line, m)
|
||||
}
|
||||
test := Test{name: m[6], partnr: len(part) - 1, number: counter}
|
||||
counter++
|
||||
for j := 1; j < len(m)-1; j++ {
|
||||
for _, split := range strings.Split(m[j], " ") {
|
||||
r, err := strconv.ParseUint(split, 16, 64)
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
if test.r == 0 {
|
||||
// save for CharacterByCharacterTests
|
||||
test.r = rune(r)
|
||||
}
|
||||
var buf [utf8.UTFMax]byte
|
||||
sz := utf8.EncodeRune(buf[:], rune(r))
|
||||
test.cols[j-1] += string(buf[:sz])
|
||||
}
|
||||
}
|
||||
part := &part[len(part)-1]
|
||||
part.tests = append(part.tests, test)
|
||||
}
|
||||
if scanner.Err() != nil {
|
||||
logger.Fatal(scanner.Err())
|
||||
}
|
||||
}
|
||||
|
||||
var fstr = []string{"NFC", "NFD", "NFKC", "NFKD"}
|
||||
|
||||
var errorCount int
|
||||
|
||||
func cmpResult(t *Test, name string, f norm.Form, gold, test, result string) {
|
||||
if gold != result {
|
||||
errorCount++
|
||||
if errorCount > 20 {
|
||||
return
|
||||
}
|
||||
logger.Printf("%s:%s: %s(%+q)=%+q; want %+q: %s",
|
||||
t.Name(), name, fstr[f], test, result, gold, t.name)
|
||||
}
|
||||
}
|
||||
|
||||
func cmpIsNormal(t *Test, name string, f norm.Form, test string, result, want bool) {
|
||||
if result != want {
|
||||
errorCount++
|
||||
if errorCount > 20 {
|
||||
return
|
||||
}
|
||||
logger.Printf("%s:%s: %s(%+q)=%v; want %v", t.Name(), name, fstr[f], test, result, want)
|
||||
}
|
||||
}
|
||||
|
||||
func doTest(t *Test, f norm.Form, gold, test string) {
|
||||
testb := []byte(test)
|
||||
result := f.Bytes(testb)
|
||||
cmpResult(t, "Bytes", f, gold, test, string(result))
|
||||
|
||||
sresult := f.String(test)
|
||||
cmpResult(t, "String", f, gold, test, sresult)
|
||||
|
||||
acc := []byte{}
|
||||
i := norm.Iter{}
|
||||
i.InitString(f, test)
|
||||
for !i.Done() {
|
||||
acc = append(acc, i.Next()...)
|
||||
}
|
||||
cmpResult(t, "Iter.Next", f, gold, test, string(acc))
|
||||
|
||||
buf := make([]byte, 128)
|
||||
acc = nil
|
||||
for p := 0; p < len(testb); {
|
||||
nDst, nSrc, _ := f.Transform(buf, testb[p:], true)
|
||||
acc = append(acc, buf[:nDst]...)
|
||||
p += nSrc
|
||||
}
|
||||
cmpResult(t, "Transform", f, gold, test, string(acc))
|
||||
|
||||
for i := range test {
|
||||
out := f.Append(f.Bytes([]byte(test[:i])), []byte(test[i:])...)
|
||||
cmpResult(t, fmt.Sprintf(":Append:%d", i), f, gold, test, string(out))
|
||||
}
|
||||
cmpIsNormal(t, "IsNormal", f, test, f.IsNormal([]byte(test)), test == gold)
|
||||
cmpIsNormal(t, "IsNormalString", f, test, f.IsNormalString(test), test == gold)
|
||||
}
|
||||
|
||||
func doConformanceTests(t *Test, partn int) {
|
||||
for i := 0; i <= 2; i++ {
|
||||
doTest(t, norm.NFC, t.cols[1], t.cols[i])
|
||||
doTest(t, norm.NFD, t.cols[2], t.cols[i])
|
||||
doTest(t, norm.NFKC, t.cols[3], t.cols[i])
|
||||
doTest(t, norm.NFKD, t.cols[4], t.cols[i])
|
||||
}
|
||||
for i := 3; i <= 4; i++ {
|
||||
doTest(t, norm.NFC, t.cols[3], t.cols[i])
|
||||
doTest(t, norm.NFD, t.cols[4], t.cols[i])
|
||||
doTest(t, norm.NFKC, t.cols[3], t.cols[i])
|
||||
doTest(t, norm.NFKD, t.cols[4], t.cols[i])
|
||||
}
|
||||
}
|
||||
|
||||
func CharacterByCharacterTests() {
|
||||
tests := part[1].tests
|
||||
var last rune = 0
|
||||
for i := 0; i <= len(tests); i++ { // last one is special case
|
||||
var r rune
|
||||
if i == len(tests) {
|
||||
r = 0x2FA1E // Don't have to go to 0x10FFFF
|
||||
} else {
|
||||
r = tests[i].r
|
||||
}
|
||||
for last++; last < r; last++ {
|
||||
// Check all characters that were not explicitly listed in the test.
|
||||
t := &Test{partnr: 1, number: -1}
|
||||
char := string(last)
|
||||
doTest(t, norm.NFC, char, char)
|
||||
doTest(t, norm.NFD, char, char)
|
||||
doTest(t, norm.NFKC, char, char)
|
||||
doTest(t, norm.NFKD, char, char)
|
||||
}
|
||||
if i < len(tests) {
|
||||
doConformanceTests(&tests[i], 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func StandardTests() {
|
||||
for _, j := range []int{0, 2, 3} {
|
||||
for _, test := range part[j].tests {
|
||||
doConformanceTests(&test, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PerformanceTest verifies that normalization is O(n). If any of the
|
||||
// code does not properly check for maxCombiningChars, normalization
|
||||
// may exhibit O(n**2) behavior.
|
||||
func PerformanceTest() {
|
||||
runtime.GOMAXPROCS(2)
|
||||
success := make(chan bool, 1)
|
||||
go func() {
|
||||
buf := bytes.Repeat([]byte("\u035D"), 1024*1024)
|
||||
buf = append(buf, "\u035B"...)
|
||||
norm.NFC.Append(nil, buf...)
|
||||
success <- true
|
||||
}()
|
||||
timeout := time.After(1 * time.Second)
|
||||
select {
|
||||
case <-success:
|
||||
// test completed before the timeout
|
||||
case <-timeout:
|
||||
errorCount++
|
||||
logger.Printf(`unexpectedly long time to complete PerformanceTest`)
|
||||
}
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package norm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var bufSizes = []int{1, 2, 3, 4, 5, 6, 7, 8, 100, 101, 102, 103, 4000, 4001, 4002, 4003}
|
||||
|
||||
func readFunc(size int) appendFunc {
|
||||
return func(f Form, out []byte, s string) []byte {
|
||||
out = append(out, s...)
|
||||
r := f.Reader(bytes.NewBuffer(out))
|
||||
buf := make([]byte, size)
|
||||
result := []byte{}
|
||||
for n, err := 0, error(nil); err == nil; {
|
||||
n, err = r.Read(buf)
|
||||
result = append(result, buf[:n]...)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
func TestReader(t *testing.T) {
|
||||
for _, s := range bufSizes {
|
||||
name := fmt.Sprintf("TestReader%d", s)
|
||||
runNormTests(t, name, readFunc(s))
|
||||
}
|
||||
}
|
||||
|
||||
func writeFunc(size int) appendFunc {
|
||||
return func(f Form, out []byte, s string) []byte {
|
||||
in := append(out, s...)
|
||||
result := new(bytes.Buffer)
|
||||
w := f.Writer(result)
|
||||
buf := make([]byte, size)
|
||||
for n := 0; len(in) > 0; in = in[n:] {
|
||||
n = copy(buf, in)
|
||||
_, _ = w.Write(buf[:n])
|
||||
}
|
||||
w.Close()
|
||||
return result.Bytes()
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriter(t *testing.T) {
|
||||
for _, s := range bufSizes {
|
||||
name := fmt.Sprintf("TestWriter%d", s)
|
||||
runNormTests(t, name, writeFunc(s))
|
||||
}
|
||||
}
|
||||
-6989
File diff suppressed because it is too large
Load Diff
-101
@@ -1,101 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package norm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"code.google.com/p/go.text/transform"
|
||||
)
|
||||
|
||||
func TestTransform(t *testing.T) {
|
||||
tests := []struct {
|
||||
f Form
|
||||
in, out string
|
||||
eof bool
|
||||
dstSize int
|
||||
err error
|
||||
}{
|
||||
{NFC, "ab", "ab", true, 2, nil},
|
||||
{NFC, "qx", "qx", true, 2, nil},
|
||||
{NFD, "qx", "qx", true, 2, nil},
|
||||
{NFC, "", "", true, 1, nil},
|
||||
{NFD, "", "", true, 1, nil},
|
||||
{NFC, "", "", false, 1, nil},
|
||||
{NFD, "", "", false, 1, nil},
|
||||
|
||||
// Normalized segment does not fit in destination.
|
||||
{NFD, "ö", "", true, 1, transform.ErrShortDst},
|
||||
{NFD, "ö", "", true, 2, transform.ErrShortDst},
|
||||
|
||||
// As an artifact of the algorithm, only full segments are written.
|
||||
// This is not strictly required, and some bytes could be written.
|
||||
// In practice, for Transform to not block, the destination buffer
|
||||
// should be at least MaxSegmentSize to work anyway and these edge
|
||||
// conditions will be relatively rare.
|
||||
{NFC, "ab", "", true, 1, transform.ErrShortDst},
|
||||
// This is even true for inert runes.
|
||||
{NFC, "qx", "", true, 1, transform.ErrShortDst},
|
||||
{NFC, "a\u0300abc", "\u00e0a", true, 4, transform.ErrShortDst},
|
||||
|
||||
// We cannot write a segment if succesive runes could still change the result.
|
||||
{NFD, "ö", "", false, 3, transform.ErrShortSrc},
|
||||
{NFC, "a\u0300", "", false, 4, transform.ErrShortSrc},
|
||||
{NFD, "a\u0300", "", false, 4, transform.ErrShortSrc},
|
||||
{NFC, "ö", "", false, 3, transform.ErrShortSrc},
|
||||
|
||||
{NFC, "a\u0300", "", true, 1, transform.ErrShortDst},
|
||||
// Theoretically could fit, but won't due to simplified checks.
|
||||
{NFC, "a\u0300", "", true, 2, transform.ErrShortDst},
|
||||
{NFC, "a\u0300", "", true, 3, transform.ErrShortDst},
|
||||
{NFC, "a\u0300", "\u00e0", true, 4, nil},
|
||||
|
||||
{NFD, "öa\u0300", "o\u0308", false, 8, transform.ErrShortSrc},
|
||||
{NFD, "öa\u0300ö", "o\u0308a\u0300", true, 8, transform.ErrShortDst},
|
||||
{NFD, "öa\u0300ö", "o\u0308a\u0300", false, 12, transform.ErrShortSrc},
|
||||
|
||||
// Illegal input is copied verbatim.
|
||||
{NFD, "\xbd\xb2=\xbc ", "\xbd\xb2=\xbc ", true, 8, nil},
|
||||
}
|
||||
b := make([]byte, 100)
|
||||
for i, tt := range tests {
|
||||
nDst, _, err := tt.f.Transform(b[:tt.dstSize], []byte(tt.in), tt.eof)
|
||||
out := string(b[:nDst])
|
||||
if out != tt.out || err != tt.err {
|
||||
t.Errorf("%d: was %+q (%v); want %+q (%v)", i, out, err, tt.out, tt.err)
|
||||
}
|
||||
if want := tt.f.String(tt.in)[:nDst]; want != out {
|
||||
t.Errorf("%d: incorect normalization: was %+q; want %+q", i, out, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var transBufSizes = []int{
|
||||
MaxTransformChunkSize,
|
||||
3 * MaxTransformChunkSize / 2,
|
||||
2 * MaxTransformChunkSize,
|
||||
3 * MaxTransformChunkSize,
|
||||
100 * MaxTransformChunkSize,
|
||||
}
|
||||
|
||||
func doTransNorm(f Form, buf []byte, b []byte) []byte {
|
||||
acc := []byte{}
|
||||
for p := 0; p < len(b); {
|
||||
nd, ns, _ := f.Transform(buf[:], b[p:], true)
|
||||
p += ns
|
||||
acc = append(acc, buf[:nd]...)
|
||||
}
|
||||
return acc
|
||||
}
|
||||
|
||||
func TestTransformNorm(t *testing.T) {
|
||||
for _, sz := range transBufSizes {
|
||||
buf := make([]byte, sz)
|
||||
runNormTests(t, fmt.Sprintf("Transform:%d", sz), func(f Form, out []byte, s string) []byte {
|
||||
return doTransNorm(f, buf, append(out, s...))
|
||||
})
|
||||
}
|
||||
}
|
||||
-232
@@ -1,232 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package norm
|
||||
|
||||
type valueRange struct {
|
||||
value uint16 // header: value:stride
|
||||
lo, hi byte // header: lo:n
|
||||
}
|
||||
|
||||
type trie struct {
|
||||
index []uint8
|
||||
values []uint16
|
||||
sparse []valueRange
|
||||
sparseOffset []uint16
|
||||
cutoff uint8 // indices >= cutoff are sparse
|
||||
}
|
||||
|
||||
// lookupValue determines the type of block n and looks up the value for b.
|
||||
// For n < t.cutoff, the block is a simple lookup table. Otherwise, the block
|
||||
// is a list of ranges with an accompanying value. Given a matching range r,
|
||||
// the value for b is by r.value + (b - r.lo) * stride.
|
||||
func (t *trie) lookupValue(n uint8, b byte) uint16 {
|
||||
if n < t.cutoff {
|
||||
return t.values[uint16(n)<<6+uint16(b)]
|
||||
}
|
||||
offset := t.sparseOffset[n-t.cutoff]
|
||||
header := t.sparse[offset]
|
||||
lo := offset + 1
|
||||
hi := lo + uint16(header.lo)
|
||||
for lo < hi {
|
||||
m := lo + (hi-lo)/2
|
||||
r := t.sparse[m]
|
||||
if r.lo <= b && b <= r.hi {
|
||||
return r.value + uint16(b-r.lo)*header.value
|
||||
}
|
||||
if b < r.lo {
|
||||
hi = m
|
||||
} else {
|
||||
lo = m + 1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
const (
|
||||
t1 = 0x00 // 0000 0000
|
||||
tx = 0x80 // 1000 0000
|
||||
t2 = 0xC0 // 1100 0000
|
||||
t3 = 0xE0 // 1110 0000
|
||||
t4 = 0xF0 // 1111 0000
|
||||
t5 = 0xF8 // 1111 1000
|
||||
t6 = 0xFC // 1111 1100
|
||||
te = 0xFE // 1111 1110
|
||||
)
|
||||
|
||||
// lookup returns the trie value for the first UTF-8 encoding in s and
|
||||
// the width in bytes of this encoding. The size will be 0 if s does not
|
||||
// hold enough bytes to complete the encoding. len(s) must be greater than 0.
|
||||
func (t *trie) lookup(s []byte) (v uint16, sz int) {
|
||||
c0 := s[0]
|
||||
switch {
|
||||
case c0 < tx:
|
||||
return t.values[c0], 1
|
||||
case c0 < t2:
|
||||
return 0, 1
|
||||
case c0 < t3:
|
||||
if len(s) < 2 {
|
||||
return 0, 0
|
||||
}
|
||||
i := t.index[c0]
|
||||
c1 := s[1]
|
||||
if c1 < tx || t2 <= c1 {
|
||||
return 0, 1
|
||||
}
|
||||
return t.lookupValue(i, c1), 2
|
||||
case c0 < t4:
|
||||
if len(s) < 3 {
|
||||
return 0, 0
|
||||
}
|
||||
i := t.index[c0]
|
||||
c1 := s[1]
|
||||
if c1 < tx || t2 <= c1 {
|
||||
return 0, 1
|
||||
}
|
||||
o := uint16(i)<<6 + uint16(c1)
|
||||
i = t.index[o]
|
||||
c2 := s[2]
|
||||
if c2 < tx || t2 <= c2 {
|
||||
return 0, 2
|
||||
}
|
||||
return t.lookupValue(i, c2), 3
|
||||
case c0 < t5:
|
||||
if len(s) < 4 {
|
||||
return 0, 0
|
||||
}
|
||||
i := t.index[c0]
|
||||
c1 := s[1]
|
||||
if c1 < tx || t2 <= c1 {
|
||||
return 0, 1
|
||||
}
|
||||
o := uint16(i)<<6 + uint16(c1)
|
||||
i = t.index[o]
|
||||
c2 := s[2]
|
||||
if c2 < tx || t2 <= c2 {
|
||||
return 0, 2
|
||||
}
|
||||
o = uint16(i)<<6 + uint16(c2)
|
||||
i = t.index[o]
|
||||
c3 := s[3]
|
||||
if c3 < tx || t2 <= c3 {
|
||||
return 0, 3
|
||||
}
|
||||
return t.lookupValue(i, c3), 4
|
||||
}
|
||||
// Illegal rune
|
||||
return 0, 1
|
||||
}
|
||||
|
||||
// lookupString returns the trie value for the first UTF-8 encoding in s and
|
||||
// the width in bytes of this encoding. The size will be 0 if s does not
|
||||
// hold enough bytes to complete the encoding. len(s) must be greater than 0.
|
||||
func (t *trie) lookupString(s string) (v uint16, sz int) {
|
||||
c0 := s[0]
|
||||
switch {
|
||||
case c0 < tx:
|
||||
return t.values[c0], 1
|
||||
case c0 < t2:
|
||||
return 0, 1
|
||||
case c0 < t3:
|
||||
if len(s) < 2 {
|
||||
return 0, 0
|
||||
}
|
||||
i := t.index[c0]
|
||||
c1 := s[1]
|
||||
if c1 < tx || t2 <= c1 {
|
||||
return 0, 1
|
||||
}
|
||||
return t.lookupValue(i, c1), 2
|
||||
case c0 < t4:
|
||||
if len(s) < 3 {
|
||||
return 0, 0
|
||||
}
|
||||
i := t.index[c0]
|
||||
c1 := s[1]
|
||||
if c1 < tx || t2 <= c1 {
|
||||
return 0, 1
|
||||
}
|
||||
o := uint16(i)<<6 + uint16(c1)
|
||||
i = t.index[o]
|
||||
c2 := s[2]
|
||||
if c2 < tx || t2 <= c2 {
|
||||
return 0, 2
|
||||
}
|
||||
return t.lookupValue(i, c2), 3
|
||||
case c0 < t5:
|
||||
if len(s) < 4 {
|
||||
return 0, 0
|
||||
}
|
||||
i := t.index[c0]
|
||||
c1 := s[1]
|
||||
if c1 < tx || t2 <= c1 {
|
||||
return 0, 1
|
||||
}
|
||||
o := uint16(i)<<6 + uint16(c1)
|
||||
i = t.index[o]
|
||||
c2 := s[2]
|
||||
if c2 < tx || t2 <= c2 {
|
||||
return 0, 2
|
||||
}
|
||||
o = uint16(i)<<6 + uint16(c2)
|
||||
i = t.index[o]
|
||||
c3 := s[3]
|
||||
if c3 < tx || t2 <= c3 {
|
||||
return 0, 3
|
||||
}
|
||||
return t.lookupValue(i, c3), 4
|
||||
}
|
||||
// Illegal rune
|
||||
return 0, 1
|
||||
}
|
||||
|
||||
// lookupUnsafe returns the trie value for the first UTF-8 encoding in s.
|
||||
// s must hold a full encoding.
|
||||
func (t *trie) lookupUnsafe(s []byte) uint16 {
|
||||
c0 := s[0]
|
||||
if c0 < tx {
|
||||
return t.values[c0]
|
||||
}
|
||||
if c0 < t2 {
|
||||
return 0
|
||||
}
|
||||
i := t.index[c0]
|
||||
if c0 < t3 {
|
||||
return t.lookupValue(i, s[1])
|
||||
}
|
||||
i = t.index[uint16(i)<<6+uint16(s[1])]
|
||||
if c0 < t4 {
|
||||
return t.lookupValue(i, s[2])
|
||||
}
|
||||
i = t.index[uint16(i)<<6+uint16(s[2])]
|
||||
if c0 < t5 {
|
||||
return t.lookupValue(i, s[3])
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// lookupStringUnsafe returns the trie value for the first UTF-8 encoding in s.
|
||||
// s must hold a full encoding.
|
||||
func (t *trie) lookupStringUnsafe(s string) uint16 {
|
||||
c0 := s[0]
|
||||
if c0 < tx {
|
||||
return t.values[c0]
|
||||
}
|
||||
if c0 < t2 {
|
||||
return 0
|
||||
}
|
||||
i := t.index[c0]
|
||||
if c0 < t3 {
|
||||
return t.lookupValue(i, s[1])
|
||||
}
|
||||
i = t.index[uint16(i)<<6+uint16(s[1])]
|
||||
if c0 < t4 {
|
||||
return t.lookupValue(i, s[2])
|
||||
}
|
||||
i = t.index[uint16(i)<<6+uint16(s[2])]
|
||||
if c0 < t5 {
|
||||
return t.lookupValue(i, s[3])
|
||||
}
|
||||
return 0
|
||||
}
|
||||
-152
@@ -1,152 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package norm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Test data is located in triedata_test.go; generated by maketesttables.
|
||||
var testdata = testdataTrie
|
||||
|
||||
type rangeTest struct {
|
||||
block uint8
|
||||
lookup byte
|
||||
result uint16
|
||||
table []valueRange
|
||||
offsets []uint16
|
||||
}
|
||||
|
||||
var range1Off = []uint16{0, 2}
|
||||
var range1 = []valueRange{
|
||||
{0, 1, 0},
|
||||
{1, 0x80, 0x80},
|
||||
{0, 2, 0},
|
||||
{1, 0x80, 0x80},
|
||||
{9, 0xff, 0xff},
|
||||
}
|
||||
|
||||
var rangeTests = []rangeTest{
|
||||
{10, 0x80, 1, range1, range1Off},
|
||||
{10, 0x00, 0, range1, range1Off},
|
||||
{11, 0x80, 1, range1, range1Off},
|
||||
{11, 0xff, 9, range1, range1Off},
|
||||
{11, 0x00, 0, range1, range1Off},
|
||||
}
|
||||
|
||||
func TestLookupSparse(t *testing.T) {
|
||||
for i, test := range rangeTests {
|
||||
n := trie{sparse: test.table, sparseOffset: test.offsets, cutoff: 10}
|
||||
v := n.lookupValue(test.block, test.lookup)
|
||||
if v != test.result {
|
||||
t.Errorf("LookupSparse:%d: found %X; want %X", i, v, test.result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test cases for illegal runes.
|
||||
type trietest struct {
|
||||
size int
|
||||
bytes []byte
|
||||
}
|
||||
|
||||
var tests = []trietest{
|
||||
// illegal runes
|
||||
{1, []byte{0x80}},
|
||||
{1, []byte{0xFF}},
|
||||
{1, []byte{t2, tx - 1}},
|
||||
{1, []byte{t2, t2}},
|
||||
{2, []byte{t3, tx, tx - 1}},
|
||||
{2, []byte{t3, tx, t2}},
|
||||
{1, []byte{t3, tx - 1, tx}},
|
||||
{3, []byte{t4, tx, tx, tx - 1}},
|
||||
{3, []byte{t4, tx, tx, t2}},
|
||||
{1, []byte{t4, t2, tx, tx - 1}},
|
||||
{2, []byte{t4, tx, t2, tx - 1}},
|
||||
|
||||
// short runes
|
||||
{0, []byte{t2}},
|
||||
{0, []byte{t3, tx}},
|
||||
{0, []byte{t4, tx, tx}},
|
||||
|
||||
// we only support UTF-8 up to utf8.UTFMax bytes (4 bytes)
|
||||
{1, []byte{t5, tx, tx, tx, tx}},
|
||||
{1, []byte{t6, tx, tx, tx, tx, tx}},
|
||||
}
|
||||
|
||||
func mkUTF8(r rune) ([]byte, int) {
|
||||
var b [utf8.UTFMax]byte
|
||||
sz := utf8.EncodeRune(b[:], r)
|
||||
return b[:sz], sz
|
||||
}
|
||||
|
||||
func TestLookup(t *testing.T) {
|
||||
for i, tt := range testRunes {
|
||||
b, szg := mkUTF8(tt)
|
||||
v, szt := testdata.lookup(b)
|
||||
if int(v) != i {
|
||||
t.Errorf("lookup(%U): found value %#x, expected %#x", tt, v, i)
|
||||
}
|
||||
if szt != szg {
|
||||
t.Errorf("lookup(%U): found size %d, expected %d", tt, szt, szg)
|
||||
}
|
||||
}
|
||||
for i, tt := range tests {
|
||||
v, sz := testdata.lookup(tt.bytes)
|
||||
if v != 0 {
|
||||
t.Errorf("lookup of illegal rune, case %d: found value %#x, expected 0", i, v)
|
||||
}
|
||||
if sz != tt.size {
|
||||
t.Errorf("lookup of illegal rune, case %d: found size %d, expected %d", i, sz, tt.size)
|
||||
}
|
||||
}
|
||||
// Verify defaults.
|
||||
if v, _ := testdata.lookup([]byte{0xC1, 0x8C}); v != 0 {
|
||||
t.Errorf("lookup of non-existing rune should be 0; found %X", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupUnsafe(t *testing.T) {
|
||||
for i, tt := range testRunes {
|
||||
b, _ := mkUTF8(tt)
|
||||
v := testdata.lookupUnsafe(b)
|
||||
if int(v) != i {
|
||||
t.Errorf("lookupUnsafe(%U): found value %#x, expected %#x", i, v, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupString(t *testing.T) {
|
||||
for i, tt := range testRunes {
|
||||
b, szg := mkUTF8(tt)
|
||||
v, szt := testdata.lookupString(string(b))
|
||||
if int(v) != i {
|
||||
t.Errorf("lookup(%U): found value %#x, expected %#x", i, v, i)
|
||||
}
|
||||
if szt != szg {
|
||||
t.Errorf("lookup(%U): found size %d, expected %d", i, szt, szg)
|
||||
}
|
||||
}
|
||||
for i, tt := range tests {
|
||||
v, sz := testdata.lookupString(string(tt.bytes))
|
||||
if int(v) != 0 {
|
||||
t.Errorf("lookup of illegal rune, case %d: found value %#x, expected 0", i, v)
|
||||
}
|
||||
if sz != tt.size {
|
||||
t.Errorf("lookup of illegal rune, case %d: found size %d, expected %d", i, sz, tt.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupStringUnsafe(t *testing.T) {
|
||||
for i, tt := range testRunes {
|
||||
b, _ := mkUTF8(tt)
|
||||
v := testdata.lookupStringUnsafe(string(b))
|
||||
if int(v) != i {
|
||||
t.Errorf("lookupUnsafe(%U): found value %#x, expected %#x", i, v, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
-85
@@ -1,85 +0,0 @@
|
||||
// Generated by running
|
||||
// maketesttables
|
||||
// DO NOT EDIT
|
||||
|
||||
package norm
|
||||
|
||||
var testRunes = []int32{1, 12, 127, 128, 256, 2047, 2048, 2457, 65535, 65536, 65793, 1114111, 512, 513, 514, 528, 533}
|
||||
|
||||
// testdataValues: 192 entries, 384 bytes
|
||||
// Block 2 is the null block.
|
||||
var testdataValues = [192]uint16{
|
||||
// Block 0x0, offset 0x0
|
||||
0x000c: 0x0001,
|
||||
// Block 0x1, offset 0x40
|
||||
0x007f: 0x0002,
|
||||
// Block 0x2, offset 0x80
|
||||
}
|
||||
|
||||
// testdataSparseOffset: 10 entries, 20 bytes
|
||||
var testdataSparseOffset = []uint16{0x0, 0x2, 0x4, 0x8, 0xa, 0xc, 0xe, 0x10, 0x12, 0x14}
|
||||
|
||||
// testdataSparseValues: 22 entries, 88 bytes
|
||||
var testdataSparseValues = [22]valueRange{
|
||||
// Block 0x0, offset 0x1
|
||||
{value: 0x0000, lo: 0x01},
|
||||
{value: 0x0003, lo: 0x80, hi: 0x80},
|
||||
// Block 0x1, offset 0x2
|
||||
{value: 0x0000, lo: 0x01},
|
||||
{value: 0x0004, lo: 0x80, hi: 0x80},
|
||||
// Block 0x2, offset 0x3
|
||||
{value: 0x0001, lo: 0x03},
|
||||
{value: 0x000c, lo: 0x80, hi: 0x82},
|
||||
{value: 0x000f, lo: 0x90, hi: 0x90},
|
||||
{value: 0x0010, lo: 0x95, hi: 0x95},
|
||||
// Block 0x3, offset 0x4
|
||||
{value: 0x0000, lo: 0x01},
|
||||
{value: 0x0005, lo: 0xbf, hi: 0xbf},
|
||||
// Block 0x4, offset 0x5
|
||||
{value: 0x0000, lo: 0x01},
|
||||
{value: 0x0006, lo: 0x80, hi: 0x80},
|
||||
// Block 0x5, offset 0x6
|
||||
{value: 0x0000, lo: 0x01},
|
||||
{value: 0x0007, lo: 0x99, hi: 0x99},
|
||||
// Block 0x6, offset 0x7
|
||||
{value: 0x0000, lo: 0x01},
|
||||
{value: 0x0008, lo: 0xbf, hi: 0xbf},
|
||||
// Block 0x7, offset 0x8
|
||||
{value: 0x0000, lo: 0x01},
|
||||
{value: 0x0009, lo: 0x80, hi: 0x80},
|
||||
// Block 0x8, offset 0x9
|
||||
{value: 0x0000, lo: 0x01},
|
||||
{value: 0x000a, lo: 0x81, hi: 0x81},
|
||||
// Block 0x9, offset 0xa
|
||||
{value: 0x0000, lo: 0x01},
|
||||
{value: 0x000b, lo: 0xbf, hi: 0xbf},
|
||||
}
|
||||
|
||||
// testdataLookup: 640 bytes
|
||||
// Block 0 is the null block.
|
||||
var testdataLookup = [640]uint8{
|
||||
// Block 0x0, offset 0x0
|
||||
// Block 0x1, offset 0x40
|
||||
// Block 0x2, offset 0x80
|
||||
// Block 0x3, offset 0xc0
|
||||
0x0c2: 0x01, 0x0c4: 0x02,
|
||||
0x0c8: 0x03,
|
||||
0x0df: 0x04,
|
||||
0x0e0: 0x02,
|
||||
0x0ef: 0x03,
|
||||
0x0f0: 0x05, 0x0f4: 0x07,
|
||||
// Block 0x4, offset 0x100
|
||||
0x120: 0x05, 0x126: 0x06,
|
||||
// Block 0x5, offset 0x140
|
||||
0x17f: 0x07,
|
||||
// Block 0x6, offset 0x180
|
||||
0x180: 0x08, 0x184: 0x09,
|
||||
// Block 0x7, offset 0x1c0
|
||||
0x1d0: 0x04,
|
||||
// Block 0x8, offset 0x200
|
||||
0x23f: 0x0a,
|
||||
// Block 0x9, offset 0x240
|
||||
0x24f: 0x06,
|
||||
}
|
||||
|
||||
var testdataTrie = trie{testdataLookup[:], testdataValues[:], testdataSparseValues[:], testdataSparseOffset[:], 1}
|
||||
-317
@@ -1,317 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// Trie table generator.
|
||||
// Used by make*tables tools to generate a go file with trie data structures
|
||||
// for mapping UTF-8 to a 16-bit value. All but the last byte in a UTF-8 byte
|
||||
// sequence are used to lookup offsets in the index table to be used for the
|
||||
// next byte. The last byte is used to index into a table with 16-bit values.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"log"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
blockSize = 64
|
||||
blockOffset = 2 // Subtract two blocks to compensate for the 0x80 added to continuation bytes.
|
||||
maxSparseEntries = 16
|
||||
)
|
||||
|
||||
// Intermediate trie structure
|
||||
type trieNode struct {
|
||||
table [256]*trieNode
|
||||
value int
|
||||
b byte
|
||||
leaf bool
|
||||
}
|
||||
|
||||
func newNode() *trieNode {
|
||||
return new(trieNode)
|
||||
}
|
||||
|
||||
func (n trieNode) String() string {
|
||||
s := fmt.Sprint("trieNode{table: { non-nil at index: ")
|
||||
for i, v := range n.table {
|
||||
if v != nil {
|
||||
s += fmt.Sprintf("%d, ", i)
|
||||
}
|
||||
}
|
||||
s += fmt.Sprintf("}, value:%#x, b:%#x leaf:%v}", n.value, n.b, n.leaf)
|
||||
return s
|
||||
}
|
||||
|
||||
func (n trieNode) isInternal() bool {
|
||||
internal := true
|
||||
for i := 0; i < 256; i++ {
|
||||
if nn := n.table[i]; nn != nil {
|
||||
if !internal && !nn.leaf {
|
||||
log.Fatalf("triegen: isInternal: node contains both leaf and non-leaf children (%v)", n)
|
||||
}
|
||||
internal = internal && !nn.leaf
|
||||
}
|
||||
}
|
||||
return internal
|
||||
}
|
||||
|
||||
func (n trieNode) mostFrequentStride() int {
|
||||
counts := make(map[int]int)
|
||||
v := 0
|
||||
for _, t := range n.table[0x80 : 0x80+blockSize] {
|
||||
if t != nil {
|
||||
if stride := t.value - v; v != 0 && stride >= 0 {
|
||||
counts[stride]++
|
||||
}
|
||||
v = t.value
|
||||
} else {
|
||||
v = 0
|
||||
}
|
||||
}
|
||||
var maxs, maxc int
|
||||
for stride, cnt := range counts {
|
||||
if cnt > maxc || (cnt == maxc && stride < maxs) {
|
||||
maxs, maxc = stride, cnt
|
||||
}
|
||||
}
|
||||
return maxs
|
||||
}
|
||||
|
||||
func (n trieNode) countSparseEntries() int {
|
||||
stride := n.mostFrequentStride()
|
||||
var count, v int
|
||||
for _, t := range n.table[0x80 : 0x80+blockSize] {
|
||||
tv := 0
|
||||
if t != nil {
|
||||
tv = t.value
|
||||
}
|
||||
if tv-v != stride {
|
||||
if tv != 0 {
|
||||
count++
|
||||
}
|
||||
}
|
||||
v = tv
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func (n *trieNode) insert(r rune, value uint16) {
|
||||
var p [utf8.UTFMax]byte
|
||||
sz := utf8.EncodeRune(p[:], r)
|
||||
|
||||
for i := 0; i < sz; i++ {
|
||||
if n.leaf {
|
||||
log.Fatalf("triegen: insert: node (%#v) should not be a leaf", n)
|
||||
}
|
||||
nn := n.table[p[i]]
|
||||
if nn == nil {
|
||||
nn = newNode()
|
||||
nn.b = p[i]
|
||||
n.table[p[i]] = nn
|
||||
}
|
||||
n = nn
|
||||
}
|
||||
n.value = int(value)
|
||||
n.leaf = true
|
||||
}
|
||||
|
||||
type nodeIndex struct {
|
||||
lookupBlocks []*trieNode
|
||||
valueBlocks []*trieNode
|
||||
sparseBlocks []*trieNode
|
||||
sparseOffset []uint16
|
||||
sparseCount int
|
||||
|
||||
lookupBlockIdx map[uint32]int
|
||||
valueBlockIdx map[uint32]int
|
||||
}
|
||||
|
||||
func newIndex() *nodeIndex {
|
||||
index := &nodeIndex{}
|
||||
index.lookupBlocks = make([]*trieNode, 0)
|
||||
index.valueBlocks = make([]*trieNode, 0)
|
||||
index.sparseBlocks = make([]*trieNode, 0)
|
||||
index.sparseOffset = make([]uint16, 1)
|
||||
index.lookupBlockIdx = make(map[uint32]int)
|
||||
index.valueBlockIdx = make(map[uint32]int)
|
||||
return index
|
||||
}
|
||||
|
||||
func computeOffsets(index *nodeIndex, n *trieNode) int {
|
||||
if n.leaf {
|
||||
return n.value
|
||||
}
|
||||
hasher := crc32.New(crc32.MakeTable(crc32.IEEE))
|
||||
// We only index continuation bytes.
|
||||
for i := 0; i < blockSize; i++ {
|
||||
v := 0
|
||||
if nn := n.table[0x80+i]; nn != nil {
|
||||
v = computeOffsets(index, nn)
|
||||
}
|
||||
hasher.Write([]byte{uint8(v >> 8), uint8(v)})
|
||||
}
|
||||
h := hasher.Sum32()
|
||||
if n.isInternal() {
|
||||
v, ok := index.lookupBlockIdx[h]
|
||||
if !ok {
|
||||
v = len(index.lookupBlocks) - blockOffset
|
||||
index.lookupBlocks = append(index.lookupBlocks, n)
|
||||
index.lookupBlockIdx[h] = v
|
||||
}
|
||||
n.value = v
|
||||
} else {
|
||||
v, ok := index.valueBlockIdx[h]
|
||||
if !ok {
|
||||
if c := n.countSparseEntries(); c > maxSparseEntries {
|
||||
v = len(index.valueBlocks) - blockOffset
|
||||
index.valueBlocks = append(index.valueBlocks, n)
|
||||
index.valueBlockIdx[h] = v
|
||||
} else {
|
||||
v = -len(index.sparseOffset)
|
||||
index.sparseBlocks = append(index.sparseBlocks, n)
|
||||
index.sparseOffset = append(index.sparseOffset, uint16(index.sparseCount))
|
||||
index.sparseCount += c + 1
|
||||
index.valueBlockIdx[h] = v
|
||||
}
|
||||
}
|
||||
n.value = v
|
||||
}
|
||||
return n.value
|
||||
}
|
||||
|
||||
func printValueBlock(nr int, n *trieNode, offset int) {
|
||||
boff := nr * blockSize
|
||||
fmt.Printf("\n// Block %#x, offset %#x", nr, boff)
|
||||
var printnewline bool
|
||||
for i := 0; i < blockSize; i++ {
|
||||
if i%6 == 0 {
|
||||
printnewline = true
|
||||
}
|
||||
v := 0
|
||||
if nn := n.table[i+offset]; nn != nil {
|
||||
v = nn.value
|
||||
}
|
||||
if v != 0 {
|
||||
if printnewline {
|
||||
fmt.Printf("\n")
|
||||
printnewline = false
|
||||
}
|
||||
fmt.Printf("%#04x:%#04x, ", boff+i, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printSparseBlock(nr int, n *trieNode) {
|
||||
boff := -n.value
|
||||
fmt.Printf("\n// Block %#x, offset %#x", nr, boff)
|
||||
v := 0
|
||||
//stride := f(n)
|
||||
stride := n.mostFrequentStride()
|
||||
c := n.countSparseEntries()
|
||||
fmt.Printf("\n{value:%#04x,lo:%#02x},", stride, uint8(c))
|
||||
for i, nn := range n.table[0x80 : 0x80+blockSize] {
|
||||
nv := 0
|
||||
if nn != nil {
|
||||
nv = nn.value
|
||||
}
|
||||
if nv-v != stride {
|
||||
if v != 0 {
|
||||
fmt.Printf(",hi:%#02x},", 0x80+i-1)
|
||||
}
|
||||
if nv != 0 {
|
||||
fmt.Printf("\n{value:%#04x,lo:%#02x", nv, nn.b)
|
||||
}
|
||||
}
|
||||
v = nv
|
||||
}
|
||||
if v != 0 {
|
||||
fmt.Printf(",hi:%#02x},", 0x80+blockSize-1)
|
||||
}
|
||||
}
|
||||
|
||||
func printLookupBlock(nr int, n *trieNode, offset, cutoff int) {
|
||||
boff := nr * blockSize
|
||||
fmt.Printf("\n// Block %#x, offset %#x", nr, boff)
|
||||
var printnewline bool
|
||||
for i := 0; i < blockSize; i++ {
|
||||
if i%8 == 0 {
|
||||
printnewline = true
|
||||
}
|
||||
v := 0
|
||||
if nn := n.table[i+offset]; nn != nil {
|
||||
v = nn.value
|
||||
}
|
||||
if v != 0 {
|
||||
if v < 0 {
|
||||
v = -v - 1 + cutoff
|
||||
}
|
||||
if printnewline {
|
||||
fmt.Printf("\n")
|
||||
printnewline = false
|
||||
}
|
||||
fmt.Printf("%#03x:%#02x, ", boff+i, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// printTables returns the size in bytes of the generated tables.
|
||||
func (t *trieNode) printTables(name string) int {
|
||||
index := newIndex()
|
||||
// Values for 7-bit ASCII are stored in first two block, followed by nil block.
|
||||
index.valueBlocks = append(index.valueBlocks, nil, nil, nil)
|
||||
// First byte of multi-byte UTF-8 codepoints are indexed in 4th block.
|
||||
index.lookupBlocks = append(index.lookupBlocks, nil, nil, nil, nil)
|
||||
// Index starter bytes of multi-byte UTF-8.
|
||||
for i := 0xC0; i < 0x100; i++ {
|
||||
if t.table[i] != nil {
|
||||
computeOffsets(index, t.table[i])
|
||||
}
|
||||
}
|
||||
|
||||
nv := len(index.valueBlocks) * blockSize
|
||||
fmt.Printf("// %sValues: %d entries, %d bytes\n", name, nv, nv*2)
|
||||
fmt.Printf("// Block 2 is the null block.\n")
|
||||
fmt.Printf("var %sValues = [%d]uint16 {", name, nv)
|
||||
printValueBlock(0, t, 0)
|
||||
printValueBlock(1, t, 64)
|
||||
printValueBlock(2, newNode(), 0)
|
||||
for i := 3; i < len(index.valueBlocks); i++ {
|
||||
printValueBlock(i, index.valueBlocks[i], 0x80)
|
||||
}
|
||||
fmt.Print("\n}\n\n")
|
||||
|
||||
ls := len(index.sparseBlocks)
|
||||
fmt.Printf("// %sSparseOffset: %d entries, %d bytes\n", name, ls, ls*2)
|
||||
fmt.Printf("var %sSparseOffset = %#v\n\n", name, index.sparseOffset[1:])
|
||||
|
||||
ns := index.sparseCount
|
||||
fmt.Printf("// %sSparseValues: %d entries, %d bytes\n", name, ns, ns*4)
|
||||
fmt.Printf("var %sSparseValues = [%d]valueRange {", name, ns)
|
||||
for i, n := range index.sparseBlocks {
|
||||
printSparseBlock(i, n)
|
||||
}
|
||||
fmt.Print("\n}\n\n")
|
||||
|
||||
cutoff := len(index.valueBlocks) - blockOffset
|
||||
ni := len(index.lookupBlocks) * blockSize
|
||||
fmt.Printf("// %sLookup: %d bytes\n", name, ni)
|
||||
fmt.Printf("// Block 0 is the null block.\n")
|
||||
fmt.Printf("var %sLookup = [%d]uint8 {", name, ni)
|
||||
printLookupBlock(0, newNode(), 0, cutoff)
|
||||
printLookupBlock(1, newNode(), 0, cutoff)
|
||||
printLookupBlock(2, newNode(), 0, cutoff)
|
||||
printLookupBlock(3, t, 0xC0, cutoff)
|
||||
for i := 4; i < len(index.lookupBlocks); i++ {
|
||||
printLookupBlock(i, index.lookupBlocks[i], 0x80, cutoff)
|
||||
}
|
||||
fmt.Print("\n}\n\n")
|
||||
fmt.Printf("var %sTrie = trie{ %sLookup[:], %sValues[:], %sSparseValues[:], %sSparseOffset[:], %d}\n\n",
|
||||
name, name, name, name, name, cutoff)
|
||||
return nv*2 + ns*4 + ni + ls*2
|
||||
}
|
||||
-124
@@ -1,124 +0,0 @@
|
||||
// Copyright 2011 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package snappy
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// ErrCorrupt reports that the input is invalid.
|
||||
var ErrCorrupt = errors.New("snappy: corrupt input")
|
||||
|
||||
// DecodedLen returns the length of the decoded block.
|
||||
func DecodedLen(src []byte) (int, error) {
|
||||
v, _, err := decodedLen(src)
|
||||
return v, err
|
||||
}
|
||||
|
||||
// decodedLen returns the length of the decoded block and the number of bytes
|
||||
// that the length header occupied.
|
||||
func decodedLen(src []byte) (blockLen, headerLen int, err error) {
|
||||
v, n := binary.Uvarint(src)
|
||||
if n == 0 {
|
||||
return 0, 0, ErrCorrupt
|
||||
}
|
||||
if uint64(int(v)) != v {
|
||||
return 0, 0, errors.New("snappy: decoded block is too large")
|
||||
}
|
||||
return int(v), n, nil
|
||||
}
|
||||
|
||||
// Decode returns the decoded form of src. The returned slice may be a sub-
|
||||
// slice of dst if dst was large enough to hold the entire decoded block.
|
||||
// Otherwise, a newly allocated slice will be returned.
|
||||
// It is valid to pass a nil dst.
|
||||
func Decode(dst, src []byte) ([]byte, error) {
|
||||
dLen, s, err := decodedLen(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(dst) < dLen {
|
||||
dst = make([]byte, dLen)
|
||||
}
|
||||
|
||||
var d, offset, length int
|
||||
for s < len(src) {
|
||||
switch src[s] & 0x03 {
|
||||
case tagLiteral:
|
||||
x := uint(src[s] >> 2)
|
||||
switch {
|
||||
case x < 60:
|
||||
s += 1
|
||||
case x == 60:
|
||||
s += 2
|
||||
if s > len(src) {
|
||||
return nil, ErrCorrupt
|
||||
}
|
||||
x = uint(src[s-1])
|
||||
case x == 61:
|
||||
s += 3
|
||||
if s > len(src) {
|
||||
return nil, ErrCorrupt
|
||||
}
|
||||
x = uint(src[s-2]) | uint(src[s-1])<<8
|
||||
case x == 62:
|
||||
s += 4
|
||||
if s > len(src) {
|
||||
return nil, ErrCorrupt
|
||||
}
|
||||
x = uint(src[s-3]) | uint(src[s-2])<<8 | uint(src[s-1])<<16
|
||||
case x == 63:
|
||||
s += 5
|
||||
if s > len(src) {
|
||||
return nil, ErrCorrupt
|
||||
}
|
||||
x = uint(src[s-4]) | uint(src[s-3])<<8 | uint(src[s-2])<<16 | uint(src[s-1])<<24
|
||||
}
|
||||
length = int(x + 1)
|
||||
if length <= 0 {
|
||||
return nil, errors.New("snappy: unsupported literal length")
|
||||
}
|
||||
if length > len(dst)-d || length > len(src)-s {
|
||||
return nil, ErrCorrupt
|
||||
}
|
||||
copy(dst[d:], src[s:s+length])
|
||||
d += length
|
||||
s += length
|
||||
continue
|
||||
|
||||
case tagCopy1:
|
||||
s += 2
|
||||
if s > len(src) {
|
||||
return nil, ErrCorrupt
|
||||
}
|
||||
length = 4 + int(src[s-2])>>2&0x7
|
||||
offset = int(src[s-2])&0xe0<<3 | int(src[s-1])
|
||||
|
||||
case tagCopy2:
|
||||
s += 3
|
||||
if s > len(src) {
|
||||
return nil, ErrCorrupt
|
||||
}
|
||||
length = 1 + int(src[s-3])>>2
|
||||
offset = int(src[s-2]) | int(src[s-1])<<8
|
||||
|
||||
case tagCopy4:
|
||||
return nil, errors.New("snappy: unsupported COPY_4 tag")
|
||||
}
|
||||
|
||||
end := d + length
|
||||
if offset > d || end > len(dst) {
|
||||
return nil, ErrCorrupt
|
||||
}
|
||||
for ; d < end; d++ {
|
||||
dst[d] = dst[d-offset]
|
||||
}
|
||||
}
|
||||
if d != dLen {
|
||||
return nil, ErrCorrupt
|
||||
}
|
||||
return dst[:d], nil
|
||||
}
|
||||
-174
@@ -1,174 +0,0 @@
|
||||
// Copyright 2011 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package snappy
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// We limit how far copy back-references can go, the same as the C++ code.
|
||||
const maxOffset = 1 << 15
|
||||
|
||||
// emitLiteral writes a literal chunk and returns the number of bytes written.
|
||||
func emitLiteral(dst, lit []byte) int {
|
||||
i, n := 0, uint(len(lit)-1)
|
||||
switch {
|
||||
case n < 60:
|
||||
dst[0] = uint8(n)<<2 | tagLiteral
|
||||
i = 1
|
||||
case n < 1<<8:
|
||||
dst[0] = 60<<2 | tagLiteral
|
||||
dst[1] = uint8(n)
|
||||
i = 2
|
||||
case n < 1<<16:
|
||||
dst[0] = 61<<2 | tagLiteral
|
||||
dst[1] = uint8(n)
|
||||
dst[2] = uint8(n >> 8)
|
||||
i = 3
|
||||
case n < 1<<24:
|
||||
dst[0] = 62<<2 | tagLiteral
|
||||
dst[1] = uint8(n)
|
||||
dst[2] = uint8(n >> 8)
|
||||
dst[3] = uint8(n >> 16)
|
||||
i = 4
|
||||
case int64(n) < 1<<32:
|
||||
dst[0] = 63<<2 | tagLiteral
|
||||
dst[1] = uint8(n)
|
||||
dst[2] = uint8(n >> 8)
|
||||
dst[3] = uint8(n >> 16)
|
||||
dst[4] = uint8(n >> 24)
|
||||
i = 5
|
||||
default:
|
||||
panic("snappy: source buffer is too long")
|
||||
}
|
||||
if copy(dst[i:], lit) != len(lit) {
|
||||
panic("snappy: destination buffer is too short")
|
||||
}
|
||||
return i + len(lit)
|
||||
}
|
||||
|
||||
// emitCopy writes a copy chunk and returns the number of bytes written.
|
||||
func emitCopy(dst []byte, offset, length int) int {
|
||||
i := 0
|
||||
for length > 0 {
|
||||
x := length - 4
|
||||
if 0 <= x && x < 1<<3 && offset < 1<<11 {
|
||||
dst[i+0] = uint8(offset>>8)&0x07<<5 | uint8(x)<<2 | tagCopy1
|
||||
dst[i+1] = uint8(offset)
|
||||
i += 2
|
||||
break
|
||||
}
|
||||
|
||||
x = length
|
||||
if x > 1<<6 {
|
||||
x = 1 << 6
|
||||
}
|
||||
dst[i+0] = uint8(x-1)<<2 | tagCopy2
|
||||
dst[i+1] = uint8(offset)
|
||||
dst[i+2] = uint8(offset >> 8)
|
||||
i += 3
|
||||
length -= x
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// Encode returns the encoded form of src. The returned slice may be a sub-
|
||||
// slice of dst if dst was large enough to hold the entire encoded block.
|
||||
// Otherwise, a newly allocated slice will be returned.
|
||||
// It is valid to pass a nil dst.
|
||||
func Encode(dst, src []byte) ([]byte, error) {
|
||||
if n := MaxEncodedLen(len(src)); len(dst) < n {
|
||||
dst = make([]byte, n)
|
||||
}
|
||||
|
||||
// The block starts with the varint-encoded length of the decompressed bytes.
|
||||
d := binary.PutUvarint(dst, uint64(len(src)))
|
||||
|
||||
// Return early if src is short.
|
||||
if len(src) <= 4 {
|
||||
if len(src) != 0 {
|
||||
d += emitLiteral(dst[d:], src)
|
||||
}
|
||||
return dst[:d], nil
|
||||
}
|
||||
|
||||
// Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive.
|
||||
const maxTableSize = 1 << 14
|
||||
shift, tableSize := uint(32-8), 1<<8
|
||||
for tableSize < maxTableSize && tableSize < len(src) {
|
||||
shift--
|
||||
tableSize *= 2
|
||||
}
|
||||
var table [maxTableSize]int
|
||||
|
||||
// Iterate over the source bytes.
|
||||
var (
|
||||
s int // The iterator position.
|
||||
t int // The last position with the same hash as s.
|
||||
lit int // The start position of any pending literal bytes.
|
||||
)
|
||||
for s+3 < len(src) {
|
||||
// Update the hash table.
|
||||
b0, b1, b2, b3 := src[s], src[s+1], src[s+2], src[s+3]
|
||||
h := uint32(b0) | uint32(b1)<<8 | uint32(b2)<<16 | uint32(b3)<<24
|
||||
p := &table[(h*0x1e35a7bd)>>shift]
|
||||
// We need to to store values in [-1, inf) in table. To save
|
||||
// some initialization time, (re)use the table's zero value
|
||||
// and shift the values against this zero: add 1 on writes,
|
||||
// subtract 1 on reads.
|
||||
t, *p = *p-1, s+1
|
||||
// If t is invalid or src[s:s+4] differs from src[t:t+4], accumulate a literal byte.
|
||||
if t < 0 || s-t >= maxOffset || b0 != src[t] || b1 != src[t+1] || b2 != src[t+2] || b3 != src[t+3] {
|
||||
s++
|
||||
continue
|
||||
}
|
||||
// Otherwise, we have a match. First, emit any pending literal bytes.
|
||||
if lit != s {
|
||||
d += emitLiteral(dst[d:], src[lit:s])
|
||||
}
|
||||
// Extend the match to be as long as possible.
|
||||
s0 := s
|
||||
s, t = s+4, t+4
|
||||
for s < len(src) && src[s] == src[t] {
|
||||
s++
|
||||
t++
|
||||
}
|
||||
// Emit the copied bytes.
|
||||
d += emitCopy(dst[d:], s-t, s-s0)
|
||||
lit = s
|
||||
}
|
||||
|
||||
// Emit any final pending literal bytes and return.
|
||||
if lit != len(src) {
|
||||
d += emitLiteral(dst[d:], src[lit:])
|
||||
}
|
||||
return dst[:d], nil
|
||||
}
|
||||
|
||||
// MaxEncodedLen returns the maximum length of a snappy block, given its
|
||||
// uncompressed length.
|
||||
func MaxEncodedLen(srcLen int) int {
|
||||
// Compressed data can be defined as:
|
||||
// compressed := item* literal*
|
||||
// item := literal* copy
|
||||
//
|
||||
// The trailing literal sequence has a space blowup of at most 62/60
|
||||
// since a literal of length 60 needs one tag byte + one extra byte
|
||||
// for length information.
|
||||
//
|
||||
// Item blowup is trickier to measure. Suppose the "copy" op copies
|
||||
// 4 bytes of data. Because of a special check in the encoding code,
|
||||
// we produce a 4-byte copy only if the offset is < 65536. Therefore
|
||||
// the copy op takes 3 bytes to encode, and this type of item leads
|
||||
// to at most the 62/60 blowup for representing literals.
|
||||
//
|
||||
// Suppose the "copy" op copies 5 bytes of data. If the offset is big
|
||||
// enough, it will take 5 bytes to encode the copy op. Therefore the
|
||||
// worst case here is a one-byte literal followed by a five-byte copy.
|
||||
// That is, 6 bytes of input turn into 7 bytes of "compressed" data.
|
||||
//
|
||||
// This last factor dominates the blowup, so the final estimate is:
|
||||
return 32 + srcLen + srcLen/6
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
// Copyright 2011 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package snappy implements the snappy block-based compression format.
|
||||
// It aims for very high speeds and reasonable compression.
|
||||
//
|
||||
// The C++ snappy implementation is at http://code.google.com/p/snappy/
|
||||
package snappy
|
||||
|
||||
/*
|
||||
Each encoded block begins with the varint-encoded length of the decoded data,
|
||||
followed by a sequence of chunks. Chunks begin and end on byte boundaries. The
|
||||
first byte of each chunk is broken into its 2 least and 6 most significant bits
|
||||
called l and m: l ranges in [0, 4) and m ranges in [0, 64). l is the chunk tag.
|
||||
Zero means a literal tag. All other values mean a copy tag.
|
||||
|
||||
For literal tags:
|
||||
- If m < 60, the next 1 + m bytes are literal bytes.
|
||||
- Otherwise, let n be the little-endian unsigned integer denoted by the next
|
||||
m - 59 bytes. The next 1 + n bytes after that are literal bytes.
|
||||
|
||||
For copy tags, length bytes are copied from offset bytes ago, in the style of
|
||||
Lempel-Ziv compression algorithms. In particular:
|
||||
- For l == 1, the offset ranges in [0, 1<<11) and the length in [4, 12).
|
||||
The length is 4 + the low 3 bits of m. The high 3 bits of m form bits 8-10
|
||||
of the offset. The next byte is bits 0-7 of the offset.
|
||||
- For l == 2, the offset ranges in [0, 1<<16) and the length in [1, 65).
|
||||
The length is 1 + m. The offset is the little-endian unsigned integer
|
||||
denoted by the next 2 bytes.
|
||||
- For l == 3, this tag is a legacy format that is no longer supported.
|
||||
*/
|
||||
const (
|
||||
tagLiteral = 0x00
|
||||
tagCopy1 = 0x01
|
||||
tagCopy2 = 0x02
|
||||
tagCopy4 = 0x03
|
||||
)
|
||||
-261
@@ -1,261 +0,0 @@
|
||||
// Copyright 2011 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package snappy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var download = flag.Bool("download", false, "If true, download any missing files before running benchmarks")
|
||||
|
||||
func roundtrip(b, ebuf, dbuf []byte) error {
|
||||
e, err := Encode(ebuf, b)
|
||||
if err != nil {
|
||||
return fmt.Errorf("encoding error: %v", err)
|
||||
}
|
||||
d, err := Decode(dbuf, e)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decoding error: %v", err)
|
||||
}
|
||||
if !bytes.Equal(b, d) {
|
||||
return fmt.Errorf("roundtrip mismatch:\n\twant %v\n\tgot %v", b, d)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestEmpty(t *testing.T) {
|
||||
if err := roundtrip(nil, nil, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSmallCopy(t *testing.T) {
|
||||
for _, ebuf := range [][]byte{nil, make([]byte, 20), make([]byte, 64)} {
|
||||
for _, dbuf := range [][]byte{nil, make([]byte, 20), make([]byte, 64)} {
|
||||
for i := 0; i < 32; i++ {
|
||||
s := "aaaa" + strings.Repeat("b", i) + "aaaabbbb"
|
||||
if err := roundtrip([]byte(s), ebuf, dbuf); err != nil {
|
||||
t.Errorf("len(ebuf)=%d, len(dbuf)=%d, i=%d: %v", len(ebuf), len(dbuf), i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSmallRand(t *testing.T) {
|
||||
rand.Seed(27354294)
|
||||
for n := 1; n < 20000; n += 23 {
|
||||
b := make([]byte, n)
|
||||
for i, _ := range b {
|
||||
b[i] = uint8(rand.Uint32())
|
||||
}
|
||||
if err := roundtrip(b, nil, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSmallRegular(t *testing.T) {
|
||||
for n := 1; n < 20000; n += 23 {
|
||||
b := make([]byte, n)
|
||||
for i, _ := range b {
|
||||
b[i] = uint8(i%10 + 'a')
|
||||
}
|
||||
if err := roundtrip(b, nil, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func benchDecode(b *testing.B, src []byte) {
|
||||
encoded, err := Encode(nil, src)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
// Bandwidth is in amount of uncompressed data.
|
||||
b.SetBytes(int64(len(src)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Decode(src, encoded)
|
||||
}
|
||||
}
|
||||
|
||||
func benchEncode(b *testing.B, src []byte) {
|
||||
// Bandwidth is in amount of uncompressed data.
|
||||
b.SetBytes(int64(len(src)))
|
||||
dst := make([]byte, MaxEncodedLen(len(src)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Encode(dst, src)
|
||||
}
|
||||
}
|
||||
|
||||
func readFile(b *testing.B, filename string) []byte {
|
||||
src, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
b.Fatalf("failed reading %s: %s", filename, err)
|
||||
}
|
||||
if len(src) == 0 {
|
||||
b.Fatalf("%s has zero length", filename)
|
||||
}
|
||||
return src
|
||||
}
|
||||
|
||||
// expand returns a slice of length n containing repeated copies of src.
|
||||
func expand(src []byte, n int) []byte {
|
||||
dst := make([]byte, n)
|
||||
for x := dst; len(x) > 0; {
|
||||
i := copy(x, src)
|
||||
x = x[i:]
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func benchWords(b *testing.B, n int, decode bool) {
|
||||
// Note: the file is OS-language dependent so the resulting values are not
|
||||
// directly comparable for non-US-English OS installations.
|
||||
data := expand(readFile(b, "/usr/share/dict/words"), n)
|
||||
if decode {
|
||||
benchDecode(b, data)
|
||||
} else {
|
||||
benchEncode(b, data)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkWordsDecode1e3(b *testing.B) { benchWords(b, 1e3, true) }
|
||||
func BenchmarkWordsDecode1e4(b *testing.B) { benchWords(b, 1e4, true) }
|
||||
func BenchmarkWordsDecode1e5(b *testing.B) { benchWords(b, 1e5, true) }
|
||||
func BenchmarkWordsDecode1e6(b *testing.B) { benchWords(b, 1e6, true) }
|
||||
func BenchmarkWordsEncode1e3(b *testing.B) { benchWords(b, 1e3, false) }
|
||||
func BenchmarkWordsEncode1e4(b *testing.B) { benchWords(b, 1e4, false) }
|
||||
func BenchmarkWordsEncode1e5(b *testing.B) { benchWords(b, 1e5, false) }
|
||||
func BenchmarkWordsEncode1e6(b *testing.B) { benchWords(b, 1e6, false) }
|
||||
|
||||
// testFiles' values are copied directly from
|
||||
// https://code.google.com/p/snappy/source/browse/trunk/snappy_unittest.cc.
|
||||
// The label field is unused in snappy-go.
|
||||
var testFiles = []struct {
|
||||
label string
|
||||
filename string
|
||||
}{
|
||||
{"html", "html"},
|
||||
{"urls", "urls.10K"},
|
||||
{"jpg", "house.jpg"},
|
||||
{"pdf", "mapreduce-osdi-1.pdf"},
|
||||
{"html4", "html_x_4"},
|
||||
{"cp", "cp.html"},
|
||||
{"c", "fields.c"},
|
||||
{"lsp", "grammar.lsp"},
|
||||
{"xls", "kennedy.xls"},
|
||||
{"txt1", "alice29.txt"},
|
||||
{"txt2", "asyoulik.txt"},
|
||||
{"txt3", "lcet10.txt"},
|
||||
{"txt4", "plrabn12.txt"},
|
||||
{"bin", "ptt5"},
|
||||
{"sum", "sum"},
|
||||
{"man", "xargs.1"},
|
||||
{"pb", "geo.protodata"},
|
||||
{"gaviota", "kppkn.gtb"},
|
||||
}
|
||||
|
||||
// The test data files are present at this canonical URL.
|
||||
const baseURL = "https://snappy.googlecode.com/svn/trunk/testdata/"
|
||||
|
||||
func downloadTestdata(basename string) (errRet error) {
|
||||
filename := filepath.Join("testdata", basename)
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create %s: %s", filename, err)
|
||||
}
|
||||
defer f.Close()
|
||||
defer func() {
|
||||
if errRet != nil {
|
||||
os.Remove(filename)
|
||||
}
|
||||
}()
|
||||
resp, err := http.Get(baseURL + basename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to download %s: %s", baseURL+basename, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
_, err = io.Copy(f, resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write %s: %s", filename, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func benchFile(b *testing.B, n int, decode bool) {
|
||||
filename := filepath.Join("testdata", testFiles[n].filename)
|
||||
if stat, err := os.Stat(filename); err != nil || stat.Size() == 0 {
|
||||
if !*download {
|
||||
b.Fatal("test data not found; skipping benchmark without the -download flag")
|
||||
}
|
||||
// Download the official snappy C++ implementation reference test data
|
||||
// files for benchmarking.
|
||||
if err := os.Mkdir("testdata", 0777); err != nil && !os.IsExist(err) {
|
||||
b.Fatalf("failed to create testdata: %s", err)
|
||||
}
|
||||
for _, tf := range testFiles {
|
||||
if err := downloadTestdata(tf.filename); err != nil {
|
||||
b.Fatalf("failed to download testdata: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
data := readFile(b, filename)
|
||||
if decode {
|
||||
benchDecode(b, data)
|
||||
} else {
|
||||
benchEncode(b, data)
|
||||
}
|
||||
}
|
||||
|
||||
// Naming convention is kept similar to what snappy's C++ implementation uses.
|
||||
func Benchmark_UFlat0(b *testing.B) { benchFile(b, 0, true) }
|
||||
func Benchmark_UFlat1(b *testing.B) { benchFile(b, 1, true) }
|
||||
func Benchmark_UFlat2(b *testing.B) { benchFile(b, 2, true) }
|
||||
func Benchmark_UFlat3(b *testing.B) { benchFile(b, 3, true) }
|
||||
func Benchmark_UFlat4(b *testing.B) { benchFile(b, 4, true) }
|
||||
func Benchmark_UFlat5(b *testing.B) { benchFile(b, 5, true) }
|
||||
func Benchmark_UFlat6(b *testing.B) { benchFile(b, 6, true) }
|
||||
func Benchmark_UFlat7(b *testing.B) { benchFile(b, 7, true) }
|
||||
func Benchmark_UFlat8(b *testing.B) { benchFile(b, 8, true) }
|
||||
func Benchmark_UFlat9(b *testing.B) { benchFile(b, 9, true) }
|
||||
func Benchmark_UFlat10(b *testing.B) { benchFile(b, 10, true) }
|
||||
func Benchmark_UFlat11(b *testing.B) { benchFile(b, 11, true) }
|
||||
func Benchmark_UFlat12(b *testing.B) { benchFile(b, 12, true) }
|
||||
func Benchmark_UFlat13(b *testing.B) { benchFile(b, 13, true) }
|
||||
func Benchmark_UFlat14(b *testing.B) { benchFile(b, 14, true) }
|
||||
func Benchmark_UFlat15(b *testing.B) { benchFile(b, 15, true) }
|
||||
func Benchmark_UFlat16(b *testing.B) { benchFile(b, 16, true) }
|
||||
func Benchmark_UFlat17(b *testing.B) { benchFile(b, 17, true) }
|
||||
func Benchmark_ZFlat0(b *testing.B) { benchFile(b, 0, false) }
|
||||
func Benchmark_ZFlat1(b *testing.B) { benchFile(b, 1, false) }
|
||||
func Benchmark_ZFlat2(b *testing.B) { benchFile(b, 2, false) }
|
||||
func Benchmark_ZFlat3(b *testing.B) { benchFile(b, 3, false) }
|
||||
func Benchmark_ZFlat4(b *testing.B) { benchFile(b, 4, false) }
|
||||
func Benchmark_ZFlat5(b *testing.B) { benchFile(b, 5, false) }
|
||||
func Benchmark_ZFlat6(b *testing.B) { benchFile(b, 6, false) }
|
||||
func Benchmark_ZFlat7(b *testing.B) { benchFile(b, 7, false) }
|
||||
func Benchmark_ZFlat8(b *testing.B) { benchFile(b, 8, false) }
|
||||
func Benchmark_ZFlat9(b *testing.B) { benchFile(b, 9, false) }
|
||||
func Benchmark_ZFlat10(b *testing.B) { benchFile(b, 10, false) }
|
||||
func Benchmark_ZFlat11(b *testing.B) { benchFile(b, 11, false) }
|
||||
func Benchmark_ZFlat12(b *testing.B) { benchFile(b, 12, false) }
|
||||
func Benchmark_ZFlat13(b *testing.B) { benchFile(b, 13, false) }
|
||||
func Benchmark_ZFlat14(b *testing.B) { benchFile(b, 14, false) }
|
||||
func Benchmark_ZFlat15(b *testing.B) { benchFile(b, 15, false) }
|
||||
func Benchmark_ZFlat16(b *testing.B) { benchFile(b, 16, false) }
|
||||
func Benchmark_ZFlat17(b *testing.B) { benchFile(b, 17, false) }
|
||||
@@ -1 +0,0 @@
|
||||
/lz4-example/lz4-example
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.1
|
||||
- 1.2
|
||||
- 1.3
|
||||
- tip
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
go-lz4
|
||||
======
|
||||
|
||||
go-lz4 is port of LZ4 lossless compression algorithm to Go. The original C code
|
||||
is located at:
|
||||
|
||||
https://code.google.com/p/lz4/
|
||||
|
||||
Status
|
||||
------
|
||||
[](http://travis-ci.org/bkaradzic/go-lz4)
|
||||
[](https://godoc.org/github.com/bkaradzic/go-lz4)
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
go get github.com/bkaradzic/go-lz4
|
||||
|
||||
import "github.com/bkaradzic/go-lz4"
|
||||
|
||||
The package name is `lz4`
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
* go-lz4 saves a uint32 with the original uncompressed length at the beginning
|
||||
of the encoded buffer. They may get in the way of interoperability with
|
||||
other implementations.
|
||||
|
||||
Contributors
|
||||
------------
|
||||
|
||||
Damian Gryski ([@dgryski](https://github.com/dgryski))
|
||||
Dustin Sallings ([@dustin](https://github.com/dustin))
|
||||
|
||||
Contact
|
||||
-------
|
||||
|
||||
[@bkaradzic](https://twitter.com/bkaradzic)
|
||||
http://www.stuckingeometry.com
|
||||
|
||||
Project page
|
||||
https://github.com/bkaradzic/go-lz4
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright 2011-2012 Branimir Karadzic. All rights reserved.
|
||||
Copyright 2013 Damian Gryski. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``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 COPYRIGHT HOLDER 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.
|
||||
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
package lz4
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testfile, _ = ioutil.ReadFile("testdata/pg1661.txt")
|
||||
|
||||
func roundtrip(t *testing.T, input []byte) {
|
||||
|
||||
dst, err := Encode(nil, input)
|
||||
if err != nil {
|
||||
t.Errorf("got error during compression: %s", err)
|
||||
}
|
||||
|
||||
output, err := Decode(nil, dst)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("got error during decompress: %s", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(output, input) {
|
||||
t.Errorf("roundtrip failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmpty(t *testing.T) {
|
||||
roundtrip(t, nil)
|
||||
}
|
||||
|
||||
func TestLengths(t *testing.T) {
|
||||
|
||||
for i := 0; i < 1024; i++ {
|
||||
roundtrip(t, testfile[:i])
|
||||
}
|
||||
|
||||
for i := 1024; i < 4096; i += 23 {
|
||||
roundtrip(t, testfile[:i])
|
||||
}
|
||||
}
|
||||
|
||||
func TestWords(t *testing.T) {
|
||||
roundtrip(t, testfile)
|
||||
}
|
||||
|
||||
func BenchmarkLZ4Encode(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
Encode(nil, testfile)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLZ4Decode(b *testing.B) {
|
||||
|
||||
var compressed, _ = Encode(nil, testfile)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
Decode(nil, compressed)
|
||||
}
|
||||
}
|
||||
-13052
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
coverage.out
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
language: go
|
||||
go:
|
||||
- tip
|
||||
|
||||
install:
|
||||
- export PATH=$PATH:$HOME/gopath/bin
|
||||
- go get code.google.com/p/go.tools/cmd/cover
|
||||
- go get github.com/mattn/goveralls
|
||||
|
||||
script:
|
||||
- ./generate.sh
|
||||
- go test -coverprofile=coverage.out
|
||||
|
||||
after_success:
|
||||
- goveralls -coverprofile=coverage.out -service=travis-ci -package=calmh/xdr -repotoken="$COVERALLS_TOKEN"
|
||||
|
||||
env:
|
||||
global:
|
||||
secure: SmgnrGfp2zLrA44ChRMpjPeujubt9veZ8Fx/OseMWECmacyV5N/TuDhzIbwo6QwV4xB0sBacoPzvxQbJRVjNKsPiSu72UbcQmQ7flN4Tf7nW09tSh1iW8NgrpBCq/3UYLoBu2iPBEBKm93IK0aGNAKs6oEkB0fU27iTVBwiTXOY=
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
xdr
|
||||
===
|
||||
|
||||
[](https://travis-ci.org/calmh/xdr)
|
||||
[](https://coveralls.io/r/calmh/xdr?branch=master)
|
||||
[](http://godoc.org/github.com/calmh/xdr)
|
||||
[](http://opensource.org/licenses/MIT)
|
||||
|
||||
This is an XDR encoding/decoding library. It uses code generation and
|
||||
not reflection. It supports the IPDR bastardized XDR format when built
|
||||
with `-tags ipdr`.
|
||||
|
||||
-113
@@ -1,113 +0,0 @@
|
||||
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||
|
||||
package xdr_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/calmh/xdr"
|
||||
)
|
||||
|
||||
type XDRBenchStruct struct {
|
||||
I1 uint64
|
||||
I2 uint32
|
||||
I3 uint16
|
||||
I4 uint8
|
||||
Bs0 []byte // max:128
|
||||
Bs1 []byte
|
||||
S0 string // max:128
|
||||
S1 string
|
||||
}
|
||||
|
||||
var res []byte // no to be optimized away
|
||||
var s = XDRBenchStruct{
|
||||
I1: 42,
|
||||
I2: 43,
|
||||
I3: 44,
|
||||
I4: 45,
|
||||
Bs0: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18},
|
||||
Bs1: []byte{11, 12, 13, 14, 15, 16, 17, 18, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
||||
S0: "Hello World! String one.",
|
||||
S1: "Hello World! String two.",
|
||||
}
|
||||
var e = s.MarshalXDR()
|
||||
|
||||
func BenchmarkThisMarshal(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
res = s.MarshalXDR()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkThisUnmarshal(b *testing.B) {
|
||||
var t XDRBenchStruct
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := t.UnmarshalXDR(e)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkThisEncode(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := s.EncodeXDR(ioutil.Discard)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkThisEncoder(b *testing.B) {
|
||||
w := xdr.NewWriter(ioutil.Discard)
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := s.encodeXDR(w)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type repeatReader struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (r *repeatReader) Read(bs []byte) (n int, err error) {
|
||||
if len(bs) > len(r.data) {
|
||||
err = io.EOF
|
||||
}
|
||||
n = copy(bs, r.data)
|
||||
r.data = r.data[n:]
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (r *repeatReader) Reset(bs []byte) {
|
||||
r.data = bs
|
||||
}
|
||||
|
||||
func BenchmarkThisDecode(b *testing.B) {
|
||||
rr := &repeatReader{e}
|
||||
var t XDRBenchStruct
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := t.DecodeXDR(rr)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
rr.Reset(e)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkThisDecoder(b *testing.B) {
|
||||
rr := &repeatReader{e}
|
||||
r := xdr.NewReader(rr)
|
||||
var t XDRBenchStruct
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := t.decodeXDR(r)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
rr.Reset(e)
|
||||
}
|
||||
}
|
||||
-183
@@ -1,183 +0,0 @@
|
||||
// ************************************************************
|
||||
// This file is automatically generated by genxdr. Do not edit.
|
||||
// ************************************************************
|
||||
|
||||
package xdr_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/calmh/xdr"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
XDRBenchStruct Structure:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
+ I1 (64 bits) +
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| I2 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 0x0000 | I3 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| uint8 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length of Bs0 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ Bs0 (variable length) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length of Bs1 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ Bs1 (variable length) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length of S0 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ S0 (variable length) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length of S1 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ S1 (variable length) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
|
||||
struct XDRBenchStruct {
|
||||
unsigned hyper I1;
|
||||
unsigned int I2;
|
||||
unsigned int I3;
|
||||
uint8 I4;
|
||||
opaque Bs0<128>;
|
||||
opaque Bs1<>;
|
||||
string S0<128>;
|
||||
string S1<>;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func (o XDRBenchStruct) EncodeXDR(w io.Writer) (int, error) {
|
||||
var xw = xdr.NewWriter(w)
|
||||
return o.encodeXDR(xw)
|
||||
}
|
||||
|
||||
func (o XDRBenchStruct) MarshalXDR() []byte {
|
||||
return o.AppendXDR(make([]byte, 0, 128))
|
||||
}
|
||||
|
||||
func (o XDRBenchStruct) AppendXDR(bs []byte) []byte {
|
||||
var aw = xdr.AppendWriter(bs)
|
||||
var xw = xdr.NewWriter(&aw)
|
||||
o.encodeXDR(xw)
|
||||
return []byte(aw)
|
||||
}
|
||||
|
||||
func (o XDRBenchStruct) encodeXDR(xw *xdr.Writer) (int, error) {
|
||||
xw.WriteUint64(o.I1)
|
||||
xw.WriteUint32(o.I2)
|
||||
xw.WriteUint16(o.I3)
|
||||
xw.WriteUint8(o.I4)
|
||||
if len(o.Bs0) > 128 {
|
||||
return xw.Tot(), xdr.ErrElementSizeExceeded
|
||||
}
|
||||
xw.WriteBytes(o.Bs0)
|
||||
xw.WriteBytes(o.Bs1)
|
||||
if len(o.S0) > 128 {
|
||||
return xw.Tot(), xdr.ErrElementSizeExceeded
|
||||
}
|
||||
xw.WriteString(o.S0)
|
||||
xw.WriteString(o.S1)
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
func (o *XDRBenchStruct) DecodeXDR(r io.Reader) error {
|
||||
xr := xdr.NewReader(r)
|
||||
return o.decodeXDR(xr)
|
||||
}
|
||||
|
||||
func (o *XDRBenchStruct) UnmarshalXDR(bs []byte) error {
|
||||
var br = bytes.NewReader(bs)
|
||||
var xr = xdr.NewReader(br)
|
||||
return o.decodeXDR(xr)
|
||||
}
|
||||
|
||||
func (o *XDRBenchStruct) decodeXDR(xr *xdr.Reader) error {
|
||||
o.I1 = xr.ReadUint64()
|
||||
o.I2 = xr.ReadUint32()
|
||||
o.I3 = xr.ReadUint16()
|
||||
o.I4 = xr.ReadUint8()
|
||||
o.Bs0 = xr.ReadBytesMax(128)
|
||||
o.Bs1 = xr.ReadBytes()
|
||||
o.S0 = xr.ReadStringMax(128)
|
||||
o.S1 = xr.ReadString()
|
||||
return xr.Error()
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
repeatReader Structure:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length of data |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ data (variable length) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
|
||||
struct repeatReader {
|
||||
opaque data<>;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func (o repeatReader) EncodeXDR(w io.Writer) (int, error) {
|
||||
var xw = xdr.NewWriter(w)
|
||||
return o.encodeXDR(xw)
|
||||
}
|
||||
|
||||
func (o repeatReader) MarshalXDR() []byte {
|
||||
return o.AppendXDR(make([]byte, 0, 128))
|
||||
}
|
||||
|
||||
func (o repeatReader) AppendXDR(bs []byte) []byte {
|
||||
var aw = xdr.AppendWriter(bs)
|
||||
var xw = xdr.NewWriter(&aw)
|
||||
o.encodeXDR(xw)
|
||||
return []byte(aw)
|
||||
}
|
||||
|
||||
func (o repeatReader) encodeXDR(xw *xdr.Writer) (int, error) {
|
||||
xw.WriteBytes(o.data)
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
func (o *repeatReader) DecodeXDR(r io.Reader) error {
|
||||
xr := xdr.NewReader(r)
|
||||
return o.decodeXDR(xr)
|
||||
}
|
||||
|
||||
func (o *repeatReader) UnmarshalXDR(bs []byte) error {
|
||||
var br = bytes.NewReader(bs)
|
||||
var xr = xdr.NewReader(br)
|
||||
return o.decodeXDR(xr)
|
||||
}
|
||||
|
||||
func (o *repeatReader) decodeXDR(xr *xdr.Reader) error {
|
||||
o.data = xr.ReadBytes()
|
||||
return xr.Error()
|
||||
}
|
||||
-434
@@ -1,434 +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 main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type fieldInfo struct {
|
||||
Name string
|
||||
IsBasic bool // handled by one the native Read/WriteUint64 etc functions
|
||||
IsSlice bool // field is a slice of FieldType
|
||||
FieldType string // original type of field, i.e. "int"
|
||||
Encoder string // the encoder name, i.e. "Uint64" for Read/WriteUint64
|
||||
Convert string // what to convert to when encoding, i.e. "uint64"
|
||||
Max int // max size for slices and strings
|
||||
}
|
||||
|
||||
type structInfo struct {
|
||||
Name string
|
||||
Fields []fieldInfo
|
||||
}
|
||||
|
||||
var headerTpl = template.Must(template.New("header").Parse(`// ************************************************************
|
||||
// This file is automatically generated by genxdr. Do not edit.
|
||||
// ************************************************************
|
||||
|
||||
package {{.Package}}
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/calmh/xdr"
|
||||
)
|
||||
`))
|
||||
|
||||
var encodeTpl = template.Must(template.New("encoder").Parse(`
|
||||
func (o {{.TypeName}}) EncodeXDR(w io.Writer) (int, error) {
|
||||
var xw = xdr.NewWriter(w)
|
||||
return o.encodeXDR(xw)
|
||||
}//+n
|
||||
|
||||
func (o {{.TypeName}}) MarshalXDR() []byte {
|
||||
return o.AppendXDR(make([]byte, 0, 128))
|
||||
}//+n
|
||||
|
||||
func (o {{.TypeName}}) AppendXDR(bs []byte) []byte {
|
||||
var aw = xdr.AppendWriter(bs)
|
||||
var xw = xdr.NewWriter(&aw)
|
||||
o.encodeXDR(xw)
|
||||
return []byte(aw)
|
||||
}//+n
|
||||
|
||||
func (o {{.TypeName}}) encodeXDR(xw *xdr.Writer) (int, error) {
|
||||
{{range $fieldInfo := .Fields}}
|
||||
{{if not $fieldInfo.IsSlice}}
|
||||
{{if ne $fieldInfo.Convert ""}}
|
||||
xw.Write{{$fieldInfo.Encoder}}({{$fieldInfo.Convert}}(o.{{$fieldInfo.Name}}))
|
||||
{{else if $fieldInfo.IsBasic}}
|
||||
{{if ge $fieldInfo.Max 1}}
|
||||
if len(o.{{$fieldInfo.Name}}) > {{$fieldInfo.Max}} {
|
||||
return xw.Tot(), xdr.ErrElementSizeExceeded
|
||||
}
|
||||
{{end}}
|
||||
xw.Write{{$fieldInfo.Encoder}}(o.{{$fieldInfo.Name}})
|
||||
{{else}}
|
||||
_, err := o.{{$fieldInfo.Name}}.encodeXDR(xw)
|
||||
if err != nil {
|
||||
return xw.Tot(), err
|
||||
}
|
||||
{{end}}
|
||||
{{else}}
|
||||
{{if ge $fieldInfo.Max 1}}
|
||||
if len(o.{{$fieldInfo.Name}}) > {{$fieldInfo.Max}} {
|
||||
return xw.Tot(), xdr.ErrElementSizeExceeded
|
||||
}
|
||||
{{end}}
|
||||
xw.WriteUint32(uint32(len(o.{{$fieldInfo.Name}})))
|
||||
for i := range o.{{$fieldInfo.Name}} {
|
||||
{{if ne $fieldInfo.Convert ""}}
|
||||
xw.Write{{$fieldInfo.Encoder}}({{$fieldInfo.Convert}}(o.{{$fieldInfo.Name}}[i]))
|
||||
{{else if $fieldInfo.IsBasic}}
|
||||
xw.Write{{$fieldInfo.Encoder}}(o.{{$fieldInfo.Name}}[i])
|
||||
{{else}}
|
||||
_, err := o.{{$fieldInfo.Name}}[i].encodeXDR(xw)
|
||||
if err != nil {
|
||||
return xw.Tot(), err
|
||||
}
|
||||
{{end}}
|
||||
}
|
||||
{{end}}
|
||||
{{end}}
|
||||
return xw.Tot(), xw.Error()
|
||||
}//+n
|
||||
|
||||
func (o *{{.TypeName}}) DecodeXDR(r io.Reader) error {
|
||||
xr := xdr.NewReader(r)
|
||||
return o.decodeXDR(xr)
|
||||
}//+n
|
||||
|
||||
func (o *{{.TypeName}}) UnmarshalXDR(bs []byte) error {
|
||||
var br = bytes.NewReader(bs)
|
||||
var xr = xdr.NewReader(br)
|
||||
return o.decodeXDR(xr)
|
||||
}//+n
|
||||
|
||||
func (o *{{.TypeName}}) decodeXDR(xr *xdr.Reader) error {
|
||||
{{range $fieldInfo := .Fields}}
|
||||
{{if not $fieldInfo.IsSlice}}
|
||||
{{if ne $fieldInfo.Convert ""}}
|
||||
o.{{$fieldInfo.Name}} = {{$fieldInfo.FieldType}}(xr.Read{{$fieldInfo.Encoder}}())
|
||||
{{else if $fieldInfo.IsBasic}}
|
||||
{{if ge $fieldInfo.Max 1}}
|
||||
o.{{$fieldInfo.Name}} = xr.Read{{$fieldInfo.Encoder}}Max({{$fieldInfo.Max}})
|
||||
{{else}}
|
||||
o.{{$fieldInfo.Name}} = xr.Read{{$fieldInfo.Encoder}}()
|
||||
{{end}}
|
||||
{{else}}
|
||||
(&o.{{$fieldInfo.Name}}).decodeXDR(xr)
|
||||
{{end}}
|
||||
{{else}}
|
||||
_{{$fieldInfo.Name}}Size := int(xr.ReadUint32())
|
||||
{{if ge $fieldInfo.Max 1}}
|
||||
if _{{$fieldInfo.Name}}Size > {{$fieldInfo.Max}} {
|
||||
return xdr.ErrElementSizeExceeded
|
||||
}
|
||||
{{end}}
|
||||
o.{{$fieldInfo.Name}} = make([]{{$fieldInfo.FieldType}}, _{{$fieldInfo.Name}}Size)
|
||||
for i := range o.{{$fieldInfo.Name}} {
|
||||
{{if ne $fieldInfo.Convert ""}}
|
||||
o.{{$fieldInfo.Name}}[i] = {{$fieldInfo.FieldType}}(xr.Read{{$fieldInfo.Encoder}}())
|
||||
{{else if $fieldInfo.IsBasic}}
|
||||
o.{{$fieldInfo.Name}}[i] = xr.Read{{$fieldInfo.Encoder}}()
|
||||
{{else}}
|
||||
(&o.{{$fieldInfo.Name}}[i]).decodeXDR(xr)
|
||||
{{end}}
|
||||
}
|
||||
{{end}}
|
||||
{{end}}
|
||||
return xr.Error()
|
||||
}`))
|
||||
|
||||
var maxRe = regexp.MustCompile(`\Wmax:(\d+)`)
|
||||
|
||||
type typeSet struct {
|
||||
Type string
|
||||
Encoder string
|
||||
}
|
||||
|
||||
var xdrEncoders = map[string]typeSet{
|
||||
"int8": typeSet{"uint8", "Uint8"},
|
||||
"uint8": typeSet{"", "Uint8"},
|
||||
"int16": typeSet{"uint16", "Uint16"},
|
||||
"uint16": typeSet{"", "Uint16"},
|
||||
"int32": typeSet{"uint32", "Uint32"},
|
||||
"uint32": typeSet{"", "Uint32"},
|
||||
"int64": typeSet{"uint64", "Uint64"},
|
||||
"uint64": typeSet{"", "Uint64"},
|
||||
"int": typeSet{"uint64", "Uint64"},
|
||||
"string": typeSet{"", "String"},
|
||||
"[]byte": typeSet{"", "Bytes"},
|
||||
"bool": typeSet{"", "Bool"},
|
||||
}
|
||||
|
||||
func handleStruct(t *ast.StructType) []fieldInfo {
|
||||
var fs []fieldInfo
|
||||
|
||||
for _, sf := range t.Fields.List {
|
||||
if len(sf.Names) == 0 {
|
||||
// We don't handle anonymous fields
|
||||
continue
|
||||
}
|
||||
|
||||
fn := sf.Names[0].Name
|
||||
var max = 0
|
||||
if sf.Comment != nil {
|
||||
c := sf.Comment.List[0].Text
|
||||
if m := maxRe.FindStringSubmatch(c); m != nil {
|
||||
max, _ = strconv.Atoi(m[1])
|
||||
}
|
||||
if strings.Contains(c, "noencode") {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
var f fieldInfo
|
||||
switch ft := sf.Type.(type) {
|
||||
case *ast.Ident:
|
||||
tn := ft.Name
|
||||
if enc, ok := xdrEncoders[tn]; ok {
|
||||
f = fieldInfo{
|
||||
Name: fn,
|
||||
IsBasic: true,
|
||||
FieldType: tn,
|
||||
Encoder: enc.Encoder,
|
||||
Convert: enc.Type,
|
||||
Max: max,
|
||||
}
|
||||
} else {
|
||||
f = fieldInfo{
|
||||
Name: fn,
|
||||
IsBasic: false,
|
||||
FieldType: tn,
|
||||
Max: max,
|
||||
}
|
||||
}
|
||||
|
||||
case *ast.ArrayType:
|
||||
if ft.Len != nil {
|
||||
// We don't handle arrays
|
||||
continue
|
||||
}
|
||||
|
||||
tn := ft.Elt.(*ast.Ident).Name
|
||||
if enc, ok := xdrEncoders["[]"+tn]; ok {
|
||||
f = fieldInfo{
|
||||
Name: fn,
|
||||
IsBasic: true,
|
||||
FieldType: tn,
|
||||
Encoder: enc.Encoder,
|
||||
Convert: enc.Type,
|
||||
Max: max,
|
||||
}
|
||||
} else if enc, ok := xdrEncoders[tn]; ok {
|
||||
f = fieldInfo{
|
||||
Name: fn,
|
||||
IsBasic: true,
|
||||
IsSlice: true,
|
||||
FieldType: tn,
|
||||
Encoder: enc.Encoder,
|
||||
Convert: enc.Type,
|
||||
Max: max,
|
||||
}
|
||||
} else {
|
||||
f = fieldInfo{
|
||||
Name: fn,
|
||||
IsBasic: false,
|
||||
IsSlice: true,
|
||||
FieldType: tn,
|
||||
Max: max,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fs = append(fs, f)
|
||||
}
|
||||
|
||||
return fs
|
||||
}
|
||||
|
||||
func generateCode(s structInfo) {
|
||||
name := s.Name
|
||||
fs := s.Fields
|
||||
|
||||
var buf bytes.Buffer
|
||||
err := encodeTpl.Execute(&buf, map[string]interface{}{"TypeName": name, "Fields": fs})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
bs := regexp.MustCompile(`(\s*\n)+`).ReplaceAll(buf.Bytes(), []byte("\n"))
|
||||
bs = bytes.Replace(bs, []byte("//+n"), []byte("\n"), -1)
|
||||
|
||||
bs, err = format.Source(bs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(bs))
|
||||
}
|
||||
|
||||
func uncamelize(s string) string {
|
||||
return regexp.MustCompile("[a-z][A-Z]").ReplaceAllStringFunc(s, func(camel string) string {
|
||||
return camel[:1] + " " + camel[1:]
|
||||
})
|
||||
}
|
||||
|
||||
func generateDiagram(s structInfo) {
|
||||
sn := s.Name
|
||||
fs := s.Fields
|
||||
|
||||
fmt.Println(sn + " Structure:")
|
||||
fmt.Println()
|
||||
fmt.Println(" 0 1 2 3")
|
||||
fmt.Println(" 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1")
|
||||
line := "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+"
|
||||
fmt.Println(line)
|
||||
|
||||
for _, f := range fs {
|
||||
tn := f.FieldType
|
||||
sl := f.IsSlice
|
||||
name := uncamelize(f.Name)
|
||||
|
||||
if sl {
|
||||
fmt.Printf("| %s |\n", center("Number of "+name, 61))
|
||||
fmt.Println(line)
|
||||
}
|
||||
switch tn {
|
||||
case "bool":
|
||||
fmt.Printf("| %s |V|\n", center(name+" (V=0 or 1)", 59))
|
||||
fmt.Println(line)
|
||||
case "uint16":
|
||||
fmt.Printf("| %s | %s |\n", center("0x0000", 29), center(name, 29))
|
||||
fmt.Println(line)
|
||||
case "uint32":
|
||||
fmt.Printf("| %s |\n", center(name, 61))
|
||||
fmt.Println(line)
|
||||
case "int64", "uint64":
|
||||
fmt.Printf("| %-61s |\n", "")
|
||||
fmt.Printf("+ %s +\n", center(name+" (64 bits)", 61))
|
||||
fmt.Printf("| %-61s |\n", "")
|
||||
fmt.Println(line)
|
||||
case "string", "byte": // XXX We assume slice of byte!
|
||||
fmt.Printf("| %s |\n", center("Length of "+name, 61))
|
||||
fmt.Println(line)
|
||||
fmt.Printf("/ %61s /\n", "")
|
||||
fmt.Printf("\\ %s \\\n", center(name+" (variable length)", 61))
|
||||
fmt.Printf("/ %61s /\n", "")
|
||||
fmt.Println(line)
|
||||
default:
|
||||
if sl {
|
||||
tn = "Zero or more " + tn + " Structures"
|
||||
fmt.Printf("/ %s /\n", center("", 61))
|
||||
fmt.Printf("\\ %s \\\n", center(tn, 61))
|
||||
fmt.Printf("/ %s /\n", center("", 61))
|
||||
} else {
|
||||
fmt.Printf("| %s |\n", center(tn, 61))
|
||||
}
|
||||
fmt.Println(line)
|
||||
}
|
||||
}
|
||||
fmt.Println()
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func generateXdr(s structInfo) {
|
||||
sn := s.Name
|
||||
fs := s.Fields
|
||||
|
||||
fmt.Printf("struct %s {\n", sn)
|
||||
|
||||
for _, f := range fs {
|
||||
tn := f.FieldType
|
||||
fn := f.Name
|
||||
suf := ""
|
||||
l := ""
|
||||
if f.Max > 0 {
|
||||
l = strconv.Itoa(f.Max)
|
||||
}
|
||||
if f.IsSlice {
|
||||
suf = "<" + l + ">"
|
||||
}
|
||||
|
||||
switch tn {
|
||||
case "uint16", "uint32":
|
||||
fmt.Printf("\tunsigned int %s%s;\n", fn, suf)
|
||||
case "int64":
|
||||
fmt.Printf("\thyper %s%s;\n", fn, suf)
|
||||
case "uint64":
|
||||
fmt.Printf("\tunsigned hyper %s%s;\n", fn, suf)
|
||||
case "string":
|
||||
fmt.Printf("\tstring %s<%s>;\n", fn, l)
|
||||
case "byte":
|
||||
fmt.Printf("\topaque %s<%s>;\n", fn, l)
|
||||
default:
|
||||
fmt.Printf("\t%s %s%s;\n", tn, fn, suf)
|
||||
}
|
||||
}
|
||||
fmt.Println("}")
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func center(s string, w int) string {
|
||||
w -= len(s)
|
||||
l := w / 2
|
||||
r := l
|
||||
if l+r < w {
|
||||
r++
|
||||
}
|
||||
return strings.Repeat(" ", l) + s + strings.Repeat(" ", r)
|
||||
}
|
||||
|
||||
func inspector(structs *[]structInfo) func(ast.Node) bool {
|
||||
return func(n ast.Node) bool {
|
||||
switch n := n.(type) {
|
||||
case *ast.TypeSpec:
|
||||
switch t := n.Type.(type) {
|
||||
case *ast.StructType:
|
||||
name := n.Name.Name
|
||||
fs := handleStruct(t)
|
||||
*structs = append(*structs, structInfo{name, fs})
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
fname := flag.Arg(0)
|
||||
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, fname, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var structs []structInfo
|
||||
i := inspector(&structs)
|
||||
ast.Inspect(f, i)
|
||||
|
||||
headerTpl.Execute(os.Stdout, map[string]string{"Package": f.Name.Name})
|
||||
for _, s := range structs {
|
||||
fmt.Printf("\n/*\n\n")
|
||||
generateDiagram(s)
|
||||
generateXdr(s)
|
||||
fmt.Printf("*/\n")
|
||||
generateCode(s)
|
||||
}
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||
|
||||
package xdr
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
debug = len(os.Getenv("XDRTRACE")) > 0
|
||||
dl = log.New(os.Stdout, "xdr: ", log.Lshortfile|log.Ltime|log.Lmicroseconds)
|
||||
)
|
||||
|
||||
const maxDebugBytes = 32
|
||||
-75
@@ -1,75 +0,0 @@
|
||||
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||
|
||||
package xdr_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
"testing/quick"
|
||||
|
||||
"github.com/calmh/xdr"
|
||||
)
|
||||
|
||||
// Contains all supported types
|
||||
type TestStruct struct {
|
||||
I int
|
||||
I8 int8
|
||||
UI8 uint8
|
||||
I16 int16
|
||||
UI16 uint16
|
||||
I32 int32
|
||||
UI32 uint32
|
||||
I64 int64
|
||||
UI64 uint64
|
||||
BS []byte
|
||||
S string
|
||||
C Opaque
|
||||
}
|
||||
|
||||
type Opaque [32]byte
|
||||
|
||||
func (u *Opaque) encodeXDR(w *xdr.Writer) (int, error) {
|
||||
return w.WriteRaw(u[:])
|
||||
}
|
||||
|
||||
func (u *Opaque) decodeXDR(r *xdr.Reader) (int, error) {
|
||||
return r.ReadRaw(u[:])
|
||||
}
|
||||
|
||||
func (Opaque) Generate(rand *rand.Rand, size int) reflect.Value {
|
||||
var u Opaque
|
||||
for i := range u[:] {
|
||||
u[i] = byte(rand.Int())
|
||||
}
|
||||
return reflect.ValueOf(u)
|
||||
}
|
||||
|
||||
func TestEncDec(t *testing.T) {
|
||||
fn := func(t0 TestStruct) bool {
|
||||
bs := t0.MarshalXDR()
|
||||
var t1 TestStruct
|
||||
err := t1.UnmarshalXDR(bs)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Not comparing with DeepEqual since we'll unmarshal nil slices as empty
|
||||
if t0.I != t1.I ||
|
||||
t0.I16 != t1.I16 || t0.UI16 != t1.UI16 ||
|
||||
t0.I32 != t1.I32 || t0.UI32 != t1.UI32 ||
|
||||
t0.I64 != t1.I64 || t0.UI64 != t1.UI64 ||
|
||||
bytes.Compare(t0.BS, t1.BS) != 0 ||
|
||||
t0.S != t1.S || t0.C != t1.C {
|
||||
t.Logf("%#v", t0)
|
||||
t.Logf("%#v", t1)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
if err := quick.Check(fn, nil); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
-136
@@ -1,136 +0,0 @@
|
||||
// ************************************************************
|
||||
// This file is automatically generated by genxdr. Do not edit.
|
||||
// ************************************************************
|
||||
|
||||
package xdr_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/calmh/xdr"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
TestStruct Structure:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| int |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| int8 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| uint8 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| int16 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 0x0000 | UI16 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| int32 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| UI32 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
+ I64 (64 bits) +
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
+ UI64 (64 bits) +
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length of BS |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ BS (variable length) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length of S |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ S (variable length) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Opaque |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
|
||||
struct TestStruct {
|
||||
int I;
|
||||
int8 I8;
|
||||
uint8 UI8;
|
||||
int16 I16;
|
||||
unsigned int UI16;
|
||||
int32 I32;
|
||||
unsigned int UI32;
|
||||
hyper I64;
|
||||
unsigned hyper UI64;
|
||||
opaque BS<>;
|
||||
string S<>;
|
||||
Opaque C;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func (o TestStruct) EncodeXDR(w io.Writer) (int, error) {
|
||||
var xw = xdr.NewWriter(w)
|
||||
return o.encodeXDR(xw)
|
||||
}
|
||||
|
||||
func (o TestStruct) MarshalXDR() []byte {
|
||||
return o.AppendXDR(make([]byte, 0, 128))
|
||||
}
|
||||
|
||||
func (o TestStruct) AppendXDR(bs []byte) []byte {
|
||||
var aw = xdr.AppendWriter(bs)
|
||||
var xw = xdr.NewWriter(&aw)
|
||||
o.encodeXDR(xw)
|
||||
return []byte(aw)
|
||||
}
|
||||
|
||||
func (o TestStruct) encodeXDR(xw *xdr.Writer) (int, error) {
|
||||
xw.WriteUint64(uint64(o.I))
|
||||
xw.WriteUint8(uint8(o.I8))
|
||||
xw.WriteUint8(o.UI8)
|
||||
xw.WriteUint16(uint16(o.I16))
|
||||
xw.WriteUint16(o.UI16)
|
||||
xw.WriteUint32(uint32(o.I32))
|
||||
xw.WriteUint32(o.UI32)
|
||||
xw.WriteUint64(uint64(o.I64))
|
||||
xw.WriteUint64(o.UI64)
|
||||
xw.WriteBytes(o.BS)
|
||||
xw.WriteString(o.S)
|
||||
_, err := o.C.encodeXDR(xw)
|
||||
if err != nil {
|
||||
return xw.Tot(), err
|
||||
}
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
func (o *TestStruct) DecodeXDR(r io.Reader) error {
|
||||
xr := xdr.NewReader(r)
|
||||
return o.decodeXDR(xr)
|
||||
}
|
||||
|
||||
func (o *TestStruct) UnmarshalXDR(bs []byte) error {
|
||||
var br = bytes.NewReader(bs)
|
||||
var xr = xdr.NewReader(br)
|
||||
return o.decodeXDR(xr)
|
||||
}
|
||||
|
||||
func (o *TestStruct) decodeXDR(xr *xdr.Reader) error {
|
||||
o.I = int(xr.ReadUint64())
|
||||
o.I8 = int8(xr.ReadUint8())
|
||||
o.UI8 = xr.ReadUint8()
|
||||
o.I16 = int16(xr.ReadUint16())
|
||||
o.UI16 = xr.ReadUint16()
|
||||
o.I32 = int32(xr.ReadUint32())
|
||||
o.UI32 = xr.ReadUint32()
|
||||
o.I64 = int64(xr.ReadUint64())
|
||||
o.UI64 = xr.ReadUint64()
|
||||
o.BS = xr.ReadBytes()
|
||||
o.S = xr.ReadString()
|
||||
(&o.C).decodeXDR(xr)
|
||||
return xr.Error()
|
||||
}
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
go run cmd/genxdr/main.go -- bench_test.go > bench_xdr_test.go
|
||||
go run cmd/genxdr/main.go -- encdec_test.go > encdec_xdr_test.go
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||
|
||||
// +build ipdr
|
||||
|
||||
package xdr
|
||||
|
||||
func pad(l int) int {
|
||||
return 0
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||
|
||||
// +build !ipdr
|
||||
|
||||
package xdr
|
||||
|
||||
func pad(l int) int {
|
||||
d := l % 4
|
||||
if d == 0 {
|
||||
return 0
|
||||
}
|
||||
return 4 - d
|
||||
}
|
||||
-164
@@ -1,164 +0,0 @@
|
||||
// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
|
||||
// All rights reserved. Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package xdr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var ErrElementSizeExceeded = errors.New("element size exceeded")
|
||||
|
||||
type Reader struct {
|
||||
r io.Reader
|
||||
err error
|
||||
b [8]byte
|
||||
}
|
||||
|
||||
func NewReader(r io.Reader) *Reader {
|
||||
return &Reader{
|
||||
r: r,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reader) ReadRaw(bs []byte) (int, error) {
|
||||
if r.err != nil {
|
||||
return 0, r.err
|
||||
}
|
||||
|
||||
var n int
|
||||
n, r.err = io.ReadFull(r.r, bs)
|
||||
return n, r.err
|
||||
}
|
||||
|
||||
func (r *Reader) ReadString() string {
|
||||
return r.ReadStringMax(0)
|
||||
}
|
||||
|
||||
func (r *Reader) ReadStringMax(max int) string {
|
||||
buf := r.ReadBytesMaxInto(max, nil)
|
||||
bh := (*reflect.SliceHeader)(unsafe.Pointer(&buf))
|
||||
sh := reflect.StringHeader{
|
||||
Data: bh.Data,
|
||||
Len: bh.Len,
|
||||
}
|
||||
return *((*string)(unsafe.Pointer(&sh)))
|
||||
}
|
||||
|
||||
func (r *Reader) ReadBytes() []byte {
|
||||
return r.ReadBytesInto(nil)
|
||||
}
|
||||
|
||||
func (r *Reader) ReadBytesMax(max int) []byte {
|
||||
return r.ReadBytesMaxInto(max, nil)
|
||||
}
|
||||
|
||||
func (r *Reader) ReadBytesInto(dst []byte) []byte {
|
||||
return r.ReadBytesMaxInto(0, dst)
|
||||
}
|
||||
|
||||
func (r *Reader) ReadBytesMaxInto(max int, dst []byte) []byte {
|
||||
if r.err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
l := int(r.ReadUint32())
|
||||
if r.err != nil {
|
||||
return nil
|
||||
}
|
||||
if max > 0 && l > max {
|
||||
r.err = ErrElementSizeExceeded
|
||||
return nil
|
||||
}
|
||||
|
||||
if fullLen := l + pad(l); fullLen > len(dst) {
|
||||
dst = make([]byte, fullLen)
|
||||
} else {
|
||||
dst = dst[:fullLen]
|
||||
}
|
||||
|
||||
var n int
|
||||
n, r.err = io.ReadFull(r.r, dst)
|
||||
if r.err != nil {
|
||||
if debug {
|
||||
dl.Printf("rd bytes (%d): %v", len(dst), r.err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if debug {
|
||||
if n > maxDebugBytes {
|
||||
dl.Printf("rd bytes (%d): %x...", len(dst), dst[:maxDebugBytes])
|
||||
} else {
|
||||
dl.Printf("rd bytes (%d): %x", len(dst), dst)
|
||||
}
|
||||
}
|
||||
return dst[:l]
|
||||
}
|
||||
|
||||
func (r *Reader) ReadBool() bool {
|
||||
return r.ReadUint8() != 0
|
||||
}
|
||||
|
||||
func (r *Reader) ReadUint32() uint32 {
|
||||
if r.err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
_, r.err = io.ReadFull(r.r, r.b[:4])
|
||||
if r.err != nil {
|
||||
if debug {
|
||||
dl.Printf("rd uint32: %v", r.err)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
v := uint32(r.b[3]) | uint32(r.b[2])<<8 | uint32(r.b[1])<<16 | uint32(r.b[0])<<24
|
||||
|
||||
if debug {
|
||||
dl.Printf("rd uint32=%d (0x%08x)", v, v)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (r *Reader) ReadUint64() uint64 {
|
||||
if r.err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
_, r.err = io.ReadFull(r.r, r.b[:8])
|
||||
if r.err != nil {
|
||||
if debug {
|
||||
dl.Printf("rd uint64: %v", r.err)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
v := uint64(r.b[7]) | uint64(r.b[6])<<8 | uint64(r.b[5])<<16 | uint64(r.b[4])<<24 |
|
||||
uint64(r.b[3])<<32 | uint64(r.b[2])<<40 | uint64(r.b[1])<<48 | uint64(r.b[0])<<56
|
||||
|
||||
if debug {
|
||||
dl.Printf("rd uint64=%d (0x%016x)", v, v)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
type XDRError struct {
|
||||
op string
|
||||
err error
|
||||
}
|
||||
|
||||
func (e XDRError) Error() string {
|
||||
return "xdr " + e.op + ": " + e.err.Error()
|
||||
}
|
||||
|
||||
func (r *Reader) Error() error {
|
||||
if r.err == nil {
|
||||
return nil
|
||||
}
|
||||
return XDRError{"read", r.err}
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
|
||||
// All rights reserved. Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ipdr
|
||||
|
||||
package xdr
|
||||
|
||||
import "io"
|
||||
|
||||
func (r *Reader) ReadUint8() uint8 {
|
||||
if r.err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
_, r.err = io.ReadFull(r.r, r.b[:1])
|
||||
if r.err != nil {
|
||||
if debug {
|
||||
dl.Printf("rd uint8: %v", r.err)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
if debug {
|
||||
dl.Printf("rd uint8=%d (0x%08x)", r.b[0], r.b[0])
|
||||
}
|
||||
return r.b[0]
|
||||
}
|
||||
|
||||
func (r *Reader) ReadUint16() uint16 {
|
||||
if r.err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
_, r.err = io.ReadFull(r.r, r.b[:2])
|
||||
if r.err != nil {
|
||||
if debug {
|
||||
dl.Printf("rd uint16: %v", r.err)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
v := uint16(r.b[1]) | uint16(r.b[0])<<8
|
||||
|
||||
if debug {
|
||||
dl.Printf("rd uint16=%d (0x%08x)", v, v)
|
||||
}
|
||||
return v
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
|
||||
// All rights reserved. Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !ipdr
|
||||
|
||||
package xdr
|
||||
|
||||
func (r *Reader) ReadUint8() uint8 {
|
||||
return uint8(r.ReadUint32())
|
||||
}
|
||||
|
||||
func (r *Reader) ReadUint16() uint16 {
|
||||
return uint16(r.ReadUint32())
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||
|
||||
// +build refl
|
||||
|
||||
package xdr_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
refl "github.com/davecgh/go-xdr/xdr"
|
||||
)
|
||||
|
||||
func TestCompareMarshals(t *testing.T) {
|
||||
e0 := s.MarshalXDR()
|
||||
e1, err := refl.Marshal(s)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if bytes.Compare(e0, e1) != 0 {
|
||||
t.Fatalf("Encoding mismatch;\n\t%x (this)\n\t%x (refl)", e0, e1)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkReflMarshal(b *testing.B) {
|
||||
var err error
|
||||
for i := 0; i < b.N; i++ {
|
||||
res, err = refl.Marshal(s)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkReflUnmarshal(b *testing.B) {
|
||||
var t XDRBenchStruct
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := refl.Unmarshal(e, &t)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
-146
@@ -1,146 +0,0 @@
|
||||
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||
|
||||
package xdr
|
||||
|
||||
import (
|
||||
"io"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var padBytes = []byte{0, 0, 0}
|
||||
|
||||
type Writer struct {
|
||||
w io.Writer
|
||||
tot int
|
||||
err error
|
||||
b [8]byte
|
||||
}
|
||||
|
||||
type AppendWriter []byte
|
||||
|
||||
func (w *AppendWriter) Write(bs []byte) (int, error) {
|
||||
*w = append(*w, bs...)
|
||||
return len(bs), nil
|
||||
}
|
||||
|
||||
func NewWriter(w io.Writer) *Writer {
|
||||
return &Writer{
|
||||
w: w,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Writer) WriteRaw(bs []byte) (int, error) {
|
||||
if w.err != nil {
|
||||
return 0, w.err
|
||||
}
|
||||
|
||||
var n int
|
||||
n, w.err = w.w.Write(bs)
|
||||
return n, w.err
|
||||
}
|
||||
|
||||
func (w *Writer) WriteString(s string) (int, error) {
|
||||
sh := *((*reflect.StringHeader)(unsafe.Pointer(&s)))
|
||||
bh := reflect.SliceHeader{
|
||||
Data: sh.Data,
|
||||
Len: sh.Len,
|
||||
Cap: sh.Len,
|
||||
}
|
||||
return w.WriteBytes(*(*[]byte)(unsafe.Pointer(&bh)))
|
||||
}
|
||||
|
||||
func (w *Writer) WriteBytes(bs []byte) (int, error) {
|
||||
if w.err != nil {
|
||||
return 0, w.err
|
||||
}
|
||||
|
||||
w.WriteUint32(uint32(len(bs)))
|
||||
if w.err != nil {
|
||||
return 0, w.err
|
||||
}
|
||||
|
||||
if debug {
|
||||
if len(bs) > maxDebugBytes {
|
||||
dl.Printf("wr bytes (%d): %x...", len(bs), bs[:maxDebugBytes])
|
||||
} else {
|
||||
dl.Printf("wr bytes (%d): %x", len(bs), bs)
|
||||
}
|
||||
}
|
||||
|
||||
var l, n int
|
||||
n, w.err = w.w.Write(bs)
|
||||
l += n
|
||||
|
||||
if p := pad(len(bs)); w.err == nil && p > 0 {
|
||||
n, w.err = w.w.Write(padBytes[:p])
|
||||
l += n
|
||||
}
|
||||
|
||||
w.tot += l
|
||||
return l, w.err
|
||||
}
|
||||
|
||||
func (w *Writer) WriteBool(v bool) (int, error) {
|
||||
if v {
|
||||
return w.WriteUint8(1)
|
||||
} else {
|
||||
return w.WriteUint8(0)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Writer) WriteUint32(v uint32) (int, error) {
|
||||
if w.err != nil {
|
||||
return 0, w.err
|
||||
}
|
||||
|
||||
if debug {
|
||||
dl.Printf("wr uint32=%d", v)
|
||||
}
|
||||
|
||||
w.b[0] = byte(v >> 24)
|
||||
w.b[1] = byte(v >> 16)
|
||||
w.b[2] = byte(v >> 8)
|
||||
w.b[3] = byte(v)
|
||||
|
||||
var l int
|
||||
l, w.err = w.w.Write(w.b[:4])
|
||||
w.tot += l
|
||||
return l, w.err
|
||||
}
|
||||
|
||||
func (w *Writer) WriteUint64(v uint64) (int, error) {
|
||||
if w.err != nil {
|
||||
return 0, w.err
|
||||
}
|
||||
|
||||
if debug {
|
||||
dl.Printf("wr uint64=%d", v)
|
||||
}
|
||||
|
||||
w.b[0] = byte(v >> 56)
|
||||
w.b[1] = byte(v >> 48)
|
||||
w.b[2] = byte(v >> 40)
|
||||
w.b[3] = byte(v >> 32)
|
||||
w.b[4] = byte(v >> 24)
|
||||
w.b[5] = byte(v >> 16)
|
||||
w.b[6] = byte(v >> 8)
|
||||
w.b[7] = byte(v)
|
||||
|
||||
var l int
|
||||
l, w.err = w.w.Write(w.b[:8])
|
||||
w.tot += l
|
||||
return l, w.err
|
||||
}
|
||||
|
||||
func (w *Writer) Tot() int {
|
||||
return w.tot
|
||||
}
|
||||
|
||||
func (w *Writer) Error() error {
|
||||
if w.err == nil {
|
||||
return nil
|
||||
}
|
||||
return XDRError{"write", w.err}
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||
|
||||
// +build ipdr
|
||||
|
||||
package xdr
|
||||
|
||||
func (w *Writer) WriteUint8(v uint8) (int, error) {
|
||||
if w.err != nil {
|
||||
return 0, w.err
|
||||
}
|
||||
|
||||
if debug {
|
||||
dl.Printf("wr uint8=%d", v)
|
||||
}
|
||||
|
||||
w.b[0] = byte(v)
|
||||
|
||||
var l int
|
||||
l, w.err = w.w.Write(w.b[:1])
|
||||
w.tot += l
|
||||
return l, w.err
|
||||
}
|
||||
|
||||
func (w *Writer) WriteUint16(v uint16) (int, error) {
|
||||
if w.err != nil {
|
||||
return 0, w.err
|
||||
}
|
||||
|
||||
if debug {
|
||||
dl.Printf("wr uint8=%d", v)
|
||||
}
|
||||
|
||||
w.b[0] = byte(v >> 8)
|
||||
w.b[1] = byte(v)
|
||||
|
||||
var l int
|
||||
l, w.err = w.w.Write(w.b[:2])
|
||||
w.tot += l
|
||||
return l, w.err
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||
|
||||
// +build !ipdr
|
||||
|
||||
package xdr
|
||||
|
||||
func (w *Writer) WriteUint8(v uint8) (int, error) {
|
||||
return w.WriteUint32(uint32(v))
|
||||
}
|
||||
|
||||
func (w *Writer) WriteUint16(v uint16) (int, error) {
|
||||
return w.WriteUint32(uint32(v))
|
||||
}
|
||||
-92
@@ -1,92 +0,0 @@
|
||||
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||
|
||||
package xdr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"testing/quick"
|
||||
)
|
||||
|
||||
func TestBytesNil(t *testing.T) {
|
||||
fn := func(bs []byte) bool {
|
||||
var b = new(bytes.Buffer)
|
||||
var w = NewWriter(b)
|
||||
var r = NewReader(b)
|
||||
w.WriteBytes(bs)
|
||||
w.WriteBytes(bs)
|
||||
r.ReadBytes()
|
||||
res := r.ReadBytes()
|
||||
return bytes.Compare(bs, res) == 0
|
||||
}
|
||||
if err := quick.Check(fn, nil); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBytesGiven(t *testing.T) {
|
||||
fn := func(bs []byte) bool {
|
||||
var b = new(bytes.Buffer)
|
||||
var w = NewWriter(b)
|
||||
var r = NewReader(b)
|
||||
w.WriteBytes(bs)
|
||||
w.WriteBytes(bs)
|
||||
res := make([]byte, 12)
|
||||
res = r.ReadBytesInto(res)
|
||||
res = r.ReadBytesInto(res)
|
||||
return bytes.Compare(bs, res) == 0
|
||||
}
|
||||
if err := quick.Check(fn, nil); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadBytesMaxInto(t *testing.T) {
|
||||
var max = 64
|
||||
for tot := 32; tot < 128; tot++ {
|
||||
for diff := -32; diff <= 32; diff++ {
|
||||
var b = new(bytes.Buffer)
|
||||
var r = NewReader(b)
|
||||
var w = NewWriter(b)
|
||||
|
||||
var toWrite = make([]byte, tot)
|
||||
w.WriteBytes(toWrite)
|
||||
|
||||
var buf = make([]byte, tot+diff)
|
||||
var bs = r.ReadBytesMaxInto(max, buf)
|
||||
|
||||
if tot <= max {
|
||||
if read := len(bs); read != tot {
|
||||
t.Errorf("Incorrect read bytes, wrote=%d, buf=%d, max=%d, read=%d", tot, tot+diff, max, read)
|
||||
}
|
||||
} else if r.err != ErrElementSizeExceeded {
|
||||
t.Errorf("Unexpected non-ErrElementSizeExceeded error for wrote=%d, max=%d: %v", tot, max, r.err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadStringMax(t *testing.T) {
|
||||
for tot := 42; tot < 72; tot++ {
|
||||
for max := 0; max < 128; max++ {
|
||||
var b = new(bytes.Buffer)
|
||||
var r = NewReader(b)
|
||||
var w = NewWriter(b)
|
||||
|
||||
var toWrite = make([]byte, tot)
|
||||
w.WriteBytes(toWrite)
|
||||
|
||||
var str = r.ReadStringMax(max)
|
||||
var read = len(str)
|
||||
|
||||
if max == 0 || tot <= max {
|
||||
if read != tot {
|
||||
t.Errorf("Incorrect read bytes, wrote=%d, max=%d, read=%d", tot, max, read)
|
||||
}
|
||||
} else if r.err != ErrElementSizeExceeded {
|
||||
t.Errorf("Unexpected non-ErrElementSizeExceeded error for wrote=%d, max=%d, read=%d: %v", tot, max, read, r.err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-185
@@ -1,185 +0,0 @@
|
||||
This software is licensed under the LGPLv3, included below.
|
||||
|
||||
As a special exception to the GNU Lesser General Public License version 3
|
||||
("LGPL3"), the copyright holders of this Library give you permission to
|
||||
convey to a third party a Combined Work that links statically or dynamically
|
||||
to this Library without providing any Minimal Corresponding Source or
|
||||
Minimal Application Code as set out in 4d or providing the installation
|
||||
information set out in section 4e, provided that you comply with the other
|
||||
provisions of LGPL3 and provided that you meet, for the Application the
|
||||
terms and conditions of the license(s) which apply to the Application.
|
||||
|
||||
Except as stated in this special exception, the provisions of LGPL3 will
|
||||
continue to comply in full to this Library. If you modify this Library, you
|
||||
may apply this exception to your version of this Library, but you are not
|
||||
obliged to do so. If you do not wish to do so, delete this exception
|
||||
statement from your version. This exception does not (and cannot) modify any
|
||||
license terms which apply to the Application, with which you must still
|
||||
comply.
|
||||
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
-117
@@ -1,117 +0,0 @@
|
||||
# ratelimit
|
||||
--
|
||||
import "github.com/juju/ratelimit"
|
||||
|
||||
The ratelimit package provides an efficient token bucket implementation. See
|
||||
http://en.wikipedia.org/wiki/Token_bucket.
|
||||
|
||||
## Usage
|
||||
|
||||
#### func Reader
|
||||
|
||||
```go
|
||||
func Reader(r io.Reader, bucket *Bucket) io.Reader
|
||||
```
|
||||
Reader returns a reader that is rate limited by the given token bucket. Each
|
||||
token in the bucket represents one byte.
|
||||
|
||||
#### func Writer
|
||||
|
||||
```go
|
||||
func Writer(w io.Writer, bucket *Bucket) io.Writer
|
||||
```
|
||||
Writer returns a reader that is rate limited by the given token bucket. Each
|
||||
token in the bucket represents one byte.
|
||||
|
||||
#### type Bucket
|
||||
|
||||
```go
|
||||
type Bucket struct {
|
||||
}
|
||||
```
|
||||
|
||||
Bucket represents a token bucket that fills at a predetermined rate. Methods on
|
||||
Bucket may be called concurrently.
|
||||
|
||||
#### func NewBucket
|
||||
|
||||
```go
|
||||
func NewBucket(fillInterval time.Duration, capacity int64) *Bucket
|
||||
```
|
||||
NewBucket returns a new token bucket that fills at the rate of one token every
|
||||
fillInterval, up to the given maximum capacity. Both arguments must be positive.
|
||||
The bucket is initially full.
|
||||
|
||||
#### func NewBucketWithQuantum
|
||||
|
||||
```go
|
||||
func NewBucketWithQuantum(fillInterval time.Duration, capacity, quantum int64) *Bucket
|
||||
```
|
||||
NewBucketWithQuantum is similar to NewBucket, but allows the specification of
|
||||
the quantum size - quantum tokens are added every fillInterval.
|
||||
|
||||
#### func NewBucketWithRate
|
||||
|
||||
```go
|
||||
func NewBucketWithRate(rate float64, capacity int64) *Bucket
|
||||
```
|
||||
NewBucketWithRate returns a token bucket that fills the bucket at the rate of
|
||||
rate tokens per second up to the given maximum capacity. Because of limited
|
||||
clock resolution, at high rates, the actual rate may be up to 1% different from
|
||||
the specified rate.
|
||||
|
||||
#### func (*Bucket) Rate
|
||||
|
||||
```go
|
||||
func (tb *Bucket) Rate() float64
|
||||
```
|
||||
Rate returns the fill rate of the bucket, in tokens per second.
|
||||
|
||||
#### func (*Bucket) Take
|
||||
|
||||
```go
|
||||
func (tb *Bucket) Take(count int64) time.Duration
|
||||
```
|
||||
Take takes count tokens from the bucket without blocking. It returns the time
|
||||
that the caller should wait until the tokens are actually available.
|
||||
|
||||
Note that if the request is irrevocable - there is no way to return tokens to
|
||||
the bucket once this method commits us to taking them.
|
||||
|
||||
#### func (*Bucket) TakeAvailable
|
||||
|
||||
```go
|
||||
func (tb *Bucket) TakeAvailable(count int64) int64
|
||||
```
|
||||
TakeAvailable takes up to count immediately available tokens from the bucket. It
|
||||
returns the number of tokens removed, or zero if there are no available tokens.
|
||||
It does not block.
|
||||
|
||||
#### func (*Bucket) TakeMaxDuration
|
||||
|
||||
```go
|
||||
func (tb *Bucket) TakeMaxDuration(count int64, maxWait time.Duration) (time.Duration, bool)
|
||||
```
|
||||
TakeMaxDuration is like Take, except that it will only take tokens from the
|
||||
bucket if the wait time for the tokens is no greater than maxWait.
|
||||
|
||||
If it would take longer than maxWait for the tokens to become available, it does
|
||||
nothing and reports false, otherwise it returns the time that the caller should
|
||||
wait until the tokens are actually available, and reports true.
|
||||
|
||||
#### func (*Bucket) Wait
|
||||
|
||||
```go
|
||||
func (tb *Bucket) Wait(count int64)
|
||||
```
|
||||
Wait takes count tokens from the bucket, waiting until they are available.
|
||||
|
||||
#### func (*Bucket) WaitMaxDuration
|
||||
|
||||
```go
|
||||
func (tb *Bucket) WaitMaxDuration(count int64, maxWait time.Duration) bool
|
||||
```
|
||||
WaitMaxDuration is like Wait except that it will only take tokens from the
|
||||
bucket if it needs to wait for no greater than maxWait. It reports whether any
|
||||
tokens have been removed from the bucket If no tokens have been removed, it
|
||||
returns immediately.
|
||||
-226
@@ -1,226 +0,0 @@
|
||||
// Copyright 2014 Canonical Ltd.
|
||||
// Licensed under the LGPLv3 with static-linking exception.
|
||||
// See LICENCE file for details.
|
||||
|
||||
// The ratelimit package provides an efficient token bucket implementation.
|
||||
// See http://en.wikipedia.org/wiki/Token_bucket.
|
||||
package ratelimit
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Bucket represents a token bucket that fills at a predetermined rate.
|
||||
// Methods on Bucket may be called concurrently.
|
||||
type Bucket struct {
|
||||
startTime time.Time
|
||||
capacity int64
|
||||
quantum int64
|
||||
fillInterval time.Duration
|
||||
|
||||
// The mutex guards the fields following it.
|
||||
mu sync.Mutex
|
||||
|
||||
// avail holds the number of available tokens
|
||||
// in the bucket, as of availTick ticks from startTime.
|
||||
// It will be negative when there are consumers
|
||||
// waiting for tokens.
|
||||
avail int64
|
||||
availTick int64
|
||||
}
|
||||
|
||||
// NewBucket returns a new token bucket that fills at the
|
||||
// rate of one token every fillInterval, up to the given
|
||||
// maximum capacity. Both arguments must be
|
||||
// positive. The bucket is initially full.
|
||||
func NewBucket(fillInterval time.Duration, capacity int64) *Bucket {
|
||||
return NewBucketWithQuantum(fillInterval, capacity, 1)
|
||||
}
|
||||
|
||||
// rateMargin specifes the allowed variance of actual
|
||||
// rate from specified rate. 1% seems reasonable.
|
||||
const rateMargin = 0.01
|
||||
|
||||
// NewBucketWithRate returns a token bucket that fills the bucket
|
||||
// at the rate of rate tokens per second up to the given
|
||||
// maximum capacity. Because of limited clock resolution,
|
||||
// at high rates, the actual rate may be up to 1% different from the
|
||||
// specified rate.
|
||||
func NewBucketWithRate(rate float64, capacity int64) *Bucket {
|
||||
for quantum := int64(1); quantum < 1<<50; quantum = nextQuantum(quantum) {
|
||||
fillInterval := time.Duration(1e9 * float64(quantum) / rate)
|
||||
if fillInterval <= 0 {
|
||||
continue
|
||||
}
|
||||
tb := NewBucketWithQuantum(fillInterval, capacity, quantum)
|
||||
if diff := abs(tb.Rate() - rate); diff/rate <= rateMargin {
|
||||
return tb
|
||||
}
|
||||
}
|
||||
panic("cannot find suitable quantum for " + strconv.FormatFloat(rate, 'g', -1, 64))
|
||||
}
|
||||
|
||||
// nextQuantum returns the next quantum to try after q.
|
||||
// We grow the quantum exponentially, but slowly, so we
|
||||
// get a good fit in the lower numbers.
|
||||
func nextQuantum(q int64) int64 {
|
||||
q1 := q * 11 / 10
|
||||
if q1 == q {
|
||||
q1++
|
||||
}
|
||||
return q1
|
||||
}
|
||||
|
||||
// NewBucketWithQuantum is similar to NewBucket, but allows
|
||||
// the specification of the quantum size - quantum tokens
|
||||
// are added every fillInterval.
|
||||
func NewBucketWithQuantum(fillInterval time.Duration, capacity, quantum int64) *Bucket {
|
||||
if fillInterval <= 0 {
|
||||
panic("token bucket fill interval is not > 0")
|
||||
}
|
||||
if capacity <= 0 {
|
||||
panic("token bucket capacity is not > 0")
|
||||
}
|
||||
if quantum <= 0 {
|
||||
panic("token bucket quantum is not > 0")
|
||||
}
|
||||
return &Bucket{
|
||||
startTime: time.Now(),
|
||||
capacity: capacity,
|
||||
quantum: quantum,
|
||||
avail: capacity,
|
||||
fillInterval: fillInterval,
|
||||
}
|
||||
}
|
||||
|
||||
// Wait takes count tokens from the bucket, waiting until they are
|
||||
// available.
|
||||
func (tb *Bucket) Wait(count int64) {
|
||||
if d := tb.Take(count); d > 0 {
|
||||
time.Sleep(d)
|
||||
}
|
||||
}
|
||||
|
||||
// WaitMaxDuration is like Wait except that it will
|
||||
// only take tokens from the bucket if it needs to wait
|
||||
// for no greater than maxWait. It reports whether
|
||||
// any tokens have been removed from the bucket
|
||||
// If no tokens have been removed, it returns immediately.
|
||||
func (tb *Bucket) WaitMaxDuration(count int64, maxWait time.Duration) bool {
|
||||
d, ok := tb.TakeMaxDuration(count, maxWait)
|
||||
if d > 0 {
|
||||
time.Sleep(d)
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
const infinityDuration time.Duration = 0x7fffffffffffffff
|
||||
|
||||
// Take takes count tokens from the bucket without blocking. It returns
|
||||
// the time that the caller should wait until the tokens are actually
|
||||
// available.
|
||||
//
|
||||
// Note that if the request is irrevocable - there is no way to return
|
||||
// tokens to the bucket once this method commits us to taking them.
|
||||
func (tb *Bucket) Take(count int64) time.Duration {
|
||||
d, _ := tb.take(time.Now(), count, infinityDuration)
|
||||
return d
|
||||
}
|
||||
|
||||
// TakeMaxDuration is like Take, except that
|
||||
// it will only take tokens from the bucket if the wait
|
||||
// time for the tokens is no greater than maxWait.
|
||||
//
|
||||
// If it would take longer than maxWait for the tokens
|
||||
// to become available, it does nothing and reports false,
|
||||
// otherwise it returns the time that the caller should
|
||||
// wait until the tokens are actually available, and reports
|
||||
// true.
|
||||
func (tb *Bucket) TakeMaxDuration(count int64, maxWait time.Duration) (time.Duration, bool) {
|
||||
return tb.take(time.Now(), count, maxWait)
|
||||
}
|
||||
|
||||
// TakeAvailable takes up to count immediately available tokens from the
|
||||
// bucket. It returns the number of tokens removed, or zero if there are
|
||||
// no available tokens. It does not block.
|
||||
func (tb *Bucket) TakeAvailable(count int64) int64 {
|
||||
return tb.takeAvailable(time.Now(), count)
|
||||
}
|
||||
|
||||
// takeAvailable is the internal version of TakeAvailable - it takes the
|
||||
// current time as an argument to enable easy testing.
|
||||
func (tb *Bucket) takeAvailable(now time.Time, count int64) int64 {
|
||||
if count <= 0 {
|
||||
return 0
|
||||
}
|
||||
tb.mu.Lock()
|
||||
defer tb.mu.Unlock()
|
||||
|
||||
tb.adjust(now)
|
||||
if tb.avail <= 0 {
|
||||
return 0
|
||||
}
|
||||
if count > tb.avail {
|
||||
count = tb.avail
|
||||
}
|
||||
tb.avail -= count
|
||||
return count
|
||||
}
|
||||
|
||||
// Rate returns the fill rate of the bucket, in tokens per second.
|
||||
func (tb *Bucket) Rate() float64 {
|
||||
return 1e9 * float64(tb.quantum) / float64(tb.fillInterval)
|
||||
}
|
||||
|
||||
// take is the internal version of Take - it takes the current time as
|
||||
// an argument to enable easy testing.
|
||||
func (tb *Bucket) take(now time.Time, count int64, maxWait time.Duration) (time.Duration, bool) {
|
||||
if count <= 0 {
|
||||
return 0, true
|
||||
}
|
||||
tb.mu.Lock()
|
||||
defer tb.mu.Unlock()
|
||||
|
||||
currentTick := tb.adjust(now)
|
||||
avail := tb.avail - count
|
||||
if avail >= 0 {
|
||||
tb.avail = avail
|
||||
return 0, true
|
||||
}
|
||||
// Round up the missing tokens to the nearest multiple
|
||||
// of quantum - the tokens won't be available until
|
||||
// that tick.
|
||||
endTick := currentTick + (-avail+tb.quantum-1)/tb.quantum
|
||||
endTime := tb.startTime.Add(time.Duration(endTick) * tb.fillInterval)
|
||||
waitTime := endTime.Sub(now)
|
||||
if waitTime > maxWait {
|
||||
return 0, false
|
||||
}
|
||||
tb.avail = avail
|
||||
return waitTime, true
|
||||
}
|
||||
|
||||
// adjust adjusts the current bucket capacity based on the current time.
|
||||
// It returns the current tick.
|
||||
func (tb *Bucket) adjust(now time.Time) (currentTick int64) {
|
||||
currentTick = int64(now.Sub(tb.startTime) / tb.fillInterval)
|
||||
|
||||
if tb.avail >= tb.capacity {
|
||||
return
|
||||
}
|
||||
tb.avail += (currentTick - tb.availTick) * tb.quantum
|
||||
if tb.avail > tb.capacity {
|
||||
tb.avail = tb.capacity
|
||||
}
|
||||
tb.availTick = currentTick
|
||||
return
|
||||
}
|
||||
|
||||
func abs(f float64) float64 {
|
||||
if f < 0 {
|
||||
return -f
|
||||
}
|
||||
return f
|
||||
}
|
||||
-328
@@ -1,328 +0,0 @@
|
||||
// Copyright 2014 Canonical Ltd.
|
||||
// Licensed under the LGPLv3 with static-linking exception.
|
||||
// See LICENCE file for details.
|
||||
|
||||
package ratelimit
|
||||
|
||||
import (
|
||||
gc "launchpad.net/gocheck"
|
||||
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestPackage(t *testing.T) {
|
||||
gc.TestingT(t)
|
||||
}
|
||||
|
||||
type rateLimitSuite struct{}
|
||||
|
||||
var _ = gc.Suite(rateLimitSuite{})
|
||||
|
||||
type takeReq struct {
|
||||
time time.Duration
|
||||
count int64
|
||||
expectWait time.Duration
|
||||
}
|
||||
|
||||
var takeTests = []struct {
|
||||
about string
|
||||
fillInterval time.Duration
|
||||
capacity int64
|
||||
reqs []takeReq
|
||||
}{{
|
||||
about: "serial requests",
|
||||
fillInterval: 250 * time.Millisecond,
|
||||
capacity: 10,
|
||||
reqs: []takeReq{{
|
||||
time: 0,
|
||||
count: 0,
|
||||
expectWait: 0,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 10,
|
||||
expectWait: 0,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 1,
|
||||
expectWait: 250 * time.Millisecond,
|
||||
}, {
|
||||
time: 250 * time.Millisecond,
|
||||
count: 1,
|
||||
expectWait: 250 * time.Millisecond,
|
||||
}},
|
||||
}, {
|
||||
about: "concurrent requests",
|
||||
fillInterval: 250 * time.Millisecond,
|
||||
capacity: 10,
|
||||
reqs: []takeReq{{
|
||||
time: 0,
|
||||
count: 10,
|
||||
expectWait: 0,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 2,
|
||||
expectWait: 500 * time.Millisecond,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 2,
|
||||
expectWait: 1000 * time.Millisecond,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 1,
|
||||
expectWait: 1250 * time.Millisecond,
|
||||
}},
|
||||
}, {
|
||||
about: "more than capacity",
|
||||
fillInterval: 1 * time.Millisecond,
|
||||
capacity: 10,
|
||||
reqs: []takeReq{{
|
||||
time: 0,
|
||||
count: 10,
|
||||
expectWait: 0,
|
||||
}, {
|
||||
time: 20 * time.Millisecond,
|
||||
count: 15,
|
||||
expectWait: 5 * time.Millisecond,
|
||||
}},
|
||||
}, {
|
||||
about: "sub-quantum time",
|
||||
fillInterval: 10 * time.Millisecond,
|
||||
capacity: 10,
|
||||
reqs: []takeReq{{
|
||||
time: 0,
|
||||
count: 10,
|
||||
expectWait: 0,
|
||||
}, {
|
||||
time: 7 * time.Millisecond,
|
||||
count: 1,
|
||||
expectWait: 3 * time.Millisecond,
|
||||
}, {
|
||||
time: 8 * time.Millisecond,
|
||||
count: 1,
|
||||
expectWait: 12 * time.Millisecond,
|
||||
}},
|
||||
}, {
|
||||
about: "within capacity",
|
||||
fillInterval: 10 * time.Millisecond,
|
||||
capacity: 5,
|
||||
reqs: []takeReq{{
|
||||
time: 0,
|
||||
count: 5,
|
||||
expectWait: 0,
|
||||
}, {
|
||||
time: 60 * time.Millisecond,
|
||||
count: 5,
|
||||
expectWait: 0,
|
||||
}, {
|
||||
time: 60 * time.Millisecond,
|
||||
count: 1,
|
||||
expectWait: 10 * time.Millisecond,
|
||||
}, {
|
||||
time: 80 * time.Millisecond,
|
||||
count: 2,
|
||||
expectWait: 10 * time.Millisecond,
|
||||
}},
|
||||
}}
|
||||
|
||||
func (rateLimitSuite) TestTake(c *gc.C) {
|
||||
for i, test := range takeTests {
|
||||
tb := NewBucket(test.fillInterval, test.capacity)
|
||||
for j, req := range test.reqs {
|
||||
d, ok := tb.take(tb.startTime.Add(req.time), req.count, infinityDuration)
|
||||
c.Assert(ok, gc.Equals, true)
|
||||
if d != req.expectWait {
|
||||
c.Fatalf("test %d.%d, %s, got %v want %v", i, j, test.about, d, req.expectWait)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rateLimitSuite) TestTakeMaxDuration(c *gc.C) {
|
||||
for i, test := range takeTests {
|
||||
tb := NewBucket(test.fillInterval, test.capacity)
|
||||
for j, req := range test.reqs {
|
||||
if req.expectWait > 0 {
|
||||
d, ok := tb.take(tb.startTime.Add(req.time), req.count, req.expectWait-1)
|
||||
c.Assert(ok, gc.Equals, false)
|
||||
c.Assert(d, gc.Equals, time.Duration(0))
|
||||
}
|
||||
d, ok := tb.take(tb.startTime.Add(req.time), req.count, req.expectWait)
|
||||
c.Assert(ok, gc.Equals, true)
|
||||
if d != req.expectWait {
|
||||
c.Fatalf("test %d.%d, %s, got %v want %v", i, j, test.about, d, req.expectWait)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type takeAvailableReq struct {
|
||||
time time.Duration
|
||||
count int64
|
||||
expect int64
|
||||
}
|
||||
|
||||
var takeAvailableTests = []struct {
|
||||
about string
|
||||
fillInterval time.Duration
|
||||
capacity int64
|
||||
reqs []takeAvailableReq
|
||||
}{{
|
||||
about: "serial requests",
|
||||
fillInterval: 250 * time.Millisecond,
|
||||
capacity: 10,
|
||||
reqs: []takeAvailableReq{{
|
||||
time: 0,
|
||||
count: 0,
|
||||
expect: 0,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 10,
|
||||
expect: 10,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 1,
|
||||
expect: 0,
|
||||
}, {
|
||||
time: 250 * time.Millisecond,
|
||||
count: 1,
|
||||
expect: 1,
|
||||
}},
|
||||
}, {
|
||||
about: "concurrent requests",
|
||||
fillInterval: 250 * time.Millisecond,
|
||||
capacity: 10,
|
||||
reqs: []takeAvailableReq{{
|
||||
time: 0,
|
||||
count: 5,
|
||||
expect: 5,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 2,
|
||||
expect: 2,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 5,
|
||||
expect: 3,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 1,
|
||||
expect: 0,
|
||||
}},
|
||||
}, {
|
||||
about: "more than capacity",
|
||||
fillInterval: 1 * time.Millisecond,
|
||||
capacity: 10,
|
||||
reqs: []takeAvailableReq{{
|
||||
time: 0,
|
||||
count: 10,
|
||||
expect: 10,
|
||||
}, {
|
||||
time: 20 * time.Millisecond,
|
||||
count: 15,
|
||||
expect: 10,
|
||||
}},
|
||||
}, {
|
||||
about: "within capacity",
|
||||
fillInterval: 10 * time.Millisecond,
|
||||
capacity: 5,
|
||||
reqs: []takeAvailableReq{{
|
||||
time: 0,
|
||||
count: 5,
|
||||
expect: 5,
|
||||
}, {
|
||||
time: 60 * time.Millisecond,
|
||||
count: 5,
|
||||
expect: 5,
|
||||
}, {
|
||||
time: 70 * time.Millisecond,
|
||||
count: 1,
|
||||
expect: 1,
|
||||
}},
|
||||
}}
|
||||
|
||||
func (rateLimitSuite) TestTakeAvailable(c *gc.C) {
|
||||
for i, test := range takeAvailableTests {
|
||||
tb := NewBucket(test.fillInterval, test.capacity)
|
||||
for j, req := range test.reqs {
|
||||
d := tb.takeAvailable(tb.startTime.Add(req.time), req.count)
|
||||
if d != req.expect {
|
||||
c.Fatalf("test %d.%d, %s, got %v want %v", i, j, test.about, d, req.expect)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rateLimitSuite) TestPanics(c *gc.C) {
|
||||
c.Assert(func() { NewBucket(0, 1) }, gc.PanicMatches, "token bucket fill interval is not > 0")
|
||||
c.Assert(func() { NewBucket(-2, 1) }, gc.PanicMatches, "token bucket fill interval is not > 0")
|
||||
c.Assert(func() { NewBucket(1, 0) }, gc.PanicMatches, "token bucket capacity is not > 0")
|
||||
c.Assert(func() { NewBucket(1, -2) }, gc.PanicMatches, "token bucket capacity is not > 0")
|
||||
}
|
||||
|
||||
func isCloseTo(x, y, tolerance float64) bool {
|
||||
return abs(x-y)/y < tolerance
|
||||
}
|
||||
|
||||
func (rateLimitSuite) TestRate(c *gc.C) {
|
||||
tb := NewBucket(1, 1)
|
||||
if !isCloseTo(tb.Rate(), 1e9, 0.00001) {
|
||||
c.Fatalf("got %v want 1e9", tb.Rate())
|
||||
}
|
||||
tb = NewBucket(2*time.Second, 1)
|
||||
if !isCloseTo(tb.Rate(), 0.5, 0.00001) {
|
||||
c.Fatalf("got %v want 0.5", tb.Rate())
|
||||
}
|
||||
tb = NewBucketWithQuantum(100*time.Millisecond, 1, 5)
|
||||
if !isCloseTo(tb.Rate(), 50, 0.00001) {
|
||||
c.Fatalf("got %v want 50", tb.Rate())
|
||||
}
|
||||
}
|
||||
|
||||
func checkRate(c *gc.C, rate float64) {
|
||||
tb := NewBucketWithRate(rate, 1<<62)
|
||||
if !isCloseTo(tb.Rate(), rate, rateMargin) {
|
||||
c.Fatalf("got %g want %v", tb.Rate(), rate)
|
||||
}
|
||||
d, ok := tb.take(tb.startTime, 1<<62, infinityDuration)
|
||||
c.Assert(ok, gc.Equals, true)
|
||||
c.Assert(d, gc.Equals, time.Duration(0))
|
||||
|
||||
// Check that the actual rate is as expected by
|
||||
// asking for a not-quite multiple of the bucket's
|
||||
// quantum and checking that the wait time
|
||||
// correct.
|
||||
d, ok = tb.take(tb.startTime, tb.quantum*2-tb.quantum/2, infinityDuration)
|
||||
c.Assert(ok, gc.Equals, true)
|
||||
expectTime := 1e9 * float64(tb.quantum) * 2 / rate
|
||||
if !isCloseTo(float64(d), expectTime, rateMargin) {
|
||||
c.Fatalf("rate %g: got %g want %v", rate, float64(d), expectTime)
|
||||
}
|
||||
}
|
||||
|
||||
func (rateLimitSuite) TestNewWithRate(c *gc.C) {
|
||||
for rate := float64(1); rate < 1e6; rate += 7 {
|
||||
checkRate(c, rate)
|
||||
}
|
||||
for _, rate := range []float64{
|
||||
1024 * 1024 * 1024,
|
||||
1e-5,
|
||||
0.9e-5,
|
||||
0.5,
|
||||
0.9,
|
||||
0.9e8,
|
||||
3e12,
|
||||
4e18,
|
||||
} {
|
||||
checkRate(c, rate)
|
||||
checkRate(c, rate/3)
|
||||
checkRate(c, rate*1.3)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkWait(b *testing.B) {
|
||||
tb := NewBucket(1, 16*1024)
|
||||
for i := b.N - 1; i >= 0; i-- {
|
||||
tb.Wait(1)
|
||||
}
|
||||
}
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
// Copyright 2014 Canonical Ltd.
|
||||
// Licensed under the LGPLv3 with static-linking exception.
|
||||
// See LICENCE file for details.
|
||||
|
||||
package ratelimit
|
||||
|
||||
import "io"
|
||||
|
||||
type reader struct {
|
||||
r io.Reader
|
||||
bucket *Bucket
|
||||
}
|
||||
|
||||
// Reader returns a reader that is rate limited by
|
||||
// the given token bucket. Each token in the bucket
|
||||
// represents one byte.
|
||||
func Reader(r io.Reader, bucket *Bucket) io.Reader {
|
||||
return &reader{
|
||||
r: r,
|
||||
bucket: bucket,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *reader) Read(buf []byte) (int, error) {
|
||||
n, err := r.r.Read(buf)
|
||||
if n <= 0 {
|
||||
return n, err
|
||||
}
|
||||
r.bucket.Wait(int64(n))
|
||||
return n, err
|
||||
}
|
||||
|
||||
type writer struct {
|
||||
w io.Writer
|
||||
bucket *Bucket
|
||||
}
|
||||
|
||||
// Writer returns a reader that is rate limited by
|
||||
// the given token bucket. Each token in the bucket
|
||||
// represents one byte.
|
||||
func Writer(w io.Writer, bucket *Bucket) io.Writer {
|
||||
return &writer{
|
||||
w: w,
|
||||
bucket: bucket,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *writer) Write(buf []byte) (int, error) {
|
||||
w.bucket.Wait(int64(len(buf)))
|
||||
return w.w.Write(buf)
|
||||
}
|
||||
-216
@@ -1,216 +0,0 @@
|
||||
// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
|
||||
// All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package leveldb
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb/memdb"
|
||||
)
|
||||
|
||||
var (
|
||||
errBatchTooShort = errors.New("leveldb: batch is too short")
|
||||
errBatchBadRecord = errors.New("leveldb: bad record in batch")
|
||||
)
|
||||
|
||||
const kBatchHdrLen = 8 + 4
|
||||
|
||||
type batchReplay interface {
|
||||
put(key, value []byte, seq uint64)
|
||||
delete(key []byte, seq uint64)
|
||||
}
|
||||
|
||||
// Batch is a write batch.
|
||||
type Batch struct {
|
||||
buf []byte
|
||||
rLen, bLen int
|
||||
seq uint64
|
||||
sync bool
|
||||
}
|
||||
|
||||
func (b *Batch) grow(n int) {
|
||||
off := len(b.buf)
|
||||
if off == 0 {
|
||||
// include headers
|
||||
off = kBatchHdrLen
|
||||
n += off
|
||||
}
|
||||
if cap(b.buf)-off >= n {
|
||||
return
|
||||
}
|
||||
buf := make([]byte, 2*cap(b.buf)+n)
|
||||
copy(buf, b.buf)
|
||||
b.buf = buf[:off]
|
||||
}
|
||||
|
||||
func (b *Batch) appendRec(t vType, key, value []byte) {
|
||||
n := 1 + binary.MaxVarintLen32 + len(key)
|
||||
if t == tVal {
|
||||
n += binary.MaxVarintLen32 + len(value)
|
||||
}
|
||||
b.grow(n)
|
||||
off := len(b.buf)
|
||||
buf := b.buf[:off+n]
|
||||
buf[off] = byte(t)
|
||||
off += 1
|
||||
off += binary.PutUvarint(buf[off:], uint64(len(key)))
|
||||
copy(buf[off:], key)
|
||||
off += len(key)
|
||||
if t == tVal {
|
||||
off += binary.PutUvarint(buf[off:], uint64(len(value)))
|
||||
copy(buf[off:], value)
|
||||
off += len(value)
|
||||
}
|
||||
b.buf = buf[:off]
|
||||
b.rLen++
|
||||
// Include 8-byte ikey header
|
||||
b.bLen += len(key) + len(value) + 8
|
||||
}
|
||||
|
||||
// Put appends 'put operation' of the given key/value pair to the batch.
|
||||
// It is safe to modify the contents of the argument after Put returns.
|
||||
func (b *Batch) Put(key, value []byte) {
|
||||
b.appendRec(tVal, key, value)
|
||||
}
|
||||
|
||||
// Delete appends 'delete operation' of the given key to the batch.
|
||||
// It is safe to modify the contents of the argument after Delete returns.
|
||||
func (b *Batch) Delete(key []byte) {
|
||||
b.appendRec(tDel, key, nil)
|
||||
}
|
||||
|
||||
// Reset resets the batch.
|
||||
func (b *Batch) Reset() {
|
||||
b.buf = nil
|
||||
b.seq = 0
|
||||
b.rLen = 0
|
||||
b.bLen = 0
|
||||
b.sync = false
|
||||
}
|
||||
|
||||
func (b *Batch) init(sync bool) {
|
||||
b.sync = sync
|
||||
}
|
||||
|
||||
func (b *Batch) put(key, value []byte, seq uint64) {
|
||||
if b.rLen == 0 {
|
||||
b.seq = seq
|
||||
}
|
||||
b.Put(key, value)
|
||||
}
|
||||
|
||||
func (b *Batch) delete(key []byte, seq uint64) {
|
||||
if b.rLen == 0 {
|
||||
b.seq = seq
|
||||
}
|
||||
b.Delete(key)
|
||||
}
|
||||
|
||||
func (b *Batch) append(p *Batch) {
|
||||
if p.rLen > 0 {
|
||||
b.grow(len(p.buf) - kBatchHdrLen)
|
||||
b.buf = append(b.buf, p.buf[kBatchHdrLen:]...)
|
||||
b.rLen += p.rLen
|
||||
}
|
||||
if p.sync {
|
||||
b.sync = true
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Batch) len() int {
|
||||
return b.rLen
|
||||
}
|
||||
|
||||
func (b *Batch) size() int {
|
||||
return b.bLen
|
||||
}
|
||||
|
||||
func (b *Batch) encode() []byte {
|
||||
b.grow(0)
|
||||
binary.LittleEndian.PutUint64(b.buf, b.seq)
|
||||
binary.LittleEndian.PutUint32(b.buf[8:], uint32(b.rLen))
|
||||
|
||||
return b.buf
|
||||
}
|
||||
|
||||
func (b *Batch) decode(buf []byte) error {
|
||||
if len(buf) < kBatchHdrLen {
|
||||
return errBatchTooShort
|
||||
}
|
||||
|
||||
b.seq = binary.LittleEndian.Uint64(buf)
|
||||
b.rLen = int(binary.LittleEndian.Uint32(buf[8:]))
|
||||
// No need to be precise at this point, it won't be used anyway
|
||||
b.bLen = len(buf) - kBatchHdrLen
|
||||
b.buf = buf
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Batch) decodeRec(f func(i int, t vType, key, value []byte)) error {
|
||||
off := kBatchHdrLen
|
||||
for i := 0; i < b.rLen; i++ {
|
||||
if off >= len(b.buf) {
|
||||
return errors.New("leveldb: invalid batch record length")
|
||||
}
|
||||
|
||||
t := vType(b.buf[off])
|
||||
if t > tVal {
|
||||
return errors.New("leveldb: invalid batch record type in batch")
|
||||
}
|
||||
off += 1
|
||||
|
||||
x, n := binary.Uvarint(b.buf[off:])
|
||||
off += n
|
||||
if n <= 0 || off+int(x) > len(b.buf) {
|
||||
return errBatchBadRecord
|
||||
}
|
||||
key := b.buf[off : off+int(x)]
|
||||
off += int(x)
|
||||
|
||||
var value []byte
|
||||
if t == tVal {
|
||||
x, n := binary.Uvarint(b.buf[off:])
|
||||
off += n
|
||||
if n <= 0 || off+int(x) > len(b.buf) {
|
||||
return errBatchBadRecord
|
||||
}
|
||||
value = b.buf[off : off+int(x)]
|
||||
off += int(x)
|
||||
}
|
||||
|
||||
f(i, t, key, value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Batch) replay(to batchReplay) error {
|
||||
return b.decodeRec(func(i int, t vType, key, value []byte) {
|
||||
switch t {
|
||||
case tVal:
|
||||
to.put(key, value, b.seq+uint64(i))
|
||||
case tDel:
|
||||
to.delete(key, b.seq+uint64(i))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (b *Batch) memReplay(to *memdb.DB) error {
|
||||
return b.decodeRec(func(i int, t vType, key, value []byte) {
|
||||
ikey := newIKey(key, b.seq+uint64(i), t)
|
||||
to.Put(ikey, value)
|
||||
})
|
||||
}
|
||||
|
||||
func (b *Batch) revertMemReplay(to *memdb.DB) error {
|
||||
return b.decodeRec(func(i int, t vType, key, value []byte) {
|
||||
ikey := newIKey(key, b.seq+uint64(i), t)
|
||||
to.Delete(ikey)
|
||||
})
|
||||
}
|
||||
-120
@@ -1,120 +0,0 @@
|
||||
// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
|
||||
// All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package leveldb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb/comparer"
|
||||
"github.com/syndtr/goleveldb/leveldb/memdb"
|
||||
)
|
||||
|
||||
type tbRec struct {
|
||||
t vType
|
||||
key, value []byte
|
||||
}
|
||||
|
||||
type testBatch struct {
|
||||
rec []*tbRec
|
||||
}
|
||||
|
||||
func (p *testBatch) put(key, value []byte, seq uint64) {
|
||||
p.rec = append(p.rec, &tbRec{tVal, key, value})
|
||||
}
|
||||
|
||||
func (p *testBatch) delete(key []byte, seq uint64) {
|
||||
p.rec = append(p.rec, &tbRec{tDel, key, nil})
|
||||
}
|
||||
|
||||
func compareBatch(t *testing.T, b1, b2 *Batch) {
|
||||
if b1.seq != b2.seq {
|
||||
t.Errorf("invalid seq number want %d, got %d", b1.seq, b2.seq)
|
||||
}
|
||||
if b1.len() != b2.len() {
|
||||
t.Fatalf("invalid record length want %d, got %d", b1.len(), b2.len())
|
||||
}
|
||||
p1, p2 := new(testBatch), new(testBatch)
|
||||
err := b1.replay(p1)
|
||||
if err != nil {
|
||||
t.Fatal("error when replaying batch 1: ", err)
|
||||
}
|
||||
err = b2.replay(p2)
|
||||
if err != nil {
|
||||
t.Fatal("error when replaying batch 2: ", err)
|
||||
}
|
||||
for i := range p1.rec {
|
||||
r1, r2 := p1.rec[i], p2.rec[i]
|
||||
if r1.t != r2.t {
|
||||
t.Errorf("invalid type on record '%d' want %d, got %d", i, r1.t, r2.t)
|
||||
}
|
||||
if !bytes.Equal(r1.key, r2.key) {
|
||||
t.Errorf("invalid key on record '%d' want %s, got %s", i, string(r1.key), string(r2.key))
|
||||
}
|
||||
if r1.t == tVal {
|
||||
if !bytes.Equal(r1.value, r2.value) {
|
||||
t.Errorf("invalid value on record '%d' want %s, got %s", i, string(r1.value), string(r2.value))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBatch_EncodeDecode(t *testing.T) {
|
||||
b1 := new(Batch)
|
||||
b1.seq = 10009
|
||||
b1.Put([]byte("key1"), []byte("value1"))
|
||||
b1.Put([]byte("key2"), []byte("value2"))
|
||||
b1.Delete([]byte("key1"))
|
||||
b1.Put([]byte("k"), []byte(""))
|
||||
b1.Put([]byte("zzzzzzzzzzz"), []byte("zzzzzzzzzzzzzzzzzzzzzzzz"))
|
||||
b1.Delete([]byte("key10000"))
|
||||
b1.Delete([]byte("k"))
|
||||
buf := b1.encode()
|
||||
b2 := new(Batch)
|
||||
err := b2.decode(buf)
|
||||
if err != nil {
|
||||
t.Error("error when decoding batch: ", err)
|
||||
}
|
||||
compareBatch(t, b1, b2)
|
||||
}
|
||||
|
||||
func TestBatch_Append(t *testing.T) {
|
||||
b1 := new(Batch)
|
||||
b1.seq = 10009
|
||||
b1.Put([]byte("key1"), []byte("value1"))
|
||||
b1.Put([]byte("key2"), []byte("value2"))
|
||||
b1.Delete([]byte("key1"))
|
||||
b1.Put([]byte("foo"), []byte("foovalue"))
|
||||
b1.Put([]byte("bar"), []byte("barvalue"))
|
||||
b2a := new(Batch)
|
||||
b2a.seq = 10009
|
||||
b2a.Put([]byte("key1"), []byte("value1"))
|
||||
b2a.Put([]byte("key2"), []byte("value2"))
|
||||
b2a.Delete([]byte("key1"))
|
||||
b2b := new(Batch)
|
||||
b2b.Put([]byte("foo"), []byte("foovalue"))
|
||||
b2b.Put([]byte("bar"), []byte("barvalue"))
|
||||
b2a.append(b2b)
|
||||
compareBatch(t, b1, b2a)
|
||||
}
|
||||
|
||||
func TestBatch_Size(t *testing.T) {
|
||||
b := new(Batch)
|
||||
for i := 0; i < 2; i++ {
|
||||
b.Put([]byte("key1"), []byte("value1"))
|
||||
b.Put([]byte("key2"), []byte("value2"))
|
||||
b.Delete([]byte("key1"))
|
||||
b.Put([]byte("foo"), []byte("foovalue"))
|
||||
b.Put([]byte("bar"), []byte("barvalue"))
|
||||
mem := memdb.New(&iComparer{comparer.DefaultComparer}, 0)
|
||||
b.memReplay(mem)
|
||||
if b.size() != mem.Size() {
|
||||
t.Errorf("invalid batch size calculation, want=%d got=%d", mem.Size(), b.size())
|
||||
}
|
||||
b.Reset()
|
||||
}
|
||||
}
|
||||
-461
@@ -1,461 +0,0 @@
|
||||
// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
|
||||
// All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package leveldb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb/iterator"
|
||||
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||
"github.com/syndtr/goleveldb/leveldb/storage"
|
||||
)
|
||||
|
||||
func randomString(r *rand.Rand, n int) []byte {
|
||||
b := new(bytes.Buffer)
|
||||
for i := 0; i < n; i++ {
|
||||
b.WriteByte(' ' + byte(r.Intn(95)))
|
||||
}
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
func compressibleStr(r *rand.Rand, frac float32, n int) []byte {
|
||||
nn := int(float32(n) * frac)
|
||||
rb := randomString(r, nn)
|
||||
b := make([]byte, 0, n+nn)
|
||||
for len(b) < n {
|
||||
b = append(b, rb...)
|
||||
}
|
||||
return b[:n]
|
||||
}
|
||||
|
||||
type valueGen struct {
|
||||
src []byte
|
||||
pos int
|
||||
}
|
||||
|
||||
func newValueGen(frac float32) *valueGen {
|
||||
v := new(valueGen)
|
||||
r := rand.New(rand.NewSource(301))
|
||||
v.src = make([]byte, 0, 1048576+100)
|
||||
for len(v.src) < 1048576 {
|
||||
v.src = append(v.src, compressibleStr(r, frac, 100)...)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *valueGen) get(n int) []byte {
|
||||
if v.pos+n > len(v.src) {
|
||||
v.pos = 0
|
||||
}
|
||||
v.pos += n
|
||||
return v.src[v.pos-n : v.pos]
|
||||
}
|
||||
|
||||
var benchDB = filepath.Join(os.TempDir(), fmt.Sprintf("goleveldbbench-%d", os.Getuid()))
|
||||
|
||||
type dbBench struct {
|
||||
b *testing.B
|
||||
stor storage.Storage
|
||||
db *DB
|
||||
|
||||
o *opt.Options
|
||||
ro *opt.ReadOptions
|
||||
wo *opt.WriteOptions
|
||||
|
||||
keys, values [][]byte
|
||||
}
|
||||
|
||||
func openDBBench(b *testing.B, noCompress bool) *dbBench {
|
||||
_, err := os.Stat(benchDB)
|
||||
if err == nil {
|
||||
err = os.RemoveAll(benchDB)
|
||||
if err != nil {
|
||||
b.Fatal("cannot remove old db: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
p := &dbBench{
|
||||
b: b,
|
||||
o: &opt.Options{},
|
||||
ro: &opt.ReadOptions{},
|
||||
wo: &opt.WriteOptions{},
|
||||
}
|
||||
p.stor, err = storage.OpenFile(benchDB)
|
||||
if err != nil {
|
||||
b.Fatal("cannot open stor: ", err)
|
||||
}
|
||||
if noCompress {
|
||||
p.o.Compression = opt.NoCompression
|
||||
}
|
||||
|
||||
p.db, err = Open(p.stor, p.o)
|
||||
if err != nil {
|
||||
b.Fatal("cannot open db: ", err)
|
||||
}
|
||||
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *dbBench) reopen() {
|
||||
p.db.Close()
|
||||
var err error
|
||||
p.db, err = Open(p.stor, p.o)
|
||||
if err != nil {
|
||||
p.b.Fatal("Reopen: got error: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *dbBench) populate(n int) {
|
||||
p.keys, p.values = make([][]byte, n), make([][]byte, n)
|
||||
v := newValueGen(0.5)
|
||||
for i := range p.keys {
|
||||
p.keys[i], p.values[i] = []byte(fmt.Sprintf("%016d", i)), v.get(100)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *dbBench) randomize() {
|
||||
m := len(p.keys)
|
||||
times := m * 2
|
||||
r1, r2 := rand.New(rand.NewSource(0xdeadbeef)), rand.New(rand.NewSource(0xbeefface))
|
||||
for n := 0; n < times; n++ {
|
||||
i, j := r1.Int()%m, r2.Int()%m
|
||||
if i == j {
|
||||
continue
|
||||
}
|
||||
p.keys[i], p.keys[j] = p.keys[j], p.keys[i]
|
||||
p.values[i], p.values[j] = p.values[j], p.values[i]
|
||||
}
|
||||
}
|
||||
|
||||
func (p *dbBench) writes(perBatch int) {
|
||||
b := p.b
|
||||
db := p.db
|
||||
|
||||
n := len(p.keys)
|
||||
m := n / perBatch
|
||||
if n%perBatch > 0 {
|
||||
m++
|
||||
}
|
||||
batches := make([]Batch, m)
|
||||
j := 0
|
||||
for i := range batches {
|
||||
first := true
|
||||
for ; j < n && ((j+1)%perBatch != 0 || first); j++ {
|
||||
first = false
|
||||
batches[i].Put(p.keys[j], p.values[j])
|
||||
}
|
||||
}
|
||||
runtime.GC()
|
||||
|
||||
b.ResetTimer()
|
||||
b.StartTimer()
|
||||
for i := range batches {
|
||||
err := db.Write(&(batches[i]), p.wo)
|
||||
if err != nil {
|
||||
b.Fatal("write failed: ", err)
|
||||
}
|
||||
}
|
||||
b.StopTimer()
|
||||
b.SetBytes(116)
|
||||
}
|
||||
|
||||
func (p *dbBench) drop() {
|
||||
p.keys, p.values = nil, nil
|
||||
runtime.GC()
|
||||
}
|
||||
|
||||
func (p *dbBench) puts() {
|
||||
b := p.b
|
||||
db := p.db
|
||||
|
||||
b.ResetTimer()
|
||||
b.StartTimer()
|
||||
for i := range p.keys {
|
||||
err := db.Put(p.keys[i], p.values[i], p.wo)
|
||||
if err != nil {
|
||||
b.Fatal("put failed: ", err)
|
||||
}
|
||||
}
|
||||
b.StopTimer()
|
||||
b.SetBytes(116)
|
||||
}
|
||||
|
||||
func (p *dbBench) fill() {
|
||||
b := p.b
|
||||
db := p.db
|
||||
|
||||
perBatch := 10000
|
||||
batch := new(Batch)
|
||||
for i, n := 0, len(p.keys); i < n; {
|
||||
first := true
|
||||
for ; i < n && ((i+1)%perBatch != 0 || first); i++ {
|
||||
first = false
|
||||
batch.Put(p.keys[i], p.values[i])
|
||||
}
|
||||
err := db.Write(batch, p.wo)
|
||||
if err != nil {
|
||||
b.Fatal("write failed: ", err)
|
||||
}
|
||||
batch.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *dbBench) gets() {
|
||||
b := p.b
|
||||
db := p.db
|
||||
|
||||
b.ResetTimer()
|
||||
for i := range p.keys {
|
||||
_, err := db.Get(p.keys[i], p.ro)
|
||||
if err != nil {
|
||||
b.Error("got error: ", err)
|
||||
}
|
||||
}
|
||||
b.StopTimer()
|
||||
}
|
||||
|
||||
func (p *dbBench) seeks() {
|
||||
b := p.b
|
||||
|
||||
iter := p.newIter()
|
||||
defer iter.Release()
|
||||
b.ResetTimer()
|
||||
for i := range p.keys {
|
||||
if !iter.Seek(p.keys[i]) {
|
||||
b.Error("value not found for: ", string(p.keys[i]))
|
||||
}
|
||||
}
|
||||
b.StopTimer()
|
||||
}
|
||||
|
||||
func (p *dbBench) newIter() iterator.Iterator {
|
||||
iter := p.db.NewIterator(nil, p.ro)
|
||||
err := iter.Error()
|
||||
if err != nil {
|
||||
p.b.Fatal("cannot create iterator: ", err)
|
||||
}
|
||||
return iter
|
||||
}
|
||||
|
||||
func (p *dbBench) close() {
|
||||
p.db.Close()
|
||||
p.stor.Close()
|
||||
os.RemoveAll(benchDB)
|
||||
p.db = nil
|
||||
p.keys = nil
|
||||
p.values = nil
|
||||
runtime.GC()
|
||||
runtime.GOMAXPROCS(1)
|
||||
}
|
||||
|
||||
func BenchmarkDBWrite(b *testing.B) {
|
||||
p := openDBBench(b, false)
|
||||
p.populate(b.N)
|
||||
p.writes(1)
|
||||
p.close()
|
||||
}
|
||||
|
||||
func BenchmarkDBWriteBatch(b *testing.B) {
|
||||
p := openDBBench(b, false)
|
||||
p.populate(b.N)
|
||||
p.writes(1000)
|
||||
p.close()
|
||||
}
|
||||
|
||||
func BenchmarkDBWriteUncompressed(b *testing.B) {
|
||||
p := openDBBench(b, true)
|
||||
p.populate(b.N)
|
||||
p.writes(1)
|
||||
p.close()
|
||||
}
|
||||
|
||||
func BenchmarkDBWriteBatchUncompressed(b *testing.B) {
|
||||
p := openDBBench(b, true)
|
||||
p.populate(b.N)
|
||||
p.writes(1000)
|
||||
p.close()
|
||||
}
|
||||
|
||||
func BenchmarkDBWriteRandom(b *testing.B) {
|
||||
p := openDBBench(b, false)
|
||||
p.populate(b.N)
|
||||
p.randomize()
|
||||
p.writes(1)
|
||||
p.close()
|
||||
}
|
||||
|
||||
func BenchmarkDBWriteRandomSync(b *testing.B) {
|
||||
p := openDBBench(b, false)
|
||||
p.wo.Sync = true
|
||||
p.populate(b.N)
|
||||
p.writes(1)
|
||||
p.close()
|
||||
}
|
||||
|
||||
func BenchmarkDBOverwrite(b *testing.B) {
|
||||
p := openDBBench(b, false)
|
||||
p.populate(b.N)
|
||||
p.writes(1)
|
||||
p.writes(1)
|
||||
p.close()
|
||||
}
|
||||
|
||||
func BenchmarkDBOverwriteRandom(b *testing.B) {
|
||||
p := openDBBench(b, false)
|
||||
p.populate(b.N)
|
||||
p.writes(1)
|
||||
p.randomize()
|
||||
p.writes(1)
|
||||
p.close()
|
||||
}
|
||||
|
||||
func BenchmarkDBPut(b *testing.B) {
|
||||
p := openDBBench(b, false)
|
||||
p.populate(b.N)
|
||||
p.puts()
|
||||
p.close()
|
||||
}
|
||||
|
||||
func BenchmarkDBRead(b *testing.B) {
|
||||
p := openDBBench(b, false)
|
||||
p.populate(b.N)
|
||||
p.fill()
|
||||
p.drop()
|
||||
|
||||
iter := p.newIter()
|
||||
b.ResetTimer()
|
||||
for iter.Next() {
|
||||
}
|
||||
iter.Release()
|
||||
b.StopTimer()
|
||||
b.SetBytes(116)
|
||||
p.close()
|
||||
}
|
||||
|
||||
func BenchmarkDBReadGC(b *testing.B) {
|
||||
p := openDBBench(b, false)
|
||||
p.populate(b.N)
|
||||
p.fill()
|
||||
|
||||
iter := p.newIter()
|
||||
b.ResetTimer()
|
||||
for iter.Next() {
|
||||
}
|
||||
iter.Release()
|
||||
b.StopTimer()
|
||||
b.SetBytes(116)
|
||||
p.close()
|
||||
}
|
||||
|
||||
func BenchmarkDBReadUncompressed(b *testing.B) {
|
||||
p := openDBBench(b, true)
|
||||
p.populate(b.N)
|
||||
p.fill()
|
||||
p.drop()
|
||||
|
||||
iter := p.newIter()
|
||||
b.ResetTimer()
|
||||
for iter.Next() {
|
||||
}
|
||||
iter.Release()
|
||||
b.StopTimer()
|
||||
b.SetBytes(116)
|
||||
p.close()
|
||||
}
|
||||
|
||||
func BenchmarkDBReadTable(b *testing.B) {
|
||||
p := openDBBench(b, false)
|
||||
p.populate(b.N)
|
||||
p.fill()
|
||||
p.reopen()
|
||||
p.drop()
|
||||
|
||||
iter := p.newIter()
|
||||
b.ResetTimer()
|
||||
for iter.Next() {
|
||||
}
|
||||
iter.Release()
|
||||
b.StopTimer()
|
||||
b.SetBytes(116)
|
||||
p.close()
|
||||
}
|
||||
|
||||
func BenchmarkDBReadReverse(b *testing.B) {
|
||||
p := openDBBench(b, false)
|
||||
p.populate(b.N)
|
||||
p.fill()
|
||||
p.drop()
|
||||
|
||||
iter := p.newIter()
|
||||
b.ResetTimer()
|
||||
iter.Last()
|
||||
for iter.Prev() {
|
||||
}
|
||||
iter.Release()
|
||||
b.StopTimer()
|
||||
b.SetBytes(116)
|
||||
p.close()
|
||||
}
|
||||
|
||||
func BenchmarkDBReadReverseTable(b *testing.B) {
|
||||
p := openDBBench(b, false)
|
||||
p.populate(b.N)
|
||||
p.fill()
|
||||
p.reopen()
|
||||
p.drop()
|
||||
|
||||
iter := p.newIter()
|
||||
b.ResetTimer()
|
||||
iter.Last()
|
||||
for iter.Prev() {
|
||||
}
|
||||
iter.Release()
|
||||
b.StopTimer()
|
||||
b.SetBytes(116)
|
||||
p.close()
|
||||
}
|
||||
|
||||
func BenchmarkDBSeek(b *testing.B) {
|
||||
p := openDBBench(b, false)
|
||||
p.populate(b.N)
|
||||
p.fill()
|
||||
p.seeks()
|
||||
p.close()
|
||||
}
|
||||
|
||||
func BenchmarkDBSeekRandom(b *testing.B) {
|
||||
p := openDBBench(b, false)
|
||||
p.populate(b.N)
|
||||
p.fill()
|
||||
p.randomize()
|
||||
p.seeks()
|
||||
p.close()
|
||||
}
|
||||
|
||||
func BenchmarkDBGet(b *testing.B) {
|
||||
p := openDBBench(b, false)
|
||||
p.populate(b.N)
|
||||
p.fill()
|
||||
p.gets()
|
||||
p.close()
|
||||
}
|
||||
|
||||
func BenchmarkDBGetRandom(b *testing.B) {
|
||||
p := openDBBench(b, false)
|
||||
p.populate(b.N)
|
||||
p.fill()
|
||||
p.randomize()
|
||||
p.gets()
|
||||
p.close()
|
||||
}
|
||||
-125
@@ -1,125 +0,0 @@
|
||||
// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
|
||||
// All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Package cache provides interface and implementation of a cache algorithms.
|
||||
package cache
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// SetFunc used by Namespace.Get method to create a cache object. SetFunc
|
||||
// may return ok false, in that case the cache object will not be created.
|
||||
type SetFunc func() (ok bool, value interface{}, charge int, fin SetFin)
|
||||
|
||||
// SetFin will be called when corresponding cache object are released.
|
||||
type SetFin func()
|
||||
|
||||
// DelFin will be called when corresponding cache object are released.
|
||||
// DelFin will be called after SetFin. The exist is true if the corresponding
|
||||
// cache object is actually exist in the cache tree.
|
||||
type DelFin func(exist bool)
|
||||
|
||||
// PurgeFin will be called when corresponding cache object are released.
|
||||
// PurgeFin will be called after SetFin. If PurgeFin present DelFin will
|
||||
// not be executed but passed to the PurgeFin, it is up to the caller
|
||||
// to call it or not.
|
||||
type PurgeFin func(ns, key uint64, delfin DelFin)
|
||||
|
||||
// Cache is a cache tree. A cache instance must be goroutine-safe.
|
||||
type Cache interface {
|
||||
// SetCapacity sets cache capacity.
|
||||
SetCapacity(capacity int)
|
||||
|
||||
// GetNamespace gets or creates a cache namespace for the given id.
|
||||
GetNamespace(id uint64) Namespace
|
||||
|
||||
// Purge purges all cache namespaces, read Namespace.Purge method documentation.
|
||||
Purge(fin PurgeFin)
|
||||
|
||||
// Zap zaps all cache namespaces, read Namespace.Zap method documentation.
|
||||
Zap(closed bool)
|
||||
}
|
||||
|
||||
// Namespace is a cache namespace. A namespace instance must be goroutine-safe.
|
||||
type Namespace interface {
|
||||
// Get gets cache object for the given key. The given SetFunc (if not nil) will
|
||||
// be called if the given key does not exist.
|
||||
// If the given key does not exist, SetFunc is nil or SetFunc return ok false, Get
|
||||
// will return ok false.
|
||||
Get(key uint64, setf SetFunc) (obj Object, ok bool)
|
||||
|
||||
// Get deletes cache object for the given key. If exist the cache object will
|
||||
// be deleted later when all of its handles have been released (i.e. no one use
|
||||
// it anymore) and the given DelFin (if not nil) will finally be executed. If
|
||||
// such cache object does not exist the given DelFin will be executed anyway.
|
||||
//
|
||||
// Delete returns true if such cache object exist.
|
||||
Delete(key uint64, fin DelFin) bool
|
||||
|
||||
// Purge deletes all cache objects, read Delete method documentation.
|
||||
Purge(fin PurgeFin)
|
||||
|
||||
// Zap detaches the namespace from the cache tree and delete all its cache
|
||||
// objects. The cache objects deletion and finalizers execution are happen
|
||||
// immediately, even if its existing handles haven't yet been released.
|
||||
// A zapped namespace can't never be filled again.
|
||||
// If closed is false then the Get function will always call the given SetFunc
|
||||
// if it is not nil, but resultant of the SetFunc will not be cached.
|
||||
Zap(closed bool)
|
||||
}
|
||||
|
||||
// Object is a cache object.
|
||||
type Object interface {
|
||||
// Release releases the cache object. Other methods should not be called
|
||||
// after the cache object has been released.
|
||||
Release()
|
||||
|
||||
// Value returns value of the cache object.
|
||||
Value() interface{}
|
||||
}
|
||||
|
||||
// Namespace state.
|
||||
type nsState int
|
||||
|
||||
const (
|
||||
nsEffective nsState = iota
|
||||
nsZapped
|
||||
nsClosed
|
||||
)
|
||||
|
||||
// Node state.
|
||||
type nodeState int
|
||||
|
||||
const (
|
||||
nodeEffective nodeState = iota
|
||||
nodeEvicted
|
||||
nodeRemoved
|
||||
)
|
||||
|
||||
// Fake object.
|
||||
type fakeObject struct {
|
||||
value interface{}
|
||||
fin func()
|
||||
once uint32
|
||||
}
|
||||
|
||||
func (o *fakeObject) Value() interface{} {
|
||||
if atomic.LoadUint32(&o.once) == 0 {
|
||||
return o.value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *fakeObject) Release() {
|
||||
if !atomic.CompareAndSwapUint32(&o.once, 0, 1) {
|
||||
return
|
||||
}
|
||||
if o.fin != nil {
|
||||
o.fin()
|
||||
o.fin = nil
|
||||
}
|
||||
}
|
||||
-236
@@ -1,236 +0,0 @@
|
||||
// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
|
||||
// All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func set(ns Namespace, key uint64, value interface{}, charge int, fin func()) Object {
|
||||
obj, _ := ns.Get(key, func() (bool, interface{}, int, SetFin) {
|
||||
return true, value, charge, fin
|
||||
})
|
||||
return obj
|
||||
}
|
||||
|
||||
func TestCache_HitMiss(t *testing.T) {
|
||||
cases := []struct {
|
||||
key uint64
|
||||
value string
|
||||
}{
|
||||
{1, "vvvvvvvvv"},
|
||||
{100, "v1"},
|
||||
{0, "v2"},
|
||||
{12346, "v3"},
|
||||
{777, "v4"},
|
||||
{999, "v5"},
|
||||
{7654, "v6"},
|
||||
{2, "v7"},
|
||||
{3, "v8"},
|
||||
{9, "v9"},
|
||||
}
|
||||
|
||||
setfin := 0
|
||||
c := NewLRUCache(1000)
|
||||
ns := c.GetNamespace(0)
|
||||
for i, x := range cases {
|
||||
set(ns, x.key, x.value, len(x.value), func() {
|
||||
setfin++
|
||||
}).Release()
|
||||
for j, y := range cases {
|
||||
r, ok := ns.Get(y.key, nil)
|
||||
if j <= i {
|
||||
// should hit
|
||||
if !ok {
|
||||
t.Errorf("case '%d' iteration '%d' is miss", i, j)
|
||||
} else if r.Value().(string) != y.value {
|
||||
t.Errorf("case '%d' iteration '%d' has invalid value got '%s', want '%s'", i, j, r.Value().(string), y.value)
|
||||
}
|
||||
} else {
|
||||
// should miss
|
||||
if ok {
|
||||
t.Errorf("case '%d' iteration '%d' is hit , value '%s'", i, j, r.Value().(string))
|
||||
}
|
||||
}
|
||||
if ok {
|
||||
r.Release()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i, x := range cases {
|
||||
finalizerOk := false
|
||||
ns.Delete(x.key, func(exist bool) {
|
||||
finalizerOk = true
|
||||
})
|
||||
|
||||
if !finalizerOk {
|
||||
t.Errorf("case %d delete finalizer not executed", i)
|
||||
}
|
||||
|
||||
for j, y := range cases {
|
||||
r, ok := ns.Get(y.key, nil)
|
||||
if j > i {
|
||||
// should hit
|
||||
if !ok {
|
||||
t.Errorf("case '%d' iteration '%d' is miss", i, j)
|
||||
} else if r.Value().(string) != y.value {
|
||||
t.Errorf("case '%d' iteration '%d' has invalid value got '%s', want '%s'", i, j, r.Value().(string), y.value)
|
||||
}
|
||||
} else {
|
||||
// should miss
|
||||
if ok {
|
||||
t.Errorf("case '%d' iteration '%d' is hit, value '%s'", i, j, r.Value().(string))
|
||||
}
|
||||
}
|
||||
if ok {
|
||||
r.Release()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if setfin != len(cases) {
|
||||
t.Errorf("some set finalizer may not be executed, want=%d got=%d", len(cases), setfin)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLRUCache_Eviction(t *testing.T) {
|
||||
c := NewLRUCache(12)
|
||||
ns := c.GetNamespace(0)
|
||||
o1 := set(ns, 1, 1, 1, nil)
|
||||
set(ns, 2, 2, 1, nil).Release()
|
||||
set(ns, 3, 3, 1, nil).Release()
|
||||
set(ns, 4, 4, 1, nil).Release()
|
||||
set(ns, 5, 5, 1, nil).Release()
|
||||
if r, ok := ns.Get(2, nil); ok { // 1,3,4,5,2
|
||||
r.Release()
|
||||
}
|
||||
set(ns, 9, 9, 10, nil).Release() // 5,2,9
|
||||
|
||||
for _, x := range []uint64{9, 2, 5, 1} {
|
||||
r, ok := ns.Get(x, nil)
|
||||
if !ok {
|
||||
t.Errorf("miss for key '%d'", x)
|
||||
} else {
|
||||
if r.Value().(int) != int(x) {
|
||||
t.Errorf("invalid value for key '%d' want '%d', got '%d'", x, x, r.Value().(int))
|
||||
}
|
||||
r.Release()
|
||||
}
|
||||
}
|
||||
o1.Release()
|
||||
for _, x := range []uint64{1, 2, 5} {
|
||||
r, ok := ns.Get(x, nil)
|
||||
if !ok {
|
||||
t.Errorf("miss for key '%d'", x)
|
||||
} else {
|
||||
if r.Value().(int) != int(x) {
|
||||
t.Errorf("invalid value for key '%d' want '%d', got '%d'", x, x, r.Value().(int))
|
||||
}
|
||||
r.Release()
|
||||
}
|
||||
}
|
||||
for _, x := range []uint64{3, 4, 9} {
|
||||
r, ok := ns.Get(x, nil)
|
||||
if ok {
|
||||
t.Errorf("hit for key '%d'", x)
|
||||
if r.Value().(int) != int(x) {
|
||||
t.Errorf("invalid value for key '%d' want '%d', got '%d'", x, x, r.Value().(int))
|
||||
}
|
||||
r.Release()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLRUCache_SetGet(t *testing.T) {
|
||||
c := NewLRUCache(13)
|
||||
ns := c.GetNamespace(0)
|
||||
for i := 0; i < 200; i++ {
|
||||
n := uint64(rand.Intn(99999) % 20)
|
||||
set(ns, n, n, 1, nil).Release()
|
||||
if p, ok := ns.Get(n, nil); ok {
|
||||
if p.Value() == nil {
|
||||
t.Errorf("key '%d' contains nil value", n)
|
||||
} else {
|
||||
got := p.Value().(uint64)
|
||||
if got != n {
|
||||
t.Errorf("invalid value for key '%d' want '%d', got '%d'", n, n, got)
|
||||
}
|
||||
}
|
||||
p.Release()
|
||||
} else {
|
||||
t.Errorf("key '%d' doesn't exist", n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLRUCache_Purge(t *testing.T) {
|
||||
c := NewLRUCache(3)
|
||||
ns1 := c.GetNamespace(0)
|
||||
o1 := set(ns1, 1, 1, 1, nil)
|
||||
o2 := set(ns1, 2, 2, 1, nil)
|
||||
ns1.Purge(nil)
|
||||
set(ns1, 3, 3, 1, nil).Release()
|
||||
for _, x := range []uint64{1, 2, 3} {
|
||||
r, ok := ns1.Get(x, nil)
|
||||
if !ok {
|
||||
t.Errorf("miss for key '%d'", x)
|
||||
} else {
|
||||
if r.Value().(int) != int(x) {
|
||||
t.Errorf("invalid value for key '%d' want '%d', got '%d'", x, x, r.Value().(int))
|
||||
}
|
||||
r.Release()
|
||||
}
|
||||
}
|
||||
o1.Release()
|
||||
o2.Release()
|
||||
for _, x := range []uint64{1, 2} {
|
||||
r, ok := ns1.Get(x, nil)
|
||||
if ok {
|
||||
t.Errorf("hit for key '%d'", x)
|
||||
if r.Value().(int) != int(x) {
|
||||
t.Errorf("invalid value for key '%d' want '%d', got '%d'", x, x, r.Value().(int))
|
||||
}
|
||||
r.Release()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLRUCache_SetRelease(b *testing.B) {
|
||||
capacity := b.N / 100
|
||||
if capacity <= 0 {
|
||||
capacity = 10
|
||||
}
|
||||
c := NewLRUCache(capacity)
|
||||
ns := c.GetNamespace(0)
|
||||
b.ResetTimer()
|
||||
for i := uint64(0); i < uint64(b.N); i++ {
|
||||
set(ns, i, nil, 1, nil).Release()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLRUCache_SetReleaseTwice(b *testing.B) {
|
||||
capacity := b.N / 100
|
||||
if capacity <= 0 {
|
||||
capacity = 10
|
||||
}
|
||||
c := NewLRUCache(capacity)
|
||||
ns := c.GetNamespace(0)
|
||||
b.ResetTimer()
|
||||
|
||||
na := b.N / 2
|
||||
nb := b.N - na
|
||||
|
||||
for i := uint64(0); i < uint64(na); i++ {
|
||||
set(ns, i, nil, 1, nil).Release()
|
||||
}
|
||||
|
||||
for i := uint64(0); i < uint64(nb); i++ {
|
||||
set(ns, i, nil, 1, nil).Release()
|
||||
}
|
||||
}
|
||||
-246
@@ -1,246 +0,0 @@
|
||||
// Copyright (c) 2013, Suryandaru Triandana <syndtr@gmail.com>
|
||||
// All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type emptyCache struct {
|
||||
sync.Mutex
|
||||
table map[uint64]*emptyNS
|
||||
}
|
||||
|
||||
// NewEmptyCache creates a new initialized empty cache.
|
||||
func NewEmptyCache() Cache {
|
||||
return &emptyCache{
|
||||
table: make(map[uint64]*emptyNS),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *emptyCache) GetNamespace(id uint64) Namespace {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
if ns, ok := c.table[id]; ok {
|
||||
return ns
|
||||
}
|
||||
|
||||
ns := &emptyNS{
|
||||
cache: c,
|
||||
id: id,
|
||||
table: make(map[uint64]*emptyNode),
|
||||
}
|
||||
c.table[id] = ns
|
||||
return ns
|
||||
}
|
||||
|
||||
func (c *emptyCache) Purge(fin PurgeFin) {
|
||||
c.Lock()
|
||||
for _, ns := range c.table {
|
||||
ns.purgeNB(fin)
|
||||
}
|
||||
c.Unlock()
|
||||
}
|
||||
|
||||
func (c *emptyCache) Zap(closed bool) {
|
||||
c.Lock()
|
||||
for _, ns := range c.table {
|
||||
ns.zapNB(closed)
|
||||
}
|
||||
c.table = make(map[uint64]*emptyNS)
|
||||
c.Unlock()
|
||||
}
|
||||
|
||||
func (*emptyCache) SetCapacity(capacity int) {}
|
||||
|
||||
type emptyNS struct {
|
||||
cache *emptyCache
|
||||
id uint64
|
||||
table map[uint64]*emptyNode
|
||||
state nsState
|
||||
}
|
||||
|
||||
func (ns *emptyNS) Get(key uint64, setf SetFunc) (o Object, ok bool) {
|
||||
ns.cache.Lock()
|
||||
|
||||
switch ns.state {
|
||||
case nsZapped:
|
||||
ns.cache.Unlock()
|
||||
if setf == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var value interface{}
|
||||
var fin func()
|
||||
ok, value, _, fin = setf()
|
||||
if ok {
|
||||
o = &fakeObject{
|
||||
value: value,
|
||||
fin: fin,
|
||||
}
|
||||
}
|
||||
return
|
||||
case nsClosed:
|
||||
ns.cache.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
n, ok := ns.table[key]
|
||||
if ok {
|
||||
n.ref++
|
||||
} else {
|
||||
if setf == nil {
|
||||
ns.cache.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
var value interface{}
|
||||
var fin func()
|
||||
ok, value, _, fin = setf()
|
||||
if !ok {
|
||||
ns.cache.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
n = &emptyNode{
|
||||
ns: ns,
|
||||
key: key,
|
||||
value: value,
|
||||
setfin: fin,
|
||||
ref: 1,
|
||||
}
|
||||
ns.table[key] = n
|
||||
}
|
||||
|
||||
ns.cache.Unlock()
|
||||
o = &emptyObject{node: n}
|
||||
return
|
||||
}
|
||||
|
||||
func (ns *emptyNS) Delete(key uint64, fin DelFin) bool {
|
||||
ns.cache.Lock()
|
||||
|
||||
if ns.state != nsEffective {
|
||||
ns.cache.Unlock()
|
||||
if fin != nil {
|
||||
fin(false)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
n, ok := ns.table[key]
|
||||
if !ok {
|
||||
ns.cache.Unlock()
|
||||
if fin != nil {
|
||||
fin(false)
|
||||
}
|
||||
return false
|
||||
}
|
||||
n.delfin = fin
|
||||
ns.cache.Unlock()
|
||||
return true
|
||||
}
|
||||
|
||||
func (ns *emptyNS) purgeNB(fin PurgeFin) {
|
||||
if ns.state != nsEffective {
|
||||
return
|
||||
}
|
||||
for _, n := range ns.table {
|
||||
n.purgefin = fin
|
||||
}
|
||||
}
|
||||
|
||||
func (ns *emptyNS) Purge(fin PurgeFin) {
|
||||
ns.cache.Lock()
|
||||
ns.purgeNB(fin)
|
||||
ns.cache.Unlock()
|
||||
}
|
||||
|
||||
func (ns *emptyNS) zapNB(closed bool) {
|
||||
if ns.state != nsEffective {
|
||||
return
|
||||
}
|
||||
for _, n := range ns.table {
|
||||
n.execFin()
|
||||
}
|
||||
if closed {
|
||||
ns.state = nsClosed
|
||||
} else {
|
||||
ns.state = nsZapped
|
||||
}
|
||||
ns.table = nil
|
||||
}
|
||||
|
||||
func (ns *emptyNS) Zap(closed bool) {
|
||||
ns.cache.Lock()
|
||||
ns.zapNB(closed)
|
||||
delete(ns.cache.table, ns.id)
|
||||
ns.cache.Unlock()
|
||||
}
|
||||
|
||||
type emptyNode struct {
|
||||
ns *emptyNS
|
||||
key uint64
|
||||
value interface{}
|
||||
ref int
|
||||
setfin SetFin
|
||||
delfin DelFin
|
||||
purgefin PurgeFin
|
||||
}
|
||||
|
||||
func (n *emptyNode) execFin() {
|
||||
if n.setfin != nil {
|
||||
n.setfin()
|
||||
n.setfin = nil
|
||||
}
|
||||
if n.purgefin != nil {
|
||||
n.purgefin(n.ns.id, n.key, n.delfin)
|
||||
n.delfin = nil
|
||||
n.purgefin = nil
|
||||
} else if n.delfin != nil {
|
||||
n.delfin(true)
|
||||
n.delfin = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (n *emptyNode) evict() {
|
||||
n.ns.cache.Lock()
|
||||
n.ref--
|
||||
if n.ref == 0 {
|
||||
if n.ns.state == nsEffective {
|
||||
// Remove elem.
|
||||
delete(n.ns.table, n.key)
|
||||
// Execute finalizer.
|
||||
n.execFin()
|
||||
}
|
||||
} else if n.ref < 0 {
|
||||
panic("leveldb/cache: emptyNode: negative node reference")
|
||||
}
|
||||
n.ns.cache.Unlock()
|
||||
}
|
||||
|
||||
type emptyObject struct {
|
||||
node *emptyNode
|
||||
once uint32
|
||||
}
|
||||
|
||||
func (o *emptyObject) Value() interface{} {
|
||||
if atomic.LoadUint32(&o.once) == 0 {
|
||||
return o.node.value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *emptyObject) Release() {
|
||||
if !atomic.CompareAndSwapUint32(&o.once, 0, 1) {
|
||||
return
|
||||
}
|
||||
o.node.evict()
|
||||
o.node = nil
|
||||
}
|
||||
-354
@@ -1,354 +0,0 @@
|
||||
// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
|
||||
// All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// lruCache represent a LRU cache state.
|
||||
type lruCache struct {
|
||||
sync.Mutex
|
||||
|
||||
recent lruNode
|
||||
table map[uint64]*lruNs
|
||||
capacity int
|
||||
size int
|
||||
}
|
||||
|
||||
// NewLRUCache creates a new initialized LRU cache with the given capacity.
|
||||
func NewLRUCache(capacity int) Cache {
|
||||
c := &lruCache{
|
||||
table: make(map[uint64]*lruNs),
|
||||
capacity: capacity,
|
||||
}
|
||||
c.recent.rNext = &c.recent
|
||||
c.recent.rPrev = &c.recent
|
||||
return c
|
||||
}
|
||||
|
||||
// SetCapacity set cache capacity.
|
||||
func (c *lruCache) SetCapacity(capacity int) {
|
||||
c.Lock()
|
||||
c.capacity = capacity
|
||||
c.evict()
|
||||
c.Unlock()
|
||||
}
|
||||
|
||||
// GetNamespace return namespace object for given id.
|
||||
func (c *lruCache) GetNamespace(id uint64) Namespace {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
if p, ok := c.table[id]; ok {
|
||||
return p
|
||||
}
|
||||
|
||||
p := &lruNs{
|
||||
lru: c,
|
||||
id: id,
|
||||
table: make(map[uint64]*lruNode),
|
||||
}
|
||||
c.table[id] = p
|
||||
return p
|
||||
}
|
||||
|
||||
// Purge purge entire cache.
|
||||
func (c *lruCache) Purge(fin PurgeFin) {
|
||||
c.Lock()
|
||||
for _, ns := range c.table {
|
||||
ns.purgeNB(fin)
|
||||
}
|
||||
c.Unlock()
|
||||
}
|
||||
|
||||
func (c *lruCache) Zap(closed bool) {
|
||||
c.Lock()
|
||||
for _, ns := range c.table {
|
||||
ns.zapNB(closed)
|
||||
}
|
||||
c.table = make(map[uint64]*lruNs)
|
||||
c.Unlock()
|
||||
}
|
||||
|
||||
func (c *lruCache) evict() {
|
||||
top := &c.recent
|
||||
for n := c.recent.rPrev; c.size > c.capacity && n != top; {
|
||||
n.state = nodeEvicted
|
||||
n.rRemove()
|
||||
n.evictNB()
|
||||
c.size -= n.charge
|
||||
n = c.recent.rPrev
|
||||
}
|
||||
}
|
||||
|
||||
type lruNs struct {
|
||||
lru *lruCache
|
||||
id uint64
|
||||
table map[uint64]*lruNode
|
||||
state nsState
|
||||
}
|
||||
|
||||
func (ns *lruNs) Get(key uint64, setf SetFunc) (o Object, ok bool) {
|
||||
lru := ns.lru
|
||||
lru.Lock()
|
||||
|
||||
switch ns.state {
|
||||
case nsZapped:
|
||||
lru.Unlock()
|
||||
if setf == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var value interface{}
|
||||
var fin func()
|
||||
ok, value, _, fin = setf()
|
||||
if ok {
|
||||
o = &fakeObject{
|
||||
value: value,
|
||||
fin: fin,
|
||||
}
|
||||
}
|
||||
return
|
||||
case nsClosed:
|
||||
lru.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
n, ok := ns.table[key]
|
||||
if ok {
|
||||
switch n.state {
|
||||
case nodeEvicted:
|
||||
// Insert to recent list.
|
||||
n.state = nodeEffective
|
||||
n.ref++
|
||||
lru.size += n.charge
|
||||
lru.evict()
|
||||
fallthrough
|
||||
case nodeEffective:
|
||||
// Bump to front
|
||||
n.rRemove()
|
||||
n.rInsert(&lru.recent)
|
||||
}
|
||||
n.ref++
|
||||
} else {
|
||||
if setf == nil {
|
||||
lru.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
var value interface{}
|
||||
var charge int
|
||||
var fin func()
|
||||
ok, value, charge, fin = setf()
|
||||
if !ok {
|
||||
lru.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
n = &lruNode{
|
||||
ns: ns,
|
||||
key: key,
|
||||
value: value,
|
||||
charge: charge,
|
||||
setfin: fin,
|
||||
ref: 2,
|
||||
}
|
||||
ns.table[key] = n
|
||||
n.rInsert(&lru.recent)
|
||||
|
||||
lru.size += charge
|
||||
lru.evict()
|
||||
}
|
||||
|
||||
lru.Unlock()
|
||||
o = &lruObject{node: n}
|
||||
return
|
||||
}
|
||||
|
||||
func (ns *lruNs) Delete(key uint64, fin DelFin) bool {
|
||||
lru := ns.lru
|
||||
lru.Lock()
|
||||
|
||||
if ns.state != nsEffective {
|
||||
lru.Unlock()
|
||||
if fin != nil {
|
||||
fin(false)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
n, ok := ns.table[key]
|
||||
if !ok {
|
||||
lru.Unlock()
|
||||
if fin != nil {
|
||||
fin(false)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
n.delfin = fin
|
||||
switch n.state {
|
||||
case nodeRemoved:
|
||||
lru.Unlock()
|
||||
return false
|
||||
case nodeEffective:
|
||||
lru.size -= n.charge
|
||||
n.rRemove()
|
||||
n.evictNB()
|
||||
}
|
||||
n.state = nodeRemoved
|
||||
|
||||
lru.Unlock()
|
||||
return true
|
||||
}
|
||||
|
||||
func (ns *lruNs) purgeNB(fin PurgeFin) {
|
||||
lru := ns.lru
|
||||
if ns.state != nsEffective {
|
||||
return
|
||||
}
|
||||
|
||||
for _, n := range ns.table {
|
||||
n.purgefin = fin
|
||||
if n.state == nodeEffective {
|
||||
lru.size -= n.charge
|
||||
n.rRemove()
|
||||
n.evictNB()
|
||||
}
|
||||
n.state = nodeRemoved
|
||||
}
|
||||
}
|
||||
|
||||
func (ns *lruNs) Purge(fin PurgeFin) {
|
||||
ns.lru.Lock()
|
||||
ns.purgeNB(fin)
|
||||
ns.lru.Unlock()
|
||||
}
|
||||
|
||||
func (ns *lruNs) zapNB(closed bool) {
|
||||
lru := ns.lru
|
||||
if ns.state != nsEffective {
|
||||
return
|
||||
}
|
||||
|
||||
if closed {
|
||||
ns.state = nsClosed
|
||||
} else {
|
||||
ns.state = nsZapped
|
||||
}
|
||||
for _, n := range ns.table {
|
||||
if n.state == nodeEffective {
|
||||
lru.size -= n.charge
|
||||
n.rRemove()
|
||||
}
|
||||
n.state = nodeRemoved
|
||||
n.execFin()
|
||||
}
|
||||
ns.table = nil
|
||||
}
|
||||
|
||||
func (ns *lruNs) Zap(closed bool) {
|
||||
ns.lru.Lock()
|
||||
ns.zapNB(closed)
|
||||
delete(ns.lru.table, ns.id)
|
||||
ns.lru.Unlock()
|
||||
}
|
||||
|
||||
type lruNode struct {
|
||||
ns *lruNs
|
||||
|
||||
rNext, rPrev *lruNode
|
||||
|
||||
key uint64
|
||||
value interface{}
|
||||
charge int
|
||||
ref int
|
||||
state nodeState
|
||||
setfin SetFin
|
||||
delfin DelFin
|
||||
purgefin PurgeFin
|
||||
}
|
||||
|
||||
func (n *lruNode) rInsert(at *lruNode) {
|
||||
x := at.rNext
|
||||
at.rNext = n
|
||||
n.rPrev = at
|
||||
n.rNext = x
|
||||
x.rPrev = n
|
||||
}
|
||||
|
||||
func (n *lruNode) rRemove() bool {
|
||||
// only remove if not already removed
|
||||
if n.rPrev == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
n.rPrev.rNext = n.rNext
|
||||
n.rNext.rPrev = n.rPrev
|
||||
n.rPrev = nil
|
||||
n.rNext = nil
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (n *lruNode) execFin() {
|
||||
if n.setfin != nil {
|
||||
n.setfin()
|
||||
n.setfin = nil
|
||||
}
|
||||
if n.purgefin != nil {
|
||||
n.purgefin(n.ns.id, n.key, n.delfin)
|
||||
n.delfin = nil
|
||||
n.purgefin = nil
|
||||
} else if n.delfin != nil {
|
||||
n.delfin(true)
|
||||
n.delfin = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (n *lruNode) evictNB() {
|
||||
n.ref--
|
||||
if n.ref == 0 {
|
||||
if n.ns.state == nsEffective {
|
||||
// remove elem
|
||||
delete(n.ns.table, n.key)
|
||||
// execute finalizer
|
||||
n.execFin()
|
||||
}
|
||||
} else if n.ref < 0 {
|
||||
panic("leveldb/cache: lruCache: negative node reference")
|
||||
}
|
||||
}
|
||||
|
||||
func (n *lruNode) evict() {
|
||||
n.ns.lru.Lock()
|
||||
n.evictNB()
|
||||
n.ns.lru.Unlock()
|
||||
}
|
||||
|
||||
type lruObject struct {
|
||||
node *lruNode
|
||||
once uint32
|
||||
}
|
||||
|
||||
func (o *lruObject) Value() interface{} {
|
||||
if atomic.LoadUint32(&o.once) == 0 {
|
||||
return o.node.value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *lruObject) Release() {
|
||||
if !atomic.CompareAndSwapUint32(&o.once, 0, 1) {
|
||||
return
|
||||
}
|
||||
|
||||
o.node.evict()
|
||||
o.node = nil
|
||||
}
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
|
||||
// All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package leveldb
|
||||
|
||||
const (
|
||||
kNumLevels = 7
|
||||
|
||||
// Level-0 compaction is started when we hit this many files.
|
||||
kL0_CompactionTrigger float64 = 4
|
||||
|
||||
// Soft limit on number of level-0 files. We slow down writes at this point.
|
||||
kL0_SlowdownWritesTrigger = 8
|
||||
|
||||
// Maximum number of level-0 files. We stop writes at this point.
|
||||
kL0_StopWritesTrigger = 12
|
||||
|
||||
// Maximum level to which a new compacted memdb is pushed if it
|
||||
// does not create overlap. We try to push to level 2 to avoid the
|
||||
// relatively expensive level 0=>1 compactions and to avoid some
|
||||
// expensive manifest file operations. We do not push all the way to
|
||||
// the largest level since that can generate a lot of wasted disk
|
||||
// space if the same key space is being repeatedly overwritten.
|
||||
kMaxMemCompactLevel = 2
|
||||
|
||||
// Maximum size of a table.
|
||||
kMaxTableSize = 2 * 1048576
|
||||
|
||||
// Maximum bytes of overlaps in grandparent (i.e., level+2) before we
|
||||
// stop building a single file in a level->level+1 compaction.
|
||||
kMaxGrandParentOverlapBytes = 10 * kMaxTableSize
|
||||
|
||||
// Maximum number of bytes in all compacted files. We avoid expanding
|
||||
// the lower level file set of a compaction if it would make the
|
||||
// total compaction cover more than this many bytes.
|
||||
kExpCompactionMaxBytes = 25 * kMaxTableSize
|
||||
)
|
||||
-472
@@ -1,472 +0,0 @@
|
||||
// Copyright (c) 2013, 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 (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb/cache"
|
||||
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||
"github.com/syndtr/goleveldb/leveldb/storage"
|
||||
)
|
||||
|
||||
const ctValSize = 1000
|
||||
|
||||
type dbCorruptHarness struct {
|
||||
dbHarness
|
||||
}
|
||||
|
||||
func newDbCorruptHarnessWopt(t *testing.T, o *opt.Options) *dbCorruptHarness {
|
||||
h := new(dbCorruptHarness)
|
||||
h.init(t, o)
|
||||
return h
|
||||
}
|
||||
|
||||
func newDbCorruptHarness(t *testing.T) *dbCorruptHarness {
|
||||
return newDbCorruptHarnessWopt(t, &opt.Options{
|
||||
BlockCache: cache.NewLRUCache(100),
|
||||
Strict: opt.StrictJournalChecksum,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *dbCorruptHarness) recover() {
|
||||
p := &h.dbHarness
|
||||
t := p.t
|
||||
|
||||
var err error
|
||||
p.db, err = Recover(h.stor, h.o)
|
||||
if err != nil {
|
||||
t.Fatal("Repair: got error: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *dbCorruptHarness) build(n int) {
|
||||
p := &h.dbHarness
|
||||
t := p.t
|
||||
db := p.db
|
||||
|
||||
batch := new(Batch)
|
||||
for i := 0; i < n; i++ {
|
||||
batch.Reset()
|
||||
batch.Put(tkey(i), tval(i, ctValSize))
|
||||
err := db.Write(batch, p.wo)
|
||||
if err != nil {
|
||||
t.Fatal("write error: ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *dbCorruptHarness) buildShuffled(n int, rnd *rand.Rand) {
|
||||
p := &h.dbHarness
|
||||
t := p.t
|
||||
db := p.db
|
||||
|
||||
batch := new(Batch)
|
||||
for i := range rnd.Perm(n) {
|
||||
batch.Reset()
|
||||
batch.Put(tkey(i), tval(i, ctValSize))
|
||||
err := db.Write(batch, p.wo)
|
||||
if err != nil {
|
||||
t.Fatal("write error: ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *dbCorruptHarness) deleteRand(n, max int, rnd *rand.Rand) {
|
||||
p := &h.dbHarness
|
||||
t := p.t
|
||||
db := p.db
|
||||
|
||||
batch := new(Batch)
|
||||
for i := 0; i < n; i++ {
|
||||
batch.Reset()
|
||||
batch.Delete(tkey(rnd.Intn(max)))
|
||||
err := db.Write(batch, p.wo)
|
||||
if err != nil {
|
||||
t.Fatal("write error: ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *dbCorruptHarness) corrupt(ft storage.FileType, offset, n int) {
|
||||
p := &h.dbHarness
|
||||
t := p.t
|
||||
|
||||
var file storage.File
|
||||
ff, _ := p.stor.GetFiles(ft)
|
||||
for _, f := range ff {
|
||||
if file == nil || f.Num() > file.Num() {
|
||||
file = f
|
||||
}
|
||||
}
|
||||
if file == nil {
|
||||
t.Fatalf("no such file with type %q", ft)
|
||||
}
|
||||
|
||||
r, err := file.Open()
|
||||
if err != nil {
|
||||
t.Fatal("cannot open file: ", err)
|
||||
}
|
||||
x, err := r.Seek(0, 2)
|
||||
if err != nil {
|
||||
t.Fatal("cannot query file size: ", err)
|
||||
}
|
||||
m := int(x)
|
||||
if _, err := r.Seek(0, 0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if offset < 0 {
|
||||
if -offset > m {
|
||||
offset = 0
|
||||
} else {
|
||||
offset = m + offset
|
||||
}
|
||||
}
|
||||
if offset > m {
|
||||
offset = m
|
||||
}
|
||||
if offset+n > m {
|
||||
n = m - offset
|
||||
}
|
||||
|
||||
buf := make([]byte, m)
|
||||
_, err = io.ReadFull(r, buf)
|
||||
if err != nil {
|
||||
t.Fatal("cannot read file: ", err)
|
||||
}
|
||||
r.Close()
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
buf[offset+i] ^= 0x80
|
||||
}
|
||||
|
||||
err = file.Remove()
|
||||
if err != nil {
|
||||
t.Fatal("cannot remove old file: ", err)
|
||||
}
|
||||
w, err := file.Create()
|
||||
if err != nil {
|
||||
t.Fatal("cannot create new file: ", err)
|
||||
}
|
||||
_, err = w.Write(buf)
|
||||
if err != nil {
|
||||
t.Fatal("cannot write new file: ", err)
|
||||
}
|
||||
w.Close()
|
||||
}
|
||||
|
||||
func (h *dbCorruptHarness) removeAll(ft storage.FileType) {
|
||||
ff, err := h.stor.GetFiles(ft)
|
||||
if err != nil {
|
||||
h.t.Fatal("get files: ", err)
|
||||
}
|
||||
for _, f := range ff {
|
||||
if err := f.Remove(); err != nil {
|
||||
h.t.Error("remove file: ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *dbCorruptHarness) removeOne(ft storage.FileType) {
|
||||
ff, err := h.stor.GetFiles(ft)
|
||||
if err != nil {
|
||||
h.t.Fatal("get files: ", err)
|
||||
}
|
||||
f := ff[rand.Intn(len(ff))]
|
||||
h.t.Logf("removing file @%d", f.Num())
|
||||
if err := f.Remove(); err != nil {
|
||||
h.t.Error("remove file: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *dbCorruptHarness) check(min, max int) {
|
||||
p := &h.dbHarness
|
||||
t := p.t
|
||||
db := p.db
|
||||
|
||||
var n, badk, badv, missed, good int
|
||||
iter := db.NewIterator(nil, p.ro)
|
||||
for iter.Next() {
|
||||
k := 0
|
||||
fmt.Sscanf(string(iter.Key()), "%d", &k)
|
||||
if k < n {
|
||||
badk++
|
||||
continue
|
||||
}
|
||||
missed += k - n
|
||||
n = k + 1
|
||||
if !bytes.Equal(iter.Value(), tval(k, ctValSize)) {
|
||||
badv++
|
||||
} else {
|
||||
good++
|
||||
}
|
||||
}
|
||||
err := iter.Error()
|
||||
iter.Release()
|
||||
t.Logf("want=%d..%d got=%d badkeys=%d badvalues=%d missed=%d, err=%v",
|
||||
min, max, good, badk, badv, missed, err)
|
||||
if good < min || good > max {
|
||||
t.Errorf("good entries number not in range")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCorruptDB_Journal(t *testing.T) {
|
||||
h := newDbCorruptHarness(t)
|
||||
|
||||
h.build(100)
|
||||
h.check(100, 100)
|
||||
h.closeDB()
|
||||
h.corrupt(storage.TypeJournal, 19, 1)
|
||||
h.corrupt(storage.TypeJournal, 32*1024+1000, 1)
|
||||
|
||||
h.openDB()
|
||||
h.check(36, 36)
|
||||
|
||||
h.close()
|
||||
}
|
||||
|
||||
func TestCorruptDB_Table(t *testing.T) {
|
||||
h := newDbCorruptHarness(t)
|
||||
|
||||
h.build(100)
|
||||
h.compactMem()
|
||||
h.compactRangeAt(0, "", "")
|
||||
h.compactRangeAt(1, "", "")
|
||||
h.closeDB()
|
||||
h.corrupt(storage.TypeTable, 100, 1)
|
||||
|
||||
h.openDB()
|
||||
h.check(99, 99)
|
||||
|
||||
h.close()
|
||||
}
|
||||
|
||||
func TestCorruptDB_TableIndex(t *testing.T) {
|
||||
h := newDbCorruptHarness(t)
|
||||
|
||||
h.build(10000)
|
||||
h.compactMem()
|
||||
h.closeDB()
|
||||
h.corrupt(storage.TypeTable, -2000, 500)
|
||||
|
||||
h.openDB()
|
||||
h.check(5000, 9999)
|
||||
|
||||
h.close()
|
||||
}
|
||||
|
||||
func TestCorruptDB_MissingManifest(t *testing.T) {
|
||||
rnd := rand.New(rand.NewSource(0x0badda7a))
|
||||
h := newDbCorruptHarnessWopt(t, &opt.Options{
|
||||
BlockCache: cache.NewLRUCache(100),
|
||||
Strict: opt.StrictJournalChecksum,
|
||||
WriteBuffer: 1000 * 60,
|
||||
})
|
||||
|
||||
h.build(1000)
|
||||
h.compactMem()
|
||||
h.buildShuffled(1000, rnd)
|
||||
h.compactMem()
|
||||
h.deleteRand(500, 1000, rnd)
|
||||
h.compactMem()
|
||||
h.buildShuffled(1000, rnd)
|
||||
h.compactMem()
|
||||
h.deleteRand(500, 1000, rnd)
|
||||
h.compactMem()
|
||||
h.buildShuffled(1000, rnd)
|
||||
h.compactMem()
|
||||
h.closeDB()
|
||||
|
||||
h.stor.SetIgnoreOpenErr(storage.TypeManifest)
|
||||
h.removeAll(storage.TypeManifest)
|
||||
h.openAssert(false)
|
||||
h.stor.SetIgnoreOpenErr(0)
|
||||
|
||||
h.recover()
|
||||
h.check(1000, 1000)
|
||||
h.build(1000)
|
||||
h.compactMem()
|
||||
h.compactRange("", "")
|
||||
h.closeDB()
|
||||
|
||||
h.recover()
|
||||
h.check(1000, 1000)
|
||||
|
||||
h.close()
|
||||
}
|
||||
|
||||
func TestCorruptDB_SequenceNumberRecovery(t *testing.T) {
|
||||
h := newDbCorruptHarness(t)
|
||||
|
||||
h.put("foo", "v1")
|
||||
h.put("foo", "v2")
|
||||
h.put("foo", "v3")
|
||||
h.put("foo", "v4")
|
||||
h.put("foo", "v5")
|
||||
h.closeDB()
|
||||
|
||||
h.recover()
|
||||
h.getVal("foo", "v5")
|
||||
h.put("foo", "v6")
|
||||
h.getVal("foo", "v6")
|
||||
|
||||
h.reopenDB()
|
||||
h.getVal("foo", "v6")
|
||||
|
||||
h.close()
|
||||
}
|
||||
|
||||
func TestCorruptDB_SequenceNumberRecoveryTable(t *testing.T) {
|
||||
h := newDbCorruptHarness(t)
|
||||
|
||||
h.put("foo", "v1")
|
||||
h.put("foo", "v2")
|
||||
h.put("foo", "v3")
|
||||
h.compactMem()
|
||||
h.put("foo", "v4")
|
||||
h.put("foo", "v5")
|
||||
h.compactMem()
|
||||
h.closeDB()
|
||||
|
||||
h.recover()
|
||||
h.getVal("foo", "v5")
|
||||
h.put("foo", "v6")
|
||||
h.getVal("foo", "v6")
|
||||
|
||||
h.reopenDB()
|
||||
h.getVal("foo", "v6")
|
||||
|
||||
h.close()
|
||||
}
|
||||
|
||||
func TestCorruptDB_CorruptedManifest(t *testing.T) {
|
||||
h := newDbCorruptHarness(t)
|
||||
|
||||
h.put("foo", "hello")
|
||||
h.compactMem()
|
||||
h.compactRange("", "")
|
||||
h.closeDB()
|
||||
h.corrupt(storage.TypeManifest, 0, 1000)
|
||||
h.openAssert(false)
|
||||
|
||||
h.recover()
|
||||
h.getVal("foo", "hello")
|
||||
|
||||
h.close()
|
||||
}
|
||||
|
||||
func TestCorruptDB_CompactionInputError(t *testing.T) {
|
||||
h := newDbCorruptHarness(t)
|
||||
|
||||
h.build(10)
|
||||
h.compactMem()
|
||||
h.closeDB()
|
||||
h.corrupt(storage.TypeTable, 100, 1)
|
||||
|
||||
h.openDB()
|
||||
h.check(9, 9)
|
||||
|
||||
h.build(10000)
|
||||
h.check(10000, 10000)
|
||||
|
||||
h.close()
|
||||
}
|
||||
|
||||
func TestCorruptDB_UnrelatedKeys(t *testing.T) {
|
||||
h := newDbCorruptHarness(t)
|
||||
|
||||
h.build(10)
|
||||
h.compactMem()
|
||||
h.closeDB()
|
||||
h.corrupt(storage.TypeTable, 100, 1)
|
||||
|
||||
h.openDB()
|
||||
h.put(string(tkey(1000)), string(tval(1000, ctValSize)))
|
||||
h.getVal(string(tkey(1000)), string(tval(1000, ctValSize)))
|
||||
h.compactMem()
|
||||
h.getVal(string(tkey(1000)), string(tval(1000, ctValSize)))
|
||||
|
||||
h.close()
|
||||
}
|
||||
|
||||
func TestCorruptDB_Level0NewerFileHasOlderSeqnum(t *testing.T) {
|
||||
h := newDbCorruptHarness(t)
|
||||
|
||||
h.put("a", "v1")
|
||||
h.put("b", "v1")
|
||||
h.compactMem()
|
||||
h.put("a", "v2")
|
||||
h.put("b", "v2")
|
||||
h.compactMem()
|
||||
h.put("a", "v3")
|
||||
h.put("b", "v3")
|
||||
h.compactMem()
|
||||
h.put("c", "v0")
|
||||
h.put("d", "v0")
|
||||
h.compactMem()
|
||||
h.compactRangeAt(1, "", "")
|
||||
h.closeDB()
|
||||
|
||||
h.recover()
|
||||
h.getVal("a", "v3")
|
||||
h.getVal("b", "v3")
|
||||
h.getVal("c", "v0")
|
||||
h.getVal("d", "v0")
|
||||
|
||||
h.close()
|
||||
}
|
||||
|
||||
func TestCorruptDB_RecoverInvalidSeq_Issue53(t *testing.T) {
|
||||
h := newDbCorruptHarness(t)
|
||||
|
||||
h.put("a", "v1")
|
||||
h.put("b", "v1")
|
||||
h.compactMem()
|
||||
h.put("a", "v2")
|
||||
h.put("b", "v2")
|
||||
h.compactMem()
|
||||
h.put("a", "v3")
|
||||
h.put("b", "v3")
|
||||
h.compactMem()
|
||||
h.put("c", "v0")
|
||||
h.put("d", "v0")
|
||||
h.compactMem()
|
||||
h.compactRangeAt(0, "", "")
|
||||
h.closeDB()
|
||||
|
||||
h.recover()
|
||||
h.getVal("a", "v3")
|
||||
h.getVal("b", "v3")
|
||||
h.getVal("c", "v0")
|
||||
h.getVal("d", "v0")
|
||||
|
||||
h.close()
|
||||
}
|
||||
|
||||
func TestCorruptDB_MissingTableFiles(t *testing.T) {
|
||||
h := newDbCorruptHarness(t)
|
||||
|
||||
h.put("a", "v1")
|
||||
h.put("b", "v1")
|
||||
h.compactMem()
|
||||
h.put("c", "v2")
|
||||
h.put("d", "v2")
|
||||
h.compactMem()
|
||||
h.put("e", "v3")
|
||||
h.put("f", "v3")
|
||||
h.closeDB()
|
||||
|
||||
h.removeOne(storage.TypeTable)
|
||||
h.openAssert(false)
|
||||
|
||||
h.close()
|
||||
}
|
||||
-800
@@ -1,800 +0,0 @@
|
||||
// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
|
||||
// All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package leveldb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb/iterator"
|
||||
"github.com/syndtr/goleveldb/leveldb/journal"
|
||||
"github.com/syndtr/goleveldb/leveldb/memdb"
|
||||
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||
"github.com/syndtr/goleveldb/leveldb/storage"
|
||||
"github.com/syndtr/goleveldb/leveldb/table"
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
)
|
||||
|
||||
// DB is a LevelDB database.
|
||||
type DB struct {
|
||||
// Need 64-bit alignment.
|
||||
seq uint64
|
||||
|
||||
// Session.
|
||||
s *session
|
||||
|
||||
// MemDB.
|
||||
memMu sync.RWMutex
|
||||
mem *memdb.DB
|
||||
frozenMem *memdb.DB
|
||||
journal *journal.Writer
|
||||
journalWriter storage.Writer
|
||||
journalFile storage.File
|
||||
frozenJournalFile storage.File
|
||||
frozenSeq uint64
|
||||
|
||||
// Snapshot.
|
||||
snapsMu sync.Mutex
|
||||
snapsRoot snapshotElement
|
||||
|
||||
// Write.
|
||||
writeC chan *Batch
|
||||
writeMergedC chan bool
|
||||
writeLockC chan struct{}
|
||||
writeAckC chan error
|
||||
journalC chan *Batch
|
||||
journalAckC chan error
|
||||
|
||||
// Compaction.
|
||||
tcompCmdC chan cCmd
|
||||
tcompPauseC chan chan<- struct{}
|
||||
tcompTriggerC chan struct{}
|
||||
mcompCmdC chan cCmd
|
||||
mcompTriggerC chan struct{}
|
||||
compErrC chan error
|
||||
compErrSetC chan error
|
||||
compStats [kNumLevels]cStats
|
||||
|
||||
// Close.
|
||||
closeW sync.WaitGroup
|
||||
closeC chan struct{}
|
||||
closed uint32
|
||||
closer io.Closer
|
||||
}
|
||||
|
||||
func openDB(s *session) (*DB, error) {
|
||||
s.log("db@open opening")
|
||||
start := time.Now()
|
||||
db := &DB{
|
||||
s: s,
|
||||
// Initial sequence
|
||||
seq: s.stSeq,
|
||||
// Write
|
||||
writeC: make(chan *Batch),
|
||||
writeMergedC: make(chan bool),
|
||||
writeLockC: make(chan struct{}, 1),
|
||||
writeAckC: make(chan error),
|
||||
journalC: make(chan *Batch),
|
||||
journalAckC: make(chan error),
|
||||
// Compaction
|
||||
tcompCmdC: make(chan cCmd),
|
||||
tcompPauseC: make(chan chan<- struct{}),
|
||||
tcompTriggerC: make(chan struct{}, 1),
|
||||
mcompCmdC: make(chan cCmd),
|
||||
mcompTriggerC: make(chan struct{}, 1),
|
||||
compErrC: make(chan error),
|
||||
compErrSetC: make(chan error),
|
||||
// Close
|
||||
closeC: make(chan struct{}),
|
||||
}
|
||||
db.initSnapshot()
|
||||
|
||||
if err := db.recoverJournal(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Remove any obsolete files.
|
||||
if err := db.checkAndCleanFiles(); err != nil {
|
||||
// Close journal.
|
||||
if db.journal != nil {
|
||||
db.journal.Close()
|
||||
db.journalWriter.Close()
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Don't include compaction error goroutine into wait group.
|
||||
go db.compactionError()
|
||||
|
||||
db.closeW.Add(3)
|
||||
go db.tCompaction()
|
||||
go db.mCompaction()
|
||||
go db.jWriter()
|
||||
|
||||
s.logf("db@open done T·%v", time.Since(start))
|
||||
|
||||
runtime.SetFinalizer(db, (*DB).Close)
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// Open opens or creates a DB for the given storage.
|
||||
// The DB will be created if not exist, unless ErrorIfMissing is true.
|
||||
// Also, if ErrorIfExist is true and the DB exist Open will returns
|
||||
// os.ErrExist error.
|
||||
//
|
||||
// Open will return an error with type of ErrCorrupted if corruption
|
||||
// detected in the DB. Corrupted DB can be recovered with Recover
|
||||
// function.
|
||||
//
|
||||
// The returned DB instance is goroutine-safe.
|
||||
// The DB must be closed after use, by calling Close method.
|
||||
func Open(stor storage.Storage, o *opt.Options) (db *DB, err error) {
|
||||
s, err := newSession(stor, o)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
s.close()
|
||||
s.release()
|
||||
}
|
||||
}()
|
||||
|
||||
err = s.recover()
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) || s.o.GetErrorIfMissing() {
|
||||
return
|
||||
}
|
||||
err = s.create()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else if s.o.GetErrorIfExist() {
|
||||
err = os.ErrExist
|
||||
return
|
||||
}
|
||||
|
||||
return openDB(s)
|
||||
}
|
||||
|
||||
// OpenFile opens or creates a DB for the given path.
|
||||
// The DB will be created if not exist, unless ErrorIfMissing is true.
|
||||
// Also, if ErrorIfExist is true and the DB exist OpenFile will returns
|
||||
// os.ErrExist error.
|
||||
//
|
||||
// OpenFile uses standard file-system backed storage implementation as
|
||||
// desribed in the leveldb/storage package.
|
||||
//
|
||||
// OpenFile will return an error with type of ErrCorrupted if corruption
|
||||
// detected in the DB. Corrupted DB can be recovered with Recover
|
||||
// function.
|
||||
//
|
||||
// The returned DB instance is goroutine-safe.
|
||||
// The DB must be closed after use, by calling Close method.
|
||||
func OpenFile(path string, o *opt.Options) (db *DB, err error) {
|
||||
stor, err := storage.OpenFile(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
db, err = Open(stor, o)
|
||||
if err != nil {
|
||||
stor.Close()
|
||||
} else {
|
||||
db.closer = stor
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Recover recovers and opens a DB with missing or corrupted manifest files
|
||||
// for the given storage. It will ignore any manifest files, valid or not.
|
||||
// The DB must already exist or it will returns an error.
|
||||
// Also, Recover will ignore ErrorIfMissing and ErrorIfExist options.
|
||||
//
|
||||
// The returned DB instance is goroutine-safe.
|
||||
// The DB must be closed after use, by calling Close method.
|
||||
func Recover(stor storage.Storage, o *opt.Options) (db *DB, err error) {
|
||||
s, err := newSession(stor, o)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
s.close()
|
||||
s.release()
|
||||
}
|
||||
}()
|
||||
|
||||
err = recoverTable(s, o)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return openDB(s)
|
||||
}
|
||||
|
||||
// RecoverFile recovers and opens a DB with missing or corrupted manifest files
|
||||
// for the given path. It will ignore any manifest files, valid or not.
|
||||
// The DB must already exist or it will returns an error.
|
||||
// Also, Recover will ignore ErrorIfMissing and ErrorIfExist options.
|
||||
//
|
||||
// RecoverFile uses standard file-system backed storage implementation as desribed
|
||||
// in the leveldb/storage package.
|
||||
//
|
||||
// The returned DB instance is goroutine-safe.
|
||||
// The DB must be closed after use, by calling Close method.
|
||||
func RecoverFile(path string, o *opt.Options) (db *DB, err error) {
|
||||
stor, err := storage.OpenFile(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
db, err = Recover(stor, o)
|
||||
if err != nil {
|
||||
stor.Close()
|
||||
} else {
|
||||
db.closer = stor
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func recoverTable(s *session, o *opt.Options) error {
|
||||
// Get all tables and sort it by file number.
|
||||
tableFiles_, err := s.getFiles(storage.TypeTable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tableFiles := files(tableFiles_)
|
||||
tableFiles.sort()
|
||||
|
||||
var mSeq uint64
|
||||
var good, corrupted int
|
||||
rec := new(sessionRecord)
|
||||
buildTable := func(iter iterator.Iterator) (tmp storage.File, size int64, err error) {
|
||||
tmp = s.newTemp()
|
||||
writer, err := tmp.Create()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
writer.Close()
|
||||
if err != nil {
|
||||
tmp.Remove()
|
||||
tmp = nil
|
||||
}
|
||||
}()
|
||||
|
||||
// Copy entries.
|
||||
tw := table.NewWriter(writer, o)
|
||||
for iter.Next() {
|
||||
key := iter.Key()
|
||||
if validIkey(key) {
|
||||
err = tw.Append(key, iter.Value())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
err = iter.Error()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = tw.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = writer.Sync()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
size = int64(tw.BytesLen())
|
||||
return
|
||||
}
|
||||
recoverTable := func(file storage.File) error {
|
||||
s.logf("table@recovery recovering @%d", file.Num())
|
||||
reader, err := file.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
// Get file size.
|
||||
size, err := reader.Seek(0, 2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var tSeq uint64
|
||||
var tgood, tcorrupted, blockerr int
|
||||
var imin, imax []byte
|
||||
tr := table.NewReader(reader, size, nil, o)
|
||||
iter := tr.NewIterator(nil, nil)
|
||||
iter.(iterator.ErrorCallbackSetter).SetErrorCallback(func(err error) {
|
||||
s.logf("table@recovery found error @%d %q", file.Num(), err)
|
||||
blockerr++
|
||||
})
|
||||
|
||||
// Scan the table.
|
||||
for iter.Next() {
|
||||
key := iter.Key()
|
||||
_, seq, _, ok := parseIkey(key)
|
||||
if !ok {
|
||||
tcorrupted++
|
||||
continue
|
||||
}
|
||||
tgood++
|
||||
if seq > tSeq {
|
||||
tSeq = seq
|
||||
}
|
||||
if imin == nil {
|
||||
imin = append([]byte{}, key...)
|
||||
}
|
||||
imax = append(imax[:0], key...)
|
||||
}
|
||||
if err := iter.Error(); err != nil {
|
||||
iter.Release()
|
||||
return err
|
||||
}
|
||||
iter.Release()
|
||||
|
||||
if tgood > 0 {
|
||||
if tcorrupted > 0 || blockerr > 0 {
|
||||
// Rebuild the table.
|
||||
s.logf("table@recovery rebuilding @%d", file.Num())
|
||||
iter := tr.NewIterator(nil, nil)
|
||||
tmp, newSize, err := buildTable(iter)
|
||||
iter.Release()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
reader.Close()
|
||||
if err := file.Replace(tmp); err != nil {
|
||||
return err
|
||||
}
|
||||
size = newSize
|
||||
}
|
||||
if tSeq > mSeq {
|
||||
mSeq = tSeq
|
||||
}
|
||||
// Add table to level 0.
|
||||
rec.addTable(0, file.Num(), uint64(size), imin, imax)
|
||||
s.logf("table@recovery recovered @%d N·%d C·%d B·%d S·%d Q·%d", file.Num(), tgood, tcorrupted, blockerr, size, tSeq)
|
||||
} else {
|
||||
s.logf("table@recovery unrecoverable @%d C·%d B·%d S·%d", file.Num(), tcorrupted, blockerr, size)
|
||||
}
|
||||
|
||||
good += tgood
|
||||
corrupted += tcorrupted
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Recover all tables.
|
||||
if len(tableFiles) > 0 {
|
||||
s.logf("table@recovery F·%d", len(tableFiles))
|
||||
|
||||
// Mark file number as used.
|
||||
s.markFileNum(tableFiles[len(tableFiles)-1].Num())
|
||||
|
||||
for _, file := range tableFiles {
|
||||
if err := recoverTable(file); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
s.logf("table@recovery recovered F·%d N·%d C·%d Q·%d", len(tableFiles), good, corrupted, mSeq)
|
||||
}
|
||||
|
||||
// Set sequence number.
|
||||
rec.setSeq(mSeq + 1)
|
||||
|
||||
// Create new manifest.
|
||||
if err := s.create(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Commit.
|
||||
return s.commit(rec)
|
||||
}
|
||||
|
||||
func (db *DB) recoverJournal() error {
|
||||
// Get all tables and sort it by file number.
|
||||
journalFiles_, err := db.s.getFiles(storage.TypeJournal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
journalFiles := files(journalFiles_)
|
||||
journalFiles.sort()
|
||||
|
||||
// Discard older journal.
|
||||
prev := -1
|
||||
for i, file := range journalFiles {
|
||||
if file.Num() >= db.s.stJournalNum {
|
||||
if prev >= 0 {
|
||||
i--
|
||||
journalFiles[i] = journalFiles[prev]
|
||||
}
|
||||
journalFiles = journalFiles[i:]
|
||||
break
|
||||
} else if file.Num() == db.s.stPrevJournalNum {
|
||||
prev = i
|
||||
}
|
||||
}
|
||||
|
||||
var jr *journal.Reader
|
||||
var of storage.File
|
||||
var mem *memdb.DB
|
||||
batch := new(Batch)
|
||||
cm := newCMem(db.s)
|
||||
buf := new(util.Buffer)
|
||||
// Options.
|
||||
strict := db.s.o.GetStrict(opt.StrictJournal)
|
||||
checksum := db.s.o.GetStrict(opt.StrictJournalChecksum)
|
||||
writeBuffer := db.s.o.GetWriteBuffer()
|
||||
recoverJournal := func(file storage.File) error {
|
||||
db.logf("journal@recovery recovering @%d", file.Num())
|
||||
reader, err := file.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
// Create/reset journal reader instance.
|
||||
if jr == nil {
|
||||
jr = journal.NewReader(reader, dropper{db.s, file}, strict, checksum)
|
||||
} else {
|
||||
jr.Reset(reader, dropper{db.s, file}, strict, checksum)
|
||||
}
|
||||
|
||||
// Flush memdb and remove obsolete journal file.
|
||||
if of != nil {
|
||||
if mem.Len() > 0 {
|
||||
if err := cm.flush(mem, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := cm.commit(file.Num(), db.seq); err != nil {
|
||||
return err
|
||||
}
|
||||
cm.reset()
|
||||
of.Remove()
|
||||
of = nil
|
||||
}
|
||||
|
||||
// Replay journal to memdb.
|
||||
mem.Reset()
|
||||
for {
|
||||
r, err := jr.Next()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
if _, err := buf.ReadFrom(r); err != nil {
|
||||
if err == io.ErrUnexpectedEOF {
|
||||
continue
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := batch.decode(buf.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batch.memReplay(mem); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Save sequence number.
|
||||
db.seq = batch.seq + uint64(batch.len())
|
||||
|
||||
// Flush it if large enough.
|
||||
if mem.Size() >= writeBuffer {
|
||||
if err := cm.flush(mem, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
mem.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
of = file
|
||||
return nil
|
||||
}
|
||||
|
||||
// Recover all journals.
|
||||
if len(journalFiles) > 0 {
|
||||
db.logf("journal@recovery F·%d", len(journalFiles))
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new journal.
|
||||
if _, err := db.newMem(0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Commit.
|
||||
if err := cm.commit(db.journalFile.Num(), db.seq); err != nil {
|
||||
// Close journal.
|
||||
if db.journal != nil {
|
||||
db.journal.Close()
|
||||
db.journalWriter.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove the last obsolete journal file.
|
||||
if of != nil {
|
||||
of.Remove()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) get(key []byte, seq uint64, ro *opt.ReadOptions) (value []byte, err error) {
|
||||
ikey := newIKey(key, seq, tSeek)
|
||||
|
||||
em, fm := db.getMems()
|
||||
for _, m := range [...]*memdb.DB{em, fm} {
|
||||
if m == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
mk, mv, me := m.Find(ikey)
|
||||
if me == nil {
|
||||
ukey, _, t, ok := parseIkey(mk)
|
||||
if ok && db.s.icmp.uCompare(ukey, key) == 0 {
|
||||
if t == tDel {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
return mv, nil
|
||||
}
|
||||
} else if me != ErrNotFound {
|
||||
return nil, me
|
||||
}
|
||||
}
|
||||
|
||||
v := db.s.version()
|
||||
value, cSched, err := v.get(ikey, ro)
|
||||
v.release()
|
||||
if cSched {
|
||||
// Trigger table compaction.
|
||||
db.compTrigger(db.tcompTriggerC)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Get gets the value for the given key. It returns ErrNotFound if the
|
||||
// DB does not contain the key.
|
||||
//
|
||||
// The caller should not modify the contents of the returned slice, but
|
||||
// it is safe to modify the contents of the argument after Get returns.
|
||||
func (db *DB) Get(key []byte, ro *opt.ReadOptions) (value []byte, err error) {
|
||||
err = db.ok()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return db.get(key, db.getSeq(), ro)
|
||||
}
|
||||
|
||||
// NewIterator returns an iterator for the latest snapshot of the
|
||||
// uderlying DB.
|
||||
// The returned iterator is not goroutine-safe, but it is safe to use
|
||||
// multiple iterators concurrently, with each in a dedicated goroutine.
|
||||
// It is also safe to use an iterator concurrently with modifying its
|
||||
// underlying DB. The resultant key/value pairs are guaranteed to be
|
||||
// consistent.
|
||||
//
|
||||
// Slice allows slicing the iterator to only contains keys in the given
|
||||
// range. A nil Range.Start is treated as a key before all keys in the
|
||||
// DB. And a nil Range.Limit is treated as a key after all keys in
|
||||
// the DB.
|
||||
//
|
||||
// The iterator must be released after use, by calling Release method.
|
||||
//
|
||||
// Also read Iterator documentation of the leveldb/iterator package.
|
||||
func (db *DB) NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator {
|
||||
if err := db.ok(); err != nil {
|
||||
return iterator.NewEmptyIterator(err)
|
||||
}
|
||||
|
||||
snap := db.newSnapshot()
|
||||
defer snap.Release()
|
||||
return snap.NewIterator(slice, ro)
|
||||
}
|
||||
|
||||
// GetSnapshot returns a latest snapshot of the underlying DB. A snapshot
|
||||
// is a frozen snapshot of a DB state at a particular point in time. The
|
||||
// content of snapshot are guaranteed to be consistent.
|
||||
//
|
||||
// The snapshot must be released after use, by calling Release method.
|
||||
func (db *DB) GetSnapshot() (*Snapshot, error) {
|
||||
if err := db.ok(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return db.newSnapshot(), nil
|
||||
}
|
||||
|
||||
// GetProperty returns value of the given property name.
|
||||
//
|
||||
// Property names:
|
||||
// leveldb.num-files-at-level{n}
|
||||
// Returns the number of filer at level 'n'.
|
||||
// leveldb.stats
|
||||
// Returns statistics of the underlying DB.
|
||||
// leveldb.sstables
|
||||
// Returns sstables list for each level.
|
||||
func (db *DB) GetProperty(name string) (value string, err error) {
|
||||
err = db.ok()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
const prefix = "leveldb."
|
||||
if !strings.HasPrefix(name, prefix) {
|
||||
return "", errors.New("leveldb: GetProperty: unknown property: " + name)
|
||||
}
|
||||
p := name[len(prefix):]
|
||||
|
||||
v := db.s.version()
|
||||
defer v.release()
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(p, "num-files-at-level"):
|
||||
var level uint
|
||||
var rest string
|
||||
n, _ := fmt.Scanf("%d%s", &level, &rest)
|
||||
if n != 1 || level >= kNumLevels {
|
||||
err = errors.New("leveldb: GetProperty: invalid property: " + name)
|
||||
} else {
|
||||
value = fmt.Sprint(v.tLen(int(level)))
|
||||
}
|
||||
case p == "stats":
|
||||
value = "Compactions\n" +
|
||||
" Level | Tables | Size(MB) | Time(sec) | Read(MB) | Write(MB)\n" +
|
||||
"-------+------------+---------------+---------------+---------------+---------------\n"
|
||||
for level, tables := range v.tables {
|
||||
duration, read, write := db.compStats[level].get()
|
||||
if len(tables) == 0 && duration == 0 {
|
||||
continue
|
||||
}
|
||||
value += fmt.Sprintf(" %3d | %10d | %13.5f | %13.5f | %13.5f | %13.5f\n",
|
||||
level, len(tables), float64(tables.size())/1048576.0, duration.Seconds(),
|
||||
float64(read)/1048576.0, float64(write)/1048576.0)
|
||||
}
|
||||
case p == "sstables":
|
||||
for level, tables := range v.tables {
|
||||
value += fmt.Sprintf("--- level %d ---\n", level)
|
||||
for _, t := range tables {
|
||||
value += fmt.Sprintf("%d:%d[%q .. %q]\n", t.file.Num(), t.size, t.imin, t.imax)
|
||||
}
|
||||
}
|
||||
default:
|
||||
err = errors.New("leveldb: GetProperty: unknown property: " + name)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SizeOf calculates approximate sizes of the given key ranges.
|
||||
// The length of the returned sizes are equal with the length of the given
|
||||
// ranges. The returned sizes measure storage space usage, so if the user
|
||||
// data compresses by a factor of ten, the returned sizes will be one-tenth
|
||||
// the size of the corresponding user data size.
|
||||
// The results may not include the sizes of recently written data.
|
||||
func (db *DB) SizeOf(ranges []util.Range) (Sizes, error) {
|
||||
if err := db.ok(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := db.s.version()
|
||||
defer v.release()
|
||||
|
||||
sizes := make(Sizes, 0, len(ranges))
|
||||
for _, r := range ranges {
|
||||
imin := newIKey(r.Start, kMaxSeq, tSeek)
|
||||
imax := newIKey(r.Limit, kMaxSeq, tSeek)
|
||||
start, err := v.offsetOf(imin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
limit, err := v.offsetOf(imax)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var size uint64
|
||||
if limit >= start {
|
||||
size = limit - start
|
||||
}
|
||||
sizes = append(sizes, size)
|
||||
}
|
||||
|
||||
return sizes, nil
|
||||
}
|
||||
|
||||
// Close closes the DB. This will also releases any outstanding snapshot and
|
||||
// abort any in-flight compaction.
|
||||
//
|
||||
// It is not safe to close a DB until all outstanding iterators are released.
|
||||
// It is valid to call Close multiple times. Other methods should not be
|
||||
// called after the DB has been closed.
|
||||
func (db *DB) Close() error {
|
||||
if !db.setClosed() {
|
||||
return ErrClosed
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
db.log("db@close closing")
|
||||
|
||||
// Clear the finalizer.
|
||||
runtime.SetFinalizer(db, nil)
|
||||
|
||||
// Get compaction error.
|
||||
var err error
|
||||
select {
|
||||
case err = <-db.compErrC:
|
||||
default:
|
||||
}
|
||||
|
||||
close(db.closeC)
|
||||
|
||||
// Wait for the close WaitGroup.
|
||||
db.closeW.Wait()
|
||||
|
||||
// Close journal.
|
||||
db.writeLockC <- struct{}{}
|
||||
if db.journal != nil {
|
||||
db.journal.Close()
|
||||
db.journalWriter.Close()
|
||||
}
|
||||
|
||||
// Close session.
|
||||
db.s.close()
|
||||
db.logf("db@close done T·%v", time.Since(start))
|
||||
db.s.release()
|
||||
|
||||
if db.closer != nil {
|
||||
if err1 := db.closer.Close(); err == nil {
|
||||
err = err1
|
||||
}
|
||||
}
|
||||
|
||||
// NIL'ing pointers.
|
||||
db.s = nil
|
||||
db.mem = nil
|
||||
db.frozenMem = nil
|
||||
db.journal = nil
|
||||
db.journalWriter = nil
|
||||
db.journalFile = nil
|
||||
db.frozenJournalFile = nil
|
||||
db.snapsRoot = snapshotElement{}
|
||||
db.closer = nil
|
||||
|
||||
return err
|
||||
}
|
||||
-688
@@ -1,688 +0,0 @@
|
||||
// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
|
||||
// All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package leveldb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb/memdb"
|
||||
)
|
||||
|
||||
var (
|
||||
errCompactionTransactExiting = errors.New("leveldb: compaction transact exiting")
|
||||
)
|
||||
|
||||
type cStats struct {
|
||||
sync.Mutex
|
||||
duration time.Duration
|
||||
read uint64
|
||||
write uint64
|
||||
}
|
||||
|
||||
func (p *cStats) add(n *cStatsStaging) {
|
||||
p.Lock()
|
||||
p.duration += n.duration
|
||||
p.read += n.read
|
||||
p.write += n.write
|
||||
p.Unlock()
|
||||
}
|
||||
|
||||
func (p *cStats) get() (duration time.Duration, read, write uint64) {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
return p.duration, p.read, p.write
|
||||
}
|
||||
|
||||
type cStatsStaging struct {
|
||||
start time.Time
|
||||
duration time.Duration
|
||||
on bool
|
||||
read uint64
|
||||
write uint64
|
||||
}
|
||||
|
||||
func (p *cStatsStaging) startTimer() {
|
||||
if !p.on {
|
||||
p.start = time.Now()
|
||||
p.on = true
|
||||
}
|
||||
}
|
||||
|
||||
func (p *cStatsStaging) stopTimer() {
|
||||
if p.on {
|
||||
p.duration += time.Since(p.start)
|
||||
p.on = false
|
||||
}
|
||||
}
|
||||
|
||||
type cMem struct {
|
||||
s *session
|
||||
level int
|
||||
rec *sessionRecord
|
||||
}
|
||||
|
||||
func newCMem(s *session) *cMem {
|
||||
return &cMem{s: s, rec: new(sessionRecord)}
|
||||
}
|
||||
|
||||
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 {
|
||||
level = s.version_NB().pickLevel(t.imin.ukey(), t.imax.ukey())
|
||||
}
|
||||
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 = new(sessionRecord)
|
||||
}
|
||||
|
||||
func (c *cMem) commit(journal, seq uint64) error {
|
||||
c.rec.setJournalNum(journal)
|
||||
c.rec.setSeq(seq)
|
||||
|
||||
// Commit changes.
|
||||
return c.s.commit(c.rec)
|
||||
}
|
||||
|
||||
func (db *DB) compactionError() {
|
||||
var err error
|
||||
noerr:
|
||||
for {
|
||||
select {
|
||||
case err = <-db.compErrSetC:
|
||||
if err != nil {
|
||||
goto haserr
|
||||
}
|
||||
case _, _ = <-db.closeC:
|
||||
return
|
||||
}
|
||||
}
|
||||
haserr:
|
||||
for {
|
||||
select {
|
||||
case db.compErrC <- err:
|
||||
case err = <-db.compErrSetC:
|
||||
if err == nil {
|
||||
goto noerr
|
||||
}
|
||||
case _, _ = <-db.closeC:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type compactionTransactCounter int
|
||||
|
||||
func (cnt *compactionTransactCounter) incr() {
|
||||
*cnt++
|
||||
}
|
||||
|
||||
func (db *DB) compactionTransact(name string, exec func(cnt *compactionTransactCounter) error, rollback func() error) {
|
||||
defer func() {
|
||||
if x := recover(); x != nil {
|
||||
if x == errCompactionTransactExiting && rollback != nil {
|
||||
if err := rollback(); err != nil {
|
||||
db.logf("%s rollback error %q", name, err)
|
||||
}
|
||||
}
|
||||
panic(x)
|
||||
}
|
||||
}()
|
||||
|
||||
const (
|
||||
backoffMin = 1 * time.Second
|
||||
backoffMax = 8 * time.Second
|
||||
backoffMul = 2 * time.Second
|
||||
)
|
||||
backoff := backoffMin
|
||||
backoffT := time.NewTimer(backoff)
|
||||
lastCnt := compactionTransactCounter(0)
|
||||
for n := 0; ; n++ {
|
||||
// Check wether the DB is closed.
|
||||
if db.isClosed() {
|
||||
db.logf("%s exiting", name)
|
||||
db.compactionExitTransact()
|
||||
} else if n > 0 {
|
||||
db.logf("%s retrying N·%d", name, n)
|
||||
}
|
||||
|
||||
// Execute.
|
||||
cnt := compactionTransactCounter(0)
|
||||
err := exec(&cnt)
|
||||
|
||||
// Set compaction error status.
|
||||
select {
|
||||
case db.compErrSetC <- err:
|
||||
case _, _ = <-db.closeC:
|
||||
db.logf("%s exiting", name)
|
||||
db.compactionExitTransact()
|
||||
}
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
db.logf("%s error I·%d %q", name, cnt, err)
|
||||
|
||||
// Reset backoff duration if counter is advancing.
|
||||
if cnt > lastCnt {
|
||||
backoff = backoffMin
|
||||
lastCnt = cnt
|
||||
}
|
||||
|
||||
// Backoff.
|
||||
backoffT.Reset(backoff)
|
||||
if backoff < backoffMax {
|
||||
backoff *= backoffMul
|
||||
if backoff > backoffMax {
|
||||
backoff = backoffMax
|
||||
}
|
||||
}
|
||||
select {
|
||||
case <-backoffT.C:
|
||||
case _, _ = <-db.closeC:
|
||||
db.logf("%s exiting", name)
|
||||
db.compactionExitTransact()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (db *DB) compactionExitTransact() {
|
||||
panic(errCompactionTransactExiting)
|
||||
}
|
||||
|
||||
func (db *DB) memCompaction() {
|
||||
mem := db.getFrozenMem()
|
||||
if mem == nil {
|
||||
return
|
||||
}
|
||||
|
||||
c := newCMem(db.s)
|
||||
stats := new(cStatsStaging)
|
||||
|
||||
db.logf("mem@flush N·%d S·%s", mem.Len(), shortenb(mem.Size()))
|
||||
|
||||
// Don't compact empty memdb.
|
||||
if mem.Len() == 0 {
|
||||
db.logf("mem@flush skipping")
|
||||
// drop frozen mem
|
||||
db.dropFrozenMem()
|
||||
return
|
||||
}
|
||||
|
||||
// Pause table compaction.
|
||||
ch := make(chan struct{})
|
||||
select {
|
||||
case db.tcompPauseC <- (chan<- struct{})(ch):
|
||||
case _, _ = <-db.closeC:
|
||||
return
|
||||
}
|
||||
|
||||
db.compactionTransact("mem@flush", func(cnt *compactionTransactCounter) (err error) {
|
||||
stats.startTimer()
|
||||
defer stats.stopTimer()
|
||||
return c.flush(mem, -1)
|
||||
}, func() error {
|
||||
for _, r := range c.rec.addedTables {
|
||||
db.logf("mem@flush rollback @%d", r.num)
|
||||
f := db.s.getTableFile(r.num)
|
||||
if err := f.Remove(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
db.compactionTransact("mem@commit", func(cnt *compactionTransactCounter) (err error) {
|
||||
stats.startTimer()
|
||||
defer stats.stopTimer()
|
||||
return c.commit(db.journalFile.Num(), db.frozenSeq)
|
||||
}, nil)
|
||||
|
||||
db.logf("mem@flush commited F·%d T·%v", len(c.rec.addedTables), stats.duration)
|
||||
|
||||
for _, r := range c.rec.addedTables {
|
||||
stats.write += r.size
|
||||
}
|
||||
db.compStats[c.level].add(stats)
|
||||
|
||||
// Drop frozen mem.
|
||||
db.dropFrozenMem()
|
||||
|
||||
// Resume table compaction.
|
||||
select {
|
||||
case <-ch:
|
||||
case _, _ = <-db.closeC:
|
||||
return
|
||||
}
|
||||
|
||||
// Trigger table compaction.
|
||||
db.compTrigger(db.mcompTriggerC)
|
||||
}
|
||||
|
||||
func (db *DB) tableCompaction(c *compaction, noTrivial bool) {
|
||||
rec := new(sessionRecord)
|
||||
rec.addCompactionPointer(c.level, c.imax)
|
||||
|
||||
if !noTrivial && c.trivial() {
|
||||
t := c.tables[0][0]
|
||||
db.logf("table@move L%d@%d -> L%d", c.level, t.file.Num(), c.level+1)
|
||||
rec.deleteTable(c.level, t.file.Num())
|
||||
rec.addTableFile(c.level+1, t)
|
||||
db.compactionTransact("table@move", func(cnt *compactionTransactCounter) (err error) {
|
||||
return db.s.commit(rec)
|
||||
}, nil)
|
||||
return
|
||||
}
|
||||
|
||||
var stats [2]cStatsStaging
|
||||
for i, tables := range c.tables {
|
||||
for _, t := range tables {
|
||||
stats[i].read += t.size
|
||||
// Insert deleted tables into record
|
||||
rec.deleteTable(c.level+i, t.file.Num())
|
||||
}
|
||||
}
|
||||
sourceSize := int(stats[0].read + stats[1].read)
|
||||
minSeq := db.minSeq()
|
||||
db.logf("table@compaction L%d·%d -> L%d·%d S·%s Q·%d", c.level, len(c.tables[0]), c.level+1, len(c.tables[1]), shortenb(sourceSize), minSeq)
|
||||
|
||||
var snapUkey []byte
|
||||
var snapHasUkey bool
|
||||
var snapSeq uint64
|
||||
var snapIter int
|
||||
var snapDropCnt int
|
||||
var dropCnt int
|
||||
db.compactionTransact("table@build", func(cnt *compactionTransactCounter) (err error) {
|
||||
ukey := append([]byte{}, snapUkey...)
|
||||
hasUkey := snapHasUkey
|
||||
lseq := snapSeq
|
||||
dropCnt = snapDropCnt
|
||||
snapSched := snapIter == 0
|
||||
|
||||
var tw *tWriter
|
||||
finish := func() error {
|
||||
t, err := tw.finish()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rec.addTableFile(c.level+1, t)
|
||||
stats[1].write += t.size
|
||||
db.logf("table@build created L%d@%d N·%d S·%s %q:%q", c.level+1, t.file.Num(), tw.tw.EntriesLen(), shortenb(int(t.size)), t.imin, t.imax)
|
||||
return nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
stats[1].stopTimer()
|
||||
if tw != nil {
|
||||
tw.drop()
|
||||
tw = nil
|
||||
}
|
||||
}()
|
||||
|
||||
stats[1].startTimer()
|
||||
iter := c.newIterator()
|
||||
defer iter.Release()
|
||||
for i := 0; iter.Next(); i++ {
|
||||
// Incr transact counter.
|
||||
cnt.incr()
|
||||
|
||||
// Skip until last state.
|
||||
if i < snapIter {
|
||||
continue
|
||||
}
|
||||
|
||||
ikey := iKey(iter.Key())
|
||||
|
||||
if c.shouldStopBefore(ikey) && tw != nil {
|
||||
err = finish()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
snapSched = true
|
||||
tw = nil
|
||||
}
|
||||
|
||||
// Scheduled for snapshot, snapshot will used to retry compaction
|
||||
// if error occured.
|
||||
if snapSched {
|
||||
snapUkey = append(snapUkey[:0], ukey...)
|
||||
snapHasUkey = hasUkey
|
||||
snapSeq = lseq
|
||||
snapIter = i
|
||||
snapDropCnt = dropCnt
|
||||
snapSched = false
|
||||
}
|
||||
|
||||
if seq, vt, ok := ikey.parseNum(); !ok {
|
||||
// Don't drop error keys
|
||||
ukey = ukey[:0]
|
||||
hasUkey = false
|
||||
lseq = kMaxSeq
|
||||
} else {
|
||||
if !hasUkey || db.s.icmp.uCompare(ikey.ukey(), ukey) != 0 {
|
||||
// First occurrence of this user key
|
||||
ukey = append(ukey[:0], ikey.ukey()...)
|
||||
hasUkey = true
|
||||
lseq = kMaxSeq
|
||||
}
|
||||
|
||||
drop := false
|
||||
if lseq <= minSeq {
|
||||
// Dropped because newer entry for same user key exist
|
||||
drop = true // (A)
|
||||
} else if vt == tDel && seq <= minSeq && c.baseLevelForKey(ukey) {
|
||||
// For this user key:
|
||||
// (1) there is no data in higher levels
|
||||
// (2) data in lower levels will have larger seq numbers
|
||||
// (3) data in layers that are being compacted here and have
|
||||
// smaller seq numbers will be dropped in the next
|
||||
// few iterations of this loop (by rule (A) above).
|
||||
// Therefore this deletion marker is obsolete and can be dropped.
|
||||
drop = true
|
||||
}
|
||||
|
||||
lseq = seq
|
||||
if drop {
|
||||
dropCnt++
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Create new table if not already
|
||||
if tw == nil {
|
||||
// Check for pause event.
|
||||
select {
|
||||
case ch := <-db.tcompPauseC:
|
||||
db.pauseCompaction(ch)
|
||||
case _, _ = <-db.closeC:
|
||||
db.compactionExitTransact()
|
||||
default:
|
||||
}
|
||||
|
||||
// Create new table.
|
||||
tw, err = db.s.tops.create()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Write key/value into table
|
||||
err = tw.append(ikey, iter.Value())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Finish table if it is big enough
|
||||
if tw.tw.BytesLen() >= kMaxTableSize {
|
||||
err = finish()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
snapSched = true
|
||||
tw = nil
|
||||
}
|
||||
}
|
||||
|
||||
err = iter.Error()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Finish last table
|
||||
if tw != nil && !tw.empty() {
|
||||
err = finish()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tw = nil
|
||||
}
|
||||
return
|
||||
}, func() error {
|
||||
for _, r := range rec.addedTables {
|
||||
db.logf("table@build rollback @%d", r.num)
|
||||
f := db.s.getTableFile(r.num)
|
||||
if err := f.Remove(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// Commit changes
|
||||
db.compactionTransact("table@commit", func(cnt *compactionTransactCounter) (err error) {
|
||||
stats[1].startTimer()
|
||||
defer stats[1].stopTimer()
|
||||
return db.s.commit(rec)
|
||||
}, nil)
|
||||
|
||||
resultSize := int(stats[1].write)
|
||||
db.logf("table@compaction commited F%s S%s D·%d T·%v", sint(len(rec.addedTables)-len(rec.deletedTables)), sshortenb(resultSize-sourceSize), dropCnt, stats[1].duration)
|
||||
|
||||
// Save compaction stats
|
||||
for i := range stats {
|
||||
db.compStats[c.level+1].add(&stats[i])
|
||||
}
|
||||
}
|
||||
|
||||
func (db *DB) tableRangeCompaction(level int, umin, umax []byte) {
|
||||
db.logf("table@compaction range L%d %q:%q", level, umin, umax)
|
||||
|
||||
if level >= 0 {
|
||||
if c := db.s.getCompactionRange(level, umin, umax); c != nil {
|
||||
db.tableCompaction(c, true)
|
||||
}
|
||||
} else {
|
||||
v := db.s.version_NB()
|
||||
|
||||
m := 1
|
||||
for i, t := range v.tables[1:] {
|
||||
if t.overlaps(db.s.icmp, umin, umax, false) {
|
||||
m = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
for level := 0; level < m; level++ {
|
||||
if c := db.s.getCompactionRange(level, umin, umax); c != nil {
|
||||
db.tableCompaction(c, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (db *DB) tableAutoCompaction() {
|
||||
if c := db.s.pickCompaction(); c != nil {
|
||||
db.tableCompaction(c, false)
|
||||
}
|
||||
}
|
||||
|
||||
func (db *DB) tableNeedCompaction() bool {
|
||||
return db.s.version_NB().needCompaction()
|
||||
}
|
||||
|
||||
func (db *DB) pauseCompaction(ch chan<- struct{}) {
|
||||
select {
|
||||
case ch <- struct{}{}:
|
||||
case _, _ = <-db.closeC:
|
||||
db.compactionExitTransact()
|
||||
}
|
||||
}
|
||||
|
||||
type cCmd interface {
|
||||
ack(err error)
|
||||
}
|
||||
|
||||
type cIdle struct {
|
||||
ackC chan<- error
|
||||
}
|
||||
|
||||
func (r cIdle) ack(err error) {
|
||||
r.ackC <- err
|
||||
}
|
||||
|
||||
type cRange struct {
|
||||
level int
|
||||
min, max []byte
|
||||
ackC chan<- error
|
||||
}
|
||||
|
||||
func (r cRange) ack(err error) {
|
||||
defer func() {
|
||||
recover()
|
||||
}()
|
||||
if r.ackC != nil {
|
||||
r.ackC <- err
|
||||
}
|
||||
}
|
||||
|
||||
func (db *DB) compSendIdle(compC chan<- cCmd) error {
|
||||
ch := make(chan error)
|
||||
defer close(ch)
|
||||
// Send cmd.
|
||||
select {
|
||||
case compC <- cIdle{ch}:
|
||||
case err := <-db.compErrC:
|
||||
return err
|
||||
case _, _ = <-db.closeC:
|
||||
return ErrClosed
|
||||
}
|
||||
// Wait cmd.
|
||||
return <-ch
|
||||
}
|
||||
|
||||
func (db *DB) compSendRange(compC chan<- cCmd, level int, min, max []byte) (err error) {
|
||||
ch := make(chan error)
|
||||
defer close(ch)
|
||||
// Send cmd.
|
||||
select {
|
||||
case compC <- cRange{level, min, max, ch}:
|
||||
case err := <-db.compErrC:
|
||||
return err
|
||||
case _, _ = <-db.closeC:
|
||||
return ErrClosed
|
||||
}
|
||||
// Wait cmd.
|
||||
select {
|
||||
case err = <-db.compErrC:
|
||||
case err = <-ch:
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *DB) compTrigger(compTriggerC chan struct{}) {
|
||||
select {
|
||||
case compTriggerC <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (db *DB) mCompaction() {
|
||||
var x cCmd
|
||||
|
||||
defer func() {
|
||||
if x := recover(); x != nil {
|
||||
if x != errCompactionTransactExiting {
|
||||
panic(x)
|
||||
}
|
||||
}
|
||||
if x != nil {
|
||||
x.ack(ErrClosed)
|
||||
}
|
||||
db.closeW.Done()
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case x = <-db.mcompCmdC:
|
||||
db.memCompaction()
|
||||
x.ack(nil)
|
||||
x = nil
|
||||
case <-db.mcompTriggerC:
|
||||
db.memCompaction()
|
||||
case _, _ = <-db.closeC:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (db *DB) tCompaction() {
|
||||
var x cCmd
|
||||
var ackQ []cCmd
|
||||
|
||||
defer func() {
|
||||
if x := recover(); x != nil {
|
||||
if x != errCompactionTransactExiting {
|
||||
panic(x)
|
||||
}
|
||||
}
|
||||
for i := range ackQ {
|
||||
ackQ[i].ack(ErrClosed)
|
||||
ackQ[i] = nil
|
||||
}
|
||||
if x != nil {
|
||||
x.ack(ErrClosed)
|
||||
}
|
||||
db.closeW.Done()
|
||||
}()
|
||||
|
||||
for {
|
||||
if db.tableNeedCompaction() {
|
||||
select {
|
||||
case x = <-db.tcompCmdC:
|
||||
case <-db.tcompTriggerC:
|
||||
case ch := <-db.tcompPauseC:
|
||||
db.pauseCompaction(ch)
|
||||
continue
|
||||
case _, _ = <-db.closeC:
|
||||
return
|
||||
default:
|
||||
}
|
||||
} else {
|
||||
for i := range ackQ {
|
||||
ackQ[i].ack(nil)
|
||||
ackQ[i] = nil
|
||||
}
|
||||
ackQ = ackQ[:0]
|
||||
select {
|
||||
case x = <-db.tcompCmdC:
|
||||
case <-db.tcompTriggerC:
|
||||
case ch := <-db.tcompPauseC:
|
||||
db.pauseCompaction(ch)
|
||||
continue
|
||||
case _, _ = <-db.closeC:
|
||||
return
|
||||
}
|
||||
}
|
||||
if x != nil {
|
||||
switch cmd := x.(type) {
|
||||
case cIdle:
|
||||
ackQ = append(ackQ, x)
|
||||
case cRange:
|
||||
db.tableRangeCompaction(cmd.level, cmd.min, cmd.max)
|
||||
x.ack(nil)
|
||||
}
|
||||
x = nil
|
||||
}
|
||||
db.tableAutoCompaction()
|
||||
}
|
||||
}
|
||||
-115
@@ -1,115 +0,0 @@
|
||||
// Copyright (c) 2013, 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/journal"
|
||||
"github.com/syndtr/goleveldb/leveldb/memdb"
|
||||
)
|
||||
|
||||
// Get latest sequence number.
|
||||
func (db *DB) getSeq() uint64 {
|
||||
return atomic.LoadUint64(&db.seq)
|
||||
}
|
||||
|
||||
// Atomically adds delta to seq.
|
||||
func (db *DB) addSeq(delta uint64) {
|
||||
atomic.AddUint64(&db.seq, delta)
|
||||
}
|
||||
|
||||
// Create new memdb and froze the old one; need external synchronization.
|
||||
// newMem only called synchronously by the writer.
|
||||
func (db *DB) newMem(n int) (mem *memdb.DB, err error) {
|
||||
num := db.s.allocFileNum()
|
||||
file := db.s.getJournalFile(num)
|
||||
w, err := file.Create()
|
||||
if err != nil {
|
||||
db.s.reuseFileNum(num)
|
||||
return
|
||||
}
|
||||
|
||||
db.memMu.Lock()
|
||||
defer db.memMu.Unlock()
|
||||
|
||||
if db.journal == nil {
|
||||
db.journal = journal.NewWriter(w)
|
||||
} else {
|
||||
db.journal.Reset(w)
|
||||
db.journalWriter.Close()
|
||||
db.frozenJournalFile = db.journalFile
|
||||
}
|
||||
db.journalWriter = w
|
||||
db.journalFile = file
|
||||
db.frozenMem = db.mem
|
||||
db.mem = memdb.New(db.s.icmp, maxInt(db.s.o.GetWriteBuffer(), n))
|
||||
mem = db.mem
|
||||
// The seq only incremented by the writer. And whoever called newMem
|
||||
// should hold write lock, so no need additional synchronization here.
|
||||
db.frozenSeq = db.seq
|
||||
return
|
||||
}
|
||||
|
||||
// Get all memdbs.
|
||||
func (db *DB) getMems() (e *memdb.DB, f *memdb.DB) {
|
||||
db.memMu.RLock()
|
||||
defer db.memMu.RUnlock()
|
||||
return db.mem, db.frozenMem
|
||||
}
|
||||
|
||||
// Get frozen memdb.
|
||||
func (db *DB) getEffectiveMem() *memdb.DB {
|
||||
db.memMu.RLock()
|
||||
defer db.memMu.RUnlock()
|
||||
return db.mem
|
||||
}
|
||||
|
||||
// Check whether we has frozen memdb.
|
||||
func (db *DB) hasFrozenMem() bool {
|
||||
db.memMu.RLock()
|
||||
defer db.memMu.RUnlock()
|
||||
return db.frozenMem != nil
|
||||
}
|
||||
|
||||
// Get frozen memdb.
|
||||
func (db *DB) getFrozenMem() *memdb.DB {
|
||||
db.memMu.RLock()
|
||||
defer db.memMu.RUnlock()
|
||||
return db.frozenMem
|
||||
}
|
||||
|
||||
// Drop frozen memdb; assume that frozen memdb isn't nil.
|
||||
func (db *DB) dropFrozenMem() {
|
||||
db.memMu.Lock()
|
||||
if err := db.frozenJournalFile.Remove(); err != nil {
|
||||
db.logf("journal@remove removing @%d %q", db.frozenJournalFile.Num(), err)
|
||||
} else {
|
||||
db.logf("journal@remove removed @%d", db.frozenJournalFile.Num())
|
||||
}
|
||||
db.frozenJournalFile = nil
|
||||
db.frozenMem = nil
|
||||
db.memMu.Unlock()
|
||||
}
|
||||
|
||||
// Set closed flag; return true if not already closed.
|
||||
func (db *DB) setClosed() bool {
|
||||
return atomic.CompareAndSwapUint32(&db.closed, 0, 1)
|
||||
}
|
||||
|
||||
// Check whether DB was closed.
|
||||
func (db *DB) isClosed() bool {
|
||||
return atomic.LoadUint32(&db.closed) != 0
|
||||
}
|
||||
|
||||
// Check read ok status.
|
||||
func (db *DB) ok() error {
|
||||
if db.isClosed() {
|
||||
return ErrClosed
|
||||
}
|
||||
return nil
|
||||
}
|
||||
-1886
File diff suppressed because it is too large
Load Diff
-277
@@ -1,277 +0,0 @@
|
||||
// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
|
||||
// All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package leveldb
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb/memdb"
|
||||
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
)
|
||||
|
||||
func (db *DB) writeJournal(b *Batch) error {
|
||||
w, err := db.journal.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write(b.encode()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.journal.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
if b.sync {
|
||||
return db.journalWriter.Sync()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) jWriter() {
|
||||
defer db.closeW.Done()
|
||||
for {
|
||||
select {
|
||||
case b := <-db.journalC:
|
||||
if b != nil {
|
||||
db.journalAckC <- db.writeJournal(b)
|
||||
}
|
||||
case _, _ = <-db.closeC:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (db *DB) rotateMem(n int) (mem *memdb.DB, err error) {
|
||||
// Wait for pending memdb compaction.
|
||||
err = db.compSendIdle(db.mcompCmdC)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Create new memdb and journal.
|
||||
mem, err = db.newMem(n)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Schedule memdb compaction.
|
||||
db.compTrigger(db.mcompTriggerC)
|
||||
return
|
||||
}
|
||||
|
||||
func (db *DB) flush(n int) (mem *memdb.DB, nn int, err error) {
|
||||
delayed := false
|
||||
flush := func() bool {
|
||||
v := db.s.version()
|
||||
defer v.release()
|
||||
mem = db.getEffectiveMem()
|
||||
nn = mem.Free()
|
||||
switch {
|
||||
case v.tLen(0) >= kL0_SlowdownWritesTrigger && !delayed:
|
||||
delayed = true
|
||||
time.Sleep(time.Millisecond)
|
||||
case nn >= n:
|
||||
return false
|
||||
case v.tLen(0) >= kL0_StopWritesTrigger:
|
||||
delayed = true
|
||||
err = db.compSendIdle(db.tcompCmdC)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
// Allow memdb to grow if it has no entry.
|
||||
if mem.Len() == 0 {
|
||||
nn = n
|
||||
return false
|
||||
}
|
||||
mem, err = db.rotateMem(n)
|
||||
nn = mem.Free()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
start := time.Now()
|
||||
for flush() {
|
||||
}
|
||||
if delayed {
|
||||
db.logf("db@write delayed T·%v", time.Since(start))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Write apply the given batch to the DB. The batch will be applied
|
||||
// sequentially.
|
||||
//
|
||||
// It is safe to modify the contents of the arguments after Write returns.
|
||||
func (db *DB) Write(b *Batch, wo *opt.WriteOptions) (err error) {
|
||||
err = db.ok()
|
||||
if err != nil || b == nil || b.len() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
b.init(wo.GetSync())
|
||||
|
||||
// The write happen synchronously.
|
||||
retry:
|
||||
select {
|
||||
case db.writeC <- b:
|
||||
if <-db.writeMergedC {
|
||||
return <-db.writeAckC
|
||||
}
|
||||
goto retry
|
||||
case db.writeLockC <- struct{}{}:
|
||||
case _, _ = <-db.closeC:
|
||||
return ErrClosed
|
||||
}
|
||||
|
||||
merged := 0
|
||||
defer func() {
|
||||
<-db.writeLockC
|
||||
for i := 0; i < merged; i++ {
|
||||
db.writeAckC <- err
|
||||
}
|
||||
}()
|
||||
|
||||
mem, memFree, err := db.flush(b.size())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Calculate maximum size of the batch.
|
||||
m := 1 << 20
|
||||
if x := b.size(); x <= 128<<10 {
|
||||
m = x + (128 << 10)
|
||||
}
|
||||
m = minInt(m, memFree)
|
||||
|
||||
// Merge with other batch.
|
||||
drain:
|
||||
for b.size() < m && !b.sync {
|
||||
select {
|
||||
case nb := <-db.writeC:
|
||||
if b.size()+nb.size() <= m {
|
||||
b.append(nb)
|
||||
db.writeMergedC <- true
|
||||
merged++
|
||||
} else {
|
||||
db.writeMergedC <- false
|
||||
break drain
|
||||
}
|
||||
default:
|
||||
break drain
|
||||
}
|
||||
}
|
||||
|
||||
// Set batch first seq number relative from last seq.
|
||||
b.seq = db.seq + 1
|
||||
|
||||
// Write journal concurrently if it is large enough.
|
||||
if b.size() >= (128 << 10) {
|
||||
// Push the write batch to the journal writer
|
||||
select {
|
||||
case _, _ = <-db.closeC:
|
||||
err = ErrClosed
|
||||
return
|
||||
case db.journalC <- b:
|
||||
// Write into memdb
|
||||
b.memReplay(mem)
|
||||
}
|
||||
// Wait for journal writer
|
||||
select {
|
||||
case _, _ = <-db.closeC:
|
||||
err = ErrClosed
|
||||
return
|
||||
case err = <-db.journalAckC:
|
||||
if err != nil {
|
||||
// Revert memdb if error detected
|
||||
b.revertMemReplay(mem)
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = db.writeJournal(b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
b.memReplay(mem)
|
||||
}
|
||||
|
||||
// Set last seq number.
|
||||
db.addSeq(uint64(b.len()))
|
||||
|
||||
if b.size() >= memFree {
|
||||
db.rotateMem(0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Put sets the value for the given key. It overwrites any previous value
|
||||
// for that key; a DB is not a multi-map.
|
||||
//
|
||||
// It is safe to modify the contents of the arguments after Put returns.
|
||||
func (db *DB) Put(key, value []byte, wo *opt.WriteOptions) error {
|
||||
b := new(Batch)
|
||||
b.Put(key, value)
|
||||
return db.Write(b, wo)
|
||||
}
|
||||
|
||||
// Delete deletes the value for the given key. It returns ErrNotFound if
|
||||
// the DB does not contain the key.
|
||||
//
|
||||
// It is safe to modify the contents of the arguments after Delete returns.
|
||||
func (db *DB) Delete(key []byte, wo *opt.WriteOptions) error {
|
||||
b := new(Batch)
|
||||
b.Delete(key)
|
||||
return db.Write(b, wo)
|
||||
}
|
||||
|
||||
func isMemOverlaps(icmp *iComparer, mem *memdb.DB, min, max []byte) bool {
|
||||
iter := mem.NewIterator(nil)
|
||||
defer iter.Release()
|
||||
return (max == nil || (iter.First() && icmp.uCompare(max, iKey(iter.Key()).ukey()) >= 0)) &&
|
||||
(min == nil || (iter.Last() && icmp.uCompare(min, iKey(iter.Key()).ukey()) <= 0))
|
||||
}
|
||||
|
||||
// CompactRange compacts the underlying DB for the given key range.
|
||||
// In particular, deleted and overwritten versions are discarded,
|
||||
// and the data is rearranged to reduce the cost of operations
|
||||
// needed to access the data. This operation should typically only
|
||||
// be invoked by users who understand the underlying implementation.
|
||||
//
|
||||
// A nil Range.Start is treated as a key before all keys in the DB.
|
||||
// And a nil Range.Limit is treated as a key after all keys in the DB.
|
||||
// Therefore if both is nil then it will compact entire DB.
|
||||
func (db *DB) CompactRange(r util.Range) error {
|
||||
if err := db.ok(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
select {
|
||||
case db.writeLockC <- struct{}{}:
|
||||
case _, _ = <-db.closeC:
|
||||
return ErrClosed
|
||||
}
|
||||
|
||||
// Check for overlaps in memdb.
|
||||
mem := db.getEffectiveMem()
|
||||
if isMemOverlaps(db.s.icmp, mem, r.Start, r.Limit) {
|
||||
// Memdb compaction.
|
||||
if _, err := db.rotateMem(0); err != nil {
|
||||
<-db.writeLockC
|
||||
return err
|
||||
}
|
||||
<-db.writeLockC
|
||||
if err := db.compSendIdle(db.mcompCmdC); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
<-db.writeLockC
|
||||
}
|
||||
|
||||
// Table compaction.
|
||||
return db.compSendRange(db.tcompCmdC, -1, r.Start, r.Limit)
|
||||
}
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
// Copyright (c) 2014, 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 (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||
"github.com/syndtr/goleveldb/leveldb/testutil"
|
||||
)
|
||||
|
||||
var _ = testutil.Defer(func() {
|
||||
Describe("Leveldb external", func() {
|
||||
o := &opt.Options{
|
||||
BlockCache: opt.NoCache,
|
||||
BlockRestartInterval: 5,
|
||||
BlockSize: 50,
|
||||
Compression: opt.NoCompression,
|
||||
MaxOpenFiles: 0,
|
||||
Strict: opt.StrictAll,
|
||||
WriteBuffer: 1000,
|
||||
}
|
||||
|
||||
Describe("write test", func() {
|
||||
It("should do write correctly", func(done Done) {
|
||||
db := newTestingDB(o, nil, nil)
|
||||
t := testutil.DBTesting{
|
||||
DB: db,
|
||||
Deleted: testutil.KeyValue_Generate(nil, 500, 1, 50, 5, 5).Clone(),
|
||||
}
|
||||
testutil.DoDBTesting(&t)
|
||||
db.TestClose()
|
||||
done <- true
|
||||
}, 20.0)
|
||||
})
|
||||
|
||||
Describe("read test", func() {
|
||||
testutil.AllKeyValueTesting(nil, func(kv testutil.KeyValue) testutil.DB {
|
||||
// Building the DB.
|
||||
db := newTestingDB(o, nil, nil)
|
||||
kv.IterateShuffled(nil, func(i int, key, value []byte) {
|
||||
err := db.TestPut(key, value)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
testutil.Defer("teardown", func() {
|
||||
db.TestClose()
|
||||
})
|
||||
|
||||
return db
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
-142
@@ -1,142 +0,0 @@
|
||||
// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
|
||||
// All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package filter
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type harness struct {
|
||||
t *testing.T
|
||||
|
||||
bloom Filter
|
||||
generator FilterGenerator
|
||||
filter []byte
|
||||
}
|
||||
|
||||
func newHarness(t *testing.T) *harness {
|
||||
bloom := NewBloomFilter(10)
|
||||
return &harness{
|
||||
t: t,
|
||||
bloom: bloom,
|
||||
generator: bloom.NewGenerator(),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *harness) add(key []byte) {
|
||||
h.generator.Add(key)
|
||||
}
|
||||
|
||||
func (h *harness) addNum(key uint32) {
|
||||
var b [4]byte
|
||||
binary.LittleEndian.PutUint32(b[:], key)
|
||||
h.add(b[:])
|
||||
}
|
||||
|
||||
func (h *harness) build() {
|
||||
b := &util.Buffer{}
|
||||
h.generator.Generate(b)
|
||||
h.filter = b.Bytes()
|
||||
}
|
||||
|
||||
func (h *harness) reset() {
|
||||
h.filter = nil
|
||||
}
|
||||
|
||||
func (h *harness) filterLen() int {
|
||||
return len(h.filter)
|
||||
}
|
||||
|
||||
func (h *harness) assert(key []byte, want, silent bool) bool {
|
||||
got := h.bloom.Contains(h.filter, key)
|
||||
if !silent && got != want {
|
||||
h.t.Errorf("assert on '%v' failed got '%v', want '%v'", key, got, want)
|
||||
}
|
||||
return got
|
||||
}
|
||||
|
||||
func (h *harness) assertNum(key uint32, want, silent bool) bool {
|
||||
var b [4]byte
|
||||
binary.LittleEndian.PutUint32(b[:], key)
|
||||
return h.assert(b[:], want, silent)
|
||||
}
|
||||
|
||||
func TestBloomFilter_Empty(t *testing.T) {
|
||||
h := newHarness(t)
|
||||
h.build()
|
||||
h.assert([]byte("hello"), false, false)
|
||||
h.assert([]byte("world"), false, false)
|
||||
}
|
||||
|
||||
func TestBloomFilter_Small(t *testing.T) {
|
||||
h := newHarness(t)
|
||||
h.add([]byte("hello"))
|
||||
h.add([]byte("world"))
|
||||
h.build()
|
||||
h.assert([]byte("hello"), true, false)
|
||||
h.assert([]byte("world"), true, false)
|
||||
h.assert([]byte("x"), false, false)
|
||||
h.assert([]byte("foo"), false, false)
|
||||
}
|
||||
|
||||
func nextN(n int) int {
|
||||
switch {
|
||||
case n < 10:
|
||||
n += 1
|
||||
case n < 100:
|
||||
n += 10
|
||||
case n < 1000:
|
||||
n += 100
|
||||
default:
|
||||
n += 1000
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func TestBloomFilter_VaryingLengths(t *testing.T) {
|
||||
h := newHarness(t)
|
||||
var mediocre, good int
|
||||
for n := 1; n < 10000; n = nextN(n) {
|
||||
h.reset()
|
||||
for i := 0; i < n; i++ {
|
||||
h.addNum(uint32(i))
|
||||
}
|
||||
h.build()
|
||||
|
||||
got := h.filterLen()
|
||||
want := (n * 10 / 8) + 40
|
||||
if got > want {
|
||||
t.Errorf("filter len test failed, '%d' > '%d'", got, want)
|
||||
}
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
h.assertNum(uint32(i), true, false)
|
||||
}
|
||||
|
||||
var rate float32
|
||||
for i := 0; i < 10000; i++ {
|
||||
if h.assertNum(uint32(i+1000000000), true, true) {
|
||||
rate++
|
||||
}
|
||||
}
|
||||
rate /= 10000
|
||||
if rate > 0.02 {
|
||||
t.Errorf("false positive rate is more than 2%%, got %v, at len %d", rate, n)
|
||||
}
|
||||
if rate > 0.0125 {
|
||||
mediocre++
|
||||
} else {
|
||||
good++
|
||||
}
|
||||
}
|
||||
t.Logf("false positive rate: %d good, %d mediocre", good, mediocre)
|
||||
if mediocre > good/5 {
|
||||
t.Error("mediocre false positive rate is more than expected")
|
||||
}
|
||||
}
|
||||
Generated
Vendored
-30
@@ -1,30 +0,0 @@
|
||||
// Copyright (c) 2014, 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 iterator_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
||||
. "github.com/syndtr/goleveldb/leveldb/iterator"
|
||||
"github.com/syndtr/goleveldb/leveldb/testutil"
|
||||
)
|
||||
|
||||
var _ = testutil.Defer(func() {
|
||||
Describe("Array iterator", func() {
|
||||
It("Should iterates and seeks correctly", func() {
|
||||
// Build key/value.
|
||||
kv := testutil.KeyValue_Generate(nil, 70, 1, 5, 3, 3)
|
||||
|
||||
// Test the iterator.
|
||||
t := testutil.IteratorTesting{
|
||||
KeyValue: kv.Clone(),
|
||||
Iter: NewArrayIterator(kv),
|
||||
}
|
||||
testutil.DoIteratorTesting(&t)
|
||||
})
|
||||
})
|
||||
})
|
||||
Generated
Vendored
-83
@@ -1,83 +0,0 @@
|
||||
// Copyright (c) 2014, 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 iterator_test
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb/comparer"
|
||||
. "github.com/syndtr/goleveldb/leveldb/iterator"
|
||||
"github.com/syndtr/goleveldb/leveldb/testutil"
|
||||
)
|
||||
|
||||
type keyValue struct {
|
||||
key []byte
|
||||
testutil.KeyValue
|
||||
}
|
||||
|
||||
type keyValueIndex []keyValue
|
||||
|
||||
func (x keyValueIndex) Search(key []byte) int {
|
||||
return sort.Search(x.Len(), func(i int) bool {
|
||||
return comparer.DefaultComparer.Compare(x[i].key, key) >= 0
|
||||
})
|
||||
}
|
||||
|
||||
func (x keyValueIndex) Len() int { return len(x) }
|
||||
func (x keyValueIndex) Index(i int) (key, value []byte) { return x[i].key, nil }
|
||||
func (x keyValueIndex) Get(i int) Iterator { return NewArrayIterator(x[i]) }
|
||||
|
||||
var _ = testutil.Defer(func() {
|
||||
Describe("Indexed iterator", func() {
|
||||
Test := func(n ...int) func() {
|
||||
if len(n) == 0 {
|
||||
rnd := testutil.NewRand()
|
||||
n = make([]int, rnd.Intn(17)+3)
|
||||
for i := range n {
|
||||
n[i] = rnd.Intn(19) + 1
|
||||
}
|
||||
}
|
||||
|
||||
return func() {
|
||||
It("Should iterates and seeks correctly", func(done Done) {
|
||||
// Build key/value.
|
||||
index := make(keyValueIndex, len(n))
|
||||
sum := 0
|
||||
for _, x := range n {
|
||||
sum += x
|
||||
}
|
||||
kv := testutil.KeyValue_Generate(nil, sum, 1, 10, 4, 4)
|
||||
for i, j := 0, 0; i < len(n); i++ {
|
||||
for x := n[i]; x > 0; x-- {
|
||||
key, value := kv.Index(j)
|
||||
index[i].key = key
|
||||
index[i].Put(key, value)
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
// Test the iterator.
|
||||
t := testutil.IteratorTesting{
|
||||
KeyValue: kv.Clone(),
|
||||
Iter: NewIndexedIterator(NewArrayIndexer(index), true, true),
|
||||
}
|
||||
testutil.DoIteratorTesting(&t)
|
||||
done <- true
|
||||
}, 1.5)
|
||||
}
|
||||
}
|
||||
|
||||
Describe("with 100 keys", Test(100))
|
||||
Describe("with 50-50 keys", Test(50, 50))
|
||||
Describe("with 50-1 keys", Test(50, 1))
|
||||
Describe("with 50-1-50 keys", Test(50, 1, 50))
|
||||
Describe("with 1-50 keys", Test(1, 50))
|
||||
Describe("with random N-keys", Test())
|
||||
})
|
||||
})
|
||||
Generated
Vendored
-17
@@ -1,17 +0,0 @@
|
||||
package iterator_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb/testutil"
|
||||
)
|
||||
|
||||
func TestIterator(t *testing.T) {
|
||||
testutil.RunDefer()
|
||||
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Iterator Suite")
|
||||
}
|
||||
Generated
Vendored
-60
@@ -1,60 +0,0 @@
|
||||
// Copyright (c) 2014, 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 iterator_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb/comparer"
|
||||
. "github.com/syndtr/goleveldb/leveldb/iterator"
|
||||
"github.com/syndtr/goleveldb/leveldb/testutil"
|
||||
)
|
||||
|
||||
var _ = testutil.Defer(func() {
|
||||
Describe("Merged iterator", func() {
|
||||
Test := func(filled int, empty int) func() {
|
||||
return func() {
|
||||
It("Should iterates and seeks correctly", func(done Done) {
|
||||
rnd := testutil.NewRand()
|
||||
|
||||
// Build key/value.
|
||||
filledKV := make([]testutil.KeyValue, filled)
|
||||
kv := testutil.KeyValue_Generate(nil, 100, 1, 10, 4, 4)
|
||||
kv.Iterate(func(i int, key, value []byte) {
|
||||
filledKV[rnd.Intn(filled)].Put(key, value)
|
||||
})
|
||||
|
||||
// Create itearators.
|
||||
iters := make([]Iterator, filled+empty)
|
||||
for i := range iters {
|
||||
if empty == 0 || (rnd.Int()%2 == 0 && filled > 0) {
|
||||
filled--
|
||||
Expect(filledKV[filled].Len()).ShouldNot(BeZero())
|
||||
iters[i] = NewArrayIterator(filledKV[filled])
|
||||
} else {
|
||||
empty--
|
||||
iters[i] = NewEmptyIterator(nil)
|
||||
}
|
||||
}
|
||||
|
||||
// Test the iterator.
|
||||
t := testutil.IteratorTesting{
|
||||
KeyValue: kv.Clone(),
|
||||
Iter: NewMergedIterator(iters, comparer.DefaultComparer, true),
|
||||
}
|
||||
testutil.DoIteratorTesting(&t)
|
||||
done <- true
|
||||
}, 1.5)
|
||||
}
|
||||
}
|
||||
|
||||
Describe("with three, all filled iterators", Test(3, 0))
|
||||
Describe("with one filled, one empty iterators", Test(1, 1))
|
||||
Describe("with one filled, two empty iterators", Test(1, 2))
|
||||
})
|
||||
})
|
||||
Generated
Vendored
-473
@@ -1,473 +0,0 @@
|
||||
// Copyright 2011 The LevelDB-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.
|
||||
|
||||
// Taken from: https://code.google.com/p/leveldb-go/source/browse/leveldb/record/record_test.go?r=df1fa28f7f3be6c3935548169002309c12967135
|
||||
// License, authors and contributors informations can be found at bellow URLs respectively:
|
||||
// https://code.google.com/p/leveldb-go/source/browse/LICENSE
|
||||
// https://code.google.com/p/leveldb-go/source/browse/AUTHORS
|
||||
// https://code.google.com/p/leveldb-go/source/browse/CONTRIBUTORS
|
||||
|
||||
package journal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type dropper struct {
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
func (d dropper) Drop(err error) {
|
||||
d.t.Log(err)
|
||||
}
|
||||
|
||||
func short(s string) string {
|
||||
if len(s) < 64 {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("%s...(skipping %d bytes)...%s", s[:20], len(s)-40, s[len(s)-20:])
|
||||
}
|
||||
|
||||
// big returns a string of length n, composed of repetitions of partial.
|
||||
func big(partial string, n int) string {
|
||||
return strings.Repeat(partial, n/len(partial)+1)[:n]
|
||||
}
|
||||
|
||||
func TestEmpty(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
r := NewReader(buf, dropper{t}, true, true)
|
||||
if _, err := r.Next(); err != io.EOF {
|
||||
t.Fatalf("got %v, want %v", err, io.EOF)
|
||||
}
|
||||
}
|
||||
|
||||
func testGenerator(t *testing.T, reset func(), gen func() (string, bool)) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
reset()
|
||||
w := NewWriter(buf)
|
||||
for {
|
||||
s, ok := gen()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
ww, err := w.Next()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := ww.Write([]byte(s)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
reset()
|
||||
r := NewReader(buf, dropper{t}, true, true)
|
||||
for {
|
||||
s, ok := gen()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
rr, err := r.Next()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
x, err := ioutil.ReadAll(rr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(x) != s {
|
||||
t.Fatalf("got %q, want %q", short(string(x)), short(s))
|
||||
}
|
||||
}
|
||||
if _, err := r.Next(); err != io.EOF {
|
||||
t.Fatalf("got %v, want %v", err, io.EOF)
|
||||
}
|
||||
}
|
||||
|
||||
func testLiterals(t *testing.T, s []string) {
|
||||
var i int
|
||||
reset := func() {
|
||||
i = 0
|
||||
}
|
||||
gen := func() (string, bool) {
|
||||
if i == len(s) {
|
||||
return "", false
|
||||
}
|
||||
i++
|
||||
return s[i-1], true
|
||||
}
|
||||
testGenerator(t, reset, gen)
|
||||
}
|
||||
|
||||
func TestMany(t *testing.T) {
|
||||
const n = 1e5
|
||||
var i int
|
||||
reset := func() {
|
||||
i = 0
|
||||
}
|
||||
gen := func() (string, bool) {
|
||||
if i == n {
|
||||
return "", false
|
||||
}
|
||||
i++
|
||||
return fmt.Sprintf("%d.", i-1), true
|
||||
}
|
||||
testGenerator(t, reset, gen)
|
||||
}
|
||||
|
||||
func TestRandom(t *testing.T) {
|
||||
const n = 1e2
|
||||
var (
|
||||
i int
|
||||
r *rand.Rand
|
||||
)
|
||||
reset := func() {
|
||||
i, r = 0, rand.New(rand.NewSource(0))
|
||||
}
|
||||
gen := func() (string, bool) {
|
||||
if i == n {
|
||||
return "", false
|
||||
}
|
||||
i++
|
||||
return strings.Repeat(string(uint8(i)), r.Intn(2*blockSize+16)), true
|
||||
}
|
||||
testGenerator(t, reset, gen)
|
||||
}
|
||||
|
||||
func TestBasic(t *testing.T) {
|
||||
testLiterals(t, []string{
|
||||
strings.Repeat("a", 1000),
|
||||
strings.Repeat("b", 97270),
|
||||
strings.Repeat("c", 8000),
|
||||
})
|
||||
}
|
||||
|
||||
func TestBoundary(t *testing.T) {
|
||||
for i := blockSize - 16; i < blockSize+16; i++ {
|
||||
s0 := big("abcd", i)
|
||||
for j := blockSize - 16; j < blockSize+16; j++ {
|
||||
s1 := big("ABCDE", j)
|
||||
testLiterals(t, []string{s0, s1})
|
||||
testLiterals(t, []string{s0, "", s1})
|
||||
testLiterals(t, []string{s0, "x", s1})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlush(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
w := NewWriter(buf)
|
||||
// Write a couple of records. Everything should still be held
|
||||
// in the record.Writer buffer, so that buf.Len should be 0.
|
||||
w0, _ := w.Next()
|
||||
w0.Write([]byte("0"))
|
||||
w1, _ := w.Next()
|
||||
w1.Write([]byte("11"))
|
||||
if got, want := buf.Len(), 0; got != want {
|
||||
t.Fatalf("buffer length #0: got %d want %d", got, want)
|
||||
}
|
||||
// Flush the record.Writer buffer, which should yield 17 bytes.
|
||||
// 17 = 2*7 + 1 + 2, which is two headers and 1 + 2 payload bytes.
|
||||
if err := w.Flush(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, want := buf.Len(), 17; got != want {
|
||||
t.Fatalf("buffer length #1: got %d want %d", got, want)
|
||||
}
|
||||
// Do another write, one that isn't large enough to complete the block.
|
||||
// The write should not have flowed through to buf.
|
||||
w2, _ := w.Next()
|
||||
w2.Write(bytes.Repeat([]byte("2"), 10000))
|
||||
if got, want := buf.Len(), 17; got != want {
|
||||
t.Fatalf("buffer length #2: got %d want %d", got, want)
|
||||
}
|
||||
// Flushing should get us up to 10024 bytes written.
|
||||
// 10024 = 17 + 7 + 10000.
|
||||
if err := w.Flush(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, want := buf.Len(), 10024; got != want {
|
||||
t.Fatalf("buffer length #3: got %d want %d", got, want)
|
||||
}
|
||||
// Do a bigger write, one that completes the current block.
|
||||
// We should now have 32768 bytes (a complete block), without
|
||||
// an explicit flush.
|
||||
w3, _ := w.Next()
|
||||
w3.Write(bytes.Repeat([]byte("3"), 40000))
|
||||
if got, want := buf.Len(), 32768; got != want {
|
||||
t.Fatalf("buffer length #4: got %d want %d", got, want)
|
||||
}
|
||||
// Flushing should get us up to 50038 bytes written.
|
||||
// 50038 = 10024 + 2*7 + 40000. There are two headers because
|
||||
// the one record was split into two chunks.
|
||||
if err := w.Flush(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, want := buf.Len(), 50038; got != want {
|
||||
t.Fatalf("buffer length #5: got %d want %d", got, want)
|
||||
}
|
||||
// Check that reading those records give the right lengths.
|
||||
r := NewReader(buf, dropper{t}, true, true)
|
||||
wants := []int64{1, 2, 10000, 40000}
|
||||
for i, want := range wants {
|
||||
rr, _ := r.Next()
|
||||
n, err := io.Copy(ioutil.Discard, rr)
|
||||
if err != nil {
|
||||
t.Fatalf("read #%d: %v", i, err)
|
||||
}
|
||||
if n != want {
|
||||
t.Fatalf("read #%d: got %d bytes want %d", i, n, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonExhaustiveRead(t *testing.T) {
|
||||
const n = 100
|
||||
buf := new(bytes.Buffer)
|
||||
p := make([]byte, 10)
|
||||
rnd := rand.New(rand.NewSource(1))
|
||||
|
||||
w := NewWriter(buf)
|
||||
for i := 0; i < n; i++ {
|
||||
length := len(p) + rnd.Intn(3*blockSize)
|
||||
s := string(uint8(i)) + "123456789abcdefgh"
|
||||
ww, _ := w.Next()
|
||||
ww.Write([]byte(big(s, length)))
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r := NewReader(buf, dropper{t}, true, true)
|
||||
for i := 0; i < n; i++ {
|
||||
rr, _ := r.Next()
|
||||
_, err := io.ReadFull(rr, p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := string(uint8(i)) + "123456789"
|
||||
if got := string(p); got != want {
|
||||
t.Fatalf("read #%d: got %q want %q", i, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStaleReader(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
w := NewWriter(buf)
|
||||
w0, err := w.Next()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
w0.Write([]byte("0"))
|
||||
w1, err := w.Next()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
w1.Write([]byte("11"))
|
||||
if err := w.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r := NewReader(buf, dropper{t}, true, true)
|
||||
r0, err := r.Next()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
r1, err := r.Next()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
p := make([]byte, 1)
|
||||
if _, err := r0.Read(p); err == nil || !strings.Contains(err.Error(), "stale") {
|
||||
t.Fatalf("stale read #0: unexpected error: %v", err)
|
||||
}
|
||||
if _, err := r1.Read(p); err != nil {
|
||||
t.Fatalf("fresh read #1: got %v want nil error", err)
|
||||
}
|
||||
if p[0] != '1' {
|
||||
t.Fatalf("fresh read #1: byte contents: got '%c' want '1'", p[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestStaleWriter(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
w := NewWriter(buf)
|
||||
w0, err := w.Next()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
w1, err := w.Next()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := w0.Write([]byte("0")); err == nil || !strings.Contains(err.Error(), "stale") {
|
||||
t.Fatalf("stale write #0: unexpected error: %v", err)
|
||||
}
|
||||
if _, err := w1.Write([]byte("11")); err != nil {
|
||||
t.Fatalf("fresh write #1: got %v want nil error", err)
|
||||
}
|
||||
if err := w.Flush(); err != nil {
|
||||
t.Fatalf("flush: %v", err)
|
||||
}
|
||||
if _, err := w1.Write([]byte("0")); err == nil || !strings.Contains(err.Error(), "stale") {
|
||||
t.Fatalf("stale write #1: unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCorrupt_MissingLastBlock(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
w := NewWriter(buf)
|
||||
|
||||
// First record.
|
||||
ww, err := w.Next()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := ww.Write(bytes.Repeat([]byte("0"), blockSize-1024)); err != nil {
|
||||
t.Fatalf("write #0: unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Second record.
|
||||
ww, err = w.Next()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := ww.Write(bytes.Repeat([]byte("0"), blockSize-headerSize)); err != nil {
|
||||
t.Fatalf("write #1: unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if err := w.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Cut the last block.
|
||||
b := buf.Bytes()[:blockSize]
|
||||
r := NewReader(bytes.NewReader(b), dropper{t}, false, true)
|
||||
|
||||
// First read.
|
||||
rr, err := r.Next()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
n, err := io.Copy(ioutil.Discard, rr)
|
||||
if err != nil {
|
||||
t.Fatalf("read #0: %v", err)
|
||||
}
|
||||
if n != blockSize-1024 {
|
||||
t.Fatalf("read #0: got %d bytes want %d", n, blockSize-1024)
|
||||
}
|
||||
|
||||
// Second read.
|
||||
rr, err = r.Next()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
n, err = io.Copy(ioutil.Discard, rr)
|
||||
if err != io.ErrUnexpectedEOF {
|
||||
t.Fatalf("read #1: unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCorrupt_CorruptedMiddleBlock(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
w := NewWriter(buf)
|
||||
|
||||
// First record.
|
||||
ww, err := w.Next()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := ww.Write(bytes.Repeat([]byte("0"), blockSize/2)); err != nil {
|
||||
t.Fatalf("write #0: unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Second record.
|
||||
ww, err = w.Next()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := ww.Write(bytes.Repeat([]byte("0"), blockSize-headerSize)); err != nil {
|
||||
t.Fatalf("write #1: unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Third record.
|
||||
ww, err = w.Next()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := ww.Write(bytes.Repeat([]byte("0"), (blockSize-headerSize)+1)); err != nil {
|
||||
t.Fatalf("write #2: unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Fourth record.
|
||||
ww, err = w.Next()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := ww.Write(bytes.Repeat([]byte("0"), (blockSize-headerSize)+2)); err != nil {
|
||||
t.Fatalf("write #3: unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if err := w.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
b := buf.Bytes()
|
||||
// Corrupting block #1.
|
||||
for i := 0; i < 1024; i++ {
|
||||
b[blockSize+i] = '1'
|
||||
}
|
||||
|
||||
r := NewReader(bytes.NewReader(b), dropper{t}, false, true)
|
||||
|
||||
// First read.
|
||||
rr, err := r.Next()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
n, err := io.Copy(ioutil.Discard, rr)
|
||||
if err != nil {
|
||||
t.Fatalf("read #0: %v", err)
|
||||
}
|
||||
if want := int64(blockSize / 2); n != want {
|
||||
t.Fatalf("read #0: got %d bytes want %d", n, want)
|
||||
}
|
||||
|
||||
// Second read.
|
||||
rr, err = r.Next()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
n, err = io.Copy(ioutil.Discard, rr)
|
||||
if err != io.ErrUnexpectedEOF {
|
||||
t.Fatalf("read #1: unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Third read.
|
||||
rr, err = r.Next()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
n, err = io.Copy(ioutil.Discard, rr)
|
||||
if err != nil {
|
||||
t.Fatalf("read #2: %v", err)
|
||||
}
|
||||
if want := int64(blockSize-headerSize) + 2; n != want {
|
||||
t.Fatalf("read #2: got %d bytes want %d", n, want)
|
||||
}
|
||||
}
|
||||
-139
@@ -1,139 +0,0 @@
|
||||
// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
|
||||
// All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package leveldb
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type vType int
|
||||
|
||||
func (t vType) String() string {
|
||||
switch t {
|
||||
case tDel:
|
||||
return "d"
|
||||
case tVal:
|
||||
return "v"
|
||||
}
|
||||
return "x"
|
||||
}
|
||||
|
||||
// Value types encoded as the last component of internal keys.
|
||||
// Don't modify; this value are saved to disk.
|
||||
const (
|
||||
tDel vType = iota
|
||||
tVal
|
||||
)
|
||||
|
||||
// tSeek defines the vType that should be passed when constructing an
|
||||
// internal key for seeking to a particular sequence number (since we
|
||||
// sort sequence numbers in decreasing order and the value type is
|
||||
// embedded as the low 8 bits in the sequence number in internal keys,
|
||||
// we need to use the highest-numbered ValueType, not the lowest).
|
||||
const tSeek = tVal
|
||||
|
||||
const (
|
||||
// Maximum value possible for sequence number; the 8-bits are
|
||||
// used by value type, so its can packed together in single
|
||||
// 64-bit integer.
|
||||
kMaxSeq uint64 = (uint64(1) << 56) - 1
|
||||
// Maximum value possible for packed sequence number and type.
|
||||
kMaxNum uint64 = (kMaxSeq << 8) | uint64(tSeek)
|
||||
)
|
||||
|
||||
// Maximum number encoded in bytes.
|
||||
var kMaxNumBytes = make([]byte, 8)
|
||||
|
||||
func init() {
|
||||
binary.LittleEndian.PutUint64(kMaxNumBytes, kMaxNum)
|
||||
}
|
||||
|
||||
type iKey []byte
|
||||
|
||||
func newIKey(ukey []byte, seq uint64, t vType) iKey {
|
||||
if seq > kMaxSeq || t > tVal {
|
||||
panic("invalid seq number or value type")
|
||||
}
|
||||
|
||||
b := make(iKey, len(ukey)+8)
|
||||
copy(b, ukey)
|
||||
binary.LittleEndian.PutUint64(b[len(ukey):], (seq<<8)|uint64(t))
|
||||
return b
|
||||
}
|
||||
|
||||
func parseIkey(p []byte) (ukey []byte, seq uint64, t vType, ok bool) {
|
||||
if len(p) < 8 {
|
||||
return
|
||||
}
|
||||
num := binary.LittleEndian.Uint64(p[len(p)-8:])
|
||||
seq, t = uint64(num>>8), vType(num&0xff)
|
||||
if t > tVal {
|
||||
return
|
||||
}
|
||||
ukey = p[:len(p)-8]
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
func validIkey(p []byte) bool {
|
||||
_, _, _, ok := parseIkey(p)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (p iKey) assert() {
|
||||
if p == nil {
|
||||
panic("nil iKey")
|
||||
}
|
||||
if len(p) < 8 {
|
||||
panic(fmt.Sprintf("invalid iKey %q, len=%d", []byte(p), len(p)))
|
||||
}
|
||||
}
|
||||
|
||||
func (p iKey) ok() bool {
|
||||
if len(p) < 8 {
|
||||
return false
|
||||
}
|
||||
_, _, ok := p.parseNum()
|
||||
return ok
|
||||
}
|
||||
|
||||
func (p iKey) ukey() []byte {
|
||||
p.assert()
|
||||
return p[:len(p)-8]
|
||||
}
|
||||
|
||||
func (p iKey) num() uint64 {
|
||||
p.assert()
|
||||
return binary.LittleEndian.Uint64(p[len(p)-8:])
|
||||
}
|
||||
|
||||
func (p iKey) parseNum() (seq uint64, t vType, ok bool) {
|
||||
if p == nil {
|
||||
panic("nil iKey")
|
||||
}
|
||||
if len(p) < 8 {
|
||||
return
|
||||
}
|
||||
num := p.num()
|
||||
seq, t = uint64(num>>8), vType(num&0xff)
|
||||
if t > tVal {
|
||||
return 0, 0, false
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
func (p iKey) String() string {
|
||||
if len(p) == 0 {
|
||||
return "<nil>"
|
||||
}
|
||||
if seq, t, ok := p.parseNum(); ok {
|
||||
return fmt.Sprintf("%s,%s%d", shorten(string(p.ukey())), t, seq)
|
||||
}
|
||||
return "<invalid>"
|
||||
}
|
||||
-123
@@ -1,123 +0,0 @@
|
||||
// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
|
||||
// All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package leveldb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb/comparer"
|
||||
)
|
||||
|
||||
var defaultIComparer = &iComparer{comparer.DefaultComparer}
|
||||
|
||||
func ikey(key string, seq uint64, t vType) iKey {
|
||||
return newIKey([]byte(key), uint64(seq), t)
|
||||
}
|
||||
|
||||
func shortSep(a, b []byte) []byte {
|
||||
dst := make([]byte, len(a))
|
||||
dst = defaultIComparer.Separator(dst[:0], a, b)
|
||||
if dst == nil {
|
||||
return a
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func shortSuccessor(b []byte) []byte {
|
||||
dst := make([]byte, len(b))
|
||||
dst = defaultIComparer.Successor(dst[:0], b)
|
||||
if dst == nil {
|
||||
return b
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func testSingleKey(t *testing.T, key string, seq uint64, vt vType) {
|
||||
ik := ikey(key, seq, vt)
|
||||
|
||||
if !bytes.Equal(ik.ukey(), []byte(key)) {
|
||||
t.Errorf("user key does not equal, got %v, want %v", string(ik.ukey()), key)
|
||||
}
|
||||
|
||||
if rseq, rt, ok := ik.parseNum(); ok {
|
||||
if rseq != seq {
|
||||
t.Errorf("seq number does not equal, got %v, want %v", rseq, seq)
|
||||
}
|
||||
|
||||
if rt != vt {
|
||||
t.Errorf("type does not equal, got %v, want %v", rt, vt)
|
||||
}
|
||||
} else {
|
||||
t.Error("cannot parse seq and type")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIKey_EncodeDecode(t *testing.T) {
|
||||
keys := []string{"", "k", "hello", "longggggggggggggggggggggg"}
|
||||
seqs := []uint64{
|
||||
1, 2, 3,
|
||||
(1 << 8) - 1, 1 << 8, (1 << 8) + 1,
|
||||
(1 << 16) - 1, 1 << 16, (1 << 16) + 1,
|
||||
(1 << 32) - 1, 1 << 32, (1 << 32) + 1,
|
||||
}
|
||||
for _, key := range keys {
|
||||
for _, seq := range seqs {
|
||||
testSingleKey(t, key, seq, tVal)
|
||||
testSingleKey(t, "hello", 1, tDel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func assertBytes(t *testing.T, want, got []byte) {
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("assert failed, got %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIKeyShortSeparator(t *testing.T) {
|
||||
// When user keys are same
|
||||
assertBytes(t, ikey("foo", 100, tVal),
|
||||
shortSep(ikey("foo", 100, tVal),
|
||||
ikey("foo", 99, tVal)))
|
||||
assertBytes(t, ikey("foo", 100, tVal),
|
||||
shortSep(ikey("foo", 100, tVal),
|
||||
ikey("foo", 101, tVal)))
|
||||
assertBytes(t, ikey("foo", 100, tVal),
|
||||
shortSep(ikey("foo", 100, tVal),
|
||||
ikey("foo", 100, tVal)))
|
||||
assertBytes(t, ikey("foo", 100, tVal),
|
||||
shortSep(ikey("foo", 100, tVal),
|
||||
ikey("foo", 100, tDel)))
|
||||
|
||||
// When user keys are misordered
|
||||
assertBytes(t, ikey("foo", 100, tVal),
|
||||
shortSep(ikey("foo", 100, tVal),
|
||||
ikey("bar", 99, tVal)))
|
||||
|
||||
// When user keys are different, but correctly ordered
|
||||
assertBytes(t, ikey("g", uint64(kMaxSeq), tSeek),
|
||||
shortSep(ikey("foo", 100, tVal),
|
||||
ikey("hello", 200, tVal)))
|
||||
|
||||
// When start user key is prefix of limit user key
|
||||
assertBytes(t, ikey("foo", 100, tVal),
|
||||
shortSep(ikey("foo", 100, tVal),
|
||||
ikey("foobar", 200, tVal)))
|
||||
|
||||
// When limit user key is prefix of start user key
|
||||
assertBytes(t, ikey("foobar", 100, tVal),
|
||||
shortSep(ikey("foobar", 100, tVal),
|
||||
ikey("foo", 200, tVal)))
|
||||
}
|
||||
|
||||
func TestIKeyShortestSuccessor(t *testing.T) {
|
||||
assertBytes(t, ikey("g", uint64(kMaxSeq), tSeek),
|
||||
shortSuccessor(ikey("foo", 100, tVal)))
|
||||
assertBytes(t, ikey("\xff\xff", 100, tVal),
|
||||
shortSuccessor(ikey("\xff\xff", 100, tVal)))
|
||||
}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package leveldb
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb/testutil"
|
||||
)
|
||||
|
||||
func TestLeveldb(t *testing.T) {
|
||||
testutil.RunDefer()
|
||||
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Leveldb Suite")
|
||||
|
||||
RegisterTestingT(t)
|
||||
testutil.RunDefer("teardown")
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user