fix: assertion failed "s->leftUntilDone <= s->sizeWhenDone" (#3406)

This commit is contained in:
Charles Kerr 2022-07-04 11:48:54 -05:00 committed by GitHub
parent 707fce44da
commit 513f4bc91b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 53 additions and 25 deletions

View File

@ -74,6 +74,17 @@ public:
return { pieceLoc(piece).block, pieceLastLoc(piece).block + 1 };
}
[[nodiscard]] tr_byte_span_t byteSpanForPiece(tr_piece_index_t piece) const noexcept
{
if (!isInitialized())
{
return {};
}
auto const offset = pieceLoc(piece).byte;
return { offset, offset + pieceSize(piece) };
}
struct Location
{
uint64_t byte = 0;

View File

@ -57,7 +57,7 @@ uint64_t tr_completion::computeSizeWhenDone() const
}
else
{
size += countHasBytesInBlocks(block_info_->blockSpanForPiece(piece));
size += countHasBytesInPiece(piece);
}
}
@ -101,7 +101,7 @@ size_t tr_completion::countMissingBlocksInPiece(tr_piece_index_t piece) const
size_t tr_completion::countMissingBytesInPiece(tr_piece_index_t piece) const
{
return block_info_->pieceSize(piece) - countHasBytesInBlocks(block_info_->blockSpanForPiece(piece));
return block_info_->pieceSize(piece) - countHasBytesInPiece(piece);
}
tr_completeness tr_completion::status() const
@ -151,6 +151,7 @@ void tr_completion::addBlock(tr_block_index_t block)
blocks_.set(block);
size_now_ += block_info_->blockSize(block);
size_when_done_.reset();
has_valid_.reset();
}
@ -159,7 +160,7 @@ void tr_completion::setBlocks(tr_bitfield blocks)
TR_ASSERT(std::size(blocks_) == std::size(blocks));
blocks_ = std::move(blocks);
size_now_ = countHasBytesInBlocks({ 0, tr_block_index_t(std::size(blocks_)) });
size_now_ = countHasBytesInSpan({ 0, block_info_->totalSize() });
size_when_done_.reset();
has_valid_.reset();
}
@ -187,30 +188,12 @@ void tr_completion::addPiece(tr_piece_index_t piece)
void tr_completion::removePiece(tr_piece_index_t piece)
{
auto const [begin, end] = block_info_->blockSpanForPiece(piece);
size_now_ -= countHasBytesInBlocks(block_info_->blockSpanForPiece(piece));
size_now_ -= countHasBytesInPiece(piece);
size_when_done_.reset();
has_valid_.reset();
blocks_.unsetSpan(begin, end);
}
uint64_t tr_completion::countHasBytesInBlocks(tr_block_span_t span) const
{
auto const [begin, end] = span;
if (begin >= end)
{
return 0;
}
uint64_t n = blocks_.count(begin, end);
n *= tr_block_info::BlockSize;
if (end == block_info_->blockCount() && blocks_.test(end - 1))
{
n -= tr_block_info::BlockSize - block_info_->blockSize(end - 1);
}
return n;
}
uint64_t tr_completion::countHasBytesInSpan(tr_byte_span_t span) const
{
// confirm the span is valid

View File

@ -140,7 +140,11 @@ struct tr_completion
private:
[[nodiscard]] uint64_t computeHasValid() const;
[[nodiscard]] uint64_t computeSizeWhenDone() const;
[[nodiscard]] uint64_t countHasBytesInBlocks(tr_block_span_t) const;
[[nodiscard]] uint64_t countHasBytesInPiece(tr_piece_index_t piece) const
{
return countHasBytesInSpan(block_info_->byteSpanForPiece(piece));
}
torrent_view const* tor_;
tr_block_info const* block_info_;

View File

@ -471,6 +471,36 @@ TEST_F(CompletionTest, countHasBytesInSpan)
EXPECT_EQ(BlockSize * 1.5, completion.countHasBytesInSpan({ BlockSize / 2, BlockSize * 2 + BlockSize / 2 }));
}
TEST_F(CompletionTest, status)
TEST_F(CompletionTest, wantNone)
{
auto torrent = TestTorrent{};
auto constexpr TotalSize = uint64_t{ BlockSize * 4096 };
auto constexpr PieceSize = uint64_t{ BlockSize * 64 };
auto const block_info = tr_block_info{ TotalSize, PieceSize };
auto completion = tr_completion(&torrent, &block_info);
// we have some data
completion.addBlock(0);
// and want nothing
for (tr_piece_index_t i = 0, n = block_info.blockCount(); i < n; ++i)
{
torrent.dnd_pieces.insert(i);
}
completion.invalidateSizeWhenDone();
EXPECT_LE(completion.hasTotal(), completion.sizeWhenDone());
EXPECT_EQ(completion.hasTotal(), block_info.BlockSize);
EXPECT_EQ(completion.sizeWhenDone(), block_info.BlockSize);
EXPECT_LE(completion.leftUntilDone(), completion.sizeWhenDone());
EXPECT_EQ(completion.leftUntilDone(), 0);
// but we magically get a block anyway
completion.addBlock(1);
EXPECT_LE(completion.hasTotal(), completion.sizeWhenDone());
EXPECT_EQ(completion.hasTotal(), 2 * block_info.BlockSize);
EXPECT_EQ(completion.sizeWhenDone(), 2 * block_info.BlockSize);
EXPECT_LE(completion.leftUntilDone(), completion.sizeWhenDone());
EXPECT_EQ(completion.leftUntilDone(), 0);
}