Fixes #221. Additionally Torrent.length is a pointer and isn't set until the info is available to avoid it defaulting to zero.
import (
"strings"
- "github.com/anacrolix/missinggo"
"github.com/anacrolix/torrent/metainfo"
)
}
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
}
+++ /dev/null
-package torrent
-
-import (
- "io"
-
- "github.com/anacrolix/missinggo"
-)
-
-type fileReaderInherited interface {
- io.Closer
- SetReadahead(int64)
- SetResponsive()
-}
-
-type fileReader struct {
- missinggo.ReadSeekContexter
- fileReaderInherited
-}
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
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)
}
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
}
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
// 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
}
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")
}
mu: &t.cl.mu,
t: t,
readahead: 5 * 1024 * 1024,
+ length: *t.length,
}
t.addReader(&r)
return &r
// 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
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
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()
}
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) {
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
}
// 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 {