Remove unnecessary string allocations from structured headers 1. ReadKey can return a view into the input string, allowing creation of owned std::string keys to be deferred until after other validation has occurred. 2. Numbers can be parsed from a view into the input string, as absl::SimpleAtod and absl::SimpleAtoi accept absl::string_view. 3. ReadString can append to the output std::string using a view into the input string. 4. The serializer can return the final std::string by moving out of the std::ostringstream instead of copying. PiperOrigin-RevId: 938557918
diff --git a/quiche/common/structured_headers.cc b/quiche/common/structured_headers.cc index 5a3e3f4..9012bba 100644 --- a/quiche/common/structured_headers.cc +++ b/quiche/common/structured_headers.cc
@@ -14,6 +14,7 @@ #include <vector> #include "absl/algorithm/container.h" +#include "absl/base/attributes.h" #include "absl/container/flat_hash_set.h" #include "absl/strings/ascii.h" #include "absl/strings/escaping.h" @@ -184,7 +185,7 @@ QUICHE_CHECK_EQ(version_, kFinal); Dictionary members; while (!input_.empty()) { - std::optional<std::string> key(ReadKey()); + std::optional<absl::string_view> key(ReadKey()); if (!key) return std::nullopt; std::optional<ParameterizedMember> member; if (ConsumeChar('=')) { @@ -233,7 +234,7 @@ while (ConsumeChar(';')) { SkipWhitespaces(); - std::optional<std::string> name = ReadKey(); + std::optional<absl::string_view> name = ReadKey(); if (!name) return std::nullopt; Item value; @@ -270,12 +271,12 @@ // Parses Parameters ([RFC8941] 4.2.3.2) std::optional<Parameters> ReadParameters() { Parameters parameters; - absl::flat_hash_set<std::string> keys; + absl::flat_hash_set<absl::string_view> keys; while (ConsumeChar(';')) { SkipWhitespaces(); - std::optional<std::string> name = ReadKey(); + std::optional<absl::string_view> name = ReadKey(); if (!name) return std::nullopt; bool is_duplicate_key = !keys.insert(*name).second; @@ -293,7 +294,7 @@ } } } else { - parameters.emplace_back(std::move(*name), std::move(value)); + parameters.emplace_back(*name, std::move(value)); } } return parameters; @@ -323,7 +324,7 @@ } // Parses a Key ([SH09] 4.2.2, [RFC8941] 4.2.3.3). - std::optional<std::string> ReadKey() { + std::optional<absl::string_view> ReadKey() ABSL_ATTRIBUTE_LIFETIME_BOUND { if (version_ == kDraft09) { if (input_.empty() || !absl::ascii_islower(input_.front())) { LogParseError("ReadKey", "lcalpha"); @@ -340,7 +341,7 @@ (version_ == kDraft09 ? kKeyChars09 : kKeyChars); size_t len = input_.find_first_not_of(allowed_chars); if (len == absl::string_view::npos) len = input_.size(); - std::string key(input_.substr(0, len)); + absl::string_view key = input_.substr(0, len); input_.remove_prefix(len); return key; } @@ -410,7 +411,7 @@ "Whether a decimal point is erroneously accepted without any " "digits following it."); } - std::string output_number_string(input_.substr(0, i)); + absl::string_view output_number_string = input_.substr(0, i); input_.remove_prefix(i); if (is_decimal) { @@ -450,7 +451,7 @@ QUICHE_DVLOG(1) << "ReadString: missing closing '\"'"; return std::nullopt; } - s.append(std::string(input_.substr(0, i))); + s.append(input_.substr(0, i)); input_.remove_prefix(i); if (ConsumeChar('\\')) { if (input_.empty()) { @@ -547,7 +548,7 @@ return false; } - void LogParseError(const char* func, const char* expected) { + void LogParseError(const char* func, const char* expected) const { QUICHE_DVLOG(1) << func << ": " << expected << " expected, got " << (input_.empty() ? "EOS" @@ -568,7 +569,7 @@ StructuredHeaderSerializer& operator=(const StructuredHeaderSerializer&) = delete; - std::string Output() { return output_.str(); } + std::string Output() && { return std::move(output_).str(); } // Serializes a List ([RFC8941] 4.1.1). bool WriteList(const List& value) { @@ -938,25 +939,25 @@ std::optional<std::string> SerializeItem(const Item& value) { StructuredHeaderSerializer s; - if (s.WriteItem(ParameterizedItem(value, {}))) return s.Output(); + if (s.WriteItem(ParameterizedItem(value, {}))) return std::move(s).Output(); return std::nullopt; } std::optional<std::string> SerializeItem(const ParameterizedItem& value) { StructuredHeaderSerializer s; - if (s.WriteItem(value)) return s.Output(); + if (s.WriteItem(value)) return std::move(s).Output(); return std::nullopt; } std::optional<std::string> SerializeList(const List& value) { StructuredHeaderSerializer s; - if (s.WriteList(value)) return s.Output(); + if (s.WriteList(value)) return std::move(s).Output(); return std::nullopt; } std::optional<std::string> SerializeDictionary(const Dictionary& value) { StructuredHeaderSerializer s; - if (s.WriteDictionary(value)) return s.Output(); + if (s.WriteDictionary(value)) return std::move(s).Output(); return std::nullopt; }