introduce a prio_index_t type (#1453)

* introduce a prio_index_t type for indexing into the priority sorted piece list, m_pieces, in piece_picker

* some piece picker simplification

* fix msvc warning for prio_index_t
This commit is contained in:
Arvid Norberg 2016-12-26 08:25:50 -08:00 committed by GitHub
parent 5ed0086b51
commit 42a27b3ebc
4 changed files with 180 additions and 113 deletions

View File

@ -66,6 +66,30 @@ namespace libtorrent { namespace aux {
{ return IndexType(static_cast<underlying_index>(this->size())); } { return IndexType(static_cast<underlying_index>(this->size())); }
}; };
template <typename Iter>
struct iterator_range
{
Iter _begin, _end;
Iter begin() { return _begin; }
Iter end() { return _end; }
};
template <typename T, typename IndexType>
iterator_range<T*> range(vector<T, IndexType>& vec
, IndexType begin, IndexType end)
{
using type = typename IndexType::underlying_type;
return { vec.data() + static_cast<type>(begin), vec.data() + static_cast<type>(end) };
}
template <typename T, typename IndexType>
iterator_range<T const*> range(vector<T, IndexType> const& vec
, IndexType begin, IndexType end)
{
using type = typename IndexType::underlying_type;
return { vec.data() + static_cast<type>(begin), vec.data() + static_cast<type>(end) };
}
}} }}
#endif #endif

View File

