github.com/anacrolix/torrent@v1.61.0/file.go (about)

     1  package torrent
     2  
     3  import (
     4  	"iter"
     5  
     6  	"github.com/RoaringBitmap/roaring"
     7  	g "github.com/anacrolix/generics"
     8  	"github.com/anacrolix/missinggo/v2/bitmap"
     9  
    10  	"github.com/anacrolix/torrent/metainfo"
    11  	infohash_v2 "github.com/anacrolix/torrent/types/infohash-v2"
    12  )
    13  
    14  // Provides access to regions of torrent data that correspond to its files.
    15  type File struct {
    16  	t           *Torrent
    17  	path        string
    18  	offset      int64
    19  	length      int64
    20  	fi          metainfo.FileInfo
    21  	displayPath string
    22  	prio        PiecePriority
    23  	piecesRoot  g.Option[infohash_v2.T]
    24  }
    25  
    26  func (f *File) String() string {
    27  	return f.Path()
    28  }
    29  
    30  func (f *File) Torrent() *Torrent {
    31  	return f.t
    32  }
    33  
    34  // Data for this file begins this many bytes into the Torrent.
    35  func (f *File) Offset() int64 {
    36  	return f.offset
    37  }
    38  
    39  // The FileInfo from the metainfo.Info to which this file corresponds.
    40  func (f *File) FileInfo() metainfo.FileInfo {
    41  	return f.fi
    42  }
    43  
    44  // The file's path components including the directory name joined by '/'.
    45  func (f *File) Path() string {
    46  	return f.path
    47  }
    48  
    49  // The file's length in bytes.
    50  func (f *File) Length() int64 {
    51  	return f.length
    52  }
    53  
    54  // Number of bytes of the entire file we have completed. This is the sum of
    55  // completed pieces, and dirtied chunks of incomplete pieces.
    56  func (f *File) BytesCompleted() (n int64) {
    57  	f.t.cl.rLock()
    58  	n = f.bytesCompletedLocked()
    59  	f.t.cl.rUnlock()
    60  	return
    61  }
    62  
    63  func (f *File) bytesCompletedLocked() int64 {
    64  	return f.length - f.bytesLeft()
    65  }
    66  
    67  func fileBytesLeft(
    68  	torrentUsualPieceSize int64,
    69  	fileFirstPieceIndex int,
    70  	fileEndPieceIndex int,
    71  	fileTorrentOffset int64,
    72  	fileLength int64,
    73  	torrentCompletedPieces *roaring.Bitmap,
    74  	pieceSizeCompletedFn func(pieceIndex int) int64,
    75  ) (left int64) {
    76  	if fileLength == 0 {
    77  		return
    78  	}
    79  
    80  	noCompletedMiddlePieces := roaring.New()
    81  	noCompletedMiddlePieces.AddRange(bitmap.BitRange(fileFirstPieceIndex), bitmap.BitRange(fileEndPieceIndex))
    82  	noCompletedMiddlePieces.AndNot(torrentCompletedPieces)
    83  	noCompletedMiddlePieces.Iterate(func(pieceIndex uint32) bool {
    84  		i := int(pieceIndex)
    85  		pieceSizeCompleted := pieceSizeCompletedFn(i)
    86  		if i == fileFirstPieceIndex {
    87  			beginOffset := fileTorrentOffset % torrentUsualPieceSize
    88  			beginSize := torrentUsualPieceSize - beginOffset
    89  			beginDownLoaded := pieceSizeCompleted - beginOffset
    90  			if beginDownLoaded < 0 {
    91  				beginDownLoaded = 0
    92  			}
    93  			left += beginSize - beginDownLoaded
    94  		} else if i == fileEndPieceIndex-1 {
    95  			endSize := (fileTorrentOffset + fileLength) % torrentUsualPieceSize
    96  			if endSize == 0 {
    97  				endSize = torrentUsualPieceSize
    98  			}
    99  			endDownloaded := pieceSizeCompleted
   100  			if endDownloaded > endSize {
   101  				endDownloaded = endSize
   102  			}
   103  			left += endSize - endDownloaded
   104  		} else {
   105  			left += torrentUsualPieceSize - pieceSizeCompleted
   106  		}
   107  		return true
   108  	})
   109  
   110  	if left > fileLength {
   111  		left = fileLength
   112  	}
   113  	//
   114  	//numPiecesSpanned := f.EndPieceIndex() - f.BeginPieceIndex()
   115  	//completedMiddlePieces := f.t._completedPieces.Clone()
   116  	//completedMiddlePieces.RemoveRange(0, bitmap.BitRange(f.BeginPieceIndex()+1))
   117  	//completedMiddlePieces.RemoveRange(bitmap.BitRange(f.EndPieceIndex()-1), bitmap.ToEnd)
   118  	//left += int64(numPiecesSpanned-2-pieceIndex(completedMiddlePieces.GetCardinality())) * torrentUsualPieceSize
   119  	return
   120  }
   121  
   122  func (f *File) bytesLeft() (left int64) {
   123  	return fileBytesLeft(
   124  		int64(f.t.usualPieceSize()),
   125  		f.BeginPieceIndex(),
   126  		f.EndPieceIndex(),
   127  		f.offset,
   128  		f.length,
   129  		&f.t._completedPieces,
   130  		func(pieceIndex int) int64 {
   131  			return int64(f.t.piece(pieceIndex).numDirtyBytes())
   132  		},
   133  	)
   134  }
   135  
   136  // The relative file path for a multi-file torrent, and the torrent name for a
   137  // single-file torrent. Dir separators are '/'.
   138  func (f *File) DisplayPath() string {
   139  	return f.displayPath
   140  }
   141  
   142  // The download status of a piece that comprises part of a File.
   143  type FilePieceState struct {
   144  	Bytes int64 // Bytes within the piece that are part of this File.
   145  	PieceState
   146  }
   147  
   148  // Returns the state of pieces in this file.
   149  func (f *File) State() (ret []FilePieceState) {
   150  	f.t.cl.rLock()
   151  	defer f.t.cl.rUnlock()
   152  	pieceSize := int64(f.t.usualPieceSize())
   153  	off := f.offset % pieceSize
   154  	remaining := f.length
   155  	for i := pieceIndex(f.offset / pieceSize); ; i++ {
   156  		if remaining == 0 {
   157  			break
   158  		}
   159  		len1 := pieceSize - off
   160  		if len1 > remaining {
   161  			len1 = remaining
   162  		}
   163  		ps := f.t.pieceState(i)
   164  		ret = append(ret, FilePieceState{len1, ps})
   165  		off = 0
   166  		remaining -= len1
   167  	}
   168  	return
   169  }
   170  
   171  // Requests that all pieces containing data in the file be downloaded.
   172  func (f *File) Download() {
   173  	f.SetPriority(PiecePriorityNormal)
   174  }
   175  
   176  func byteRegionExclusivePieces(off, size, pieceSize int64) (begin, end int) {
   177  	begin = int((off + pieceSize - 1) / pieceSize)
   178  	end = int((off + size) / pieceSize)
   179  	return
   180  }
   181  
   182  // Deprecated: Use File.SetPriority.
   183  func (f *File) Cancel() {
   184  	f.SetPriority(PiecePriorityNone)
   185  }
   186  
   187  func (f *File) NewReader() Reader {
   188  	return f.t.newReader(f.Offset(), f.Length())
   189  }
   190  
   191  // Sets the minimum priority for pieces in the File.
   192  func (f *File) SetPriority(prio PiecePriority) {
   193  	f.t.cl.lock()
   194  	if prio != f.prio {
   195  		f.prio = prio
   196  		f.t.updatePiecePriorities(f.BeginPieceIndex(), f.EndPieceIndex(), "File.SetPriority")
   197  	}
   198  	f.t.cl.unlock()
   199  }
   200  
   201  // Returns the priority per File.SetPriority.
   202  func (f *File) Priority() (prio PiecePriority) {
   203  	f.t.cl.rLock()
   204  	prio = f.prio
   205  	f.t.cl.rUnlock()
   206  	return
   207  }
   208  
   209  // Returns the index of the first piece containing data for the file.
   210  func (f *File) BeginPieceIndex() int {
   211  	return f.fi.BeginPieceIndex(int64(f.t.usualPieceSize()))
   212  }
   213  
   214  // Returns the index of the piece after the last one containing data for the file.
   215  func (f *File) EndPieceIndex() int {
   216  	return f.fi.EndPieceIndex(int64(f.t.usualPieceSize()))
   217  }
   218  
   219  func (f *File) numPieces() int {
   220  	return f.EndPieceIndex() - f.BeginPieceIndex()
   221  }
   222  
   223  func (f *File) PieceIndices() iter.Seq[int] {
   224  	return func(yield func(int) bool) {
   225  		for i := f.BeginPieceIndex(); i < f.EndPieceIndex(); i++ {
   226  			if !yield(i) {
   227  				break
   228  			}
   229  		}
   230  	}
   231  }
   232  
   233  func (f *File) Pieces() iter.Seq[*Piece] {
   234  	return func(yield func(*Piece) bool) {
   235  		for i := range f.PieceIndices() {
   236  			p := f.t.piece(i)
   237  			if !yield(p) {
   238  				break
   239  			}
   240  		}
   241  	}
   242  }