X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=file.go;h=ed2f5da9ced0e74c87a903d1b160a3793331701d;hb=98234f943f98032a1be79097f13b89818d821ec1;hp=7cc0ad64bf5d67f520ba37863f8c7d368fb78b13;hpb=29aa07f1a96e405790f6afabab1e919711d4737f;p=btrtrc.git diff --git a/file.go b/file.go index 7cc0ad64..ed2f5da9 100644 --- a/file.go +++ b/file.go @@ -1,20 +1,21 @@ package torrent import ( - "strings" + "github.com/RoaringBitmap/roaring" + "github.com/anacrolix/missinggo/v2/bitmap" "github.com/anacrolix/torrent/metainfo" - pwp "github.com/anacrolix/torrent/peer_protocol" ) // Provides access to regions of torrent data that correspond to its files. type File struct { - t *Torrent - path string - offset int64 - length int64 - fi metainfo.FileInfo - prio piecePriority + t *Torrent + path string + offset int64 + length int64 + fi metainfo.FileInfo + displayPath string + prio piecePriority } func (f *File) Torrent() *Torrent { @@ -41,15 +42,57 @@ func (f *File) Length() int64 { return f.length } -// The relative file path for a multi-file torrent, and the torrent name for a -// single-file torrent. -func (f *File) DisplayPath() string { - fip := f.FileInfo().Path - if len(fip) == 0 { - return f.t.info.Name +// Number of bytes of the entire file we have completed. This is the sum of +// completed pieces, and dirtied chunks of incomplete pieces. +func (f *File) BytesCompleted() (n int64) { + f.t.cl.rLock() + n = f.bytesCompletedLocked() + f.t.cl.rUnlock() + return +} + +func (f *File) bytesCompletedLocked() int64 { + return f.length - f.bytesLeft() +} + +func fileBytesLeft( + torrentUsualPieceSize int64, + fileFirstPieceIndex int, + fileEndPieceIndex int, + fileTorrentOffset int64, + fileLength int64, + torrentCompletedPieces *roaring.Bitmap, +) (left int64) { + numPiecesSpanned := fileEndPieceIndex - fileFirstPieceIndex + switch numPiecesSpanned { + case 0: + case 1: + if !torrentCompletedPieces.Contains(bitmap.BitIndex(fileFirstPieceIndex)) { + left += fileLength + } + default: + if !torrentCompletedPieces.Contains(bitmap.BitIndex(fileFirstPieceIndex)) { + left += torrentUsualPieceSize - (fileTorrentOffset % torrentUsualPieceSize) + } + if !torrentCompletedPieces.Contains(bitmap.BitIndex(fileEndPieceIndex - 1)) { + left += fileTorrentOffset + fileLength - int64(fileEndPieceIndex-1)*torrentUsualPieceSize + } + completedMiddlePieces := torrentCompletedPieces.Clone() + completedMiddlePieces.RemoveRange(0, bitmap.BitRange(fileFirstPieceIndex+1)) + completedMiddlePieces.RemoveRange(bitmap.BitRange(fileEndPieceIndex-1), bitmap.ToEnd) + left += int64(numPiecesSpanned-2-pieceIndex(completedMiddlePieces.GetCardinality())) * torrentUsualPieceSize } - return strings.Join(fip, "/") + return +} +func (f *File) bytesLeft() (left int64) { + return fileBytesLeft(int64(f.t.usualPieceSize()), f.BeginPieceIndex(), f.EndPieceIndex(), f.offset, f.length, &f.t._completedPieces) +} + +// The relative file path for a multi-file torrent, and the torrent name for a +// single-file torrent. Dir separators are '/'. +func (f *File) DisplayPath() string { + return f.displayPath } // The download status of a piece that comprises part of a File. @@ -60,12 +103,12 @@ type FilePieceState struct { // Returns the state of pieces in this file. func (f *File) State() (ret []FilePieceState) { - f.t.cl.mu.RLock() - defer f.t.cl.mu.RUnlock() + f.t.cl.rLock() + defer f.t.cl.rUnlock() pieceSize := int64(f.t.usualPieceSize()) off := f.offset % pieceSize remaining := f.length - for i := int(f.offset / pieceSize); ; i++ { + for i := pieceIndex(f.offset / pieceSize); ; i++ { if remaining == 0 { break } @@ -92,49 +135,45 @@ func byteRegionExclusivePieces(off, size, pieceSize int64) (begin, end int) { return } -func (f *File) exclusivePieces() (begin, end int) { - return byteRegionExclusivePieces(f.offset, f.length, int64(f.t.usualPieceSize())) -} - // Deprecated: Use File.SetPriority. func (f *File) Cancel() { f.SetPriority(PiecePriorityNone) } func (f *File) NewReader() Reader { - 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 + return f.t.newReader(f.Offset(), f.Length()) } // Sets the minimum priority for pieces in the File. func (f *File) SetPriority(prio piecePriority) { - f.t.cl.mu.Lock() - defer f.t.cl.mu.Unlock() - if prio == f.prio { - return + f.t.cl.lock() + if prio != f.prio { + f.prio = prio + f.t.updatePiecePriorities(f.BeginPieceIndex(), f.EndPieceIndex(), "File.SetPriority") } - f.prio = prio - f.t.updatePiecePriorities(f.firstPieceIndex().Int(), f.lastPieceIndex().Int()+1) + f.t.cl.unlock() } // Returns the priority per File.SetPriority. -func (f *File) Priority() piecePriority { - f.t.cl.mu.Lock() - defer f.t.cl.mu.Unlock() - return f.prio +func (f *File) Priority() (prio piecePriority) { + f.t.cl.rLock() + prio = f.prio + f.t.cl.rUnlock() + return } -func (f *File) firstPieceIndex() pwp.Integer { - return pwp.Integer(f.offset / int64(f.t.usualPieceSize())) +// Returns the index of the first piece containing data for the file. +func (f *File) BeginPieceIndex() int { + if f.t.usualPieceSize() == 0 { + return 0 + } + return pieceIndex(f.offset / int64(f.t.usualPieceSize())) } -func (f *File) lastPieceIndex() pwp.Integer { - return pwp.Integer((f.offset + f.length) / int64(f.t.usualPieceSize())) +// Returns the index of the piece after the last one containing data for the file. +func (f *File) EndPieceIndex() int { + if f.t.usualPieceSize() == 0 { + return 0 + } + return pieceIndex((f.offset + f.length + int64(f.t.usualPieceSize()) - 1) / int64(f.t.usualPieceSize())) }