1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef SRC_TRACE_PROCESSOR_CONTAINERS_BIT_VECTOR_ITERATORS_H_
18 #define SRC_TRACE_PROCESSOR_CONTAINERS_BIT_VECTOR_ITERATORS_H_
19 
20 #include "src/trace_processor/containers/bit_vector.h"
21 
22 namespace perfetto {
23 namespace trace_processor {
24 namespace internal {
25 
26 // Base iterator class for all iterators on BitVector.
27 //
28 // This class implements caching of one Block at a time to reduce pointer
29 // chasing. It also defers updating counts on Clear calls until the end of each
30 // block.
31 class BaseIterator {
32  public:
33   BaseIterator(BitVector* bv);
34   ~BaseIterator();
35 
36   BaseIterator(BaseIterator&&) noexcept = default;
37   BaseIterator& operator=(BaseIterator&&) = default;
38 
39   // Sets the current bit the iterator points to.
Set()40   void Set() {
41     if (!IsSet()) {
42       block_.Set(block_offset());
43 
44       is_block_changed_ = true;
45       ++set_bit_count_diff_;
46     }
47   }
48 
49   // Clears the current bit the iterator points to.
Clear()50   void Clear() {
51     if (IsSet()) {
52       block_.Clear(block_offset());
53 
54       is_block_changed_ = true;
55       --set_bit_count_diff_;
56     }
57   }
58 
59   // Returns whether the current bit the iterator points to is set.
IsSet()60   bool IsSet() { return block_.IsSet(block_offset()); }
61 
62   // Returns the index of the current bit the iterator points to.
index()63   uint32_t index() const { return index_; }
64 
65  protected:
66   // Sets the index this iterator points to the given value.
67   //
68   // This method also performs some extra work on block boundaries
69   // as it caches the block to improve performance by reducing pointer
70   // chasing on every IsSet and Clear calls.
SetIndex(uint32_t index)71   void SetIndex(uint32_t index) {
72     // We should always move the index forward.
73     PERFETTO_DCHECK(index >= index_);
74 
75     uint32_t old_index = index_;
76     index_ = index;
77 
78     // If we've reached the end of the iterator, just bail out.
79     if (index >= size_)
80       return;
81 
82     uint32_t old_block = old_index / BitVector::Block::kBits;
83     uint32_t new_block = index / BitVector::Block::kBits;
84 
85     // Fast path: we're in the same block so we don't need to do
86     // any other work.
87     if (PERFETTO_LIKELY(old_block == new_block))
88       return;
89 
90     // Slow path: we have to change block so this will involve flushing the old
91     // block and counts (if necessary) and caching the new block.
92     OnBlockChange(old_block, new_block);
93   }
94 
95   // Handles flushing count changes and modified blocks to the bitvector
96   // and caching the new block.
97   void OnBlockChange(uint32_t old_block, uint32_t new_block);
98 
size()99   uint32_t size() const { return size_; }
100 
bv()101   const BitVector& bv() const { return *bv_; }
102 
103  private:
104   BaseIterator(const BaseIterator&) = delete;
105   BaseIterator& operator=(const BaseIterator&) = delete;
106 
block_offset()107   BitVector::BlockOffset block_offset() const {
108     uint16_t bit_idx_inside_block = index_ % BitVector::Block::kBits;
109 
110     BitVector::BlockOffset bo;
111     bo.word_idx = bit_idx_inside_block / BitVector::BitWord::kBits;
112     bo.bit_idx = bit_idx_inside_block % BitVector::BitWord::kBits;
113     return bo;
114   }
115 
116   uint32_t index_ = 0;
117   uint32_t size_ = 0;
118 
119   bool is_block_changed_ = false;
120   int32_t set_bit_count_diff_ = 0;
121 
122   BitVector* bv_ = nullptr;
123 
124   BitVector::Block block_{};
125 };
126 
127 // Iterator over all the bits in a bitvector.
128 class AllBitsIterator : public BaseIterator {
129  public:
130   AllBitsIterator(const BitVector*);
131 
132   // Increments the iterator to point to the next bit.
Next()133   void Next() { SetIndex(index() + 1); }
134 
135   // Increments the iterator to skip the next |n| bits and point to the
136   // following one.
137   // Precondition: n >= 1 & index() + n <= size().
Skip(uint32_t n)138   void Skip(uint32_t n) {
139     PERFETTO_DCHECK(n >= 1);
140     PERFETTO_DCHECK(index() + n <= size());
141 
142     SetIndex(index() + n);
143   }
144 
145   // Returns whether the iterator is valid.
146   operator bool() const { return index() < size(); }
147 };
148 
149 // Iterator over all the set bits in a bitvector.
150 //
151 // This iterator works by first finding a batch of indices of set bits.
152 // Then, the fast path involves simply incrementing a counter to go to
153 // the next index in this batch. On every batch boundary, we hit the
154 // slow path where we need to find another n set bits.
155 class SetBitsIterator : public BaseIterator {
156  public:
157   SetBitsIterator(const BitVector*);
158 
159   // Increments the iterator to point to the next set bit.
Next()160   void Next() {
161     // If we are out of bounds, just bail out.
162     if (PERFETTO_UNLIKELY(++set_bit_index_ >= set_bit_count_))
163       return;
164 
165     if (PERFETTO_UNLIKELY(set_bit_index_ % kBatchSize == 0))
166       ReadSetBitBatch(batch_.back() + 1);
167 
168     SetIndex(batch_[set_bit_index_ % kBatchSize]);
169   }
170 
171   // Returns whether the iterator is valid.
172   operator bool() const { return set_bit_index_ < set_bit_count_; }
173 
174   // Returns the index of the bit interms of set bits (i.e. how many times
175   // Next() has been called).
ordinal()176   uint32_t ordinal() const { return set_bit_index_; }
177 
178  private:
179   static constexpr uint32_t kBatchSize = 1024;
180 
181   // Reads a full batch of set bit indices from the bitvector and stores them
182   // in |batch_| below.
183   //
184   // This batch of indices is used on the fast path to quickly jump between
185   // set bits.
186   void ReadSetBitBatch(uint32_t start_idx);
187 
188   uint32_t set_bit_index_ = 0;
189   uint32_t set_bit_count_ = 0;
190 
191   // Contains an array of indexes; each index points to a set bit in the
192   // bitvector.
193   std::array<uint32_t, kBatchSize> batch_;
194 };
195 
196 }  // namespace internal
197 }  // namespace trace_processor
198 }  // namespace perfetto
199 
200 #endif  // SRC_TRACE_PROCESSOR_CONTAINERS_BIT_VECTOR_ITERATORS_H_
201