blob: e4ee878c8c0f256a8daa43b50f6a3188ad8a91c1 [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.
*/
#ifndef SRC_TRACE_PROCESSOR_CONTAINERS_BIT_VECTOR_ITERATORS_H_
#define SRC_TRACE_PROCESSOR_CONTAINERS_BIT_VECTOR_ITERATORS_H_
#include "src/trace_processor/containers/bit_vector.h"
namespace perfetto {
namespace trace_processor {
namespace internal {
// Base iterator class for all iterators on BitVector.
//
// This class implements caching of one Block at a time to reduce pointer
// chasing. It also defers updating counts on Clear calls until the end of each
// block.
class BaseIterator {
public:
BaseIterator(BitVector* bv);
~BaseIterator();
BaseIterator(BaseIterator&&) noexcept = default;
BaseIterator& operator=(BaseIterator&&) = default;
// Sets the current bit the iterator points to.
void Set() {
if (!IsSet()) {
block_.Set(block_offset());
is_block_changed_ = true;
++set_bit_count_diff_;
}
}
// Clears the current bit the iterator points to.
void Clear() {
if (IsSet()) {
block_.Clear(block_offset());
is_block_changed_ = true;
--set_bit_count_diff_;
}
}
// Returns whether the current bit the iterator points to is set.
bool IsSet() { return BitVector::ConstBlock(block_).IsSet(block_offset()); }
// Returns the index of the current bit the iterator points to.
uint32_t index() const { return index_; }
protected:
// Sets the index this iterator points to the given value.
//
// This method also performs some extra work on block boundaries
// as it caches the block to improve performance by reducing pointer
// chasing on every IsSet and Clear calls.
void SetIndex(uint32_t index) {
// We should always move the index forward.
PERFETTO_DCHECK(index >= index_);
uint32_t old_index = index_;
index_ = index;
// If we've reached the end of the iterator, just bail out.
if (index >= size_)
return;
uint32_t old_block = bv_->IndexToAddress(old_index).block_idx;
uint32_t new_block = bv_->IndexToAddress(index).block_idx;
// Fast path: we're in the same block so we don't need to do
// any other work.
if (PERFETTO_LIKELY(old_block == new_block))
return;
// Slow path: we have to change block so this will involve flushing the old
// block and counts (if necessary).
OnBlockChange(old_block, new_block);
}
// Handles flushing count changes and caches a new block.
void OnBlockChange(uint32_t old_block, uint32_t new_block);
uint32_t size() const { return size_; }
const BitVector& bv() const { return *bv_; }
private:
BaseIterator(const BaseIterator&) = delete;
BaseIterator& operator=(const BaseIterator&) = delete;
BitVector::BlockOffset block_offset() const {
uint16_t bit_idx_inside_block = index_ % BitVector::Block::kBits;
BitVector::BlockOffset bo;
bo.word_idx = bit_idx_inside_block / BitVector::BitWord::kBits;
bo.bit_idx = bit_idx_inside_block % BitVector::BitWord::kBits;
return bo;
}
uint32_t index_ = 0;
uint32_t size_ = 0;
bool is_block_changed_ = false;
int32_t set_bit_count_diff_ = 0;
BitVector* bv_;
BitVector::Block block_{bv_->words_.data()};
};
// Iterator over all the bits in a bitvector.
class AllBitsIterator : public BaseIterator {
public:
AllBitsIterator(const BitVector*);
// Increments the iterator to point to the next bit.
void Next() { SetIndex(index() + 1); }
// Increments the iterator to skip the next |n| bits and point to the
// following one.
// Precondition: n >= 1 & index() + n <= size().
void Skip(uint32_t n) {
PERFETTO_DCHECK(n >= 1);
PERFETTO_DCHECK(index() + n <= size());
SetIndex(index() + n);
}
// Returns whether the iterator is valid.
operator bool() const { return index() < size(); }
};
// Iterator over all the set bits in a bitvector.
//
// This iterator works by first finding a batch of indices of set bits.
// Then, the fast path involves simply incrementing a counter to go to
// the next index in this batch. On every batch boundary, we hit the
// slow path where we need to find another n set bits.
class SetBitsIterator : public BaseIterator {
public:
SetBitsIterator(const BitVector*);
// Increments the iterator to point to the next set bit.
void Next() {
// If we are out of bounds, just bail out.
if (PERFETTO_UNLIKELY(++set_bit_index_ >= set_bit_count_))
return;
if (PERFETTO_UNLIKELY(set_bit_index_ % kBatchSize == 0))
ReadSetBitBatch(batch_.back() + 1);
SetIndex(batch_[set_bit_index_ % kBatchSize]);
}
// Returns whether the iterator is valid.
operator bool() const { return set_bit_index_ < set_bit_count_; }
// Returns the index of the bit interms of set bits (i.e. how many times
// Next() has been called).
uint32_t ordinal() const { return set_bit_index_; }
private:
static constexpr uint32_t kBatchSize = 1024;
// Reads a full batch of set bit indices from the bitvector and stores them
// in |batch_| below.
//
// This batch of indices is used on the fast path to quickly jump between
// set bits.
void ReadSetBitBatch(uint32_t start_idx);
uint32_t set_bit_index_ = 0;
uint32_t set_bit_count_ = 0;
// Contains an array of indexes; each index points to a set bit in the
// bitvector.
std::array<uint32_t, kBatchSize> batch_;
};
} // namespace internal
} // namespace trace_processor
} // namespace perfetto
#endif // SRC_TRACE_PROCESSOR_CONTAINERS_BIT_VECTOR_ITERATORS_H_