]> Sergey Matveev's repositories - btrtrc.git/commitdiff
Readers obtained from File.NewReader should not readahead into other Files
authorMatt Joiner <anacrolix@gmail.com>
Sat, 6 Jan 2018 12:15:41 +0000 (23:15 +1100)
committerMatt Joiner <anacrolix@gmail.com>
Sat, 6 Jan 2018 12:15:41 +0000 (23:15 +1100)
Fixes #221. Additionally Torrent.length is a pointer and isn't set until the info is available to avoid it defaulting to zero.

file.go
file_reader.go [deleted file]
reader.go
t.go
torrent.go

diff --git a/file.go b/file.go
index 8c5ac6d79e69320b7041b1babb27a59893052c9d..19729b4cad8c074c7d39d2e327daa0f8547b15f2 100644 (file)
--- a/file.go
+++ b/file.go
@@ -3,7 +3,6 @@ package torrent
 import (
        "strings"
 
-       "github.com/anacrolix/missinggo"
        "github.com/anacrolix/torrent/metainfo"
 )
 
@@ -107,6 +106,13 @@ func (f *File) Cancel() {
 }
 
 func (f *File) NewReader() Reader {
-       tr := f.t.NewReader()
-       return fileReader{missinggo.NewSectionReadSeeker(tr, f.Offset(), f.Length()), tr}
+       tr := reader{
+               mu:        &f.t.cl.mu,
+               t:         f.t,
+               readahead: 5 * 1024 * 1024,
+               offset:    f.Offset(),
+               length:    f.Length(),
+       }
+       f.t.addReader(&tr)
+       return &tr
 }
