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