big change to improve piece picker. Better end-game mode. More options on which pieces to pick.

This commit is contained in:
Arvid Norberg 2008-09-06 21:04:57 +00:00
parent 7f143791d5
commit 41808f8742
8 changed files with 1205 additions and 675 deletions

View File

@ -151,7 +151,7 @@ namespace libtorrent
policy::peer* peer_info_struct() const policy::peer* peer_info_struct() const
{ return m_peer_info; } { return m_peer_info; }
enum peer_speed_t { slow, medium, fast }; enum peer_speed_t { slow = 1, medium, fast };
peer_speed_t peer_speed(); peer_speed_t peer_speed();
void send_allowed_set(); void send_allowed_set();
@ -187,6 +187,8 @@ namespace libtorrent
bool on_parole() const bool on_parole() const
{ return peer_info_struct() && peer_info_struct()->on_parole; } { return peer_info_struct() && peer_info_struct()->on_parole; }
int picker_options() const;
void prefer_whole_pieces(int num) void prefer_whole_pieces(int num)
{ m_prefer_whole_pieces = num; } { m_prefer_whole_pieces = num; }

View File

@ -88,6 +88,14 @@ namespace libtorrent
{ {
public: public:
enum
{
// the number of priority levels
priority_levels = 8,
// priority factor
prio_factor = priority_levels - 4
};
struct block_info struct block_info
{ {
block_info(): peer(0), num_peers(0), state(state_none) {} block_info(): peer(0), num_peers(0), state(state_none) {}
@ -110,6 +118,20 @@ namespace libtorrent
enum piece_state_t enum piece_state_t
{ none, slow, medium, fast }; { none, slow, medium, fast };
enum options_t
{
// pick rarest first
rarest_first = 1,
// pick the most common first, or the last pieces if sequential
reverse = 2,
// only pick pieces exclusively requested from this peer
on_parole = 4,
// always pick partial pieces before any other piece
prioritize_partials = 8,
// pick pieces in sequential order
sequential = 16
};
struct downloading_piece struct downloading_piece
{ {
downloading_piece(): finished(0), writing(0), requested(0) {} downloading_piece(): finished(0), writing(0), requested(0) {}
@ -133,9 +155,6 @@ namespace libtorrent
void get_availability(std::vector<int>& avail) const; void get_availability(std::vector<int>& avail) const;
void sequential_download(bool sd);
bool sequential_download() const { return m_sequential_download >= 0; }
// increases the peer count for the given piece // increases the peer count for the given piece
// (is used when a HAVE message is received) // (is used when a HAVE message is received)
void inc_refcount(int index); void inc_refcount(int index);
@ -161,6 +180,9 @@ namespace libtorrent
void we_have(int index); void we_have(int index);
void we_dont_have(int index); void we_dont_have(int index);
int cursor() const { return m_cursor; }
int reverse_cursor() const { return m_reverse_cursor; }
// sets all pieces to dont-have // sets all pieces to dont-have
void init(int blocks_per_piece, int total_num_blocks); void init(int blocks_per_piece, int total_num_blocks);
int num_pieces() const { return int(m_piece_map.size()); } int num_pieces() const { return int(m_piece_map.size()); }
@ -202,11 +224,9 @@ namespace libtorrent
// The last argument is the policy::peer pointer for the peer that // The last argument is the policy::peer pointer for the peer that
// we'll download from. // we'll download from.
void pick_pieces(bitfield const& pieces void pick_pieces(bitfield const& pieces
, std::vector<piece_block>& interesting_blocks , std::vector<piece_block>& interesting_blocks, int num_blocks
, int num_pieces, int prefer_whole_pieces , int prefer_whole_pieces, void* peer, piece_state_t speed
, void* peer, piece_state_t speed , int options, std::vector<int> const& suggested_pieces) const;
, bool rarest_first, bool on_parole
, std::vector<int> const& suggested_pieces) const;
// picks blocks from each of the pieces in the piece_list // picks blocks from each of the pieces in the piece_list
// vector that is also in the piece bitmask. The blocks // vector that is also in the piece bitmask. The blocks
@ -214,20 +234,23 @@ namespace libtorrent
// added to backup_blocks. num blocks is the number of // added to backup_blocks. num blocks is the number of
// blocks to be picked. Blocks are not picked from pieces // blocks to be picked. Blocks are not picked from pieces
// that are being downloaded // that are being downloaded
int add_blocks(std::vector<int> const& piece_list int add_blocks(int piece, bitfield const& pieces
, bitfield const& pieces
, std::vector<piece_block>& interesting_blocks
, int num_blocks, int prefer_whole_pieces
, void* peer, std::vector<int> const& ignore) const;
// picks blocks only from downloading pieces
int add_blocks_downloading(
bitfield const& pieces
, std::vector<piece_block>& interesting_blocks , std::vector<piece_block>& interesting_blocks
, std::vector<piece_block>& backup_blocks , std::vector<piece_block>& backup_blocks
, std::vector<piece_block>& backup_blocks2
, int num_blocks, int prefer_whole_pieces
, void* peer, std::vector<int> const& ignore
, piece_state_t speed, int options) const;
// picks blocks only from downloading pieces
int add_blocks_downloading(downloading_piece const& dp
, bitfield const& pieces
, std::vector<piece_block>& interesting_blocks
, std::vector<piece_block>& backup_blocks
, std::vector<piece_block>& backup_blocks2
, int num_blocks, int prefer_whole_pieces , int num_blocks, int prefer_whole_pieces
, void* peer, piece_state_t speed , void* peer, piece_state_t speed
, bool on_parole) const; , int options) const;
// clears the peer pointer in all downloading pieces with this // clears the peer pointer in all downloading pieces with this
// peer pointer // peer pointer
@ -291,6 +314,8 @@ namespace libtorrent
void check_invariant(const torrent* t = 0) const; void check_invariant(const torrent* t = 0) const;
void verify_pick(std::vector<piece_block> const& picked void verify_pick(std::vector<piece_block> const& picked
, bitfield const& bits) const; , bitfield const& bits) const;
#endif
#if defined TORRENT_PICKER_LOG || !defined NDEBUG
void print_pieces() const; void print_pieces() const;
#endif #endif
@ -313,6 +338,7 @@ namespace libtorrent
friend struct piece_pos; friend struct piece_pos;
bool can_pick(int piece, bitfield const& bitmask) const; bool can_pick(int piece, bitfield const& bitmask) const;
bool is_piece_free(int piece, bitfield const& bitmask) const;
std::pair<int, int> expand_piece(int piece, int whole_pieces std::pair<int, int> expand_piece(int piece, int whole_pieces
, bitfield const& have) const; , bitfield const& have) const;
@ -354,7 +380,7 @@ namespace libtorrent
// the priority value that means the piece is filtered // the priority value that means the piece is filtered
filter_priority = 0, filter_priority = 0,
// the max number the peer count can hold // the max number the peer count can hold
max_peer_count = 0x3ff max_peer_count = 0x3ff,
}; };
bool have() const { return index == we_have_index; } bool have() const { return index == we_have_index; }
@ -364,20 +390,41 @@ namespace libtorrent
bool filtered() const { return piece_priority == filter_priority; } bool filtered() const { return piece_priority == filter_priority; }
void filtered(bool f) { piece_priority = f ? filter_priority : 0; } void filtered(bool f) { piece_priority = f ? filter_priority : 0; }
// prio 7 is always top priority
// prio 0 is always -1 (don't pick)
// downloading pieces are always on an even prio_factor priority
//
// availability x, downloading
// | availability x, prio 3; availability 2x, prio 6
// | | availability x, prio 2; availability 2x, prio 5
// | | | availability x, prio 1; availability 2x, prio 4
// | | | |
// +---+---+---+---+
// | 0 | 1 | 2 | 3 |
// +---+---+---+---+
int priority(piece_picker const* picker) const int priority(piece_picker const* picker) const
{ {
if (downloading || filtered() // filtered pieces (prio = 0), pieces we have or pieces with
|| have() || peer_count + picker->m_seeds == 0) // availability = 0 should not be present in the piece list
// returning -1 indicates that they shouldn't.
if (filtered() || have() || peer_count + picker->m_seeds == 0)
return -1; return -1;
// priority 5, 6 and 7 disregards availability of the piece // prio 7 disregards availability
if (piece_priority > 4) return 7 - piece_priority; if (piece_priority == priority_levels - 1) return 1 - downloading;
// pieces we are currently downloading have high priority // prio 4,5,6 halves the availability of a piece
int prio = peer_count * 4; int availability = peer_count;
// if (prio >= picker->m_prio_limit * 6) prio = picker->m_prio_limit * 6; int priority = piece_priority;
if (piece_priority >= priority_levels / 2)
{
availability /= 2;
priority -= (priority_levels - 2) / 2;
}
return prio + (4 - piece_priority); if (downloading) return availability * prio_factor;
return availability * prio_factor + (priority_levels / 2) - priority;
} }
bool operator!=(piece_pos p) const bool operator!=(piece_pos p) const
@ -467,11 +514,14 @@ namespace libtorrent
// the number of pieces we have // the number of pieces we have
int m_num_have; int m_num_have;
// -1 means sequential download is not active. // we have all pieces in the range [0, m_cursor)
// >= 0 means that pieces are requested in sequential order // m_cursor is the first piece we don't have
// and this variable is the next piece to request. int m_cursor;
// in that case m_pieces is cleared and not used.
int m_sequential_download; // we have all pieces in the range [m_reverse_cursor, end)
// m_reverse_cursor is the first piece where we also have
// all the subsequent pieces
int m_reverse_cursor;
// if this is set to true, it means update_pieces() // if this is set to true, it means update_pieces()
// has to be called before accessing m_pieces. // has to be called before accessing m_pieces.

View File

@ -141,6 +141,7 @@ namespace libtorrent
, auto_scrape_min_interval(300) , auto_scrape_min_interval(300)
, max_peerlist_size(8000) , max_peerlist_size(8000)
, min_announce_interval(5 * 60) , min_announce_interval(5 * 60)
, prioritize_partial_pieces(false)
{} {}
// this is the user agent that will be sent to the tracker // this is the user agent that will be sent to the tracker
@ -436,6 +437,10 @@ namespace libtorrent
// that is lower than this, will be clamped to this // that is lower than this, will be clamped to this
// value. It's specified in seconds // value. It's specified in seconds
int min_announce_interval; int min_announce_interval;
// if true, partial pieces are picked before pieces
// that are more rare
bool prioritize_partial_pieces;
}; };
#ifndef TORRENT_DISABLE_DHT #ifndef TORRENT_DISABLE_DHT

View File

@ -572,6 +572,50 @@ namespace libtorrent
#endif #endif
} }
int peer_connection::picker_options() const
{
int ret = 0;
boost::shared_ptr<torrent> t = m_torrent.lock();
TORRENT_ASSERT(t);
if (!t) return 0;
if (t->is_sequential_download())
{
ret |= piece_picker::sequential;
}
else if (t->num_have() < t->settings().initial_picker_threshold)
{
// if we have fewer pieces than a certain threshols
// don't pick rare pieces, just pick random ones,
// and prioritize finishing them
ret |= piece_picker::prioritize_partials;
}
else
{
ret |= piece_picker::rarest_first;
}
if (m_snubbed)
{
// snubbed peers should request
// the common pieces first, just to make
// it more likely for all snubbed peers to
// request blocks from the same piece
ret |= piece_picker::reverse;
}
if (t->settings().prioritize_partial_pieces)
ret |= piece_picker::prioritize_partials;
if (on_parole()) ret |= piece_picker::on_parole
| piece_picker::prioritize_partials;
// only one of rarest_first, common_first and sequential can be set.
TORRENT_ASSERT(bool(ret & piece_picker::rarest_first)
+ bool(ret & piece_picker::sequential) <= 1);
return ret;
}
void peer_connection::fast_reconnect(bool r) void peer_connection::fast_reconnect(bool r)
{ {
if (!peer_info_struct() || peer_info_struct()->fast_reconnects > 1) if (!peer_info_struct() || peer_info_struct()->fast_reconnects > 1)
@ -1724,9 +1768,12 @@ namespace libtorrent
bool multi = picker.num_peers(block_finished) > 1; bool multi = picker.num_peers(block_finished) > 1;
picker.mark_as_writing(block_finished, peer_info_struct()); picker.mark_as_writing(block_finished, peer_info_struct());
TORRENT_ASSERT(picker.num_peers(block_finished) == 0);
// if we requested this block from other peers, cancel it now // if we requested this block from other peers, cancel it now
if (multi) t->cancel_block(block_finished); if (multi) t->cancel_block(block_finished);
TORRENT_ASSERT(picker.num_peers(block_finished) == 0);
#if !defined NDEBUG && !defined TORRENT_DISABLE_INVARIANT_CHECKS #if !defined NDEBUG && !defined TORRENT_DISABLE_INVARIANT_CHECKS
t->check_invariant(); t->check_invariant();
#endif #endif
@ -1777,6 +1824,7 @@ namespace libtorrent
TORRENT_ASSERT(p.piece == j.piece); TORRENT_ASSERT(p.piece == j.piece);
TORRENT_ASSERT(p.start == j.offset); TORRENT_ASSERT(p.start == j.offset);
TORRENT_ASSERT(picker.num_peers(block_finished) == 0);
picker.mark_as_finished(block_finished, peer_info_struct()); picker.mark_as_finished(block_finished, peer_info_struct());
if (t->alerts().should_post<block_finished_alert>()) if (t->alerts().should_post<block_finished_alert>())
{ {
@ -3899,7 +3947,7 @@ namespace libtorrent
m_speed = medium; m_speed = medium;
else if (download_rate < torrent_download_rate / 15 && m_speed == fast) else if (download_rate < torrent_download_rate / 15 && m_speed == fast)
m_speed = medium; m_speed = medium;
else if (download_rate < torrent_download_rate / 63 && m_speed == medium) else
m_speed = slow; m_speed = slow;
return m_speed; return m_speed;

File diff suppressed because it is too large Load Diff

View File

@ -200,8 +200,6 @@ namespace libtorrent
int prefer_whole_pieces = c.prefer_whole_pieces(); int prefer_whole_pieces = c.prefer_whole_pieces();
bool rarest_first = t.num_have() >= t.settings().initial_picker_threshold;
if (prefer_whole_pieces == 0) if (prefer_whole_pieces == 0)
{ {
prefer_whole_pieces = c.statistics().download_payload_rate() prefer_whole_pieces = c.statistics().download_payload_rate()
@ -250,7 +248,7 @@ namespace libtorrent
p.pick_pieces(mask, interesting_pieces p.pick_pieces(mask, interesting_pieces
, num_requests, prefer_whole_pieces, c.peer_info_struct() , num_requests, prefer_whole_pieces, c.peer_info_struct()
, state, rarest_first, c.on_parole(), suggested); , state, c.picker_options(), suggested);
} }
else else
{ {
@ -264,7 +262,7 @@ namespace libtorrent
// then use this mode. // then use this mode.
p.pick_pieces(bits, interesting_pieces p.pick_pieces(bits, interesting_pieces
, num_requests, prefer_whole_pieces, c.peer_info_struct() , num_requests, prefer_whole_pieces, c.peer_info_struct()
, state, rarest_first, c.on_parole(), suggested); , state, c.picker_options(), suggested);
} }
#ifdef TORRENT_VERBOSE_LOGGING #ifdef TORRENT_VERBOSE_LOGGING
@ -303,10 +301,6 @@ namespace libtorrent
if (busy_pieces.empty() || num_requests <= 0) if (busy_pieces.empty() || num_requests <= 0)
{ {
// in this case, we could not find any blocks
// that was free. If we couldn't find any busy
// blocks as well, we cannot download anything
// more from this peer.
return; return;
} }

View File

@ -754,10 +754,14 @@ namespace libtorrent
m_progress = j.piece / float(torrent_file().num_pieces()); m_progress = j.piece / float(torrent_file().num_pieces());
m_picker->check_invariant();
TORRENT_ASSERT(m_picker); TORRENT_ASSERT(m_picker);
if (j.offset >= 0 && !m_picker->have_piece(j.offset)) if (j.offset >= 0 && !m_picker->have_piece(j.offset))
m_picker->we_have(j.offset); m_picker->we_have(j.offset);
m_picker->check_invariant();
// we're not done checking yet // we're not done checking yet
// this handler will be called repeatedly until // this handler will be called repeatedly until
// we're done, or encounter a failure // we're done, or encounter a failure
@ -3298,9 +3302,6 @@ namespace libtorrent
if (!is_seed()) if (!is_seed())
{ {
if (m_sequential_download)
picker().sequential_download(m_sequential_download);
// if we just finished checking and we're not a seed, we are // if we just finished checking and we're not a seed, we are
// likely to be unpaused // likely to be unpaused
if (m_ses.m_auto_manage_time_scaler > 1) if (m_ses.m_auto_manage_time_scaler > 1)
@ -3549,13 +3550,7 @@ namespace libtorrent
#endif #endif
void torrent::set_sequential_download(bool sd) void torrent::set_sequential_download(bool sd)
{ { m_sequential_download = sd; }
m_sequential_download = sd;
if (has_picker())
{
picker().sequential_download(sd);
}
}
void torrent::set_queue_position(int p) void torrent::set_queue_position(int p)
{ {

View File

@ -71,6 +71,14 @@ boost::shared_ptr<piece_picker> setup_picker(
boost::shared_ptr<piece_picker> p(new piece_picker); boost::shared_ptr<piece_picker> p(new piece_picker);
p->init(blocks_per_piece, num_pieces * blocks_per_piece); p->init(blocks_per_piece, num_pieces * blocks_per_piece);
for (int i = 0; i < num_pieces; ++i)
{
const int avail = availability[i] - '0';
assert(avail >= 0);
for (int j = 0; j < avail; ++j) p->inc_refcount(i);
}
bitfield have = string2vec(have_str); bitfield have = string2vec(have_str);
for (int i = 0; i < num_pieces; ++i) for (int i = 0; i < num_pieces; ++i)
@ -86,25 +94,24 @@ boost::shared_ptr<piece_picker> setup_picker(
blocks = partial[i] - 'a' + 10; blocks = partial[i] - 'a' + 10;
int counter = 0; int counter = 0;
if (blocks & 1) for (int j = 0; j < 4; ++j)
{ {
TEST_CHECK(!p->is_finished(piece_block(i, j)));
if ((blocks & (1 << j)) == 0) continue;
++counter; ++counter;
p->mark_as_finished(piece_block(i, 0), 0); bool ret = p->mark_as_downloading(piece_block(i, j), 0, piece_picker::slow);
} TEST_CHECK(ret == true);
if (blocks & 2) TEST_CHECK(p->is_requested(piece_block(i, j)) == bool(blocks & (1 << j)));
{ p->mark_as_writing(piece_block(i, j), 0);
++counter; TEST_CHECK(!p->is_finished(piece_block(i, j)));
p->mark_as_finished(piece_block(i, 1), 0); // trying to mark a block as requested after it has been completed
} // should fail (return false)
if (blocks & 4) ret = p->mark_as_downloading(piece_block(i, j), 0, piece_picker::slow);
{ TEST_CHECK(ret == false);
++counter; p->mark_as_finished(piece_block(i, j), 0);
p->mark_as_finished(piece_block(i, 2), 0);
} TEST_CHECK(p->is_downloaded(piece_block(i, j)) == bool(blocks & (1 << j)));
if (blocks & 8) TEST_CHECK(p->is_finished(piece_block(i, j)) == bool(blocks & (1 << j)));
{
++counter;
p->mark_as_finished(piece_block(i, 3), 0);
} }
piece_picker::downloading_piece st; piece_picker::downloading_piece st;
@ -114,6 +121,9 @@ boost::shared_ptr<piece_picker> setup_picker(
TEST_CHECK(st.index == i); TEST_CHECK(st.index == i);
TEST_CHECK(st.finished == counter); TEST_CHECK(st.finished == counter);
TEST_CHECK(st.finished + st.requested + st.writing == counter);
TEST_CHECK(p->is_piece_finished(i) == (counter == 4));
} }
for (int i = 0; i < num_pieces; ++i) for (int i = 0; i < num_pieces; ++i)
@ -134,14 +144,6 @@ boost::shared_ptr<piece_picker> setup_picker(
TEST_CHECK(p->is_finished(piece_block(i, j))); TEST_CHECK(p->is_finished(piece_block(i, j)));
} }
for (int i = 0; i < num_pieces; ++i)
{
const int avail = availability[i] - '0';
assert(avail >= 0);
for (int j = 0; j < avail; ++j) p->inc_refcount(i);
}
std::vector<int> availability_vec; std::vector<int> availability_vec;
p->get_availability(availability_vec); p->get_availability(availability_vec);
for (int i = 0; i < num_pieces; ++i) for (int i = 0; i < num_pieces; ++i)
@ -158,20 +160,25 @@ boost::shared_ptr<piece_picker> setup_picker(
} }
bool verify_pick(boost::shared_ptr<piece_picker> p bool verify_pick(boost::shared_ptr<piece_picker> p
, std::vector<piece_block> const& picked) , std::vector<piece_block> const& picked, bool allow_multi_blocks = false)
{ {
#ifndef NDEBUG #ifndef NDEBUG
p->check_invariant(); p->check_invariant();
#endif #endif
if (!allow_multi_blocks)
{
for (std::vector<piece_block>::const_iterator i = picked.begin() for (std::vector<piece_block>::const_iterator i = picked.begin()
, end(picked.end()); i != end; ++i) , end(picked.end()); i != end; ++i)
{ {
if (p->num_peers(*i) > 0) return false; if (p->num_peers(*i) > 0) return false;
} }
}
// make sure there are no duplicated // make sure there are no duplicated
std::set<piece_block> blocks; std::set<piece_block> blocks;
std::copy(picked.begin(), picked.end(), std::insert_iterator<std::set<piece_block> >(blocks, blocks.end())); std::copy(picked.begin(), picked.end()
, std::insert_iterator<std::set<piece_block> >(blocks, blocks.end()));
std::cerr << " verify: " << picked.size() << " " << blocks.size() << std::endl;
return picked.size() == blocks.size(); return picked.size() == blocks.size();
} }
@ -189,60 +196,182 @@ void print_title(char const* name)
std::cerr << "==== " << name << " ====\n"; std::cerr << "==== " << name << " ====\n";
} }
int test_pick(boost::shared_ptr<piece_picker> const& p) std::vector<piece_block> pick_pieces(boost::shared_ptr<piece_picker> const& p, char const* availability
, int num_blocks, int prefer_whole_pieces, void* peer_struct, piece_picker::piece_state_t state
, int options, std::vector<int> const& suggested_pieces)
{ {
std::vector<piece_block> picked; std::vector<piece_block> picked;
const std::vector<int> empty_vector; p->pick_pieces(string2vec(availability), picked, num_blocks, prefer_whole_pieces, peer_struct
p->pick_pieces(string2vec("*******"), picked, 1, false, 0, piece_picker::fast, true, false, empty_vector); , state, options, suggested_pieces);
print_pick(picked); print_pick(picked);
TEST_CHECK(verify_pick(p, picked)); TEST_CHECK(verify_pick(p, picked));
TEST_CHECK(int(picked.size()) == 1); return picked;
}
int test_pick(boost::shared_ptr<piece_picker> const& p, int options = piece_picker::rarest_first)
{
const std::vector<int> empty_vector;
std::vector<piece_block> picked = pick_pieces(p, "*******", 1, 0, 0
, piece_picker::fast, options, empty_vector);
if (picked.empty()) return -1;
return picked[0].piece_index; return picked[0].piece_index;
} }
int test_main() int test_main()
{ {
int tmp1;
int tmp2;
int tmp3;
tcp::endpoint endp; tcp::endpoint endp;
piece_picker::downloading_piece st;
policy::peer peer_struct(endp, policy::peer::connectable, 0); policy::peer peer_struct(endp, policy::peer::connectable, 0);
std::vector<piece_block> picked; std::vector<piece_block> picked;
boost::shared_ptr<piece_picker> p; boost::shared_ptr<piece_picker> p;
const std::vector<int> empty_vector; const std::vector<int> empty_vector;
int options = piece_picker::rarest_first;
// ========================================================
// test abort_download
print_title("test abort_download");
p = setup_picker("1111111", " ", "7110000", "");
picked = pick_pieces(p, "*******", blocks_per_piece, 0, 0, piece_picker::fast
, options, empty_vector);
TEST_CHECK(p->is_requested(piece_block(0, 0)) == false);
TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) != picked.end());
p->abort_download(piece_block(piece_block(0,0)));
picked = pick_pieces(p, "*******", blocks_per_piece, 0, 0, piece_picker::fast
, options, empty_vector);
TEST_CHECK(p->is_requested(piece_block(0, 0)) == false);
TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) != picked.end());
p->mark_as_downloading(piece_block(0,0), &tmp1, piece_picker::fast);
picked = pick_pieces(p, "*******", blocks_per_piece, 0, 0, piece_picker::fast
, options, empty_vector);
TEST_CHECK(p->is_requested(piece_block(0, 0)) == true);
TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) == picked.end());
p->abort_download(piece_block(0,0));
picked = pick_pieces(p, "*******", blocks_per_piece, 0, 0, piece_picker::fast
, options, empty_vector);
TEST_CHECK(p->is_requested(piece_block(0, 0)) == false);
TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) != picked.end());
p->mark_as_downloading(piece_block(0,0), &tmp1, piece_picker::fast);
p->mark_as_downloading(piece_block(0,1), &tmp1, piece_picker::fast);
p->abort_download(piece_block(0,0));
picked = pick_pieces(p, "*******", blocks_per_piece, 0, 0, piece_picker::fast
, options, empty_vector);
TEST_CHECK(p->is_requested(piece_block(0, 0)) == false);
TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) != picked.end());
p->mark_as_downloading(piece_block(0,0), &tmp1, piece_picker::fast);
p->mark_as_writing(piece_block(0,0), &tmp1);
p->write_failed(piece_block(0,0));
picked = pick_pieces(p, "*******", blocks_per_piece, 0, 0, piece_picker::fast
, options, empty_vector);
TEST_CHECK(p->is_requested(piece_block(0, 0)) == false);
TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) != picked.end());
p->mark_as_downloading(piece_block(0,0), &tmp1, piece_picker::fast);
p->mark_as_writing(piece_block(0,0), &tmp1);
p->mark_as_finished(piece_block(0,0), &tmp1);
p->abort_download(piece_block(0,0));
picked = pick_pieces(p, "*******", blocks_per_piece, 0, 0, piece_picker::fast
, options, empty_vector);
TEST_CHECK(p->is_requested(piece_block(0, 0)) == false);
TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) == picked.end());
p = setup_picker("1111111", " ", "7110000", "");
p->mark_as_downloading(piece_block(0,0), &tmp1, piece_picker::fast);
p->mark_as_finished(piece_block(0,1), 0);
p->piece_info(0, st);
TEST_CHECK(st.requested == 1);
TEST_CHECK(st.finished == 1);
TEST_CHECK(st.state == piece_picker::fast);
p->abort_download(piece_block(0,0));
p->piece_info(0, st);
TEST_CHECK(st.requested == 0);
TEST_CHECK(st.finished == 1);
TEST_CHECK(st.state == piece_picker::none);
picked = pick_pieces(p, "*******", blocks_per_piece, 0, 0, piece_picker::fast
, options, empty_vector);
TEST_CHECK(p->is_requested(piece_block(0, 0)) == false);
TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) != picked.end());
// ========================================================
// make sure the block that is picked is from piece 1, since it // make sure the block that is picked is from piece 1, since it
// it is the piece with the lowest availability // it is the piece with the lowest availability
print_title("test pick lowest availability"); print_title("test pick lowest availability");
p = setup_picker("2223333", "* * * ", "", ""); p = setup_picker("2223333", "* * * ", "", "");
picked.clear(); TEST_CHECK(test_pick(p) == 1);
p->pick_pieces(string2vec("*******"), picked, 1, false, 0, piece_picker::fast, true, false, empty_vector);
TEST_CHECK(verify_pick(p, picked)); // ========================================================
TEST_CHECK(int(picked.size()) > 0);
TEST_CHECK(picked.front().piece_index == 1); // make sure pieces with equal priority and availability
// are picked at random
print_title("test random pick at same priority");
std::map<int, int> random_prio_pieces;
for (int i = 0; i < 100; ++i)
{
p = setup_picker("1111112", " ", "", "");
++random_prio_pieces[test_pick(p)];
}
TEST_CHECK(random_prio_pieces.size() == 6);
for (std::map<int, int>::iterator i = random_prio_pieces.begin()
, end(random_prio_pieces.end()); i != end; ++i)
std::cout << i->first << ": " << i->second << " ";
std::cout << std::endl;
// ======================================================== // ========================================================
// make sure the block that is picked is from piece 5, since it // make sure the block that is picked is from piece 5, since it
// has the highest priority among the available pieces // has the highest priority among the available pieces
print_title("test pick highest priority"); print_title("test pick highest priority");
p = setup_picker("1111111", "* * * ", "1111122", ""); p = setup_picker("1111111", "* * * ", "1111121", "");
picked.clear(); TEST_CHECK(test_pick(p) == 5);
p->pick_pieces(string2vec("****** "), picked, 1, false, 0, piece_picker::fast, true, false, empty_vector);
TEST_CHECK(verify_pick(p, picked)); // ========================================================
TEST_CHECK(int(picked.size()) > 0);
TEST_CHECK(picked.front().piece_index == 5); print_title("test reverse rarest first");
p = setup_picker("4179253", " ", "", "");
picked = pick_pieces(p, "*******", 7 * blocks_per_piece, 0, &peer_struct, piece_picker::fast
, piece_picker::rarest_first | piece_picker::reverse, empty_vector);
int expected_common_pieces[] = {3, 2, 5, 0, 6, 4, 1};
for (int i = 0; i < int(picked.size()); ++i)
TEST_CHECK(picked[i] == piece_block(expected_common_pieces[i / blocks_per_piece], i % blocks_per_piece));
// piece 3 should be prioritized since it's a partial
p = setup_picker("1122111", " ", "3333333", " 1 ");
TEST_CHECK(test_pick(p, piece_picker::rarest_first | piece_picker::reverse) == 3);
// ======================================================== // ========================================================
// make sure the 4 blocks are picked from the same piece if // make sure the 4 blocks are picked from the same piece if
// whole pieces are preferred. The only whole piece is 1. // whole pieces are preferred. Priority and availability is more
// important. Piece 1 has the lowest availability even though
// it is not a whole piece
print_title("test pick whole pieces"); print_title("test pick whole pieces");
p = setup_picker("1111111", " ", "1111111", "1023460"); p = setup_picker("2212222", " ", "1111111", "1023460");
picked.clear(); picked = pick_pieces(p, "****** ", 1, 1, &peer_struct, piece_picker::fast, options, empty_vector);
p->pick_pieces(string2vec("****** "), picked, 1, 1, &peer_struct, piece_picker::fast, true, true, empty_vector); TEST_CHECK(int(picked.size()) == 3);
TEST_CHECK(verify_pick(p, picked));
TEST_CHECK(int(picked.size()) >= blocks_per_piece);
for (int i = 0; i < blocks_per_piece && i < int(picked.size()); ++i) for (int i = 0; i < blocks_per_piece && i < int(picked.size()); ++i)
TEST_CHECK(picked[i].piece_index == 1); TEST_CHECK(picked[i].piece_index == 2);
p = setup_picker("1111111", " ", "1111111", "");
picked = pick_pieces(p, "****** ", 1, 1, &peer_struct, piece_picker::fast, options, empty_vector);
TEST_CHECK(int(picked.size()) == blocks_per_piece);
for (int i = 0; i < blocks_per_piece && i < int(picked.size()); ++i)
TEST_CHECK(picked[i].block_index == i);
p = setup_picker("2221222", " ", "", "");
picked = pick_pieces(p, "*******", 1, 7, &peer_struct, piece_picker::fast, options, empty_vector);
TEST_CHECK(int(picked.size()) == 7 * blocks_per_piece);
for (int i = 0; i < int(picked.size()); ++i)
TEST_CHECK(picked[i] == piece_block(i / blocks_per_piece, i % blocks_per_piece));
// ======================================================== // ========================================================
@ -259,22 +388,23 @@ int test_main()
// make sure filtered pieces are ignored // make sure filtered pieces are ignored
print_title("test filtered pieces"); print_title("test filtered pieces");
p = setup_picker("1111111", " ", "0010000", ""); p = setup_picker("1111111", " ", "0010000", "");
picked.clear(); TEST_CHECK(test_pick(p, piece_picker::rarest_first) == 2);
p->pick_pieces(string2vec("*** ** "), picked, 1, false, 0, piece_picker::fast, true, false, empty_vector); TEST_CHECK(test_pick(p, piece_picker::rarest_first | piece_picker::reverse) == 2);
TEST_CHECK(verify_pick(p, picked)); TEST_CHECK(test_pick(p, piece_picker::sequential) == 2);
TEST_CHECK(int(picked.size()) > 0); TEST_CHECK(test_pick(p, piece_picker::sequential | piece_picker::reverse) == 2);
TEST_CHECK(picked.front().piece_index == 2);
// ======================================================== // ========================================================
// make sure we_dont_have works // make sure we_dont_have works
print_title("test we_dont_have"); print_title("test we_dont_have");
p = setup_picker("1111111", "*******", "0100000", ""); p = setup_picker("1111111", "*******", "0100000", "");
picked.clear(); TEST_CHECK(p->have_piece(1));
TEST_CHECK(p->have_piece(2));
p->we_dont_have(1); p->we_dont_have(1);
p->we_dont_have(2); p->we_dont_have(2);
p->pick_pieces(string2vec("*** ** "), picked, 1, false, 0, piece_picker::fast, true, false, empty_vector); TEST_CHECK(!p->have_piece(1));
TEST_CHECK(verify_pick(p, picked)); TEST_CHECK(!p->have_piece(2));
picked = pick_pieces(p, "*** ** ", 1, 0, 0, piece_picker::fast, options, empty_vector);
TEST_CHECK(int(picked.size()) > 0); TEST_CHECK(int(picked.size()) > 0);
TEST_CHECK(picked.front().piece_index == 1); TEST_CHECK(picked.front().piece_index == 1);
@ -309,76 +439,122 @@ int test_main()
// make sure requested blocks aren't picked // make sure requested blocks aren't picked
print_title("test don't pick requested blocks"); print_title("test don't pick requested blocks");
p = setup_picker("1234567", " ", "", ""); p = setup_picker("1111111", " ", "", "");
picked.clear(); picked = pick_pieces(p, "*******", 1, 0, 0, piece_picker::fast, options, empty_vector);
p->pick_pieces(string2vec("*******"), picked, 1, false, 0, piece_picker::fast, true, false, empty_vector);
TEST_CHECK(verify_pick(p, picked));
TEST_CHECK(int(picked.size()) > 0); TEST_CHECK(int(picked.size()) > 0);
TEST_CHECK(picked.front().piece_index == 0);
piece_block first = picked.front(); piece_block first = picked.front();
p->mark_as_downloading(picked.front(), &peer_struct, piece_picker::fast); p->mark_as_downloading(picked.front(), &peer_struct, piece_picker::fast);
TEST_CHECK(p->num_peers(picked.front()) == 1); TEST_CHECK(p->num_peers(picked.front()) == 1);
picked.clear(); picked = pick_pieces(p, "*******", 1, 0, 0, piece_picker::fast, options, empty_vector);
p->pick_pieces(string2vec("*******"), picked, 1, false, 0, piece_picker::fast, true, false, empty_vector);
TEST_CHECK(verify_pick(p, picked));
TEST_CHECK(int(picked.size()) > 0); TEST_CHECK(int(picked.size()) > 0);
TEST_CHECK(picked.front() != first); TEST_CHECK(picked.front() != first);
TEST_CHECK(picked.front().piece_index == 0);
// ======================================================== // ========================================================
/*
// test sequenced download // make sure downloading pieces have higher priority
print_title("test downloading piece priority");
p = setup_picker("1111111", " ", "", "");
picked = pick_pieces(p, "*******", 1, 0, 0, piece_picker::fast, options, empty_vector);
TEST_CHECK(int(picked.size()) > 0);
first = picked.front();
p->mark_as_downloading(picked.front(), &peer_struct, piece_picker::fast);
TEST_CHECK(p->num_peers(picked.front()) == 1);
picked = pick_pieces(p, "*******", 1, 0, 0, piece_picker::fast, options, empty_vector);
TEST_CHECK(int(picked.size()) > 0);
TEST_CHECK(picked.front() != first);
TEST_CHECK(picked.front().piece_index == first.piece_index);
// ========================================================
// make sure downloading pieces closer to completion have higher priority
// piece 3 has only 1 block from being completed, and should be picked
print_title("test downloading piece order");
p = setup_picker("1111111", " ", "", "013700f");
picked = pick_pieces(p, "*******", 1, 0, 0, piece_picker::fast
, options | piece_picker::prioritize_partials, empty_vector);
TEST_CHECK(int(picked.size()) > 0);
TEST_CHECK(picked.front() == piece_block(3, 3));
// ========================================================
// test sequential download
print_title("test sequential download");
p = setup_picker("7654321", " ", "", ""); p = setup_picker("7654321", " ", "", "");
picked.clear(); picked = pick_pieces(p, "*******", 7 * blocks_per_piece, 0, 0, piece_picker::fast
p->set_sequenced_download_threshold(3); , piece_picker::sequential, empty_vector);
p->pick_pieces(string2vec("***** "), picked, 5 * blocks_per_piece, false, 0, piece_picker::fast, true, false, empty_vector); TEST_CHECK(int(picked.size()) == 7 * blocks_per_piece);
print_pick(picked); for (int i = 0; i < int(picked.size()); ++i)
TEST_CHECK(verify_pick(p, picked)); TEST_CHECK(picked[i] == piece_block(i / blocks_per_piece, i % blocks_per_piece));
TEST_CHECK(int(picked.size()) == 5 * blocks_per_piece);
for (int i = 0; i < 5 * blocks_per_piece && i < int(picked.size()); ++i)
TEST_CHECK(picked[i].piece_index == i / blocks_per_piece);
picked.clear(); // ========================================================
p->set_sequenced_download_threshold(4);
p->pick_pieces(string2vec("**** "), picked, 5 * blocks_per_piece, false, 0, piece_picker::fast, true, false, empty_vector);
print_pick(picked);
TEST_CHECK(verify_pick(p, picked));
TEST_CHECK(int(picked.size()) == 4 * blocks_per_piece);
for (int i = 0; i < 4 * blocks_per_piece && i < int(picked.size()); ++i)
TEST_CHECK(picked[i].piece_index == i / blocks_per_piece);
picked.clear(); // test reverse sequential download
p->set_sequenced_download_threshold(2); print_title("test reverse sequential download");
p->pick_pieces(string2vec("****** "), picked, 6 * blocks_per_piece, false, 0, piece_picker::fast, true, false, empty_vector); p = setup_picker("7654321", " ", "", "");
print_pick(picked); picked = pick_pieces(p, "*******", 7 * blocks_per_piece, 0, 0, piece_picker::fast
TEST_CHECK(verify_pick(p, picked)); , piece_picker::sequential | piece_picker::reverse, empty_vector);
TEST_CHECK(int(picked.size()) == 6 * blocks_per_piece); TEST_CHECK(int(picked.size()) == 7 * blocks_per_piece);
for (int i = 0; i < 6 * blocks_per_piece && i < int(picked.size()); ++i) for (int i = 0; i < int(picked.size()); ++i)
TEST_CHECK(picked[i].piece_index == i / blocks_per_piece); TEST_CHECK(picked[i] == piece_block(6 - (i / blocks_per_piece), i % blocks_per_piece));
picked.clear(); // ========================================================
// test cursors
print_title("test cursors");
p = setup_picker("7654321", " ", "", "");
TEST_CHECK(p->cursor() == 0);
TEST_CHECK(p->reverse_cursor() == 7);
p->we_have(1);
TEST_CHECK(p->cursor() == 0);
TEST_CHECK(p->reverse_cursor() == 7);
p->we_have(0);
TEST_CHECK(p->cursor() == 2);
TEST_CHECK(p->reverse_cursor() == 7);
p->we_have(5);
TEST_CHECK(p->cursor() == 2);
TEST_CHECK(p->reverse_cursor() == 7);
p->we_have(6);
TEST_CHECK(p->cursor() == 2);
TEST_CHECK(p->reverse_cursor() == 5);
p->we_have(4);
p->we_have(3);
p->we_have(2);
TEST_CHECK(p->cursor() == 7);
TEST_CHECK(p->reverse_cursor() == 0);
p->we_dont_have(3);
TEST_CHECK(p->cursor() == 3);
TEST_CHECK(p->reverse_cursor() == 4);
p = setup_picker("7654321", " ", "", "");
TEST_CHECK(p->cursor() == 0);
TEST_CHECK(p->reverse_cursor() == 7);
p->set_piece_priority(1, 0);
TEST_CHECK(p->cursor() == 0);
TEST_CHECK(p->reverse_cursor() == 7);
p->set_piece_priority(0, 0); p->set_piece_priority(0, 0);
p->pick_pieces(string2vec("****** "), picked, 6 * blocks_per_piece, false, 0, piece_picker::fast, true, false, empty_vector); TEST_CHECK(p->cursor() == 2);
print_pick(picked); TEST_CHECK(p->reverse_cursor() == 7);
TEST_CHECK(verify_pick(p, picked)); p->set_piece_priority(5, 0);
TEST_CHECK(int(picked.size()) == 5 * blocks_per_piece); TEST_CHECK(p->cursor() == 2);
for (int i = 0; i < 5 * blocks_per_piece && i < int(picked.size()); ++i) TEST_CHECK(p->reverse_cursor() == 7);
TEST_CHECK(picked[i].piece_index == i / blocks_per_piece + 1); p->set_piece_priority(6, 0);
TEST_CHECK(p->cursor() == 2);
TEST_CHECK(p->reverse_cursor() == 5);
p->set_piece_priority(4, 0);
p->set_piece_priority(3, 0);
p->set_piece_priority(2, 0);
TEST_CHECK(p->cursor() == 7);
TEST_CHECK(p->reverse_cursor() == 0);
p->set_piece_priority(3, 1);
TEST_CHECK(p->cursor() == 3);
TEST_CHECK(p->reverse_cursor() == 4);
picked.clear();
p->set_piece_priority(0, 1);
p->pick_pieces(string2vec("****** "), picked, 6 * blocks_per_piece, false, 0, piece_picker::fast, true, false, empty_vector);
print_pick(picked);
TEST_CHECK(verify_pick(p, picked));
TEST_CHECK(int(picked.size()) == 6 * blocks_per_piece);
for (int i = 0; i < 6 * blocks_per_piece && i < int(picked.size()); ++i)
TEST_CHECK(picked[i].piece_index == i / blocks_per_piece);
*/
// ======================================================== // ========================================================
// test piece priorities // test piece priorities
print_title("test piece priorities"); print_title("test piece priorities");
p = setup_picker("5555555", " ", "3214576", ""); p = setup_picker("5555555", " ", "7654321", "");
TEST_CHECK(p->num_filtered() == 0); TEST_CHECK(p->num_filtered() == 0);
TEST_CHECK(p->num_have_filtered() == 0); TEST_CHECK(p->num_have_filtered() == 0);
p->set_piece_priority(0, 0); p->set_piece_priority(0, 0);
@ -389,25 +565,34 @@ int test_main()
TEST_CHECK(p->num_filtered() == 0); TEST_CHECK(p->num_filtered() == 0);
TEST_CHECK(p->num_have_filtered() == 1); TEST_CHECK(p->num_have_filtered() == 1);
picked.clear(); p->we_dont_have(0);
p->pick_pieces(string2vec("*******"), picked, 6 * blocks_per_piece, false, 0, piece_picker::fast, true, false, empty_vector); p->set_piece_priority(0, 7);
print_pick(picked);
TEST_CHECK(verify_pick(p, picked)); picked = pick_pieces(p, "*******", 7 * blocks_per_piece, 0, 0
TEST_CHECK(int(picked.size()) == 6 * blocks_per_piece); , piece_picker::fast, options, empty_vector);
TEST_CHECK(picked[0 * blocks_per_piece].piece_index == 5); TEST_CHECK(int(picked.size()) == 7 * blocks_per_piece);
// priority 5 and 6 is currently the same
TEST_CHECK(picked[1 * blocks_per_piece].piece_index == 6 || picked[1 * blocks_per_piece].piece_index == 4); for (int i = 0; i < int(picked.size()); ++i)
TEST_CHECK(picked[2 * blocks_per_piece].piece_index == 6 || picked[2 * blocks_per_piece].piece_index == 4); TEST_CHECK(picked[i] == piece_block(i / blocks_per_piece, i % blocks_per_piece));
TEST_CHECK(picked[3 * blocks_per_piece].piece_index == 3);
TEST_CHECK(picked[4 * blocks_per_piece].piece_index == 1); // test changing priority on a piece we have
TEST_CHECK(picked[5 * blocks_per_piece].piece_index == 2); p->we_have(0);
p->set_piece_priority(0, 0);
p->set_piece_priority(0, 1);
p->set_piece_priority(0, 0);
std::vector<int> prios; std::vector<int> prios;
p->piece_priorities(prios); p->piece_priorities(prios);
TEST_CHECK(prios.size() == 7); TEST_CHECK(prios.size() == 7);
int prio_comp[] = {0, 2, 1, 4, 5, 7, 6}; int prio_comp[] = {0, 6, 5, 4, 3, 2, 1};
TEST_CHECK(std::equal(prios.begin(), prios.end(), prio_comp)); TEST_CHECK(std::equal(prios.begin(), prios.end(), prio_comp));
std::vector<bool> filter;
p->filtered_pieces(filter);
TEST_CHECK(prios.size() == 7);
bool filter_comp[] = {true, false, false, false, false, false, false};
TEST_CHECK(std::equal(filter.begin(), filter.end(), filter_comp));
// ======================================================== // ========================================================
// test restore_piece // test restore_piece
@ -418,18 +603,12 @@ int test_main()
p->mark_as_finished(piece_block(0,2), 0); p->mark_as_finished(piece_block(0,2), 0);
p->mark_as_finished(piece_block(0,3), 0); p->mark_as_finished(piece_block(0,3), 0);
picked.clear(); picked = pick_pieces(p, "*******", 1, 0, 0, piece_picker::fast, options, empty_vector);
p->pick_pieces(string2vec("*******"), picked, 1, false, 0, piece_picker::fast, true, false, empty_vector);
print_pick(picked);
TEST_CHECK(verify_pick(p, picked));
TEST_CHECK(int(picked.size()) >= 1); TEST_CHECK(int(picked.size()) >= 1);
TEST_CHECK(picked.front().piece_index == 1); TEST_CHECK(picked.front().piece_index == 1);
p->restore_piece(0); p->restore_piece(0);
picked.clear(); picked = pick_pieces(p, "*******", 1, 0, 0, piece_picker::fast, options, empty_vector);
p->pick_pieces(string2vec("*******"), picked, 1, false, 0, piece_picker::fast, true, false, empty_vector);
print_pick(picked);
TEST_CHECK(verify_pick(p, picked));
TEST_CHECK(int(picked.size()) >= 1); TEST_CHECK(int(picked.size()) >= 1);
TEST_CHECK(picked.front().piece_index == 0); TEST_CHECK(picked.front().piece_index == 0);
@ -439,46 +618,128 @@ int test_main()
p->mark_as_finished(piece_block(0,3), 0); p->mark_as_finished(piece_block(0,3), 0);
p->set_piece_priority(0, 0); p->set_piece_priority(0, 0);
picked.clear(); picked = pick_pieces(p, "*******", 1, 0, 0, piece_picker::fast, options, empty_vector);
p->pick_pieces(string2vec("*******"), picked, 1, false, 0, piece_picker::fast, true, false, empty_vector);
print_pick(picked);
TEST_CHECK(verify_pick(p, picked));
TEST_CHECK(int(picked.size()) >= 1); TEST_CHECK(int(picked.size()) >= 1);
TEST_CHECK(picked.front().piece_index == 1); TEST_CHECK(picked.front().piece_index == 1);
p->restore_piece(0); p->restore_piece(0);
picked.clear(); picked = pick_pieces(p, "*******", 1, 0, 0, piece_picker::fast, options, empty_vector);
p->pick_pieces(string2vec("*******"), picked, 1, false, 0, piece_picker::fast, true, false, empty_vector);
print_pick(picked);
TEST_CHECK(verify_pick(p, picked));
TEST_CHECK(int(picked.size()) >= 1); TEST_CHECK(int(picked.size()) >= 1);
TEST_CHECK(picked.front().piece_index == 1); TEST_CHECK(picked.front().piece_index == 1);
p->set_piece_priority(0, 1); p->set_piece_priority(0, 1);
picked.clear(); picked = pick_pieces(p, "*******", 1, 0, 0, piece_picker::fast, options, empty_vector);
p->pick_pieces(string2vec("*******"), picked, 1, false, 0, piece_picker::fast, true, false, empty_vector);
print_pick(picked);
TEST_CHECK(verify_pick(p, picked));
TEST_CHECK(int(picked.size()) >= 1); TEST_CHECK(int(picked.size()) >= 1);
TEST_CHECK(picked.front().piece_index == 0); TEST_CHECK(picked.front().piece_index == 0);
// ======================================================== // ========================================================
// test non-rarest-first mode // test random mode
print_title("test not rarest first"); print_title("test random pick");
p = setup_picker("1234567", "* * * ", "1111122", ""); p = setup_picker("1234567", " ", "1111122", "");
picked.clear(); std::set<int> random_pieces;
p->pick_pieces(string2vec("****** "), picked, 5 * blocks_per_piece, false, 0, piece_picker::fast, false, false, empty_vector); for (int i = 0; i < 100; ++i)
print_pick(picked); random_pieces.insert(test_pick(p, 0));
TEST_CHECK(verify_pick(p, picked)); TEST_CHECK(random_pieces.size() == 7);
TEST_CHECK(int(picked.size()) == 3 * blocks_per_piece);
for (int i = 0; i < 4 * blocks_per_piece && i < int(picked.size()); ++i) random_pieces.clear();
for (int i = 0; i < 7; ++i)
{ {
TEST_CHECK(picked[i].piece_index != 0); int piece = test_pick(p, 0);
TEST_CHECK(picked[i].piece_index != 2); p->we_have(piece);
TEST_CHECK(picked[i].piece_index != 4); random_pieces.insert(piece);
} }
TEST_CHECK(random_pieces.size() == 7);
// ========================================================
// make sure that blocks from a slow piece are picked
// by a fast peer if there are no other options
print_title("test downloading piece affinity");
p = setup_picker("1111111", " ", "", "");
p->mark_as_downloading(piece_block(2,2), &tmp1, piece_picker::slow);
picked = pick_pieces(p, "*******", 7 * blocks_per_piece - 1, 0, &tmp2
, piece_picker::fast, options, empty_vector);
TEST_CHECK(picked.size() == 7 * blocks_per_piece - 1);
TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(2,2)) == picked.end());
// piece 2 sould be the last one (least matching piece to pick)
TEST_CHECK(picked[7 * blocks_per_piece - 2].piece_index == 2);
TEST_CHECK(picked[7 * blocks_per_piece - 3].piece_index == 2);
TEST_CHECK(picked[7 * blocks_per_piece - 4].piece_index == 2);
// test the affinity of pieces with the same speed state
p = setup_picker("1111111", " ", "", "");
p->mark_as_downloading(piece_block(3,2), &tmp1, piece_picker::slow);
p->mark_as_downloading(piece_block(2,2), &tmp1, piece_picker::medium);
p->mark_as_downloading(piece_block(4,2), &tmp1, piece_picker::fast);
picked = pick_pieces(p, "*******", 2 * blocks_per_piece, 0, 0
, piece_picker::fast, piece_picker::prioritize_partials, empty_vector);
TEST_CHECK(picked.size() == 2 * blocks_per_piece);
TEST_CHECK(picked[0].piece_index == 4);
TEST_CHECK(picked[blocks_per_piece - 1].piece_index == 2);
TEST_CHECK(picked[2 * blocks_per_piece - 2].piece_index == 3);
// ========================================================
// make sure the piece picker will pick pieces that
// are already requested from other peers if it has to
print_title("test picking downloading blocks");
p = setup_picker("1111111", " ", "", "");
p->mark_as_downloading(piece_block(2,2), &tmp1, piece_picker::fast);
p->mark_as_downloading(piece_block(1,2), &tmp1, piece_picker::slow);
picked.clear();
p->pick_pieces(string2vec("*******"), picked, 7 * blocks_per_piece, 0, 0
, piece_picker::fast, piece_picker::prioritize_partials, empty_vector);
TEST_CHECK(verify_pick(p, picked, true));
print_pick(picked);
TEST_CHECK(picked.size() == 7 * blocks_per_piece);
picked.clear();
p->pick_pieces(string2vec("*******"), picked, 7 * blocks_per_piece, 0, 0
, piece_picker::fast, piece_picker::prioritize_partials
| piece_picker::rarest_first, empty_vector);
TEST_CHECK(verify_pick(p, picked, true));
print_pick(picked);
TEST_CHECK(picked.size() == 7 * blocks_per_piece);
picked.clear();
p->pick_pieces(string2vec("*******"), picked, 7 * blocks_per_piece, 0, 0
, piece_picker::fast, piece_picker::rarest_first, empty_vector);
TEST_CHECK(verify_pick(p, picked, true));
print_pick(picked);
TEST_CHECK(picked.size() == 7 * blocks_per_piece);
// ========================================================
// test clear_peer
print_title("test clear_peer");
p = setup_picker("1123333", " ", "", "");
p->mark_as_downloading(piece_block(0, 0), &tmp1, piece_picker::slow);
p->mark_as_downloading(piece_block(0, 1), &tmp2, piece_picker::slow);
p->mark_as_downloading(piece_block(0, 2), &tmp3, piece_picker::slow);
p->mark_as_downloading(piece_block(1, 1), &tmp1, piece_picker::slow);
p->mark_as_downloading(piece_block(2, 1), &tmp2, piece_picker::slow);
p->mark_as_downloading(piece_block(3, 1), &tmp3, piece_picker::slow);
std::vector<void*> dls;
void* expected_dls1[] = {&tmp1, &tmp2, &tmp3, 0};
void* expected_dls2[] = {0, &tmp1, 0, 0};
void* expected_dls3[] = {0, &tmp2, 0, 0};
void* expected_dls4[] = {0, &tmp3, 0, 0};
void* expected_dls5[] = {&tmp1, 0, &tmp3, 0};
p->get_downloaders(dls, 0);
TEST_CHECK(std::equal(dls.begin(), dls.end(), expected_dls1));
p->get_downloaders(dls, 1);
TEST_CHECK(std::equal(dls.begin(), dls.end(), expected_dls2));
p->get_downloaders(dls, 2);
TEST_CHECK(std::equal(dls.begin(), dls.end(), expected_dls3));
p->get_downloaders(dls, 3);
TEST_CHECK(std::equal(dls.begin(), dls.end(), expected_dls4));
p->clear_peer(&tmp2);
p->get_downloaders(dls, 0);
TEST_CHECK(std::equal(dls.begin(), dls.end(), expected_dls5));
// ======================================================== // ========================================================
@ -505,7 +766,7 @@ int test_main()
// ======================================================== // ========================================================
// test have_all and have_none with sequential download // test have_all and have_none
print_title("test have_all and have_none with sequential download"); print_title("test have_all and have_none with sequential download");
p = setup_picker("0123333", "* ", "", ""); p = setup_picker("0123333", "* ", "", "");
dc = p->distributed_copies(); dc = p->distributed_copies();
@ -515,17 +776,6 @@ int test_main()
dc = p->distributed_copies(); dc = p->distributed_copies();
std::cout << "distributed copies: " << dc << std::endl; std::cout << "distributed copies: " << dc << std::endl;
TEST_CHECK(fabs(dc - (2.f + 5.f / 7.f)) < 0.01f); TEST_CHECK(fabs(dc - (2.f + 5.f / 7.f)) < 0.01f);
p->sequential_download(true);
p->dec_refcount_all();
dc = p->distributed_copies();
std::cout << "distributed copies: " << dc << std::endl;
TEST_CHECK(fabs(dc - (1.f + 5.f / 7.f)) < 0.01f);
p->inc_refcount(0);
p->dec_refcount_all();
dc = p->distributed_copies();
std::cout << "distributed copies: " << dc << std::endl;
TEST_CHECK(fabs(dc - (0.f + 6.f / 7.f)) < 0.01f);
p->inc_refcount(1);
TEST_CHECK(test_pick(p) == 1); TEST_CHECK(test_pick(p) == 1);
// ======================================================== // ========================================================
@ -546,40 +796,15 @@ int test_main()
p->dec_refcount(5); p->dec_refcount(5);
p->inc_refcount(5); p->inc_refcount(5);
p->inc_refcount(0); bitfield bits(7);
p->dec_refcount(4); bits.clear_all();
bits.set_bit(0);
p->inc_refcount(bits);
bits.clear_all();
bits.set_bit(4);
p->dec_refcount(bits);
TEST_CHECK(test_pick(p) == 0); TEST_CHECK(test_pick(p) == 0);
// ========================================================
/*
// test have_all and have_none, with a sequenced download threshold
p = setup_picker("1233333", "* ", "", "");
p->set_sequenced_download_threshold(3);
p->inc_refcount_all();
dc = p->distributed_copies();
TEST_CHECK(fabs(dc - (3.f + 5.f / 7.f)) < 0.01f);
p->dec_refcount_all();
dc = p->distributed_copies();
TEST_CHECK(fabs(dc - (2.f + 5.f / 7.f)) < 0.01f);
p->dec_refcount(2);
dc = p->distributed_copies();
TEST_CHECK(fabs(dc - (2.f + 4.f / 7.f)) < 0.01f);
p->mark_as_downloading(piece_block(1,0), &peer_struct, piece_picker::fast);
p->mark_as_downloading(piece_block(1,1), &peer_struct, piece_picker::fast);
p->we_have(1);
dc = p->distributed_copies();
TEST_CHECK(fabs(dc - (2.f + 5.f / 7.f)) < 0.01f);
picked.clear();
// make sure it won't pick the piece we just got
p->pick_pieces(string2vec(" * ****"), picked, 100, false, 0, piece_picker::fast, true, false, empty_vector);
print_pick(picked);
TEST_CHECK(verify_pick(p, picked));
TEST_CHECK(int(picked.size()) >= 4 * blocks_per_piece);
TEST_CHECK(picked[0 * blocks_per_piece].piece_index == 3);
TEST_CHECK(picked[1 * blocks_per_piece].piece_index == 4);
TEST_CHECK(picked[2 * blocks_per_piece].piece_index == 5);
TEST_CHECK(picked[3 * blocks_per_piece].piece_index == 6);
*/
// ======================================================== // ========================================================
// test unverified_blocks, marking blocks and get_downloader // test unverified_blocks, marking blocks and get_downloader
@ -592,7 +817,6 @@ int test_main()
TEST_CHECK(p->get_downloader(piece_block(4, 3)) == 0); TEST_CHECK(p->get_downloader(piece_block(4, 3)) == 0);
p->mark_as_downloading(piece_block(4, 3), &peer_struct, piece_picker::fast); p->mark_as_downloading(piece_block(4, 3), &peer_struct, piece_picker::fast);
TEST_CHECK(p->get_downloader(piece_block(4, 3)) == &peer_struct); TEST_CHECK(p->get_downloader(piece_block(4, 3)) == &peer_struct);
piece_picker::downloading_piece st;
p->piece_info(4, st); p->piece_info(4, st);
TEST_CHECK(st.requested == 1); TEST_CHECK(st.requested == 1);
TEST_CHECK(st.writing == 0); TEST_CHECK(st.writing == 0);
@ -625,10 +849,7 @@ int test_main()
// test prefer_whole_pieces // test prefer_whole_pieces
print_title("test prefer whole pieces"); print_title("test prefer whole pieces");
p = setup_picker("1111111", " ", "", ""); p = setup_picker("1111111", " ", "", "");
picked.clear(); picked = pick_pieces(p, "*******", 1, 3, 0, piece_picker::fast, options, empty_vector);
p->pick_pieces(string2vec("*******"), picked, 1, 3, 0, piece_picker::fast, true, false, empty_vector);
print_pick(picked);
TEST_CHECK(verify_pick(p, picked));
TEST_CHECK(int(picked.size()) >= 3 * blocks_per_piece); TEST_CHECK(int(picked.size()) >= 3 * blocks_per_piece);
piece_block b = picked.front(); piece_block b = picked.front();
for (int i = 1; i < int(picked.size()); ++i) for (int i = 1; i < int(picked.size()); ++i)
@ -638,10 +859,7 @@ int test_main()
b = picked[i]; b = picked[i];
} }
picked.clear(); picked = pick_pieces(p, "*******", 1, 3, 0, piece_picker::fast, options, empty_vector);
p->pick_pieces(string2vec("*******"), picked, 1, 3, 0, piece_picker::fast, false, false, empty_vector);
print_pick(picked);
TEST_CHECK(verify_pick(p, picked));
TEST_CHECK(int(picked.size()) >= 3 * blocks_per_piece); TEST_CHECK(int(picked.size()) >= 3 * blocks_per_piece);
b = picked.front(); b = picked.front();
for (int i = 1; i < int(picked.size()); ++i) for (int i = 1; i < int(picked.size()); ++i)
@ -651,34 +869,36 @@ int test_main()
b = picked[i]; b = picked[i];
} }
// make sure pieces that don't match the 'whole pieces' requirement
// are picked if there's no other choice
p = setup_picker("1111111", " ", "", "");
p->mark_as_downloading(piece_block(2,2), &tmp1, piece_picker::fast);
picked = pick_pieces(p, "*******", 7 * blocks_per_piece - 1, 1, 0
, piece_picker::fast, options, empty_vector);
TEST_CHECK(picked.size() == 7 * blocks_per_piece - 1);
TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(2,2)) == picked.end());
//#error test picking with partial pieces and other peers present so that both backup_pieces and backup_pieces2 are used
// ======================================================== // ========================================================
// test parole mode // test parole mode
print_title("test parole mode"); print_title("test parole mode");
p = setup_picker("3333133", " ", "", ""); p = setup_picker("3333133", " ", "", "");
p->mark_as_finished(piece_block(0, 0), 0); p->mark_as_finished(piece_block(0, 0), 0);
picked.clear(); picked = pick_pieces(p, "*******", 1, 1, 0, piece_picker::fast
p->pick_pieces(string2vec("*******"), picked, 1, 1, 0, piece_picker::fast, true, true, empty_vector); , options | piece_picker::on_parole | piece_picker::prioritize_partials, empty_vector);
print_pick(picked); TEST_CHECK(int(picked.size()) == blocks_per_piece - 1);
TEST_CHECK(verify_pick(p, picked));
TEST_CHECK(int(picked.size()) >= blocks_per_piece - 1);
for (int i = 1; i < int(picked.size()); ++i) for (int i = 1; i < int(picked.size()); ++i)
{ TEST_CHECK(picked[i] == piece_block(0, i + 1));
TEST_CHECK(picked[i].piece_index == 0);
TEST_CHECK(picked[i].block_index == i + 1);
}
// make sure that the partial piece is not picked by a // make sure that the partial piece is not picked by a
// peer that is has not downloaded/requested the other blocks // peer that is has not downloaded/requested the other blocks
picked.clear(); picked = pick_pieces(p, "*******", 1, 1, &peer_struct, piece_picker::fast
p->pick_pieces(string2vec("*******"), picked, 1, 1, &peer_struct, piece_picker::fast, true, true, empty_vector); , options | piece_picker::on_parole | piece_picker::prioritize_partials, empty_vector);
print_pick(picked); TEST_CHECK(int(picked.size()) == blocks_per_piece);
TEST_CHECK(int(picked.size()) >= blocks_per_piece);
for (int i = 1; i < int(picked.size()); ++i) for (int i = 1; i < int(picked.size()); ++i)
{ TEST_CHECK(picked[i] == piece_block(4, i));
TEST_CHECK(picked[i].piece_index == 4);
TEST_CHECK(picked[i].block_index == i);
}
// ======================================================== // ========================================================
@ -688,53 +908,29 @@ int test_main()
int v[] = {1, 5}; int v[] = {1, 5};
std::vector<int> suggested_pieces(v, v + 2); std::vector<int> suggested_pieces(v, v + 2);
picked.clear(); picked = pick_pieces(p, "****************", 1, 1, 0, piece_picker::fast, options, suggested_pieces);
p->pick_pieces(string2vec("****************"), picked, 1, 1, 0, piece_picker::fast, true, true, suggested_pieces);
print_pick(picked);
TEST_CHECK(verify_pick(p, picked));
TEST_CHECK(int(picked.size()) >= blocks_per_piece); TEST_CHECK(int(picked.size()) >= blocks_per_piece);
for (int i = 1; i < int(picked.size()); ++i) for (int i = 1; i < int(picked.size()); ++i)
{ TEST_CHECK(picked[i] == piece_block(1, i));
TEST_CHECK(picked[i].piece_index == 1);
TEST_CHECK(picked[i].block_index == i);
}
p->set_piece_priority(0, 0); p->set_piece_priority(0, 0);
p->set_piece_priority(1, 0); p->set_piece_priority(1, 0);
p->set_piece_priority(2, 0); p->set_piece_priority(2, 0);
p->set_piece_priority(3, 0); p->set_piece_priority(3, 0);
picked.clear(); picked = pick_pieces(p, "****************", 1, 1, 0, piece_picker::fast, options, suggested_pieces);
p->pick_pieces(string2vec("****************"), picked, 1, 1, 0, piece_picker::fast, true, true, suggested_pieces);
print_pick(picked);
TEST_CHECK(verify_pick(p, picked));
TEST_CHECK(int(picked.size()) >= blocks_per_piece); TEST_CHECK(int(picked.size()) >= blocks_per_piece);
for (int i = 1; i < int(picked.size()); ++i) for (int i = 1; i < int(picked.size()); ++i)
{ TEST_CHECK(picked[i] == piece_block(5, i));
TEST_CHECK(picked[i].piece_index == 5);
TEST_CHECK(picked[i].block_index == i);
}
p = setup_picker("1111222233334444", "**** ", "", ""); p = setup_picker("1111222233334444", "**** ", "", "");
picked.clear(); picked = pick_pieces(p, "****************", 1, 1, 0, piece_picker::fast, options, suggested_pieces);
p->pick_pieces(string2vec("****************"), picked, 1, 1, 0, piece_picker::fast, true, true, suggested_pieces);
print_pick(picked);
TEST_CHECK(verify_pick(p, picked));
TEST_CHECK(int(picked.size()) >= blocks_per_piece); TEST_CHECK(int(picked.size()) >= blocks_per_piece);
for (int i = 1; i < int(picked.size()); ++i) for (int i = 1; i < int(picked.size()); ++i)
{ TEST_CHECK(picked[i] == piece_block(5, i));
TEST_CHECK(picked[i].piece_index == 5);
TEST_CHECK(picked[i].block_index == i);
}
// MISSING TESTS: // MISSING TESTS:
// 2. inc_ref() from 0 to 1 while sequenced download threshold is 1 // 1. abort_download
// 4. filtered_pieces // 2. write_failed
// 5. clear peer
// 6. pick_pieces with prefer whole pieces
// 7. is_requested
// 8. is_downloaded
// 9. get_downloaders
// 10. abort_download
/* /*