Automated g4 rollback of changelist 432178898.

*** Reason for rollback ***

TAP has detected 10 or more targets failed to build at cl/432178898.

TO ROLLFORWARD (without additional approval): Use go/undo-autorollback and consider filing a go/autorollback-bug.

To see all broken targets visit http://test/432178898 or go/newly-broken?p=cl:432178898 if the former is slow to load.
To prevent noise from flakes, TAP double-checked the following target fails to build:
http://sponge2/label%3Atap%20label%3Apostsubmit%20cl%3A432178898%20target%3A%2F%2Fthird_party%2Farcore%2Fjava%2Fcom%2Fgoogle%2Far%2Fcore%3AARCore_arm_32_security_test_audit_android_security
but used to build fine:
http://sponge2/label%3Atap%20label%3Apostsubmit%20cl%3A432178897%20target%3A%2F%2Fthird_party%2Farcore%2Fjava%2Fcom%2Fgoogle%2Far%2Fcore%3AARCore_arm_32_security_test_audit_android_security

Questions? Comments? See the URL: go/autorollback

*** Original change description ***

Simplify QuicStreamSendBuffer::SaveStreamData().

This method is called at two places in production:
QuicCryptoStream::WriteCryptoData() and QuicStream::WriteOrBufferDataAtLevel(),
both times with an iovec created locally from an absl::string_view.  In order to
reduce complexity, this CL changes SaveStreamData() to take an absl::string_view
directly instead of an iovec.

***

PiperOrigin-RevId: 432192634
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index a629827..311cf1f 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -341,12 +341,14 @@
   }
 
   QuicConsumedData SendStreamData(QuicStream* stream) {
+    struct iovec iov;
     if (!QuicUtils::IsCryptoStreamId(connection()->transport_version(),
                                      stream->id()) &&
         connection()->encryption_level() != ENCRYPTION_FORWARD_SECURE) {
       this->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
     }
-    QuicStreamPeer::SendBuffer(stream).SaveStreamData("not empty");
+    MakeIOVector("not empty", &iov);
+    QuicStreamPeer::SendBuffer(stream).SaveStreamData(&iov, 1, 0, 9);
     QuicConsumedData consumed =
         WritevData(stream->id(), 9, 0, FIN, NOT_RETRANSMISSION,
                    GetEncryptionLevelToSendApplicationData());
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 84f0bce..fc4df23 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -230,15 +230,16 @@
   }
 
   QuicConsumedData SaveAndSendStreamData(QuicStreamId id,
-                                         absl::string_view data,
+                                         const struct iovec* iov, int iov_count,
+                                         size_t total_length,
                                          QuicStreamOffset offset,
                                          StreamSendingState state) {
     ScopedPacketFlusher flusher(this);
-    producer_.SaveStreamData(id, data);
+    producer_.SaveStreamData(id, iov, iov_count, 0u, total_length);
     if (notifier_ != nullptr) {
-      return notifier_->WriteOrBufferData(id, data.length(), state);
+      return notifier_->WriteOrBufferData(id, total_length, state);
     }
-    return QuicConnection::SendStreamData(id, data.length(), offset, state);
+    return QuicConnection::SendStreamData(id, total_length, offset, state);
   }
 
   QuicConsumedData SendStreamDataWithString(QuicStreamId id,
@@ -256,7 +257,9 @@
         QuicConnectionPeer::SetAddressValidated(this);
       }
     }
-    return SaveAndSendStreamData(id, data, offset, state);
+    struct iovec iov;
+    MakeIOVector(data, &iov);
+    return SaveAndSendStreamData(id, &iov, 1, data.length(), offset, state);
   }
 
   QuicConsumedData SendApplicationDataAtLevel(EncryptionLevel encryption_level,
@@ -268,7 +271,9 @@
     QUICHE_DCHECK(encryption_level >= ENCRYPTION_ZERO_RTT);
     SetEncrypter(encryption_level, std::make_unique<TaggingEncrypter>(0x01));
     SetDefaultEncryptionLevel(encryption_level);
-    return SaveAndSendStreamData(id, data, offset, state);
+    struct iovec iov;
+    MakeIOVector(data, &iov);
+    return SaveAndSendStreamData(id, &iov, 1, data.length(), offset, state);
   }
 
   QuicConsumedData SendStreamData3() {
@@ -3702,11 +3707,19 @@
 
 TEST_P(QuicConnectionTest, FramePackingSendv) {
   connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+  // Send data in 1 packet by writing multiple blocks in a single iovector
+  // using writev.
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
 
+  char data[] = "ABCDEF";
+  struct iovec iov[2];
+  iov[0].iov_base = data;
+  iov[0].iov_len = 4;
+  iov[1].iov_base = data + 4;
+  iov[1].iov_len = 2;
   QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
       connection_.transport_version(), Perspective::IS_CLIENT);
-  connection_.SaveAndSendStreamData(stream_id, "ABCDEF", 0, NO_FIN);
+  connection_.SaveAndSendStreamData(stream_id, iov, 2, 6, 0, NO_FIN);
 
   EXPECT_EQ(0u, connection_.NumQueuedPackets());
   EXPECT_FALSE(connection_.HasQueuedData());
@@ -3724,12 +3737,19 @@
 
 TEST_P(QuicConnectionTest, FramePackingSendvQueued) {
   connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+  // Try to send two stream frames in 1 packet by using writev.
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
 
   BlockOnNextWrite();
+  char data[] = "ABCDEF";
+  struct iovec iov[2];
+  iov[0].iov_base = data;
+  iov[0].iov_len = 4;
+  iov[1].iov_base = data + 4;
+  iov[1].iov_len = 2;
   QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
       connection_.transport_version(), Perspective::IS_CLIENT);
-  connection_.SaveAndSendStreamData(stream_id, "ABCDEF", 0, NO_FIN);
+  connection_.SaveAndSendStreamData(stream_id, iov, 2, 6, 0, NO_FIN);
 
   EXPECT_EQ(1u, connection_.NumQueuedPackets());
   EXPECT_TRUE(connection_.HasQueuedData());
@@ -3743,10 +3763,7 @@
   EXPECT_EQ(1u, writer_->frame_count());
   EXPECT_EQ(1u, writer_->stream_frames().size());
   EXPECT_EQ(0u, writer_->padding_frames().size());
-  QuicStreamFrame* frame = writer_->stream_frames()[0].get();
-  EXPECT_EQ(stream_id, frame->stream_id);
-  EXPECT_EQ("ABCDEF",
-            absl::string_view(frame->data_buffer, frame->data_length));
+  EXPECT_EQ(stream_id, writer_->stream_frames()[0]->stream_id);
 }
 
 TEST_P(QuicConnectionTest, SendingZeroBytes) {
@@ -3755,7 +3772,7 @@
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
   QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
       connection_.transport_version(), Perspective::IS_CLIENT);
-  connection_.SaveAndSendStreamData(stream_id, {}, 0, FIN);
+  connection_.SaveAndSendStreamData(stream_id, nullptr, 0, 0, 0, FIN);
 
   EXPECT_EQ(0u, connection_.NumQueuedPackets());
   EXPECT_FALSE(connection_.HasQueuedData());
@@ -3788,11 +3805,16 @@
 
   // Send data and ensure the ack is bundled.
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(9);
-  const std::string data(10000, '?');
+  size_t len = 10000;
+  std::unique_ptr<char[]> data_array(new char[len]);
+  memset(data_array.get(), '?', len);
+  struct iovec iov;
+  iov.iov_base = data_array.get();
+  iov.iov_len = len;
   QuicConsumedData consumed = connection_.SaveAndSendStreamData(
-      GetNthClientInitiatedStreamId(0, connection_.transport_version()), data,
-      0, FIN);
-  EXPECT_EQ(data.length(), consumed.bytes_consumed);
+      GetNthClientInitiatedStreamId(0, connection_.transport_version()), &iov,
+      1, len, 0, FIN);
+  EXPECT_EQ(len, consumed.bytes_consumed);
   EXPECT_TRUE(consumed.fin_consumed);
   EXPECT_EQ(0u, connection_.NumQueuedPackets());
   EXPECT_FALSE(connection_.HasQueuedData());
@@ -7387,7 +7409,9 @@
 
   EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF))
       .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame));
