]> Sergey Matveev's repositories - btrtrc.git/blob - t.go
Make readahead algorithm linear
[btrtrc.git] / t.go
1 package torrent
2
3 import (
4         "strconv"
5         "strings"
6
7         "github.com/anacrolix/missinggo/pubsub"
8
9         "github.com/anacrolix/torrent/metainfo"
10 )
11
12 // The Torrent's infohash. This is fixed and cannot change. It uniquely identifies a torrent.
13 func (t *Torrent) InfoHash() metainfo.Hash {
14         return t.infoHash
15 }
16
17 // Returns a channel that is closed when the info (.Info()) for the torrent has become available.
18 func (t *Torrent) GotInfo() <-chan struct{} {
19         // TODO: We shouldn't need to lock to take a channel here, if the event is only ever set.
20         t.cl.lock()
21         defer t.cl.unlock()
22         return t.gotMetainfo.C()
23 }
24
25 // Returns the metainfo info dictionary, or nil if it's not yet available.
26 func (t *Torrent) Info() *metainfo.Info {
27         t.cl.lock()
28         defer t.cl.unlock()
29         return t.info
30 }
31
32 // Returns a Reader bound to the torrent's data. All read calls block until the data requested is
33 // actually available. Note that you probably want to ensure the Torrent Info is available first.
34 func (t *Torrent) NewReader() Reader {
35         return t.newReader(0, *t.length)
36 }
37
38 func (t *Torrent) newReader(offset, length int64) Reader {
39         r := reader{
40                 mu:     t.cl.locker(),
41                 t:      t,
42                 offset: offset,
43                 length: length,
44         }
45         r.readaheadFunc = r.defaultReadaheadFunc
46         t.addReader(&r)
47         return &r
48 }
49
50 type PieceStateRuns []PieceStateRun
51
52 func (me PieceStateRuns) String() (s string) {
53         if len(me) > 0 {
54                 var sb strings.Builder
55                 sb.WriteString(me[0].String())
56                 for i := 1; i < len(me); i += 1 {
57                         sb.WriteByte(' ')
58                         sb.WriteString(me[i].String())
59                 }
60                 return sb.String()
61         }
62         return
63 }
64
65 // Returns the state of pieces of the torrent. They are grouped into runs of same state. The sum of
66 // the state run-lengths is the number of pieces in the torrent.
67 func (t *Torrent) PieceStateRuns() (runs PieceStateRuns) {
68         t.cl.rLock()
69         runs = t.pieceStateRuns()
70         t.cl.rUnlock()
71         return
72 }
73
74 func (t *Torrent) PieceState(piece pieceIndex) PieceState {
75         t.cl.rLock()
76         defer t.cl.rUnlock()
77         return t.pieceState(piece)
78 }
79
80 // The number of pieces in the torrent. This requires that the info has been
81 // obtained first.
82 func (t *Torrent) NumPieces() pieceIndex {
83         return t.numPieces()
84 }
85
86 // Get missing bytes count for specific piece.
87 func (t *Torrent) PieceBytesMissing(piece int) int64 {
88         t.cl.lock()
89         defer t.cl.unlock()
90
91         return int64(t.pieces[piece].bytesLeft())
92 }
93
94 // Drop the torrent from the client, and close it. It's always safe to do
95 // this. No data corruption can, or should occur to either the torrent's data,
96 // or connected peers.
97 func (t *Torrent) Drop() {
98         t.cl.lock()
99         defer t.cl.unlock()
100         t.cl.dropTorrent(t.infoHash)
101 }
102
103 // Number of bytes of the entire torrent we have completed. This is the sum of
104 // completed pieces, and dirtied chunks of incomplete pieces. Do not use this
105 // for download rate, as it can go down when pieces are lost or fail checks.
106 // Sample Torrent.Stats.DataBytesRead for actual file data download rate.
107 func (t *Torrent) BytesCompleted() int64 {
108         t.cl.rLock()
109         defer t.cl.rUnlock()
110         return t.bytesCompleted()
111 }
112
113 // The subscription emits as (int) the index of pieces as their state changes.
114 // A state change is when the PieceState for a piece alters in value.
115 func (t *Torrent) SubscribePieceStateChanges() *pubsub.Subscription {
116         return t.pieceStateChanges.Subscribe()
117 }
118
119 // Returns true if the torrent is currently being seeded. This occurs when the
120 // client is willing to upload without wanting anything in return.
121 func (t *Torrent) Seeding() bool {
122         t.cl.lock()
123         defer t.cl.unlock()
124         return t.seeding()
125 }
126
127 // Clobbers the torrent display name. The display name is used as the torrent
128 // name if the metainfo is not available.
129 func (t *Torrent) SetDisplayName(dn string) {
130         t.nameMu.Lock()
131         defer t.nameMu.Unlock()
132         if t.haveInfo() {
133                 return
134         }
135         t.displayName = dn
136 }
137
138 // The current working name for the torrent. Either the name in the info dict,
139 // or a display name given such as by the dn value in a magnet link, or "".
140 func (t *Torrent) Name() string {
141         return t.name()
142 }
143
144 // The completed length of all the torrent data, in all its files. This is
145 // derived from the torrent info, when it is available.
146 func (t *Torrent) Length() int64 {
147         return *t.length
148 }
149
150 // Returns a run-time generated metainfo for the torrent that includes the
151 // info bytes and announce-list as currently known to the client.
152 func (t *Torrent) Metainfo() metainfo.MetaInfo {
153         t.cl.lock()
154         defer t.cl.unlock()
155         return t.newMetaInfo()
156 }
157
158 func (t *Torrent) addReader(r *reader) {
159         t.cl.lock()
160         defer t.cl.unlock()
161         if t.readers == nil {
162                 t.readers = make(map[*reader]struct{})
163         }
164         t.readers[r] = struct{}{}
165         r.posChanged()
166 }
167
168 func (t *Torrent) deleteReader(r *reader) {
169         delete(t.readers, r)
170         t.readersChanged()
171 }
172
173 // Raise the priorities of pieces in the range [begin, end) to at least Normal
174 // priority. Piece indexes are not the same as bytes. Requires that the info
175 // has been obtained, see Torrent.Info and Torrent.GotInfo.
176 func (t *Torrent) DownloadPieces(begin, end pieceIndex) {
177         t.cl.lock()
178         defer t.cl.unlock()
179         t.downloadPiecesLocked(begin, end)
180 }
181
182 func (t *Torrent) downloadPiecesLocked(begin, end pieceIndex) {
183         for i := begin; i < end; i++ {
184                 if t.pieces[i].priority.Raise(PiecePriorityNormal) {
185                         t.updatePiecePriority(i)
186                 }
187         }
188 }
189
190 func (t *Torrent) CancelPieces(begin, end pieceIndex) {
191         t.cl.lock()
192         defer t.cl.unlock()
193         t.cancelPiecesLocked(begin, end)
194 }
195
196 func (t *Torrent) cancelPiecesLocked(begin, end pieceIndex) {
197         for i := begin; i < end; i++ {
198                 p := &t.pieces[i]
199                 if p.priority == PiecePriorityNone {
200                         continue
201                 }
202                 p.priority = PiecePriorityNone
203                 t.updatePiecePriority(i)
204         }
205 }
206
207 func (t *Torrent) initFiles() {
208         var offset int64
209         t.files = new([]*File)
210         for _, fi := range t.info.UpvertedFiles() {
211                 var path []string
212                 if len(fi.PathUTF8) != 0 {
213                         path = fi.PathUTF8
214                 } else {
215                         path = fi.Path
216                 }
217                 dp := t.info.Name
218                 if len(fi.Path) != 0 {
219                         dp = strings.Join(fi.Path, "/")
220                 }
221                 *t.files = append(*t.files, &File{
222                         t,
223                         strings.Join(append([]string{t.info.Name}, path...), "/"),
224                         offset,
225                         fi.Length,
226                         fi,
227                         dp,
228                         PiecePriorityNone,
229                 })
230                 offset += fi.Length
231         }
232
233 }
234
235 // Returns handles to the files in the torrent. This requires that the Info is
236 // available first.
237 func (t *Torrent) Files() []*File {
238         return *t.files
239 }
240
241 func (t *Torrent) AddPeers(pp []PeerInfo) int {
242         cl := t.cl
243         cl.lock()
244         defer cl.unlock()
245         return t.addPeers(pp)
246 }
247
248 // Marks the entire torrent for download. Requires the info first, see
249 // GotInfo. Sets piece priorities for historical reasons.
250 func (t *Torrent) DownloadAll() {
251         t.DownloadPieces(0, t.numPieces())
252 }
253
254 func (t *Torrent) String() string {
255         s := t.name()
256         if s == "" {
257                 return t.infoHash.HexString()
258         } else {
259                 return strconv.Quote(s)
260         }
261 }
262
263 func (t *Torrent) AddTrackers(announceList [][]string) {
264         t.cl.lock()
265         defer t.cl.unlock()
266         t.addTrackers(announceList)
267 }
268
269 func (t *Torrent) Piece(i pieceIndex) *Piece {
270         return t.piece(i)
271 }
272
273 func (t *Torrent) PeerConns() []*PeerConn {
274         t.cl.rLock()
275         defer t.cl.rUnlock()
276         ret := make([]*PeerConn, 0, len(t.conns))
277         for c := range t.conns {
278                 ret = append(ret, c)
279         }
280         return ret
281 }