Skip to content

Commit

Permalink
fix: requests across piece boundaries when piece is not a multiple of…
Browse files Browse the repository at this point in the history
… block_size

Fixes #3324.
  • Loading branch information
ckerr committed Jun 20, 2022
1 parent b983a3b commit 85c11b7
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 204 deletions.
22 changes: 8 additions & 14 deletions libtransmission/cache.cc
Expand Up @@ -76,13 +76,13 @@ int Cache::writeContiguous(CIter const begin, CIter const end) const
begin,
end,
size_t{},
[](size_t sum, auto const& block) { return sum + std::size(block.buf); });
[](size_t sum, auto const& block) { return sum + std::size(*block.buf); });
buf.reserve(buflen);
for (auto iter = begin; iter != end; ++iter)
{
TR_ASSERT(begin->key.first == iter->key.first);
TR_ASSERT(begin->key.second + std::distance(begin, iter) == iter->key.second);
buf.insert(std::end(buf), std::begin(iter->buf), std::end(iter->buf));
buf.insert(std::end(buf), std::begin(*iter->buf), std::end(*iter->buf));
}
TR_ASSERT(std::size(buf) == buflen);

Expand Down Expand Up @@ -126,14 +126,9 @@ Cache::Cache(tr_torrents& torrents, int64_t max_bytes)
****
***/

