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