Add find and clear methods to structured-header Dictionary type
- Precedent in [`std::map::find`](https://en.cppreference.com/w/cpp/container/map/find)
- Reimplement other indexing methods using `find`
- Explicitly default move constructor and move-assign operator
PiperOrigin-RevId: 607749355
diff --git a/quiche/common/structured_headers.cc b/quiche/common/structured_headers.cc
index 164894b..abbc2b6 100644
--- a/quiche/common/structured_headers.cc
+++ b/quiche/common/structured_headers.cc
@@ -820,21 +820,16 @@
Dictionary::Dictionary() = default;
Dictionary::Dictionary(const Dictionary&) = default;
+Dictionary::Dictionary(Dictionary&&) = default;
Dictionary::Dictionary(std::vector<DictionaryMember> members)
: members_(std::move(members)) {}
Dictionary::~Dictionary() = default;
-std::vector<DictionaryMember>::iterator Dictionary::begin() {
+Dictionary::iterator Dictionary::begin() { return members_.begin(); }
+Dictionary::const_iterator Dictionary::begin() const {
return members_.begin();
}
-std::vector<DictionaryMember>::const_iterator Dictionary::begin() const {
- return members_.begin();
-}
-std::vector<DictionaryMember>::iterator Dictionary::end() {
- return members_.end();
-}
-std::vector<DictionaryMember>::const_iterator Dictionary::end() const {
- return members_.end();
-}
+Dictionary::iterator Dictionary::end() { return members_.end(); }
+Dictionary::const_iterator Dictionary::end() const { return members_.end(); }
ParameterizedMember& Dictionary::operator[](std::size_t idx) {
return members_[idx].second;
}
@@ -846,32 +841,34 @@
return (*this)[idx];
}
ParameterizedMember& Dictionary::operator[](absl::string_view key) {
- auto it = absl::c_find_if(
- members_, [key](const auto& member) { return member.first == key; });
- if (it != members_.end()) return it->second;
- members_.push_back({std::string(key), ParameterizedMember()});
- return members_.back().second;
+ auto it = find(key);
+ if (it != end()) return it->second;
+ return members_.emplace_back(key, ParameterizedMember()).second;
}
ParameterizedMember& Dictionary::at(absl::string_view key) {
- auto it = absl::c_find_if(
- members_, [key](const auto& member) { return member.first == key; });
- QUICHE_CHECK(it != members_.end()) << "Provided key not found in dictionary";
+ auto it = find(key);
+ QUICHE_CHECK(it != end()) << "Provided key not found in dictionary";
return it->second;
}
const ParameterizedMember& Dictionary::at(absl::string_view key) const {
- auto it = absl::c_find_if(
- members_, [key](const auto& member) { return member.first == key; });
- QUICHE_CHECK(it != members_.end()) << "Provided key not found in dictionary";
+ auto it = find(key);
+ QUICHE_CHECK(it != end()) << "Provided key not found in dictionary";
return it->second;
}
+Dictionary::const_iterator Dictionary::find(absl::string_view key) const {
+ return absl::c_find_if(
+ members_, [key](const auto& member) { return member.first == key; });
+}
+Dictionary::iterator Dictionary::find(absl::string_view key) {
+ return absl::c_find_if(
+ members_, [key](const auto& member) { return member.first == key; });
+}
bool Dictionary::empty() const { return members_.empty(); }
std::size_t Dictionary::size() const { return members_.size(); }
bool Dictionary::contains(absl::string_view key) const {
- for (auto& member : members_) {
- if (member.first == key) return true;
- }
- return false;
+ return find(key) != end();
}
+void Dictionary::clear() { members_.clear(); }
std::optional<ParameterizedItem> ParseItem(absl::string_view str) {
StructuredHeaderParser parser(str, StructuredHeaderParser::kFinal);
diff --git a/quiche/common/structured_headers.h b/quiche/common/structured_headers.h
index a9a79cd..32acedd 100644
--- a/quiche/common/structured_headers.h
+++ b/quiche/common/structured_headers.h
@@ -231,9 +231,11 @@
Dictionary();
Dictionary(const Dictionary&);
+ Dictionary(Dictionary&&);
explicit Dictionary(std::vector<DictionaryMember> members);
~Dictionary();
Dictionary& operator=(const Dictionary&) = default;
+ Dictionary& operator=(Dictionary&&) = default;
iterator begin();
const_iterator begin() const;
iterator end();
@@ -253,6 +255,11 @@
ParameterizedMember& at(absl::string_view key);
const ParameterizedMember& at(absl::string_view key) const;
+ const_iterator find(absl::string_view key) const;
+ iterator find(absl::string_view key);
+
+ void clear();
+
bool empty() const;
std::size_t size() const;
bool contains(absl::string_view key) const;
diff --git a/quiche/common/structured_headers_test.cc b/quiche/common/structured_headers_test.cc
index 988a660..e8310f1 100644
--- a/quiche/common/structured_headers_test.cc
+++ b/quiche/common/structured_headers_test.cc
@@ -696,6 +696,21 @@
EXPECT_EQ(member1, dict_init.at(key1));
}
+TEST(StructuredHeaderTest, DictionaryClear) {
+ const std::string key0 = "key0";
+ const ParameterizedMember member0{Item("Applepie"), {}};
+
+ Dictionary dict({{key0, member0}});
+ EXPECT_EQ(1U, dict.size());
+ EXPECT_FALSE(dict.empty());
+ EXPECT_TRUE(dict.contains(key0));
+
+ dict.clear();
+ EXPECT_EQ(0U, dict.size());
+ EXPECT_TRUE(dict.empty());
+ EXPECT_FALSE(dict.contains(key0));
+}
+
TEST(StructuredHeaderTest, DictionaryAccessors) {
const std::string key0 = "key0";
const std::string key1 = "key1";
@@ -712,9 +727,17 @@
EXPECT_EQ(&dict[key0], &dict[0]);
EXPECT_EQ(&dict[key0], &dict.at(0));
+ {
+ auto it = dict.find(key0);
+ ASSERT_TRUE(it != dict.end());
+ EXPECT_EQ(it->first, key0);
+ EXPECT_EQ(it->second, nonempty_member0);
+ }
+
// Even if the key does not yet exist in |dict|, operator[]() should
// automatically create an empty entry.
ASSERT_FALSE(dict.contains(key1));
+ EXPECT_TRUE(dict.find(key1) == dict.end());
ParameterizedMember& member1 = dict[key1];
EXPECT_TRUE(dict.contains(key1));
EXPECT_EQ(empty_member, member1);