From 97753a06555f3224d74ef10fda7da3e66cd45d88 Mon Sep 17 00:00:00 2001 From: Justin King Date: Fri, 9 Oct 2026 17:21:36 -0700 Subject: [PATCH] Improve variable matching performance and add utilities for matching attributes PiperOrigin-RevId: 996851438 --- base/attribute.h | 25 +++++++++ base/attribute_matcher.h | 97 ++++++++++++++++++++++++++++++++-- base/attribute_matcher_test.cc | 28 ++++++++++ base/attribute_test.cc | 17 ++++++ 4 files changed, 163 insertions(+), 4 deletions(-) diff --git a/base/attribute.h b/base/attribute.h index 2229dc495..521391c68 100644 --- a/base/attribute.h +++ b/base/attribute.h @@ -116,6 +116,8 @@ class AttributeQualifier { AttributeQualifier& operator=(const AttributeQualifier&) = default; AttributeQualifier& operator=(AttributeQualifier&&) = default; + explicit AttributeQualifier(const AttributeQualifierView& other); + Kind kind() const; ABSL_DEPRECATE_AND_INLINE() @@ -324,6 +326,10 @@ class AttributeQualifierView { AttributeQualifierView& operator=(AttributeQualifier&&) = delete; + explicit operator bool() const { + return !std::holds_alternative(value_); + } + ABSL_DEPRECATE_AND_INLINE() std::optional GetInt64Key() const { return AsInt(); } @@ -450,6 +456,25 @@ class AttributeQualifierView { Variant value_; }; +inline AttributeQualifier::AttributeQualifier( + const AttributeQualifierView& other) + : value_(std::visit( + absl::Overload([](std::monostate) -> Variant { return Variant(); }, + [](int64_t value) -> Variant { + return Variant(std::in_place_type, value); + }, + [](uint64_t value) -> Variant { + return Variant(std::in_place_type, value); + }, + [](absl::string_view value) -> Variant { + return Variant(std::in_place_type, + value); + }, + [](bool value) -> Variant { + return Variant(std::in_place_type, value); + }), + common_internal::AsVariant(other))) {} + // AttributeQualifierPattern matches a segment in // attribute resolution path. AttributeQualifierPattern is capable of // matching path elements of types string/int64/uint64/bool. diff --git a/base/attribute_matcher.h b/base/attribute_matcher.h index 69174868e..01d60ba85 100644 --- a/base/attribute_matcher.h +++ b/base/attribute_matcher.h @@ -23,6 +23,7 @@ #include #include "absl/base/attributes.h" +#include "absl/base/optimization.h" #include "absl/functional/function_ref.h" #include "absl/log/absl_check.h" #include "absl/status/status.h" @@ -78,6 +79,32 @@ class AttributeMatcherNode { const AttributeMatcherNode& rhs) const { return lhs < rhs.key(); } + + bool operator()(absl::string_view lhs, + const AttributeQualifierPattern& rhs) const { + // Fast path for top-level variable matching, should only be called in + // that context. + ABSL_DCHECK(rhs.IsString()); + return lhs < rhs.GetString(); + } + + bool operator()(const AttributeQualifierPattern& lhs, + absl::string_view rhs) const { + // Fast path for top-level variable matching, should only be called in + // that context. + ABSL_DCHECK(lhs.IsString()); + return lhs.GetString() < rhs; + } + + bool operator()(absl::string_view lhs, + const AttributeMatcherNode& rhs) const { + return (*this)(lhs, rhs.key()); + } + + bool operator()(const AttributeMatcherNode& lhs, + absl::string_view rhs) const { + return (*this)(lhs.key(), rhs); + } }; AttributeMatcherNode(AttributeMatcherNode* parent, @@ -113,7 +140,21 @@ class AttributeMatcherNode { [[nodiscard]] const AttributeMatcherNode* MatchQualifier( - const cel::AttributeQualifierView& qualifier) const { + const AttributeQualifierView& qualifier) const { + if (children_ != nullptr) { + auto it = children_->find(qualifier); + if (it != children_->end()) { + return &*it; + } + } else if (wildcard_ != nullptr) { + return wildcard_.get(); + } + return nullptr; + } + + [[nodiscard]] + const AttributeMatcherNode* MatchQualifier( + const AttributeQualifier& qualifier) const { if (children_ != nullptr) { auto it = children_->find(qualifier); if (it != children_->end()) { @@ -199,6 +240,7 @@ class [[nodiscard]] AttributeMatch { // match should be handled. AttributeMatch MatchQualifier(const AttributeQualifierView& qualifier) const { ABSL_DCHECK(!IsFull()); + ABSL_DCHECK(qualifier); if (IsNone()) { return AttributeMatch(); } @@ -206,11 +248,19 @@ class [[nodiscard]] AttributeMatch { } private: + friend class AttributeMatcher; friend AttributeMatch common_internal::MakeAttributeMatch( const common_internal::AttributeMatcherNode* node); friend const common_internal::AttributeMatcherNode* common_internal::GetAttributeMatcherNode(const AttributeMatch& match); + AttributeMatch NotNoneMatchQualifier( + const AttributeQualifierView& qualifier) const { + ABSL_DCHECK(!IsNone()); + ABSL_DCHECK(qualifier); + return AttributeMatch(node_->MatchQualifier(qualifier)); + } + explicit AttributeMatch(const common_internal::AttributeMatcherNode* node) : node_(node) {} @@ -273,13 +323,52 @@ class AttributeMatcher { AttributeMatch MatchVariable(absl::string_view variable) const ABSL_ATTRIBUTE_LIFETIME_BOUND { - auto it = children_.find(AttributeQualifierView::OfString(variable)); - if (it != children_.end()) { - return common_internal::MakeAttributeMatch(&*it); + if (!variable.empty()) { + auto it = children_.find(variable); + if (it != children_.end()) { + return common_internal::MakeAttributeMatch(&*it); + } } return AttributeMatch(); } + AttributeMatch MatchAttribute( + absl::string_view variable, + absl::Span qualifiers) const { + AttributeMatch match = MatchVariable(variable); + for (const auto& qualifier : qualifiers) { + if (ABSL_PREDICT_FALSE(!qualifier)) { + return AttributeMatch(); + } + if (match.IsNone()) { + break; + } + match = match.NotNoneMatchQualifier(qualifier); + } + return match; + } + + AttributeMatch MatchAttribute( + absl::string_view variable, + absl::Span qualifiers) const { + AttributeMatch match = MatchVariable(variable); + for (const auto& qualifier : qualifiers) { + if (ABSL_PREDICT_FALSE(!qualifier)) { + return AttributeMatch(); + } + if (match.IsNone()) { + break; + } + match = match.NotNoneMatchQualifier(qualifier); + } + return match; + } + + AttributeMatch MatchAttribute(const Attribute& attribute) const { + return MatchAttribute(attribute.variable_name(), + attribute.qualifier_path()); + } + // Returns an array of attribute patterns corresponding to the // previously added attribute patterns. The order of the attribute // patterns in the array is not guaranteed. diff --git a/base/attribute_matcher_test.cc b/base/attribute_matcher_test.cc index 899f2f03f..32f002e7e 100644 --- a/base/attribute_matcher_test.cc +++ b/base/attribute_matcher_test.cc @@ -423,5 +423,33 @@ TEST(AttributeMatcher, PastFullMatch) { _); } +TEST(AttributeMatcher, MatchAttribute) { + AttributeMatcher matcher; + EXPECT_THAT(matcher.InsertAttribute( + Attribute("foo", {AttributeQualifier::OfString("bar"), + AttributeQualifier::OfString("baz")})), + IsOk()); + EXPECT_THAT(matcher.MatchAttribute( + Attribute("foo", {AttributeQualifier::OfString("bar"), + AttributeQualifier::OfString("baz")})), + AttributeMatchIs(MatchType::FULL)); + EXPECT_THAT( + matcher.MatchAttribute("foo", {AttributeQualifierView::OfString("bar"), + AttributeQualifierView::OfString("baz")}), + AttributeMatchIs(MatchType::FULL)); + EXPECT_THAT(matcher.MatchAttribute( + Attribute("foo", {AttributeQualifier::OfString("bar")})), + AttributeMatchIs(MatchType::PARTIAL)); + EXPECT_THAT( + matcher.MatchAttribute("foo", {AttributeQualifierView::OfString("bar")}), + AttributeMatchIs(MatchType::PARTIAL)); + EXPECT_THAT(matcher.MatchAttribute( + Attribute("bar", {AttributeQualifier::OfString("baz")})), + AttributeMatchIs(MatchType::NONE)); + EXPECT_THAT( + matcher.MatchAttribute("bar", {AttributeQualifierView::OfString("baz")}), + AttributeMatchIs(MatchType::NONE)); +} + } // namespace } // namespace cel diff --git a/base/attribute_test.cc b/base/attribute_test.cc index 945a8569a..d9aa1ef98 100644 --- a/base/attribute_test.cc +++ b/base/attribute_test.cc @@ -148,5 +148,22 @@ TEST(AttributeQualifierView, TransparentOrder) { AttributeQualifierView::OfBool(true)); } +template +void TestAttributeQualifierConversion() { + EXPECT_EQ(To(From::OfInt(1)), To::OfInt(1)); + EXPECT_EQ(To(From::OfUint(1)), To::OfUint(1)); + EXPECT_EQ(To(From::OfString("foo")), To::OfString("foo")); + EXPECT_EQ(To(From::OfBool(true)), To::OfBool(true)); +} + +TEST(AttributeQualifier, Conversion) { + TestAttributeQualifierConversion(); + TestAttributeQualifierConversion(); + TestAttributeQualifierConversion(); +} + } // namespace } // namespace cel