transmission/libtransmission/completion.c

357 lines
9.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 <string.h> /* memcpy() */
#include "transmission.h"
#include "completion.h"
#include "torrent.h"
#include "utils.h"
/***
****
***/
static void
tr_cpReset( tr_completion * cp )
{
tr_bitsetSetHaveNone( &cp->blockBitset );
tr_free( cp->completeBlocks );
cp->completeBlocks = NULL;
cp->sizeNow = 0;
cp->sizeWhenDoneIsDirty = true;
cp->blocksWantedIsDirty = true;
cp->haveValidIsDirty = true;
}
tr_completion *
tr_cpConstruct( tr_completion * cp, tr_torrent * tor )
{
cp->tor = tor;
cp->completeBlocks = NULL;
tr_bitsetConstruct( &cp->blockBitset, tor->blockCount );
tr_cpReset( cp );
return cp;
}
tr_completion*
tr_cpDestruct( tr_completion * cp )
{
tr_free( cp->completeBlocks );
tr_bitsetDestruct( &cp->blockBitset );
return cp;
}
/***
****
***/
static inline bool
isSeed( const tr_completion * cp )
{
return cp->blockBitset.haveAll;
}
tr_completeness
tr_cpGetStatus( const tr_completion * cp )
{
if( !tr_torrentHasMetadata( cp->tor ) ) return TR_LEECH;
if( cp->sizeNow == cp->tor->info.totalSize ) return TR_SEED;
if( cp->sizeNow == tr_cpSizeWhenDone( cp ) ) return TR_PARTIAL_SEED;
return TR_LEECH;
}
/* how many blocks are in this piece? */
static inline uint16_t
countBlocksInPiece( const tr_torrent * tor, const tr_piece_index_t piece )
{
return piece + 1 == tor->info.pieceCount ? tor->blockCountInLastPiece
: tor->blockCountInPiece;
}
static uint16_t *
getCompleteBlocks( const tr_completion * ccp )
{
if( ccp->completeBlocks == NULL )
{
tr_completion * cp = (tr_completion*) ccp;
cp->completeBlocks = tr_new0( uint16_t, ccp->tor->info.pieceCount );
}
return ccp->completeBlocks;
}
void
tr_cpInvalidateDND( tr_completion * cp )
{
cp->sizeWhenDoneIsDirty = true;
cp->blocksWantedIsDirty = true;
}
tr_block_index_t
tr_cpBlocksMissing( const tr_completion * ccp )
{
if( isSeed( ccp ) )
return 0;
if( ccp->blocksWantedIsDirty )
{
tr_piece_index_t i;
tr_block_index_t wanted = 0;
tr_block_index_t complete = 0;
tr_completion * cp = (tr_completion *) ccp; /* mutable */
const uint16_t * complete_blocks = getCompleteBlocks( cp );
const tr_torrent * tor = ccp->tor;
const tr_info * info = &tor->info;
for( i = 0; i < info->pieceCount; ++i )
{
if( !info->pieces[i].dnd )
{
wanted += countBlocksInPiece( tor, i );
complete += complete_blocks[i];
}
}
cp->blocksWantedLazy = wanted;
cp->blocksWantedCompleteLazy = complete;
cp->blocksWantedIsDirty = false;
}
return ccp->blocksWantedLazy - ccp->blocksWantedCompleteLazy;
}
void
tr_cpPieceRem( tr_completion * cp, tr_piece_index_t piece )
{
tr_block_index_t i;
tr_block_index_t first;
tr_block_index_t last;
const tr_torrent * tor = cp->tor;
uint16_t * complete_blocks = getCompleteBlocks( cp );
tr_torGetPieceBlockRange( cp->tor, piece, &first, &last );
for( i=first; i<=last; ++i )
if( tr_cpBlockIsComplete( cp, i ) )
cp->sizeNow -= tr_torBlockCountBytes( tor, i );
if( !tor->info.pieces[piece].dnd )
cp->blocksWantedCompleteLazy -= complete_blocks[piece];
cp->sizeWhenDoneIsDirty = true;
cp->haveValidIsDirty = true;
complete_blocks[piece] = 0;
tr_bitsetRemRange( &cp->blockBitset, first, last+1 );
}
void
tr_cpPieceAdd( tr_completion * cp, tr_piece_index_t piece )
{
tr_block_index_t i;
tr_block_index_t first;
tr_block_index_t last;
tr_torGetPieceBlockRange( cp->tor, piece, &first, &last );
for( i=first; i<=last; ++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 ) )
{
const tr_piece_index_t piece = tr_torBlockPiece( tor, block );
const int blockSize = tr_torBlockCountBytes( tor, block );
getCompleteBlocks(cp)[piece]++;
tr_bitsetAdd( &cp->blockBitset, block );
cp->sizeNow += blockSize;
if( !tor->info.pieces[piece].dnd )
cp->blocksWantedCompleteLazy++;
cp->sizeWhenDoneIsDirty = true;
cp->haveValidIsDirty = true;
}
}
bool
tr_cpBlockBitsetInit( tr_completion * cp, const tr_bitset * blocks )
{
bool success = false;
tr_torrent * tor = cp->tor;
/* start cp with a state where it thinks we have nothing */
tr_cpReset( cp );
if( blocks->haveAll )
{
tr_bitsetSetHaveAll( &cp->blockBitset );
cp->sizeNow = tor->info.totalSize;
success = true;
}
else if( blocks->haveNone )
{
/* already reset... */
success = true;
}
else
{
const tr_bitfield * src = &blocks->bitfield;
tr_bitfield * tgt = &cp->blockBitset.bitfield;
tr_bitfieldConstruct( tgt, tor->blockCount );
/* The bitfield of block flags is typically loaded from a resume file.
Test the bitfield's length in case the resume file is corrupt */
if(( success = src->byteCount == tgt->byteCount ))
{
size_t i = 0;
uint16_t * complete_blocks_in_piece = getCompleteBlocks( cp );
/* init our block bitfield from the one passed in */
memcpy( tgt->bits, src->bits, src->byteCount );
/* update cp.sizeNow and the cp.blockBitset flags */
i = tr_bitfieldCountTrueBits( tgt );
if( i == tor->blockCount ) {
tr_bitsetSetHaveAll( &cp->blockBitset );
cp->sizeNow = cp->tor->info.totalSize;
} else if( !i ) {
tr_bitsetSetHaveNone( &cp->blockBitset );
cp->sizeNow = 0;
} else {
cp->blockBitset.haveAll = cp->blockBitset.haveNone = false;
cp->sizeNow = tr_bitfieldCountRange( tgt, 0, tor->blockCount-1 );
cp->sizeNow *= tor->blockSize;
if( tr_bitfieldHas( tgt, tor->blockCount-1 ) )
cp->sizeNow += tr_torBlockCountBytes( tor, tor->blockCount-1 );
}
/* update complete_blocks_in_piece */
for( i=0; i<tor->info.pieceCount; ++i ) {
tr_block_index_t first, last;
tr_torGetPieceBlockRange( tor, i, &first, &last );
complete_blocks_in_piece[i] = tr_bitfieldCountRange( src, first, last+1 );
}
}
}
return success;
}
/***
****
***/
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->haveValidIsDirty = false;
cp->haveValidLazy = size;
}
return ccp->haveValidLazy;
}
uint64_t
tr_cpSizeWhenDone( const tr_completion * ccp )
{
if( ccp->sizeWhenDoneIsDirty )
{
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( !info->pieces[i].dnd || tr_cpPieceIsComplete( cp, i ) )
size += tr_torPieceCountBytes( tor, i );
cp->sizeWhenDoneIsDirty = false;
cp->sizeWhenDoneLazy = size;
}
return ccp->sizeWhenDoneLazy;
}
void
tr_cpGetAmountDone( const tr_completion * cp, float * tab, int tabCount )
{
int i;
const float interval = cp->tor->info.pieceCount / (float)tabCount;
const bool seed = isSeed( cp );
for( i=0; i<tabCount; ++i ) {
if( seed )
tab[i] = 1.0f;
else {
const tr_piece_index_t piece = (tr_piece_index_t)i * interval;
tab[i] = getCompleteBlocks(cp)[piece] / (float)countBlocksInPiece( cp->tor, piece );
}
}
}
int
tr_cpMissingBlocksInPiece( const tr_completion * cp, tr_piece_index_t i )
{
if( isSeed( cp ) )
return 0;
return countBlocksInPiece( cp->tor, i ) - getCompleteBlocks(cp)[i];
}
bool
tr_cpFileIsComplete( const tr_completion * cp, tr_file_index_t i )
{
tr_block_index_t f, l;
if( cp->tor->info.files[i].length == 0 )
return true;
tr_torGetFileBlockRange( cp->tor, i, &f, &l );
return tr_bitsetCountRange( &cp->blockBitset, f, l+1 ) == (l+1-f);
}
tr_bitfield *
tr_cpCreatePieceBitfield( const tr_completion * cp )
{
tr_piece_index_t i;
const tr_piece_index_t n = cp->tor->info.pieceCount;
tr_bitfield * bf = tr_bitfieldNew( n );
for( i=0; i<n; ++i )
if( tr_cpPieceIsComplete( cp, i ) )
tr_bitfieldAdd( bf, i );
return bf;
}