318 lines
8.3 KiB
C
318 lines
8.3 KiB
C
/*
|
|
* This file Copyright (C) Mnemosyne LLC
|
|
*
|
|
* This file is licensed by the GPL version 2. Works owned by the
|
|
* Transmission project are granted a special exemption to clause 2(b)
|
|
* so that the bulk of its code can remain under the MIT license.
|
|
* This exemption does not extend to derived works not owned by
|
|
* the Transmission project.
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
#include <assert.h>
|
|
|
|
#include "transmission.h"
|
|
#include "completion.h"
|
|
#include "torrent.h"
|
|
#include "utils.h"
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
static void
|
|
tr_cpReset( tr_completion * cp )
|
|
{
|
|
cp->sizeNow = 0;
|
|
cp->sizeWhenDoneIsDirty = true;
|
|
cp->haveValidIsDirty = true;
|
|
tr_bitfieldSetHasNone( &cp->blockBitfield );
|
|
}
|
|
|
|
void
|
|
tr_cpConstruct( tr_completion * cp, tr_torrent * tor )
|
|
{
|
|
cp->tor = tor;
|
|
tr_bitfieldConstruct( &cp->blockBitfield, tor->blockCount );
|
|
tr_cpReset( cp );
|
|
}
|
|
|
|
void
|
|
tr_cpBlockInit( tr_completion * cp, const tr_bitfield * b )
|
|
{
|
|
tr_cpReset( cp );
|
|
|
|
/* set blockBitfield */
|
|
tr_bitfieldSetFromBitfield( &cp->blockBitfield, b );
|
|
|
|
/* set sizeNow */
|
|
cp->sizeNow = tr_bitfieldCountTrueBits( &cp->blockBitfield );
|
|
assert( cp->sizeNow <= cp->tor->blockCount );
|
|
cp->sizeNow *= cp->tor->blockSize;
|
|
if( tr_bitfieldHas( b, cp->tor->blockCount-1 ) )
|
|
cp->sizeNow -= ( cp->tor->blockSize - cp->tor->lastBlockSize );
|
|
assert( cp->sizeNow <= cp->tor->info.totalSize );
|
|
}
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
tr_completeness
|
|
tr_cpGetStatus( const tr_completion * cp )
|
|
{
|
|
if( tr_cpHasAll( cp ) ) return TR_SEED;
|
|
if( !tr_torrentHasMetadata( cp->tor ) ) return TR_LEECH;
|
|
if( cp->sizeNow == tr_cpSizeWhenDone( cp ) ) return TR_PARTIAL_SEED;
|
|
return TR_LEECH;
|
|
}
|
|
|
|
void
|
|
tr_cpPieceRem( tr_completion * cp, tr_piece_index_t piece )
|
|
{
|
|
tr_block_index_t i, f, l;
|
|
const tr_torrent * tor = cp->tor;
|
|
|
|
tr_torGetPieceBlockRange( cp->tor, piece, &f, &l );
|
|
|
|
for( i=f; i<=l; ++i )
|
|
if( tr_cpBlockIsComplete( cp, i ) )
|
|
cp->sizeNow -= tr_torBlockCountBytes( tor, i );
|
|
|
|
cp->haveValidIsDirty = true;
|
|
cp->sizeWhenDoneIsDirty = true;
|
|
tr_bitfieldRemRange( &cp->blockBitfield, f, l+1 );
|
|
}
|
|
|
|
void
|
|
tr_cpPieceAdd( tr_completion * cp, tr_piece_index_t piece )
|
|
{
|
|
tr_block_index_t i, f, l;
|
|
tr_torGetPieceBlockRange( cp->tor, piece, &f, &l );
|
|
|
|
for( i=f; i<=l; ++i )
|
|
tr_cpBlockAdd( cp, i );
|
|
}
|
|
|
|
void
|
|
tr_cpBlockAdd( tr_completion * cp, tr_block_index_t block )
|
|
{
|
|
const tr_torrent * tor = cp->tor;
|
|
|
|
if( !tr_cpBlockIsComplete( cp, block ) )
|
|
{
|
|
tr_bitfieldAdd( &cp->blockBitfield, block );
|
|
cp->sizeNow += tr_torBlockCountBytes( tor, block );
|
|
|
|
cp->haveValidIsDirty = true;
|
|
cp->sizeWhenDoneIsDirty = true;
|
|
}
|
|
}
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
uint64_t
|
|
tr_cpHaveValid( const tr_completion * ccp )
|
|
{
|
|
if( ccp->haveValidIsDirty )
|
|
{
|
|
tr_piece_index_t i;
|
|
uint64_t size = 0;
|
|
tr_completion * cp = (tr_completion *) ccp; /* mutable */
|
|
const tr_torrent * tor = ccp->tor;
|
|
const tr_info * info = &tor->info;
|
|
|
|
for( i=0; i<info->pieceCount; ++i )
|
|
if( tr_cpPieceIsComplete( ccp, i ) )
|
|
size += tr_torPieceCountBytes( tor, i );
|
|
|
|
cp->haveValidLazy = size;
|
|
cp->haveValidIsDirty = false;
|
|
}
|
|
|
|
return ccp->haveValidLazy;
|
|
}
|
|
|
|
uint64_t
|
|
tr_cpSizeWhenDone( const tr_completion * ccp )
|
|
{
|
|
if( ccp->sizeWhenDoneIsDirty )
|
|
{
|
|
uint64_t size = 0;
|
|
const tr_torrent * tor = ccp->tor;
|
|
const tr_info * inf = tr_torrentInfo( tor );
|
|
tr_completion * cp = (tr_completion *) ccp; /* mutable */
|
|
|
|
if( tr_cpHasAll( ccp ) )
|
|
{
|
|
size = inf->totalSize;
|
|
}
|
|
else
|
|
{
|
|
tr_piece_index_t p;
|
|
|
|
for( p=0; p<inf->pieceCount; ++p )
|
|
{
|
|
uint64_t n = 0;
|
|
const uint64_t pieceSize = tr_torPieceCountBytes( tor, p );
|
|
|
|
if( !inf->pieces[p].dnd )
|
|
{
|
|
n = pieceSize;
|
|
}
|
|
else
|
|
{
|
|
tr_block_index_t f, l;
|
|
tr_torGetPieceBlockRange( cp->tor, p, &f, &l );
|
|
|
|
n = tr_bitfieldCountRange( &cp->blockBitfield, f, l+1 );
|
|
n *= cp->tor->blockSize;
|
|
if( l == ( cp->tor->blockCount - 1 ) && tr_bitfieldHas( &cp->blockBitfield, l ) )
|
|
n -= ( cp->tor->blockSize - cp->tor->lastBlockSize );
|
|
}
|
|
|
|
assert( n <= tr_torPieceCountBytes( tor, p ) );
|
|
size += n;
|
|
}
|
|
}
|
|
|
|
assert( size <= inf->totalSize );
|
|
assert( size >= cp->sizeNow );
|
|
|
|
cp->sizeWhenDoneLazy = size;
|
|
cp->sizeWhenDoneIsDirty = false;
|
|
}
|
|
|
|
return ccp->sizeWhenDoneLazy;
|
|
}
|
|
|
|
uint64_t
|
|
tr_cpLeftUntilDone( const tr_completion * cp )
|
|
{
|
|
const uint64_t sizeWhenDone = tr_cpSizeWhenDone( cp );
|
|
|
|
assert( sizeWhenDone >= cp->sizeNow );
|
|
|
|
return sizeWhenDone - cp->sizeNow;
|
|
}
|
|
|
|
void
|
|
tr_cpGetAmountDone( const tr_completion * cp, float * tab, int tabCount )
|
|
{
|
|
int i;
|
|
const bool seed = tr_cpHasAll( cp );
|
|
const float interval = cp->tor->info.pieceCount / (float)tabCount;
|
|
|
|
for( i=0; i<tabCount; ++i ) {
|
|
if( seed )
|
|
tab[i] = 1.0f;
|
|
else {
|
|
tr_block_index_t f, l;
|
|
const tr_piece_index_t piece = (tr_piece_index_t)i * interval;
|
|
tr_torGetPieceBlockRange( cp->tor, piece, &f, &l );
|
|
tab[i] = tr_bitfieldCountRange( &cp->blockBitfield, f, l+1 )
|
|
/ (float)(l+1-f);
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t
|
|
tr_cpMissingBlocksInPiece( const tr_completion * cp, tr_piece_index_t piece )
|
|
{
|
|
if( tr_cpHasAll( cp ) )
|
|
return 0;
|
|
else {
|
|
tr_block_index_t f, l;
|
|
tr_torGetPieceBlockRange( cp->tor, piece, &f, &l );
|
|
return (l+1-f) - tr_bitfieldCountRange( &cp->blockBitfield, f, l+1 );
|
|
}
|
|
}
|
|
|
|
size_t
|
|
tr_cpMissingBytesInPiece( const tr_completion * cp, tr_piece_index_t piece )
|
|
{
|
|
if( tr_cpHasAll( cp ) )
|
|
return 0;
|
|
else {
|
|
size_t haveBytes = 0;
|
|
tr_block_index_t f, l;
|
|
const size_t pieceByteSize = tr_torPieceCountBytes( cp->tor, piece );
|
|
tr_torGetPieceBlockRange( cp->tor, piece, &f, &l );
|
|
if( f != l ) {
|
|
/* nb: we don't pass the usual l+1 here to tr_bitfieldCountRange().
|
|
It's faster to handle the last block separately because its size
|
|
needs to be checked separately. */
|
|
haveBytes = tr_bitfieldCountRange( &cp->blockBitfield, f, l );
|
|
haveBytes *= cp->tor->blockSize;
|
|
}
|
|
if( tr_bitfieldHas( &cp->blockBitfield, l ) ) /* handle the last block */
|
|
haveBytes += tr_torBlockCountBytes( cp->tor, l );
|
|
assert( haveBytes <= pieceByteSize );
|
|
return pieceByteSize - haveBytes;
|
|
}
|
|
}
|
|
|
|
bool
|
|
tr_cpFileIsComplete( const tr_completion * cp, tr_file_index_t i )
|
|
{
|
|
if( cp->tor->info.files[i].length == 0 )
|
|
return true;
|
|
else {
|
|
tr_block_index_t f, l;
|
|
tr_torGetFileBlockRange( cp->tor, i, &f, &l );
|
|
return tr_bitfieldCountRange( &cp->blockBitfield, f, l+1 ) == (l+1-f);
|
|
}
|
|
}
|
|
|
|
void *
|
|
tr_cpCreatePieceBitfield( const tr_completion * cp, size_t * byte_count )
|
|
{
|
|
void * ret;
|
|
tr_piece_index_t n;
|
|
tr_bitfield pieces;
|
|
|
|
assert( tr_torrentHasMetadata( cp->tor ) );
|
|
|
|
n = cp->tor->info.pieceCount;
|
|
tr_bitfieldConstruct( &pieces, n );
|
|
|
|
if( tr_cpHasAll( cp ) )
|
|
tr_bitfieldSetHasAll( &pieces );
|
|
else if( !tr_cpHasNone( cp ) ) {
|
|
tr_piece_index_t i;
|
|
bool * flags = tr_new( bool, n );
|
|
for( i=0; i<n; ++i )
|
|
flags[i] = tr_cpPieceIsComplete( cp, i );
|
|
tr_bitfieldSetFromFlags( &pieces, flags, n );
|
|
tr_free( flags );
|
|
}
|
|
|
|
ret = tr_bitfieldGetRaw( &pieces, byte_count );
|
|
tr_bitfieldDestruct( &pieces );
|
|
return ret;
|
|
}
|
|
|
|
double
|
|
tr_cpPercentComplete( const tr_completion * cp )
|
|
{
|
|
const double ratio = tr_getRatio( cp->sizeNow, cp->tor->info.totalSize );
|
|
|
|
if( (int)ratio == TR_RATIO_NA )
|
|
return 0.0;
|
|
else if( (int)ratio == TR_RATIO_INF )
|
|
return 1.0;
|
|
else
|
|
return ratio;
|
|
}
|
|
|
|
double
|
|
tr_cpPercentDone( const tr_completion * cp )
|
|
{
|
|
const double ratio = tr_getRatio( cp->sizeNow, tr_cpSizeWhenDone( cp ) );
|
|
const int iratio = (int)ratio;
|
|
return ((iratio == TR_RATIO_NA) || (iratio == TR_RATIO_INF)) ? 0.0 : ratio;
|
|
}
|