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