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