.m_state = state,
};
if (state == BeesTreeBlockState::data) {
+ // A region's hash vector is dense: index k is the block at
+ // m_begin + k * BLOCK_SIZE_SUMS, with no gaps. Every
+ // consumer relies on that -- tree_slice_block_map converts
+ // a byte offset to an index, visible_hashes advances one
+ // block per hash, and the matcher walks two regions in
+ // lockstep by index. Skipping a block here instead of
+ // placing an entry would shift every later hash down one
+ // block and silently mismatch, so a data block without a
+ // hash is a contradiction rather than something to step
+ // over: unreachable blocks belong in their own regions,
+ // which the packed ones do not cover.
MmapVector<BeesHash> hashes;
for (size_t k = i; k < j; ++k) {
- if (blocks[k].hash) {
- hashes.push_back(*blocks[k].hash);
- }
- }
- if (!hashes.empty()) {
- region.m_hashes = std::move(hashes);
+ THROW_CHECK2(runtime_error, to_hex(node.m_bytenr), k,
+ blocks[k].hash.has_value());
+ hashes.push_back(*blocks[k].hash);
}
+ const auto want = ranged_cast<size_t>(
+ (region_end - region_begin + BLOCK_SIZE_SUMS - 1)
+ / BLOCK_SIZE_SUMS);
+ THROW_CHECK3(runtime_error, to_hex(node.m_bytenr),
+ hashes.size(), want, hashes.size() == want);
+ region.m_hashes = std::move(hashes);
}
result.m_regions.push_back(std::move(region));
i = j;
(clipped_begin - region.m_begin) / BLOCK_SIZE_SUMS);
const auto count = ranged_cast<size_t>(
(clipped_end - clipped_begin + BLOCK_SIZE_SUMS - 1) / BLOCK_SIZE_SUMS);
- const auto last = min(first + count, region.m_hashes.size());
+ // The source region is dense, so first + count must be
+ // within it. Clamping instead would silently return a
+ // short vector, and every consumer indexes by block
+ // position -- a short vector shifts hashes rather than
+ // dropping them. Fail here, where the extent is still
+ // identifiable, instead of thousands of operations later
+ // as a kernel byte-compare rejection.
+ THROW_CHECK3(runtime_error, first, count,
+ region.m_hashes.size(),
+ first + count <= region.m_hashes.size());
+ const auto last = first + count;
clipped.m_hashes = MmapVector<BeesHash>(
region.m_hashes.begin() + ranged_cast<ptrdiff_t>(first),
region.m_hashes.begin() + ranged_cast<ptrdiff_t>(last));