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