-  EXPECT_QUIC_BUG(connection_.SaveAndSendStreamData(3, {}, 0, FIN),
+  struct iovec iov;
+  MakeIOVector("", &iov);
+  EXPECT_QUIC_BUG(connection_.SaveAndSendStreamData(3, &iov, 1, 0, 0, FIN),
                   "Cannot send stream data with level: ENCRYPTION_INITIAL");
   EXPECT_FALSE(connection_.connected());
   EXPECT_EQ(1, connection_close_frame_count_);
@@ -12268,8 +12292,11 @@
       .WillOnce(Invoke([=](const QuicStreamFrame& frame) {
         // Send some data on the stream. The STREAM_FRAME should be built into
         // one packet together with the latter PATH_RESPONSE and PATH_CHALLENGE.
-        const std::string data{"response body"};
-        connection_.producer()->SaveStreamData(frame.stream_id, data);
+        std::string data{"response body"};
+        struct iovec iov;
+        MakeIOVector(data, &iov);
+        connection_.producer()->SaveStreamData(frame.stream_id, &iov, 1, 0u,
+                                               data.length());
         return notifier_.WriteOrBufferData(frame.stream_id, data.length(),
                                            NO_FIN);
       }));
@@ -12338,8 +12365,11 @@
       .WillOnce(Invoke([=](const QuicStreamFrame& frame) {
         // Send some data on the stream. The STREAM_FRAME should be built into a
         // new packet but throttled by anti-amplifciation limit.
-        const std::string data{"response body"};
-        connection_.producer()->SaveStreamData(frame.stream_id, data);
+        std::string data{"response body"};
+        struct iovec iov;
+        MakeIOVector(data, &iov);
+        connection_.producer()->SaveStreamData(frame.stream_id, &iov, 1, 0u,
+                                               data.length());
         return notifier_.WriteOrBufferData(frame.stream_id, data.length(),
                                            NO_FIN);
       }));
@@ -12379,8 +12409,11 @@
       .WillRepeatedly(Invoke([=](const QuicStreamFrame& frame) {
         // Send some data on the stream. The STREAM_FRAME should be built into
         // one packet together with the latter PATH_RESPONSE.
-        const std::string data{"response body"};
-        connection_.producer()->SaveStreamData(frame.stream_id, data);
+        std::string data{"response body"};
+        struct iovec iov;
+        MakeIOVector(data, &iov);
+        connection_.producer()->SaveStreamData(frame.stream_id, &iov, 1, 0u,
+                                               data.length());
         return notifier_.WriteOrBufferData(frame.stream_id, data.length(),
                                            NO_FIN);
       }));
diff --git a/quic/core/quic_crypto_stream.cc b/quic/core/quic_crypto_stream.cc
index 5690743..b579ff5 100644
--- a/quic/core/quic_crypto_stream.cc
+++ b/quic/core/quic_crypto_stream.cc
@@ -140,9 +140,11 @@
   }
   const bool had_buffered_data = HasBufferedCryptoFrames();
   // Append |data| to the send buffer for this encryption level.
+  struct iovec iov(QuicUtils::MakeIovec(data));
   QuicStreamSendBuffer* send_buffer = &substreams_[level].send_buffer;
   QuicStreamOffset offset = send_buffer->stream_offset();
-  send_buffer->SaveStreamData(data);
+  send_buffer->SaveStreamData(&iov, /*iov_count=*/1, /*iov_offset=*/0,
+                              data.length());
   if (kMaxStreamLength - offset < data.length()) {
     QUIC_BUG(quic_bug_10322_2) << "Writing too much crypto handshake data";
     // TODO(nharper): Switch this to an IETF QUIC error code, possibly
diff --git a/quic/core/quic_packet_creator_test.cc b/quic/core/quic_packet_creator_test.cc
index f038e33..3b56f82 100644
--- a/quic/core/quic_packet_creator_test.cc
+++ b/quic/core/quic_packet_creator_test.cc
@@ -104,18 +104,24 @@
         producer_(producer),
         version_(framer->version()) {}
 
-  bool ConsumeDataToFillCurrentPacket(QuicStreamId id, absl::string_view data,
-                                      QuicStreamOffset offset, bool fin,
+  bool ConsumeDataToFillCurrentPacket(QuicStreamId id,
+                                      const struct iovec* iov,
+                                      int iov_count,
+                                      size_t total_length,
+                                      size_t iov_offset,
+                                      QuicStreamOffset offset,
+                                      bool fin,
                                       bool needs_full_padding,
                                       TransmissionType transmission_type,
                                       QuicFrame* frame) {
     // Save data before data is consumed.
-    if (!data.empty()) {
-      producer_->SaveStreamData(id, data);
+    QuicByteCount data_length = total_length - iov_offset;
+    if (data_length > 0) {
+      producer_->SaveStreamData(id, iov, iov_count, iov_offset, data_length);
     }
     return QuicPacketCreator::ConsumeDataToFillCurrentPacket(
-        id, data.length(), offset, fin, needs_full_padding, transmission_type,
-        frame);
+        id, data_length - iov_offset, offset, fin, needs_full_padding,
+        transmission_type, frame);
   }
 
   void StopSendingVersion() {
@@ -277,6 +283,7 @@
   StrictMock<MockFramerVisitor> framer_visitor_;
   StrictMock<MockPacketCreatorDelegate> delegate_;
   std::string data_;
+  struct iovec iov_;
   TestPacketCreator creator_;
   std::unique_ptr<SerializedPacket> serialized_packet_;
   SimpleDataProducer producer_;
@@ -377,11 +384,12 @@
 TEST_P(QuicPacketCreatorTest, ConsumeDataToFillCurrentPacket) {
   creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
   QuicFrame frame;
+  MakeIOVector("test", &iov_);
   QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
       client_framer_.transport_version(), Perspective::IS_CLIENT);
-  const std::string data("test");
   ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket(
-      stream_id, data, 0u, false, false, NOT_RETRANSMISSION, &frame));
+      stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u, false, false,
+      NOT_RETRANSMISSION, &frame));
   size_t consumed = frame.stream_frame.data_length;
   EXPECT_EQ(4u, consumed);
   CheckStreamFrame(frame, stream_id, "test", 0u, false);
@@ -391,11 +399,12 @@
 TEST_P(QuicPacketCreatorTest, ConsumeDataFin) {
   creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
   QuicFrame frame;
+  MakeIOVector("test", &iov_);
   QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
       client_framer_.transport_version(), Perspective::IS_CLIENT);
-  const std::string data("test");
   ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket(
-      stream_id, data, 0u, true, false, NOT_RETRANSMISSION, &frame));
+      stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u, true, false,
+      NOT_RETRANSMISSION, &frame));
   size_t consumed = frame.stream_frame.data_length;
   EXPECT_EQ(4u, consumed);
   CheckStreamFrame(frame, stream_id, "test", 0u, true);
@@ -408,7 +417,8 @@
   QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
       client_framer_.transport_version(), Perspective::IS_CLIENT);
   ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket(
-      stream_id, {}, 0u, true, false, NOT_RETRANSMISSION, &frame));
+      stream_id, nullptr, 0u, 0u, 0u, 0u, true, false, NOT_RETRANSMISSION,
+      &frame));
   size_t consumed = frame.stream_frame.data_length;
   EXPECT_EQ(0u, consumed);
   CheckStreamFrame(frame, stream_id, std::string(), 0u, true);
@@ -435,13 +445,13 @@
                                              kOffset, /* data_size=*/0xffff));
     if (should_have_room) {
       QuicFrame frame;
-      const std::string data("testdata");
+      MakeIOVector("testdata", &iov_);
       EXPECT_CALL(delegate_, OnSerializedPacket(_))
           .WillRepeatedly(Invoke(
               this, &QuicPacketCreatorTest::ClearSerializedPacketForTests));
       ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket(
-          GetNthClientInitiatedStreamId(1), data, kOffset, false, false,
-          NOT_RETRANSMISSION, &frame));
+          GetNthClientInitiatedStreamId(1), &iov_, 1u, iov_.iov_len, 0u,
+          kOffset, false, false, NOT_RETRANSMISSION, &frame));
       size_t bytes_consumed = frame.stream_frame.data_length;
       EXPECT_LT(0u, bytes_consumed);
       creator_.FlushCurrentPacket();
@@ -462,9 +472,10 @@
     std::string data(capacity + delta, 'A');
     size_t bytes_free = delta > 0 ? 0 : 0 - delta;
     QuicFrame frame;
+    MakeIOVector(data, &iov_);
     ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket(
-        GetNthClientInitiatedStreamId(1), data, kOffset, false, false,
-        NOT_RETRANSMISSION, &frame));
+        GetNthClientInitiatedStreamId(1), &iov_, 1u, iov_.iov_len, 0u, kOffset,
+        false, false, NOT_RETRANSMISSION, &frame));
 
     // BytesFree() returns bytes available for the next frame, which will
     // be two bytes smaller since the stream frame would need to be grown.
@@ -502,6 +513,7 @@
     size_t bytes_free = delta > 0 ? 0 : 0 - delta;
 
     QuicFrame frame;
