| // Copyright 2022 The Fuchsia Authors |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #include <zircon/assert.h> |
| |
| #include <string_view> |
| |
| #include "lib/devicetree/devicetree.h" |
| |
| namespace devicetree { |
| namespace { |
| |
| // Gives an incrementing, pairwise comparison of two ranges of string_view |
| // iterators, returning the first pair of iterators that point to differing |
| // values. |
| template <typename IterA, typename IterB> |
| std::pair<IterA, IterB> CompareStringRanges(IterA start_a, IterA end_a, IterB start_b, |
| IterB end_b) { |
| while (start_a != end_a && start_b != end_b) { |
| if (*start_a != *start_b) { |
| break; |
| } |
| ++start_a; |
| ++start_b; |
| } |
| return {start_a, start_b}; |
| } |
| |
| } // namespace |
| |
| NodePath::Comparison NodePath::CompareWith(const ResolvedPath& path) const { |
| // The use of `std::next()` below would require a bunch of obscure member type |
| // definitions in `StringList<'/'>::iterator`. |
| auto next = [](auto it) { return ++it; }; |
| |
| StringList<'/'> path_prefix = path.Prefix(); |
| auto path_prefix_begin = path_prefix.begin(); |
| auto path_prefix_end = path_prefix.end(); |
| |
| auto [this_prefix_it, path_prefix_it] = |
| CompareStringRanges(begin(), end(), path_prefix_begin, path_prefix_end); |
| |
| // We found a mismatch in the prefix. |
| if (this_prefix_it != end() && path_prefix_it != path_prefix_end) { |
| return Comparison::kMismatch; |
| } |
| |
| // If we iterated through each node in this path and there are still tokens |
| // left in the other path, then this is an ancestor of the other |
| if (this_prefix_it == end() && path_prefix_it != path_prefix_end) { |
| return next(path_prefix_it) == path_prefix_end && path.suffix.empty() |
| ? Comparison::kParent |
| : Comparison::kIndirectAncestor; |
| } |
| |
| StringList<'/'> path_suffix = path.Suffix(); |
| auto path_suffix_begin = path_suffix.begin(); |
| auto path_suffix_end = path_suffix.end(); |
| auto [this_suffix_it, path_suffix_it] = |
| CompareStringRanges(this_prefix_it, end(), path_suffix_begin, path_suffix_end); |
| |
| // We found a mismatch in the suffix. |
| if (this_suffix_it != end() && path_suffix_it != path_suffix_end) { |
| return Comparison::kMismatch; |
| } |
| |
| // As above, if we iterated through each node in this path and there are still |
| // tokens left in the other, then this is an ancestor. |
| if (this_suffix_it == end() && path_suffix_it != path_suffix_end) { |
| return next(path_prefix_it) == path_prefix_end ? Comparison::kParent |
| : Comparison::kIndirectAncestor; |
| } |
| |
| // Similarly, if we iterated through each token in the other path and there |
| // are still nodes left to compare in this one, then this is a descendent. |
| if (this_suffix_it != end() && path_suffix_it == path_suffix_end) { |
| return next(this_suffix_it) == end() ? Comparison::kChild : Comparison::kIndirectDescendent; |
| } |
| |
| return Comparison::kEqual; |
| } |
| |
| NodePath::Comparison NodePath::CompareWith(std::string_view path) const { |
| ZX_ASSERT(path.empty() || path[0] == '/'); |
| return CompareWith(ResolvedPath{.prefix = path}); |
| } |
| |
| } // namespace devicetree |