struct weighted_piece
{
tr_piece_index_t index;
- tr_piece_index_t salt;
+ int16_t salt;
int16_t requestCount;
};
int requestAlloc;
struct weighted_piece * pieces;
- int piecesSort;
int pieceCount;
-
- tr_bool isInEndgame;
}
Torrent;
assert( mode==PIECES_SORTED_BY_INDEX
|| mode==PIECES_SORTED_BY_WEIGHT );
- if( t->piecesSort != mode )
- {
- t->piecesSort = mode;
-
- switch( mode ) {
- case PIECES_SORTED_BY_WEIGHT: compar = comparePieceByWeight; break;
- case PIECES_SORTED_BY_INDEX: compar = comparePieceByIndex; break;
- default: assert( 0 && "unhandled" ); break;
- }
-
- weightTorrent = t->tor;
- qsort( t->pieces, t->pieceCount,
- sizeof( struct weighted_piece ), compar );
+ switch( mode ) {
+ case PIECES_SORTED_BY_WEIGHT: compar = comparePieceByWeight; break;
+ case PIECES_SORTED_BY_INDEX: compar = comparePieceByIndex; break;
+ default: assert( 0 && "unhandled" ); break;
}
- /* Also, as long as we've got the pieces sorted by weight,
- * let's also update t.isInEndgame */
- if( t->piecesSort == PIECES_SORTED_BY_WEIGHT )
- {
- tr_bool endgame = TRUE;
+ weightTorrent = t->tor;
+ qsort( t->pieces, t->pieceCount,
+ sizeof( struct weighted_piece ), compar );
+}
- if( ( t->pieces != NULL ) && ( t->pieceCount > 0 ) )
- {
- const tr_completion * cp = &t->tor->completion;
- const struct weighted_piece * p = t->pieces;
- const int pending = p->requestCount;
- const int missing = tr_cpMissingBlocksInPiece( cp, p->index );
- endgame = pending >= missing;
- }
+static tr_bool
+isInEndgame( Torrent * t )
+{
+ tr_bool endgame = TRUE;
- t->isInEndgame = endgame;
- /*if( t->isInEndgame ) fprintf( stderr, "ENDGAME reached\n" );*/
+ if( ( t->pieces != NULL ) && ( t->pieceCount > 0 ) )
+ {
+ const tr_completion * cp = &t->tor->completion;
+ const struct weighted_piece * p = t->pieces;
+ const int pending = p->requestCount;
+ const int missing = tr_cpMissingBlocksInPiece( cp, p->index );
+ endgame = pending >= missing;
}
+
+ /*if( endgame ) fprintf( stderr, "ENDGAME reached\n" );*/
+ return endgame;
}
static struct weighted_piece *
pieceListLookup( Torrent * t, tr_piece_index_t index )
{
- struct weighted_piece key;
- key.index = index;
+ const struct weighted_piece * limit = t->pieces;
+ struct weighted_piece * piece = t->pieces + t->pieceCount - 1;
- pieceListSort( t, PIECES_SORTED_BY_INDEX );
+ if ( t->pieceCount == 0 ) return NULL;
- return bsearch( &key, t->pieces, t->pieceCount,
- sizeof( struct weighted_piece ),
- comparePieceByIndex );
+ /* reverse linear search */
+ for( ;; )
+ {
+ if( piece < limit ) return NULL;
+ if( index == piece->index ) return piece; else --piece;
+ }
}
static void
t->pieces = pieces;
t->pieceCount = pieceCount;
- t->piecesSort = PIECES_SORTED_BY_INDEX;
+
+ pieceListSort( t, PIECES_SORTED_BY_WEIGHT );
/* cleanup */
tr_free( pool );
static void
pieceListRemoveRequest( Torrent * t, tr_block_index_t block )
{
- struct weighted_piece * p;
const tr_piece_index_t index = tr_torBlockPiece( t->tor, block );
+ const struct weighted_piece * p = pieceListLookup( t, index );
+
+ if( p != NULL )
+ {
+ const int pos = p - t->pieces;
+ struct weighted_piece piece = t->pieces[pos];
+ int newpos;
+ tr_bool exact;
- if(( p = pieceListLookup( t, index )))
- if( p->requestCount > 0 )
- --p->requestCount;
+ /* remove request */
+ if( piece.requestCount > 0 )
+ --piece.requestCount;
+
+ /* List is nearly sorted (by weight) : insert piece into the right place. */
+
+ weightTorrent = t->tor;
+ newpos = tr_lowerBound( &piece, t->pieces, t->pieceCount,
+ sizeof( struct weighted_piece ),
+ comparePieceByWeight, &exact );
- /* note: this invalidates the weighted.piece.weight field,
- * but that's OK since the call to pieceListLookup ensured
- * that we were sorted by index anyway.. next time we resort
- * by weight, pieceListSort() will update the weights */
+ if( newpos == pos || newpos == pos + 1 )
+ {
+ /* it's VERY likely that piece keeps its position */
+ t->pieces[pos].requestCount = piece.requestCount;
+ }
+ else
+ {
+ /* piece is removed temporally to make insertion easier */
+ memmove( &t->pieces[pos],
+ &t->pieces[pos + 1],
+ sizeof( struct weighted_piece ) * ( --t->pieceCount - pos ) );
+
+ if( newpos > pos ) --newpos;
+
+ memmove( &t->pieces[newpos + 1],
+ &t->pieces[newpos],
+ sizeof( struct weighted_piece ) * ( t->pieceCount++ - newpos ) );
+
+ t->pieces[newpos] = piece;
+ }
+ }
}
/**
int i;
int got;
Torrent * t;
+ tr_bool endgame;
struct weighted_piece * pieces;
const tr_bitset * have = &peer->have;
/* prep the pieces list */
if( t->pieces == NULL )
pieceListRebuild( t );
- pieceListSort( t, PIECES_SORTED_BY_WEIGHT );
+
+ endgame = isInEndgame( t );
pieces = t->pieces;
for( i=0; i<t->pieceCount && got<numwant; ++i )
{
struct weighted_piece * p = pieces + i;
const int missing = tr_cpMissingBlocksInPiece( &tor->completion, p->index );
- const int maxDuplicatesPerBlock = t->isInEndgame ? 3 : 1;
+ const int maxDuplicatesPerBlock = endgame ? 3 : 1;
if( p->requestCount > ( missing * maxDuplicatesPerBlock ) )
continue;
}
/* In most cases we've just changed the weights of a small number of pieces.
- * So rather than qsort()ing the entire array, it's faster to sort only the
- * changed ones, then do a standard merge-two-sorted-arrays pass on the
- * changed and unchanged pieces. */
+ * So rather than qsort()ing the entire array, it's faster to apply an
+ * adaptive insertion sort algorithm. */
if( got > 0 )
{
- struct weighted_piece * p;
- struct weighted_piece * pieces;
- struct weighted_piece * a = t->pieces;
- struct weighted_piece * a_end = t->pieces + i;
- struct weighted_piece * b = a_end;
- struct weighted_piece * b_end = t->pieces + t->pieceCount;
+ /* not enough requests || last piece modified */
+ if ( i == t->pieceCount ) --i;
- /* resort the pieces that we changed */
weightTorrent = t->tor;
- qsort( a, a_end-a, sizeof( struct weighted_piece ), comparePieceByWeight );
-
- /* allocate a new array */
- p = pieces = tr_new( struct weighted_piece, t->pieceCount );
-
- /* merge the two sorted arrays into this new array */
- weightTorrent = t->tor;
- while( a!=a_end && b!=b_end )
- *p++ = comparePieceByWeight( a, b ) < 0 ? *a++ : *b++;
- while( a!=a_end ) *p++ = *a++;
- while( b!=b_end ) *p++ = *b++;
-
-#if 0
- /* make sure we did it right */
- assert( p - pieces == t->pieceCount );
- for( it=pieces; it+1<p; ++it )
- assert( it->weight <= it[1].weight );
-#endif
+ while( --i >= 0 )
+ {
+ tr_bool exact;
- /* update */
- tr_free( t->pieces );
- t->pieces = pieces;
+ /* relative position! */
+ const int newpos = tr_lowerBound( &t->pieces[i], &t->pieces[i + 1],
+ t->pieceCount - (i + 1),
+ sizeof( struct weighted_piece ),
+ comparePieceByWeight, &exact );
+ if( newpos > 0 )
+ {
+ const struct weighted_piece piece = t->pieces[i];
+ memmove( &t->pieces[i],
+ &t->pieces[i + 1],
+ sizeof( struct weighted_piece ) * ( newpos ) );
+ t->pieces[i + newpos] = piece;
+ }
+ }
}
*numgot = got;
tr_block_index_t block = _tr_block( tor, e->pieceIndex, e->offset );
requestListRemove( t, block, peer );
- pieceListRemoveRequest( t, block );
if( tr_cpBlockIsComplete( &tor->completion, block ) )
{
tordbg( t, "we have this block already..." );
clientGotUnwantedBlock( tor, block );
+ pieceListRemoveRequest( t, block );
}
else
{
tr_cpBlockAdd( &tor->completion, block );
+ pieceListRemoveRequest( t, block );
tr_torrentSetDirty( tor );
if( tr_cpPieceIsComplete( &tor->completion, e->pieceIndex ) )