1 /*
2 * This file Copyright (C) 2009-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 */
8
9 #include "transmission.h"
10 #include "completion.h"
11 #include "torrent.h"
12 #include "tr-assert.h"
13 #include "utils.h"
14
15 /***
16 ****
17 ***/
18
tr_cpReset(tr_completion * cp)19 static void tr_cpReset(tr_completion* cp)
20 {
21 cp->sizeNow = 0;
22 cp->sizeWhenDoneIsDirty = true;
23 cp->haveValidIsDirty = true;
24 tr_bitfieldSetHasNone(&cp->blockBitfield);
25 }
26
tr_cpConstruct(tr_completion * cp,tr_torrent * tor)27 void tr_cpConstruct(tr_completion* cp, tr_torrent* tor)
28 {
29 cp->tor = tor;
30 tr_bitfieldConstruct(&cp->blockBitfield, tor->blockCount);
31 tr_cpReset(cp);
32 }
33
tr_cpBlockInit(tr_completion * cp,tr_bitfield const * b)34 void tr_cpBlockInit(tr_completion* cp, tr_bitfield const* b)
35 {
36 tr_cpReset(cp);
37
38 /* set blockBitfield */
39 tr_bitfieldSetFromBitfield(&cp->blockBitfield, b);
40
41 /* set sizeNow */
42 cp->sizeNow = tr_bitfieldCountTrueBits(&cp->blockBitfield);
43 TR_ASSERT(cp->sizeNow <= cp->tor->blockCount);
44 cp->sizeNow *= cp->tor->blockSize;
45
46 if (tr_bitfieldHas(b, cp->tor->blockCount - 1))
47 {
48 cp->sizeNow -= (cp->tor->blockSize - cp->tor->lastBlockSize);
49 }
50
51 TR_ASSERT(cp->sizeNow <= cp->tor->info.totalSize);
52 }
53
54 /***
55 ****
56 ***/
57
tr_cpGetStatus(tr_completion const * cp)58 tr_completeness tr_cpGetStatus(tr_completion const* cp)
59 {
60 if (tr_cpHasAll(cp))
61 {
62 return TR_SEED;
63 }
64
65 if (!tr_torrentHasMetadata(cp->tor))
66 {
67 return TR_LEECH;
68 }
69
70 if (cp->sizeNow == tr_cpSizeWhenDone(cp))
71 {
72 return TR_PARTIAL_SEED;
73 }
74
75 return TR_LEECH;
76 }
77
tr_cpPieceRem(tr_completion * cp,tr_piece_index_t piece)78 void tr_cpPieceRem(tr_completion* cp, tr_piece_index_t piece)
79 {
80 tr_block_index_t f;
81 tr_block_index_t l;
82 tr_torrent const* tor = cp->tor;
83
84 tr_torGetPieceBlockRange(cp->tor, piece, &f, &l);
85
86 for (tr_block_index_t i = f; i <= l; ++i)
87 {
88 if (tr_cpBlockIsComplete(cp, i))
89 {
90 cp->sizeNow -= tr_torBlockCountBytes(tor, i);
91 }
92 }
93
94 cp->haveValidIsDirty = true;
95 cp->sizeWhenDoneIsDirty = true;
96 tr_bitfieldRemRange(&cp->blockBitfield, f, l + 1);
97 }
98
tr_cpPieceAdd(tr_completion * cp,tr_piece_index_t piece)99 void tr_cpPieceAdd(tr_completion* cp, tr_piece_index_t piece)
100 {
101 tr_block_index_t f;
102 tr_block_index_t l;
103 tr_torGetPieceBlockRange(cp->tor, piece, &f, &l);
104
105 for (tr_block_index_t i = f; i <= l; ++i)
106 {
107 tr_cpBlockAdd(cp, i);
108 }
109 }
110
tr_cpBlockAdd(tr_completion * cp,tr_block_index_t block)111 void tr_cpBlockAdd(tr_completion* cp, tr_block_index_t block)
112 {
113 tr_torrent const* tor = cp->tor;
114
115 if (!tr_cpBlockIsComplete(cp, block))
116 {
117 tr_piece_index_t const piece = tr_torBlockPiece(cp->tor, block);
118
119 tr_bitfieldAdd(&cp->blockBitfield, block);
120 cp->sizeNow += tr_torBlockCountBytes(tor, block);
121
122 cp->haveValidIsDirty = true;
123 cp->sizeWhenDoneIsDirty = cp->sizeWhenDoneIsDirty || tor->info.pieces[piece].dnd;
124 }
125 }
126
127 /***
128 ****
129 ***/
130
tr_cpHaveValid(tr_completion const * ccp)131 uint64_t tr_cpHaveValid(tr_completion const* ccp)
132 {
133 if (ccp->haveValidIsDirty)
134 {
135 uint64_t size = 0;
136 tr_completion* cp = (tr_completion*)ccp; /* mutable */
137 tr_torrent const* tor = ccp->tor;
138 tr_info const* info = &tor->info;
139
140 for (tr_piece_index_t i = 0; i < info->pieceCount; ++i)
141 {
142 if (tr_cpPieceIsComplete(ccp, i))
143 {
144 size += tr_torPieceCountBytes(tor, i);
145 }
146 }
147
148 cp->haveValidLazy = size;
149 cp->haveValidIsDirty = false;
150 }
151
152 return ccp->haveValidLazy;
153 }
154
tr_cpSizeWhenDone(tr_completion const * ccp)155 uint64_t tr_cpSizeWhenDone(tr_completion const* ccp)
156 {
157 if (ccp->sizeWhenDoneIsDirty)
158 {
159 uint64_t size = 0;
160 tr_torrent const* tor = ccp->tor;
161 tr_info const* inf = tr_torrentInfo(tor);
162 tr_completion* cp = (tr_completion*)ccp; /* mutable */
163
164 if (tr_cpHasAll(ccp))
165 {
166 size = inf->totalSize;
167 }
168 else
169 {
170 for (tr_piece_index_t p = 0; p < inf->pieceCount; ++p)
171 {
172 uint64_t n = 0;
173 uint64_t const pieceSize = tr_torPieceCountBytes(tor, p);
174
175 if (!inf->pieces[p].dnd)
176 {
177 n = pieceSize;
178 }
179 else
180 {
181 tr_block_index_t f;
182 tr_block_index_t l;
183 tr_torGetPieceBlockRange(cp->tor, p, &f, &l);
184
185 n = tr_bitfieldCountRange(&cp->blockBitfield, f, l + 1);
186 n *= cp->tor->blockSize;
187
188 if (l == cp->tor->blockCount - 1 && tr_bitfieldHas(&cp->blockBitfield, l))
189 {
190 n -= cp->tor->blockSize - cp->tor->lastBlockSize;
191 }
192 }
193
194 TR_ASSERT(n <= tr_torPieceCountBytes(tor, p));
195 size += n;
196 }
197 }
198
199 TR_ASSERT(size <= inf->totalSize);
200 TR_ASSERT(size >= cp->sizeNow);
201
202 cp->sizeWhenDoneLazy = size;
203 cp->sizeWhenDoneIsDirty = false;
204 }
205
206 return ccp->sizeWhenDoneLazy;
207 }
208
tr_cpLeftUntilDone(tr_completion const * cp)209 uint64_t tr_cpLeftUntilDone(tr_completion const* cp)
210 {
211 uint64_t const sizeWhenDone = tr_cpSizeWhenDone(cp);
212
213 TR_ASSERT(sizeWhenDone >= cp->sizeNow);
214
215 return sizeWhenDone - cp->sizeNow;
216 }
217
tr_cpGetAmountDone(tr_completion const * cp,float * tab,int tabCount)218 void tr_cpGetAmountDone(tr_completion const* cp, float* tab, int tabCount)
219 {
220 bool const seed = tr_cpHasAll(cp);
221 float const interval = cp->tor->info.pieceCount / (float)tabCount;
222
223 for (int i = 0; i < tabCount; ++i)
224 {
225 if (seed)
226 {
227 tab[i] = 1.0F;
228 }
229 else
230 {
231 tr_block_index_t f;
232 tr_block_index_t l;
233 tr_piece_index_t const piece = (tr_piece_index_t)i * interval;
234 tr_torGetPieceBlockRange(cp->tor, piece, &f, &l);
235 tab[i] = tr_bitfieldCountRange(&cp->blockBitfield, f, l + 1) / (float)(l + 1 - f);
236 }
237 }
238 }
239
tr_cpMissingBlocksInPiece(tr_completion const * cp,tr_piece_index_t piece)240 size_t tr_cpMissingBlocksInPiece(tr_completion const* cp, tr_piece_index_t piece)
241 {
242 if (tr_cpHasAll(cp))
243 {
244 return 0;
245 }
246 else
247 {
248 tr_block_index_t f;
249 tr_block_index_t l;
250 tr_torGetPieceBlockRange(cp->tor, piece, &f, &l);
251 return (l + 1 - f) - tr_bitfieldCountRange(&cp->blockBitfield, f, l + 1);
252 }
253 }
254
tr_cpMissingBytesInPiece(tr_completion const * cp,tr_piece_index_t piece)255 size_t tr_cpMissingBytesInPiece(tr_completion const* cp, tr_piece_index_t piece)
256 {
257 if (tr_cpHasAll(cp))
258 {
259 return 0;
260 }
261 else
262 {
263 size_t haveBytes = 0;
264 tr_block_index_t f;
265 tr_block_index_t l;
266 size_t const pieceByteSize = tr_torPieceCountBytes(cp->tor, piece);
267 tr_torGetPieceBlockRange(cp->tor, piece, &f, &l);
268
269 if (f != l)
270 {
271 /* nb: we don't pass the usual l+1 here to tr_bitfieldCountRange().
272 It's faster to handle the last block separately because its size
273 needs to be checked separately. */
274 haveBytes = tr_bitfieldCountRange(&cp->blockBitfield, f, l);
275 haveBytes *= cp->tor->blockSize;
276 }
277
278 if (tr_bitfieldHas(&cp->blockBitfield, l)) /* handle the last block */
279 {
280 haveBytes += tr_torBlockCountBytes(cp->tor, l);
281 }
282
283 TR_ASSERT(haveBytes <= pieceByteSize);
284 return pieceByteSize - haveBytes;
285 }
286 }
287
tr_cpFileIsComplete(tr_completion const * cp,tr_file_index_t i)288 bool tr_cpFileIsComplete(tr_completion const* cp, tr_file_index_t i)
289 {
290 if (cp->tor->info.files[i].length == 0)
291 {
292 return true;
293 }
294 else
295 {
296 tr_block_index_t f;
297 tr_block_index_t l;
298 tr_torGetFileBlockRange(cp->tor, i, &f, &l);
299 return tr_bitfieldCountRange(&cp->blockBitfield, f, l + 1) == (l + 1 - f);
300 }
301 }
302
tr_cpCreatePieceBitfield(tr_completion const * cp,size_t * byte_count)303 void* tr_cpCreatePieceBitfield(tr_completion const* cp, size_t* byte_count)
304 {
305 TR_ASSERT(tr_torrentHasMetadata(cp->tor));
306
307 void* ret;
308 tr_piece_index_t n;
309 tr_bitfield pieces;
310
311 n = cp->tor->info.pieceCount;
312 tr_bitfieldConstruct(&pieces, n);
313
314 if (tr_cpHasAll(cp))
315 {
316 tr_bitfieldSetHasAll(&pieces);
317 }
318 else if (!tr_cpHasNone(cp))
319 {
320 bool* flags = tr_new(bool, n);
321
322 for (tr_piece_index_t i = 0; i < n; ++i)
323 {
324 flags[i] = tr_cpPieceIsComplete(cp, i);
325 }
326
327 tr_bitfieldSetFromFlags(&pieces, flags, n);
328 tr_free(flags);
329 }
330
331 ret = tr_bitfieldGetRaw(&pieces, byte_count);
332 tr_bitfieldDestruct(&pieces);
333 return ret;
334 }
335
tr_cpPercentComplete(tr_completion const * cp)336 double tr_cpPercentComplete(tr_completion const* cp)
337 {
338 double const ratio = tr_getRatio(cp->sizeNow, cp->tor->info.totalSize);
339
340 if ((int)ratio == TR_RATIO_NA)
341 {
342 return 0.0;
343 }
344 else if ((int)ratio == TR_RATIO_INF)
345 {
346 return 1.0;
347 }
348 else
349 {
350 return ratio;
351 }
352 }
353
tr_cpPercentDone(tr_completion const * cp)354 double tr_cpPercentDone(tr_completion const* cp)
355 {
356 double const ratio = tr_getRatio(cp->sizeNow, tr_cpSizeWhenDone(cp));
357 int const iratio = (int)ratio;
358 return (iratio == TR_RATIO_NA || iratio == TR_RATIO_INF) ? 0.0 : ratio;
359 }
360