blob: 076a1290b698f7a85fc72e97505f23af7d7672af [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "src/trace_processor/containers/bit_vector_iterators.h"
namespace perfetto {
namespace trace_processor {
namespace internal {
BaseIterator::BaseIterator(BitVector* bv)
: size_(bv->size()), bv_(bv), block_(bv_->words_.data()) {}
BaseIterator::~BaseIterator() {
if (size_ > 0) {
uint32_t block_idx = bv_->IndexToAddress(index_).block_idx;
uint32_t last_block_idx = bv_->BlockCount() - 1;
// If |index_| == |size_| and the last index was on a block boundary, we
// can end up one block past the end of the bitvector. Take the
// min of the block index and the last block
OnBlockChange(std::min(block_idx, last_block_idx), last_block_idx);
}
}
void BaseIterator::OnBlockChange(uint32_t old_block_idx,
uint32_t new_block_idx) {
if (set_bit_count_diff_ != 0) {
// If the count of set bits has changed, go through all the counts between
// the old and new blocks and modify them.
// We only need to go to new_block and not to the end of the bitvector as
// the blocks after new_block will either be updated in a future call to
// OnBlockChange or in the destructor.
for (uint32_t i = old_block_idx + 1; i <= new_block_idx; ++i) {
int32_t new_count =
static_cast<int32_t>(bv_->counts_[i]) + set_bit_count_diff_;
PERFETTO_DCHECK(new_count >= 0);
bv_->counts_[i] = static_cast<uint32_t>(new_count);
}
}
// Reset the changed flag and cache the new block.
is_block_changed_ = false;
block_ = bv_->BlockFromIndex(new_block_idx);
}
AllBitsIterator::AllBitsIterator(const BitVector* bv)
: BaseIterator(const_cast<BitVector*>(bv)) {}
SetBitsIterator::SetBitsIterator(const BitVector* bv)
: BaseIterator(const_cast<BitVector*>(bv)) {
set_bit_count_ = bv->CountSetBits();
if (set_bit_count_ > 0) {
// Read a batch of set bit indices starting at index 0.
ReadSetBitBatch(0);
// Fast forward the iterator to the first index in the freshly read
// batch of set bots.
SetIndex(batch_[0]);
}
}
void SetBitsIterator::ReadSetBitBatch(uint32_t start_idx) {
PERFETTO_DCHECK(set_bit_index_ % kBatchSize == 0);
uint32_t set_bit_count_until_i = set_bit_index_;
for (uint32_t i = start_idx; i < size(); ++i) {
auto addr = BitVector::IndexToAddress(i);
// Compute the count to the end of the block noting that the last block
// needs to use |set_bit_count_| and not the next count in the vector
// because that is OOB.
uint32_t set_bits_to_end_of_block =
addr.block_idx == bv().counts_.size() - 1
? set_bit_count_
: bv().counts_[addr.block_idx + 1];
// Optimization: If the count of set bits to the end of the block is the
// same as the count to the current index, we can just skip the whole
// block without iterating through the bits inside.
if (set_bits_to_end_of_block == set_bit_count_until_i) {
static constexpr BitVector::BlockOffset kLastBlockOffset = {
BitVector::Block::kWords - 1, BitVector::BitWord::kBits - 1};
i = BitVector::AddressToIndex({addr.block_idx, kLastBlockOffset});
continue;
}
// If the bit is not set, just bail out.
const BitVector::ConstBlock& block =
bv().ConstBlockFromIndex(addr.block_idx);
if (!block.IsSet(addr.block_offset))
continue;
// Update |batch_| with the index of the current bit.
uint32_t batch_idx = set_bit_count_until_i++ % kBatchSize;
batch_[batch_idx] = i;
// If we've reached as many indicies as the batch can store, just
// return.
if (PERFETTO_UNLIKELY(batch_idx == kBatchSize - 1))
return;
}
// We should only get here when we've managed to read all the set bits.
// End of batch should return from the body of the loop.
PERFETTO_DCHECK(set_bit_count_until_i == set_bit_count_);
}
} // namespace internal
} // namespace trace_processor
} // namespace perfetto