source: trunk/libtransmission/completion.c @ 8212

Last change on this file since 8212 was 8212, checked in by charles, 13 years ago

(trunk libT) omit some unnecessary tests on the bitfield checks. these seem small, but bitfields are always the top CPU abuser when I profile...

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