+    MakeIOVector(data, &iov_);
     EXPECT_CALL(delegate_, OnSerializedPacket(_))
         .WillRepeatedly(
             Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
@@ -512,7 +524,8 @@
     if (!QuicVersionUsesCryptoFrames(client_framer_.transport_version())) {
       ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket(
           QuicUtils::GetCryptoStreamId(client_framer_.transport_version()),
-          data, kOffset, false, true, NOT_RETRANSMISSION, &frame));
+          &iov_, 1u, iov_.iov_len, 0u, kOffset, false, true, NOT_RETRANSMISSION,
+          &frame));
       size_t bytes_consumed = frame.stream_frame.data_length;
       EXPECT_LT(0u, bytes_consumed);
     } else {
@@ -556,11 +569,12 @@
     size_t bytes_free = delta > 0 ? 0 : 0 - delta;
 
     QuicFrame frame;
+    MakeIOVector(data, &iov_);
     EXPECT_CALL(delegate_, OnSerializedPacket(_))
         .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
     ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket(
-        GetNthClientInitiatedStreamId(1), data, kOffset, false, false,
-        NOT_RETRANSMISSION, &frame));
+        GetNthClientInitiatedStreamId(1), &iov_, 1u, iov_.iov_len, 0u, kOffset,
+        false, false, NOT_RETRANSMISSION, &frame));
     size_t bytes_consumed = frame.stream_frame.data_length;
     EXPECT_LT(0u, bytes_consumed);
     creator_.FlushCurrentPacket();
@@ -1384,13 +1398,14 @@
   QuicFrame frame;
   size_t payload_length = creator_.max_packet_length();
   const std::string too_long_payload(payload_length, 'a');
+  MakeIOVector(too_long_payload, &iov_);
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
       .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
   QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
       client_framer_.transport_version(), Perspective::IS_CLIENT);
   ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket(
-      stream_id, too_long_payload, 0u, true, false, NOT_RETRANSMISSION,
-      &frame));
+      stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u, true, false,
+      NOT_RETRANSMISSION, &frame));
   size_t consumed = frame.stream_frame.data_length;
   // The entire payload could not be consumed.
   EXPECT_GT(payload_length, consumed);
@@ -1435,10 +1450,11 @@
   EXPECT_FALSE(creator_.HasPendingStreamFramesOfStream(stream_id));
 
   QuicFrame frame;
-  const std::string data("test");
+  MakeIOVector("test", &iov_);
   EXPECT_CALL(debug, OnFrameAddedToPacket(_));
   ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket(
-      stream_id, data, 0u, false, false, NOT_RETRANSMISSION, &frame));
+      stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u, false, false,
+      NOT_RETRANSMISSION, &frame));
   size_t consumed = frame.stream_frame.data_length;
   EXPECT_EQ(4u, consumed);
   EXPECT_TRUE(creator_.HasPendingFrames());
@@ -1488,8 +1504,9 @@
   }
   EXPECT_FALSE(creator_.HasPendingFrames());
 
-  const std::string data("test");
-  producer_.SaveStreamData(GetNthClientInitiatedStreamId(0), data);
+  MakeIOVector("test", &iov_);
+  producer_.SaveStreamData(GetNthClientInitiatedStreamId(0), &iov_, 1u, 0u,
+                           iov_.iov_len);
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
       .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
   size_t num_bytes_consumed;
@@ -1497,7 +1514,7 @@
   creator_.set_debug_delegate(&debug);
   EXPECT_CALL(debug, OnFrameAddedToPacket(_));
   creator_.CreateAndSerializeStreamFrame(
-      GetNthClientInitiatedStreamId(0), data.length(), 0, 0, true,
+      GetNthClientInitiatedStreamId(0), iov_.iov_len, 0, 0, true,
       NOT_RETRANSMISSION, &num_bytes_consumed);
   EXPECT_EQ(4u, num_bytes_consumed);
 
@@ -1524,13 +1541,14 @@
   EXPECT_FALSE(creator_.HasPendingFrames());
 
   // Send one byte of stream data.
-  const std::string data("a");
-  producer_.SaveStreamData(GetNthClientInitiatedStreamId(0), data);
+  MakeIOVector("a", &iov_);
+  producer_.SaveStreamData(GetNthClientInitiatedStreamId(0), &iov_, 1u, 0u,
+                           iov_.iov_len);
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
       .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
   size_t num_bytes_consumed;
   creator_.CreateAndSerializeStreamFrame(
-      GetNthClientInitiatedStreamId(0), data.length(), 0, 0, true,
+      GetNthClientInitiatedStreamId(0), iov_.iov_len, 0, 0, true,
       NOT_RETRANSMISSION, &num_bytes_consumed);
   EXPECT_EQ(1u, num_bytes_consumed);
 
@@ -1603,13 +1621,16 @@
   std::unique_ptr<QuicData> message_data;
   message_data = framer.ConstructHandshakeMessage(message);
 
+  struct iovec iov;
+  MakeIOVector(absl::string_view(message_data->data(), message_data->length()),
+               &iov);
   QuicFrame frame;
   EXPECT_CALL(delegate_, OnUnrecoverableError(QUIC_CRYPTO_CHLO_TOO_LARGE, _));
   EXPECT_QUIC_BUG(
       creator_.ConsumeDataToFillCurrentPacket(
           QuicUtils::GetCryptoStreamId(client_framer_.transport_version()),
-          absl::string_view(message_data->data(), message_data->length()), 0u,
-          false, false, NOT_RETRANSMISSION, &frame),
+          &iov, 1u, iov.iov_len, 0u, 0u, false, false, NOT_RETRANSMISSION,
+          &frame),
       "Client hello won't fit in a single packet.");
 }
 
@@ -1644,11 +1665,11 @@
   creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
   creator_.AddPendingPadding(kMaxNumRandomPaddingBytes);
   QuicFrame frame;
+  MakeIOVector("test", &iov_);
   QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
       client_framer_.transport_version(), Perspective::IS_CLIENT);
-  const std::string data("test");
   ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket(
-      stream_id, data, 0u, false,
+      stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u, false,
       /*needs_full_padding=*/true, NOT_RETRANSMISSION, &frame));
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
       .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
@@ -1679,16 +1700,18 @@
       .WillRepeatedly(
           Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
   // Send stream frame of size kStreamFramePayloadSize.
-  creator_.ConsumeDataToFillCurrentPacket(
-      stream_id, absl::string_view(buf, kStreamFramePayloadSize), 0u, false,
-      false, NOT_RETRANSMISSION, &frame);
+  MakeIOVector(absl::string_view(buf, kStreamFramePayloadSize), &iov_);
+  creator_.ConsumeDataToFillCurrentPacket(stream_id, &iov_, 1u, iov_.iov_len,
+                                          0u, 0u, false, false,
+                                          NOT_RETRANSMISSION, &frame);
   creator_.FlushCurrentPacket();
   // 1 byte padding is sent.
   EXPECT_EQ(pending_padding_bytes - 1, creator_.pending_padding_bytes());
   // Send stream frame of size kStreamFramePayloadSize + 1.
-  creator_.ConsumeDataToFillCurrentPacket(
-      stream_id, absl::string_view(buf, kStreamFramePayloadSize + 1),
-      kStreamFramePayloadSize, false, false, NOT_RETRANSMISSION, &frame);
+  MakeIOVector(absl::string_view(buf, kStreamFramePayloadSize + 1), &iov_);
+  creator_.ConsumeDataToFillCurrentPacket(stream_id, &iov_, 1u, iov_.iov_len,
+                                          0u, kStreamFramePayloadSize, false,
+                                          false, NOT_RETRANSMISSION, &frame);
   // No padding is sent.
   creator_.FlushCurrentPacket();
   EXPECT_EQ(pending_padding_bytes - 1, creator_.pending_padding_bytes());
@@ -1707,11 +1730,11 @@
   EXPECT_CALL(delegate_, GetPacketBuffer()).WillOnce(Return(external_buffer));
 
   QuicFrame frame;
+  MakeIOVector("test", &iov_);
   QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
       client_framer_.transport_version(), Perspective::IS_CLIENT);
-  const std::string data("test");
   ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket(
-      stream_id, data, 0u, false,
+      stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u, false,
       /*needs_full_padding=*/true, NOT_RETRANSMISSION, &frame));
 
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
@@ -1769,11 +1792,12 @@
   creator_.FlushCurrentPacket();
 
   QuicFrame frame;
+  MakeIOVector("test", &iov_);
   QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
       client_framer_.transport_version(), Perspective::IS_CLIENT);
-  const std::string data("test");
   EXPECT_TRUE(creator_.ConsumeDataToFillCurrentPacket(
-      stream_id, data, 0u, false, false, NOT_RETRANSMISSION, &frame));
+      stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u, false, false,
+      NOT_RETRANSMISSION, &frame));
   QuicMessageFrame* frame4 =
       new QuicMessageFrame(4, MemSliceFromString("message"));
   EXPECT_TRUE(creator_.AddFrame(QuicFrame(frame4), NOT_RETRANSMISSION));
