• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_ROW_MAP_H_
18 #define SRC_TRACE_PROCESSOR_CONTAINERS_ROW_MAP_H_
19 
20 #include <stdint.h>
21 
22 #include <memory>
23 #include <vector>
24 
25 #include "perfetto/base/logging.h"
26 #include "perfetto/ext/base/optional.h"
27 #include "src/trace_processor/containers/bit_vector.h"
28 #include "src/trace_processor/containers/bit_vector_iterators.h"
29 
30 namespace perfetto {
31 namespace trace_processor {
32 
33 // Stores a list of row indicies in a space efficient manner. One or more
34 // columns can refer to the same RowMap. The RowMap defines the access pattern
35 // to iterate on rows.
36 //
37 // Implementation details:
38 //
39 // Behind the scenes, this class is impelemented using one of three backing
40 // data-structures:
41 // 1. A start and end index (internally named 'range')
42 // 1. BitVector
43 // 2. std::vector<uint32_t> (internally named IndexVector).
44 //
45 // Generally the preference for data structures is range > BitVector >
46 // std::vector<uint32>; this ordering is based mainly on memory efficiency as we
47 // expect RowMaps to be large.
48 //
49 // However, BitVector and std::vector<uint32_t> allow things which are not
50 // possible with the data-structures preferred to them:
51 //  * a range (as the name suggests) can only store a compact set of indices
52 //  with no holes. A BitVector works around this limitation by storing a 1 at an
53 //  index where that row is part of the RowMap and 0 otherwise.
54 //  * as soon as ordering or duplicate rows come into play, we cannot use a
55 //   BitVector anymore as ordering/duplicate row information cannot be captured
56 //   by a BitVector.
57 //
58 // For small, sparse RowMaps, it is possible that a std::vector<uint32_t> is
59 // more efficient than a BitVector; in this case, we will make a best effort
60 // switch to it but the cases where this happens is not precisely defined.
61 class RowMap {
62  private:
63   // We need to declare these iterator classes before RowMap::Iterator as it
64   // depends on them. However, we don't want to make these public so keep them
65   // under a special private section.
66 
67   // Iterator for ranged mode of RowMap.
68   // This class should act as a drop-in replacement for
69   // BitVector::SetBitsIterator.
70   class RangeIterator {
71    public:
RangeIterator(const RowMap * rm)72     RangeIterator(const RowMap* rm) : rm_(rm), index_(rm->start_idx_) {}
73 
Next()74     void Next() { ++index_; }
75 
76     operator bool() const { return index_ < rm_->end_idx_; }
77 
index()78     uint32_t index() const { return index_; }
79 
ordinal()80     uint32_t ordinal() const { return index_ - rm_->start_idx_; }
81 
82    private:
83     const RowMap* rm_ = nullptr;
84     uint32_t index_ = 0;
85   };
86 
87   // Iterator for index vector mode of RowMap.
88   // This class should act as a drop-in replacement for
89   // BitVector::SetBitsIterator.
90   class IndexVectorIterator {
91    public:
IndexVectorIterator(const RowMap * rm)92     IndexVectorIterator(const RowMap* rm) : rm_(rm) {}
93 
Next()94     void Next() { ++ordinal_; }
95 
96     operator bool() const { return ordinal_ < rm_->index_vector_.size(); }
97 
index()98     uint32_t index() const { return rm_->index_vector_[ordinal_]; }
99 
ordinal()100     uint32_t ordinal() const { return ordinal_; }
101 
102    private:
103     const RowMap* rm_ = nullptr;
104     uint32_t ordinal_ = 0;
105   };
106 
107  public:
108   // Allows efficient iteration over the rows of a RowMap.
109   //
110   // Note: you should usually prefer to use the methods on RowMap directly (if
111   // they exist for the task being attempted) to avoid the lookup for the mode
112   // of the RowMap on every method call.
113   class Iterator {
114    public:
Iterator(const RowMap * rm)115     Iterator(const RowMap* rm) : rm_(rm) {
116       switch (rm->mode_) {
117         case Mode::kRange:
118           range_it_.reset(new RangeIterator(rm));
119           break;
120         case Mode::kBitVector:
121           set_bits_it_.reset(
122               new BitVector::SetBitsIterator(rm->bit_vector_.IterateSetBits()));
123           break;
124         case Mode::kIndexVector:
125           iv_it_.reset(new IndexVectorIterator(rm));
126           break;
127       }
128     }
129 
130     Iterator(Iterator&&) noexcept = default;
131     Iterator& operator=(Iterator&&) = default;
132 
133     // Forwards the iterator to the next row of the RowMap.
Next()134     void Next() {
135       switch (rm_->mode_) {
136         case Mode::kRange:
137           range_it_->Next();
138           break;
139         case Mode::kBitVector:
140           set_bits_it_->Next();
141           break;
142         case Mode::kIndexVector:
143           iv_it_->Next();
144           break;
145       }
146     }
147 
148     // Returns if the iterator is still valid.
149     operator bool() const {
150       switch (rm_->mode_) {
151         case Mode::kRange:
152           return *range_it_;
153         case Mode::kBitVector:
154           return *set_bits_it_;
155         case Mode::kIndexVector:
156           return *iv_it_;
157       }
158       PERFETTO_FATAL("For GCC");
159     }
160 
161     // Returns the row pointed to by this iterator.
row()162     uint32_t row() const {
163       // RowMap uses the row/index nomenclature for referring to the mapping
164       // from index to a row (as the name suggests). However, the data
165       // structures used by RowMap use the index/ordinal naming (because they
166       // don't have the concept of a "row"). Switch the naming here.
167       switch (rm_->mode_) {
168         case Mode::kRange:
169           return range_it_->index();
170         case Mode::kBitVector:
171           return set_bits_it_->index();
172         case Mode::kIndexVector:
173           return iv_it_->index();
174       }
175       PERFETTO_FATAL("For GCC");
176     }
177 
178     // Returns the index of the row the iterator points to.
index()179     uint32_t index() const {
180       // RowMap uses the row/index nomenclature for referring to the mapping
181       // from index to a row (as the name suggests). However, the data
182       // structures used by RowMap use the index/ordinal naming (because they
183       // don't have the concept of a "row"). Switch the naming here.
184       switch (rm_->mode_) {
185         case Mode::kRange:
186           return range_it_->ordinal();
187         case Mode::kBitVector:
188           return set_bits_it_->ordinal();
189         case Mode::kIndexVector:
190           return iv_it_->ordinal();
191       }
192       PERFETTO_FATAL("For GCC");
193     }
194 
195    private:
196     Iterator(const Iterator&) = delete;
197     Iterator& operator=(const Iterator&) = delete;
198 
199     // Only one of the below will be non-null depending on the mode of the
200     // RowMap.
201     std::unique_ptr<RangeIterator> range_it_;
202     std::unique_ptr<BitVector::SetBitsIterator> set_bits_it_;
203     std::unique_ptr<IndexVectorIterator> iv_it_;
204 
205     const RowMap* rm_ = nullptr;
206   };
207 
208   // Enum to allow users of RowMap to decide whether they want to optimize for
209   // memory usage or for speed of lookups.
210   enum class OptimizeFor {
211     kMemory,
212     kLookupSpeed,
213   };
214 
215   // Creates an empty RowMap.
216   // By default this will be implemented using a range.
217   RowMap();
218 
219   // Creates a RowMap containing the range of rows between |start| and |end|
220   // i.e. all rows between |start| (inclusive) and |end| (exclusive).
221   explicit RowMap(uint32_t start,
222                   uint32_t end,
223                   OptimizeFor optimize_for = OptimizeFor::kMemory);
224 
225   // Creates a RowMap backed by a BitVector.
226   explicit RowMap(BitVector bit_vector);
227 
228   // Creates a RowMap backed by an std::vector<uint32_t>.
229   explicit RowMap(std::vector<uint32_t> vec);
230 
231   // Creates a RowMap containing just |row|.
232   // By default this will be implemented using a range.
SingleRow(uint32_t row)233   static RowMap SingleRow(uint32_t row) { return RowMap(row, row + 1); }
234 
235   // Creates a copy of the RowMap.
236   // We have an explicit copy function because RowMap can hold onto large chunks
237   // of memory and we want to be very explicit when making a copy to avoid
238   // accidental leaks and copies.
239   RowMap Copy() const;
240 
241   // Returns the size of the RowMap; that is the number of rows in the RowMap.
size()242   uint32_t size() const {
243     switch (mode_) {
244       case Mode::kRange:
245         return end_idx_ - start_idx_;
246       case Mode::kBitVector:
247         return bit_vector_.GetNumBitsSet();
248       case Mode::kIndexVector:
249         return static_cast<uint32_t>(index_vector_.size());
250     }
251     PERFETTO_FATAL("For GCC");
252   }
253 
254   // Returns whether this rowmap is empty.
empty()255   bool empty() const { return size() == 0; }
256 
257   // Returns the row at index |row|.
Get(uint32_t idx)258   uint32_t Get(uint32_t idx) const {
259     PERFETTO_DCHECK(idx < size());
260     switch (mode_) {
261       case Mode::kRange:
262         return GetRange(idx);
263       case Mode::kBitVector:
264         return GetBitVector(idx);
265       case Mode::kIndexVector:
266         return GetIndexVector(idx);
267     }
268     PERFETTO_FATAL("For GCC");
269   }
270 
271   // Returns whether the RowMap contains the given row.
Contains(uint32_t row)272   bool Contains(uint32_t row) const {
273     switch (mode_) {
274       case Mode::kRange: {
275         return row >= start_idx_ && row < end_idx_;
276       }
277       case Mode::kBitVector: {
278         return row < bit_vector_.size() && bit_vector_.IsSet(row);
279       }
280       case Mode::kIndexVector: {
281         auto it = std::find(index_vector_.begin(), index_vector_.end(), row);
282         return it != index_vector_.end();
283       }
284     }
285     PERFETTO_FATAL("For GCC");
286   }
287 
288   // Returns the first index of the given |row| in the RowMap.
IndexOf(uint32_t row)289   base::Optional<uint32_t> IndexOf(uint32_t row) const {
290     switch (mode_) {
291       case Mode::kRange: {
292         if (row < start_idx_ || row >= end_idx_)
293           return base::nullopt;
294         return row - start_idx_;
295       }
296       case Mode::kBitVector: {
297         return row < bit_vector_.size() && bit_vector_.IsSet(row)
298                    ? base::make_optional(bit_vector_.GetNumBitsSet(row))
299                    : base::nullopt;
300       }
301       case Mode::kIndexVector: {
302         auto it = std::find(index_vector_.begin(), index_vector_.end(), row);
303         return it != index_vector_.end()
304                    ? base::make_optional(static_cast<uint32_t>(
305                          std::distance(index_vector_.begin(), it)))
306                    : base::nullopt;
307       }
308     }
309     PERFETTO_FATAL("For GCC");
310   }
311 
312   // Performs an ordered insert the row into the current RowMap (precondition:
313   // this RowMap is ordered based on the rows it contains).
314   //
315   // Example:
316   // this = [1, 5, 10, 11, 20]
317   // Insert(10)  // this = [1, 5, 10, 11, 20]
318   // Insert(12)  // this = [1, 5, 10, 11, 12, 20]
319   // Insert(21)  // this = [1, 5, 10, 11, 12, 20, 21]
320   // Insert(2)   // this = [1, 2, 5, 10, 11, 12, 20, 21]
321   //
322   // Speecifically, this means that it is only valid to call Insert on a RowMap
323   // which is sorted by the rows it contains; this is automatically true when
324   // the RowMap is in range or BitVector mode but is a required condition for
325   // IndexVector mode.
Insert(uint32_t row)326   void Insert(uint32_t row) {
327     switch (mode_) {
328       case Mode::kRange:
329         if (row == end_idx_) {
330           // Fast path: if we're just appending to the end of the range, we can
331           // stay in range mode and just bump the end index.
332           end_idx_++;
333         } else {
334           // Slow path: the insert is somewhere else other than the end. This
335           // means we need to switch to using a BitVector instead.
336           bit_vector_.Resize(start_idx_, false);
337           bit_vector_.Resize(end_idx_, true);
338           *this = RowMap(std::move(bit_vector_));
339 
340           InsertIntoBitVector(row);
341         }
342         break;
343       case Mode::kBitVector:
344         InsertIntoBitVector(row);
345         break;
346       case Mode::kIndexVector: {
347         PERFETTO_DCHECK(
348             std::is_sorted(index_vector_.begin(), index_vector_.end()));
349         auto it =
350             std::upper_bound(index_vector_.begin(), index_vector_.end(), row);
351         index_vector_.insert(it, row);
352         break;
353       }
354     }
355   }
356 
357   // Updates this RowMap by 'picking' the rows at indicies given by |picker|.
358   // This is easiest to explain with an example; suppose we have the following
359   // RowMaps:
360   // this  : [0, 1, 4, 10, 11]
361   // picker: [0, 3, 4, 4, 2]
362   //
363   // After calling Apply(picker), we now have the following:
364   // this  : [0, 10, 11, 11, 4]
365   //
366   // Conceptually, we are performing the following algorithm:
367   // RowMap rm = Copy()
368   // for (idx : picker)
369   //   rm[i++] = this[idx]
370   // return rm;
SelectRows(const RowMap & selector)371   RowMap SelectRows(const RowMap& selector) const {
372     uint32_t size = selector.size();
373 
374     // If the selector is empty, just return an empty RowMap.
375     if (size == 0u)
376       return RowMap();
377 
378     // If the selector is just picking a single row, just return that row
379     // without any additional overhead.
380     if (size == 1u)
381       return RowMap::SingleRow(Get(selector.Get(0)));
382 
383     // For all other cases, go into the slow-path.
384     return SelectRowsSlow(selector);
385   }
386 
387   // Intersects |other| with |this| writing the result into |this|.
388   // By "intersect", we mean to keep only the rows present in both RowMaps. The
389   // order of the preserved rows will be the same as |this|.
390   //
391   // Conceptually, we are performing the following algorithm:
392   // for (idx : this)
393   //   if (!other.Contains(idx))
394   //     Remove(idx)
Intersect(const RowMap & other)395   void Intersect(const RowMap& other) {
396     if (mode_ == Mode::kRange && other.mode_ == Mode::kRange) {
397       // If both RowMaps have ranges, we can just take the smallest intersection
398       // of them as the new RowMap.
399       // We have this as an explicit fast path as this is very common for
400       // constraints on id and sorted columns to satisfy this condition.
401       start_idx_ = std::max(start_idx_, other.start_idx_);
402       end_idx_ = std::max(start_idx_, std::min(end_idx_, other.end_idx_));
403       return;
404     }
405 
406     // TODO(lalitm): improve efficiency of this if we end up needing it.
407     Filter([&other](uint32_t row) { return other.Contains(row); });
408   }
409 
410   // Filters the current RowMap into the RowMap given by |out| based on the
411   // return value of |p(idx)|.
412   //
413   // Precondition: |out| should be sorted by the rows inside it (this is
414   // required to keep this method efficient). This is automatically true if the
415   // mode is out is Range or BitVector but needs to be enforced if the mode is
416   // IndexVector.
417   //
418   // Specifically, the setup for each of the variables is as follows:
419   //  this: contains the RowMap indices which will be looked up and passed to
420   //        p to filter.
421   //  out : contains indicies into |this| and will be filtered down to only
422   //        contain indicies where p returns true.
423   //  p   : takes an index given by |this| and returns whether the index should
424   //        be retained in |out|.
425   //
426   // Concretely, the algorithm being invoked looks like (but more efficient
427   // based on the mode of |this| and |out|):
428   // for (idx : out)
429   //   this_idx = (*this)[idx]
430   //   if (!p(this_idx))
431   //     out->Remove(idx)
432   template <typename Predicate>
FilterInto(RowMap * out,Predicate p)433   void FilterInto(RowMap* out, Predicate p) const {
434     PERFETTO_DCHECK(size() >= out->size());
435 
436     if (out->empty()) {
437       // If the output RowMap is empty, we don't need to do anything.
438       return;
439     }
440 
441     if (out->size() == 1) {
442       // If the output RowMap has a single entry, just lookup that entry and see
443       // if we should keep it.
444       if (!p(Get(out->Get(0))))
445         *out = RowMap();
446       return;
447     }
448 
449     // TODO(lalitm): investigate whether we should have another fast path for
450     // cases where |out| has only a few entries so we can scan |out| instead of
451     // scanning |this|.
452 
453     // Ideally, we'd always just scan the rows in |out| and keep those which
454     // meet |p|. However, if |this| is a BitVector, we end up needing expensive
455     // |IndexOfNthSet| calls (as we need to lookup the row before passing it to
456     // |p|).
457     switch (mode_) {
458       case Mode::kRange: {
459         auto ip = [this, p](uint32_t idx) { return p(GetRange(idx)); };
460         out->Filter(ip);
461         break;
462       }
463       case Mode::kBitVector: {
464         FilterIntoScanSelfBv(out, p);
465         break;
466       }
467       case Mode::kIndexVector: {
468         auto ip = [this, p](uint32_t row) { return p(GetIndexVector(row)); };
469         out->Filter(ip);
470         break;
471       }
472     }
473   }
474 
475   template <typename Comparator = bool(uint32_t, uint32_t)>
StableSort(std::vector<uint32_t> * out,Comparator c)476   void StableSort(std::vector<uint32_t>* out, Comparator c) const {
477     switch (mode_) {
478       case Mode::kRange:
479         std::stable_sort(out->begin(), out->end(),
480                          [this, c](uint32_t a, uint32_t b) {
481                            return c(GetRange(a), GetRange(b));
482                          });
483         break;
484       case Mode::kBitVector:
485         std::stable_sort(out->begin(), out->end(),
486                          [this, c](uint32_t a, uint32_t b) {
487                            return c(GetBitVector(a), GetBitVector(b));
488                          });
489         break;
490       case Mode::kIndexVector:
491         std::stable_sort(out->begin(), out->end(),
492                          [this, c](uint32_t a, uint32_t b) {
493                            return c(GetIndexVector(a), GetIndexVector(b));
494                          });
495         break;
496     }
497   }
498 
499   // Returns the iterator over the rows in this RowMap.
IterateRows()500   Iterator IterateRows() const { return Iterator(this); }
501 
502   // Returns if the RowMap is internally represented using a range.
IsRange()503   bool IsRange() const { return mode_ == Mode::kRange; }
504 
505  private:
506   enum class Mode {
507     kRange,
508     kBitVector,
509     kIndexVector,
510   };
511 
512   // Filters the indices in |out| by keeping those which meet |p|.
513   template <typename Predicate>
Filter(Predicate p)514   void Filter(Predicate p) {
515     switch (mode_) {
516       case Mode::kRange:
517         FilterRange(p);
518         break;
519       case Mode::kBitVector: {
520         for (auto it = bit_vector_.IterateSetBits(); it; it.Next()) {
521           if (!p(it.index()))
522             it.Clear();
523         }
524         break;
525       }
526       case Mode::kIndexVector: {
527         auto ret = std::remove_if(index_vector_.begin(), index_vector_.end(),
528                                   [p](uint32_t i) { return !p(i); });
529         index_vector_.erase(ret, index_vector_.end());
530         break;
531       }
532     }
533   }
534 
535   // Filters the current RowMap into |out| by performing a full scan on |this|
536   // where |this| is a BitVector.
537   // See |FilterInto| for a full breakdown of the semantics of this function.
538   template <typename Predicate>
FilterIntoScanSelfBv(RowMap * out,Predicate p)539   void FilterIntoScanSelfBv(RowMap* out, Predicate p) const {
540     auto it = bit_vector_.IterateSetBits();
541     switch (out->mode_) {
542       case Mode::kRange: {
543         // TODO(lalitm): investigate whether we can reuse the data inside
544         // out->bit_vector_ at some point.
545         BitVector bv(out->end_idx_, false);
546         for (auto out_it = bv.IterateAllBits(); it; it.Next(), out_it.Next()) {
547           uint32_t ordinal = it.ordinal();
548           if (ordinal < out->start_idx_)
549             continue;
550           if (ordinal >= out->end_idx_)
551             break;
552 
553           if (p(it.index())) {
554             out_it.Set();
555           }
556         }
557         *out = RowMap(std::move(bv));
558         break;
559       }
560       case Mode::kBitVector: {
561         auto out_it = out->bit_vector_.IterateAllBits();
562         for (; out_it; it.Next(), out_it.Next()) {
563           PERFETTO_DCHECK(it);
564           if (out_it.IsSet() && !p(it.index()))
565             out_it.Clear();
566         }
567         break;
568       }
569       case Mode::kIndexVector: {
570         PERFETTO_DCHECK(std::is_sorted(out->index_vector_.begin(),
571                                        out->index_vector_.end()));
572         auto fn = [&p, &it](uint32_t i) {
573           while (it.ordinal() < i) {
574             it.Next();
575             PERFETTO_DCHECK(it);
576           }
577           PERFETTO_DCHECK(it.ordinal() == i);
578           return !p(it.index());
579         };
580         auto iv_it = std::remove_if(out->index_vector_.begin(),
581                                     out->index_vector_.end(), fn);
582         out->index_vector_.erase(iv_it, out->index_vector_.end());
583         break;
584       }
585     }
586   }
587 
588   template <typename Predicate>
FilterRange(Predicate p)589   void FilterRange(Predicate p) {
590     uint32_t count = end_idx_ - start_idx_;
591 
592     // Optimization: if we are only going to scan a few rows, it's not
593     // worth the haslle of working with a BitVector.
594     constexpr uint32_t kSmallRangeLimit = 2048;
595     bool is_small_range = count < kSmallRangeLimit;
596 
597     // Optimization: weif the cost of a BitVector is more than the highest
598     // possible cost an index vector could have, use the index vector.
599     uint32_t bit_vector_cost = BitVector::ApproxBytesCost(end_idx_);
600     uint32_t index_vector_cost_ub = sizeof(uint32_t) * count;
601 
602     // If either of the conditions hold which make it better to use an
603     // index vector, use it instead. Alternatively, if we are optimizing for
604     // lookup speed, we also want to use an index vector.
605     if (is_small_range || index_vector_cost_ub <= bit_vector_cost ||
606         optimize_for_ == OptimizeFor::kLookupSpeed) {
607       // Try and strike a good balance between not making the vector too
608       // big and good performance.
609       std::vector<uint32_t> iv(std::min(kSmallRangeLimit, count));
610 
611       uint32_t out_idx = 0;
612       for (uint32_t i = 0; i < count; ++i) {
613         // If we reach the capacity add another small set of indices.
614         if (PERFETTO_UNLIKELY(out_idx == iv.size()))
615           iv.resize(iv.size() + kSmallRangeLimit);
616 
617         // We keep this branch free by always writing the index but only
618         // incrementing the out index if the return value is true.
619         bool value = p(i + start_idx_);
620         iv[out_idx] = i + start_idx_;
621         out_idx += value;
622       }
623 
624       // Make the vector the correct size and as small as possible.
625       iv.resize(out_idx);
626       iv.shrink_to_fit();
627 
628       *this = RowMap(std::move(iv));
629       return;
630     }
631 
632     // Otherwise, create a bitvector which spans the full range using
633     // |p| as the filler for the bits between start and end.
634     *this = RowMap(BitVector::Range(start_idx_, end_idx_, p));
635   }
636 
InsertIntoBitVector(uint32_t row)637   void InsertIntoBitVector(uint32_t row) {
638     PERFETTO_DCHECK(mode_ == Mode::kBitVector);
639 
640     if (row >= bit_vector_.size())
641       bit_vector_.Resize(row + 1, false);
642     bit_vector_.Set(row);
643   }
644 
GetRange(uint32_t idx)645   PERFETTO_ALWAYS_INLINE uint32_t GetRange(uint32_t idx) const {
646     PERFETTO_DCHECK(mode_ == Mode::kRange);
647     return start_idx_ + idx;
648   }
GetBitVector(uint32_t idx)649   PERFETTO_ALWAYS_INLINE uint32_t GetBitVector(uint32_t idx) const {
650     PERFETTO_DCHECK(mode_ == Mode::kBitVector);
651     return bit_vector_.IndexOfNthSet(idx);
652   }
GetIndexVector(uint32_t idx)653   PERFETTO_ALWAYS_INLINE uint32_t GetIndexVector(uint32_t idx) const {
654     PERFETTO_DCHECK(mode_ == Mode::kIndexVector);
655     return index_vector_[idx];
656   }
657 
658   RowMap SelectRowsSlow(const RowMap& selector) const;
659 
660   Mode mode_ = Mode::kRange;
661 
662   // Only valid when |mode_| == Mode::kRange.
663   uint32_t start_idx_ = 0;  // This is an inclusive index.
664   uint32_t end_idx_ = 0;    // This is an exclusive index.
665 
666   // Only valid when |mode_| == Mode::kBitVector.
667   BitVector bit_vector_;
668 
669   // Only valid when |mode_| == Mode::kIndexVector.
670   std::vector<uint32_t> index_vector_;
671 
672   OptimizeFor optimize_for_ = OptimizeFor::kMemory;
673 };
674 
675 }  // namespace trace_processor
676 }  // namespace perfetto
677 
678 #endif  // SRC_TRACE_PROCESSOR_CONTAINERS_ROW_MAP_H_
679