source: trunk/libtransmission/completion.c @ 9550

Last change on this file since 9550 was 9550, checked in by charles, 12 years ago

(trunk) #2096: Magnet, BEP #9 support

  • Property svn:keywords set to Date Rev Author Id
File size: 11.0 KB
Line 
1/*
2 * This file Copyright (C) 2009 Charles Kerr <charles@transmissionbt.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: completion.c 9550 2009-11-24 02:16:31Z charles $
11 */
12
13#include <assert.h>
14#include <string.h>
15
16#include "transmission.h"
17#include "completion.h"
18#include "torrent.h"
19#include "torrent-magnet.h"
20#include "utils.h"
21
22static void
23tr_cpReset( tr_completion * cp )
24{
25    tr_bitfieldClear( &cp->pieceBitfield );
26    tr_bitfieldClear( &cp->blockBitfield );
27    memset( cp->completeBlocks, 0, sizeof( uint16_t ) * cp->tor->info.pieceCount );
28    cp->sizeNow = 0;
29    cp->sizeWhenDoneIsDirty = 1;
30    cp->haveValidIsDirty = 1;
31}
32
33tr_completion *
34tr_cpConstruct( tr_completion * cp, tr_torrent * tor )
35{
36    cp->tor = tor;
37    cp->completeBlocks  = tr_new( uint16_t, tor->info.pieceCount );
38    tr_bitfieldConstruct( &cp->blockBitfield, tor->blockCount );
39    tr_bitfieldConstruct( &cp->pieceBitfield, tor->info.pieceCount );
40    tr_cpReset( cp );
41    return cp;
42}
43
44tr_completion*
45tr_cpDestruct( tr_completion * cp )
46{
47    tr_free( cp->completeBlocks );
48    tr_bitfieldDestruct( &cp->pieceBitfield );
49    tr_bitfieldDestruct( &cp->blockBitfield );
50    return cp;
51}
52
53void
54tr_cpInvalidateDND( tr_completion * cp )
55{
56    cp->sizeWhenDoneIsDirty = 1;
57}
58
59uint64_t
60tr_cpSizeWhenDone( const tr_completion * ccp )
61{
62    if( ccp->sizeWhenDoneIsDirty )
63    {
64        tr_completion *    cp = (tr_completion *) ccp; /* mutable */
65        const tr_torrent * tor = cp->tor;
66        const tr_info *    info = &tor->info;
67        tr_piece_index_t   i;
68        uint64_t           size = 0;
69
70        for( i = 0; i < info->pieceCount; ++i )
71        {
72            if( !info->pieces[i].dnd )
73            {
74                /* we want the piece... */
75                size += tr_torPieceCountBytes( tor, i );
76            }
77            else if( tr_cpPieceIsComplete( cp, i ) )
78            {
79                /* we have the piece... */
80                size += tr_torPieceCountBytes( tor, i );
81            }
82            else if( cp->completeBlocks[i] )
83            {
84                /* we have part of the piece... */
85                const tr_block_index_t b = tr_torPieceFirstBlock( tor, i );
86                const tr_block_index_t e = b + tr_torPieceCountBlocks( tor, i );
87                tr_block_index_t j;
88                for( j = b; j < e; ++j )
89                    if( tr_cpBlockIsCompleteFast( cp, j ) )
90                        size += tr_torBlockCountBytes( tor, j );
91            }
92        }
93
94        cp->sizeWhenDoneLazy = size;
95        cp->sizeWhenDoneIsDirty = 0;
96    }
97
98    assert( ccp->sizeWhenDoneLazy <= ccp->tor->info.totalSize );
99    assert( ccp->sizeWhenDoneLazy >= ccp->sizeNow );
100    return ccp->sizeWhenDoneLazy;
101}
102
103void
104tr_cpPieceAdd( tr_completion *  cp,
105               tr_piece_index_t piece )
106{
107    const tr_torrent *     tor = cp->tor;
108    const tr_block_index_t start = tr_torPieceFirstBlock( tor, piece );
109    const tr_block_index_t end = start + tr_torPieceCountBlocks( tor, piece );
110    tr_block_index_t       i;
111
112    for( i = start; i < end; ++i )
113        tr_cpBlockAdd( cp, i );
114}
115
116void
117tr_cpPieceRem( tr_completion *  cp,
118               tr_piece_index_t piece )
119{
120    const tr_torrent *     tor = cp->tor;
121    const tr_block_index_t start = tr_torPieceFirstBlock( tor, piece );
122    const tr_block_index_t end = start + tr_torPieceCountBlocks( tor, piece );
123    tr_block_index_t       block;
124
125    assert( cp );
126    assert( piece < tor->info.pieceCount );
127    assert( start < tor->blockCount );
128    assert( start <= end );
129    assert( end <= tor->blockCount );
130
131    for( block = start; block < end; ++block )
132        if( tr_cpBlockIsCompleteFast( cp, block ) )
133            cp->sizeNow -= tr_torBlockCountBytes( tor, block );
134
135    cp->sizeWhenDoneIsDirty = 1;
136    cp->haveValidIsDirty = 1;
137    cp->completeBlocks[piece] = 0;
138    tr_bitfieldRemRange ( &cp->blockBitfield, start, end );
139    tr_bitfieldRem( &cp->pieceBitfield, piece );
140}
141
142void
143tr_cpBlockAdd( tr_completion * cp, tr_block_index_t block )
144{
145    const tr_torrent * tor = cp->tor;
146
147    if( !tr_cpBlockIsComplete( cp, block ) )
148    {
149        const tr_piece_index_t piece = tr_torBlockPiece( tor, block );
150        const int              blockSize = tr_torBlockCountBytes( tor,
151                                                                  block );
152
153        ++cp->completeBlocks[piece];
154
155        if( tr_cpPieceIsComplete( cp, piece ) )
156            tr_bitfieldAdd( &cp->pieceBitfield, piece );
157
158        tr_bitfieldAdd( &cp->blockBitfield, block );
159
160        cp->sizeNow += blockSize;
161
162        cp->haveValidIsDirty = 1;
163        cp->sizeWhenDoneIsDirty = 1;
164    }
165}
166
167/* Initialize a completion object from a bitfield indicating which blocks we have */
168tr_bool
169tr_cpBlockBitfieldSet( tr_completion * cp, tr_bitfield * blockBitfield )
170{
171    int success = FALSE;
172
173    assert( cp );
174    assert( blockBitfield );
175
176    /* The bitfield of block flags is typically loaded from a resume file.
177       Test the bitfield's length in case the resume file somehow got corrupted */
178    if(( success = blockBitfield->byteCount == cp->blockBitfield.byteCount ))
179    {
180        tr_block_index_t b = 0;
181        tr_piece_index_t p = 0;
182        uint32_t pieceBlock = 0;
183        uint32_t completeBlocksInPiece = 0;
184        tr_block_index_t completeBlocksInTorrent = 0;
185        uint32_t blocksInCurrentPiece = tr_torPieceCountBlocks( cp->tor, p );
186
187        /* start cp with a state where it thinks we have nothing */
188        tr_cpReset( cp );
189
190        /* init our block bitfield from the one passed in */
191        memcpy( cp->blockBitfield.bits, blockBitfield->bits, blockBitfield->byteCount );
192
193        /* invalidate the fields that are lazy-evaluated */
194        cp->sizeWhenDoneIsDirty = TRUE;
195        cp->haveValidIsDirty = TRUE;
196
197        /* to set the remaining fields, we walk through every block... */
198        while( b < cp->tor->blockCount )
199        {
200            if( tr_bitfieldHasFast( blockBitfield, b ) )
201                ++completeBlocksInPiece;
202
203            ++b;
204            ++pieceBlock;
205
206            /* by the time we reach the end of a piece, we have enough info
207               to update that piece's slot in cp.completeBlocks and cp.pieceBitfield */
208            if( pieceBlock == blocksInCurrentPiece )
209            {
210                cp->completeBlocks[p] = completeBlocksInPiece;
211                completeBlocksInTorrent += completeBlocksInPiece;
212                if( completeBlocksInPiece == blocksInCurrentPiece )
213                    tr_bitfieldAdd( &cp->pieceBitfield, p );
214
215                /* reset the per-piece counters because we're starting on a new piece now */
216                ++p;
217                completeBlocksInPiece = 0;
218                pieceBlock = 0;
219                blocksInCurrentPiece = tr_torPieceCountBlocks( cp->tor, p );
220            }
221        }
222
223        /* update sizeNow */
224        cp->sizeNow = completeBlocksInTorrent;
225        cp->sizeNow *= tr_torBlockCountBytes( cp->tor, 0 );
226        if( tr_bitfieldHasFast( &cp->blockBitfield, cp->tor->blockCount-1 ) ) {
227            /* the last block is usually smaller than the other blocks,
228               so handle that special case or cp->sizeNow might be too large */
229            cp->sizeNow -= tr_torBlockCountBytes( cp->tor, 0 );
230            cp->sizeNow += tr_torBlockCountBytes( cp->tor, cp->tor->blockCount-1 );
231        }
232    }
233
234    return success;
235}
236
237/***
238****
239***/
240
241tr_completeness
242tr_cpGetStatus( const tr_completion * cp )
243{
244    if( !tr_torrentHasMetadata( cp->tor ) ) return TR_LEECH;
245    if( cp->sizeNow == cp->tor->info.totalSize ) return TR_SEED;
246    if( cp->sizeNow == tr_cpSizeWhenDone( cp ) ) return TR_PARTIAL_SEED;
247    return TR_LEECH;
248}
249
250static uint64_t
251calculateHaveValid( const tr_completion * ccp )
252{
253    uint64_t                  b = 0;
254    tr_piece_index_t          i;
255    const tr_torrent        * tor            = ccp->tor;
256    const uint64_t            pieceSize      = tor->info.pieceSize;
257    const uint64_t            lastPieceSize  = tor->lastPieceSize;
258    const tr_piece_index_t    lastPiece      = tor->info.pieceCount - 1;
259
260    if( !tr_torrentHasMetadata( tor ) )
261        return 0;
262
263    for( i=0; i!=lastPiece; ++i )
264        if( tr_cpPieceIsComplete( ccp, i ) )
265            b += pieceSize;
266
267    if( tr_cpPieceIsComplete( ccp, lastPiece ) )
268        b += lastPieceSize;
269
270    return b;
271}
272
273uint64_t
274tr_cpHaveValid( const tr_completion * ccp )
275{
276    if( ccp->haveValidIsDirty )
277    {
278        tr_completion * cp = (tr_completion *) ccp; /* mutable */
279        cp->haveValidLazy = calculateHaveValid( ccp );
280        cp->haveValidIsDirty = 0;
281    }
282
283    return ccp->haveValidLazy;
284}
285
286void
287tr_cpGetAmountDone( const tr_completion * cp,
288                    float *               tab,
289                    int                   tabCount )
290{
291    int                i;
292    const tr_torrent * tor = cp->tor;
293    const float        interval = tor->info.pieceCount / (float)tabCount;
294    const int          isSeed = tr_cpGetStatus( cp ) == TR_SEED;
295
296    for( i = 0; i < tabCount; ++i )
297    {
298        const tr_piece_index_t piece = i * interval;
299
300        if( tor == NULL )
301            tab[i] = 0.0f;
302        else if( isSeed || tr_cpPieceIsComplete( cp, piece ) )
303            tab[i] = 1.0f;
304        else
305            tab[i] = (float)cp->completeBlocks[piece] /
306                     tr_torPieceCountBlocks( tor, piece );
307    }
308}
309
310int
311tr_cpMissingBlocksInPiece( const tr_completion * cp, tr_piece_index_t piece )
312{
313    return tr_torPieceCountBlocks( cp->tor, piece ) - cp->completeBlocks[piece];
314}
315
316
317tr_bool
318tr_cpPieceIsComplete( const tr_completion * cp, tr_piece_index_t piece )
319{
320    return cp->completeBlocks[piece] == tr_torPieceCountBlocks( cp->tor, piece );
321}
322
323tr_bool
324tr_cpFileIsComplete( const tr_completion * cp, tr_file_index_t fileIndex )
325{
326    tr_block_index_t block;
327
328    const tr_torrent * tor = cp->tor;
329    const tr_file * file = &tor->info.files[fileIndex];
330    const tr_block_index_t firstBlock = file->offset / tor->blockSize;
331    const tr_block_index_t lastBlock = file->length ? ( ( file->offset + file->length - 1 ) / tor->blockSize ) : firstBlock;
332
333    tr_assert( tr_torBlockPiece( tor, firstBlock ) == file->firstPiece,
334               "file->offset %"PRIu64"; file->length %"PRIu64"; "
335               "pieceSize %"PRIu32"; blockSize %"PRIu32"; "
336               "firstBlock %"PRIu64"; lastBlock %"PRIu64,
337               file->offset, file->length,
338               tor->info.pieceSize, tor->blockSize,
339               firstBlock, lastBlock );
340
341    tr_assert( tr_torBlockPiece( tor, lastBlock ) == file->lastPiece,
342               "file->offset %"PRIu64"; file->length %"PRIu64"; "
343               "pieceSize %"PRIu32"; blockSize %"PRIu32"; "
344               "firstBlock %"PRIu64"; lastBlock %"PRIu64,
345               file->offset, file->length,
346               tor->info.pieceSize, tor->blockSize,
347               firstBlock, lastBlock );
348
349    for( block=firstBlock; block<=lastBlock; ++block )
350        if( !tr_cpBlockIsCompleteFast( cp, block ) )
351            return FALSE;
352
353    return TRUE;
354}
Note: See TracBrowser for help on using the repository browser.