@ -67,6 +67,9 @@ namespace libtorrent
struct counters; struct counters;
struct torrent_peer; struct torrent_peer;
struct prio_index_tag_t {};
using prio_index_t = aux::strong_typedef<int, prio_index_tag_t>;
class TORRENT_EXTRA_EXPORT piece_picker class TORRENT_EXTRA_EXPORT piece_picker
{ {
public: public:
@ -405,7 +408,7 @@ namespace libtorrent
#if TORRENT_USE_INVARIANT_CHECKS #if TORRENT_USE_INVARIANT_CHECKS
void check_piece_state() const; void check_piece_state() const;
// used in debug mode // used in debug mode
void verify_priority(int start, int end, int prio) const; void verify_priority(prio_index_t start, prio_index_t end, int prio) const;
void verify_pick(std::vector<piece_block> const& picked void verify_pick(std::vector<piece_block> const& picked
, typed_bitfield<piece_index_t> const& bits) const; , typed_bitfield<piece_index_t> const& bits) const;
@ -569,19 +572,29 @@ namespace libtorrent
std::uint32_t piece_priority : 3; std::uint32_t piece_priority : 3;
// index in to the piece_info vector // index in to the piece_info vector
std::uint32_t index; prio_index_t index;
#ifdef TORRENT_DEBUG_REFCOUNTS #ifdef TORRENT_DEBUG_REFCOUNTS
// all the peers that have this piece // all the peers that have this piece
std::set<const torrent_peer*> have_peers; std::set<const torrent_peer*> have_peers;
#endif #endif
#ifdef _MSC_VER
#pragma warning(push, 1)
#pragma warning(disable : 4268)
#endif
// index is set to this to indicate that we have the
// piece. There is no entry for the piece in the
// buckets if this is the case.
constexpr static prio_index_t we_have_index{-1};
#ifdef _MSC_VER
#pragma warning(pop)
#endif
enum : std::uint32_t enum : std::uint32_t
{ {
// index is set to this to indicate that we have the
// piece. There is no entry for the piece in the
// buckets if this is the case.
we_have_index = 0xffffffff,
// the priority value that means the piece is filtered // the priority value that means the piece is filtered
filter_priority = 0, filter_priority = 0,
// the max number the peer count can hold // the max number the peer count can hold
@ -590,7 +603,7 @@ namespace libtorrent
bool have() const { return index == we_have_index; } bool have() const { return index == we_have_index; }
void set_have() { index = we_have_index; TORRENT_ASSERT(have()); } void set_have() { index = we_have_index; TORRENT_ASSERT(have()); }
void set_not_have() { index = 0; TORRENT_ASSERT(!have()); } void set_not_have() { index = prio_index_t(0); TORRENT_ASSERT(!have()); }
bool downloading() const { return download_state != piece_open; } bool downloading() const { return download_state != piece_open; }
bool filtered() const { return piece_priority == filter_priority; } bool filtered() const { return piece_priority == filter_priority; }
@ -663,20 +676,23 @@ namespace libtorrent
void update_pieces() const; void update_pieces() const;
prio_index_t priority_begin(int prio) const;
prio_index_t priority_end(int prio) const;
// fills in the range [start, end) of pieces in // fills in the range [start, end) of pieces in
// m_pieces that have priority 'prio' // m_pieces that have priority 'prio'
std::pair<int, int> priority_range(int prio); std::pair<prio_index_t, prio_index_t> priority_range(int prio) const;
// adds the piece 'index' to m_pieces // adds the piece 'index' to m_pieces
void add(piece_index_t index); void add(piece_index_t index);
// removes the piece with the given priority and the // removes the piece with the given priority and the
// elem_index in the m_pieces vector // elem_index in the m_pieces vector
void remove(int priority, int elem_index); void remove(int priority, prio_index_t elem_index);
// updates the position of the piece with the given // updates the position of the piece with the given
// priority and the elem_index in the m_pieces vector // priority and the elem_index in the m_pieces vector
void update(int priority, int elem_index); void update(int priority, prio_index_t elem_index);
// shuffles the given piece inside it's priority range // shuffles the given piece inside it's priority range
void shuffle(int priority, int elem_index); void shuffle(int priority, prio_index_t elem_index);
std::vector<downloading_piece>::iterator add_download_piece(piece_index_t index); std::vector<downloading_piece>::iterator add_download_piece(piece_index_t index);
void erase_download_piece(std::vector<downloading_piece>::iterator i); void erase_download_piece(std::vector<downloading_piece>::iterator i);
@ -713,12 +729,12 @@ namespace libtorrent
// this vector contains all piece indices that are pickable // this vector contains all piece indices that are pickable
// sorted by priority. Pieces are in random random order // sorted by priority. Pieces are in random random order
// among pieces with the same priority // among pieces with the same priority
mutable std::vector<piece_index_t> m_pieces; mutable aux::vector<piece_index_t, prio_index_t> m_pieces;
// these are indices to the priority boundaries inside // these are indices to the priority boundaries inside
// the m_pieces vector. priority 0 always start at // the m_pieces vector. priority 0 always start at
// 0, priority 1 starts at m_priority_boundaries[0] etc. // 0, priority 1 starts at m_priority_boundaries[0] etc.
mutable std::vector<int> m_priority_boundaries; mutable std::vector<prio_index_t> m_priority_boundaries;
// each piece that's currently being downloaded has an entry in this list // each piece that's currently being downloaded has an entry in this list
// with block allocations. i.e. it says which parts of the piece that is // with block allocations. i.e. it says which parts of the piece that is

View File

@ -44,11 +44,15 @@ POSSIBILITY OF SUCH DAMAGE.
namespace libtorrent { namespace libtorrent {
namespace aux { namespace aux {
template <typename Tag>
struct difference_tag {};
template<typename UnderlyingType, typename Tag template<typename UnderlyingType, typename Tag
, typename Cond = typename std::enable_if<std::is_integral<UnderlyingType>::value>::type> , typename Cond = typename std::enable_if<std::is_integral<UnderlyingType>::value>::type>
struct strong_typedef struct strong_typedef
{ {
using underlying_type = UnderlyingType; using underlying_type = UnderlyingType;
using diff_type = strong_typedef<UnderlyingType, difference_tag<Tag>>;
constexpr strong_typedef(strong_typedef const& rhs) : m_val(rhs.m_val) {} constexpr strong_typedef(strong_typedef const& rhs) : m_val(rhs.m_val) {}
strong_typedef() = default; strong_typedef() = default;
@ -71,6 +75,20 @@ namespace aux {
strong_typedef operator++(int) { return strong_typedef{m_val++}; } strong_typedef operator++(int) { return strong_typedef{m_val++}; }
strong_typedef operator--(int) { return strong_typedef{m_val--}; } strong_typedef operator--(int) { return strong_typedef{m_val--}; }
friend diff_type operator-(strong_typedef lhs, strong_typedef rhs)
{ return diff_type{lhs.m_val - rhs.m_val}; }
friend strong_typedef operator+(strong_typedef lhs, diff_type rhs)
{ return strong_typedef{lhs.m_val + static_cast<UnderlyingType>(rhs)}; }
friend strong_typedef operator+(diff_type lhs, strong_typedef rhs)
{ return strong_typedef{static_cast<UnderlyingType>(lhs) + rhs.m_val}; }
friend strong_typedef operator-(strong_typedef lhs, diff_type rhs)
{ return strong_typedef{lhs.m_val - static_cast<UnderlyingType>(rhs)}; }
strong_typedef& operator+=(diff_type rhs)
{ m_val += static_cast<UnderlyingType>(rhs); return *this; }
strong_typedef& operator-=(diff_type rhs)
{ m_val -= static_cast<UnderlyingType>(rhs); return *this; }
strong_typedef& operator=(strong_typedef rhs) { m_val = rhs.m_val; return *this; } strong_typedef& operator=(strong_typedef rhs) { m_val = rhs.m_val; return *this; }
private: private:
UnderlyingType m_val; UnderlyingType m_val;

View File

@ -65,8 +65,10 @@ namespace libtorrent
std::numeric_limits<piece_index_t>::max() std::numeric_limits<piece_index_t>::max()
, std::numeric_limits<int>::max()); , std::numeric_limits<int>::max());
constexpr prio_index_t piece_picker::piece_pos::we_have_index;
piece_picker::piece_picker() piece_picker::piece_picker()
: m_priority_boundaries(1, int(m_pieces.size())) : m_priority_boundaries(1, m_pieces.end_index())
{ {
#ifdef TORRENT_PICKER_LOG #ifdef TORRENT_PICKER_LOG
std::cerr << "[" << this << "] " << "new piece_picker" << std::endl; std::cerr << "[" << this << "] " << "new piece_picker" << std::endl;
@ -105,7 +107,7 @@ namespace libtorrent
{ {
i->peer_count = 0; i->peer_count = 0;
i->download_state = piece_pos::piece_open; i->download_state = piece_pos::piece_open;
i->index = 0; i->index = prio_index_t(0);
#ifdef TORRENT_DEBUG_REFCOUNTS #ifdef TORRENT_DEBUG_REFCOUNTS
i->have_peers.clear(); i->have_peers.clear();
#endif #endif
@ -119,10 +121,6 @@ namespace libtorrent
m_reverse_cursor > piece_index_t(0) && (i->have() || i->filtered()); m_reverse_cursor > piece_index_t(0) && (i->have() || i->filtered());
++i, --m_reverse_cursor); ++i, --m_reverse_cursor);
// the piece index is stored in 20 bits, which limits the allowed
// number of pieces somewhat
TORRENT_ASSERT(m_piece_map.size() < piece_pos::we_have_index);
m_blocks_per_piece = std::uint16_t(blocks_per_piece); m_blocks_per_piece = std::uint16_t(blocks_per_piece);
m_blocks_in_last_piece = std::uint16_t(blocks_in_last_piece); m_blocks_in_last_piece = std::uint16_t(blocks_in_last_piece);
if (m_blocks_in_last_piece == 0) m_blocks_in_last_piece = std::uint16_t(blocks_per_piece); if (m_blocks_in_last_piece == 0) m_blocks_in_last_piece = std::uint16_t(blocks_per_piece);
@ -342,16 +340,14 @@ namespace libtorrent
} }
} }
void piece_picker::verify_priority(int const range_start void piece_picker::verify_priority(prio_index_t const range_start
, int const range_end , prio_index_t const range_end
, int const prio) const , int const prio) const
{ {
TORRENT_ASSERT(range_start <= range_end); TORRENT_ASSERT(range_start <= range_end);
TORRENT_ASSERT(range_end <= int(m_pieces.size())); TORRENT_ASSERT(range_end <= m_pieces.end_index());
for (auto i = m_pieces.begin() + range_start for (auto index : range(m_pieces, range_start, range_end))
, end(m_pieces.begin() + range_end); i != end; ++i)
{ {
piece_index_t const index = *i;
int p = m_piece_map[index].priority(this); int p = m_piece_map[index].priority(this);
TORRENT_ASSERT(p == prio); TORRENT_ASSERT(p == prio);
} }
@ -368,16 +364,14 @@ namespace libtorrent
return; return;
} }
for (int b : m_priority_boundaries) for (prio_index_t b : m_priority_boundaries)
{
std::cerr << b << " "; std::cerr << b << " ";
}
std::cerr << std::endl; std::cerr << std::endl;
int index = 0; prio_index_t index(0);
std::cerr << "[" << this << "] "; std::cerr << "[" << this << "] ";
std::vector<int>::const_iterator j = m_priority_boundaries.begin(); auto j = m_priority_boundaries.begin();
for (auto i = m_pieces.begin() for (auto i = m_pieces.begin(), end(m_pieces.end()); i != end; ++i, ++index)
, end(m_pieces.end()); i != end; ++i, ++index)
{ {
if (limit == 0) if (limit == 0)
{ {
@ -424,8 +418,8 @@ namespace libtorrent
// make sure the priority boundaries are monotonically increasing. The // make sure the priority boundaries are monotonically increasing. The
// difference between two cursors cannot be negative, but ranges are // difference between two cursors cannot be negative, but ranges are
// allowed to be empty. // allowed to be empty.
int last = 0; prio_index_t last(0);
for (int b : m_priority_boundaries) for (prio_index_t b : m_priority_boundaries)
{ {
TORRENT_ASSERT(b >= last); TORRENT_ASSERT(b >= last);
last = b; last = b;
@ -545,14 +539,14 @@ namespace libtorrent
{ {
TORRENT_ASSERT(!m_priority_boundaries.empty()); TORRENT_ASSERT(!m_priority_boundaries.empty());
int prio = 0; int prio = 0;
int start = 0; prio_index_t start(0);
for (int b : m_priority_boundaries) for (prio_index_t b : m_priority_boundaries)
{ {
verify_priority(start, b, prio); verify_priority(start, b, prio);
++prio; ++prio;
start = b; start = b;
} }
TORRENT_ASSERT(m_priority_boundaries.back() == int(m_pieces.size())); TORRENT_ASSERT(m_priority_boundaries.back() == m_pieces.end_index());
} }
#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS
@ -582,10 +576,9 @@ namespace libtorrent
int num_filtered = 0; int num_filtered = 0;
int num_have_filtered = 0; int num_have_filtered = 0;
int num_have = 0; int num_have = 0;
for (std::vector<piece_pos>::const_iterator i = m_piece_map.begin(); piece_index_t piece(0);
i != m_piece_map.end(); ++i) for (auto i = m_piece_map.begin(); i != m_piece_map.end(); ++i, ++piece)
{ {
piece_index_t const index = piece_index_t(int(i - m_piece_map.begin()));
piece_pos const& p = *i; piece_pos const& p = *i;
if (p.filtered()) if (p.filtered())
@ -604,12 +597,12 @@ namespace libtorrent
if (p.index == piece_pos::we_have_index) if (p.index == piece_pos::we_have_index)
{ {
TORRENT_ASSERT(t == nullptr || t->have_piece(index)); TORRENT_ASSERT(t == nullptr || t->have_piece(piece));
TORRENT_ASSERT(p.downloading() == false); TORRENT_ASSERT(p.downloading() == false);
} }
if (t != nullptr) if (t != nullptr)
TORRENT_ASSERT(!t->have_piece(index)); TORRENT_ASSERT(!t->have_piece(piece));
int const prio = p.priority(this); int const prio = p.priority(this);
@ -632,8 +625,8 @@ namespace libtorrent
TORRENT_ASSERT(prio < int(m_priority_boundaries.size())); TORRENT_ASSERT(prio < int(m_priority_boundaries.size()));
if (prio >= 0) if (prio >= 0)
{ {
TORRENT_ASSERT(p.index < m_pieces.size()); TORRENT_ASSERT(p.index < m_pieces.end_index());
TORRENT_ASSERT(m_pieces[p.index] == index); TORRENT_ASSERT(m_pieces[p.index] == piece);
} }
else else
{ {
@ -641,29 +634,29 @@ namespace libtorrent
// make sure there's no entry // make sure there's no entry
// with this index. (there shouldn't // with this index. (there shouldn't
// be since the priority is -1) // be since the priority is -1)
TORRENT_ASSERT(std::count(m_pieces.begin(), m_pieces.end(), index) == 0); TORRENT_ASSERT(std::count(m_pieces.begin(), m_pieces.end(), piece) == 0);
} }
} }
int const count_downloading = int(std::count_if( int const count_downloading = int(std::count_if(
m_downloads[piece_pos::piece_downloading].begin() m_downloads[piece_pos::piece_downloading].begin()
, m_downloads[piece_pos::piece_downloading].end() , m_downloads[piece_pos::piece_downloading].end()
, has_index(index))); , has_index(piece)));
int const count_full = int(std::count_if( int const count_full = int(std::count_if(
m_downloads[piece_pos::piece_full].begin() m_downloads[piece_pos::piece_full].begin()
, m_downloads[piece_pos::piece_full].end() , m_downloads[piece_pos::piece_full].end()
, has_index(index))); , has_index(piece)));
int const count_finished = int(std::count_if( int const count_finished = int(std::count_if(
m_downloads[piece_pos::piece_finished].begin() m_downloads[piece_pos::piece_finished].begin()
, m_downloads[piece_pos::piece_finished].end() , m_downloads[piece_pos::piece_finished].end()
, has_index(index))); , has_index(piece)));
int const count_zero = int(std::count_if( int const count_zero = int(std::count_if(
m_downloads[piece_pos::piece_zero_prio].begin() m_downloads[piece_pos::piece_zero_prio].begin()
, m_downloads[piece_pos::piece_zero_prio].end() , m_downloads[piece_pos::piece_zero_prio].end()
, has_index(index))); , has_index(piece)));
TORRENT_ASSERT(i->download_queue() == piece_pos::piece_open TORRENT_ASSERT(i->download_queue() == piece_pos::piece_open
|| count_zero + count_downloading + count_full || count_zero + count_downloading + count_full
@ -742,15 +735,25 @@ namespace libtorrent
return std::make_pair(min_availability + m_seeds, fraction_part * 1000 / num_pieces); return std::make_pair(min_availability + m_seeds, fraction_part * 1000 / num_pieces);
} }
std::pair<int, int> piece_picker::priority_range(int const prio) prio_index_t piece_picker::priority_begin(int const prio) const
{ {
TORRENT_ASSERT(prio >= 0); TORRENT_ASSERT(prio >= 0);
TORRENT_ASSERT(prio < int(m_priority_boundaries.size()) || m_dirty); TORRENT_ASSERT(prio < int(m_priority_boundaries.size()));
std::pair<int, int> const ret{ return prio == 0 ? prio_index_t(0) : m_priority_boundaries[prio-1];
prio == 0 ? 0 : m_priority_boundaries[prio-1] }
, m_priority_boundaries[prio]};
TORRENT_ASSERT(ret.first <= ret.second); prio_index_t piece_picker::priority_end(int const prio) const
return ret; {
TORRENT_ASSERT(prio >= 0);
TORRENT_ASSERT(prio < int(m_priority_boundaries.size()));
return m_priority_boundaries[prio];
}
std::pair<prio_index_t, prio_index_t> piece_picker::priority_range(int const prio) const
{
TORRENT_ASSERT(prio >= 0);
TORRENT_ASSERT(prio < int(m_priority_boundaries.size()));
return { priority_begin(prio), priority_end(prio) };
} }
void piece_picker::add(piece_index_t index) void piece_picker::add(piece_index_t index)
@ -765,14 +768,14 @@ namespace libtorrent
if (priority < 0) return; if (priority < 0) return;
if (int(m_priority_boundaries.size()) <= priority) if (int(m_priority_boundaries.size()) <= priority)
m_priority_boundaries.resize(priority + 1, int(m_pieces.size())); m_priority_boundaries.resize(priority + 1, m_pieces.end_index());
TORRENT_ASSERT(int(m_priority_boundaries.size()) >= priority); TORRENT_ASSERT(int(m_priority_boundaries.size()) >= priority);
auto const range = priority_range(priority); auto const range = priority_range(priority);
int new_index = (range.second == range.first) prio_index_t new_index = (range.second == range.first)
? range.first ? range.first
: random(range.second - range.first) + range.first; : prio_index_t(random(static_cast<int>(range.second - range.first)) + static_cast<int>(range.first));
#ifdef TORRENT_PICKER_LOG #ifdef TORRENT_PICKER_LOG
std::cerr << "[" << this << "] " << "add " << index << " (" << priority << ")" << std::endl; std::cerr << "[" << this << "] " << "add " << index << " (" << priority << ")" << std::endl;
@ -786,14 +789,14 @@ namespace libtorrent
for (;;) for (;;)
{ {
TORRENT_ASSERT(new_index < int(m_pieces.size())); TORRENT_ASSERT(new_index < m_pieces.end_index());
{ {
piece_index_t temp = m_pieces[new_index]; piece_index_t temp = m_pieces[new_index];
m_pieces[new_index] = index; m_pieces[new_index] = index;
m_piece_map[index].index = new_index; m_piece_map[index].index = new_index;
index = temp; index = temp;
} }
int temp = -1; prio_index_t temp(-1);
do do
{ {
temp = m_priority_boundaries[priority]++; temp = m_priority_boundaries[priority]++;
@ -808,11 +811,11 @@ namespace libtorrent
<< std::endl; << std::endl;
#endif #endif
if (priority >= int(m_priority_boundaries.size())) break; if (priority >= int(m_priority_boundaries.size())) break;
TORRENT_ASSERT(temp >= 0); TORRENT_ASSERT(temp >= prio_index_t(0));
} }
if (index != piece_index_t(-1)) if (index != piece_index_t(-1))
{ {
TORRENT_ASSERT(new_index == int(m_pieces.size() - 1)); TORRENT_ASSERT(new_index == prev(m_pieces.end_index()));
m_pieces[new_index] = index; m_pieces[new_index] = index;
m_piece_map[index].index = new_index; m_piece_map[index].index = new_index;
@ -822,25 +825,23 @@ namespace libtorrent
} }
} }
void piece_picker::remove(int priority, int elem_index) void piece_picker::remove(int priority, prio_index_t elem_index)
{ {
TORRENT_ASSERT(!m_dirty); TORRENT_ASSERT(!m_dirty);
TORRENT_ASSERT(priority >= 0); TORRENT_ASSERT(priority >= 0);
TORRENT_ASSERT(elem_index < int(m_pieces.size()));
TORRENT_ASSERT(elem_index >= 0);
#ifdef TORRENT_PICKER_LOG #ifdef TORRENT_PICKER_LOG
std::cerr << "[" << this << "] " << "remove " << m_pieces[elem_index] << " (" << priority << ")" << std::endl; std::cerr << "[" << this << "] " << "remove " << m_pieces[elem_index] << " (" << priority << ")" << std::endl;
#endif #endif
int next_index = elem_index; prio_index_t next_index = elem_index;
TORRENT_ASSERT(m_piece_map[m_pieces[elem_index]].priority(this) == -1); TORRENT_ASSERT(m_piece_map[m_pieces[elem_index]].priority(this) == -1);
for (;;) for (;;)
{ {
#ifdef TORRENT_PICKER_LOG #ifdef TORRENT_PICKER_LOG
print_pieces(); print_pieces();
#endif #endif
TORRENT_ASSERT(elem_index < int(m_pieces.size())); TORRENT_ASSERT(elem_index < m_pieces.end_index());
int temp; prio_index_t temp;
do do
{ {
temp = --m_priority_boundaries[priority]; temp = --m_priority_boundaries[priority];
@ -853,14 +854,14 @@ namespace libtorrent
m_pieces[elem_index] = piece; m_pieces[elem_index] = piece;
m_piece_map[piece].index = elem_index; m_piece_map[piece].index = elem_index;
TORRENT_ASSERT(m_piece_map[piece].priority(this) == priority - 1); TORRENT_ASSERT(m_piece_map[piece].priority(this) == priority - 1);
TORRENT_ASSERT(elem_index < int(m_pieces.size() - 1)); TORRENT_ASSERT(elem_index < prev(m_pieces.end_index()));
elem_index = next_index; elem_index = next_index;
if (priority == int(m_priority_boundaries.size())) if (priority == int(m_priority_boundaries.size()))
break; break;
} }
m_pieces.pop_back(); m_pieces.pop_back();
TORRENT_ASSERT(next_index == int(m_pieces.size())); TORRENT_ASSERT(next_index == m_pieces.end_index());
#ifdef TORRENT_PICKER_LOG #ifdef TORRENT_PICKER_LOG
print_pieces(); print_pieces();
#endif #endif
@ -868,11 +869,9 @@ namespace libtorrent
// will update the piece with the given properties (priority, elem_index) // will update the piece with the given properties (priority, elem_index)
// to place it at the correct position // to place it at the correct position
void piece_picker::update(int priority, int elem_index) void piece_picker::update(int priority, prio_index_t elem_index)
{ {
TORRENT_ASSERT(!m_dirty); TORRENT_ASSERT(!m_dirty);
TORRENT_ASSERT(elem_index >= 0);
TORRENT_ASSERT(elem_index < int(m_piece_map.size()));
TORRENT_ASSERT(priority >= 0); TORRENT_ASSERT(priority >= 0);
TORRENT_ASSERT(int(m_priority_boundaries.size()) > priority); TORRENT_ASSERT(int(m_priority_boundaries.size()) > priority);
@ -880,13 +879,13 @@ namespace libtorrent
// priority bucket. If it doesn't, it means this piece changed // priority bucket. If it doesn't, it means this piece changed
// state without updating the corresponding entry in the pieces list // state without updating the corresponding entry in the pieces list
TORRENT_ASSERT(m_priority_boundaries[priority] >= elem_index); TORRENT_ASSERT(m_priority_boundaries[priority] >= elem_index);
TORRENT_ASSERT(priority == 0 || m_priority_boundaries[priority - 1] <= elem_index); TORRENT_ASSERT(elem_index >= priority_begin(priority));
TORRENT_ASSERT(priority + 1 == int(m_priority_boundaries.size()) || m_priority_boundaries[priority + 1] > elem_index); TORRENT_ASSERT(elem_index < priority_end(priority));
piece_index_t const index = m_pieces[elem_index]; piece_index_t const index = m_pieces[elem_index];
// update the piece_map // update the piece_map
piece_pos& p = m_piece_map[index]; piece_pos& p = m_piece_map[index];
TORRENT_ASSERT(int(p.index) == elem_index || p.have()); TORRENT_ASSERT(p.index == elem_index || p.have());
int const new_priority = p.priority(this); int const new_priority = p.priority(this);
@ -899,14 +898,14 @@ namespace libtorrent
} }
if (int(m_priority_boundaries.size()) <= new_priority) if (int(m_priority_boundaries.size()) <= new_priority)
m_priority_boundaries.resize(new_priority + 1, int(m_pieces.size())); m_priority_boundaries.resize(new_priority + 1, m_pieces.end_index());
#ifdef TORRENT_PICKER_LOG #ifdef TORRENT_PICKER_LOG
std::cerr << "[" << this << "] " << "update " << index << " (" << priority << "->" << new_priority << ")" << std::endl; std::cerr << "[" << this << "] " << "update " << index << " (" << priority << "->" << new_priority << ")" << std::endl;
#endif #endif
if (priority > new_priority) if (priority > new_priority)
{ {
int new_index; prio_index_t new_index;
piece_index_t temp = index; piece_index_t temp = index;
for (;;) for (;;)
{ {
@ -916,14 +915,12 @@ namespace libtorrent
TORRENT_ASSERT(priority > 0); TORRENT_ASSERT(priority > 0);
--priority; --priority;
new_index = m_priority_boundaries[priority]++; new_index = m_priority_boundaries[priority]++;
TORRENT_ASSERT(new_index >= 0);
TORRENT_ASSERT(new_index < int(m_pieces.size()));
if (temp != m_pieces[new_index]) if (temp != m_pieces[new_index])
{ {
temp = m_pieces[new_index]; temp = m_pieces[new_index];
m_pieces[elem_index] = temp; m_pieces[elem_index] = temp;
m_piece_map[temp].index = elem_index; m_piece_map[temp].index = elem_index;
TORRENT_ASSERT(elem_index < int(m_pieces.size())); TORRENT_ASSERT(elem_index < m_pieces.end_index());
} }
elem_index = new_index; elem_index = new_index;
if (priority == new_priority) break; if (priority == new_priority) break;
@ -933,7 +930,7 @@ namespace libtorrent
#endif #endif
m_pieces[elem_index] = index; m_pieces[elem_index] = index;
m_piece_map[index].index = elem_index; m_piece_map[index].index = elem_index;
TORRENT_ASSERT(elem_index < int(m_pieces.size())); TORRENT_ASSERT(elem_index < m_pieces.end_index());
#ifdef TORRENT_PICKER_LOG #ifdef TORRENT_PICKER_LOG
print_pieces(); print_pieces();
#endif #endif
@ -945,7 +942,7 @@ namespace libtorrent
} }
else else
{ {
int new_index; prio_index_t new_index;
piece_index_t temp = index; piece_index_t temp = index;
for (;;) for (;;)
{ {
@ -955,14 +952,12 @@ namespace libtorrent
TORRENT_ASSERT(priority >= 0); TORRENT_ASSERT(priority >= 0);
TORRENT_ASSERT(priority < int(m_priority_boundaries.size())); TORRENT_ASSERT(priority < int(m_priority_boundaries.size()));
new_index = --m_priority_boundaries[priority]; new_index = --m_priority_boundaries[priority];
TORRENT_ASSERT(new_index >= 0);
TORRENT_ASSERT(new_index < int(m_pieces.size()));
if (temp != m_pieces[new_index]) if (temp != m_pieces[new_index])
{ {
temp = m_pieces[new_index]; temp = m_pieces[new_index];
m_pieces[elem_index] = temp; m_pieces[elem_index] = temp;
m_piece_map[temp].index = elem_index; m_piece_map[temp].index = elem_index;
TORRENT_ASSERT(elem_index < int(m_pieces.size())); TORRENT_ASSERT(elem_index < m_pieces.end_index());
} }
elem_index = new_index; elem_index = new_index;
++priority; ++priority;
@ -973,7 +968,7 @@ namespace libtorrent
#endif #endif
m_pieces[elem_index] = index; m_pieces[elem_index] = index;
m_piece_map[index].index = elem_index; m_piece_map[index].index = elem_index;
TORRENT_ASSERT(elem_index < int(m_pieces.size())); TORRENT_ASSERT(elem_index < m_pieces.end_index());
#ifdef TORRENT_PICKER_LOG #ifdef TORRENT_PICKER_LOG
print_pieces(); print_pieces();
#endif #endif
@ -985,7 +980,7 @@ namespace libtorrent
} }
} }
void piece_picker::shuffle(int priority, int elem_index) void piece_picker::shuffle(int priority, prio_index_t elem_index)
{ {
#ifdef TORRENT_PICKER_LOG #ifdef TORRENT_PICKER_LOG
std::cerr << "[" << this << "] " << "shuffle()" << std::endl; std::cerr << "[" << this << "] " << "shuffle()" << std::endl;
@ -993,12 +988,12 @@ namespace libtorrent
TORRENT_ASSERT(!m_dirty); TORRENT_ASSERT(!m_dirty);
TORRENT_ASSERT(priority >= 0); TORRENT_ASSERT(priority >= 0);
TORRENT_ASSERT(elem_index >= 0); TORRENT_ASSERT(elem_index >= prio_index_t(0));
TORRENT_ASSERT(elem_index < int(m_pieces.size())); TORRENT_ASSERT(elem_index < m_pieces.end_index());
TORRENT_ASSERT(m_piece_map[m_pieces[elem_index]].priority(this) == priority); TORRENT_ASSERT(m_piece_map[m_pieces[elem_index]].priority(this) == priority);
auto const range = priority_range(priority); auto const range = priority_range(priority);
int const other_index = random(range.second - range.first - 1) + range.first; prio_index_t const other_index(random(static_cast<int>(range.second - range.first) - 1) + static_cast<int>(range.first));
if (other_index == elem_index) return; if (other_index == elem_index) return;
@ -1429,19 +1424,23 @@ namespace libtorrent
void piece_picker::update_pieces() const void piece_picker::update_pieces() const
{ {
TORRENT_ASSERT(m_dirty); TORRENT_ASSERT(m_dirty);
if (m_priority_boundaries.empty()) m_priority_boundaries.resize(1, 0); if (m_priority_boundaries.empty()) m_priority_boundaries.resize(1, prio_index_t(0));
#ifdef TORRENT_PICKER_LOG #ifdef TORRENT_PICKER_LOG
std::cerr << "[" << this << "] " << "update_pieces" << std::endl; std::cerr << "[" << this << "] " << "update_pieces" << std::endl;
#endif #endif
std::fill(m_priority_boundaries.begin(), m_priority_boundaries.end(), 0);
for (std::vector<piece_pos>::iterator i = m_piece_map.begin() // This code is unfortunately not very straight-forward. What we do here
, end(m_piece_map.end()); i != end; ++i) // is to count the number of pieces at every priority level. After this
// first step, m_priority_boundaries will contain *deltas* rather than
// absolute indices. This is fixed up in a second pass below
std::fill(m_priority_boundaries.begin(), m_priority_boundaries.end(), prio_index_t(0));
for (auto& pos : m_piece_map)
{ {
int prio = i->priority(this); int prio = pos.priority(this);
if (prio == -1) continue; if (prio == -1) continue;
if (prio >= int(m_priority_boundaries.size())) if (prio >= int(m_priority_boundaries.size()))
m_priority_boundaries.resize(prio + 1, 0); m_priority_boundaries.resize(prio + 1, prio_index_t(0));
i->index = m_priority_boundaries[prio]; pos.index = m_priority_boundaries[prio];
++m_priority_boundaries[prio]; ++m_priority_boundaries[prio];
} }
@ -1449,11 +1448,15 @@ namespace libtorrent
print_pieces(); print_pieces();
#endif #endif
// m_priority_boundaries just contain counters of
// each priority level at this point. Now, make the m_priority_boundaries
// be cumulative indices into m_pieces (but m_pieces hasn't been set up
// yet)
int new_size = 0; int new_size = 0;
for (int& b : m_priority_boundaries) for (prio_index_t& b : m_priority_boundaries)
{ {
new_size += b; new_size += static_cast<int>(b);
b = new_size; b = prio_index_t(new_size);
} }
m_pieces.resize(new_size, piece_index_t(0)); m_pieces.resize(new_size, piece_index_t(0));
@ -1461,26 +1464,32 @@ namespace libtorrent
print_pieces(); print_pieces();
#endif #endif
// set up m_pieces to contain valid piece indices, based on piece
// priority. m_piece_map[].index is still just an index relative to the
// respective priority range.
piece_index_t piece = piece_index_t(0); piece_index_t piece = piece_index_t(0);
for (auto i = m_piece_map.begin() for (auto i = m_piece_map.begin(), end(m_piece_map.end()); i != end; ++i, ++piece)
, end(m_piece_map.end()); i != end; ++i, ++piece)
{ {
piece_pos& p = *i; piece_pos& p = *i;
int const prio = p.priority(this); int const prio = p.priority(this);
if (prio == -1) continue; if (prio == -1) continue;
int const new_index = (prio == 0 ? 0 : m_priority_boundaries[prio - 1]) + p.index; prio_index_t const new_index(priority_begin(prio)
+ prio_index_t::diff_type(static_cast<int>(p.index)));
m_pieces[new_index] = piece; m_pieces[new_index] = piece;
} }
int start = 0; prio_index_t start(0);
for (int b : m_priority_boundaries) for (auto b : m_priority_boundaries)
{ {
if (start == b) continue; if (start == b) continue;
aux::random_shuffle(&m_pieces[0] + start, &m_pieces[0] + b); auto r = range(m_pieces, start, b);
aux::random_shuffle(r.begin(), r.end());
start = b; start = b;
} }
int index = 0; // this is where we set fix up the m_piece_map[].index to actually map
// back to the piece list ordered by priority (m_pieces)
prio_index_t index(0);
for (auto p : m_pieces) for (auto p : m_pieces)
{ {
m_piece_map[p].index = index; m_piece_map[p].index = index;
@ -1585,7 +1594,7 @@ namespace libtorrent
<< index << ")" << std::endl; << index << ")" << std::endl;
#endif #endif
piece_pos& p = m_piece_map[index]; piece_pos& p = m_piece_map[index];
int const info_index = p.index; prio_index_t const info_index = p.index;
int const priority = p.priority(this); int const priority = p.priority(this);
TORRENT_ASSERT(priority < int(m_priority_boundaries.size()) || m_dirty); TORRENT_ASSERT(priority < int(m_priority_boundaries.size()) || m_dirty);
@ -2068,9 +2077,9 @@ namespace libtorrent
{ {
for (int i = int(m_priority_boundaries.size()) - 1; i >= 0; --i) for (int i = int(m_priority_boundaries.size()) - 1; i >= 0; --i)
{ {
int const start = (i == 0) ? 0 : m_priority_boundaries[i - 1]; prio_index_t const start = priority_begin(i);
int const end = m_priority_boundaries[i]; prio_index_t const end = priority_end(i);
for (int p = end - 1; p >= start; --p) for (prio_index_t p = prev(end); p >= start; --p)
{ {
pc.inc_stats_counter(counters::piece_picker_reverse_rare_loops); pc.inc_stats_counter(counters::piece_picker_reverse_rare_loops);