]> Sergey Matveev's repositories - btrtrc.git/blobdiff - file.go
Attribute accepted connection to holepunching when connect message is late
[btrtrc.git] / file.go
diff --git a/file.go b/file.go
index 1ec0d21dbf7d447cba0bb1c9c554235e5550c46e..ed2f5da9ced0e74c87a903d1b160a3793331701d 100644 (file)
--- a/file.go
+++ b/file.go
 package torrent
 
-import "github.com/anacrolix/libtorgo/metainfo"
+import (
+       "github.com/RoaringBitmap/roaring"
+       "github.com/anacrolix/missinggo/v2/bitmap"
+
+       "github.com/anacrolix/torrent/metainfo"
+)
 
 // 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
+       t           *Torrent
+       path        string
+       offset      int64
+       length      int64
+       fi          metainfo.FileInfo
+       displayPath string
+       prio        piecePriority
+}
+
+func (f *File) Torrent() *Torrent {
+       return f.t
 }
 
-// Data for this file begins this far into the torrent.
+// Data for this file begins this many bytes into the Torrent.
 func (f *File) Offset() int64 {
        return f.offset
 }
 
+// The FileInfo from the metainfo.Info to which this file corresponds.
 func (f File) FileInfo() metainfo.FileInfo {
        return f.fi
 }
 
+// The file's path components joined by '/'.
 func (f File) Path() string {
        return f.path
 }
 
+// The file's length in bytes.
 func (f *File) Length() int64 {
        return f.length
 }
 
+// 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
+}
+
+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.
 type FilePieceState struct {
-       Length int64
-       State  byte
+       Bytes int64 // Bytes within the piece that are part of this File.
+       PieceState
 }
 
-func (f *File) Progress() (ret []FilePieceState) {
+// Returns the state of pieces in this file.
+func (f *File) State() (ret []FilePieceState) {
+       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
                }
@@ -45,20 +116,64 @@ func (f *File) Progress() (ret []FilePieceState) {
                if len1 > remaining {
                        len1 = remaining
                }
-               ret = append(ret, FilePieceState{len1, f.t.pieceStatusChar(i)})
+               ps := f.t.pieceState(i)
+               ret = append(ret, FilePieceState{len1, ps})
                off = 0
                remaining -= len1
        }
        return
 }
 
-func (f *File) PrioritizeRegion(off, len int64) {
-       if off < 0 || off >= f.length {
-               return
+// Requests that all pieces containing data in the file be downloaded.
+func (f *File) Download() {
+       f.SetPriority(PiecePriorityNormal)
+}
+
+func byteRegionExclusivePieces(off, size, pieceSize int64) (begin, end int) {
+       begin = int((off + pieceSize - 1) / pieceSize)
+       end = int((off + size) / pieceSize)
+       return
+}
+
+// Deprecated: Use File.SetPriority.
+func (f *File) Cancel() {
+       f.SetPriority(PiecePriorityNone)
+}
+
+func (f *File) NewReader() Reader {
+       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.lock()
+       if prio != f.prio {
+               f.prio = prio
+               f.t.updatePiecePriorities(f.BeginPieceIndex(), f.EndPieceIndex(), "File.SetPriority")
+       }
+       f.t.cl.unlock()
+}
+
+// Returns the priority per File.SetPriority.
+func (f *File) Priority() (prio piecePriority) {
+       f.t.cl.rLock()
+       prio = f.prio
+       f.t.cl.rUnlock()
+       return
+}
+
+// Returns the index of the first piece containing data for the file.
+func (f *File) BeginPieceIndex() int {
+       if f.t.usualPieceSize() == 0 {
+               return 0
        }
-       if off+len > f.length {
-               len = f.length - off
+       return pieceIndex(f.offset / 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
        }
-       off += f.offset
-       f.t.SetRegionPriority(off, len)
+       return pieceIndex((f.offset + f.length + int64(f.t.usualPieceSize()) - 1) / int64(f.t.usualPieceSize()))
 }