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