@@ -2038,38 +2062,43 @@
   StrictMock<MockDebugDelegate> debug;
   creator_.set_debug_delegate(&debug);
 
+  MakeIOVector("test", &iov_);
   QuicFrame frame;
-  const std::string data1("test");
   EXPECT_CALL(debug, OnFrameAddedToPacket(_));
   ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket(
-      stream_id1, data1, 0u, false, false, NOT_RETRANSMISSION, &frame));
+      stream_id1, &iov_, 1u, iov_.iov_len, 0u, 0u, false, false,
+      NOT_RETRANSMISSION, &frame));
   EXPECT_TRUE(creator_.HasPendingFrames());
   EXPECT_TRUE(creator_.HasPendingStreamFramesOfStream(stream_id1));
 
-  const std::string data2("coalesce");
+  MakeIOVector("coalesce", &iov_);
   // frame will be coalesced with the first frame.
   const auto previous_size = creator_.PacketSize();
-  QuicStreamFrame target(stream_id1, true, 0, data1.length() + data2.length());
+  QuicStreamFrame target(stream_id1, true, 0, 12);
   EXPECT_CALL(debug, OnStreamFrameCoalesced(target));
   ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket(
-      stream_id1, data2, 4u, true, false, NOT_RETRANSMISSION, &frame));
+      stream_id1, &iov_, 1u, iov_.iov_len, 0u, 4u, true, false,
+      NOT_RETRANSMISSION, &frame));
   EXPECT_EQ(frame.stream_frame.data_length,
             creator_.PacketSize() - previous_size);
 
   // frame is for another stream, so it won't be coalesced.
   const auto length = creator_.BytesFree() - 10u;
-  const std::string data3(length, 'x');
+  std::string large_data(length, 'x');
+  MakeIOVector(large_data, &iov_);
   EXPECT_CALL(debug, OnFrameAddedToPacket(_));
   ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket(
-      stream_id2, data3, 0u, false, false, NOT_RETRANSMISSION, &frame));
+      stream_id2, &iov_, 1u, iov_.iov_len, 0u, 0u, false, false,
+      NOT_RETRANSMISSION, &frame));
   EXPECT_TRUE(creator_.HasPendingStreamFramesOfStream(stream_id2));
 
   // The packet doesn't have enough free bytes for all data, but will still be
   // able to consume and coalesce part of them.
   EXPECT_CALL(debug, OnStreamFrameCoalesced(_));
-  const std::string data4("somerandomdata");
+  MakeIOVector("somerandomdata", &iov_);
   ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket(
-      stream_id2, data4, length, false, false, NOT_RETRANSMISSION, &frame));
+      stream_id2, &iov_, 1u, iov_.iov_len, 0u, length, false, false,
+      NOT_RETRANSMISSION, &frame));
 
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
       .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
@@ -2225,12 +2254,14 @@
   // restored.
   creator_.SetSoftMaxPacketLength(overhead);
   EXPECT_EQ(overhead, creator_.max_packet_length());
-  const std::string data = "crypto data";
+  std::string data = "crypto data";
+  MakeIOVector(data, &iov_);
   QuicFrame frame;
   if (!QuicVersionUsesCryptoFrames(client_framer_.transport_version())) {
     ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket(
-        QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), data,
-        kOffset, false, true, NOT_RETRANSMISSION, &frame));
+        QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_,
+        1u, iov_.iov_len, 0u, kOffset, false, true, NOT_RETRANSMISSION,
+        &frame));
     size_t bytes_consumed = frame.stream_frame.data_length;
     EXPECT_LT(0u, bytes_consumed);
   } else {
@@ -2386,28 +2417,34 @@
   }
 
   QuicConsumedData ConsumeDataFastPath(QuicStreamId id,
-                                       absl::string_view data) {
+                                       const struct iovec* iov,
+                                       int iov_count,
+                                       size_t total_length,
+                                       QuicStreamOffset offset,
+                                       bool fin) {
     // Save data before data is consumed.
-    if (!data.empty()) {
-      producer_->SaveStreamData(id, data);
+    if (total_length > 0) {
+      producer_->SaveStreamData(id, iov, iov_count, 0, total_length);
     }
-    return QuicPacketCreator::ConsumeDataFastPath(id, data.length(),
-                                                  /* offset = */ 0,
-                                                  /* fin = */ true, 0);
+    return QuicPacketCreator::ConsumeDataFastPath(id, total_length, offset, fin,
+                                                  0);
   }
 
-  QuicConsumedData ConsumeData(QuicStreamId id, absl::string_view data,
+  QuicConsumedData ConsumeData(QuicStreamId id,
+                               const struct iovec* iov,
+                               int iov_count,
+                               size_t total_length,
                                QuicStreamOffset offset,
                                StreamSendingState state) {
     // Save data before data is consumed.
-    if (!data.empty()) {
-      producer_->SaveStreamData(id, data);
+    if (total_length > 0) {
+      producer_->SaveStreamData(id, iov, iov_count, 0, total_length);
     }
     if (!has_ack() && delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA,
                                                       NOT_HANDSHAKE)) {
       EXPECT_CALL(*delegate_, MaybeBundleAckOpportunistically()).Times(1);
     }
-    return QuicPacketCreator::ConsumeData(id, data.length(), offset, state);
+    return QuicPacketCreator::ConsumeData(id, total_length, offset, state);
   }
 
   MessageStatus AddMessageFrame(QuicMessageId message_id,
@@ -2554,6 +2591,13 @@
     }
   }
 
+  void CreateData(size_t len) {
+    data_array_.reset(new char[len]);
+    memset(data_array_.get(), '?', len);
+    iov_.iov_base = data_array_.get();
+    iov_.iov_len = len;
+  }
+
   QuicFramer framer_;
   MockRandom random_creator_;
   StrictMock<MockDelegate> delegate_;
@@ -2586,13 +2630,14 @@
        WrongEncryptionLevelForStreamDataFastPath) {
   creator_.set_encryption_level(ENCRYPTION_HANDSHAKE);
   delegate_.SetCanWriteAnything();
-  const std::string data(10000, '?');
+  // Create a 10000 byte IOVector.
+  CreateData(10000);
   EXPECT_CALL(delegate_, OnSerializedPacket(_)).Times(0);
   EXPECT_CALL(delegate_, OnUnrecoverableError(_, _));
   EXPECT_QUIC_BUG(creator_.ConsumeDataFastPath(
                       QuicUtils::GetFirstBidirectionalStreamId(
                           framer_.transport_version(), Perspective::IS_CLIENT),
-                      data),
+                      &iov_, 1u, iov_.iov_len, 0, true),
                   "");
 }
 
@@ -2689,10 +2734,11 @@
 TEST_F(QuicPacketCreatorMultiplePacketsTest, ConsumeData_NotWritable) {
   delegate_.SetCanNotWrite();
 
+  MakeIOVector("foo", &iov_);
   QuicConsumedData consumed = creator_.ConsumeData(
       QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(),
                                                Perspective::IS_CLIENT),
-      "foo", 0, FIN);
+      &iov_, 1u, iov_.iov_len, 0, FIN);
   EXPECT_EQ(0u, consumed.bytes_consumed);
   EXPECT_FALSE(consumed.fin_consumed);
   EXPECT_FALSE(creator_.HasPendingFrames());
@@ -2703,10 +2749,11 @@
        ConsumeData_WritableAndShouldNotFlush) {
   delegate_.SetCanWriteAnything();
 
+  MakeIOVector("foo", &iov_);
   QuicConsumedData consumed = creator_.ConsumeData(
       QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(),
                                                Perspective::IS_CLIENT),
-      "foo", 0, FIN);
+      &iov_, 1u, iov_.iov_len, 0, FIN);
   EXPECT_EQ(3u, consumed.bytes_consumed);
   EXPECT_TRUE(consumed.fin_consumed);
   EXPECT_TRUE(creator_.HasPendingFrames());
@@ -2720,10 +2767,11 @@
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
       .WillOnce(
           Invoke(this, &QuicPacketCreatorMultiplePacketsTest::SavePacket));
+  MakeIOVector("foo", &iov_);
   QuicConsumedData consumed = creator_.ConsumeData(
       QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(),
                                                Perspective::IS_CLIENT),
-      "foo", 0, FIN);
+      &iov_, 1u, iov_.iov_len, 0, FIN);
   creator_.Flush();
   EXPECT_EQ(3u, consumed.bytes_consumed);
   EXPECT_TRUE(consumed.fin_consumed);
@@ -2744,7 +2792,8 @@
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
       .WillOnce(
           Invoke(this, &QuicPacketCreatorMultiplePacketsTest::SavePacket));