diff --git a/file_reader.go b/file_reader.go
deleted file mode 100644 (file)
index dde4362..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-package torrent
-
-import (
-       "io"
-
-       "github.com/anacrolix/missinggo"
-)
-
-type fileReaderInherited interface {
-       io.Closer
-       SetReadahead(int64)
-       SetResponsive()
-}
-
-type fileReader struct {
-       missinggo.ReadSeekContexter
-       fileReaderInherited
-}
index 1be3b0ade37faeadbd7bb4f53177f1c54be83ca4..8b5ca81de560c8bdca190dd257601103a134c44a 100644 (file)
--- a/reader.go
+++ b/reader.go
@@ -29,6 +29,9 @@ type pieceRange struct {
 type reader struct {
        t          *Torrent
        responsive bool
+       // Adjust the read/seek window to handle Readers locked to File extents
+       // and the like.
+       offset, length int64
        // Ensure operations that change the position are exclusive, like Read()
        // and Seek().
        opMu sync.Mutex
@@ -74,7 +77,7 @@ func (r *reader) readable(off int64) (ret bool) {
        if r.t.closed.IsSet() {
                return true
        }
-       req, ok := r.t.offsetRequest(off)
+       req, ok := r.t.offsetRequest(r.offset + off)
        if !ok {
                panic(off)
        }
@@ -117,9 +120,14 @@ func (r *reader) waitReadable(off int64) {
 func (r *reader) piecesUncached() (ret pieceRange) {
        ra := r.readahead
        if ra < 1 {
+               // Needs to be at least 1, because [x, x) means we don't want
+               // anything.
                ra = 1
        }
-       ret.begin, ret.end = r.t.byteRegionPieces(r.pos, ra)
+       if ra > r.length-r.pos {
+               ra = r.length - r.pos
+       }
+       ret.begin, ret.end = r.t.byteRegionPieces(r.offset+r.pos, ra)
        return
 }
 
@@ -162,7 +170,7 @@ func (r *reader) ReadContext(ctx context.Context, b []byte) (n int, err error) {
                r.posChanged()
                r.mu.Unlock()
        }
-       if r.pos >= r.t.length {
+       if r.pos >= r.length {
                err = io.EOF
        } else if err == io.EOF {
                err = io.ErrUnexpectedEOF
@@ -183,7 +191,7 @@ func (r *reader) waitAvailable(pos, wanted int64, ctxErr *error) (avail int64) {
 
 // Performs at most one successful read to torrent storage.
 func (r *reader) readOnceAt(b []byte, pos int64, ctxErr *error) (n int, err error) {
-       if pos >= r.t.length {
+       if pos >= r.length {
                err = io.EOF
                return
        }
@@ -245,7 +253,7 @@ func (r *reader) Seek(off int64, whence int) (ret int64, err error) {
        case io.SeekCurrent:
                r.pos += off
        case io.SeekEnd:
-               r.pos = r.t.info.TotalLength() + off
+               r.pos = r.length + off
        default:
                err = errors.New("bad whence")
        }
diff --git a/t.go b/t.go
index f687cb59fe963b85fde06a63dee00f4f6f9b99e8..93d8c3bfd2114bb0501efd457f2b9591d31772d3 100644 (file)
--- a/t.go
+++ b/t.go
@@ -36,6 +36,7 @@ func (t *Torrent) NewReader() Reader {
                mu:        &t.cl.mu,
                t:         t,
                readahead: 5 * 1024 * 1024,
+               length:    *t.length,
        }
        t.addReader(&r)
        return &r
@@ -119,10 +120,7 @@ func (t *Torrent) Name() string {
 // The completed length of all the torrent data, in all its files. This is
 // derived from the torrent info, when it is available.
 func (t *Torrent) Length() int64 {
-       if t.info == nil {
-               panic("not valid until info obtained")
-       }
-       return t.length
+       return *t.length
 }
 
 // Returns a run-time generated metainfo for the torrent that includes the
index 3cb32a13928c49732b44bb6be1718523f5a455a8..301e2d6d67c4c8406f099add71465e47a48832dd 100644 (file)
@@ -58,7 +58,7 @@ type Torrent struct {
        chunkPool *sync.Pool
        // Total length of the torrent in bytes. Stored because it's not O(1) to
        // get this from the info dict.
-       length int64
+       length *int64
 
        // The storage to open when the info dict becomes available.
        storageOpener *storage.Client
@@ -328,10 +328,11 @@ func (t *Torrent) setInfoBytes(b []byte) error {
        if err != nil {
                return fmt.Errorf("error opening torrent storage: %s", err)
        }
-       t.length = 0
+       var l int64
        for _, f := range t.info.UpvertedFiles() {
-               t.length += f.Length
+               l += f.Length
        }
+       t.length = &l
        t.metadataBytes = b
        t.metadataCompletedChunks = nil
        t.makePieces()
@@ -631,13 +632,13 @@ func (t *Torrent) close() (err error) {
 }
 
 func (t *Torrent) requestOffset(r request) int64 {
-       return torrentRequestOffset(t.length, int64(t.usualPieceSize()), r)
+       return torrentRequestOffset(*t.length, int64(t.usualPieceSize()), r)
 }
 
 // Return the request that would include the given offset into the torrent
 // data. Returns !ok if there is no such request.
 func (t *Torrent) offsetRequest(off int64) (req request, ok bool) {
-       return torrentOffsetRequest(t.length, t.info.PieceLength, int64(t.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) {
@@ -681,7 +682,7 @@ type Peer struct {
 
 func (t *Torrent) pieceLength(piece int) pp.Integer {
        if piece == t.numPieces()-1 {
-               ret := pp.Integer(t.length % t.info.PieceLength)
+               ret := pp.Integer(*t.length % t.info.PieceLength)
                if ret != 0 {
                        return ret
                }
@@ -897,7 +898,7 @@ func (t *Torrent) updatePiecePriorities(begin, end int) {
 
 // Returns the range of pieces [begin, end) that contains the extent of bytes.
 func (t *Torrent) byteRegionPieces(off, size int64) (begin, end int) {
-       if off >= t.length {
+       if off >= *t.length {
                return
        }
        if off < 0 {