From 193417143193d4b95f546841787d74986a7f7a83 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Sun, 14 Aug 2005 22:04:58 +0000 Subject: [PATCH] bugfixes, fixes for boost 1.33, made the piece picker pick whole pieces from fast peers --- docs/manual.html | 4 +- docs/manual.rst | 4 +- examples/Jamfile | 6 +- examples/client_test.cpp | 89 ++++++++++++---- include/libtorrent/debug.hpp | 2 +- include/libtorrent/piece_picker.hpp | 27 +++-- src/http_tracker_connection.cpp | 2 +- src/piece_picker.cpp | 151 ++++++++++++++++++++++------ src/policy.cpp | 35 +++++-- src/session.cpp | 9 -- src/storage.cpp | 4 +- src/torrent.cpp | 12 ++- src/torrent_handle.cpp | 4 +- test/Jamfile | 2 +- test/test_piece_picker.cpp | 87 ++++++++++++++-- 15 files changed, 338 insertions(+), 100 deletions(-) diff --git a/docs/manual.html b/docs/manual.html index f0c4e5c22..1efd76ff1 100755 --- a/docs/manual.html +++ b/docs/manual.html @@ -1255,7 +1255,7 @@ struct partial_piece_info int blocks_in_piece; std::bitset<max_blocks_per_piece> requested_blocks; std::bitset<max_blocks_per_piece> finished_blocks; - peer_id peer[max_blocks_per_piece]; + address peer[max_blocks_per_piece]; int num_downloads[max_blocks_per_piece]; }; @@ -1266,7 +1266,7 @@ the last piece may have fewer blocks than the standard pieces.

means that that block has been requested, but not necessarily fully downloaded yet. To know from whom the block has been requested, have a look in the peer array. The bit-index in the requested_blocks and finished_blocks correspons to the array-index into -peers and num_downloads. The array of peers is contains the id of the +peers and num_downloads. The array of peers is contains the address of the peer the piece was requested from. If a piece hasn't been requested (the bit in requested_blocks is not set) the peer array entry will be undefined.

The finished_blocks is a bitset where each bit says if the block is fully downloaded diff --git a/docs/manual.rst b/docs/manual.rst index 61abd35d8..64b4c810c 100755 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -1192,7 +1192,7 @@ requested. The entry in the vector (``partial_piece_info``) looks like this:: int blocks_in_piece; std::bitset requested_blocks; std::bitset finished_blocks; - peer_id peer[max_blocks_per_piece]; + address peer[max_blocks_per_piece]; int num_downloads[max_blocks_per_piece]; }; @@ -1204,7 +1204,7 @@ the last piece may have fewer blocks than the standard pieces. means that that block has been requested, but not necessarily fully downloaded yet. To know from whom the block has been requested, have a look in the ``peer`` array. The bit-index in the ``requested_blocks`` and ``finished_blocks`` correspons to the array-index into -``peers`` and ``num_downloads``. The array of peers is contains the id of the +``peers`` and ``num_downloads``. The array of peers is contains the address of the peer the piece was requested from. If a piece hasn't been requested (the bit in ``requested_blocks`` is not set) the peer array entry will be undefined. diff --git a/examples/Jamfile b/examples/Jamfile index f13e74f27..34d6aef88 100755 --- a/examples/Jamfile +++ b/examples/Jamfile @@ -1,13 +1,13 @@ use-project /torrent : .. ; -project +project client_test : requirements multi /torrent ; -exe client_test : client_test.cpp ; +exe client_test : client_test.cpp ; exe simple_client : simple_client.cpp ; exe dump_torrent : dump_torrent.cpp ; exe make_torrent : make_torrent.cpp ; -stage . : dump_torrent make_torrent simple_client client_test ; +#stage . : dump_torrent make_torrent simple_client client_test ; diff --git a/examples/client_test.cpp b/examples/client_test.cpp index 637ece6c5..8bf9424f3 100755 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -55,6 +55,8 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/identify_client.hpp" #include "libtorrent/alert_types.hpp" +using boost::bind; + #ifdef _WIN32 #if defined(_MSC_VER) @@ -100,6 +102,8 @@ void clear() #include #include +#define ANSI_TERMINAL_COLORS + struct set_keypress { set_keypress() @@ -147,6 +151,20 @@ void clear() #endif +std::string esc(char const* code) +{ +#ifdef ANSI_TERMINAL_COLORS + std::string ret; + ret += char(0x1b); + ret += "["; + ret += code; + ret += "m"; + return ret; +#else + return std::string(); +#endif +} + std::string to_string(float v, int width, int precision = 3) { std::stringstream s; @@ -203,17 +221,32 @@ std::string add_suffix(float val) return to_string(val, 6) + "PB"; } -std::string progress_bar(float progress, int width) +std::string progress_bar(float progress, int width, char const* code = "33") { - std::vector bar; + std::string bar; bar.reserve(width); int progress_chars = static_cast(progress * width + .5f); + bar = esc(code); std::fill_n(std::back_inserter(bar), progress_chars, '#'); + bar += esc("0"); std::fill_n(std::back_inserter(bar), width - progress_chars, '-'); return std::string(bar.begin(), bar.end()); } +char const* peer_index(libtorrent::address addr, std::vector const& peers) +{ + using namespace libtorrent; + std::vector::const_iterator i = std::find_if(peers.begin() + , peers.end(), bind(std::equal_to

