Add view type to allow serializing unowned structured headers items Callers of SerializeItem without parameters can avoid copying a string into an Item just to serialize it. PiperOrigin-RevId: 982583398
diff --git a/quiche/common/structured_headers.cc b/quiche/common/structured_headers.cc index 0653b5c..152887d 100644 --- a/quiche/common/structured_headers.cc +++ b/quiche/common/structured_headers.cc
@@ -616,10 +616,10 @@ } // Serializes an Item ([RFC8941] 4.1.3). - [[nodiscard]] bool WriteBareItem(const Item& value) { + [[nodiscard]] bool WriteBareItem(ItemView value) { return std::visit( absl::Overload{ - [&](const std::string& string) { + [&](absl::string_view string) { // Serializes a String ([RFC8941] 4.1.6). output_ << "\""; for (const char c : string) { @@ -630,7 +630,7 @@ output_ << "\""; return true; }, - [&](const Item::Token& token) { + [&](const ItemView::Token& token) { // Serializes a Token ([RFC8941] 4.1.7). if (!IsValidToken(token.value)) { return false; @@ -638,7 +638,7 @@ output_ << token.value; return true; }, - [&](const Item::ByteSequence& byte_sequence) { + [&](const ItemView::ByteSequence& byte_sequence) { // Serializes a Byte Sequence ([RFC8941] 4.1.8). output_ << ":"; output_ << absl::Base64Escape(byte_sequence.value); @@ -1142,7 +1142,7 @@ return std::nullopt; } -std::optional<std::string> SerializeItem(const Item& value) { +std::optional<std::string> SerializeItem(ItemView value) { StructuredHeaderSerializer s; if (s.WriteBareItem(value)) return std::move(s).Output(); return std::nullopt; @@ -1166,5 +1166,21 @@ return std::nullopt; } +ItemView::ItemView() = default; +ItemView::ItemView(int64_t value) : value_(value) {} +ItemView::ItemView(double value) : value_(value) {} +ItemView::ItemView(bool value) : value_(value) {} + +ItemView::ItemView(string_t, absl::string_view value) : value_(value) {} + +ItemView::ItemView(token_t, absl::string_view value) : value_(Token(value)) {} + +ItemView::ItemView(byte_sequence_t, absl::string_view value) + : value_(ByteSequence(value)) {} + +ItemView::ItemView(const Item& value) + : value_(std::visit([](const auto& value) { return Variant(value); }, + value.value_)) {} + } // namespace structured_headers } // namespace quiche
diff --git a/quiche/common/structured_headers.h b/quiche/common/structured_headers.h index 5483138..e3b7565 100644 --- a/quiche/common/structured_headers.h +++ b/quiche/common/structured_headers.h
@@ -184,7 +184,7 @@ ItemType Type() const { return static_cast<ItemType>(value_.index()); } private: - friend class StructuredHeaderSerializer; + friend class ItemView; // Wrapper types to permit simplified use of `std::visit`. struct Token { @@ -475,8 +475,75 @@ QUICHE_EXPORT std::optional<Dictionary> ParseDictionary(absl::string_view str, bool strict = false); +class QUICHE_EXPORT ItemView final { + public: + using string_t = Item::string_t; + using token_t = Item::token_t; + using byte_sequence_t = Item::byte_sequence_t; + + inline static constexpr string_t string; + inline static constexpr token_t token; + inline static constexpr byte_sequence_t byte_sequence; + + ItemView(); + ItemView(int64_t value); + ItemView(double value); + ItemView(bool value); + + // Prevent pointers from implicitly converting to bool. + template <typename T> + explicit ItemView(const T*) = delete; + explicit ItemView(std::nullptr_t) = delete; + + ItemView(string_t, absl::string_view value ABSL_ATTRIBUTE_LIFETIME_BOUND); + + ItemView(token_t, absl::string_view value ABSL_ATTRIBUTE_LIFETIME_BOUND); + + ItemView(byte_sequence_t, + absl::string_view value ABSL_ATTRIBUTE_LIFETIME_BOUND); + + ItemView(const Item& value ABSL_ATTRIBUTE_LIFETIME_BOUND); + + ItemView(const ItemView&) = default; + ItemView& operator=(const ItemView&) = default; + + ItemView(ItemView&&) = default; + ItemView& operator=(ItemView&&) = default; + + ~ItemView() = default; + + private: + friend class StructuredHeaderSerializer; + + // Wrapper types to permit simplified use of `std::visit`. + struct Token { + absl::string_view value; + + explicit Token(absl::string_view value ABSL_ATTRIBUTE_LIFETIME_BOUND) + : value(value) {} + + Token(const Item::Token& value ABSL_ATTRIBUTE_LIFETIME_BOUND) + : value(value.value) {} + }; + + struct ByteSequence { + absl::string_view value; + + explicit ByteSequence(absl::string_view value ABSL_ATTRIBUTE_LIFETIME_BOUND) + : value(value) {} + + ByteSequence(const Item::ByteSequence& value ABSL_ATTRIBUTE_LIFETIME_BOUND) + : value(value.value) {} + }; + + using Variant = std::variant<std::monostate, int64_t, double, + absl::string_view, Token, ByteSequence, bool>; + + Variant value_; +}; + // Serialization is implemented for RFC 8941 only. -QUICHE_EXPORT std::optional<std::string> SerializeItem(const Item& value); +QUICHE_EXPORT std::optional<std::string> SerializeItem(ItemView value); QUICHE_EXPORT std::optional<std::string> SerializeItem( const ParameterizedItem& value); QUICHE_EXPORT std::optional<std::string> SerializeList(const List& value);
diff --git a/quiche/web_transport/web_transport_headers.cc b/quiche/web_transport/web_transport_headers.cc index d5a7931..4fa6b2a 100644 --- a/quiche/web_transport/web_transport_headers.cc +++ b/quiche/web_transport/web_transport_headers.cc
@@ -30,6 +30,7 @@ using ::quiche::structured_headers::DictionaryMember; using ::quiche::structured_headers::Item; using ::quiche::structured_headers::ItemTypeToString; +using ::quiche::structured_headers::ItemView; using ::quiche::structured_headers::List; using ::quiche::structured_headers::ParameterizedItem; using ::quiche::structured_headers::ParameterizedMember; @@ -123,9 +124,9 @@ absl::StatusOr<std::string> SerializeSubprotocolResponseHeader( absl::string_view subprotocol) { - Item item(Item::string, subprotocol); std::optional<std::string> serialized = - quiche::structured_headers::SerializeItem(item); + quiche::structured_headers::SerializeItem( + ItemView(ItemView::string, subprotocol)); if (!serialized.has_value()) { return absl::InvalidArgumentError("Invalid subprotocol name supplied"); }