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 }