go 1.17
require (
- crawshaw.io/sqlite v0.3.3-0.20210127221821-98b1f83c5508
github.com/RoaringBitmap/roaring v0.9.4
github.com/alexflint/go-arg v1.4.2
github.com/anacrolix/args v0.4.1-0.20211104085705-59f0fe94eb8f
github.com/anacrolix/missinggo/perf v1.0.0
github.com/anacrolix/missinggo/v2 v2.5.2
github.com/anacrolix/multiless v0.2.0
- github.com/anacrolix/squirrel v0.1.1-0.20210914065657-81bc5ecdc43a
+ github.com/anacrolix/squirrel v0.2.1-0.20211119092713-2efaee06d169
github.com/anacrolix/sync v0.4.0
github.com/anacrolix/tagflag v1.3.0
github.com/anacrolix/upnp v0.1.2-0.20200416075019-5e9378ed1425
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect
golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac
+ zombiezen.com/go/sqlite v0.8.0
)
-require zombiezen.com/go/sqlite v0.8.0
-
require (
github.com/alexflint/go-scalar v1.1.0 // indirect
github.com/anacrolix/mmsg v1.0.0 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
- modernc.org/libc v1.11.3 // indirect
+ modernc.org/libc v1.11.82 // indirect
modernc.org/mathutil v1.4.1 // indirect
modernc.org/memory v1.0.5 // indirect
- modernc.org/sqlite v1.13.0 // indirect
+ // https://gitlab.com/cznic/sqlite/-/issues/77#note_744477407
+ modernc.org/sqlite v1.14.2-0.20211125151325-d4ed92c0a70f // indirect
)
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
-crawshaw.io/iox v0.0.0-20181124134642-c51c3df30797 h1:yDf7ARQc637HoxDho7xjqdvO5ZA2Yb+xzv/fOnnvZzw=
crawshaw.io/iox v0.0.0-20181124134642-c51c3df30797/go.mod h1:sXBiorCo8c46JlQV3oXPKINnZ8mcqnye1EkVkqsectk=
crawshaw.io/sqlite v0.3.2/go.mod h1:igAO5JulrQ1DbdZdtVq48mnZUBAPOeFzer7VhDWNtW4=
-crawshaw.io/sqlite v0.3.3-0.20210127221821-98b1f83c5508 h1:fILCBBFnjnrQ0whVJlGhfv1E/QiaFDNtGFBObEVRnYg=
crawshaw.io/sqlite v0.3.3-0.20210127221821-98b1f83c5508/go.mod h1:igAO5JulrQ1DbdZdtVq48mnZUBAPOeFzer7VhDWNtW4=
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/anacrolix/multiless v0.2.0/go.mod h1:TrCLEZfIDbMVfLoQt5tOoiBS/uq4y8+ojuEVVvTNPX4=
github.com/anacrolix/publicip v0.2.0/go.mod h1:67G1lVkLo8UjdEcJkwScWVTvlJ35OCDsRJoWXl/wi4g=
github.com/anacrolix/squirrel v0.1.0/go.mod h1:YzgVvikMdFD441oTWlNG189bpKabO9Sbf3uCSVgca04=
-github.com/anacrolix/squirrel v0.1.1-0.20210914065657-81bc5ecdc43a h1:8LAUQgDPqnzuF/WrGQzTY6i+bVO/FpA90Hi6jXA+2vQ=
github.com/anacrolix/squirrel v0.1.1-0.20210914065657-81bc5ecdc43a/go.mod h1:YzgVvikMdFD441oTWlNG189bpKabO9Sbf3uCSVgca04=
+github.com/anacrolix/squirrel v0.2.1-0.20211119092713-2efaee06d169 h1:OFAU76N1th2tW6EGi/FvT7EYP+xcpgN9ZqA8/rE09/0=
+github.com/anacrolix/squirrel v0.2.1-0.20211119092713-2efaee06d169/go.mod h1:dJyE7VefQvX0KAKMkOQDGOVEs91a+LvXQ2BjEKl/DIw=
github.com/anacrolix/stm v0.1.0/go.mod h1:ZKz7e7ERWvP0KgL7WXfRjBXHNRhlVRlbBQecqFtPq+A=
github.com/anacrolix/stm v0.1.1-0.20191106051447-e749ba3531cf/go.mod h1:zoVQRvSiGjGoTmbM0vSLIiaKjWtNPeTvXUSdJQA4hsg=
github.com/anacrolix/stm v0.2.0/go.mod h1:zoVQRvSiGjGoTmbM0vSLIiaKjWtNPeTvXUSdJQA4hsg=
github.com/mattn/go-sqlite3 v1.7.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.13.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
+github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v2.0.2+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 h1:SeSEfdIxyvwGJliREIJhRPPXvW6sDlLT+UQ3B0hD0NA=
golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
+modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878=
modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/ccgo/v3 v3.9.0/go.mod h1:nQbgkn8mwzPdp4mm6BT6+p85ugQ7FrGgIcYaE7nSrpY=
modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60=
modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw=
modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI=
modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag=
modernc.org/ccgo/v3 v3.11.2/go.mod h1:6kii3AptTDI+nUrM9RFBoIEUEisSWCbdczD9ZwQH2FE=
+modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw=
+modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ=
+modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c=
+modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo=
+modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg=
+modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I=
+modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs=
+modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8=
+modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE=
+modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk=
+modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w=
+modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE=
+modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8=
+modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc=
+modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU=
+modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE=
+modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk=
+modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI=
+modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE=
+modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg=
+modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74=
+modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU=
+modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU=
+modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc=
+modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM=
+modernc.org/ccgo/v3 v3.12.65/go.mod h1:D6hQtKxPNZiY6wDBtehSGKFKmyXn53F8nGTpH+POmS4=
+modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ=
+modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84=
+modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
+modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
+modernc.org/libc v1.8.0/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q=
modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg=
modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M=
-modernc.org/libc v1.11.3 h1:q//spBhqp23lC/if8/o8hlyET57P8mCZqrqftzT2WmY=
modernc.org/libc v1.11.3/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU=
+modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU=
+modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE=
+modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso=
+modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8=
+modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8=
+modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I=
+modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk=
+modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY=
+modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE=
+modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg=
+modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM=
+modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg=
+modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo=
+modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8=
+modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ=
+modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA=
+modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM=
+modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg=
+modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE=
+modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM=
+modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU=
+modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw=
+modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M=
+modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18=
+modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8=
+modernc.org/libc v1.11.70/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw=
+modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw=
+modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0=
+modernc.org/libc v1.11.82 h1:CSl/6n4odvPYWKKqBtFb8e0ZWVTjxDqwxTjaoee9V7E=
+modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI=
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14=
modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
-modernc.org/sqlite v1.13.0 h1:cwhUj0jTBgPjk/demWheV+T6xi6ifTfsGIFKFq0g3Ck=
+modernc.org/sqlite v1.10.0/go.mod h1:PGzq6qlhyYjL6uVbSgS6WoF7ZopTW/sI7+7p+mb4ZVU=
modernc.org/sqlite v1.13.0/go.mod h1:2qO/6jZJrcQaxFUHxOwa6Q6WfiGSsiVj6GXX0Ker+Jg=
+modernc.org/sqlite v1.14.2-0.20211125151325-d4ed92c0a70f h1:yQwkmqKCIgLzFIfjfPfZAAxLZernckpo7zGTv37Ahv0=
+modernc.org/sqlite v1.14.2-0.20211125151325-d4ed92c0a70f/go.mod h1:YT5XFRKOueohjppHO4cHb54eQlnaUGsZMHoryaCpNo4=
+modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
+modernc.org/tcl v1.5.0/go.mod h1:gb57hj4pO8fRrK54zveIfFXBaMHK3SKJNWcmRw1cRzc=
modernc.org/tcl v1.5.9/go.mod h1:bcwjvBJ2u0exY6K35eAmxXBBij5kXb1dHlAWmfhqThE=
+modernc.org/tcl v1.8.13/go.mod h1:V+q/Ef0IJaNUSECieLU4o+8IScapxnMyFV6i/7uQlAY=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
+modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
+modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
modernc.org/z v1.1.2/go.mod h1:sj9T1AGBG0dm6SCVzldPOHWrif6XBpooJtbttMn1+Js=
+modernc.org/z v1.2.19/go.mod h1:+ZpP0pc4zz97eukOzW3xagV/lS82IpPN9NGG5pNF9vY=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
+zombiezen.com/go/sqlite v0.2.0/go.mod h1:VyBqNtpcF4vdvYgdwTSHJlwxyvTYCDQAZM9/qmGPyLg=
zombiezen.com/go/sqlite v0.8.0 h1:fgbFUVLlkDnrNjWV4M28QbHjHvNMCoKjDMWiUYs0R2g=
zombiezen.com/go/sqlite v0.8.0/go.mod h1:EMNzBZwTS5Yg6nwujgJdEo0brNm2a6f8Y4zoGiWZ5RU=
func (c *Peer) peerHasWantedPieces() bool {
if all, _ := c.peerHasAllPieces(); all {
- return !c.t.haveAllPieces()
+ return !c.t.haveAllPieces() && !c.t._pendingPieces.IsEmpty()
}
if !c.t.haveInfo() {
return !c.peerPieces().IsEmpty()
package torrent
+import (
+ rbm "github.com/RoaringBitmap/roaring"
+ roaring "github.com/RoaringBitmap/roaring/BitSliceIndexing"
+)
+
type pendingRequests struct {
- m []int
+ m *roaring.BSI
}
func (p *pendingRequests) Dec(r RequestIndex) {
- prev := p.m[r]
+ _r := uint64(r)
+ prev, _ := p.m.GetValue(_r)
if prev <= 0 {
panic(prev)
}
- p.m[r]--
+ p.m.SetValue(_r, prev-1)
}
func (p *pendingRequests) Inc(r RequestIndex) {
- p.m[r]++
+ _r := uint64(r)
+ prev, _ := p.m.GetValue(_r)
+ p.m.SetValue(_r, prev+1)
}
func (p *pendingRequests) Init(maxIndex RequestIndex) {
- p.m = make([]int, maxIndex)
+ p.m = roaring.NewDefaultBSI()
+}
+
+var allBits rbm.Bitmap
+
+func init() {
+ allBits.AddRange(0, rbm.MaxRange)
}
func (p *pendingRequests) AssertEmpty() {
- for _, count := range p.m {
- if count != 0 {
- panic(count)
- }
+ if p.m == nil {
+ panic(p.m)
+ }
+ sum, _ := p.m.Sum(&allBits)
+ if sum != 0 {
+ panic(sum)
}
}
func (p *pendingRequests) Get(r RequestIndex) int {
- return p.m[r]
+ count, _ := p.m.GetValue(uint64(r))
+ return int(count)
}
package torrent
-import (
- "testing"
-
- qt "github.com/frankban/quicktest"
- "github.com/google/go-cmp/cmp"
-)
-
-// Ensure that cmp.Diff will detect errors as required.
-func TestPendingRequestsDiff(t *testing.T) {
- var a, b pendingRequests
- c := qt.New(t)
- diff := func() string { return cmp.Diff(a.m, b.m) }
- c.Check(diff(), qt.ContentEquals, "")
- a.m = []int{1, 3}
- b.m = []int{1, 2, 3}
- c.Check(diff(), qt.Not(qt.Equals), "")
-}
+// // Ensure that cmp.Diff will detect errors as required.
+// func TestPendingRequestsDiff(t *testing.T) {
+// var a, b pendingRequests
+// c := qt.New(t)
+// diff := func() string { return cmp.Diff(a.m, b.m) }
+// c.Check(diff(), qt.ContentEquals, "")
+// a.m = []int{1, 3}
+// b.m = []int{1, 2, 3}
+// c.Check(diff(), qt.Not(qt.Equals), "")
+// }
import (
"bytes"
- "fmt"
"sort"
"sync"
"github.com/anacrolix/multiless"
- "github.com/anacrolix/torrent/metainfo"
"github.com/anacrolix/torrent/storage"
"github.com/anacrolix/torrent/types"
MaxUnverifiedBytes int64
}
-// TODO: We could do metainfo requests here.
-func Run(input Input) map[PeerId]PeerNextRequestState {
- var requestPieces []requestablePiece
- GetRequestablePieces(input, func(t *Torrent, piece *Piece, pieceIndex int) {
- requestPieces = append(requestPieces, requestablePiece{
- index: pieceIndex,
- t: t,
- NumPendingChunks: piece.NumPendingChunks,
- IterPendingChunks: piece.iterPendingChunksWrapper,
- alwaysReallocate: piece.Priority >= types.PiecePriorityNext,
- })
- })
- torrents := input.Torrents
- allPeers := make(map[metainfo.Hash][]*requestsPeer, len(torrents))
- for _, t := range torrents {
- peers := make([]*requestsPeer, 0, len(t.Peers))
- for _, p := range t.Peers {
- peers = append(peers, &requestsPeer{
- Peer: p,
- })
- }
- allPeers[t.InfoHash] = peers
- }
- for _, piece := range requestPieces {
- for _, peer := range allPeers[piece.t.InfoHash] {
- if peer.canRequestPiece(piece.index) {
- peer.requestablePiecesRemaining++
- }
- }
- }
- for _, piece := range requestPieces {
- allocatePendingChunks(piece, allPeers[piece.t.InfoHash])
- }
- ret := make(map[PeerId]PeerNextRequestState)
- for _, peers := range allPeers {
- for _, rp := range peers {
- if rp.requestablePiecesRemaining != 0 {
- panic(rp.requestablePiecesRemaining)
- }
- if _, ok := ret[rp.Id]; ok {
- panic(fmt.Sprintf("duplicate peer id: %v", rp.Id))
- }
- ret[rp.Id] = rp.nextState
- }
- }
- return ret
-}
-
// Checks that a sorted peersForPiece slice makes sense.
func ensureValidSortedPeersForPieceRequests(peers *peersForPieceSorter) {
if !sort.IsSorted(peers) {
import (
"encoding/gob"
- "math"
"testing"
"github.com/RoaringBitmap/roaring"
return
}()
-func TestStealingFromSlowerPeer(t *testing.T) {
- c := qt.New(t)
- basePeer := Peer{
- MaxRequests: math.MaxInt16,
- DownloadRate: 2,
- }
- basePeer.Pieces.Add(0)
- // Slower than the stealers, but has all requests already.
- stealee := basePeer
- stealee.DownloadRate = 1
- stealee.ExistingRequests = hasAllRequests
- stealee.Id = intPeerId(1)
- firstStealer := basePeer
- firstStealer.Id = intPeerId(2)
- secondStealer := basePeer
- secondStealer.Id = intPeerId(3)
- results := Run(Input{Torrents: []Torrent{{
- ChunksPerPiece: 9,
- Pieces: []Piece{{
- Request: true,
- NumPendingChunks: 5,
- IterPendingChunks: chunkIterRange(5),
- }},
- Peers: []Peer{
- stealee,
- firstStealer,
- secondStealer,
- },
- }}})
-
- c.Assert(results, qt.HasLen, 3)
- check := func(p PeerId, l uint64) {
- addressableBm := results[p].Requests
- c.Check(addressableBm.GetCardinality(), qt.ContentEquals, l)
- c.Check(results[p].Interested, qt.Equals, l > 0)
- }
- check(stealee.Id, 1)
- check(firstStealer.Id, 2)
- check(secondStealer.Id, 2)
-}
-
func checkNumRequestsAndInterest(c *qt.C, next PeerNextRequestState, num uint64, interest bool) {
addressableBm := next.Requests
c.Check(addressableBm.GetCardinality(), qt.ContentEquals, num)
c.Check(next.Interested, qt.Equals, interest)
}
-func TestStealingFromSlowerPeersBasic(t *testing.T) {
- c := qt.New(t)
- basePeer := Peer{
- MaxRequests: math.MaxInt16,
- DownloadRate: 2,
- }
- basePeer.Pieces.Add(0)
- stealee := basePeer
- stealee.DownloadRate = 1
- stealee.ExistingRequests = hasAllRequests
- stealee.Id = intPeerId(1)
- firstStealer := basePeer
- firstStealer.Id = intPeerId(2)
- secondStealer := basePeer
- secondStealer.Id = intPeerId(3)
- results := Run(Input{Torrents: []Torrent{{
- ChunksPerPiece: 9,
- Pieces: []Piece{{
- Request: true,
- NumPendingChunks: 2,
- IterPendingChunks: chunkIter(0, 1),
- }},
- Peers: []Peer{
- stealee,
- firstStealer,
- secondStealer,
- },
- }}})
-
- checkNumRequestsAndInterest(c, results[firstStealer.Id], 1, true)
- checkNumRequestsAndInterest(c, results[secondStealer.Id], 1, true)
- checkNumRequestsAndInterest(c, results[stealee.Id], 0, false)
-}
-
func checkResultsRequestsLen(t *testing.T, reqs roaring.Bitmap, l uint64) {
qt.Check(t, reqs.GetCardinality(), qt.Equals, l)
}
-func TestPeerKeepsExistingIfReasonable(t *testing.T) {
- c := qt.New(t)
- basePeer := Peer{
- MaxRequests: math.MaxInt16,
- DownloadRate: 2,
- }
- basePeer.Pieces.Add(0)
- // Slower than the stealers, but has all requests already.
- stealee := basePeer
- stealee.DownloadRate = 1
- keepReq := RequestIndex(0)
- stealee.ExistingRequests = requestSetFromSlice(keepReq)
- stealee.Id = intPeerId(1)
- firstStealer := basePeer
- firstStealer.Id = intPeerId(2)
- secondStealer := basePeer
- secondStealer.Id = intPeerId(3)
- results := Run(Input{Torrents: []Torrent{{
- ChunksPerPiece: 9,
- Pieces: []Piece{{
- Request: true,
- NumPendingChunks: 4,
- IterPendingChunks: chunkIter(0, 1, 3, 4),
- }},
- Peers: []Peer{
- stealee,
- firstStealer,
- secondStealer,
- },
- }}})
-
- c.Assert(results, qt.HasLen, 3)
- check := func(p PeerId, l uint64) {
- checkResultsRequestsLen(t, results[p].Requests, l)
- c.Check(results[p].Interested, qt.Equals, l > 0)
- }
- check(firstStealer.Id, 2)
- check(secondStealer.Id, 1)
- c.Check(
- results[stealee.Id],
- peerNextRequestStateChecker,
- PeerNextRequestState{
- Interested: true,
- Requests: requestSetFromSlice(keepReq),
- },
- )
-}
-
var peerNextRequestStateChecker = qt.CmpEquals(
cmp.Transformer(
"bitmap",
func(bm roaring.Bitmap) []uint32 {
return bm.ToArray()
}))
-
-func TestDontStealUnnecessarily(t *testing.T) {
- c := qt.New(t)
- basePeer := Peer{
- MaxRequests: math.MaxInt16,
- DownloadRate: 2,
- }
- basePeer.Pieces.AddRange(0, 5)
- // Slower than the stealers, but has all requests already.
- stealee := basePeer
- stealee.DownloadRate = 1
- r := func(i, c RequestIndex) RequestIndex {
- return i*9 + c
- }
- keepReqs := requestSetFromSlice(
- r(3, 2), r(3, 4), r(3, 6), r(3, 8),
- r(4, 0), r(4, 1), r(4, 7), r(4, 8))
- stealee.ExistingRequests = keepReqs
- stealee.Id = intPeerId(1)
- firstStealer := basePeer
- firstStealer.Id = intPeerId(2)
- secondStealer := basePeer
- secondStealer.Id = intPeerId(3)
- secondStealer.Pieces = roaring.Bitmap{}
- secondStealer.Pieces.Add(1)
- secondStealer.Pieces.Add(3)
- results := Run(Input{Torrents: []Torrent{{
- ChunksPerPiece: 9,
- Pieces: []Piece{
- {
- Request: true,
- NumPendingChunks: 0,
- IterPendingChunks: chunkIterRange(9),
- },
- {
- Request: true,
- NumPendingChunks: 7,
- IterPendingChunks: chunkIterRange(7),
- },
- {
- Request: true,
- NumPendingChunks: 0,
- IterPendingChunks: chunkIterRange(0),
- },
- {
- Request: true,
- NumPendingChunks: 9,
- IterPendingChunks: chunkIterRange(9),
- },
- {
- Request: true,
- NumPendingChunks: 9,
- IterPendingChunks: chunkIterRange(9),
- },
- },
- Peers: []Peer{
- firstStealer,
- stealee,
- secondStealer,
- },
- }}})
-
- c.Assert(results, qt.HasLen, 3)
- check := func(p PeerId, l uint64) {
- checkResultsRequestsLen(t, results[p].Requests, l)
- c.Check(results[p].Interested, qt.Equals, l > 0)
- }
- check(firstStealer.Id, 5)
- check(secondStealer.Id, 7+9)
- c.Check(
- results[stealee.Id],
- peerNextRequestStateChecker,
- PeerNextRequestState{
- Interested: true,
- Requests: requestSetFromSlice(r(4, 0), r(4, 1), r(4, 7), r(4, 8)),
- },
- )
-}
-
-// This tests a situation where multiple peers had the same existing request, due to "actual" and
-// "next" request states being out of sync. This reasonable occurs when a peer hasn't fully updated
-// its actual request state since the last request strategy run.
-func TestDuplicatePreallocations(t *testing.T) {
- peer := func(id int, downloadRate float64) Peer {
- p := Peer{
- ExistingRequests: hasAllRequests,
- MaxRequests: 2,
- Id: intPeerId(id),
- DownloadRate: downloadRate,
- }
- p.Pieces.AddRange(0, roaring.MaxRange)
- return p
- }
- results := Run(Input{
- Torrents: []Torrent{{
- ChunksPerPiece: 1,
- Pieces: []Piece{{
- Request: true,
- NumPendingChunks: 1,
- IterPendingChunks: chunkIterRange(1),
- }, {
- Request: true,
- NumPendingChunks: 1,
- IterPendingChunks: chunkIterRange(1),
- }},
- Peers: []Peer{
- // The second peer was be marked as the preallocation, clobbering the first. The
- // first peer is preferred, and the piece isn't striped, so it gets preallocated a
- // request, and then gets reallocated from the peer the same request.
- peer(1, 2),
- peer(2, 1),
- },
- }},
- })
- c := qt.New(t)
- req1 := results[intPeerId(1)].Requests
- req2 := results[intPeerId(2)].Requests
- c.Assert(uint64(2), qt.Equals, req1.GetCardinality()+req2.GetCardinality())
-}
type Torrent struct {
Pieces []Piece
Capacity storage.TorrentCapacity
- // Unclosed Peers. Not necessary for getting requestable piece ordering.
- Peers []Peer
- // Some value that's unique and stable between runs. Could even use the infohash?
+ // Some value that's unique and stable between runs.
InfoHash metainfo.Hash
ChunksPerPiece uint32
IterPendingChunks: &p.undirtiedChunksIter,
})
}
- t.iterPeers(func(p *Peer) {
- if p.closed.IsSet() {
- return
- }
- if p.piecesReceivedSinceLastRequestUpdate > p.maxPiecesReceivedBetweenRequestUpdates {
- p.maxPiecesReceivedBetweenRequestUpdates = p.piecesReceivedSinceLastRequestUpdate
- }
- p.piecesReceivedSinceLastRequestUpdate = 0
- rst.Peers = append(rst.Peers, request_strategy.Peer{
- Pieces: *p.newPeerPieces(),
- MaxRequests: p.nominalMaxRequests(),
- ExistingRequests: p.actualRequestState.Requests,
- Choking: p.peerChoking,
- PieceAllowedFast: p.peerAllowedFast,
- DownloadRate: p.downloadRate(),
- Age: time.Since(p.completedHandshake),
- Id: peerId{
- Peer: p,
- ptr: uintptr(unsafe.Pointer(p)),
- },
- })
- })
ts = append(ts, rst)
}
return request_strategy.Input{
-//go:build cgo
-
package sqliteStorage
import (
-//go:build cgo
-// +build cgo
-
package sqliteStorage
import (
"io"
- "crawshaw.io/sqlite"
"github.com/anacrolix/squirrel"
+ "zombiezen.com/go/sqlite"
"github.com/anacrolix/torrent/metainfo"
"github.com/anacrolix/torrent/storage"
-//go:build cgo
-// +build cgo
-
package sqliteStorage
import (
"github.com/anacrolix/multiless"
"github.com/anacrolix/sync"
"github.com/davecgh/go-spew/spew"
- "github.com/google/go-cmp/cmp"
"github.com/pion/datachannel"
"github.com/anacrolix/torrent/bencode"
}
func (t *Torrent) appendUnclosedConns(ret []*PeerConn) []*PeerConn {
+ return t.appendConns(ret, func(conn *PeerConn) bool {
+ return !conn.closed.IsSet()
+ })
+}
+
+func (t *Torrent) appendConns(ret []*PeerConn, f func(*PeerConn) bool) []*PeerConn {
for c := range t.conns {
- if !c.closed.IsSet() {
+ if f(c) {
ret = append(ret, c)
}
}
}
func (t *Torrent) haveAnyPieces() bool {
- return t._completedPieces.GetCardinality() != 0
+ return !t._completedPieces.IsEmpty()
}
func (t *Torrent) haveAllPieces() bool {
// conns (which is a map).
var peerConnSlices sync.Pool
+func getPeerConnSlice(cap int) []*PeerConn {
+ getInterface := peerConnSlices.Get()
+ if getInterface == nil {
+ return make([]*PeerConn, 0, cap)
+ } else {
+ return getInterface.([]*PeerConn)[:0]
+ }
+}
+
// The worst connection is one that hasn't been sent, or sent anything useful for the longest. A bad
// connection is one that usually sends us unwanted pieces, or has been in the worse half of the
// established connections for more than a minute. This is O(n log n). If there was a way to not
// consider the position of a conn relative to the total number, it could be reduced to O(n).
func (t *Torrent) worstBadConn() (ret *PeerConn) {
- var sl []*PeerConn
- getInterface := peerConnSlices.Get()
- if getInterface == nil {
- sl = make([]*PeerConn, 0, len(t.conns))
- } else {
- sl = getInterface.([]*PeerConn)[:0]
- }
- sl = t.appendUnclosedConns(sl)
- defer peerConnSlices.Put(sl)
- wcs := worseConnSlice{sl}
+ wcs := worseConnSlice{conns: t.appendUnclosedConns(getPeerConnSlice(len(t.conns)))}
+ defer peerConnSlices.Put(wcs.conns)
+ wcs.initKeys()
heap.Init(&wcs)
for wcs.Len() != 0 {
c := heap.Pop(&wcs).(*PeerConn)
x := uint32(piece)
if complete {
t._completedPieces.Add(x)
+ t.openNewConns()
} else {
t._completedPieces.Remove(x)
}
if !check {
return
}
- var actual pendingRequests
- if t.haveInfo() {
- actual.m = make([]int, t.numRequests())
- }
- t.iterPeers(func(p *Peer) {
- p.actualRequestState.Requests.Iterate(func(x uint32) bool {
- actual.Inc(x)
- return true
- })
- })
- diff := cmp.Diff(actual.m, t.pendingRequests.m)
- if diff != "" {
- panic(diff)
- }
+ // var actual pendingRequests
+ // if t.haveInfo() {
+ // actual.m = make([]int, t.numRequests())
+ // }
+ // t.iterPeers(func(p *Peer) {
+ // p.actualRequestState.Requests.Iterate(func(x uint32) bool {
+ // actual.Inc(x)
+ // return true
+ // })
+ // })
+ // diff := cmp.Diff(actual.m, t.pendingRequests.m)
+ // if diff != "" {
+ // panic(diff)
+ // }
}
func (t *Torrent) dropConnection(c *PeerConn) {
}
}
+// Peers as in contact information for dialing out.
func (t *Torrent) wantPeers() bool {
if t.closed.IsSet() {
return false
if t.peers.Len() > t.cl.config.TorrentPeersLowWater {
return false
}
- return t.needData() || t.seeding()
+ return t.wantConns()
}
func (t *Torrent) updateWantPeersEvent() {
if t.closed.IsSet() {
return
}
- if !t.wantPeers() {
+ // We're also announcing ourselves as a listener, so we don't just want peer addresses.
+ // TODO: We can include the announce_peer step depending on whether we can receive
+ // inbound connections. We should probably only announce once every 15 mins too.
+ if !t.wantConns() {
goto wait
}
// TODO: Determine if there's a listener on the port we're announcing.
if t.closed.IsSet() {
return false
}
- if !t.seeding() && !t.needData() {
+ if !t.needData() && (!t.seeding() || !t.haveAnyPieces()) {
return false
}
- if len(t.conns) < t.maxEstablishedConns {
- return true
- }
- return t.worstBadConn() != nil
+ return len(t.conns) < t.maxEstablishedConns || t.worstBadConn() != nil
}
func (t *Torrent) SetMaxEstablishedConns(max int) (oldMax int) {
defer t.cl.unlock()
oldMax = t.maxEstablishedConns
t.maxEstablishedConns = max
- wcs := slices.HeapInterface(slices.FromMapKeys(t.conns), func(l, r *PeerConn) bool {
- return worseConn(&l.Peer, &r.Peer)
- })
+ wcs := worseConnSlice{
+ conns: t.appendConns(nil, func(*PeerConn) bool {
+ return true
+ }),
+ }
+ wcs.initKeys()
+ heap.Init(&wcs)
for len(t.conns) > t.maxEstablishedConns && wcs.Len() > 0 {
- t.dropConnection(wcs.Pop().(*PeerConn))
+ t.dropConnection(heap.Pop(&wcs).(*PeerConn))
}
t.openNewConns()
return oldMax
--- /dev/null
+package torrent
+
+import (
+ "container/heap"
+ "fmt"
+ "time"
+ "unsafe"
+
+ "github.com/anacrolix/multiless"
+ "github.com/anacrolix/sync"
+)
+
+type worseConnInput struct {
+ Useful bool
+ LastHelpful time.Time
+ CompletedHandshake time.Time
+ GetPeerPriority func() (peerPriority, error)
+ getPeerPriorityOnce sync.Once
+ peerPriority peerPriority
+ peerPriorityErr error
+ Pointer uintptr
+}
+
+func (me *worseConnInput) doGetPeerPriority() {
+ me.peerPriority, me.peerPriorityErr = me.GetPeerPriority()
+}
+
+func (me *worseConnInput) doGetPeerPriorityOnce() {
+ me.getPeerPriorityOnce.Do(me.doGetPeerPriority)
+}
+
+func worseConnInputFromPeer(p *Peer) worseConnInput {
+ ret := worseConnInput{
+ Useful: p.useful(),
+ LastHelpful: p.lastHelpful(),
+ CompletedHandshake: p.completedHandshake,
+ Pointer: uintptr(unsafe.Pointer(p)),
+ GetPeerPriority: p.peerPriority,
+ }
+ return ret
+}
+
+func worseConn(_l, _r *Peer) bool {
+ // TODO: Use generics for ptr to
+ l := worseConnInputFromPeer(_l)
+ r := worseConnInputFromPeer(_r)
+ return l.Less(&r)
+}
+
+func (l *worseConnInput) Less(r *worseConnInput) bool {
+ less, ok := multiless.New().Bool(
+ l.Useful, r.Useful).CmpInt64(
+ l.LastHelpful.Sub(r.LastHelpful).Nanoseconds()).CmpInt64(
+ l.CompletedHandshake.Sub(r.CompletedHandshake).Nanoseconds()).LazySameLess(
+ func() (same, less bool) {
+ l.doGetPeerPriorityOnce()
+ if l.peerPriorityErr != nil {
+ same = true
+ return
+ }
+ r.doGetPeerPriorityOnce()
+ if r.peerPriorityErr != nil {
+ same = true
+ return
+ }
+ same = l.peerPriority == r.peerPriority
+ less = l.peerPriority < r.peerPriority
+ return
+ }).Uintptr(
+ l.Pointer, r.Pointer,
+ ).LessOk()
+ if !ok {
+ panic(fmt.Sprintf("cannot differentiate %#v and %#v", l, r))
+ }
+ return less
+}
+
+type worseConnSlice struct {
+ conns []*PeerConn
+ keys []worseConnInput
+}
+
+func (me *worseConnSlice) initKeys() {
+ me.keys = make([]worseConnInput, len(me.conns))
+ for i, c := range me.conns {
+ me.keys[i] = worseConnInputFromPeer(&c.Peer)
+ }
+}
+
+var _ heap.Interface = &worseConnSlice{}
+
+func (me worseConnSlice) Len() int {
+ return len(me.conns)
+}
+
+func (me worseConnSlice) Less(i, j int) bool {
+ return me.keys[i].Less(&me.keys[j])
+}
+
+func (me *worseConnSlice) Pop() interface{} {
+ i := len(me.conns) - 1
+ ret := me.conns[i]
+ me.conns = me.conns[:i]
+ return ret
+}
+
+func (me *worseConnSlice) Push(x interface{}) {
+ panic("not implemented")
+}
+
+func (me worseConnSlice) Swap(i, j int) {
+ me.conns[i], me.conns[j] = me.conns[j], me.conns[i]
+ me.keys[i], me.keys[j] = me.keys[j], me.keys[i]
+}
--- /dev/null
+package torrent
+
+import (
+ "testing"
+ "time"
+
+ qt "github.com/frankban/quicktest"
+)
+
+func TestWorseConnLastHelpful(t *testing.T) {
+ c := qt.New(t)
+ c.Check((&worseConnInput{}).Less(&worseConnInput{LastHelpful: time.Now()}), qt.IsTrue)
+ c.Check((&worseConnInput{}).Less(&worseConnInput{CompletedHandshake: time.Now()}), qt.IsTrue)
+ c.Check((&worseConnInput{LastHelpful: time.Now()}).Less(&worseConnInput{CompletedHandshake: time.Now()}), qt.IsFalse)
+ c.Check((&worseConnInput{
+ LastHelpful: time.Now(),
+ }).Less(&worseConnInput{
+ LastHelpful: time.Now(),
+ CompletedHandshake: time.Now(),
+ }), qt.IsTrue)
+ now := time.Now()
+ c.Check((&worseConnInput{
+ LastHelpful: now,
+ }).Less(&worseConnInput{
+ LastHelpful: now.Add(-time.Nanosecond),
+ CompletedHandshake: now,
+ }), qt.IsFalse)
+ readyPeerPriority := func() (peerPriority, error) {
+ return 42, nil
+ }
+ c.Check((&worseConnInput{
+ GetPeerPriority: readyPeerPriority,
+ }).Less(&worseConnInput{
+ GetPeerPriority: readyPeerPriority,
+ Pointer: 1,
+ }), qt.IsTrue)
+ c.Check((&worseConnInput{
+ GetPeerPriority: readyPeerPriority,
+ Pointer: 2,
+ }).Less(&worseConnInput{
+ GetPeerPriority: readyPeerPriority,
+ Pointer: 1,
+ }), qt.IsFalse)
+}
+++ /dev/null
-package torrent
-
-import (
- "container/heap"
- "fmt"
- "unsafe"
-
- "github.com/anacrolix/multiless"
-)
-
-func worseConn(l, r *Peer) bool {
- less, ok := multiless.New().Bool(
- l.useful(), r.useful()).CmpInt64(
- l.lastHelpful().Sub(r.lastHelpful()).Nanoseconds()).CmpInt64(
- l.completedHandshake.Sub(r.completedHandshake).Nanoseconds()).LazySameLess(
- func() (same, less bool) {
- lpp, err := l.peerPriority()
- if err != nil {
- same = true
- return
- }
- rpp, err := r.peerPriority()
- if err != nil {
- same = true
- return
- }
- return lpp == rpp, lpp < rpp
- }).Uintptr(
- uintptr(unsafe.Pointer(l)), uintptr(unsafe.Pointer(r)),
- ).LessOk()
- if !ok {
- panic(fmt.Sprintf("cannot differentiate %#v and %#v", l, r))
- }
- return less
-}
-
-type worseConnSlice struct {
- conns []*PeerConn
-}
-
-var _ heap.Interface = &worseConnSlice{}
-
-func (me worseConnSlice) Len() int {
- return len(me.conns)
-}
-
-func (me worseConnSlice) Less(i, j int) bool {
- return worseConn(&me.conns[i].Peer, &me.conns[j].Peer)
-}
-
-func (me *worseConnSlice) Pop() interface{} {
- i := len(me.conns) - 1
- ret := me.conns[i]
- me.conns = me.conns[:i]
- return ret
-}
-
-func (me *worseConnSlice) Push(x interface{}) {
- me.conns = append(me.conns, x.(*PeerConn))
-}
-
-func (me worseConnSlice) Swap(i, j int) {
- me.conns[i], me.conns[j] = me.conns[j], me.conns[i]
-}