int Cache::writeBlock(tr_torrent* torrent, tr_block_info::Location loc, uint32_t length, struct evbuffer* writeme)
void Cache::writeBlock(tr_torrent_id_t tor_id, tr_block_index_t block, std::unique_ptr<std::vector<uint8_t>>& data)
{
TR_ASSERT(tr_amInEventThread(torrent->session));
TR_ASSERT(loc.block_offset == 0);
TR_ASSERT(torrent->blockSize(loc.block) == length);
TR_ASSERT(torrent->blockSize(loc.block) <= evbuffer_get_length(writeme));

auto const key = makeKey(torrent, loc);
auto const key = Key{ tor_id, block };
auto iter = std::lower_bound(std::begin(blocks_), std::end(blocks_), key, CompareCacheBlockByKey{});
if (iter == std::end(blocks_) || iter->key != key)
{
Expand All @@ -143,13 +138,12 @@ int Cache::writeBlock(tr_torrent* torrent, tr_block_info::Location loc, uint32_t

iter->time_added = tr_time();

iter->buf.resize(length);
evbuffer_remove(writeme, std::data(iter->buf), std::size(iter->buf));
iter->buf = std::move(data);

++cache_writes_;
cache_write_bytes_ += length;
cache_write_bytes_ += std::size(*iter->buf);

return cacheTrim();
(void)cacheTrim();
}

Cache::CIter Cache::getBlock(tr_torrent const* torrent, tr_block_info::Location loc) noexcept
Expand All @@ -171,7 +165,7 @@ int Cache::readBlock(tr_torrent* torrent, tr_block_info::Location loc, uint32_t
{
if (auto const iter = getBlock(torrent, loc); iter != std::end(blocks_))
{
std::copy_n(std::begin(iter->buf), len, setme);
std::copy_n(std::begin(*iter->buf), len, setme);
return {};
}

Expand Down
5 changes: 3 additions & 2 deletions libtransmission/cache.h
Expand Up @@ -10,6 +10,7 @@
#endif

#include <cstdint> // intX_t, uintX_t
#include <memory> // std::unique_ptr
#include <utility> // std::pair
#include <vector>

Expand All @@ -33,7 +34,7 @@ class Cache
return max_bytes_;
}

int writeBlock(tr_torrent* torrent, tr_block_info::Location loc, uint32_t length, struct evbuffer* writeme);
void writeBlock(tr_torrent_id_t tor, tr_block_index_t block, std::unique_ptr<std::vector<uint8_t>>& writeme);
int readBlock(tr_torrent* torrent, tr_block_info::Location loc, uint32_t len, uint8_t* setme);
int prefetchBlock(tr_torrent* torrent, tr_block_info::Location loc, uint32_t len);
int flushTorrent(tr_torrent* torrent);
Expand All @@ -45,7 +46,7 @@ class Cache
struct CacheBlock
{
Key key;
std::vector<uint8_t> buf;
std::unique_ptr<std::vector<uint8_t>> buf;
time_t time_added = {};
};

Expand Down
24 changes: 0 additions & 24 deletions libtransmission/peer-io.cc
Expand Up @@ -1123,30 +1123,6 @@ void evbuffer_add_uint64(struct evbuffer* outbuf, uint64_t addme_hll)
****
***/

static inline void maybeDecryptBuffer(tr_peerIo* io, struct evbuffer* buf, size_t offset, size_t size)
{
if (io->encryption_type == PEER_ENCRYPTION_RC4)
{
processBuffer(&io->crypto, buf, offset, size, &tr_cryptoDecrypt);
}
}

void tr_peerIoReadBytesToBuf(tr_peerIo* io, struct evbuffer* inbuf, struct evbuffer* outbuf, size_t byteCount)
{
TR_ASSERT(tr_isPeerIo(io));
TR_ASSERT(evbuffer_get_length(inbuf) >= byteCount);

size_t const old_length = evbuffer_get_length(outbuf);

/* append it to outbuf */
struct evbuffer* tmp = evbuffer_new();
evbuffer_remove_buffer(inbuf, tmp, byteCount);
evbuffer_add_buffer(outbuf, tmp);
evbuffer_free(tmp);

maybeDecryptBuffer(io, outbuf, old_length, byteCount);
}

void tr_peerIoReadBytes(tr_peerIo* io, struct evbuffer* inbuf, void* bytes, size_t byteCount)
{
TR_ASSERT(tr_isPeerIo(io));
Expand Down
2 changes: 0 additions & 2 deletions libtransmission/peer-io.h
Expand Up @@ -325,8 +325,6 @@ static inline void evbuffer_add_hton_64(struct evbuffer* buf, uint64_t val)
evbuffer_add_uint64(buf, val);
}

void tr_peerIoReadBytesToBuf(tr_peerIo* io, struct evbuffer* inbuf, struct evbuffer* outbuf, size_t byteCount);

void tr_peerIoReadBytes(tr_peerIo* io, struct evbuffer* inbuf, void* bytes, size_t byteCount);

static inline void tr_peerIoReadUint8(tr_peerIo* io, struct evbuffer* inbuf, uint8_t* setme)
Expand Down
34 changes: 7 additions & 27 deletions libtransmission/peer-mgr-wishlist.cc
Expand Up @@ -7,6 +7,7 @@
#include <cstddef>
#include <iterator>
#include <numeric>
#include <set>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -136,29 +137,25 @@ std::vector<tr_block_span_t> Wishlist::next(Wishlist::Mediator const& mediator,
return {};
}

size_t n_blocks = 0;
auto spans = std::vector<tr_block_span_t>{};

// We usually won't need all the candidates until endgame, so don't
// waste cycles sorting all of them here. partial sort is enough.
auto candidates = getCandidates(mediator);
auto constexpr MaxSortedPieces = size_t{ 30 };
auto const middle = std::min(std::size(candidates), MaxSortedPieces);
std::partial_sort(std::begin(candidates), std::begin(candidates) + middle, std::end(candidates));

auto blocks = std::set<tr_block_index_t>{};
for (auto const& candidate : candidates)
{
// do we have enough?
if (n_blocks >= n_wanted_blocks)
if (std::size(blocks) >= n_wanted_blocks)
{
break;
}

// walk the blocks in this piece
auto const [begin, end] = mediator.blockSpan(candidate.piece);
auto blocks = std::vector<tr_block_index_t>{};
blocks.reserve(end - begin);
for (tr_block_index_t block = begin; block < end && n_blocks + std::size(blocks) < n_wanted_blocks; ++block)
for (tr_block_index_t block = begin; block < end && std::size(blocks) < n_wanted_blocks; ++block)
{
// don't request blocks we've already got
if (!mediator.clientCanRequestBlock(block))
Expand All @@ -173,27 +170,10 @@ std::vector<tr_block_span_t> Wishlist::next(Wishlist::Mediator const& mediator,
continue;
}

blocks.push_back(block);
}

if (std::empty(blocks))
{
continue;
}

// copy the spans into `spans`
auto const tmp = makeSpans(std::data(blocks), std::size(blocks));
std::copy(std::begin(tmp), std::end(tmp), std::back_inserter(spans));
n_blocks += std::accumulate(
std::begin(tmp),
std::end(tmp),
size_t{},
[](size_t sum, auto span) { return sum + span.end - span.begin; });
if (n_blocks >= n_wanted_blocks)
{
break;
blocks.insert(block);
}
}

return spans;
auto const blocks_v = std::vector<tr_block_index_t>{ std::begin(blocks), std::end(blocks) };
return makeSpans(std::data(blocks_v), std::size(blocks_v));
}

0 comments on commit 85c11b7

Please sign in to comment.