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);