2003-10-23 01:00:57 +02:00
|
|
|
/*
|
|
|
|
|
|
|
|
Copyright (c) 2003, Arvid Norberg
|
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in
|
|
|
|
the documentation and/or other materials provided with the distribution.
|
|
|
|
* Neither the name of the author nor the names of its
|
|
|
|
contributors may be used to endorse or promote products derived
|
|
|
|
from this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2007-03-17 18:15:16 +01:00
|
|
|
#include "libtorrent/pch.hpp"
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
#include <vector>
|
|
|
|
#include <cmath>
|
2003-12-01 06:01:40 +01:00
|
|
|
#include <algorithm>
|
2003-12-17 20:03:23 +01:00
|
|
|
#include <numeric>
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2006-07-16 02:08:50 +02:00
|
|
|
// non-standard header, is_sorted()
|
|
|
|
//#include <algo.h>
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
#include "libtorrent/piece_picker.hpp"
|
2006-10-11 16:02:21 +02:00
|
|
|
#include "libtorrent/aux_/session_impl.hpp"
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
#include "libtorrent/peer_connection.hpp"
|
2006-01-11 02:32:26 +01:00
|
|
|
#include "libtorrent/torrent.hpp"
|
2003-10-23 01:00:57 +02:00
|
|
|
#endif
|
|
|
|
|
2007-03-29 02:52:16 +02:00
|
|
|
#define TORRENT_PIECE_PICKER_INVARIANT_CHECK INVARIANT_CHECK
|
|
|
|
//#define TORRENT_PIECE_PICKER_INVARIANT_CHECK
|
2006-07-16 02:08:50 +02:00
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
namespace libtorrent
|
|
|
|
{
|
|
|
|
|
2007-03-17 18:28:59 +01:00
|
|
|
piece_picker::piece_picker(int blocks_per_piece, int total_num_blocks)
|
2003-10-23 01:00:57 +02:00
|
|
|
: m_piece_info(2)
|
|
|
|
, m_piece_map((total_num_blocks + blocks_per_piece-1) / blocks_per_piece)
|
2005-05-30 19:43:03 +02:00
|
|
|
, m_num_filtered(0)
|
|
|
|
, m_num_have_filtered(0)
|
2006-09-04 19:17:45 +02:00
|
|
|
, m_sequenced_download_threshold(100)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-01-26 01:21:12 +01:00
|
|
|
assert(blocks_per_piece > 0);
|
2005-08-15 00:04:58 +02:00
|
|
|
assert(total_num_blocks >= 0);
|
2007-03-17 18:28:59 +01:00
|
|
|
#ifndef NDEBUG
|
|
|
|
m_files_checked_called = false;
|
|
|
|
#endif
|
2004-01-25 23:41:55 +01:00
|
|
|
|
2005-03-19 13:22:40 +01:00
|
|
|
// the piece index is stored in 20 bits, which limits the allowed
|
|
|
|
// number of pieces somewhat
|
2005-08-12 14:40:58 +02:00
|
|
|
if (m_piece_map.size() >= piece_pos::we_have_index)
|
|
|
|
throw std::runtime_error("too many pieces in torrent");
|
2005-03-19 13:22:40 +01:00
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
m_blocks_per_piece = blocks_per_piece;
|
|
|
|
m_blocks_in_last_piece = total_num_blocks % blocks_per_piece;
|
|
|
|
if (m_blocks_in_last_piece == 0) m_blocks_in_last_piece = blocks_per_piece;
|
|
|
|
|
|
|
|
assert(m_blocks_per_piece <= max_blocks_per_piece);
|
|
|
|
assert(m_blocks_in_last_piece <= m_blocks_per_piece);
|
|
|
|
assert(m_blocks_in_last_piece <= max_blocks_per_piece);
|
|
|
|
|
|
|
|
// allocate the piece_map to cover all pieces
|
|
|
|
// and make them invalid (as if though we already had every piece)
|
2005-08-12 14:40:58 +02:00
|
|
|
std::fill(m_piece_map.begin(), m_piece_map.end()
|
|
|
|
, piece_pos(0, piece_pos::we_have_index));
|
2007-03-17 18:28:59 +01:00
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2007-03-17 18:28:59 +01:00
|
|
|
// pieces is a bitmask with the pieces we have
|
|
|
|
void piece_picker::files_checked(
|
|
|
|
const std::vector<bool>& pieces
|
|
|
|
, const std::vector<downloading_piece>& unfinished)
|
|
|
|
{
|
|
|
|
#ifndef NDEBUG
|
|
|
|
m_files_checked_called = true;
|
|
|
|
#endif
|
2003-10-23 01:00:57 +02:00
|
|
|
for (std::vector<bool>::const_iterator i = pieces.begin();
|
2005-05-13 02:39:39 +02:00
|
|
|
i != pieces.end(); ++i)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
|
|
|
if (*i) continue;
|
2004-01-25 19:18:36 +01:00
|
|
|
int index = static_cast<int>(i - pieces.begin());
|
2007-03-15 23:03:56 +01:00
|
|
|
m_piece_map[index].index = 0;
|
|
|
|
if (m_piece_map[index].filtered())
|
2005-05-30 19:43:03 +02:00
|
|
|
{
|
|
|
|
++m_num_filtered;
|
|
|
|
--m_num_have_filtered;
|
2005-09-01 23:04:21 +02:00
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2004-01-03 03:10:11 +01:00
|
|
|
|
|
|
|
// if we have fast resume info
|
|
|
|
// use it
|
|
|
|
if (!unfinished.empty())
|
|
|
|
{
|
|
|
|
for (std::vector<downloading_piece>::const_iterator i
|
2005-05-13 02:39:39 +02:00
|
|
|
= unfinished.begin(); i != unfinished.end(); ++i)
|
2004-01-03 03:10:11 +01:00
|
|
|
{
|
2006-04-25 23:04:48 +02:00
|
|
|
tcp::endpoint peer;
|
2004-01-03 03:10:11 +01:00
|
|
|
for (int j = 0; j < m_blocks_per_piece; ++j)
|
|
|
|
{
|
|
|
|
if (i->finished_blocks[j])
|
|
|
|
mark_as_finished(piece_block(i->index, j), peer);
|
|
|
|
}
|
2006-12-31 15:48:18 +01:00
|
|
|
if (is_piece_finished(i->index))
|
|
|
|
{
|
|
|
|
// TODO: handle this case by verifying the
|
|
|
|
// piece and either accept it or discard it
|
|
|
|
assert(false);
|
|
|
|
}
|
2004-01-03 03:10:11 +01:00
|
|
|
}
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2006-04-25 23:04:48 +02:00
|
|
|
void piece_picker::set_sequenced_download_threshold(
|
|
|
|
int sequenced_download_threshold)
|
|
|
|
{
|
2006-07-16 02:08:50 +02:00
|
|
|
TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
2006-04-25 23:04:48 +02:00
|
|
|
|
|
|
|
if (sequenced_download_threshold == m_sequenced_download_threshold)
|
|
|
|
return;
|
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
assert(sequenced_download_threshold > 0);
|
|
|
|
|
2006-04-25 23:04:48 +02:00
|
|
|
int old_limit = m_sequenced_download_threshold;
|
|
|
|
m_sequenced_download_threshold = sequenced_download_threshold;
|
|
|
|
|
|
|
|
for (std::vector<piece_pos>::iterator i = m_piece_map.begin()
|
|
|
|
, end(m_piece_map.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
if (i->priority(old_limit) != i->priority(m_sequenced_download_threshold))
|
|
|
|
{
|
2006-06-12 23:20:32 +02:00
|
|
|
piece_pos& p = *i;
|
2006-09-01 11:36:43 +02:00
|
|
|
int prev_priority = p.priority(old_limit);
|
2007-03-15 23:03:56 +01:00
|
|
|
if (prev_priority == 0) continue;
|
|
|
|
move(prev_priority, p.index);
|
2006-04-25 23:04:48 +02:00
|
|
|
}
|
|
|
|
}
|
2006-09-04 00:59:54 +02:00
|
|
|
|
|
|
|
typedef std::vector<int> info_t;
|
|
|
|
|
|
|
|
if (old_limit < sequenced_download_threshold)
|
|
|
|
{
|
2006-10-02 10:58:28 +02:00
|
|
|
// the threshold was incremented, in case
|
|
|
|
// the previous max availability was reached
|
|
|
|
// we need to shuffle that bucket, if not, we
|
|
|
|
// don't have to do anything
|
|
|
|
if (int(m_piece_info.size()) > old_limit)
|
2006-09-04 00:59:54 +02:00
|
|
|
{
|
2006-10-02 10:58:28 +02:00
|
|
|
info_t& in = m_piece_info[old_limit];
|
|
|
|
std::random_shuffle(in.begin(), in.end());
|
|
|
|
int c = 0;
|
|
|
|
for (info_t::iterator i = in.begin()
|
|
|
|
, end(in.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
m_piece_map[*i].index = c++;
|
|
|
|
assert(m_piece_map[*i].priority(old_limit) == old_limit);
|
|
|
|
}
|
2006-09-04 00:59:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (int(m_piece_info.size()) > sequenced_download_threshold)
|
|
|
|
{
|
|
|
|
info_t& in = m_piece_info[sequenced_download_threshold];
|
|
|
|
std::sort(in.begin(), in.end());
|
|
|
|
int c = 0;
|
|
|
|
for (info_t::iterator i = in.begin()
|
|
|
|
, end(in.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
m_piece_map[*i].index = c++;
|
|
|
|
assert(m_piece_map[*i].priority(
|
|
|
|
sequenced_download_threshold) == sequenced_download_threshold);
|
|
|
|
}
|
|
|
|
}
|
2006-04-25 23:04:48 +02:00
|
|
|
}
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
#ifndef NDEBUG
|
|
|
|
|
2006-07-16 02:08:50 +02:00
|
|
|
void piece_picker::check_invariant(const torrent* t) const
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
|
|
|
assert(sizeof(piece_pos) == 4);
|
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
assert(m_piece_info.empty() || m_piece_info[0].empty());
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
if (t != 0)
|
2004-01-26 11:29:00 +01:00
|
|
|
assert((int)m_piece_map.size() == t->torrent_file().num_pieces());
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2007-04-17 02:20:39 +02:00
|
|
|
for (int i = m_sequenced_download_threshold * 2 + 1; i < int(m_piece_info.size()); ++i)
|
|
|
|
assert(m_piece_info[i].empty());
|
|
|
|
|
2007-04-27 02:27:37 +02:00
|
|
|
for (std::vector<downloading_piece>::const_iterator i = m_downloads.begin()
|
|
|
|
, end(m_downloads.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
bool blocks_requested = false;
|
|
|
|
int num_blocks = blocks_in_piece(i->index);
|
|
|
|
for (int k = 0; k < num_blocks; ++k)
|
|
|
|
{
|
|
|
|
if (i->finished_blocks[k]) continue;
|
|
|
|
if (i->requested_blocks[k])
|
|
|
|
{
|
|
|
|
blocks_requested = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(blocks_requested == (i->state != none));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-05-30 19:43:03 +02:00
|
|
|
int num_filtered = 0;
|
|
|
|
int num_have_filtered = 0;
|
2003-10-23 01:00:57 +02:00
|
|
|
for (std::vector<piece_pos>::const_iterator i = m_piece_map.begin();
|
2005-05-13 02:39:39 +02:00
|
|
|
i != m_piece_map.end(); ++i)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
int index = static_cast<int>(i - m_piece_map.begin());
|
2007-03-15 23:03:56 +01:00
|
|
|
if (i->filtered())
|
2005-05-30 19:43:03 +02:00
|
|
|
{
|
|
|
|
if (i->index != piece_pos::we_have_index)
|
|
|
|
++num_filtered;
|
|
|
|
else
|
|
|
|
++num_have_filtered;
|
|
|
|
}
|
2007-03-29 02:52:16 +02:00
|
|
|
#if 0
|
2003-10-23 01:00:57 +02:00
|
|
|
if (t != 0)
|
|
|
|
{
|
|
|
|
int actual_peer_count = 0;
|
2004-01-13 04:08:59 +01:00
|
|
|
for (torrent::const_peer_iterator peer = t->begin();
|
2005-05-13 02:39:39 +02:00
|
|
|
peer != t->end(); ++peer)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-01-13 04:08:59 +01:00
|
|
|
if (peer->second->has_piece(index)) actual_peer_count++;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2004-01-26 11:29:00 +01:00
|
|
|
assert((int)i->peer_count == actual_peer_count);
|
2003-10-23 01:00:57 +02:00
|
|
|
/*
|
|
|
|
int num_downloaders = 0;
|
|
|
|
for (std::vector<peer_connection*>::const_iterator peer = t->begin();
|
|
|
|
peer != t->end();
|
|
|
|
++peer)
|
|
|
|
{
|
|
|
|
const std::vector<piece_block>& queue = (*peer)->download_queue();
|
|
|
|
if (std::find_if(queue.begin(), queue.end(), has_index(index)) == queue.end()) continue;
|
|
|
|
|
|
|
|
++num_downloaders;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i->downloading)
|
|
|
|
{
|
|
|
|
assert(num_downloaders == 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
assert(num_downloaders == 0);
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
}
|
2007-03-29 02:52:16 +02:00
|
|
|
#endif
|
2007-04-15 04:14:02 +02:00
|
|
|
|
2005-05-25 12:01:01 +02:00
|
|
|
if (i->index == piece_pos::we_have_index)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
|
|
|
assert(t == 0 || t->have_piece(index));
|
|
|
|
assert(i->downloading == 0);
|
2007-04-15 04:14:02 +02:00
|
|
|
/*
|
2003-10-23 01:00:57 +02:00
|
|
|
// make sure there's no entry
|
|
|
|
// with this index. (there shouldn't
|
2005-05-25 12:01:01 +02:00
|
|
|
// be since the piece_map is piece_pos::we_have_index)
|
2007-03-15 23:03:56 +01:00
|
|
|
for (int i = 0; i < int(m_piece_info.size()); ++i)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2007-03-15 23:03:56 +01:00
|
|
|
for (int j = 0; j < int(m_piece_info[i].size()); ++j)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2007-03-15 23:03:56 +01:00
|
|
|
assert(m_piece_info[i][j] != index);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
}
|
2007-04-15 04:14:02 +02:00
|
|
|
*/
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2007-03-21 03:09:50 +01:00
|
|
|
else
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
|
|
|
if (t != 0)
|
|
|
|
assert(!t->have_piece(index));
|
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
int prio = i->priority(m_sequenced_download_threshold);
|
|
|
|
if (prio > 0)
|
2006-07-16 02:08:50 +02:00
|
|
|
{
|
2007-03-15 23:03:56 +01:00
|
|
|
const std::vector<int>& vec = m_piece_info[prio];
|
|
|
|
assert (i->index < vec.size());
|
|
|
|
assert(vec[i->index] == index);
|
2006-07-16 02:08:50 +02:00
|
|
|
}
|
2007-04-15 04:14:02 +02:00
|
|
|
/*
|
2007-03-21 03:09:50 +01:00
|
|
|
for (int k = 0; k < int(m_piece_info.size()); ++k)
|
|
|
|
{
|
|
|
|
for (int j = 0; j < int(m_piece_info[k].size()); ++j)
|
|
|
|
{
|
|
|
|
assert(int(m_piece_info[k][j]) != index
|
|
|
|
|| (prio > 0 && prio == k && int(i->index) == j));
|
|
|
|
}
|
|
|
|
}
|
2007-03-29 02:52:16 +02:00
|
|
|
*/
|
2007-04-15 04:14:02 +02:00
|
|
|
}
|
|
|
|
|
2007-03-29 02:52:16 +02:00
|
|
|
int count = std::count_if(m_downloads.begin(), m_downloads.end()
|
|
|
|
, has_index(index));
|
2003-10-23 01:00:57 +02:00
|
|
|
if (i->downloading == 1)
|
|
|
|
{
|
2007-03-29 02:52:16 +02:00
|
|
|
assert(count == 1);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2007-03-29 02:52:16 +02:00
|
|
|
assert(count == 0);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
}
|
2005-05-30 19:43:03 +02:00
|
|
|
assert(num_filtered == m_num_filtered);
|
|
|
|
assert(num_have_filtered == m_num_have_filtered);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2004-09-08 01:16:11 +02:00
|
|
|
#endif
|
|
|
|
|
2004-08-05 15:56:26 +02:00
|
|
|
float piece_picker::distributed_copies() const
|
|
|
|
{
|
2004-09-08 01:16:11 +02:00
|
|
|
const float num_pieces = static_cast<float>(m_piece_map.size());
|
|
|
|
|
2007-04-11 22:33:28 +02:00
|
|
|
int min_availability = piece_pos::max_peer_count;
|
|
|
|
// find the lowest availability count
|
|
|
|
// count the number of pieces that have that availability
|
|
|
|
// and also the number of pieces that have more than that.
|
|
|
|
int integer_part = 0;
|
|
|
|
int fraction_part = 0;
|
|
|
|
for (std::vector<piece_pos>::const_iterator i = m_piece_map.begin()
|
|
|
|
, end(m_piece_map.end()); i != end; ++i)
|
2004-08-05 15:56:26 +02:00
|
|
|
{
|
2007-04-11 22:33:28 +02:00
|
|
|
int peer_count = int(i->peer_count);
|
|
|
|
// take ourself into account
|
|
|
|
if (i->have()) ++peer_count;
|
|
|
|
if (min_availability > peer_count)
|
|
|
|
{
|
|
|
|
min_availability = i->peer_count;
|
|
|
|
fraction_part += integer_part;
|
|
|
|
integer_part = 1;
|
|
|
|
}
|
|
|
|
else if (peer_count == min_availability)
|
|
|
|
{
|
|
|
|
++integer_part;
|
|
|
|
}
|
|
|
|
else
|
2004-09-08 01:16:11 +02:00
|
|
|
{
|
2007-04-11 22:33:28 +02:00
|
|
|
assert(peer_count > min_availability);
|
|
|
|
++fraction_part;
|
2004-08-05 15:56:26 +02:00
|
|
|
}
|
|
|
|
}
|
2007-04-11 22:33:28 +02:00
|
|
|
assert(integer_part + fraction_part == num_pieces);
|
|
|
|
return float(min_availability) + (fraction_part / num_pieces);
|
2004-08-05 15:56:26 +02:00
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2005-09-01 23:04:21 +02:00
|
|
|
void piece_picker::add(int index)
|
|
|
|
{
|
|
|
|
assert(index >= 0);
|
2007-03-15 23:03:56 +01:00
|
|
|
assert(index < int(m_piece_map.size()));
|
2005-09-01 23:04:21 +02:00
|
|
|
piece_pos& p = m_piece_map[index];
|
2007-03-15 23:03:56 +01:00
|
|
|
assert(!p.filtered());
|
|
|
|
assert(!p.have());
|
2005-09-01 23:04:21 +02:00
|
|
|
|
2006-04-25 23:04:48 +02:00
|
|
|
int priority = p.priority(m_sequenced_download_threshold);
|
2007-03-15 23:03:56 +01:00
|
|
|
assert(priority > 0);
|
|
|
|
if (int(m_piece_info.size()) <= priority)
|
|
|
|
m_piece_info.resize(priority + 1);
|
2006-04-25 23:04:48 +02:00
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
assert(int(m_piece_info.size()) > priority);
|
2005-09-01 23:04:21 +02:00
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
if (is_ordered(priority))
|
2006-04-25 23:04:48 +02:00
|
|
|
{
|
|
|
|
// the piece should be inserted ordered, not randomly
|
2007-03-15 23:03:56 +01:00
|
|
|
std::vector<int>& v = m_piece_info[priority];
|
2006-07-16 02:08:50 +02:00
|
|
|
// assert(is_sorted(v.begin(), v.end()/*, std::greater<int>()*/));
|
2006-04-25 23:04:48 +02:00
|
|
|
std::vector<int>::iterator i = std::lower_bound(v.begin(), v.end()
|
2006-07-16 02:08:50 +02:00
|
|
|
, index/*, std::greater<int>()*/);
|
2006-04-25 23:04:48 +02:00
|
|
|
p.index = i - v.begin();
|
|
|
|
v.insert(i, index);
|
|
|
|
i = v.begin() + p.index + 1;
|
|
|
|
for (;i != v.end(); ++i)
|
|
|
|
{
|
|
|
|
++m_piece_map[*i].index;
|
|
|
|
assert(v[m_piece_map[*i].index] == *i);
|
|
|
|
}
|
2006-07-16 02:08:50 +02:00
|
|
|
// assert(is_sorted(v.begin(), v.end()/*, std::greater<int>()*/));
|
2006-04-25 23:04:48 +02:00
|
|
|
}
|
2007-03-15 23:03:56 +01:00
|
|
|
else if (m_piece_info[priority].size() < 2)
|
2006-04-25 23:04:48 +02:00
|
|
|
{
|
2007-03-15 23:03:56 +01:00
|
|
|
p.index = m_piece_info[priority].size();
|
|
|
|
m_piece_info[priority].push_back(index);
|
2006-04-25 23:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// find a random position in the destination vector where we will place
|
|
|
|
// this entry.
|
2007-03-15 23:03:56 +01:00
|
|
|
int dst_index = rand() % m_piece_info[priority].size();
|
2006-04-25 23:04:48 +02:00
|
|
|
|
|
|
|
// copy the entry at that position to the back
|
2007-03-15 23:03:56 +01:00
|
|
|
m_piece_map[m_piece_info[priority][dst_index]].index
|
|
|
|
= m_piece_info[priority].size();
|
|
|
|
m_piece_info[priority].push_back(m_piece_info[priority][dst_index]);
|
2006-04-25 23:04:48 +02:00
|
|
|
|
|
|
|
// and then replace the one at dst_index with the one we're moving.
|
|
|
|
// this procedure is to make sure there's no ordering when pieces
|
|
|
|
// are moved in sequenced order.
|
|
|
|
p.index = dst_index;
|
2007-03-15 23:03:56 +01:00
|
|
|
m_piece_info[priority][p.index] = index;
|
2006-04-25 23:04:48 +02:00
|
|
|
}
|
2005-09-01 23:04:21 +02:00
|
|
|
}
|
2005-05-25 12:01:01 +02:00
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
// will update the piece with the given properties (priority, elem_index)
|
|
|
|
// to place it at the correct position in the vectors.
|
|
|
|
void piece_picker::move(int priority, int elem_index)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2007-03-15 23:03:56 +01:00
|
|
|
assert(priority > 0);
|
2004-01-25 23:41:55 +01:00
|
|
|
assert(elem_index >= 0);
|
2007-03-17 18:28:59 +01:00
|
|
|
assert(m_files_checked_called);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
assert(int(m_piece_info.size()) > priority);
|
|
|
|
assert(int(m_piece_info[priority].size()) > elem_index);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
int index = m_piece_info[priority][elem_index];
|
2003-10-23 01:00:57 +02:00
|
|
|
// update the piece_map
|
|
|
|
piece_pos& p = m_piece_map[index];
|
2007-03-21 03:14:31 +01:00
|
|
|
assert(int(p.index) == elem_index || p.have());
|
2007-03-21 03:09:50 +01:00
|
|
|
|
2006-04-25 23:04:48 +02:00
|
|
|
int new_priority = p.priority(m_sequenced_download_threshold);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
if (new_priority == priority) return;
|
|
|
|
|
|
|
|
if (int(m_piece_info.size()) <= new_priority
|
|
|
|
&& new_priority > 0)
|
2006-07-16 02:08:50 +02:00
|
|
|
{
|
2007-03-15 23:03:56 +01:00
|
|
|
m_piece_info.resize(new_priority + 1);
|
|
|
|
assert(int(m_piece_info.size()) > new_priority);
|
2006-07-16 02:08:50 +02:00
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
if (new_priority == 0)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2007-03-15 23:03:56 +01:00
|
|
|
// this means the piece should not have an entry
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2007-03-15 23:03:56 +01:00
|
|
|
else if (is_ordered(new_priority))
|
2006-04-25 23:04:48 +02:00
|
|
|
{
|
|
|
|
// the piece should be inserted ordered, not randomly
|
2007-03-15 23:03:56 +01:00
|
|
|
std::vector<int>& v = m_piece_info[new_priority];
|
2006-07-16 02:08:50 +02:00
|
|
|
// assert(is_sorted(v.begin(), v.end()/*, std::greater<int>()*/));
|
2006-04-25 23:04:48 +02:00
|
|
|
std::vector<int>::iterator i = std::lower_bound(v.begin(), v.end()
|
2006-07-16 02:08:50 +02:00
|
|
|
, index/*, std::greater<int>()*/);
|
2006-04-25 23:04:48 +02:00
|
|
|
p.index = i - v.begin();
|
|
|
|
v.insert(i, index);
|
|
|
|
i = v.begin() + p.index + 1;
|
|
|
|
for (;i != v.end(); ++i)
|
|
|
|
{
|
|
|
|
++m_piece_map[*i].index;
|
|
|
|
assert(v[m_piece_map[*i].index] == *i);
|
|
|
|
}
|
2006-07-16 02:08:50 +02:00
|
|
|
// assert(is_sorted(v.begin(), v.end()/*, std::greater<int>()*/));
|
2006-04-25 23:04:48 +02:00
|
|
|
}
|
2007-03-15 23:03:56 +01:00
|
|
|
else if (m_piece_info[new_priority].size() < 2)
|
2006-04-25 23:04:48 +02:00
|
|
|
{
|
2007-03-15 23:03:56 +01:00
|
|
|
p.index = m_piece_info[new_priority].size();
|
|
|
|
m_piece_info[new_priority].push_back(index);
|
2006-04-25 23:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// find a random position in the destination vector where we will place
|
|
|
|
// this entry.
|
2007-03-15 23:03:56 +01:00
|
|
|
int dst_index = rand() % m_piece_info[new_priority].size();
|
2006-04-25 23:04:48 +02:00
|
|
|
|
|
|
|
// copy the entry at that position to the back
|
2007-03-15 23:03:56 +01:00
|
|
|
m_piece_map[m_piece_info[new_priority][dst_index]].index
|
|
|
|
= m_piece_info[new_priority].size();
|
|
|
|
m_piece_info[new_priority].push_back(m_piece_info[new_priority][dst_index]);
|
2006-04-25 23:04:48 +02:00
|
|
|
|
|
|
|
// and then replace the one at dst_index with the one we're moving.
|
|
|
|
// this procedure is to make sure there's no ordering when pieces
|
|
|
|
// are moved in sequenced order.
|
|
|
|
p.index = dst_index;
|
2007-03-15 23:03:56 +01:00
|
|
|
m_piece_info[new_priority][p.index] = index;
|
2006-04-25 23:04:48 +02:00
|
|
|
}
|
2007-03-15 23:03:56 +01:00
|
|
|
assert(new_priority == 0 || p.index < m_piece_info[p.priority(m_sequenced_download_threshold)].size());
|
|
|
|
assert(new_priority == 0 || m_piece_info[p.priority(m_sequenced_download_threshold)][p.index] == index);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
if (is_ordered(priority))
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2006-07-16 02:08:50 +02:00
|
|
|
// remove the element from the source vector and preserve the order
|
2007-03-15 23:03:56 +01:00
|
|
|
std::vector<int>& v = m_piece_info[priority];
|
2006-07-16 02:08:50 +02:00
|
|
|
v.erase(v.begin() + elem_index);
|
|
|
|
for (std::vector<int>::iterator i = v.begin() + elem_index;
|
|
|
|
i != v.end(); ++i)
|
|
|
|
{
|
|
|
|
--m_piece_map[*i].index;
|
|
|
|
assert(v[m_piece_map[*i].index] == *i);
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-07-16 02:08:50 +02:00
|
|
|
// this will remove elem from the source vector without
|
|
|
|
// preserving order, but the order is random anyway
|
2007-03-15 23:03:56 +01:00
|
|
|
int replace_index = m_piece_info[priority][elem_index] = m_piece_info[priority].back();
|
2006-07-16 02:08:50 +02:00
|
|
|
if (index != replace_index)
|
|
|
|
{
|
|
|
|
// update the entry we moved from the back
|
|
|
|
m_piece_map[replace_index].index = elem_index;
|
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
assert(int(m_piece_info[priority].size()) > elem_index);
|
2006-09-04 00:59:54 +02:00
|
|
|
// this may not necessarily be the case. If we've just updated the threshold and are updating
|
|
|
|
// the piece map
|
|
|
|
// assert((int)m_piece_map[replace_index].priority(m_sequenced_download_threshold) == priority);
|
2007-03-15 23:03:56 +01:00
|
|
|
assert(int(m_piece_map[replace_index].index) == elem_index);
|
|
|
|
assert(m_piece_info[priority][elem_index] == replace_index);
|
2006-07-16 02:08:50 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2007-03-15 23:03:56 +01:00
|
|
|
assert(int(m_piece_info[priority].size()) == elem_index+1);
|
2006-07-16 02:08:50 +02:00
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
m_piece_info[priority].pop_back();
|
2006-07-16 02:08:50 +02:00
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2004-01-25 23:41:55 +01:00
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
void piece_picker::restore_piece(int index)
|
|
|
|
{
|
2006-07-16 02:08:50 +02:00
|
|
|
TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
assert(index >= 0);
|
2004-01-25 19:18:36 +01:00
|
|
|
assert(index < (int)m_piece_map.size());
|
2007-03-17 18:28:59 +01:00
|
|
|
assert(m_files_checked_called);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
assert(m_piece_map[index].downloading == 1);
|
|
|
|
|
|
|
|
std::vector<downloading_piece>::iterator i
|
|
|
|
= std::find_if(m_downloads.begin(),
|
|
|
|
m_downloads.end(),
|
|
|
|
has_index(index));
|
|
|
|
assert(i != m_downloads.end());
|
|
|
|
m_downloads.erase(i);
|
|
|
|
|
2005-05-25 12:01:01 +02:00
|
|
|
piece_pos& p = m_piece_map[index];
|
2007-03-20 20:50:15 +01:00
|
|
|
int prev_priority = p.priority(m_sequenced_download_threshold);
|
2007-03-15 23:03:56 +01:00
|
|
|
p.downloading = 0;
|
2007-03-20 20:50:15 +01:00
|
|
|
int new_priority = p.priority(m_sequenced_download_threshold);
|
|
|
|
|
|
|
|
if (new_priority == prev_priority) return;
|
|
|
|
|
|
|
|
if (prev_priority == 0)
|
2007-03-20 20:34:10 +01:00
|
|
|
{
|
|
|
|
add(index);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2007-03-20 20:50:15 +01:00
|
|
|
move(prev_priority, p.index);
|
2007-03-20 20:34:10 +01:00
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2007-04-15 04:14:02 +02:00
|
|
|
void piece_picker::inc_refcount_all()
|
|
|
|
{
|
|
|
|
TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
|
|
|
assert(m_files_checked_called);
|
|
|
|
|
|
|
|
// in general priority = availability * 2
|
|
|
|
// see piece_block::priority()
|
|
|
|
|
|
|
|
// this will insert two empty vectors at the start of the
|
|
|
|
// piece_info vector. It is done like this as an optimization,
|
|
|
|
// to swap vectors instead of copying them
|
|
|
|
while (m_piece_info.size() < 3
|
|
|
|
|| (!m_piece_info.rbegin()->empty())
|
|
|
|
|| (!(m_piece_info.rbegin()+1)->empty()))
|
|
|
|
{
|
|
|
|
m_piece_info.push_back(std::vector<int>());
|
|
|
|
}
|
|
|
|
assert(m_piece_info.rbegin()->empty());
|
|
|
|
assert((m_piece_info.rbegin()+1)->empty());
|
|
|
|
typedef std::vector<std::vector<int> > piece_info_t;
|
|
|
|
for (piece_info_t::reverse_iterator i = m_piece_info.rbegin(), j(i+1)
|
|
|
|
, k(j+1), end(m_piece_info.rend()); k != end; ++i, ++j, ++k)
|
|
|
|
{
|
|
|
|
k->swap(*i);
|
|
|
|
}
|
|
|
|
assert(m_piece_info.begin()->empty());
|
|
|
|
assert((m_piece_info.begin()+1)->empty());
|
2007-04-17 02:20:39 +02:00
|
|
|
|
|
|
|
// if we have some priorities that are clamped to the
|
|
|
|
// sequenced download, move that vector back down
|
|
|
|
int last_index = m_piece_info.size() - 1;
|
|
|
|
int cap_index = m_sequenced_download_threshold * 2;
|
|
|
|
if (last_index == cap_index)
|
|
|
|
{
|
|
|
|
// this is the case when the top bucket
|
|
|
|
// was moved up into the sequenced download bucket.
|
|
|
|
m_piece_info.push_back(std::vector<int>());
|
|
|
|
m_piece_info[cap_index].swap(m_piece_info[cap_index+1]);
|
|
|
|
++last_index;
|
|
|
|
}
|
|
|
|
else if (last_index > cap_index)
|
|
|
|
{
|
|
|
|
if (last_index - cap_index == 1)
|
|
|
|
{
|
|
|
|
m_piece_info.push_back(std::vector<int>());
|
|
|
|
++last_index;
|
|
|
|
}
|
|
|
|
m_piece_info[cap_index+1].swap(m_piece_info[cap_index+2]);
|
|
|
|
m_piece_info[cap_index].swap(m_piece_info[cap_index+1]);
|
|
|
|
}
|
2007-04-15 04:14:02 +02:00
|
|
|
|
|
|
|
// now, increase the peer count of all the pieces.
|
|
|
|
// because of different priorities, some pieces may have
|
|
|
|
// ended up in the wrong priority bucket. Adjust that.
|
|
|
|
for (std::vector<piece_pos>::iterator i = m_piece_map.begin()
|
|
|
|
, end(m_piece_map.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
int prev_prio = i->priority(m_sequenced_download_threshold);
|
|
|
|
++i->peer_count;
|
|
|
|
// if the assumption that the priority would
|
|
|
|
// increase by 2 when increasing the availability
|
|
|
|
// by one isn't true for this particular piece, correct it.
|
|
|
|
// that assumption is true for all pieces with priority 0 or 1
|
|
|
|
int new_prio = i->priority(m_sequenced_download_threshold);
|
2007-04-17 02:20:39 +02:00
|
|
|
assert(new_prio <= cap_index);
|
2007-04-15 04:14:02 +02:00
|
|
|
if (prev_prio == 0 && new_prio > 0)
|
|
|
|
{
|
|
|
|
add(i - m_piece_map.begin());
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (new_prio == 0)
|
|
|
|
{
|
|
|
|
assert(prev_prio == 0);
|
|
|
|
continue;
|
|
|
|
}
|
2007-04-17 02:20:39 +02:00
|
|
|
if (prev_prio == cap_index)
|
|
|
|
{
|
|
|
|
assert(new_prio == cap_index);
|
2007-04-15 04:14:02 +02:00
|
|
|
continue;
|
2007-04-17 02:20:39 +02:00
|
|
|
}
|
|
|
|
if (new_prio == prev_prio + 2 && new_prio != cap_index)
|
|
|
|
{
|
|
|
|
assert(new_prio != cap_index);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (prev_prio + 2 >= cap_index)
|
|
|
|
{
|
|
|
|
// these two vectors will have moved one extra step
|
|
|
|
// passed the sequenced download threshold
|
|
|
|
++prev_prio;
|
|
|
|
}
|
|
|
|
assert(prev_prio + 2 != cap_index);
|
|
|
|
assert(prev_prio + 2 != new_prio);
|
2007-04-15 04:14:02 +02:00
|
|
|
move(prev_prio + 2, i->index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void piece_picker::dec_refcount_all()
|
|
|
|
{
|
|
|
|
TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
|
|
|
assert(m_files_checked_called);
|
|
|
|
assert(m_piece_info.size() >= 2);
|
|
|
|
assert(m_piece_info.front().empty());
|
|
|
|
// swap all vectors two steps down
|
|
|
|
if (m_piece_info.size() > 2)
|
|
|
|
{
|
|
|
|
typedef std::vector<std::vector<int> > piece_info_t;
|
|
|
|
for (piece_info_t::iterator i = m_piece_info.begin(), j(i+1)
|
|
|
|
, k(j+1), end(m_piece_info.end()); k != end; ++i, ++j, ++k)
|
|
|
|
{
|
|
|
|
k->swap(*i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_piece_info.resize(3);
|
|
|
|
}
|
2007-04-17 02:20:39 +02:00
|
|
|
int last_index = m_piece_info.size() - 1;
|
2007-04-17 23:54:40 +02:00
|
|
|
if ((m_piece_info.size() & 1) == 0)
|
2007-04-15 04:14:02 +02:00
|
|
|
{
|
|
|
|
// if there's an even number of vectors, swap
|
|
|
|
// the last two to get the same layout in both cases
|
2007-04-17 02:20:39 +02:00
|
|
|
m_piece_info[last_index].swap(m_piece_info[last_index-1]);
|
2007-04-15 04:14:02 +02:00
|
|
|
}
|
|
|
|
assert(m_piece_info.back().empty());
|
2007-04-17 02:20:39 +02:00
|
|
|
int pushed_out_index = m_piece_info.size() - 2;
|
|
|
|
|
|
|
|
int cap_index = m_sequenced_download_threshold * 2;
|
|
|
|
assert(m_piece_info[last_index].empty());
|
|
|
|
if (last_index >= cap_index)
|
|
|
|
{
|
2007-04-23 19:14:40 +02:00
|
|
|
assert(pushed_out_index == cap_index - 1
|
|
|
|
|| m_piece_info[cap_index - 1].empty());
|
2007-04-17 02:20:39 +02:00
|
|
|
m_piece_info[cap_index].swap(m_piece_info[cap_index - 2]);
|
2007-04-19 04:54:46 +02:00
|
|
|
if (cap_index == pushed_out_index)
|
|
|
|
pushed_out_index = cap_index - 2;
|
2007-04-17 02:20:39 +02:00
|
|
|
}
|
2007-04-15 04:14:02 +02:00
|
|
|
|
|
|
|
// first is the vector that were
|
|
|
|
// bumped down to 0. The should always be moved
|
|
|
|
// since they have to be removed or reinserted
|
|
|
|
std::vector<int>().swap(m_piece_info.front());
|
|
|
|
|
|
|
|
for (std::vector<piece_pos>::iterator i = m_piece_map.begin()
|
|
|
|
, end(m_piece_map.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
int prev_prio = i->priority(m_sequenced_download_threshold);
|
|
|
|
assert(i->peer_count > 0);
|
|
|
|
--i->peer_count;
|
|
|
|
// if the assumption that the priority would
|
|
|
|
// decrease by 2 when decreasing the availability
|
|
|
|
// by one isn't true for this particular piece, correct it.
|
|
|
|
// that assumption is true for all pieces with priority 0 or 1
|
|
|
|
if (prev_prio == 0)
|
|
|
|
{
|
|
|
|
assert(i->priority(m_sequenced_download_threshold) == 0);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
int new_prio = i->priority(m_sequenced_download_threshold);
|
2007-04-17 02:20:39 +02:00
|
|
|
if (prev_prio == cap_index)
|
|
|
|
{
|
|
|
|
if (new_prio == cap_index) continue;
|
|
|
|
prev_prio += 2;
|
|
|
|
}
|
|
|
|
else if (new_prio == prev_prio - 2)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (prev_prio == 2)
|
2007-04-15 04:14:02 +02:00
|
|
|
{
|
2007-04-17 02:20:39 +02:00
|
|
|
// if this piece was pushed down to priority 0, it was
|
|
|
|
// removed
|
2007-04-15 04:14:02 +02:00
|
|
|
assert(new_prio > 0);
|
|
|
|
add(i - m_piece_map.begin());
|
|
|
|
continue;
|
|
|
|
}
|
2007-04-17 02:20:39 +02:00
|
|
|
else if (prev_prio == 1)
|
|
|
|
{
|
|
|
|
// if this piece was one of the vectors that was pushed to the
|
|
|
|
// top, adjust the prev_prio to point to that vector, so that
|
|
|
|
// the pieces are moved from there
|
|
|
|
prev_prio = pushed_out_index + 2;
|
|
|
|
}
|
2007-04-15 04:14:02 +02:00
|
|
|
move(prev_prio - 2, i->index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-12-18 04:30:41 +01:00
|
|
|
void piece_picker::inc_refcount(int i)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2007-03-15 23:03:56 +01:00
|
|
|
// TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
2003-10-23 01:00:57 +02:00
|
|
|
assert(i >= 0);
|
2004-01-25 19:18:36 +01:00
|
|
|
assert(i < (int)m_piece_map.size());
|
2007-03-17 18:28:59 +01:00
|
|
|
assert(m_files_checked_called);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2005-05-25 12:01:01 +02:00
|
|
|
piece_pos& p = m_piece_map[i];
|
2007-03-15 23:03:56 +01:00
|
|
|
int index = p.index;
|
|
|
|
int prev_priority = p.priority(m_sequenced_download_threshold);
|
|
|
|
|
|
|
|
assert(p.peer_count < piece_pos::max_peer_count);
|
|
|
|
p.peer_count++;
|
|
|
|
assert(p.peer_count != 0);
|
2005-09-01 23:04:21 +02:00
|
|
|
|
|
|
|
// if we have the piece or if it's filtered
|
|
|
|
// we don't have to move any entries in the piece_info vector
|
2007-03-15 23:03:56 +01:00
|
|
|
if (p.priority(m_sequenced_download_threshold) == prev_priority) return;
|
2005-09-01 23:04:21 +02:00
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
if (prev_priority == 0)
|
|
|
|
{
|
|
|
|
add(i);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
move(prev_priority, index);
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
#ifndef NDEBUG
|
2004-11-21 11:49:02 +01:00
|
|
|
// integrity_check();
|
2003-10-23 01:00:57 +02:00
|
|
|
#endif
|
2003-12-18 04:30:41 +01:00
|
|
|
return;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void piece_picker::dec_refcount(int i)
|
|
|
|
{
|
2007-03-15 23:03:56 +01:00
|
|
|
// TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
2004-12-21 13:30:09 +01:00
|
|
|
|
2007-03-17 18:28:59 +01:00
|
|
|
assert(m_files_checked_called);
|
2003-10-23 01:00:57 +02:00
|
|
|
assert(i >= 0);
|
2004-01-25 19:18:36 +01:00
|
|
|
assert(i < (int)m_piece_map.size());
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2005-05-25 12:01:01 +02:00
|
|
|
piece_pos& p = m_piece_map[i];
|
2007-03-15 23:03:56 +01:00
|
|
|
int prev_priority = p.priority(m_sequenced_download_threshold);
|
|
|
|
int index = p.index;
|
|
|
|
assert(p.peer_count > 0);
|
2005-09-01 23:04:21 +02:00
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
if (p.peer_count > 0)
|
|
|
|
p.peer_count--;
|
2005-09-01 23:04:21 +02:00
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
if (p.priority(m_sequenced_download_threshold) == prev_priority) return;
|
|
|
|
|
|
|
|
move(prev_priority, index);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2005-05-25 12:01:01 +02:00
|
|
|
// this is used to indicate that we succesfully have
|
|
|
|
// downloaded a piece, and that no further attempts
|
|
|
|
// to pick that piece should be made. The piece will
|
|
|
|
// be removed from the available piece list.
|
2003-10-23 01:00:57 +02:00
|
|
|
void piece_picker::we_have(int index)
|
|
|
|
{
|
2006-07-16 02:08:50 +02:00
|
|
|
TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
2004-01-26 01:21:12 +01:00
|
|
|
assert(index >= 0);
|
|
|
|
assert(index < (int)m_piece_map.size());
|
|
|
|
|
2005-05-25 12:01:01 +02:00
|
|
|
piece_pos& p = m_piece_map[index];
|
2007-03-15 23:03:56 +01:00
|
|
|
int info_index = p.index;
|
|
|
|
int priority = p.priority(m_sequenced_download_threshold);
|
|
|
|
|
|
|
|
assert(p.downloading == 1);
|
|
|
|
assert(!p.have());
|
2007-03-30 05:09:28 +02:00
|
|
|
|
|
|
|
std::vector<downloading_piece>::iterator i
|
|
|
|
= std::find_if(m_downloads.begin()
|
|
|
|
, m_downloads.end()
|
|
|
|
, has_index(index));
|
|
|
|
assert(i != m_downloads.end());
|
|
|
|
m_downloads.erase(i);
|
|
|
|
p.downloading = 0;
|
|
|
|
|
2007-03-30 21:18:03 +02:00
|
|
|
assert(std::find_if(m_downloads.begin(), m_downloads.end()
|
2007-03-30 05:09:28 +02:00
|
|
|
, has_index(index)) == m_downloads.end());
|
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
if (p.filtered())
|
2005-05-30 19:43:03 +02:00
|
|
|
{
|
|
|
|
--m_num_filtered;
|
|
|
|
++m_num_have_filtered;
|
|
|
|
}
|
2007-03-15 23:03:56 +01:00
|
|
|
if (p.have()) return;
|
|
|
|
p.set_have();
|
2007-03-30 22:21:29 +02:00
|
|
|
if (priority == 0) return;
|
2007-03-15 23:03:56 +01:00
|
|
|
assert(p.priority(m_sequenced_download_threshold) == 0);
|
|
|
|
move(priority, info_index);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2005-05-25 12:01:01 +02:00
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
void piece_picker::set_piece_priority(int index, int new_piece_priority)
|
2005-05-25 12:01:01 +02:00
|
|
|
{
|
2006-07-16 02:08:50 +02:00
|
|
|
TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
2007-03-15 23:03:56 +01:00
|
|
|
assert(new_piece_priority >= 0);
|
2007-03-20 02:59:00 +01:00
|
|
|
assert(new_piece_priority <= 7);
|
2005-05-25 12:01:01 +02:00
|
|
|
assert(index >= 0);
|
|
|
|
assert(index < (int)m_piece_map.size());
|
2005-05-29 19:25:13 +02:00
|
|
|
|
2005-05-25 12:01:01 +02:00
|
|
|
piece_pos& p = m_piece_map[index];
|
2007-03-21 03:09:50 +01:00
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
// if the priority isn't changed, don't do anything
|
|
|
|
if (new_piece_priority == int(p.piece_priority)) return;
|
|
|
|
|
2007-03-21 03:09:50 +01:00
|
|
|
int prev_priority = p.priority(m_sequenced_download_threshold);
|
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
if (new_piece_priority == piece_pos::filter_priority
|
|
|
|
&& p.piece_priority != piece_pos::filter_priority)
|
2005-05-30 19:43:03 +02:00
|
|
|
{
|
2007-03-15 23:03:56 +01:00
|
|
|
// the piece just got filtered
|
|
|
|
if (p.have()) ++m_num_have_filtered;
|
|
|
|
else ++m_num_filtered;
|
|
|
|
}
|
|
|
|
else if (new_piece_priority != piece_pos::filter_priority
|
|
|
|
&& p.piece_priority == piece_pos::filter_priority)
|
|
|
|
{
|
|
|
|
// the piece just got unfiltered
|
|
|
|
if (p.have()) --m_num_have_filtered;
|
|
|
|
else --m_num_filtered;
|
|
|
|
}
|
|
|
|
assert(m_num_filtered >= 0);
|
|
|
|
assert(m_num_have_filtered >= 0);
|
|
|
|
|
|
|
|
p.piece_priority = new_piece_priority;
|
|
|
|
int new_priority = p.priority(m_sequenced_download_threshold);
|
|
|
|
|
|
|
|
if (new_priority == prev_priority) return;
|
|
|
|
|
|
|
|
if (prev_priority == 0)
|
|
|
|
{
|
|
|
|
add(index);
|
2005-05-30 19:43:03 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2007-03-15 23:03:56 +01:00
|
|
|
move(prev_priority, p.index);
|
2005-05-30 19:43:03 +02:00
|
|
|
}
|
2005-05-25 12:01:01 +02:00
|
|
|
}
|
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
int piece_picker::piece_priority(int index) const
|
2005-05-25 12:01:01 +02:00
|
|
|
{
|
|
|
|
assert(index >= 0);
|
|
|
|
assert(index < (int)m_piece_map.size());
|
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
return m_piece_map[index].piece_priority;
|
2005-05-25 12:01:01 +02:00
|
|
|
}
|
|
|
|
|
2007-03-20 02:59:00 +01:00
|
|
|
void piece_picker::piece_priorities(std::vector<int>& pieces) const
|
|
|
|
{
|
|
|
|
pieces.resize(m_piece_map.size());
|
|
|
|
std::vector<int>::iterator j = pieces.begin();
|
|
|
|
for (std::vector<piece_pos>::const_iterator i = m_piece_map.begin(),
|
|
|
|
end(m_piece_map.end()); i != end; ++i, ++j)
|
|
|
|
{
|
|
|
|
*j = i->piece_priority;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ============ start deprecation ==============
|
|
|
|
|
2005-05-25 12:01:01 +02:00
|
|
|
void piece_picker::filtered_pieces(std::vector<bool>& mask) const
|
|
|
|
{
|
|
|
|
mask.resize(m_piece_map.size());
|
|
|
|
std::vector<bool>::iterator j = mask.begin();
|
|
|
|
for (std::vector<piece_pos>::const_iterator i = m_piece_map.begin(),
|
|
|
|
end(m_piece_map.end()); i != end; ++i, ++j)
|
|
|
|
{
|
2007-03-15 23:03:56 +01:00
|
|
|
*j = i->filtered();
|
2005-05-25 12:01:01 +02:00
|
|
|
}
|
|
|
|
}
|
2007-03-20 02:59:00 +01:00
|
|
|
|
|
|
|
// ============ end deprecation ==============
|
2007-04-27 02:27:37 +02:00
|
|
|
|
|
|
|
// pieces describes which pieces the peer we're requesting from
|
|
|
|
// has.
|
|
|
|
// interesting_blocks is an out parameter, and will be filled
|
|
|
|
// with (up to) num_blocks of interesting blocks that the peer has.
|
|
|
|
// prefer_whole_pieces can be set if this peer should download
|
|
|
|
// whole pieces rather than trying to download blocks from the
|
|
|
|
// same piece as other peers.
|
|
|
|
// the endpoint is the address of the peer we're picking pieces
|
|
|
|
// from. This is used when downloading whole pieces, to only
|
|
|
|
// pick from the same piece the same peer is downloading from.
|
|
|
|
// state is supposed to be set to fast if the peer is downloading
|
|
|
|
// relatively fast, by some notion. Slow peers will prefer not
|
|
|
|
// to pick blocks from the same pieces as fast peers, and vice
|
|
|
|
// versa. Downloading pieces are marked as being fast, medium
|
|
|
|
// or slow once they're started.
|
2005-05-25 12:01:01 +02:00
|
|
|
void piece_picker::pick_pieces(const std::vector<bool>& pieces
|
2005-08-15 00:04:58 +02:00
|
|
|
, std::vector<piece_block>& interesting_blocks
|
|
|
|
, int num_blocks, bool prefer_whole_pieces
|
2007-04-27 02:27:37 +02:00
|
|
|
, tcp::endpoint peer, piece_state_t speed) const
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2006-07-16 02:08:50 +02:00
|
|
|
TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
2004-01-26 01:21:12 +01:00
|
|
|
assert(num_blocks > 0);
|
2003-10-23 01:00:57 +02:00
|
|
|
assert(pieces.size() == m_piece_map.size());
|
2007-03-17 18:28:59 +01:00
|
|
|
assert(m_files_checked_called);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2005-02-23 17:56:32 +01:00
|
|
|
assert(m_piece_info.begin() != m_piece_info.end());
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2007-04-27 02:27:37 +02:00
|
|
|
// this will be filled with blocks that we should not request
|
|
|
|
// unless we can't find num_blocks among the other ones.
|
|
|
|
// blocks that belong to pieces with a mismatching speed
|
|
|
|
// category for instance, or if we prefer whole pieces,
|
|
|
|
// blocks belonging to a piece that others have
|
|
|
|
// downloaded to
|
2005-08-15 00:04:58 +02:00
|
|
|
std::vector<piece_block> backup_blocks;
|
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
// this loop will loop from pieces with priority 1 and up
|
2005-08-15 00:04:58 +02:00
|
|
|
// until we either reach the end of the piece list or
|
|
|
|
// has filled the interesting_blocks with num_blocks
|
|
|
|
// blocks.
|
2007-03-15 23:03:56 +01:00
|
|
|
|
2006-04-25 23:04:48 +02:00
|
|
|
// 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.
|
2005-08-15 00:04:58 +02:00
|
|
|
|
2007-04-27 02:27:37 +02:00
|
|
|
// +1 is to ignore pieces that no peer has. The bucket with index 0 contains
|
|
|
|
// pieces that 0 other peers have. bucket will point to a bucket with
|
|
|
|
// pieces with the same priority. It will be iterated in priority
|
|
|
|
// order (high priority/rare pices first). The content of each
|
|
|
|
// bucket is randomized
|
|
|
|
for (std::vector<std::vector<int> >::const_iterator bucket
|
|
|
|
= m_piece_info.begin() + 1; bucket != m_piece_info.end();
|
|
|
|
++bucket)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2007-04-27 02:27:37 +02:00
|
|
|
if (bucket->empty()) continue;
|
|
|
|
num_blocks = add_interesting_blocks(*bucket, pieces
|
2007-03-15 23:03:56 +01:00
|
|
|
, interesting_blocks, backup_blocks, num_blocks
|
2007-04-27 02:27:37 +02:00
|
|
|
, prefer_whole_pieces, peer, speed);
|
2007-03-15 23:03:56 +01:00
|
|
|
assert(num_blocks >= 0);
|
|
|
|
if (num_blocks == 0) return;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2005-08-15 00:04:58 +02:00
|
|
|
|
|
|
|
assert(num_blocks > 0);
|
|
|
|
|
2007-04-27 02:27:37 +02:00
|
|
|
if (!backup_blocks.empty())
|
|
|
|
interesting_blocks.insert(interesting_blocks.end()
|
|
|
|
, backup_blocks.begin(), backup_blocks.begin()
|
|
|
|
+ (std::min)(num_blocks, (int)backup_blocks.size()));
|
2005-08-15 00:04:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
bool exclusively_requested_from(piece_picker::downloading_piece const& p
|
2006-04-25 23:04:48 +02:00
|
|
|
, int num_blocks_in_piece, tcp::endpoint peer)
|
2005-08-15 00:04:58 +02:00
|
|
|
{
|
|
|
|
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
|
2006-04-25 23:04:48 +02:00
|
|
|
&& p.info[j].peer != tcp::endpoint())
|
2005-08-15 00:04:58 +02:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
int piece_picker::add_interesting_blocks(std::vector<int> const& piece_list
|
2005-08-15 00:04:58 +02:00
|
|
|
, std::vector<bool> const& pieces
|
|
|
|
, std::vector<piece_block>& interesting_blocks
|
|
|
|
, std::vector<piece_block>& backup_blocks
|
|
|
|
, int num_blocks, bool prefer_whole_pieces
|
2007-04-27 02:27:37 +02:00
|
|
|
, tcp::endpoint peer, piece_state_t speed) const
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
|
|
|
for (std::vector<int>::const_iterator i = piece_list.begin();
|
2005-05-13 02:39:39 +02:00
|
|
|
i != piece_list.end(); ++i)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-01-25 23:41:55 +01:00
|
|
|
assert(*i >= 0);
|
2004-01-25 19:18:36 +01:00
|
|
|
assert(*i < (int)m_piece_map.size());
|
2007-03-15 23:03:56 +01:00
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
// if the peer doesn't have the piece
|
|
|
|
// skip it
|
|
|
|
if (!pieces[*i]) continue;
|
|
|
|
|
2003-11-02 22:06:50 +01:00
|
|
|
int num_blocks_in_piece = blocks_in_piece(*i);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
if (m_piece_map[*i].downloading == 1)
|
2005-08-15 00:04:58 +02:00
|
|
|
{
|
2007-03-15 23:03:56 +01:00
|
|
|
std::vector<downloading_piece>::const_iterator p
|
|
|
|
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(*i));
|
|
|
|
assert(p != m_downloads.end());
|
|
|
|
|
2007-04-27 04:33:59 +02:00
|
|
|
// is true if all the other pieces that are currently
|
|
|
|
// requested from this piece are from the same
|
|
|
|
// peer as 'peer'.
|
|
|
|
bool only_same_peer = exclusively_requested_from(*p
|
|
|
|
, num_blocks_in_piece, peer);
|
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
// 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.
|
2007-04-27 04:33:59 +02:00
|
|
|
if (prefer_whole_pieces && !only_same_peer)
|
2007-03-15 23:03:56 +01:00
|
|
|
{
|
2007-04-27 02:27:37 +02:00
|
|
|
if (int(backup_blocks.size()) >= num_blocks) continue;
|
2007-03-15 23:03:56 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2005-08-15 00:04:58 +02:00
|
|
|
for (int j = 0; j < num_blocks_in_piece; ++j)
|
|
|
|
{
|
2007-04-27 02:27:37 +02:00
|
|
|
// ignore completed blocks
|
2005-08-15 00:04:58 +02:00
|
|
|
if (p->finished_blocks[j] == 1) continue;
|
2007-04-27 02:27:37 +02:00
|
|
|
// ignore blocks requested from this peer already
|
2005-08-15 00:04:58 +02:00
|
|
|
if (p->requested_blocks[j] == 1
|
|
|
|
&& p->info[j].peer == peer) continue;
|
2007-04-27 02:27:37 +02:00
|
|
|
// if the piece is fast and the peer is slow, or vice versa,
|
2007-04-27 04:33:59 +02:00
|
|
|
// add the block as a backup.
|
|
|
|
// override this behavior if all the other blocks
|
|
|
|
// have been requested from the same peer or
|
|
|
|
// if the state of the piece is none (the
|
|
|
|
// piece will in that case change state).
|
|
|
|
if (p->state != none && p->state != speed && !only_same_peer)
|
2007-04-27 02:27:37 +02:00
|
|
|
{
|
|
|
|
if (int(backup_blocks.size()) >= num_blocks) continue;
|
|
|
|
backup_blocks.push_back(piece_block(*i, j));
|
|
|
|
continue;
|
|
|
|
}
|
2007-03-15 23:03:56 +01:00
|
|
|
// 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
|
|
|
|
// to allow the requester to determine if the
|
|
|
|
// block should be requested from more than one
|
|
|
|
// peer. If it is being downloaded, we continue
|
|
|
|
// to look for blocks until we have num_blocks
|
|
|
|
// blocks that have not been requested from any
|
|
|
|
// other peer.
|
|
|
|
interesting_blocks.push_back(piece_block(*i, j));
|
|
|
|
if (p->requested_blocks[j] == 0)
|
|
|
|
{
|
|
|
|
// we have found a block that's free to download
|
|
|
|
num_blocks--;
|
2007-04-27 02:27:37 +02:00
|
|
|
// if we prefer whole pieces, continue picking from this
|
|
|
|
// piece even though we have num_blocks
|
2007-03-15 23:03:56 +01:00
|
|
|
if (prefer_whole_pieces) continue;
|
|
|
|
assert(num_blocks >= 0);
|
|
|
|
if (num_blocks == 0) return num_blocks;
|
|
|
|
}
|
2005-08-15 00:04:58 +02:00
|
|
|
}
|
2007-03-15 23:03:56 +01:00
|
|
|
assert(num_blocks >= 0 || prefer_whole_pieces);
|
|
|
|
if (num_blocks < 0) num_blocks = 0;
|
2005-08-15 00:04:58 +02:00
|
|
|
}
|
2007-03-15 23:03:56 +01:00
|
|
|
else
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2007-03-15 23:03:56 +01:00
|
|
|
if (!prefer_whole_pieces && num_blocks_in_piece > num_blocks)
|
|
|
|
num_blocks_in_piece = num_blocks;
|
|
|
|
for (int j = 0; j < num_blocks_in_piece; ++j)
|
2003-11-02 22:06:50 +01:00
|
|
|
{
|
2007-03-15 23:03:56 +01:00
|
|
|
interesting_blocks.push_back(piece_block(*i, j));
|
2003-11-02 22:06:50 +01:00
|
|
|
}
|
2007-03-15 23:03:56 +01:00
|
|
|
num_blocks -= (std::min)(num_blocks_in_piece, num_blocks);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2007-03-15 23:03:56 +01:00
|
|
|
assert(num_blocks >= 0);
|
2005-08-15 00:04:58 +02:00
|
|
|
if (num_blocks == 0) return num_blocks;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2003-11-02 22:06:50 +01:00
|
|
|
return num_blocks;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool piece_picker::is_piece_finished(int index) const
|
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
assert(index < (int)m_piece_map.size());
|
2003-10-23 01:00:57 +02:00
|
|
|
assert(index >= 0);
|
|
|
|
|
2006-12-22 01:45:43 +01:00
|
|
|
if (m_piece_map[index].downloading == 0)
|
|
|
|
{
|
2007-03-30 21:18:03 +02:00
|
|
|
assert(std::find_if(m_downloads.begin(), m_downloads.end()
|
|
|
|
, has_index(index)) == m_downloads.end());
|
2006-12-22 01:45:43 +01:00
|
|
|
return false;
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
std::vector<downloading_piece>::const_iterator i
|
|
|
|
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index));
|
|
|
|
assert(i != m_downloads.end());
|
2004-01-25 19:18:36 +01:00
|
|
|
assert((int)i->finished_blocks.count() <= m_blocks_per_piece);
|
2003-10-30 00:28:09 +01:00
|
|
|
int max_blocks = blocks_in_piece(index);
|
2006-12-21 23:20:28 +01:00
|
|
|
if ((int)i->finished_blocks.count() < max_blocks) return false;
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-26 11:29:00 +01:00
|
|
|
assert((int)i->requested_blocks.count() == max_blocks);
|
2003-10-23 01:00:57 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool piece_picker::is_downloading(piece_block block) const
|
|
|
|
{
|
2004-01-25 23:41:55 +01:00
|
|
|
assert(block.piece_index >= 0);
|
|
|
|
assert(block.block_index >= 0);
|
2004-01-25 19:18:36 +01:00
|
|
|
assert(block.piece_index < (int)m_piece_map.size());
|
2004-01-25 23:41:55 +01:00
|
|
|
assert(block.block_index < (int)max_blocks_per_piece);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
if (m_piece_map[block.piece_index].downloading == 0) return false;
|
|
|
|
std::vector<downloading_piece>::const_iterator i
|
2004-01-13 04:08:59 +01:00
|
|
|
= std::find_if(
|
|
|
|
m_downloads.begin()
|
|
|
|
, m_downloads.end()
|
|
|
|
, has_index(block.piece_index));
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
assert(i != m_downloads.end());
|
|
|
|
return i->requested_blocks[block.block_index];
|
|
|
|
}
|
|
|
|
|
2003-11-05 00:27:06 +01:00
|
|
|
bool piece_picker::is_finished(piece_block block) const
|
|
|
|
{
|
2004-01-25 23:41:55 +01:00
|
|
|
assert(block.piece_index >= 0);
|
|
|
|
assert(block.block_index >= 0);
|
2004-01-25 19:18:36 +01:00
|
|
|
assert(block.piece_index < (int)m_piece_map.size());
|
2004-01-25 23:41:55 +01:00
|
|
|
assert(block.block_index < (int)max_blocks_per_piece);
|
2003-11-05 00:27:06 +01:00
|
|
|
|
2005-05-25 12:01:01 +02:00
|
|
|
if (m_piece_map[block.piece_index].index == piece_pos::we_have_index) return true;
|
2003-11-05 00:27:06 +01:00
|
|
|
if (m_piece_map[block.piece_index].downloading == 0) return false;
|
|
|
|
std::vector<downloading_piece>::const_iterator i
|
|
|
|
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index));
|
|
|
|
assert(i != m_downloads.end());
|
|
|
|
return i->finished_blocks[block.block_index];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-27 02:27:37 +02:00
|
|
|
void piece_picker::mark_as_downloading(piece_block block
|
|
|
|
, const tcp::endpoint& peer, piece_state_t state)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2006-07-16 02:08:50 +02:00
|
|
|
TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
|
|
|
|
2004-01-25 23:41:55 +01:00
|
|
|
assert(block.piece_index >= 0);
|
|
|
|
assert(block.block_index >= 0);
|
2004-01-25 19:18:36 +01:00
|
|
|
assert(block.piece_index < (int)m_piece_map.size());
|
2003-11-05 00:27:06 +01:00
|
|
|
assert(block.block_index < blocks_in_piece(block.piece_index));
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
piece_pos& p = m_piece_map[block.piece_index];
|
|
|
|
if (p.downloading == 0)
|
|
|
|
{
|
2007-03-15 23:03:56 +01:00
|
|
|
int prio = p.priority(m_sequenced_download_threshold);
|
2003-10-23 01:00:57 +02:00
|
|
|
p.downloading = 1;
|
2007-03-15 23:03:56 +01:00
|
|
|
move(prio, p.index);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
downloading_piece dp;
|
2007-04-27 02:27:37 +02:00
|
|
|
dp.state = state;
|
2003-10-23 01:00:57 +02:00
|
|
|
dp.index = block.piece_index;
|
|
|
|
dp.requested_blocks[block.block_index] = 1;
|
2003-11-02 22:06:50 +01:00
|
|
|
dp.info[block.block_index].peer = peer;
|
2003-10-23 01:00:57 +02:00
|
|
|
m_downloads.push_back(dp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::vector<downloading_piece>::iterator i
|
|
|
|
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index));
|
|
|
|
assert(i != m_downloads.end());
|
|
|
|
assert(i->requested_blocks[block.block_index] == 0);
|
2003-11-02 22:06:50 +01:00
|
|
|
i->info[block.block_index].peer = peer;
|
2003-10-23 01:00:57 +02:00
|
|
|
i->requested_blocks[block.block_index] = 1;
|
2007-04-27 02:27:37 +02:00
|
|
|
if (i->state == none) i->state = state;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-25 23:04:48 +02:00
|
|
|
void piece_picker::mark_as_finished(piece_block block, const tcp::endpoint& peer)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2006-07-16 02:08:50 +02:00
|
|
|
TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
|
|
|
|
2004-01-25 23:41:55 +01:00
|
|
|
assert(block.piece_index >= 0);
|
|
|
|
assert(block.block_index >= 0);
|
2004-01-25 19:18:36 +01:00
|
|
|
assert(block.piece_index < (int)m_piece_map.size());
|
2003-11-05 00:27:06 +01:00
|
|
|
assert(block.block_index < blocks_in_piece(block.piece_index));
|
|
|
|
|
|
|
|
piece_pos& p = m_piece_map[block.piece_index];
|
2007-03-15 23:03:56 +01:00
|
|
|
int prio = p.priority(m_sequenced_download_threshold);
|
2005-05-25 12:01:01 +02:00
|
|
|
|
2003-11-05 00:27:06 +01:00
|
|
|
if (p.downloading == 0)
|
|
|
|
{
|
|
|
|
p.downloading = 1;
|
2007-03-27 10:53:48 +02:00
|
|
|
if (prio > 0) move(prio, p.index);
|
2007-04-18 22:40:41 +02:00
|
|
|
else assert(p.priority(m_sequenced_download_threshold) == 0);
|
2003-11-05 00:27:06 +01:00
|
|
|
|
|
|
|
downloading_piece dp;
|
2007-04-27 02:27:37 +02:00
|
|
|
dp.state = none;
|
2003-11-05 00:27:06 +01:00
|
|
|
dp.index = block.piece_index;
|
|
|
|
dp.requested_blocks[block.block_index] = 1;
|
|
|
|
dp.finished_blocks[block.block_index] = 1;
|
|
|
|
dp.info[block.block_index].peer = peer;
|
|
|
|
m_downloads.push_back(dp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::vector<downloading_piece>::iterator i
|
|
|
|
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index));
|
|
|
|
assert(i != m_downloads.end());
|
|
|
|
i->info[block.block_index].peer = peer;
|
|
|
|
i->requested_blocks[block.block_index] = 1;
|
|
|
|
i->finished_blocks[block.block_index] = 1;
|
2007-04-27 02:27:37 +02:00
|
|
|
|
|
|
|
// TODO: maintain requested and finished counters so that
|
|
|
|
// we don't have to count every time
|
|
|
|
bool blocks_requested = false;
|
|
|
|
int num_blocks = blocks_in_piece(i->index);
|
|
|
|
for (int k = 0; k < num_blocks; ++k)
|
|
|
|
{
|
|
|
|
if (i->finished_blocks[k]) continue;
|
|
|
|
if (i->requested_blocks[k])
|
|
|
|
{
|
|
|
|
blocks_requested = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!blocks_requested)
|
|
|
|
{
|
|
|
|
// there are no blocks requested in this piece.
|
|
|
|
// remove the fast/slow state from it
|
|
|
|
i->state = none;
|
|
|
|
}
|
2003-11-05 00:27:06 +01:00
|
|
|
}
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2006-04-25 23:04:48 +02:00
|
|
|
void piece_picker::get_downloaders(std::vector<tcp::endpoint>& d, int index) const
|
2003-11-02 22:06:50 +01:00
|
|
|
{
|
2004-01-25 23:41:55 +01:00
|
|
|
assert(index >= 0 && index <= (int)m_piece_map.size());
|
2004-01-13 04:08:59 +01:00
|
|
|
std::vector<downloading_piece>::const_iterator i
|
2003-11-02 22:06:50 +01:00
|
|
|
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index));
|
|
|
|
assert(i != m_downloads.end());
|
|
|
|
|
|
|
|
d.clear();
|
|
|
|
for (int j = 0; j < blocks_in_piece(index); ++j)
|
|
|
|
{
|
|
|
|
d.push_back(i->info[j].peer);
|
|
|
|
}
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2006-04-25 23:04:48 +02:00
|
|
|
boost::optional<tcp::endpoint> piece_picker::get_downloader(piece_block block) const
|
2004-01-13 04:08:59 +01:00
|
|
|
{
|
|
|
|
std::vector<downloading_piece>::const_iterator i = std::find_if(
|
|
|
|
m_downloads.begin()
|
|
|
|
, m_downloads.end()
|
|
|
|
, has_index(block.piece_index));
|
|
|
|
|
|
|
|
if (i == m_downloads.end())
|
2006-04-25 23:04:48 +02:00
|
|
|
return boost::optional<tcp::endpoint>();
|
2004-01-13 04:08:59 +01:00
|
|
|
|
|
|
|
assert(block.block_index < max_blocks_per_piece);
|
|
|
|
assert(block.block_index >= 0);
|
|
|
|
|
|
|
|
if (i->requested_blocks[block.block_index] == false
|
|
|
|
|| i->finished_blocks[block.block_index] == true)
|
2006-04-25 23:04:48 +02:00
|
|
|
return boost::optional<tcp::endpoint>();
|
2004-01-13 04:08:59 +01:00
|
|
|
|
2006-04-25 23:04:48 +02:00
|
|
|
return boost::optional<tcp::endpoint>(i->info[block.block_index].peer);
|
2004-01-13 04:08:59 +01:00
|
|
|
}
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
void piece_picker::abort_download(piece_block block)
|
|
|
|
{
|
2006-07-16 02:08:50 +02:00
|
|
|
TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-25 23:41:55 +01:00
|
|
|
assert(block.piece_index >= 0);
|
|
|
|
assert(block.block_index >= 0);
|
2004-01-25 19:18:36 +01:00
|
|
|
assert(block.piece_index < (int)m_piece_map.size());
|
2004-01-25 23:41:55 +01:00
|
|
|
assert(block.block_index < blocks_in_piece(block.piece_index));
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
if (m_piece_map[block.piece_index].downloading == 0)
|
|
|
|
{
|
2007-03-30 21:18:03 +02:00
|
|
|
assert(std::find_if(m_downloads.begin(), m_downloads.end()
|
|
|
|
, has_index(block.piece_index)) == m_downloads.end());
|
2003-10-23 01:00:57 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-03-30 21:18:03 +02:00
|
|
|
std::vector<downloading_piece>::iterator i = std::find_if(m_downloads.begin()
|
|
|
|
, m_downloads.end(), has_index(block.piece_index));
|
2003-10-23 01:00:57 +02:00
|
|
|
assert(i != m_downloads.end());
|
2003-11-05 00:27:06 +01:00
|
|
|
|
2007-03-30 21:18:03 +02:00
|
|
|
if (i->finished_blocks[block.block_index])
|
2006-04-25 23:04:48 +02:00
|
|
|
{
|
2007-05-03 21:25:18 +02:00
|
|
|
assert(i->requested_blocks[block.block_index]);
|
2007-03-30 21:18:03 +02:00
|
|
|
assert(std::find_if(m_downloads.begin(), m_downloads.end()
|
|
|
|
, has_index(block.piece_index)) == m_downloads.end());
|
|
|
|
return;
|
2006-04-25 23:04:48 +02:00
|
|
|
}
|
2007-03-30 21:18:03 +02:00
|
|
|
|
|
|
|
assert(block.block_index < blocks_in_piece(block.piece_index));
|
|
|
|
assert(i->requested_blocks[block.block_index]);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
// clear this block as being downloaded
|
2006-12-15 11:42:56 +01:00
|
|
|
i->requested_blocks[block.block_index] = false;
|
|
|
|
|
|
|
|
// clear the downloader of this block
|
|
|
|
i->info[block.block_index].peer = tcp::endpoint();
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2006-12-15 03:26:11 +01:00
|
|
|
// if there are no other blocks in this piece
|
2003-10-23 01:00:57 +02:00
|
|
|
// that's being downloaded, remove it from the list
|
|
|
|
if (i->requested_blocks.count() == 0)
|
|
|
|
{
|
|
|
|
m_downloads.erase(i);
|
2005-05-25 12:01:01 +02:00
|
|
|
piece_pos& p = m_piece_map[block.piece_index];
|
2007-03-15 23:03:56 +01:00
|
|
|
int prio = p.priority(m_sequenced_download_threshold);
|
|
|
|
p.downloading = 0;
|
2007-05-03 21:25:18 +02:00
|
|
|
if (prio > 0) move(prio, p.index);
|
2007-03-29 01:23:07 +02:00
|
|
|
|
2007-03-30 21:18:03 +02:00
|
|
|
assert(std::find_if(m_downloads.begin(), m_downloads.end()
|
|
|
|
, has_index(block.piece_index)) == m_downloads.end());
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2007-04-27 18:26:30 +02:00
|
|
|
else
|
2007-04-27 02:27:37 +02:00
|
|
|
{
|
2007-04-27 18:26:30 +02:00
|
|
|
// TODO: maintain requested and finished counters so that
|
|
|
|
// we don't have to count every time
|
|
|
|
bool blocks_requested = false;
|
|
|
|
int num_blocks = blocks_in_piece(i->index);
|
|
|
|
for (int k = 0; k < num_blocks; ++k)
|
2007-04-27 02:27:37 +02:00
|
|
|
{
|
2007-04-27 18:26:30 +02:00
|
|
|
if (i->finished_blocks[k]) continue;
|
|
|
|
if (i->requested_blocks[k])
|
|
|
|
{
|
|
|
|
blocks_requested = true;
|
|
|
|
break;
|
|
|
|
}
|
2007-04-27 02:27:37 +02:00
|
|
|
}
|
|
|
|
|
2007-04-27 18:26:30 +02:00
|
|
|
if (!blocks_requested)
|
|
|
|
{
|
|
|
|
// there are no blocks requested in this piece.
|
|
|
|
// remove the fast/slow state from it
|
|
|
|
i->state = none;
|
|
|
|
}
|
2007-04-27 02:27:37 +02:00
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2003-10-30 00:28:09 +01:00
|
|
|
int piece_picker::unverified_blocks() const
|
|
|
|
{
|
|
|
|
int counter = 0;
|
|
|
|
for (std::vector<downloading_piece>::const_iterator i = m_downloads.begin();
|
2005-05-13 02:39:39 +02:00
|
|
|
i != m_downloads.end(); ++i)
|
2003-10-30 00:28:09 +01:00
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
counter += (int)i->finished_blocks.count();
|
2003-10-30 00:28:09 +01:00
|
|
|
}
|
|
|
|
return counter;
|
|
|
|
}
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2005-05-13 02:39:39 +02:00
|
|
|
|