-  const std::string data = "foo bar";
+  std::string data = "foo bar";
+  MakeIOVector(data, &iov_);
   size_t consumed_bytes = 0;
   if (QuicVersionUsesCryptoFrames(framer_.transport_version())) {
     consumed_bytes = creator_.ConsumeCryptoData(ENCRYPTION_INITIAL, data, 0);
@@ -2752,8 +2801,8 @@
     consumed_bytes =
         creator_
             .ConsumeData(
-                QuicUtils::GetCryptoStreamId(framer_.transport_version()), data,
-                0, NO_FIN)
+                QuicUtils::GetCryptoStreamId(framer_.transport_version()),
+                &iov_, 1u, iov_.iov_len, 0, NO_FIN)
             .bytes_consumed;
   }
   EXPECT_EQ(7u, consumed_bytes);
@@ -2785,7 +2834,8 @@
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
       .WillOnce(
           Invoke(this, &QuicPacketCreatorMultiplePacketsTest::SavePacket));
-  const std::string data = "foo";
+  std::string data = "foo";
+  MakeIOVector(data, &iov_);
   size_t bytes_consumed = 0;
   if (QuicVersionUsesCryptoFrames(framer_.transport_version())) {
     bytes_consumed = creator_.ConsumeCryptoData(ENCRYPTION_INITIAL, data, 0);
@@ -2793,8 +2843,8 @@
     bytes_consumed =
         creator_
             .ConsumeData(
-                QuicUtils::GetCryptoStreamId(framer_.transport_version()), data,
-                0, NO_FIN)
+                QuicUtils::GetCryptoStreamId(framer_.transport_version()),
+                &iov_, 1u, iov_.iov_len, 0, NO_FIN)
             .bytes_consumed;
   }
   EXPECT_EQ(3u, bytes_consumed);
@@ -2832,7 +2882,7 @@
   EXPECT_QUIC_BUG(creator_.ConsumeData(
                       QuicUtils::QuicUtils::GetFirstBidirectionalStreamId(
                           framer_.transport_version(), Perspective::IS_CLIENT),
-                      {}, 0, NO_FIN),
+                      nullptr, 0, 0, 0, NO_FIN),
                   "Attempt to consume empty data without FIN.");
 }
 
@@ -2840,10 +2890,13 @@
        ConsumeDataMultipleTimes_WritableAndShouldNotFlush) {
   delegate_.SetCanWriteAnything();
 
+  MakeIOVector("foo", &iov_);
   creator_.ConsumeData(QuicUtils::GetFirstBidirectionalStreamId(
                            framer_.transport_version(), Perspective::IS_CLIENT),
-                       "foo", 0, FIN);
-  QuicConsumedData consumed = creator_.ConsumeData(3, "quux", 3, NO_FIN);
+                       &iov_, 1u, iov_.iov_len, 0, FIN);
+  MakeIOVector("quux", &iov_);
+  QuicConsumedData consumed =
+      creator_.ConsumeData(3, &iov_, 1u, iov_.iov_len, 3, NO_FIN);
   EXPECT_EQ(4u, consumed.bytes_consumed);
   EXPECT_FALSE(consumed.fin_consumed);
   EXPECT_TRUE(creator_.HasPendingFrames());
@@ -2853,13 +2906,15 @@
 TEST_F(QuicPacketCreatorMultiplePacketsTest, ConsumeData_BatchOperations) {
   delegate_.SetCanWriteAnything();
 
+  MakeIOVector("foo", &iov_);
   creator_.ConsumeData(QuicUtils::GetFirstBidirectionalStreamId(
                            framer_.transport_version(), Perspective::IS_CLIENT),
-                       "foo", 0, NO_FIN);
+                       &iov_, 1u, iov_.iov_len, 0, NO_FIN);
+  MakeIOVector("quux", &iov_);
   QuicConsumedData consumed = creator_.ConsumeData(
       QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(),
                                                Perspective::IS_CLIENT),
-      "quux", 3, FIN);
+      &iov_, 1u, iov_.iov_len, 3, FIN);
   EXPECT_EQ(4u, consumed.bytes_consumed);
   EXPECT_TRUE(consumed.fin_consumed);
   EXPECT_TRUE(creator_.HasPendingFrames());
@@ -2914,10 +2969,11 @@
   }
   // Queue enough data to prevent a stream frame with a non-zero offset from
   // fitting.
+  MakeIOVector("foo", &iov_);
   QuicConsumedData consumed = creator_.ConsumeData(
       QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(),
                                                Perspective::IS_CLIENT),
-      "foo", 0, NO_FIN);
+      &iov_, 1u, iov_.iov_len, 0, NO_FIN);
   EXPECT_EQ(3u, consumed.bytes_consumed);
   EXPECT_FALSE(consumed.fin_consumed);
   EXPECT_TRUE(creator_.HasPendingFrames());
@@ -2925,10 +2981,11 @@
 
   // This frame will not fit with the existing frame, causing the queued frame
   // to be serialized, and it will be added to a new open packet.
+  MakeIOVector("bar", &iov_);
   consumed = creator_.ConsumeData(
       QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(),
                                                Perspective::IS_CLIENT),
-      "bar", 3, FIN);
+      &iov_, 1u, iov_.iov_len, 3, FIN);
   EXPECT_EQ(3u, consumed.bytes_consumed);
   EXPECT_TRUE(consumed.fin_consumed);
   EXPECT_TRUE(creator_.HasPendingFrames());
@@ -2948,14 +3005,15 @@
   delegate_.SetCanWriteAnything();
   creator_.SetTransmissionType(LOSS_RETRANSMISSION);
 
-  const std::string data(10000, '?');
+  // Create a 10000 byte IOVector.
+  CreateData(10000);
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
       .WillRepeatedly(
           Invoke(this, &QuicPacketCreatorMultiplePacketsTest::SavePacket));
   QuicConsumedData consumed = creator_.ConsumeDataFastPath(
       QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(),
                                                Perspective::IS_CLIENT),
-      data);
+      &iov_, 1u, iov_.iov_len, 0, true);
   EXPECT_EQ(10000u, consumed.bytes_consumed);
   EXPECT_TRUE(consumed.fin_consumed);
   EXPECT_FALSE(creator_.HasPendingFrames());
@@ -2977,14 +3035,15 @@
 TEST_F(QuicPacketCreatorMultiplePacketsTest, ConsumeDataLarge) {
   delegate_.SetCanWriteAnything();
 
-  const std::string data(10000, '?');
+  // Create a 10000 byte IOVector.
+  CreateData(10000);
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
       .WillRepeatedly(
           Invoke(this, &QuicPacketCreatorMultiplePacketsTest::SavePacket));
   QuicConsumedData consumed = creator_.ConsumeData(
       QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(),
                                                Perspective::IS_CLIENT),
-      data, 0, FIN);
+      &iov_, 1u, iov_.iov_len, 0, FIN);
   EXPECT_EQ(10000u, consumed.bytes_consumed);
   EXPECT_TRUE(consumed.fin_consumed);
   EXPECT_FALSE(creator_.HasPendingFrames());
@@ -3018,7 +3077,8 @@
   creator_.ConsumeRetransmittableControlFrame(QuicFrame(rst_frame),
                                               /*bundle_ack=*/false);
 
-  const std::string data(10000, '?');
+  // Create a 10000 byte IOVector.
+  CreateData(10000);
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
       .WillRepeatedly(
           Invoke(this, &QuicPacketCreatorMultiplePacketsTest::SavePacket));
@@ -3027,7 +3087,7 @@
   QuicConsumedData consumed = creator_.ConsumeData(
       QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(),
                                                Perspective::IS_CLIENT),
-      data, 0, FIN);
+      &iov_, 1u, iov_.iov_len, 0, FIN);
   creator_.Flush();
 
   EXPECT_EQ(10000u, consumed.bytes_consumed);
@@ -3048,14 +3108,15 @@
   delegate_.SetCanNotWrite();
   delegate_.SetCanWriteAnything();
 
-  const std::string data(10000, '?');
+  // Create a 10000 byte IOVector.
+  CreateData(10000);
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
       .WillRepeatedly(
           Invoke(this, &QuicPacketCreatorMultiplePacketsTest::SavePacket));
   QuicConsumedData consumed = creator_.ConsumeData(
       QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(),
                                                Perspective::IS_CLIENT),
-      data, 0, FIN);
+      &iov_, 1u, iov_.iov_len, 0, FIN);
   creator_.Flush();
 
   EXPECT_EQ(10000u, consumed.bytes_consumed);
@@ -3090,7 +3151,8 @@
       creator_.ConsumeRetransmittableControlFrame(QuicFrame(rst_frame),
                                                   /*bundle_ack=*/false));
   // Send some data and a control frame
