tr_completion* tr_cpDestruct( tr_completion * cp ) { tr_free( cp->completeBlocks ); tr_bitfieldDestruct( &cp->pieceBitfield ); tr_bitfieldDestruct( &cp->blockBitfield ); return cp; }
static int test_bitfields (void) { unsigned int i; unsigned int bitcount = 500; tr_bitfield field; tr_bitfieldConstruct (&field, bitcount); /* test tr_bitfieldAdd */ for (i=0; i<bitcount; i++) if (! (i % 7)) tr_bitfieldAdd (&field, i); for (i=0; i<bitcount; i++) check (tr_bitfieldHas (&field, i) == (! (i % 7))); /* test tr_bitfieldAddRange */ tr_bitfieldAddRange (&field, 0, bitcount); for (i=0; i<bitcount; i++) check (tr_bitfieldHas (&field, i)); /* test tr_bitfieldRemRange in the middle of a boundary */ tr_bitfieldRemRange (&field, 4, 21); for (i=0; i<64; i++) check (tr_bitfieldHas (&field, i) == ((i < 4) || (i >= 21))); /* test tr_bitfieldRemRange on the boundaries */ tr_bitfieldAddRange (&field, 0, 64); tr_bitfieldRemRange (&field, 8, 24); for (i=0; i<64; i++) check (tr_bitfieldHas (&field, i) == ((i < 8) || (i >= 24))); /* test tr_bitfieldRemRange when begin & end is on the same word */ tr_bitfieldAddRange (&field, 0, 64); tr_bitfieldRemRange (&field, 4, 5); for (i=0; i<64; i++) check (tr_bitfieldHas (&field, i) == ((i < 4) || (i >= 5))); /* test tr_bitfieldAddRange */ tr_bitfieldRemRange (&field, 0, 64); tr_bitfieldAddRange (&field, 4, 21); for (i=0; i<64; i++) check (tr_bitfieldHas (&field, i) == ((4 <= i) && (i < 21))); /* test tr_bitfieldAddRange on the boundaries */ tr_bitfieldRemRange (&field, 0, 64); tr_bitfieldAddRange (&field, 8, 24); for (i=0; i<64; i++) check (tr_bitfieldHas (&field, i) == ((8 <= i) && (i < 24))); /* test tr_bitfieldAddRange when begin & end is on the same word */ tr_bitfieldRemRange (&field, 0, 64); tr_bitfieldAddRange (&field, 4, 5); for (i=0; i<64; i++) check (tr_bitfieldHas (&field, i) == ((4 <= i) && (i < 5))); tr_bitfieldDestruct (&field); return 0; }
static int test_bitfield_count_range (void) { int i; int n; int begin; int end; int count1; int count2; const int bitCount = 100 + tr_cryptoWeakRandInt (1000); tr_bitfield bf; /* generate a random bitfield */ tr_bitfieldConstruct (&bf, bitCount); for (i=0, n=tr_cryptoWeakRandInt (bitCount); i<n; ++i) tr_bitfieldAdd (&bf, tr_cryptoWeakRandInt (bitCount)); begin = tr_cryptoWeakRandInt (bitCount); do end = tr_cryptoWeakRandInt (bitCount); while (end == begin); /* ensure end <= begin */ if (end < begin) { const int tmp = begin; begin = end; end = tmp; } /* test the bitfield */ count1 = 0; for (i=begin; i<end; ++i) if (tr_bitfieldHas (&bf, i)) ++count1; count2 = tr_bitfieldCountRange (&bf, begin, end); check (count1 == count2); /* cleanup */ tr_bitfieldDestruct (&bf); return 0; }
static uint64_t loadProgress( tr_benc * dict, tr_torrent * tor ) { size_t i, n; uint64_t ret = 0; tr_benc * prog; const tr_info * inf = tr_torrentInfo( tor ); for( i=0, n=inf->pieceCount; i<n; ++i ) inf->pieces[i].timeChecked = 0; if( tr_bencDictFindDict( dict, KEY_PROGRESS, &prog ) ) { const char * err; const char * str; const uint8_t * raw; size_t rawlen; tr_benc * l; tr_benc * b; struct tr_bitfield blocks = TR_BITFIELD_INIT; if( tr_bencDictFindList( prog, KEY_PROGRESS_CHECKTIME, &l ) ) { /* per-piece timestamps were added in 2.20. If some of a file's pieces have been checked more recently than the file's mtime, and some lest recently, then that file will have a list containing timestamps for each piece. However, the most common use case is that the file doesn't change after it's downloaded. To reduce overhead in the .resume file, only a single timestamp is saved for the file if *all* or *none* of the pieces were tested more recently than the file's mtime. */ tr_file_index_t fi; for( fi=0; fi<inf->fileCount; ++fi ) { tr_benc * b = tr_bencListChild( l, fi ); const tr_file * f = &inf->files[fi]; tr_piece * p = &inf->pieces[f->firstPiece]; const tr_piece * pend = &inf->pieces[f->lastPiece]+1; if( tr_bencIsInt( b ) ) { int64_t t; tr_bencGetInt( b, &t ); for( ; p!=pend; ++p ) p->timeChecked = (time_t)t; } else if( tr_bencIsList( b ) ) { int i = 0; int64_t offset = 0; const int pieces = f->lastPiece + 1 - f->firstPiece; tr_bencGetInt( tr_bencListChild( b, 0 ), &offset ); for( i=0; i<pieces; ++i ) { int64_t t = 0; tr_bencGetInt( tr_bencListChild( b, i+1 ), &t ); inf->pieces[f->firstPiece+i].timeChecked = (time_t)(t ? t + offset : 0); } } } } else if( tr_bencDictFindList( prog, KEY_PROGRESS_MTIMES, &l ) ) { tr_file_index_t fi; /* Before 2.20, we stored the files' mtimes in the .resume file. When loading the .resume file, a torrent's file would be flagged as untested if its stored mtime didn't match its real mtime. */ for( fi=0; fi<inf->fileCount; ++fi ) { int64_t t; if( tr_bencGetInt( tr_bencListChild( l, fi ), &t ) ) { const tr_file * f = &inf->files[fi]; tr_piece * p = &inf->pieces[f->firstPiece]; const tr_piece * pend = &inf->pieces[f->lastPiece]; const time_t mtime = tr_torrentGetFileMTime( tor, fi ); const time_t timeChecked = mtime==t ? mtime : 0; for( ; p!=pend; ++p ) p->timeChecked = timeChecked; } } } err = NULL; tr_bitfieldConstruct( &blocks, tor->blockCount ); if(( b = tr_bencDictFind( prog, KEY_PROGRESS_BLOCKS ))) { size_t buflen; const uint8_t * buf; if( !tr_bencGetRaw( b, &buf, &buflen ) ) err = "Invalid value for \"blocks\""; else if( ( buflen == 3 ) && !memcmp( buf, "all", 3 ) ) tr_bitfieldSetHasAll( &blocks ); else if( ( buflen == 4 ) && !memcmp( buf, "none", 4 ) ) tr_bitfieldSetHasNone( &blocks ); else tr_bitfieldSetRaw( &blocks, buf, buflen, true ); } else if( tr_bencDictFindStr( prog, KEY_PROGRESS_HAVE, &str ) ) { if( !strcmp( str, "all" ) ) tr_bitfieldSetHasAll( &blocks ); else err = "Invalid value for HAVE"; } else if( tr_bencDictFindRaw( prog, KEY_PROGRESS_BITFIELD, &raw, &rawlen ) ) { tr_bitfieldSetRaw( &blocks, raw, rawlen, true ); } else err = "Couldn't find 'pieces' or 'have' or 'bitfield'"; if( err != NULL ) tr_tordbg( tor, "Torrent needs to be verified - %s", err ); else tr_cpBlockInit( &tor->completion, &blocks ); tr_bitfieldDestruct( &blocks ); ret = TR_FR_PROGRESS; } return ret; }
static uint64_t loadProgress(tr_variant* dict, tr_torrent* tor) { uint64_t ret = 0; tr_variant* prog; tr_info const* inf = tr_torrentInfo(tor); for (size_t i = 0; i < inf->pieceCount; ++i) { inf->pieces[i].timeChecked = 0; } if (tr_variantDictFindDict(dict, TR_KEY_progress, &prog)) { char const* err; char const* str; uint8_t const* raw; size_t rawlen; tr_variant* l; tr_variant* b; struct tr_bitfield blocks = TR_BITFIELD_INIT; if (tr_variantDictFindList(prog, TR_KEY_time_checked, &l)) { /* per-piece timestamps were added in 2.20. If some of a file's pieces have been checked more recently than the file's mtime, and some lest recently, then that file will have a list containing timestamps for each piece. However, the most common use case is that the file doesn't change after it's downloaded. To reduce overhead in the .resume file, only a single timestamp is saved for the file if *all* or *none* of the pieces were tested more recently than the file's mtime. */ for (tr_file_index_t fi = 0; fi < inf->fileCount; ++fi) { tr_variant* b = tr_variantListChild(l, fi); tr_file const* f = &inf->files[fi]; if (tr_variantIsInt(b)) { int64_t t; tr_variantGetInt(b, &t); for (tr_piece_index_t i = f->firstPiece; i <= f->lastPiece; ++i) { inf->pieces[i].timeChecked = (time_t)t; } } else if (tr_variantIsList(b)) { int64_t offset = 0; int const pieces = f->lastPiece + 1 - f->firstPiece; tr_variantGetInt(tr_variantListChild(b, 0), &offset); for (int i = 0; i < pieces; ++i) { int64_t t = 0; tr_variantGetInt(tr_variantListChild(b, i + 1), &t); inf->pieces[f->firstPiece + i].timeChecked = (time_t)(t != 0 ? t + offset : 0); } } } } else if (tr_variantDictFindList(prog, TR_KEY_mtimes, &l)) { /* Before 2.20, we stored the files' mtimes in the .resume file. When loading the .resume file, a torrent's file would be flagged as untested if its stored mtime didn't match its real mtime. */ for (tr_file_index_t fi = 0; fi < inf->fileCount; ++fi) { int64_t t; if (tr_variantGetInt(tr_variantListChild(l, fi), &t)) { tr_file const* f = &inf->files[fi]; time_t const mtime = tr_torrentGetFileMTime(tor, fi); time_t const timeChecked = mtime == t ? mtime : 0; for (tr_piece_index_t i = f->firstPiece; i <= f->lastPiece; ++i) { inf->pieces[i].timeChecked = timeChecked; } } } } err = NULL; tr_bitfieldConstruct(&blocks, tor->blockCount); if ((b = tr_variantDictFind(prog, TR_KEY_blocks)) != NULL) { size_t buflen; uint8_t const* buf; if (!tr_variantGetRaw(b, &buf, &buflen)) { err = "Invalid value for \"blocks\""; } else if (buflen == 3 && memcmp(buf, "all", 3) == 0) { tr_bitfieldSetHasAll(&blocks); } else if (buflen == 4 && memcmp(buf, "none", 4) == 0) { tr_bitfieldSetHasNone(&blocks); } else { tr_bitfieldSetRaw(&blocks, buf, buflen, true); } } else if (tr_variantDictFindStr(prog, TR_KEY_have, &str, NULL)) { if (strcmp(str, "all") == 0) { tr_bitfieldSetHasAll(&blocks); } else { err = "Invalid value for HAVE"; } } else if (tr_variantDictFindRaw(prog, TR_KEY_bitfield, &raw, &rawlen)) { tr_bitfieldSetRaw(&blocks, raw, rawlen, true); } else { err = "Couldn't find 'pieces' or 'have' or 'bitfield'"; } if (err != NULL) { tr_logAddTorDbg(tor, "Torrent needs to be verified - %s", err); } else { tr_cpBlockInit(&tor->completion, &blocks); } tr_bitfieldDestruct(&blocks); ret = TR_FR_PROGRESS; } return ret; }