|  | /* | 
|  | * Copyright (C) 2024 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_INTERVAL_INTERSECTOR_H_ | 
|  | #define SRC_TRACE_PROCESSOR_CONTAINERS_INTERVAL_INTERSECTOR_H_ | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <cstddef> | 
|  | #include <cstdint> | 
|  | #include <limits> | 
|  | #include <memory> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "perfetto/base/logging.h" | 
|  | #include "src/trace_processor/containers/interval_tree.h" | 
|  |  | 
|  | namespace perfetto::trace_processor { | 
|  |  | 
|  | // Provides functionality for efficient intersection of a set of intervals with | 
|  | // another interval. Operates in various modes: using interval tree, binary | 
|  | // search of non overlapping intervals or linear scan if the intervals are | 
|  | // overlapping but there is no need for interval tree. | 
|  | class IntervalIntersector { | 
|  | public: | 
|  | // Mode of intersection. Choosing the mode strongly impacts the performance | 
|  | // of intersector. | 
|  | enum Mode { | 
|  | // Use `IntervalTree` as an underlying implementation.. Would create an | 
|  | // interval tree - with complexity of O(N) and memory complexity of O(N). | 
|  | // Query cost is O(logN). | 
|  | // NOTE: Only use if intevals are overlapping and tree would be queried | 
|  | // multiple times. | 
|  | kIntervalTree, | 
|  | // If the intervals are non overlapping we can use simple binary search. | 
|  | // There is no memory cost and algorithmic complexity of O(logN + M), where | 
|  | // logN is the cost of binary search and M is the number of results. | 
|  | // NOTE: Only use if intervals are non overlapping. | 
|  | kBinarySearch, | 
|  | // Slightly better then linear scan, we are looking for the first | 
|  | // overlapping interval and then doing linear scan of the rest. NOTE: Only | 
|  | // use if intervals are overlapping and there would be very few queries. | 
|  | kLinearScan | 
|  | }; | 
|  |  | 
|  | // Creates an interval tree from the vector of intervals if needed. Otherwise | 
|  | // copies the vector of intervals. | 
|  | explicit IntervalIntersector(const std::vector<Interval>& sorted_intervals, | 
|  | Mode mode) | 
|  | : intervals_(sorted_intervals), mode_(mode) { | 
|  | if (sorted_intervals.empty()) { | 
|  | mode_ = kBinarySearch; | 
|  | return; | 
|  | } | 
|  | if (mode_ == kIntervalTree) { | 
|  | tree = std::make_unique<IntervalTree>(intervals_); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Modifies |res| to contain Interval::Id of all intervals that overlap | 
|  | // interval (s, e). | 
|  | template <typename T> | 
|  | void FindOverlaps(uint64_t s, uint64_t e, std::vector<T>& res) const { | 
|  | if (mode_ == kIntervalTree) { | 
|  | tree->FindOverlaps(s, e, res); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Find the first interval that ends after |s|. | 
|  | auto overlap = | 
|  | std::lower_bound(intervals_.begin(), intervals_.end(), s, | 
|  | [](const Interval& interval, uint64_t start) { | 
|  | return interval.end <= start; | 
|  | }); | 
|  |  | 
|  | if (mode_ == kBinarySearch) { | 
|  | for (; overlap != intervals_.end() && overlap->start < e; ++overlap) { | 
|  | UpdateResultVector(s, e, *overlap, res); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | PERFETTO_CHECK(mode_ == kLinearScan); | 
|  |  | 
|  | for (; overlap != intervals_.end() && overlap->start < e; ++overlap) { | 
|  | if (overlap->end <= s) { | 
|  | continue; | 
|  | } | 
|  | if (overlap->start > s) { | 
|  | break; | 
|  | } | 
|  | UpdateResultVector(s, e, *overlap, res); | 
|  | } | 
|  |  | 
|  | for (; overlap != intervals_.end() && overlap->start < e; ++overlap) { | 
|  | UpdateResultVector(s, e, *overlap, res); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Helper function to decide which intersector mode would be in given | 
|  | // situations. Only use if the number of queries is known. | 
|  | static Mode DecideMode(bool is_nonoverlapping, uint32_t queries_count) { | 
|  | if (is_nonoverlapping) { | 
|  | return kBinarySearch; | 
|  | } | 
|  | if (queries_count < 5) { | 
|  | return kLinearScan; | 
|  | } | 
|  | return kIntervalTree; | 
|  | } | 
|  |  | 
|  | private: | 
|  | void UpdateResultVector(uint64_t s, | 
|  | uint64_t e, | 
|  | const Interval& overlap, | 
|  | std::vector<Interval>& res) const { | 
|  | Interval new_int; | 
|  | new_int.start = std::max(s, overlap.start); | 
|  | new_int.end = std::min(e, overlap.end); | 
|  | new_int.id = overlap.id; | 
|  | res.push_back(new_int); | 
|  | } | 
|  |  | 
|  | void UpdateResultVector(uint64_t, | 
|  | uint64_t, | 
|  | const Interval& overlap, | 
|  |  | 
|  | std::vector<Id>& res) const { | 
|  | res.push_back(overlap.id); | 
|  | } | 
|  | const std::vector<Interval>& intervals_; | 
|  | Mode mode_; | 
|  |  | 
|  | // If |use_interval_tree_|. | 
|  | std::unique_ptr<IntervalTree> tree; | 
|  | }; | 
|  |  | 
|  | }  // namespace perfetto::trace_processor | 
|  |  | 
|  | #endif  // SRC_TRACE_PROCESSOR_CONTAINERS_INTERVAL_INTERSECTOR_H_ |