-  creator_.ConsumeData(3, "quux", 0, NO_FIN);
+  MakeIOVector("quux", &iov_);
+  creator_.ConsumeData(3, &iov_, 1u, iov_.iov_len, 0, NO_FIN);
   if (!VersionHasIetfQuicFrames(framer_.transport_version())) {
     creator_.ConsumeRetransmittableControlFrame(QuicFrame(CreateGoAwayFrame()),
                                                 /*bundle_ack=*/false);
@@ -3147,8 +3209,9 @@
                                                   /*bundle_ack=*/false));
   // Send enough data to exceed one packet
   size_t data_len = kDefaultMaxPacketSize + 100;
-  const std::string data(data_len, '?');
-  QuicConsumedData consumed = creator_.ConsumeData(3, data, 0, FIN);
+  CreateData(data_len);
+  QuicConsumedData consumed =
+      creator_.ConsumeData(3, &iov_, 1u, iov_.iov_len, 0, FIN);
   EXPECT_EQ(data_len, consumed.bytes_consumed);
   EXPECT_TRUE(consumed.fin_consumed);
   if (!VersionHasIetfQuicFrames(framer_.transport_version())) {
@@ -3187,10 +3250,11 @@
   creator_.SetTransmissionType(LOSS_RETRANSMISSION);
 
   size_t data_len = 1224;
-  const std::string data(data_len, '?');
+  CreateData(data_len);
   QuicStreamId stream1_id = QuicUtils::GetFirstBidirectionalStreamId(
       framer_.transport_version(), Perspective::IS_CLIENT);
-  QuicConsumedData consumed = creator_.ConsumeData(stream1_id, data, 0, NO_FIN);
+  QuicConsumedData consumed =
+      creator_.ConsumeData(stream1_id, &iov_, 1u, iov_.iov_len, 0, NO_FIN);
   EXPECT_EQ(data_len, consumed.bytes_consumed);
   ASSERT_EQ(0u, creator_.BytesFree())
       << "Test setup failed: Please increase data_len to "
@@ -3205,7 +3269,8 @@
 
   QuicStreamId stream2_id = stream1_id + 4;
 
-  consumed = creator_.ConsumeData(stream2_id, data, 0, NO_FIN);
+  consumed =
+      creator_.ConsumeData(stream2_id, &iov_, 1u, iov_.iov_len, 0, NO_FIN);
   EXPECT_EQ(data_len, consumed.bytes_consumed);
 
   // Ensure the packet is successfully created.
@@ -3254,11 +3319,11 @@
       .Times(3)
       .WillRepeatedly(
           Invoke(this, &QuicPacketCreatorMultiplePacketsTest::SavePacket));
-  const std::string data(data_len, '?');
+  CreateData(data_len);
   QuicConsumedData consumed = creator_.ConsumeData(
       QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(),
                                                Perspective::IS_CLIENT),
-      data,
+      &iov_, 1u, iov_.iov_len,
       /*offset=*/0, FIN);
   EXPECT_EQ(data_len, consumed.bytes_consumed);
   EXPECT_TRUE(consumed.fin_consumed);
@@ -3293,11 +3358,11 @@
           Invoke(this, &QuicPacketCreatorMultiplePacketsTest::SavePacket));
 
   // Send two packets before packet size change.
-  const std::string data(data_len, '?');
+  CreateData(data_len);
   QuicConsumedData consumed = creator_.ConsumeData(
       QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(),
                                                Perspective::IS_CLIENT),
-      data,
+      &iov_, 1u, iov_.iov_len,
       /*offset=*/0, NO_FIN);
   creator_.Flush();
   EXPECT_EQ(data_len, consumed.bytes_consumed);
@@ -3313,11 +3378,12 @@
   EXPECT_EQ(packet_len, creator_.max_packet_length());
 
   // Send a packet after packet size change.
+  CreateData(data_len);
   creator_.AttachPacketFlusher();
   consumed = creator_.ConsumeData(
       QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(),
                                                Perspective::IS_CLIENT),
-      data, data_len, FIN);
+      &iov_, 1u, iov_.iov_len, data_len, FIN);
   creator_.Flush();
   EXPECT_EQ(data_len, consumed.bytes_consumed);
   EXPECT_TRUE(consumed.fin_consumed);
@@ -3345,11 +3411,11 @@
 
   // First send half of the packet worth of data.  We are in the batch mode, so
   // should not cause packet serialization.
-  const std::string first_write(first_write_len, '?');
+  CreateData(first_write_len);
   QuicConsumedData consumed = creator_.ConsumeData(
       QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(),
                                                Perspective::IS_CLIENT),
-      first_write,
+      &iov_, 1u, iov_.iov_len,
       /*offset=*/0, NO_FIN);
   EXPECT_EQ(first_write_len, consumed.bytes_consumed);
   EXPECT_FALSE(consumed.fin_consumed);
@@ -3381,11 +3447,11 @@
 
   // Send a more than a packet worth of data to the same stream.  This should
   // trigger serialization of one packet, and queue another one.
-  const std::string second_write(second_write_len, '?');
+  CreateData(second_write_len);
   consumed = creator_.ConsumeData(
       QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(),
                                                Perspective::IS_CLIENT),
-      second_write,
+      &iov_, 1u, iov_.iov_len,
       /*offset=*/first_write_len, FIN);
   EXPECT_EQ(second_write_len, consumed.bytes_consumed);
   EXPECT_TRUE(consumed.fin_consumed);
@@ -3475,11 +3541,11 @@
           Invoke(this, &QuicPacketCreatorMultiplePacketsTest::SavePacket));
 
   // Send data before the MTU probe.
-  const std::string data(data_len, '?');
+  CreateData(data_len);
   QuicConsumedData consumed = creator_.ConsumeData(
       QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(),
                                                Perspective::IS_CLIENT),
-      data,
+      &iov_, 1u, iov_.iov_len,
       /*offset=*/0, NO_FIN);
   creator_.Flush();
   EXPECT_EQ(data_len, consumed.bytes_consumed);
@@ -3493,11 +3559,12 @@
   EXPECT_FALSE(creator_.HasPendingRetransmittableFrames());
 
   // Send data after the MTU probe.
+  CreateData(data_len);
   creator_.AttachPacketFlusher();
   consumed = creator_.ConsumeData(
       QuicUtils::GetFirstBidirectionalStreamId(framer_.transport_version(),
                                                Perspective::IS_CLIENT),
-      data,
+      &iov_, 1u, iov_.iov_len,
       /*offset=*/data_len, FIN);
   creator_.Flush();
   EXPECT_EQ(data_len, consumed.bytes_consumed);
@@ -3588,9 +3655,9 @@
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
       .WillOnce(
           Invoke(this, &QuicPacketCreatorMultiplePacketsTest::SavePacket));
+  MakeIOVector(absl::string_view(buf, kStreamFramePayloadSize), &iov_);
   QuicConsumedData consumed = creator_.ConsumeData(
-      kDataStreamId, absl::string_view(buf, kStreamFramePayloadSize), 0,
-      FIN_AND_PADDING);
+      kDataStreamId, &iov_, 1u, iov_.iov_len, 0, FIN_AND_PADDING);
   creator_.Flush();
   EXPECT_EQ(kStreamFramePayloadSize, consumed.bytes_consumed);
   EXPECT_FALSE(creator_.HasPendingFrames());
@@ -3631,9 +3698,9 @@
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
       .WillRepeatedly(
           Invoke(this, &QuicPacketCreatorMultiplePacketsTest::SavePacket));
+  MakeIOVector(absl::string_view(buf, kStreamFramePayloadSize), &iov_);
   QuicConsumedData consumed = creator_.ConsumeData(
-      kDataStreamId, absl::string_view(buf, kStreamFramePayloadSize), 0,
-      FIN_AND_PADDING);
+      kDataStreamId, &iov_, 1u, iov_.iov_len, 0, FIN_AND_PADDING);
   creator_.Flush();
   EXPECT_EQ(kStreamFramePayloadSize, consumed.bytes_consumed);
   EXPECT_FALSE(creator_.HasPendingFrames());
@@ -3686,13 +3753,13 @@
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
       .WillRepeatedly(
           Invoke(this, &QuicPacketCreatorMultiplePacketsTest::SavePacket));
+  MakeIOVector(absl::string_view(buf, kStreamFramePayloadSize), &iov_);
   QuicConsumedData consumed = creator_.ConsumeData(
-      kDataStreamId1, absl::string_view(buf, kStreamFramePayloadSize), 0,
-      FIN_AND_PADDING);
+      kDataStreamId1, &iov_, 1u, iov_.iov_len, 0, FIN_AND_PADDING);
   EXPECT_EQ(kStreamFramePayloadSize, consumed.bytes_consumed);
-  consumed = creator_.ConsumeData(
-      kDataStreamId2, absl::string_view(buf, kStreamFramePayloadSize), 0,
-      FIN_AND_PADDING);
+  MakeIOVector(absl::string_view(buf, kStreamFramePayloadSize), &iov_);
+  consumed = creator_.ConsumeData(kDataStreamId2, &iov_, 1u, iov_.iov_len, 0,
+                                  FIN_AND_PADDING);
   EXPECT_EQ(kStreamFramePayloadSize, consumed.bytes_consumed);
   creator_.Flush();
   EXPECT_FALSE(creator_.HasPendingFrames());
