Allow chunk size to be specified per torrent
This commit is contained in:
parent
4807f12ea1
commit
c018c660f0
15
client.go
15
client.go
|
@ -324,7 +324,7 @@ func (cl *Client) addUrgentRequests(t *torrent, off int64, n int) {
|
||||||
}
|
}
|
||||||
if _, ok := t.urgent[req]; !ok && !t.haveChunk(req) {
|
if _, ok := t.urgent[req]; !ok && !t.haveChunk(req) {
|
||||||
if t.urgent == nil {
|
if t.urgent == nil {
|
||||||
t.urgent = make(map[request]struct{}, (n+chunkSize-1)/chunkSize)
|
t.urgent = make(map[request]struct{}, (n+int(t.chunkSize)-1)/int(t.chunkSize))
|
||||||
}
|
}
|
||||||
t.urgent[req] = struct{}{}
|
t.urgent[req] = struct{}{}
|
||||||
cl.event.Broadcast() // Why?
|
cl.event.Broadcast() // Why?
|
||||||
|
@ -1912,6 +1912,7 @@ func (cl *Client) setMetaData(t *torrent, md *metainfo.Info, bytes []byte) (err
|
||||||
func newTorrent(ih InfoHash) (t *torrent, err error) {
|
func newTorrent(ih InfoHash) (t *torrent, err error) {
|
||||||
t = &torrent{
|
t = &torrent{
|
||||||
InfoHash: ih,
|
InfoHash: ih,
|
||||||
|
chunkSize: defaultChunkSize,
|
||||||
Peers: make(map[peersKey]Peer),
|
Peers: make(map[peersKey]Peer),
|
||||||
|
|
||||||
closing: make(chan struct{}),
|
closing: make(chan struct{}),
|
||||||
|
@ -2078,10 +2079,15 @@ func (cl *Client) torrentCacheMetaInfo(ih InfoHash) (mi *metainfo.MetaInfo, err
|
||||||
// Specifies a new torrent for adding to a client. There are helpers for
|
// Specifies a new torrent for adding to a client. There are helpers for
|
||||||
// magnet URIs and torrent metainfo files.
|
// magnet URIs and torrent metainfo files.
|
||||||
type TorrentSpec struct {
|
type TorrentSpec struct {
|
||||||
|
// The tiered tracker URIs.
|
||||||
Trackers [][]string
|
Trackers [][]string
|
||||||
InfoHash InfoHash
|
InfoHash InfoHash
|
||||||
Info *metainfo.InfoEx
|
Info *metainfo.InfoEx
|
||||||
|
// The name to use if the Name field from the Info isn't available.
|
||||||
DisplayName string
|
DisplayName string
|
||||||
|
// The chunk size to use for outbound requests. Defaults to 16KiB if not
|
||||||
|
// set.
|
||||||
|
ChunkSize int
|
||||||
}
|
}
|
||||||
|
|
||||||
func TorrentSpecFromMagnetURI(uri string) (spec *TorrentSpec, err error) {
|
func TorrentSpecFromMagnetURI(uri string) (spec *TorrentSpec, err error) {
|
||||||
|
@ -2129,6 +2135,9 @@ func (cl *Client) AddTorrentSpec(spec *TorrentSpec) (T Torrent, new bool, err er
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if spec.ChunkSize != 0 {
|
||||||
|
t.chunkSize = pp.Integer(spec.ChunkSize)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if spec.DisplayName != "" {
|
if spec.DisplayName != "" {
|
||||||
t.DisplayName = spec.DisplayName
|
t.DisplayName = spec.DisplayName
|
||||||
|
@ -2463,7 +2472,7 @@ func (me *Client) fillRequests(t *torrent, c *connection) {
|
||||||
panic("unwanted piece in connection request order")
|
panic("unwanted piece in connection request order")
|
||||||
}
|
}
|
||||||
piece := t.Pieces[pieceIndex]
|
piece := t.Pieces[pieceIndex]
|
||||||
for _, cs := range piece.shuffledPendingChunkSpecs(t.pieceLength(pieceIndex)) {
|
for _, cs := range piece.shuffledPendingChunkSpecs(t.pieceLength(pieceIndex), pp.Integer(t.chunkSize)) {
|
||||||
r := request{pp.Integer(pieceIndex), cs}
|
r := request{pp.Integer(pieceIndex), cs}
|
||||||
if !addRequest(r) {
|
if !addRequest(r) {
|
||||||
return
|
return
|
||||||
|
@ -2524,7 +2533,7 @@ func (me *Client) downloadedChunk(t *torrent, c *connection, msg *pp.Message) er
|
||||||
// log.Println("got chunk", req)
|
// log.Println("got chunk", req)
|
||||||
piece.Event.Broadcast()
|
piece.Event.Broadcast()
|
||||||
// Record that we have the chunk.
|
// Record that we have the chunk.
|
||||||
piece.unpendChunkIndex(chunkIndex(req.chunkSpec))
|
piece.unpendChunkIndex(chunkIndex(req.chunkSpec, t.chunkSize))
|
||||||
delete(t.urgent, req)
|
delete(t.urgent, req)
|
||||||
if piece.numPendingChunks() == 0 {
|
if piece.numPendingChunks() == 0 {
|
||||||
for _, c := range t.Conns {
|
for _, c := range t.Conns {
|
||||||
|
|
|
@ -93,6 +93,7 @@ func TestTorrentInitialState(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
tor.chunkSize = 2
|
||||||
err = tor.setMetadata(&mi.Info.Info, mi.Info.Bytes, nil)
|
err = tor.setMetadata(&mi.Info.Info, mi.Info.Bytes, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -102,13 +103,8 @@ func TestTorrentInitialState(t *testing.T) {
|
||||||
}
|
}
|
||||||
p := tor.Pieces[0]
|
p := tor.Pieces[0]
|
||||||
tor.pendAllChunkSpecs(0)
|
tor.pendAllChunkSpecs(0)
|
||||||
if p.numPendingChunks() != 1 {
|
assert.EqualValues(t, 3, p.numPendingChunks())
|
||||||
t.Fatalf("should only be 1 chunk: %v", p.PendingChunkSpecs)
|
assert.EqualValues(t, chunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
|
||||||
}
|
|
||||||
// TODO: Set chunkSize to 2, to test odd/even silliness.
|
|
||||||
if chunkIndexSpec(0, tor.pieceLength(0)).Length != 5 {
|
|
||||||
t.Fatal("pending chunk spec is incorrect")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalPEXMsg(t *testing.T) {
|
func TestUnmarshalPEXMsg(t *testing.T) {
|
||||||
|
@ -271,7 +267,11 @@ func TestClientTransfer(t *testing.T) {
|
||||||
cfg.TorrentDataOpener = blob.NewStore(leecherDataDir).OpenTorrent
|
cfg.TorrentDataOpener = blob.NewStore(leecherDataDir).OpenTorrent
|
||||||
leecher, _ := NewClient(&cfg)
|
leecher, _ := NewClient(&cfg)
|
||||||
defer leecher.Close()
|
defer leecher.Close()
|
||||||
leecherGreeting, _, _ := leecher.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
|
leecherGreeting, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
|
||||||
|
ret = TorrentSpecFromMetaInfo(mi)
|
||||||
|
ret.ChunkSize = 2
|
||||||
|
return
|
||||||
|
}())
|
||||||
leecherGreeting.AddPeers([]Peer{
|
leecherGreeting.AddPeers([]Peer{
|
||||||
Peer{
|
Peer{
|
||||||
IP: util.AddrIP(seeder.ListenAddr()),
|
IP: util.AddrIP(seeder.ListenAddr()),
|
||||||
|
|
4
misc.go
4
misc.go
|
@ -13,7 +13,7 @@ import (
|
||||||
const (
|
const (
|
||||||
pieceHash = crypto.SHA1
|
pieceHash = crypto.SHA1
|
||||||
maxRequests = 250 // Maximum pending requests we allow peers to send us.
|
maxRequests = 250 // Maximum pending requests we allow peers to send us.
|
||||||
chunkSize = 0x4000 // 16KiB
|
defaultChunkSize = 0x4000 // 16KiB
|
||||||
// Peer ID client identifier prefix. We'll update this occasionally to
|
// Peer ID client identifier prefix. We'll update this occasionally to
|
||||||
// reflect changes to client behaviour that other clients may depend on.
|
// reflect changes to client behaviour that other clients may depend on.
|
||||||
// Also see `extendedHandshakeClientVersion`.
|
// Also see `extendedHandshakeClientVersion`.
|
||||||
|
@ -35,7 +35,7 @@ func (ih *InfoHash) HexString() string {
|
||||||
return fmt.Sprintf("%x", ih[:])
|
return fmt.Sprintf("%x", ih[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
func lastChunkSpec(pieceLength pp.Integer) (cs chunkSpec) {
|
func lastChunkSpec(pieceLength, chunkSize pp.Integer) (cs chunkSpec) {
|
||||||
cs.Begin = (pieceLength - 1) / chunkSize * chunkSize
|
cs.Begin = (pieceLength - 1) / chunkSize * chunkSize
|
||||||
cs.Length = pieceLength - cs.Begin
|
cs.Length = pieceLength - cs.Begin
|
||||||
return
|
return
|
||||||
|
|
|
@ -4,7 +4,7 @@ import . "gopkg.in/check.v1"
|
||||||
|
|
||||||
func (suite) TestTorrentOffsetRequest(c *C) {
|
func (suite) TestTorrentOffsetRequest(c *C) {
|
||||||
check := func(tl, ps, off int64, expected request, ok bool) {
|
check := func(tl, ps, off int64, expected request, ok bool) {
|
||||||
req, _ok := torrentOffsetRequest(tl, ps, chunkSize, off)
|
req, _ok := torrentOffsetRequest(tl, ps, defaultChunkSize, off)
|
||||||
c.Check(_ok, Equals, ok)
|
c.Check(_ok, Equals, ok)
|
||||||
c.Check(req, Equals, expected)
|
c.Check(req, Equals, expected)
|
||||||
}
|
}
|
||||||
|
|
10
piece.go
10
piece.go
|
@ -32,11 +32,11 @@ type piece struct {
|
||||||
Priority piecePriority
|
Priority piecePriority
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *piece) pendingChunk(cs chunkSpec) bool {
|
func (p *piece) pendingChunk(cs chunkSpec, chunkSize pp.Integer) bool {
|
||||||
if p.PendingChunkSpecs == nil {
|
if p.PendingChunkSpecs == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return p.PendingChunkSpecs[chunkIndex(cs)]
|
return p.PendingChunkSpecs[chunkIndex(cs, chunkSize)]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *piece) numPendingChunks() (ret int) {
|
func (p *piece) numPendingChunks() (ret int) {
|
||||||
|
@ -55,7 +55,7 @@ func (p *piece) unpendChunkIndex(i int) {
|
||||||
p.PendingChunkSpecs[i] = false
|
p.PendingChunkSpecs[i] = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func chunkIndexSpec(index int, pieceLength pp.Integer) chunkSpec {
|
func chunkIndexSpec(index int, pieceLength, chunkSize pp.Integer) chunkSpec {
|
||||||
ret := chunkSpec{pp.Integer(index) * chunkSize, chunkSize}
|
ret := chunkSpec{pp.Integer(index) * chunkSize, chunkSize}
|
||||||
if ret.Begin+ret.Length > pieceLength {
|
if ret.Begin+ret.Length > pieceLength {
|
||||||
ret.Length = pieceLength - ret.Begin
|
ret.Length = pieceLength - ret.Begin
|
||||||
|
@ -63,14 +63,14 @@ func chunkIndexSpec(index int, pieceLength pp.Integer) chunkSpec {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *piece) shuffledPendingChunkSpecs(pieceLength pp.Integer) (css []chunkSpec) {
|
func (p *piece) shuffledPendingChunkSpecs(pieceLength, chunkSize pp.Integer) (css []chunkSpec) {
|
||||||
if p.numPendingChunks() == 0 {
|
if p.numPendingChunks() == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
css = make([]chunkSpec, 0, p.numPendingChunks())
|
css = make([]chunkSpec, 0, p.numPendingChunks())
|
||||||
for i, pending := range p.PendingChunkSpecs {
|
for i, pending := range p.PendingChunkSpecs {
|
||||||
if pending {
|
if pending {
|
||||||
css = append(css, chunkIndexSpec(i, pieceLength))
|
css = append(css, chunkIndexSpec(i, pieceLength, chunkSize))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(css) <= 1 {
|
if len(css) <= 1 {
|
||||||
|
|
26
torrent.go
26
torrent.go
|
@ -32,7 +32,7 @@ func (t *torrent) pieceNumPendingBytes(index int) (count pp.Integer) {
|
||||||
}
|
}
|
||||||
for i, pending := range piece.PendingChunkSpecs {
|
for i, pending := range piece.PendingChunkSpecs {
|
||||||
if pending {
|
if pending {
|
||||||
count += chunkIndexSpec(i, pieceLength).Length
|
count += chunkIndexSpec(i, pieceLength, t.chunkSize).Length
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -63,6 +63,7 @@ type torrent struct {
|
||||||
|
|
||||||
InfoHash InfoHash
|
InfoHash InfoHash
|
||||||
Pieces []*piece
|
Pieces []*piece
|
||||||
|
chunkSize pp.Integer
|
||||||
// Chunks that are wanted before all others. This is for
|
// Chunks that are wanted before all others. This is for
|
||||||
// responsive/streaming readers that want to unblock ASAP.
|
// responsive/streaming readers that want to unblock ASAP.
|
||||||
urgent map[request]struct{}
|
urgent map[request]struct{}
|
||||||
|
@ -552,7 +553,7 @@ func (t *torrent) requestOffset(r request) int64 {
|
||||||
// Return the request that would include the given offset into the torrent
|
// Return the request that would include the given offset into the torrent
|
||||||
// data. Returns !ok if there is no such request.
|
// data. Returns !ok if there is no such request.
|
||||||
func (t *torrent) offsetRequest(off int64) (req request, ok bool) {
|
func (t *torrent) offsetRequest(off int64) (req request, ok bool) {
|
||||||
return torrentOffsetRequest(t.Length(), t.Info.PieceLength, chunkSize, off)
|
return torrentOffsetRequest(t.Length(), t.Info.PieceLength, int64(t.chunkSize), off)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *torrent) writeChunk(piece int, begin int64, data []byte) (err error) {
|
func (t *torrent) writeChunk(piece int, begin int64, data []byte) (err error) {
|
||||||
|
@ -575,26 +576,26 @@ func (t *torrent) validOutgoingRequest(r request) bool {
|
||||||
if r.Index >= pp.Integer(t.Info.NumPieces()) {
|
if r.Index >= pp.Integer(t.Info.NumPieces()) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if r.Begin%chunkSize != 0 {
|
if r.Begin%t.chunkSize != 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if r.Length > chunkSize {
|
if r.Length > t.chunkSize {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
pieceLength := t.pieceLength(int(r.Index))
|
pieceLength := t.pieceLength(int(r.Index))
|
||||||
if r.Begin+r.Length > pieceLength {
|
if r.Begin+r.Length > pieceLength {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return r.Length == chunkSize || r.Begin+r.Length == pieceLength
|
return r.Length == t.chunkSize || r.Begin+r.Length == pieceLength
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *torrent) pieceChunks(piece int) (css []chunkSpec) {
|
func (t *torrent) pieceChunks(piece int) (css []chunkSpec) {
|
||||||
css = make([]chunkSpec, 0, (t.pieceLength(piece)+chunkSize-1)/chunkSize)
|
css = make([]chunkSpec, 0, (t.pieceLength(piece)+t.chunkSize-1)/t.chunkSize)
|
||||||
var cs chunkSpec
|
var cs chunkSpec
|
||||||
for left := t.pieceLength(piece); left != 0; left -= cs.Length {
|
for left := t.pieceLength(piece); left != 0; left -= cs.Length {
|
||||||
cs.Length = left
|
cs.Length = left
|
||||||
if cs.Length > chunkSize {
|
if cs.Length > t.chunkSize {
|
||||||
cs.Length = chunkSize
|
cs.Length = t.chunkSize
|
||||||
}
|
}
|
||||||
css = append(css, cs)
|
css = append(css, cs)
|
||||||
cs.Begin += cs.Length
|
cs.Begin += cs.Length
|
||||||
|
@ -606,7 +607,7 @@ func (t *torrent) pendAllChunkSpecs(pieceIndex int) {
|
||||||
piece := t.Pieces[pieceIndex]
|
piece := t.Pieces[pieceIndex]
|
||||||
if piece.PendingChunkSpecs == nil {
|
if piece.PendingChunkSpecs == nil {
|
||||||
// Allocate to exact size.
|
// Allocate to exact size.
|
||||||
piece.PendingChunkSpecs = make([]bool, (t.pieceLength(pieceIndex)+chunkSize-1)/chunkSize)
|
piece.PendingChunkSpecs = make([]bool, (t.pieceLength(pieceIndex)+t.chunkSize-1)/t.chunkSize)
|
||||||
}
|
}
|
||||||
// Pend all the chunks.
|
// Pend all the chunks.
|
||||||
pcss := piece.PendingChunkSpecs
|
pcss := piece.PendingChunkSpecs
|
||||||
|
@ -671,10 +672,11 @@ func (t *torrent) haveChunk(r request) bool {
|
||||||
if !t.haveInfo() {
|
if !t.haveInfo() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return !t.Pieces[r.Index].pendingChunk(r.chunkSpec)
|
p := t.Pieces[r.Index]
|
||||||
|
return !p.pendingChunk(r.chunkSpec, t.chunkSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
func chunkIndex(cs chunkSpec) int {
|
func chunkIndex(cs chunkSpec, chunkSize pp.Integer) int {
|
||||||
return int(cs.Begin / chunkSize)
|
return int(cs.Begin / chunkSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -683,7 +685,7 @@ func (t *torrent) wantChunk(r request) bool {
|
||||||
if !t.wantPiece(int(r.Index)) {
|
if !t.wantPiece(int(r.Index)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if t.Pieces[r.Index].pendingChunk(r.chunkSpec) {
|
if t.Pieces[r.Index].pendingChunk(r.chunkSpec, t.chunkSize) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
_, ok := t.urgent[r]
|
_, ok := t.urgent[r]
|
||||||
|
|
Loading…
Reference in New Issue