source: trunk/libtransmission/completion.c @ 7633

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

(trunk libT) re-enable the completion sanity tests for the benefit of the nightly builds

  • Property svn:keywords set to Date Rev Author Id
File size: 10.1 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 7633 2009-01-07 17:22:17Z 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
167tr_bool
168tr_cpBlockBitfieldSet( tr_completion * cp, tr_bitfield * blockBitfield )
169{
170    int success = FALSE;
171
172    assert( cp );
173    assert( blockBitfield );
174
175    if(( success = blockBitfield->byteCount == cp->blockBitfield.byteCount ))
176    {
177        tr_block_index_t b = 0;
178        tr_piece_index_t p = 0;
179        uint32_t pieceBlock = 0;
180        uint32_t completeBlocksInPiece = 0;
181        tr_block_index_t completeBlocksInTorrent = 0;
182        uint32_t blocksInCurrentPiece = tr_torPieceCountBlocks( cp->tor, p );
183
184        assert( blockBitfield->byteCount == cp->blockBitfield.byteCount );
185
186        tr_cpReset( cp );
187
188        cp->sizeWhenDoneIsDirty = TRUE;
189        cp->haveValidIsDirty = TRUE;
190        memcpy( cp->blockBitfield.bits, blockBitfield->bits, blockBitfield->byteCount );
191
192        while( b < cp->tor->blockCount )
193        {
194            if( tr_bitfieldHasFast( blockBitfield, b ) )
195                ++completeBlocksInPiece;
196
197            ++b;
198            ++pieceBlock;
199
200            if( pieceBlock == blocksInCurrentPiece )
201            {
202                cp->completeBlocks[p] = completeBlocksInPiece;
203
204                completeBlocksInTorrent += completeBlocksInPiece;
205
206                if( completeBlocksInPiece == blocksInCurrentPiece )
207                    tr_bitfieldAdd( &cp->pieceBitfield, p );
208
209                ++p;
210                completeBlocksInPiece = 0;
211                pieceBlock = 0;
212                blocksInCurrentPiece = tr_torPieceCountBlocks( cp->tor, p );
213            }
214        }
215
216        /* update sizeNow */
217        cp->sizeNow = completeBlocksInTorrent;
218        cp->sizeNow *= tr_torBlockCountBytes( cp->tor, 0 );
219        if( tr_bitfieldHasFast( &cp->blockBitfield, cp->tor->blockCount-1 ) ) {
220            cp->sizeNow -= tr_torBlockCountBytes( cp->tor, 0 );
221            cp->sizeNow += tr_torBlockCountBytes( cp->tor, cp->tor->blockCount-1 );
222        }
223
224#if 1
225#warning these checks are to see if the implementation is good, since getting this function wrong could make Transmission think their downloaded data has disappeared.  But they are also expensive, so this block should be turned off after the nightly build users had a chance to smoke out any errors.
226        /**
227        ***  correctness checks
228        **/
229        for( b=0; b<cp->tor->blockCount; ++b )
230            assert( tr_bitfieldHasFast( blockBitfield, b ) == tr_bitfieldHasFast( &cp->blockBitfield, b ) );
231
232        assert( cp->sizeNow <= cp->tor->info.totalSize );
233        for( p=0; p<cp->tor->info.pieceCount; ++p ) {
234            const uint32_t blocksInCurrentPiece = tr_torPieceCountBlocks( cp->tor, p );
235            const tr_block_index_t start = tr_torPieceFirstBlock( cp->tor, p );
236            const tr_block_index_t end = start + tr_torPieceCountBlocks( cp->tor, p );
237            uint32_t completeBlocksInPiece = 0;
238
239            assert( tr_bitfieldHasFast( &cp->pieceBitfield, p ) == ( blocksInCurrentPiece == cp->completeBlocks[p] ) );
240
241            for( b=start; b<end; ++b )
242                if( tr_bitfieldHasFast( &cp->blockBitfield, b ) )
243                    ++completeBlocksInPiece;
244            assert( completeBlocksInPiece == cp->completeBlocks[p] );
245        }
246#endif
247    }
248
249    return success;
250}
251
252/***
253****
254***/
255
256tr_completeness
257tr_cpGetStatus( const tr_completion * cp )
258{
259    if( cp->sizeNow == cp->tor->info.totalSize ) return TR_SEED;
260    if( cp->sizeNow == tr_cpSizeWhenDone( cp ) ) return TR_PARTIAL_SEED;
261    return TR_LEECH;
262}
263
264static uint64_t
265calculateHaveValid( const tr_completion * ccp )
266{
267    uint64_t                  b = 0;
268    tr_piece_index_t          i;
269    const tr_torrent        * tor            = ccp->tor;
270    const uint64_t            pieceSize      = tor->info.pieceSize;
271    const uint64_t            lastPieceSize  = tor->lastPieceSize;
272    const tr_piece_index_t    lastPiece      = tor->info.pieceCount - 1;
273
274    for( i=0; i!=lastPiece; ++i )
275        if( tr_cpPieceIsComplete( ccp, i ) )
276            b += pieceSize;
277
278    if( tr_cpPieceIsComplete( ccp, lastPiece ) )
279        b += lastPieceSize;
280
281    return b;
282}
283
284uint64_t
285tr_cpHaveValid( const tr_completion * ccp )
286{
287    if( ccp->haveValidIsDirty )
288    {
289        tr_completion * cp = (tr_completion *) ccp; /* mutable */
290        cp->haveValidLazy = calculateHaveValid( ccp );
291        cp->haveValidIsDirty = 0;
292    }
293
294    return ccp->haveValidLazy;
295}
296
297void
298tr_cpGetAmountDone( const tr_completion * cp,
299                    float *               tab,
300                    int                   tabCount )
301{
302    int                i;
303    const tr_torrent * tor = cp->tor;
304    const float        interval = tor->info.pieceCount / (float)tabCount;
305    const int          isSeed = tr_cpGetStatus( cp ) == TR_SEED;
306
307    for( i = 0; i < tabCount; ++i )
308    {
309        const tr_piece_index_t piece = i * interval;
310
311        if( tor == NULL )
312            tab[i] = 0.0f;
313        else if( isSeed || tr_cpPieceIsComplete( cp, piece ) )
314            tab[i] = 1.0f;
315        else
316            tab[i] = (float)cp->completeBlocks[piece] /
317                     tr_torPieceCountBlocks( tor, piece );
318    }
319}
320
321int
322tr_cpMissingBlocksInPiece( const tr_completion * cp, tr_piece_index_t piece )
323{
324    return tr_torPieceCountBlocks( cp->tor, piece ) - cp->completeBlocks[piece];
325}
326
327
328tr_bool
329tr_cpPieceIsComplete( const tr_completion * cp, tr_piece_index_t piece )
330{
331    return cp->completeBlocks[piece] == tr_torPieceCountBlocks( cp->tor, piece );
332}
Note: See TracBrowser for help on using the repository browser.