@@ -3729,9 +3796,10 @@
       .WillOnce(
           Invoke(this, &QuicPacketCreatorMultiplePacketsTest::SavePacket));
 
+  MakeIOVector("foo", &iov_);
   creator_.ConsumeData(QuicUtils::GetFirstBidirectionalStreamId(
                            framer_.transport_version(), Perspective::IS_CLIENT),
-                       "foo", 0, FIN);
+                       &iov_, 1u, iov_.iov_len, 0, FIN);
   EXPECT_EQ(MESSAGE_STATUS_SUCCESS,
             creator_.AddMessageFrame(1, MemSliceFromString("message")));
   EXPECT_TRUE(creator_.HasPendingFrames());
@@ -3773,9 +3841,10 @@
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
       .WillOnce(
           Invoke(this, &QuicPacketCreatorMultiplePacketsTest::SavePacket));
+  MakeIOVector("a", &iov_);
   creator_.ConsumeData(QuicUtils::GetFirstBidirectionalStreamId(
                            framer_.transport_version(), Perspective::IS_CLIENT),
-                       "a", 0, FIN);
+                       &iov_, 1u, iov_.iov_len, 0, FIN);
   creator_.Flush();
   ASSERT_FALSE(packets_[0].nonretransmittable_frames.empty());
   QuicFrame padding = packets_[0].nonretransmittable_frames[0];
@@ -3792,12 +3861,13 @@
   creator_.SetClientConnectionId(client_connection_id);
   creator_.SetServerConnectionId(server_connection_id);
   // Send some stream data.
+  MakeIOVector("foo", &iov_);
   EXPECT_CALL(delegate_, ShouldGeneratePacket(_, _))
       .WillRepeatedly(Return(true));
   QuicConsumedData consumed = creator_.ConsumeData(
       QuicUtils::GetFirstBidirectionalStreamId(creator_.transport_version(),
                                                Perspective::IS_CLIENT),
-      "foo", 0, NO_FIN);
+      &iov_, 1u, iov_.iov_len, 0, NO_FIN);
   EXPECT_EQ(3u, consumed.bytes_consumed);
   EXPECT_TRUE(creator_.HasPendingFrames());
   {
@@ -3812,7 +3882,7 @@
     QuicConsumedData consumed = creator_.ConsumeData(
         QuicUtils::GetFirstBidirectionalStreamId(creator_.transport_version(),
                                                  Perspective::IS_CLIENT),
-        "foo", 0, FIN);
+        &iov_, 1u, iov_.iov_len, 0, FIN);
     EXPECT_EQ(3u, consumed.bytes_consumed);
   }
   // After exiting the scope, the last queued frame should be flushed.
@@ -3832,12 +3902,13 @@
   QuicSocketAddress peer_addr(QuicIpAddress::Any4(), 12345);
   creator_.SetDefaultPeerAddress(peer_addr);
   // Send some stream data.
+  MakeIOVector("foo", &iov_);
   EXPECT_CALL(delegate_, ShouldGeneratePacket(_, _))
       .WillRepeatedly(Return(true));
   QuicConsumedData consumed = creator_.ConsumeData(
       QuicUtils::GetFirstBidirectionalStreamId(creator_.transport_version(),
                                                Perspective::IS_CLIENT),
-      "foo", 0, NO_FIN);
+      &iov_, 1u, iov_.iov_len, 0, NO_FIN);
   EXPECT_EQ(3u, consumed.bytes_consumed);
 
   QuicSocketAddress peer_addr1(QuicIpAddress::Any4(), 12346);
@@ -3867,7 +3938,7 @@
     QuicConsumedData consumed = creator_.ConsumeData(
         QuicUtils::GetFirstBidirectionalStreamId(creator_.transport_version(),
                                                  Perspective::IS_CLIENT),
-        "foo", 0, FIN);
+        &iov_, 1u, iov_.iov_len, 0, FIN);
     EXPECT_EQ(3u, consumed.bytes_consumed);
     EXPECT_TRUE(creator_.HasPendingFrames());
   }
@@ -3888,12 +3959,13 @@
   ASSERT_EQ(server_connection_id1, creator_.GetServerConnectionId());
 
   // Send some stream data.
+  MakeIOVector("foo", &iov_);
   EXPECT_CALL(delegate_, ShouldGeneratePacket(_, _))
       .WillRepeatedly(Return(true));
   QuicConsumedData consumed = creator_.ConsumeData(
       QuicUtils::GetFirstBidirectionalStreamId(creator_.transport_version(),
                                                Perspective::IS_CLIENT),
-      "foo", 0, NO_FIN);
+      &iov_, 1u, iov_.iov_len, 0, NO_FIN);
   EXPECT_EQ(3u, consumed.bytes_consumed);
   EXPECT_TRUE(creator_.HasPendingFrames());
 
@@ -3912,12 +3984,13 @@
             /*update_connection_id=*/true);
         ASSERT_EQ(client_connection_id2, creator_.GetClientConnectionId());
         ASSERT_EQ(server_connection_id2, creator_.GetServerConnectionId());
+        MakeIOVector("foo", &iov_);
         EXPECT_CALL(delegate_, ShouldGeneratePacket(_, _))
             .WillRepeatedly(Return(true));
         QuicConsumedData consumed = creator_.ConsumeData(
             QuicUtils::GetFirstBidirectionalStreamId(
                 creator_.transport_version(), Perspective::IS_CLIENT),
-            "foo", 0, NO_FIN);
+            &iov_, 1u, iov_.iov_len, 0, NO_FIN);
         EXPECT_EQ(3u, consumed.bytes_consumed);
         EXPECT_TRUE(creator_.HasPendingFrames());
         // This should trigger another OnSerializedPacket() with the 2nd
