source: trunk/libtransmission/completion.c @ 8050

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

(trunk libT) new macro, tr_assert(). use it to help smoke out #1749

  • 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 7797 2009-01-25 16:14:11Z 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,
86                                                                       i );
87                tr_block_index_t       j;
88                for( j = b; j < e; ++j )
89                    if( tr_cpBlockIsComplete( 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_cpBlockIsComplete( 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( cp->sizeNow == cp->tor->info.totalSize ) return TR_SEED;
245    if( cp->sizeNow == tr_cpSizeWhenDone( cp ) ) return TR_PARTIAL_SEED;
246    return TR_LEECH;
247}
248
249static uint64_t
250calculateHaveValid( const tr_completion * ccp )
251{
252    uint64_t                  b = 0;
253    tr_piece_index_t          i;
254    const tr_torrent        * tor            = ccp->tor;
255    const uint64_t            pieceSize      = tor->info.pieceSize;
256    const uint64_t            lastPieceSize  = tor->lastPieceSize;
257    const tr_piece_index_t    lastPiece      = tor->info.pieceCount - 1;
258
259    for( i=0; i!=lastPiece; ++i )
260        if( tr_cpPieceIsComplete( ccp, i ) )
261            b += pieceSize;
262
263    if( tr_cpPieceIsComplete( ccp, lastPiece ) )
264        b += lastPieceSize;
265
266    return b;
267}
268
269uint64_t
270tr_cpHaveValid( const tr_completion * ccp )
271{
272    if( ccp->haveValidIsDirty )
273    {
274        tr_completion * cp = (tr_completion *) ccp; /* mutable */
275        cp->haveValidLazy = calculateHaveValid( ccp );
276        cp->haveValidIsDirty = 0;
277    }
278
279    return ccp->haveValidLazy;
280}
281
282void
283tr_cpGetAmountDone( const tr_completion * cp,
284                    float *               tab,
285                    int                   tabCount )
286{
287    int                i;
288    const tr_torrent * tor = cp->tor;
289    const float        interval = tor->info.pieceCount / (float)tabCount;
290    const int          isSeed = tr_cpGetStatus( cp ) == TR_SEED;
291
292    for( i = 0; i < tabCount; ++i )
293    {
294        const tr_piece_index_t piece = i * interval;
295
296        if( tor == NULL )
297            tab[i] = 0.0f;
298        else if( isSeed || tr_cpPieceIsComplete( cp, piece ) )
299            tab[i] = 1.0f;
300        else
301            tab[i] = (float)cp->completeBlocks[piece] /
302                     tr_torPieceCountBlocks( tor, piece );
303    }
304}
305
306int
307tr_cpMissingBlocksInPiece( const tr_completion * cp, tr_piece_index_t piece )
308{
309    return tr_torPieceCountBlocks( cp->tor, piece ) - cp->completeBlocks[piece];
310}
311
312
313tr_bool
314tr_cpPieceIsComplete( const tr_completion * cp, tr_piece_index_t piece )
315{
316    return cp->completeBlocks[piece] == tr_torPieceCountBlocks( cp->tor, piece );
317}
318
319tr_bool
320tr_cpFileIsComplete( const tr_completion * cp, tr_file_index_t fileIndex )
321{
322    tr_block_index_t block;
323
324    const tr_torrent * tor = cp->tor;
325    const tr_file * file = &tor->info.files[fileIndex];
326    const tr_block_index_t firstBlock = file->offset / tor->blockSize;
327    const tr_block_index_t lastBlock = file->length ? ( ( file->offset + file->length - 1 ) / tor->blockSize ) : firstBlock;
328
329    tr_assert( tr_torBlockPiece( tor, firstBlock ) == file->firstPiece,
330               "file->offset %"PRIu64"; file->length %"PRIu64"; "
331               "pieceSize %"PRIu32"; blockSize %"PRIu32"; "
332               "firstBlock %"PRIu64"; lastBlock %"PRIu64,
333               file->offset, file->length,
334               tor->info.pieceSize, tor->blockSize,
335               firstBlock, lastBlock );
336
337    tr_assert( tr_torBlockPiece( tor, lastBlock ) == file->lastPiece,
338               "file->offset %"PRIu64"; file->length %"PRIu64"; "
339               "pieceSize %"PRIu32"; blockSize %"PRIu32"; "
340               "firstBlock %"PRIu64"; lastBlock %"PRIu64,
341               file->offset, file->length,
342               tor->info.pieceSize, tor->blockSize,
343               firstBlock, lastBlock );
344
345    for( block=firstBlock; block<=lastBlock; ++block )
346        if( !tr_cpBlockIsComplete( cp, block ) )
347            return FALSE;
348
349    return TRUE;
350}
Note: See TracBrowser for help on using the repository browser.