blob: 2ed2b37fd72821dc5a635e3336ca32725e72b907 [file] [log] [blame]
// 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