(), bind(&peer_info::ip, _1), addr)); + if (i == peers.end()) return "+"; + + static char str[] = " "; + int index = i - peers.begin(); + str[0] = (index < 10)?'0' + index:'A' + index - 10; + return str; +} + void print_peer_info(std::ostream& out, std::vector const& peers) { using namespace libtorrent; @@ -225,9 +258,9 @@ void print_peer_info(std::ostream& out, std::vector const { out.fill(' '); out.width(2); - out << add_suffix(i->down_speed) << "/s " + out << esc("32") << add_suffix(i->down_speed) << "/s " << esc("0") // << "(" << add_suffix(i->total_download) << ") " - << add_suffix(i->up_speed) << "/s " + << esc("31") << add_suffix(i->up_speed) << "/s " << esc("0") // << "(" << add_suffix(i->total_upload) << ") " // << "ul:" << add_suffix(i->upload_limit) << "/s " // << "uc:" << add_suffix(i->upload_ceiling) << "/s " @@ -486,6 +519,8 @@ int main(int argc, char* argv[]) a = ses.pop_alert(); } + session_status sess_stat = ses.status(); + std::stringstream out; for (std::vector::iterator i = handles.begin(); i != handles.end(); ++i) @@ -496,10 +531,10 @@ int main(int argc, char* argv[]) --i; continue; } - out << "name: "; + out << "name: " << esc("37"); if (i->has_metadata()) out << i->get_torrent_info().name(); else out << "-"; - out << "\n"; + out << esc("0") << "\n"; torrent_status s = i->status(); if (s.state != torrent_status::seeding) @@ -534,28 +569,42 @@ int main(int argc, char* argv[]) if (s.state != torrent_status::seeding) { + char const* progress_bar_color = "33"; // yellow + if (s.state == torrent_status::checking_files + || s.state == torrent_status::downloading_metadata) + { + progress_bar_color = "35"; // magenta + } + else if (s.current_tracker.empty()) + { + progress_bar_color = "31"; // red + } + else if (sess_stat.has_incoming_connections) + { + progress_bar_color = "32"; // green + } out.precision(4); out.width(5); out.fill(' '); out << (s.progress*100) << "% "; - out << progress_bar(s.progress, 49); + out << progress_bar(s.progress, 49, progress_bar_color); out << "\n"; - out << "total downloaded: " << s.total_done << " Bytes\n"; + out << "total downloaded: " << esc("32") << s.total_done << esc("0") << " Bytes\n"; out << "peers: " << s.num_peers << " " << "seeds: " << s.num_seeds << " " << "distributed copies: " << s.distributed_copies << "\n"; } - out << "download: " << add_suffix(s.download_rate) << "/s " - << "(" << add_suffix(s.total_download) << ") " - << "upload: " << add_suffix(s.upload_rate) << "/s " - << "(" << add_suffix(s.total_upload) << ") " + out << "download: " << esc("32") << add_suffix(s.download_rate) << "/s " << esc("0") + << "(" << esc("32") << add_suffix(s.total_download) << esc("0") << ") " + << "upload: " << esc("31") << add_suffix(s.upload_rate) << "/s " << esc("0") + << "(" << esc("31") << add_suffix(s.total_upload) << esc("0") << ") " << "ratio: " << ratio(s.total_payload_download, s.total_payload_upload) << "\n"; if (s.state != torrent_status::seeding) { out << "info-hash: " << i->info_hash() << "\n"; boost::posix_time::time_duration t = s.next_announce; - out << "next announce: " << boost::posix_time::to_simple_string(t) << "\n"; + out << "next announce: " << esc("37") << boost::posix_time::to_simple_string(t) << esc("0") << "\n"; out << "tracker: " << s.current_tracker << "\n"; } @@ -578,9 +627,16 @@ int main(int argc, char* argv[]) out << i->piece_index << ": ["; for (int j = 0; j < i->blocks_in_piece; ++j) { - if (i->finished_blocks[j]) out << "#"; - else if (i->requested_blocks[j]) out << "+"; + char const* peer_str = peer_index(i->peer[j], peers); +#ifdef ANSI_TERMINAL_COLORS + if (i->finished_blocks[j]) out << esc("32;7") << peer_str << esc("0"); + else if (i->requested_blocks[j]) out << peer_str; else out << "-"; +#else + if (i->finished_blocks[j]) out << "#"; + else if (i->requested_blocks[j]) out << peer_str; + else out << "-"; +#endif } out << "]\n"; } @@ -592,8 +648,7 @@ int main(int argc, char* argv[]) if (print_log) { for (std::deque::iterator i = events.begin(); - i != events.end(); - ++i) + i != events.end(); ++i) { out << *i << "\n"; } diff --git a/include/libtorrent/debug.hpp b/include/libtorrent/debug.hpp index ad44601a7..0f303405c 100755 --- a/include/libtorrent/debug.hpp +++ b/include/libtorrent/debug.hpp @@ -91,7 +91,7 @@ namespace libtorrent struct null_logger: libtorrent::logger { public: - virtual void log(const char* text) {} + virtual void log(const char*) {} }; struct cout_logger: libtorrent::logger diff --git a/include/libtorrent/piece_picker.hpp b/include/libtorrent/piece_picker.hpp index 14b80c756..c75fe0cbd 100755 --- a/include/libtorrent/piece_picker.hpp +++ b/include/libtorrent/piece_picker.hpp @@ -160,13 +160,16 @@ namespace libtorrent // decides to download a piece, it must mark it as being downloaded // itself, by using the mark_as_downloading() member function. // THIS IS DONE BY THE peer_connection::send_request() MEMBER FUNCTION! - void pick_pieces(const std::vector& pieces, - std::vector& interesting_blocks, - int num_pieces) const; + // The last argument is the address of the peer that we'll download + // from. + void pick_pieces(const std::vector& pieces + , std::vector& interesting_blocks + , int num_pieces, bool prefer_whole_pieces + , address peer) const; // returns true if any client is currently downloading this // piece-block, or if it's queued for downloading by some client - // or if it already has been successfully downlloaded + // or if it already has been successfully downloaded bool is_downloading(piece_block block) const; bool is_finished(piece_block block) const; @@ -265,10 +268,18 @@ namespace libtorrent std::vector > const& pick_piece_info_vector( bool downloading, bool filtered) const; - int add_interesting_blocks(const std::vector& piece_list, - const std::vector& pieces, - std::vector& interesting_pieces, - int num_blocks) const; + int add_interesting_blocks_free(const std::vector& piece_list + , const std::vector& pieces + , std::vector& interesting_blocks + , int num_blocks, bool prefer_whole_pieces) const; + + int add_interesting_blocks_partial(const std::vector& piece_list + , const std::vector& pieces + , std::vector& interesting_blocks + , std::vector& backup_blocks + , int num_blocks, bool prefer_whole_pieces + , address peer) const; + // this vector contains all pieces we don't have. // in the first entry (index 0) is a vector of all pieces diff --git a/src/http_tracker_connection.cpp b/src/http_tracker_connection.cpp index 2c3a50ebb..5d69c537e 100755 --- a/src/http_tracker_connection.cpp +++ b/src/http_tracker_connection.cpp @@ -408,7 +408,7 @@ namespace libtorrent try { m_content_length = boost::lexical_cast( - line.substr(16)); + line.substr(16, line.length() - 17)); } catch(boost::bad_lexical_cast&) { diff --git a/src/piece_picker.cpp b/src/piece_picker.cpp index 802e1af31..073cff78c 100755 --- a/src/piece_picker.cpp +++ b/src/piece_picker.cpp @@ -63,7 +63,7 @@ namespace libtorrent , m_num_have_filtered(0) { assert(blocks_per_piece > 0); - assert(total_num_blocks > 0); + assert(total_num_blocks >= 0); // the piece index is stored in 20 bits, which limits the allowed // number of pieces somewhat @@ -271,7 +271,7 @@ namespace libtorrent for (int i = 0; i < (int)m_piece_info.size(); ++i) { int p = (int)m_piece_info[i].size(); - assert(float(p) / num_pieces <= 1.f); + assert(num_pieces == 0 || float(p) / num_pieces <= 1.f); if (p > 0) { float fraction_above_count = @@ -560,8 +560,9 @@ namespace libtorrent } void piece_picker::pick_pieces(const std::vector& pieces - , std::vector& interesting_pieces - , int num_blocks) const + , std::vector& interesting_blocks + , int num_blocks, bool prefer_whole_pieces + , address peer) const { assert(num_blocks > 0); assert(pieces.size() == m_piece_map.size()); @@ -582,14 +583,33 @@ namespace libtorrent assert(m_downloading_piece_info.begin() != m_downloading_piece_info.end()); std::vector >::const_iterator partial = m_downloading_piece_info.begin()+1; - while((free != m_piece_info.end()) || (partial != m_downloading_piece_info.end())) + std::vector backup_blocks; + + // this loop will loop from pieces with 1 peer and up + // until we either reach the end of the piece list or + // has filled the interesting_blocks with num_blocks + // blocks. + + // it iterates over two ranges simultaneously. The pieces that are partially downloaded + // or partially requested, and the pieces that hasn't been requested at all. + // The default is to prioritize pieces that are partially requested/downloaded, so the + // loop will first look for blocks among those pieces. And it will also take two steps + // in that range when iterating. This has the effect that partial pieces doesn't have to + // be as rare as non-requested pieces in order to be prefered. + + // When prefer_whole_pieces is set (usually set when downloading from fast peers) the + // partial pieces will not be prioritized, but actually ignored as long as possible. + + while((free != m_piece_info.end()) + || (partial != m_downloading_piece_info.end())) { if (partial != m_downloading_piece_info.end()) { for (int i = 0; i < 2; ++i) { - num_blocks = add_interesting_blocks(*partial, pieces - , interesting_pieces, num_blocks); + num_blocks = add_interesting_blocks_partial(*partial, pieces + , interesting_blocks, backup_blocks, num_blocks + , prefer_whole_pieces, peer); assert(num_blocks >= 0); if (num_blocks == 0) return; ++partial; @@ -599,18 +619,77 @@ namespace libtorrent if (free != m_piece_info.end()) { - num_blocks = add_interesting_blocks(*free, pieces, interesting_pieces, num_blocks); + num_blocks = add_interesting_blocks_free(*free, pieces + , interesting_blocks, num_blocks, prefer_whole_pieces); assert(num_blocks >= 0); if (num_blocks == 0) return; ++free; } } + + if (!prefer_whole_pieces) return; + assert(num_blocks > 0); + + interesting_blocks.insert(interesting_blocks.end() + , backup_blocks.begin(), backup_blocks.begin() + + std::min(num_blocks, (int)backup_blocks.size())); } - int piece_picker::add_interesting_blocks(const std::vector& piece_list + namespace + { + bool exclusively_requested_from(piece_picker::downloading_piece const& p + , int num_blocks_in_piece, address peer) + { + for (int j = 0; j < num_blocks_in_piece; ++j) + { + if ((p.finished_blocks[j] == 1 + || p.requested_blocks[j] == 1) + && p.info[j].peer != peer + && p.info[j].peer != address()) + { + return false; + } + } + return true; + } + } + + int piece_picker::add_interesting_blocks_free(std::vector const& piece_list + , std::vector const& pieces + , std::vector& interesting_blocks + , int num_blocks, bool prefer_whole_pieces) const + { + for (std::vector::const_iterator i = piece_list.begin(); + i != piece_list.end(); ++i) + { + assert(*i >= 0); + assert(*i < (int)m_piece_map.size()); + assert(m_piece_map[*i].downloading == 0); + + // if the peer doesn't have the piece + // skip it + if (!pieces[*i]) continue; + + int piece_blocks = blocks_in_piece(*i); + if (!prefer_whole_pieces && piece_blocks > num_blocks) + piece_blocks = num_blocks; + for (int j = 0; j < piece_blocks; ++j) + { + interesting_blocks.push_back(piece_block(*i, j)); + } + num_blocks -= std::min(piece_blocks, num_blocks); + assert(num_blocks >= 0); + if (num_blocks == 0) return num_blocks; + } + return num_blocks; + } + + int piece_picker::add_interesting_blocks_partial(std::vector const& piece_list , const std::vector& pieces , std::vector& interesting_blocks - , int num_blocks) const + , std::vector& backup_blocks + , int num_blocks, bool prefer_whole_pieces + , address peer) const { assert(num_blocks > 0); @@ -623,24 +702,9 @@ namespace libtorrent // skip it if (!pieces[*i]) continue; - // if there's at least one block that - // we can request from this peer - // we can break our search (return) + assert(m_piece_map[*i].downloading == 1); - if (m_piece_map[*i].downloading == 0) - { - int piece_blocks = std::min(blocks_in_piece(*i), num_blocks); - for (int j = 0; j < piece_blocks; ++j) - { - interesting_blocks.push_back(piece_block(*i, j)); - } - num_blocks -= piece_blocks; - assert(num_blocks >= 0); - if (num_blocks == 0) return num_blocks; - continue; - } - - // calculate the number of blocks in this + // calculate the number of blocks in this // piece. It's always m_blocks_per_piece, except // in the last piece. int num_blocks_in_piece = blocks_in_piece(*i); @@ -648,11 +712,33 @@ namespace libtorrent std::vector::const_iterator p = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(*i)); assert(p != m_downloads.end()); - + + // this means that this partial piece has + // been downloaded/requested partially from + // another peer that isn't us. And since + // we prefer whole pieces, add this piece's + // blocks to the backup list. If the prioritized + // blocks aren't enough, blocks from this list + // will be picked. + if (prefer_whole_pieces + && !exclusively_requested_from(*p, num_blocks_in_piece, peer)) + { + if ((int)backup_blocks.size() >= num_blocks) continue; + for (int j = 0; j < num_blocks_in_piece; ++j) + { + if (p->finished_blocks[j] == 1) continue; + if (p->requested_blocks[j] == 1 + && p->info[j].peer == peer) continue; + backup_blocks.push_back(piece_block(*i, j)); + } + continue; + } + for (int j = 0; j < num_blocks_in_piece; ++j) { if (p->finished_blocks[j] == 1) continue; - + if (p->requested_blocks[j] == 1 + && p->info[j].peer == peer) continue; // this block is interesting (we don't have it // yet). But it may already have been requested // from another peer. We have to add it anyway @@ -665,14 +751,17 @@ namespace libtorrent interesting_blocks.push_back(piece_block(*i, j)); if (p->requested_blocks[j] == 0) { - // we have found a piece that's free to download + // we have found a block that's free to download num_blocks--; + if (prefer_whole_pieces) continue; assert(num_blocks >= 0); if (num_blocks == 0) return num_blocks; } } + assert(num_blocks >= 0 || prefer_whole_pieces); + if (num_blocks < 0) num_blocks = 0; + if (num_blocks == 0) return num_blocks; } - assert(num_blocks >= 0); return num_blocks; } diff --git a/src/policy.cpp b/src/policy.cpp index f62653c2d..02717f655 100755 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -78,7 +78,7 @@ namespace // the case where ignore_peer is motivated is if two peers // have only one piece that we don't have, and it's the // same piece for both peers. Then they might get into an - // infinite recursion, fighting to request the same blocks. + // infinite loop, fighting to request the same blocks. void request_a_block( torrent& t , peer_connection& c @@ -91,8 +91,12 @@ namespace // (if the latency is more than this, the download will stall) // so, the queue size is 5 * down_rate / 16 kiB (16 kB is the size of each request) // the minimum request size is 2 and the maximum is 48 - - int desired_queue_size = static_cast(queue_time * c.statistics().download_rate() / (16 * 1024)); + // the block size doesn't have to be 16. So we first query the torrent for it + const int block_size = t.block_size(); + assert(block_size > 0); + + int desired_queue_size = static_cast(queue_time + * c.statistics().download_rate() / block_size); if (desired_queue_size > max_request_queue) desired_queue_size = max_request_queue; if (desired_queue_size < min_request_queue) desired_queue_size = min_request_queue; @@ -113,7 +117,18 @@ namespace // should be guaranteed to be available for download // (if num_requests is too big, too many pieces are // picked and cpu-time is wasted) - p.pick_pieces(c.get_bitfield(), interesting_pieces, num_requests); + // the last argument is if we should prefer whole pieces + // for this peer. If we're downloading one piece in 20 seconds + // then use this mode. + bool prefer_whole_pieces = c.statistics().download_payload_rate() * 20.f + > t.torrent_file().piece_length(); + + // if we prefer whole pieces, the piece picker will pick at least + // the number of blocks we want, but it will try to make the picked + // blocks be from whole pieces, possibly by returning more blocks + // than we requested. + p.pick_pieces(c.get_bitfield(), interesting_pieces + , num_requests, prefer_whole_pieces, c.get_socket()->sender()); // this vector is filled with the interesting pieces // that some other peer is currently downloading @@ -137,7 +152,6 @@ namespace // and return c.send_request(*i); num_requests--; - if (num_requests <= 0) return; } // in this case, we could not find any blocks @@ -155,13 +169,18 @@ namespace while (num_requests > 0) { peer_connection* peer = 0; - float min_weight = std::numeric_limits::max(); + + // This peer's weight will be the minimum, to prevent + // cancelling requests from a faster peer. + float min_weight = (int)c.download_queue().size() == 0 + ? std::numeric_limits::max() + : c.statistics().download_payload_rate() / (int)c.download_queue().size(); + // find the peer with the lowest download // speed that also has a piece that this // peer could send us for (torrent::peer_iterator i = t.begin(); - i != t.end(); - ++i) + i != t.end(); ++i) { // don't try to take over blocks from ourself if (i->second == &c) diff --git a/src/session.cpp b/src/session.cpp index 5f682e4eb..42ec7e9a7 100755 --- a/src/session.cpp +++ b/src/session.cpp @@ -364,9 +364,6 @@ namespace libtorrent { namespace detail std::vector > error_clients; boost::posix_time::ptime timer = second_clock::universal_time(); -#ifndef NDEBUG - int loops_per_second = 0; -#endif for(;;) { try @@ -377,7 +374,6 @@ namespace libtorrent { namespace detail boost::mutex::scoped_lock l(m_mutex); check_invariant("before SELECT"); } - loops_per_second++; #endif @@ -650,11 +646,6 @@ namespace libtorrent { namespace detail // THE SECTION BELOW IS EXECUTED ONCE EVERY SECOND // ************************ -#ifndef NDEBUG - if (loops_per_second > 800) std::cout << "\n\nloops: " << loops_per_second << "\n"; - loops_per_second = 0; -#endif - // do the second_tick() on each connection // this will update their statistics (download and upload speeds) // also purge sockets that have timed out diff --git a/src/storage.cpp b/src/storage.cpp index 17e0d2f45..b1121babd 100755 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -1476,7 +1476,7 @@ namespace libtorrent boost::recursive_mutex::scoped_lock lock(m_mutex); // ---------------------------------------------------------------------- - INVARIANT_CHECK; +// INVARIANT_CHECK; assert(piece_index >= 0); assert(piece_index < (int)m_piece_to_slot.size()); @@ -1635,7 +1635,7 @@ namespace libtorrent boost::recursive_mutex::scoped_lock lock(m_mutex); // ---------------------------------------------------------------------- - INVARIANT_CHECK; +// INVARIANT_CHECK; assert(!m_unallocated_slots.empty()); diff --git a/src/torrent.cpp b/src/torrent.cpp index 5fb5a9a42..76639999f 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -263,7 +263,7 @@ namespace libtorrent { assert(m_torrent_file.is_valid()); assert(m_torrent_file.num_files() > 0); - assert(m_torrent_file.total_size() > 0); + assert(m_torrent_file.total_size() >= 0); m_have_pieces.resize(m_torrent_file.num_pieces(), false); m_storage = std::auto_ptr(new piece_manager(m_torrent_file, m_save_path)); @@ -396,6 +396,9 @@ namespace libtorrent if (!valid_metadata()) return 0; assert(m_picker.get()); + + if (m_torrent_file.num_pieces() == 0) + return boost::tuple(0,0); const int last_piece = m_torrent_file.num_pieces() - 1; size_type wanted_done = (m_num_pieces - m_picker->num_have_filtered()) @@ -554,7 +557,7 @@ namespace libtorrent m_abort = true; m_event = tracker_request::stopped; // disconnect all peers and close all - // files belonging to the torrent + // files belonging to the torrents disconnect_all(); if (m_storage.get()) m_storage->release_files(); } @@ -1263,8 +1266,9 @@ namespace libtorrent } assert(st.total_wanted >= st.total_wanted_done); - - st.progress = st.total_wanted_done + + if (st.total_wanted == 0) st.progress = 1.f; + else st.progress = st.total_wanted_done / static_cast(st.total_wanted); st.pieces = &m_have_pieces; diff --git a/src/torrent_handle.cpp b/src/torrent_handle.cpp index ed172fb9c..de1119313 100755 --- a/src/torrent_handle.cpp +++ b/src/torrent_handle.cpp @@ -673,9 +673,7 @@ namespace libtorrent = p.get_download_queue(); for (std::vector::const_iterator i - = q.begin(); - i != q.end(); - ++i) + = q.begin(); i != q.end(); ++i) { partial_piece_info pi; pi.finished_blocks = i->finished_blocks; diff --git a/test/Jamfile b/test/Jamfile index 3f5078fd9..d6812ae3c 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -5,7 +5,7 @@ project requirements multi /torrent main.cpp - ; + ; test-suite libtorrent : # [ run test_storage.cpp ] diff --git a/test/test_piece_picker.cpp b/test/test_piece_picker.cpp index 8f56da28e..11d251b8a 100644 --- a/test/test_piece_picker.cpp +++ b/test/test_piece_picker.cpp @@ -86,21 +86,21 @@ int test_main() std::vector picked; picked.clear(); - p.pick_pieces(peer1, picked, 1); + p.pick_pieces(peer1, picked, 1, false, address()); TEST_CHECK(picked.size() == 1); TEST_CHECK(picked.front().piece_index == 2); // now pick a piece from peer2. The block is supposed to be // from piece 3, since it is the rarest piece that peer has. picked.clear(); - p.pick_pieces(peer2, picked, 1); + p.pick_pieces(peer2, picked, 1, false, address()); TEST_CHECK(picked.size() == 1); TEST_CHECK(picked.front().piece_index == 3); // same thing for peer3. picked.clear(); - p.pick_pieces(peer3, picked, 1); + p.pick_pieces(peer3, picked, 1, false, address()); TEST_CHECK(picked.size() == 1); TEST_CHECK(picked.front().piece_index == 5); @@ -114,7 +114,7 @@ int test_main() p.inc_refcount(1); picked.clear(); - p.pick_pieces(peer3, picked, 1); + p.pick_pieces(peer3, picked, 1, false, address()); TEST_CHECK(picked.size() == 1); TEST_CHECK(picked.front().piece_index == 1); // and the block picked should not be 0 or 2 @@ -138,9 +138,9 @@ int test_main() // we have block 0 and 2 already, so we can't mark // them as begin downloaded. - p.mark_as_downloading(piece_block(1, 1), address()); - p.mark_as_downloading(piece_block(1, 3), address()); - p.mark_as_downloading(piece_block(2, 0), address()); + p.mark_as_downloading(piece_block(1, 1), address(1,1,1,1,0)); + p.mark_as_downloading(piece_block(1, 3), address(1,1,1,1,0)); + p.mark_as_downloading(piece_block(2, 0), address(1,1,1,1,0)); std::vector const& downloads = p.get_download_queue(); TEST_CHECK(downloads.size() == 2); @@ -168,13 +168,84 @@ int test_main() TEST_CHECK(!p.is_downloading(piece_block(2, 1))); picked.clear(); - p.pick_pieces(peer1, picked, 1); + p.pick_pieces(peer1, picked, 1, false, address()); TEST_CHECK(picked.size() == 2); piece_block expected3[] = { piece_block(2, 0), piece_block(2, 1) }; TEST_CHECK(std::equal(picked.begin() , picked.end(), expected3)); + // now, if we assume we're downloading at such a speed that + // we might prefer to download whole pieces at a time from + // this peer. It should not pick piece 1 or 2 (since those are + // partially selected) + + picked.clear(); + p.pick_pieces(peer1, picked, 1, true, address()); + + // it will pick 4 blocks, since we said we + // wanted whole pieces. + TEST_CHECK(picked.size() == 4); + + piece_block expected4[] = + { + piece_block(3, 0), piece_block(3, 1) + , piece_block(3, 2), piece_block(3, 3) + }; + TEST_CHECK(std::equal(picked.begin() + , picked.end(), expected4)); + + // now, try the same thing, but pick as many pieces as possible + // to make sure it can still fall back on partial pieces + + picked.clear(); + p.pick_pieces(peer1, picked, 100, true, address()); + + TEST_CHECK(picked.size() == 14); + + piece_block expected5[] = + { + piece_block(3, 0), piece_block(3, 1) + , piece_block(3, 2), piece_block(3, 3) + , piece_block(5, 0), piece_block(5, 1) + , piece_block(5, 2), piece_block(5, 3) + , piece_block(2, 0), piece_block(2, 1) + , piece_block(2, 2), piece_block(2, 3) + , piece_block(1, 1), piece_block(1, 3) + }; + + TEST_CHECK(std::equal(picked.begin() + , picked.end(), expected5)); + + // now, try the same thing, but pick as many pieces as possible + // to make sure it can still fall back on partial pieces + + picked.clear(); + p.pick_pieces(peer1, picked, 100, true, address(1,1,1,1,0)); + + for (std::vector::iterator i = picked.begin(); i != picked.end(); ++i) + { + std::cerr << "(" << i->piece_index << "," << i->block_index << ")\n"; + } + + TEST_CHECK(picked.size() == 11); + + piece_block expected6[] = + { + piece_block(2, 1), piece_block(2, 2) + , piece_block(2, 3) + , piece_block(3, 0), piece_block(3, 1) + , piece_block(3, 2), piece_block(3, 3) + , piece_block(5, 0), piece_block(5, 1) + , piece_block(5, 2), piece_block(5, 3) + }; + + TEST_CHECK(std::equal(picked.begin() + , picked.end(), expected6)); + + + // TODO: make sure there are no duplicates + // in the picked blocks! } return 0;