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