1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2024-12-27 18:18:10 +00:00
transmission/libtransmission/completion.c
Jordan Lee 879a2afcbd Update the copyright year in the source code comments.
The Berne Convention says that the copyright year is moot, so instead of adding another year to each file as in previous years, I've removed the year altogether from the source code comments in libtransmission, gtk, qt, utils, daemon, and cli.

Juliusz's copyright notice in tr-dht and Johannes' copyright notice in tr-lpd have been left alone; it didn't seem appropriate to modify them.
2011-01-19 13:48:47 +00:00

359 lines
11 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 <string.h>
#include "transmission.h"
#include "completion.h"
#include "torrent.h"
#include "torrent-magnet.h"
#include "utils.h"
static void
tr_cpReset( tr_completion * cp )
{
tr_bitfieldClear( &cp->pieceBitfield );
tr_bitfieldClear( &cp->blockBitfield );
memset( cp->completeBlocks, 0, sizeof( uint16_t ) * cp->tor->info.pieceCount );
cp->sizeNow = 0;
cp->sizeWhenDoneIsDirty = 1;
cp->haveValidIsDirty = 1;
}
tr_completion *
tr_cpConstruct( tr_completion * cp, tr_torrent * tor )
{
cp->tor = tor;
cp->completeBlocks = tr_new( uint16_t, tor->info.pieceCount );
tr_bitfieldConstruct( &cp->blockBitfield, tor->blockCount );
tr_bitfieldConstruct( &cp->pieceBitfield, tor->info.pieceCount );
tr_cpReset( cp );
return cp;
}
tr_completion*
tr_cpDestruct( tr_completion * cp )
{
tr_free( cp->completeBlocks );
tr_bitfieldDestruct( &cp->pieceBitfield );
tr_bitfieldDestruct( &cp->blockBitfield );
return cp;
}
void
tr_cpInvalidateDND( tr_completion * cp )
{
cp->sizeWhenDoneIsDirty = 1;
}
uint64_t
tr_cpSizeWhenDone( const tr_completion * ccp )
{
if( ccp->sizeWhenDoneIsDirty )
{
tr_completion * cp = (tr_completion *) ccp; /* mutable */
const tr_torrent * tor = cp->tor;
const tr_info * info = &tor->info;
tr_piece_index_t i;
uint64_t size = 0;
for( i = 0; i < info->pieceCount; ++i )
{
if( !info->pieces[i].dnd )
{
/* we want the piece... */
size += tr_torPieceCountBytes( tor, i );
}
else if( tr_cpPieceIsComplete( cp, i ) )
{
/* we have the piece... */
size += tr_torPieceCountBytes( tor, i );
}
else if( cp->completeBlocks[i] )
{
/* we have part of the piece... */
const tr_block_index_t b = tr_torPieceFirstBlock( tor, i );
const tr_block_index_t e = b + tr_torPieceCountBlocks( tor, i );
tr_block_index_t j;
for( j = b; j < e; ++j )
if( tr_cpBlockIsCompleteFast( cp, j ) )
size += tr_torBlockCountBytes( tor, j );
}
}
cp->sizeWhenDoneLazy = size;
cp->sizeWhenDoneIsDirty = 0;
}
assert( ccp->sizeWhenDoneLazy <= ccp->tor->info.totalSize );
assert( ccp->sizeWhenDoneLazy >= ccp->sizeNow );
return ccp->sizeWhenDoneLazy;
}
void
tr_cpPieceAdd( tr_completion * cp,
tr_piece_index_t piece )
{
const tr_torrent * tor = cp->tor;
const tr_block_index_t start = tr_torPieceFirstBlock( tor, piece );
const tr_block_index_t end = start + tr_torPieceCountBlocks( tor, piece );
tr_block_index_t i;
for( i = start; i < end; ++i )
tr_cpBlockAdd( cp, i );
}
void
tr_cpPieceRem( tr_completion * cp,
tr_piece_index_t piece )
{
const tr_torrent * tor = cp->tor;
const tr_block_index_t start = tr_torPieceFirstBlock( tor, piece );
const tr_block_index_t end = start + tr_torPieceCountBlocks( tor, piece );
tr_block_index_t block;
assert( cp );
assert( piece < tor->info.pieceCount );
assert( start < tor->blockCount );
assert( start <= end );
assert( end <= tor->blockCount );
for( block = start; block < end; ++block )
if( tr_cpBlockIsCompleteFast( cp, block ) )
cp->sizeNow -= tr_torBlockCountBytes( tor, block );
cp->sizeWhenDoneIsDirty = 1;
cp->haveValidIsDirty = 1;
cp->completeBlocks[piece] = 0;
tr_bitfieldRemRange ( &cp->blockBitfield, start, end );
tr_bitfieldRem( &cp->pieceBitfield, piece );
}
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 );
++cp->completeBlocks[piece];
if( tr_cpPieceIsComplete( cp, piece ) )
tr_bitfieldAdd( &cp->pieceBitfield, piece );
tr_bitfieldAdd( &cp->blockBitfield, block );
cp->sizeNow += blockSize;
cp->haveValidIsDirty = 1;
cp->sizeWhenDoneIsDirty = 1;
}
}
void
tr_cpSetHaveAll( tr_completion * cp )
{
tr_piece_index_t i;
tr_torrent * tor = cp->tor;
tr_cpReset( cp );
cp->sizeNow = tor->info.totalSize;
tr_bitfieldAddRange( &cp->blockBitfield, 0, tor->blockCount );
tr_bitfieldAddRange( &cp->pieceBitfield, 0, tor->info.pieceCount );
for( i=0; i<tor->info.pieceCount; ++i )
cp->completeBlocks[i] = tr_torPieceCountBlocks( tor, i );
cp->sizeWhenDoneIsDirty = 1;
cp->haveValidIsDirty = 1;
}
/* Initialize a completion object from a bitfield indicating which blocks we have */
tr_bool
tr_cpBlockBitfieldSet( tr_completion * cp, tr_bitfield * blockBitfield )
{
tr_bool success = FALSE;
assert( cp );
assert( blockBitfield );
/* The bitfield of block flags is typically loaded from a resume file.
Test the bitfield's length in case the resume file somehow got corrupted */
if(( success = blockBitfield->byteCount == cp->blockBitfield.byteCount ))
{
tr_block_index_t b = 0;
tr_piece_index_t p = 0;
uint32_t pieceBlock = 0;
uint16_t completeBlocksInPiece = 0;
tr_block_index_t completeBlocksInTorrent = 0;
uint32_t blocksInCurrentPiece = tr_torPieceCountBlocks( cp->tor, p );
/* start cp with a state where it thinks we have nothing */
tr_cpReset( cp );
/* init our block bitfield from the one passed in */
memcpy( cp->blockBitfield.bits, blockBitfield->bits, blockBitfield->byteCount );
/* invalidate the fields that are lazy-evaluated */
cp->sizeWhenDoneIsDirty = TRUE;
cp->haveValidIsDirty = TRUE;
/* to set the remaining fields, we walk through every block... */
while( b < cp->tor->blockCount )
{
if( tr_bitfieldHasFast( blockBitfield, b ) )
++completeBlocksInPiece;
++b;
++pieceBlock;
/* by the time we reach the end of a piece, we have enough info
to update that piece's slot in cp.completeBlocks and cp.pieceBitfield */
if( pieceBlock == blocksInCurrentPiece )
{
cp->completeBlocks[p] = completeBlocksInPiece;
completeBlocksInTorrent += completeBlocksInPiece;
if( completeBlocksInPiece == blocksInCurrentPiece )
tr_bitfieldAdd( &cp->pieceBitfield, p );
/* reset the per-piece counters because we're starting on a new piece now */
++p;
completeBlocksInPiece = 0;
pieceBlock = 0;
blocksInCurrentPiece = tr_torPieceCountBlocks( cp->tor, p );
}
}
/* update sizeNow */
cp->sizeNow = completeBlocksInTorrent;
cp->sizeNow *= tr_torBlockCountBytes( cp->tor, 0 );
if( tr_bitfieldHasFast( &cp->blockBitfield, cp->tor->blockCount-1 ) ) {
/* the last block is usually smaller than the other blocks,
so handle that special case or cp->sizeNow might be too large */
cp->sizeNow -= tr_torBlockCountBytes( cp->tor, 0 );
cp->sizeNow += tr_torBlockCountBytes( cp->tor, cp->tor->blockCount-1 );
}
}
return success;
}
/***
****
***/
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;
}
static uint64_t
calculateHaveValid( const tr_completion * ccp )
{
uint64_t b = 0;
tr_piece_index_t i;
const tr_torrent * tor = ccp->tor;
const uint64_t pieceSize = tor->info.pieceSize;
const uint64_t lastPieceSize = tor->lastPieceSize;
const tr_piece_index_t lastPiece = tor->info.pieceCount - 1;
if( !tr_torrentHasMetadata( tor ) )
return 0;
for( i=0; i!=lastPiece; ++i )
if( tr_cpPieceIsComplete( ccp, i ) )
b += pieceSize;
if( tr_cpPieceIsComplete( ccp, lastPiece ) )
b += lastPieceSize;
return b;
}
uint64_t
tr_cpHaveValid( const tr_completion * ccp )
{
if( ccp->haveValidIsDirty )
{
tr_completion * cp = (tr_completion *) ccp; /* mutable */
cp->haveValidLazy = calculateHaveValid( ccp );
cp->haveValidIsDirty = 0;
}
return ccp->haveValidLazy;
}
void
tr_cpGetAmountDone( const tr_completion * cp,
float * tab,
int tabCount )
{
int i;
const tr_torrent * tor = cp->tor;
const float interval = tor->info.pieceCount / (float)tabCount;
const int isSeed = tr_cpGetStatus( cp ) == TR_SEED;
for( i = 0; i < tabCount; ++i )
{
const tr_piece_index_t piece = i * interval;
if( tor == NULL )
tab[i] = 0.0f;
else if( isSeed || tr_cpPieceIsComplete( cp, piece ) )
tab[i] = 1.0f;
else
tab[i] = (float)cp->completeBlocks[piece] /
tr_torPieceCountBlocks( tor, piece );
}
}
int
tr_cpMissingBlocksInPiece( const tr_completion * cp, tr_piece_index_t piece )
{
return tr_torPieceCountBlocks( cp->tor, piece ) - cp->completeBlocks[piece];
}
tr_bool
tr_cpPieceIsComplete( const tr_completion * cp, tr_piece_index_t piece )
{
return cp->completeBlocks[piece] == tr_torPieceCountBlocks( cp->tor, piece );
}
tr_bool
tr_cpFileIsComplete( const tr_completion * cp, tr_file_index_t fileIndex )
{
tr_block_index_t block;
const tr_torrent * tor = cp->tor;
const tr_file * file = &tor->info.files[fileIndex];
const tr_block_index_t firstBlock = file->offset / tor->blockSize;
const tr_block_index_t lastBlock = file->length ? ( ( file->offset + file->length - 1 ) / tor->blockSize ) : firstBlock;
assert( tr_torBlockPiece( tor, firstBlock ) == file->firstPiece );
assert( tr_torBlockPiece( tor, lastBlock ) == file->lastPiece );
for( block=firstBlock; block<=lastBlock; ++block )
if( !tr_cpBlockIsCompleteFast( cp, block ) )
return FALSE;
return TRUE;
}