source: trunk/libtransmission/completion.c @ 10387

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

(trunk libT) #3051 "faster startup by adding 'have all' idiom to .resume files" -- implemented in trunk for 2.00

  • Property svn:keywords set to Date Rev Author Id
File size: 10.8 KB
Line 
1/*
2 * This file Copyright (C) 2009-2010 Mnemosyne LLC
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 10387 2010-03-15 23:29:56Z 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
168void
169tr_cpSetHaveAll( tr_completion * cp )
170{
171    tr_piece_index_t i;
172    tr_torrent * tor = cp->tor;
173
174    tr_cpReset( cp );
175
176    cp->sizeNow = tor->info.totalSize;
177    tr_bitfieldAddRange( &cp->blockBitfield, 0, tor->blockCount );
178    tr_bitfieldAddRange( &cp->pieceBitfield, 0, tor->info.pieceCount );
179    for( i=0; i<tor->info.pieceCount; ++i )
180        cp->completeBlocks[i] = tr_torPieceCountBlocks( tor, i );
181    cp->sizeWhenDoneIsDirty = 1;
182    cp->haveValidIsDirty = 1;
183}
184
185/* Initialize a completion object from a bitfield indicating which blocks we have */
186tr_bool
187tr_cpBlockBitfieldSet( tr_completion * cp, tr_bitfield * blockBitfield )
188{
189    int success = FALSE;
190
191    assert( cp );
192    assert( blockBitfield );
193
194    /* The bitfield of block flags is typically loaded from a resume file.
195       Test the bitfield's length in case the resume file somehow got corrupted */
196    if(( success = blockBitfield->byteCount == cp->blockBitfield.byteCount ))
197    {
198        tr_block_index_t b = 0;
199        tr_piece_index_t p = 0;
200        uint32_t pieceBlock = 0;
201        uint32_t completeBlocksInPiece = 0;
202        tr_block_index_t completeBlocksInTorrent = 0;
203        uint32_t blocksInCurrentPiece = tr_torPieceCountBlocks( cp->tor, p );
204
205        /* start cp with a state where it thinks we have nothing */
206        tr_cpReset( cp );
207
208        /* init our block bitfield from the one passed in */
209        memcpy( cp->blockBitfield.bits, blockBitfield->bits, blockBitfield->byteCount );
210
211        /* invalidate the fields that are lazy-evaluated */
212        cp->sizeWhenDoneIsDirty = TRUE;
213        cp->haveValidIsDirty = TRUE;
214
215        /* to set the remaining fields, we walk through every block... */
216        while( b < cp->tor->blockCount )
217        {
218            if( tr_bitfieldHasFast( blockBitfield, b ) )
219                ++completeBlocksInPiece;
220
221            ++b;
222            ++pieceBlock;
223
224            /* by the time we reach the end of a piece, we have enough info
225               to update that piece's slot in cp.completeBlocks and cp.pieceBitfield */
226            if( pieceBlock == blocksInCurrentPiece )
227            {
228                cp->completeBlocks[p] = completeBlocksInPiece;
229                completeBlocksInTorrent += completeBlocksInPiece;
230                if( completeBlocksInPiece == blocksInCurrentPiece )
231                    tr_bitfieldAdd( &cp->pieceBitfield, p );
232
233                /* reset the per-piece counters because we're starting on a new piece now */
234                ++p;
235                completeBlocksInPiece = 0;
236                pieceBlock = 0;
237                blocksInCurrentPiece = tr_torPieceCountBlocks( cp->tor, p );
238            }
239        }
240
241        /* update sizeNow */
242        cp->sizeNow = completeBlocksInTorrent;
243        cp->sizeNow *= tr_torBlockCountBytes( cp->tor, 0 );
244        if( tr_bitfieldHasFast( &cp->blockBitfield, cp->tor->blockCount-1 ) ) {
245            /* the last block is usually smaller than the other blocks,
246               so handle that special case or cp->sizeNow might be too large */
247            cp->sizeNow -= tr_torBlockCountBytes( cp->tor, 0 );
248            cp->sizeNow += tr_torBlockCountBytes( cp->tor, cp->tor->blockCount-1 );
249        }
250    }
251
252    return success;
253}
254
255/***
256****
257***/
258
259tr_completeness
260tr_cpGetStatus( const tr_completion * cp )
261{
262    if( !tr_torrentHasMetadata( cp->tor ) ) return TR_LEECH;
263    if( cp->sizeNow == cp->tor->info.totalSize ) return TR_SEED;
264    if( cp->sizeNow == tr_cpSizeWhenDone( cp ) ) return TR_PARTIAL_SEED;
265    return TR_LEECH;
266}
267
268static uint64_t
269calculateHaveValid( const tr_completion * ccp )
270{
271    uint64_t                  b = 0;
272    tr_piece_index_t          i;
273    const tr_torrent        * tor            = ccp->tor;
274    const uint64_t            pieceSize      = tor->info.pieceSize;
275    const uint64_t            lastPieceSize  = tor->lastPieceSize;
276    const tr_piece_index_t    lastPiece      = tor->info.pieceCount - 1;
277
278    if( !tr_torrentHasMetadata( tor ) )
279        return 0;
280
281    for( i=0; i!=lastPiece; ++i )
282        if( tr_cpPieceIsComplete( ccp, i ) )
283            b += pieceSize;
284
285    if( tr_cpPieceIsComplete( ccp, lastPiece ) )
286        b += lastPieceSize;
287
288    return b;
289}
290
291uint64_t
292tr_cpHaveValid( const tr_completion * ccp )
293{
294    if( ccp->haveValidIsDirty )
295    {
296        tr_completion * cp = (tr_completion *) ccp; /* mutable */
297        cp->haveValidLazy = calculateHaveValid( ccp );
298        cp->haveValidIsDirty = 0;
299    }
300
301    return ccp->haveValidLazy;
302}
303
304void
305tr_cpGetAmountDone( const tr_completion * cp,
306                    float *               tab,
307                    int                   tabCount )
308{
309    int                i;
310    const tr_torrent * tor = cp->tor;
311    const float        interval = tor->info.pieceCount / (float)tabCount;
312    const int          isSeed = tr_cpGetStatus( cp ) == TR_SEED;
313
314    for( i = 0; i < tabCount; ++i )
315    {
316        const tr_piece_index_t piece = i * interval;
317
318        if( tor == NULL )
319            tab[i] = 0.0f;
320        else if( isSeed || tr_cpPieceIsComplete( cp, piece ) )
321            tab[i] = 1.0f;
322        else
323            tab[i] = (float)cp->completeBlocks[piece] /
324                     tr_torPieceCountBlocks( tor, piece );
325    }
326}
327
328int
329tr_cpMissingBlocksInPiece( const tr_completion * cp, tr_piece_index_t piece )
330{
331    return tr_torPieceCountBlocks( cp->tor, piece ) - cp->completeBlocks[piece];
332}
333
334
335tr_bool
336tr_cpPieceIsComplete( const tr_completion * cp, tr_piece_index_t piece )
337{
338    return cp->completeBlocks[piece] == tr_torPieceCountBlocks( cp->tor, piece );
339}
340
341tr_bool
342tr_cpFileIsComplete( const tr_completion * cp, tr_file_index_t fileIndex )
343{
344    tr_block_index_t block;
345
346    const tr_torrent * tor = cp->tor;
347    const tr_file * file = &tor->info.files[fileIndex];
348    const tr_block_index_t firstBlock = file->offset / tor->blockSize;
349    const tr_block_index_t lastBlock = file->length ? ( ( file->offset + file->length - 1 ) / tor->blockSize ) : firstBlock;
350
351    assert( tr_torBlockPiece( tor, firstBlock ) == file->firstPiece );
352    assert( tr_torBlockPiece( tor, lastBlock ) == file->lastPiece );
353
354    for( block=firstBlock; block<=lastBlock; ++block )
355        if( !tr_cpBlockIsCompleteFast( cp, block ) )
356            return FALSE;
357
358    return TRUE;
359}
Note: See TracBrowser for help on using the repository browser.