diff --git a/quic/core/quic_session_test.cc b/quic/core/quic_session_test.cc
index 4d5eed5..68ae9f9 100644
--- a/quic/core/quic_session_test.cc
+++ b/quic/core/quic_session_test.cc
@@ -352,12 +352,14 @@
   }
 
   QuicConsumedData SendStreamData(QuicStream* stream) {
+    struct iovec iov;
     if (!QuicUtils::IsCryptoStreamId(connection()->transport_version(),
                                      stream->id()) &&
         this->connection()->encryption_level() != ENCRYPTION_FORWARD_SECURE) {
       this->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
     }
-    QuicStreamPeer::SendBuffer(stream).SaveStreamData("not empty");
+    MakeIOVector("not empty", &iov);
+    QuicStreamPeer::SendBuffer(stream).SaveStreamData(&iov, 1, 0, 9);
     QuicConsumedData consumed =
         WritevData(stream->id(), 9, 0, FIN, NOT_RETRANSMISSION,
                    GetEncryptionLevelToSendApplicationData());
diff --git a/quic/core/quic_stream.cc b/quic/core/quic_stream.cc
index 8737044..f8e00b4 100644
--- a/quic/core/quic_stream.cc
+++ b/quic/core/quic_stream.cc
@@ -688,6 +688,7 @@
   // Do not respect buffered data upper limit as WriteOrBufferData guarantees
   // all data to be consumed.
   if (data.length() > 0) {
+    struct iovec iov(QuicUtils::MakeIovec(data));
     QuicStreamOffset offset = send_buffer_.stream_offset();
     if (kMaxStreamLength - offset < data.length()) {
       QUIC_BUG(quic_bug_10586_4) << "Write too many data via stream " << id_;
@@ -696,7 +697,7 @@
           absl::StrCat("Write too many data via stream ", id_));
       return;
     }
-    send_buffer_.SaveStreamData(data);
+    send_buffer_.SaveStreamData(&iov, 1, 0, data.length());
     OnDataBuffered(offset, data.length(), ack_listener);
   }
   if (!had_buffered_data && (HasBufferedData() || fin_buffered_)) {
diff --git a/quic/core/quic_stream_send_buffer.cc b/quic/core/quic_stream_send_buffer.cc
index 6017768..aa58ead 100644
--- a/quic/core/quic_stream_send_buffer.cc
+++ b/quic/core/quic_stream_send_buffer.cc
@@ -58,19 +58,22 @@
 
 QuicStreamSendBuffer::~QuicStreamSendBuffer() {}
 
-void QuicStreamSendBuffer::SaveStreamData(absl::string_view data) {
-  QUICHE_DCHECK(!data.empty());
-
+void QuicStreamSendBuffer::SaveStreamData(const struct iovec* iov,
+                                          int iov_count,
+                                          size_t iov_offset,
+                                          QuicByteCount data_length) {
+  QUICHE_DCHECK_LT(0u, data_length);
   // Latch the maximum data slice size.
   const QuicByteCount max_data_slice_size =
       GetQuicFlag(FLAGS_quic_send_buffer_max_data_slice_size);
-  while (!data.empty()) {
-    size_t slice_len = std::min(data.length(), max_data_slice_size);
-    auto buffer =
-        quiche::QuicheBuffer::Copy(allocator_, data.substr(0, slice_len));
+  while (data_length > 0) {
+    size_t slice_len = std::min(data_length, max_data_slice_size);
+    quiche::QuicheBuffer buffer(allocator_, slice_len);
+    QuicUtils::CopyToBuffer(iov, iov_count, iov_offset, slice_len,
+                            buffer.data());
     SaveMemSlice(quiche::QuicheMemSlice(std::move(buffer)));
-
-    data = data.substr(slice_len);
+    data_length -= slice_len;
+    iov_offset += slice_len;
   }
 }
 
diff --git a/quic/core/quic_stream_send_buffer.h b/quic/core/quic_stream_send_buffer.h
index cf3fa13..6fe7b75 100644
--- a/quic/core/quic_stream_send_buffer.h
+++ b/quic/core/quic_stream_send_buffer.h
@@ -69,8 +69,11 @@
   QuicStreamSendBuffer(QuicStreamSendBuffer&& other) = delete;
   ~QuicStreamSendBuffer();
 
-  // Save |data| to send buffer.
-  void SaveStreamData(absl::string_view data);
+  // Save |data_length| of data starts at |iov_offset| in |iov| to send buffer.
+  void SaveStreamData(const struct iovec* iov,
+                      int iov_count,
+                      size_t iov_offset,
+                      QuicByteCount data_length);
 
   // Save |slice| to send buffer.
   void SaveMemSlice(quiche::QuicheMemSlice slice);
diff --git a/quic/core/quic_stream_send_buffer_test.cc b/quic/core/quic_stream_send_buffer_test.cc
index 599ebf4..2e0f9dc 100644
--- a/quic/core/quic_stream_send_buffer_test.cc
+++ b/quic/core/quic_stream_send_buffer_test.cc
@@ -20,41 +20,46 @@
 namespace test {
 namespace {
 
+struct iovec MakeIovec(absl::string_view data) {
+  struct iovec iov = {const_cast<char*>(data.data()),
+                      static_cast<size_t>(data.size())};
+  return iov;
+}
+
 class QuicStreamSendBufferTest : public QuicTest {
  public:
   QuicStreamSendBufferTest() : send_buffer_(&allocator_) {
     EXPECT_EQ(0u, send_buffer_.size());
     EXPECT_EQ(0u, send_buffer_.stream_bytes_written());
     EXPECT_EQ(0u, send_buffer_.stream_bytes_outstanding());
-    // The stream offset should be 0 since nothing is written.
-    EXPECT_EQ(0u, QuicStreamSendBufferPeer::EndOffset(&send_buffer_));
-
-    std::string data1 = absl::StrCat(
-        std::string(1536, 'a'), std::string(256, 'b'), std::string(256, 'c'));
+    std::string data1(1536, 'a');
+    std::string data2 = std::string(256, 'b') + std::string(256, 'c');
+    struct iovec iov[2];
+    iov[0] = MakeIovec(absl::string_view(data1));
+    iov[1] = MakeIovec(absl::string_view(data2));
 
     quiche::QuicheBuffer buffer1(&allocator_, 1024);
     memset(buffer1.data(), 'c', buffer1.size());
     quiche::QuicheMemSlice slice1(std::move(buffer1));
-
     quiche::QuicheBuffer buffer2(&allocator_, 768);
     memset(buffer2.data(), 'd', buffer2.size());
     quiche::QuicheMemSlice slice2(std::move(buffer2));
 
-    // `data` will be split into two BufferedSlices.
-    SetQuicFlag(FLAGS_quic_send_buffer_max_data_slice_size, 1024);
-    send_buffer_.SaveStreamData(data1);
+    // The stream offset should be 0 since nothing is written.
+    EXPECT_EQ(0u, QuicStreamSendBufferPeer::EndOffset(&send_buffer_));
 
+    // Save all data.
+    SetQuicFlag(FLAGS_quic_send_buffer_max_data_slice_size, 1024);
+    send_buffer_.SaveStreamData(iov, 2, 0, 2048);
     send_buffer_.SaveMemSlice(std::move(slice1));
     EXPECT_TRUE(slice1.empty());
     send_buffer_.SaveMemSlice(std::move(slice2));
     EXPECT_TRUE(slice2.empty());
 
     EXPECT_EQ(4u, send_buffer_.size());
-    // At this point, `send_buffer_.interval_deque_` looks like this:
-    // BufferedSlice1: 'a' * 1024
-    // BufferedSlice2: 'a' * 512 + 'b' * 256 + 'c' * 256
-    // BufferedSlice3: 'c' * 1024
-    // BufferedSlice4: 'd' * 768
+    // At this point, the whole buffer looks like:
+    // |      a * 1536      |b * 256|         c * 1280        |  d * 768  |
+    // |    slice1     |     slice2       |      slice3       |   slice4  |
   }
 
   void WriteAllData() {
diff --git a/quic/core/quic_utils.cc b/quic/core/quic_utils.cc
index aa8f59e..caffd7f 100644
--- a/quic/core/quic_utils.cc
+++ b/quic/core/quic_utils.cc
@@ -290,6 +290,13 @@
 }
 
 // static
+struct iovec QuicUtils::MakeIovec(absl::string_view data) {
+  struct iovec iov = {const_cast<char*>(data.data()),
+                      static_cast<size_t>(data.size())};
+  return iov;
+}
+
+// static
 bool QuicUtils::IsAckable(SentPacketState state) {
   return state != NEVER_SENT && state != ACKED && state != UNACKABLE;
 }
diff --git a/quic/core/quic_utils.h b/quic/core/quic_utils.h
index 085f532..43e6470 100644
--- a/quic/core/quic_utils.h
+++ b/quic/core/quic_utils.h
@@ -81,6 +81,9 @@
                            size_t buffer_length,
                            char* buffer);
 
+  // Creates an iovec pointing to the same data as |data|.
+  static struct iovec MakeIovec(absl::string_view data);
+
   // Returns the opposite Perspective of the |perspective| passed in.
   static constexpr Perspective InvertPerspective(Perspective perspective) {
     return perspective == Perspective::IS_CLIENT ? Perspective::IS_SERVER
diff --git a/quic/test_tools/simple_data_producer.cc b/quic/test_tools/simple_data_producer.cc
index 64c0a2c..aaab1d1 100644
--- a/quic/test_tools/simple_data_producer.cc
+++ b/quic/test_tools/simple_data_producer.cc
@@ -20,14 +20,17 @@
 SimpleDataProducer::~SimpleDataProducer() {}
 
 void SimpleDataProducer::SaveStreamData(QuicStreamId id,
-                                        absl::string_view data) {
-  if (data.empty()) {
+                                        const struct iovec* iov,
+                                        int iov_count,
+                                        size_t iov_offset,
+                                        QuicByteCount data_length) {
+  if (data_length == 0) {
     return;
   }
   if (!send_buffer_map_.contains(id)) {
     send_buffer_map_[id] = std::make_unique<QuicStreamSendBuffer>(&allocator_);
   }
-  send_buffer_map_[id]->SaveStreamData(data);
+  send_buffer_map_[id]->SaveStreamData(iov, iov_count, iov_offset, data_length);
 }
 
 void SimpleDataProducer::SaveCryptoData(EncryptionLevel level,
diff --git a/quic/test_tools/simple_data_producer.h b/quic/test_tools/simple_data_producer.h
index 9564f86..e8dd4e4 100644
--- a/quic/test_tools/simple_data_producer.h
+++ b/quic/test_tools/simple_data_producer.h
@@ -23,10 +23,17 @@
   SimpleDataProducer();
   ~SimpleDataProducer() override;
 
-  // Saves `data` to be provided when WriteStreamData() is called. Multiple
-  // calls to SaveStreamData() for the same stream ID append to the buffer for
-  // that stream.
-  void SaveStreamData(QuicStreamId id, absl::string_view data);
+  // Saves data to be provided when WriteStreamData is called. Data of length
+  // |data_length| is buffered to be provided for stream |id|. Multiple calls to
+  // SaveStreamData for the same stream ID append to the buffer for that stream.
+  // The data to be buffered is provided in |iov_count| iovec structs, with
+  // |iov| pointing to the first, and |iov_offset| indicating how many bytes
+  // into the iovec structs the data starts.
+  void SaveStreamData(QuicStreamId id,
+                      const struct iovec* iov,
+                      int iov_count,
+                      size_t iov_offset,
+                      QuicByteCount data_length);
 
   void SaveCryptoData(EncryptionLevel level,
                       QuicStreamOffset offset,