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