Don't split sent messages on MemSlice boundaries.

NOTE: Merging this change to chromium will require updating the implementation
of QuicMemSliceStorage.  The required changes are fairly trivial.

Instead of enqueueing a QueuedMessage with a single MemSlice for each slice in
the span representing an outgoing message, QuartcSession should enqueue a single
message containing all MemSlices that are part of that message, so that they are
all sent together, as part of a single datagram frame.

This requires a way to build a QuicMemSliceSpan from multiple QuicMemSlices,
since QuartcSession must construct a QuicMemSliceSpan from its queued message to
pass the message to SendMessage.

QuicMemSliceStorage can do this, but currently requires copies to get MemSlices
into the MemSliceStorage.  This change adds an Append() function to
QuicMemSliceStorage, which allows callers to move MemSlices into storage without
copies.

QuartcSession now consumes all the MemSlices for a single message into a single
QuicMemSliceStorage.  When the message is sent, that storage is converted to a
span and passed to QuicSession::SendMessage as a single unit.

gfe-relnote: n/a (Quartc only)
PiperOrigin-RevId: 258468175
Change-Id: I4bab1e8da1db77a5ba1068cc8fe6303032ad81c7
diff --git a/quic/platform/api/quic_mem_slice_storage.h b/quic/platform/api/quic_mem_slice_storage.h
index dc48a43..9164e9a 100644
--- a/quic/platform/api/quic_mem_slice_storage.h
+++ b/quic/platform/api/quic_mem_slice_storage.h
@@ -30,6 +30,8 @@
   // Return a QuicMemSliceSpan form of the storage.
   QuicMemSliceSpan ToSpan() { return impl_.ToSpan(); }
 
+  void Append(QuicMemSlice slice) { impl_.Append(std::move(*slice.impl())); }
+
  private:
   QuicMemSliceStorageImpl impl_;
 };
diff --git a/quic/platform/api/quic_mem_slice_storage_test.cc b/quic/platform/api/quic_mem_slice_storage_test.cc
index d0c2588..a13899d 100644
--- a/quic/platform/api/quic_mem_slice_storage_test.cc
+++ b/quic/platform/api/quic_mem_slice_storage_test.cc
@@ -3,8 +3,10 @@
 // found in the LICENSE file.
 
 #include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h"
+
 #include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h"
 
 namespace quic {
 namespace test {
@@ -55,6 +57,24 @@
   EXPECT_EQ("bbbb", span.GetData(1));
 }
 
+TEST_F(QuicMemSliceStorageImplTest, AppendMemSlices) {
+  std::string body1(3, 'a');
+  std::string body2(4, 'b');
+  std::vector<std::pair<char*, size_t>> buffers;
+  buffers.push_back(
+      std::make_pair(const_cast<char*>(body1.data()), body1.length()));
+  buffers.push_back(
+      std::make_pair(const_cast<char*>(body2.data()), body2.length()));
+  QuicTestMemSliceVector mem_slices(buffers);
+
+  QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
+  mem_slices.span().ConsumeAll(
+      [&storage](QuicMemSlice slice) { storage.Append(std::move(slice)); });
+
+  EXPECT_EQ("aaa", storage.ToSpan().GetData(0));
+  EXPECT_EQ("bbbb", storage.ToSpan().GetData(1));
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/quartc/quartc_session.cc b/quic/quartc/quartc_session.cc
index d23662f..8a918ca 100644
--- a/quic/quartc/quartc_session.cc
+++ b/quic/quartc/quartc_session.cc
@@ -58,9 +58,12 @@
 
   // There may be other messages in send queue, so we have to add message
   // to the queue and call queue processing helper.
-  message.ConsumeAll([this, datagram_id](QuicMemSlice slice) {
-    send_message_queue_.emplace_back(std::move(slice), datagram_id);
+  QueuedMessage queued_message;
+  queued_message.datagram_id = datagram_id;
+  message.ConsumeAll([&queued_message](QuicMemSlice slice) {
+    queued_message.message.Append(std::move(slice));
   });
+  send_message_queue_.push_back(std::move(queued_message));
 
   ProcessSendMessageQueue();
 
@@ -71,8 +74,9 @@
   QuicConnection::ScopedPacketFlusher flusher(connection());
   while (!send_message_queue_.empty()) {
     QueuedMessage& it = send_message_queue_.front();
-    const size_t message_size = it.message.length();
-    MessageResult result = SendMessage(QuicMemSliceSpan(&it.message));
+    QuicMemSliceSpan span = it.message.ToSpan();
+    const size_t message_size = span.total_length();
+    MessageResult result = SendMessage(span);
 
     // Handle errors.
     switch (result.status) {
diff --git a/quic/quartc/quartc_session.h b/quic/quartc/quartc_session.h
index 6828f69..638333e 100644
--- a/quic/quartc/quartc_session.h
+++ b/quic/quartc/quartc_session.h
@@ -15,6 +15,7 @@
 #include "net/third_party/quiche/src/quic/core/quic_session.h"
 #include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h"
 #include "net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h"
 #include "net/third_party/quiche/src/quic/quartc/quartc_stream.h"
 
@@ -212,9 +213,9 @@
 
   // Holds message until it's sent.
   struct QueuedMessage {
-    QueuedMessage(QuicMemSlice the_message, int64_t the_datagram_id)
-        : message(std::move(the_message)), datagram_id(the_datagram_id) {}
-    QuicMemSlice message;
+    QueuedMessage() : message(nullptr, 0, nullptr, 0), datagram_id(0) {}
+
+    QuicMemSliceStorage message;
     int64_t datagram_id;
   };
 
diff --git a/quic/quartc/quartc_session_test.cc b/quic/quartc/quartc_session_test.cc
index 8bb9ad1..3b55314 100644
--- a/quic/quartc/quartc_session_test.cc
+++ b/quic/quartc/quartc_session_test.cc
@@ -351,6 +351,28 @@
   TestSendReceiveQueuedMessages(/*direction_from_server=*/false);
 }
 
+TEST_F(QuartcSessionTest, SendMultiMemSliceMessage) {
+  CreateClientAndServerSessions(QuartcSessionConfig());
+  AwaitHandshake();
+  ASSERT_TRUE(server_peer_->CanSendMessage());
+
+  std::vector<std::pair<char*, size_t>> buffers;
+  char first_piece[] = "Hello, ";
+  char second_piece[] = "world!";
+  buffers.emplace_back(first_piece, 7);
+  buffers.emplace_back(second_piece, 6);
+  test::QuicTestMemSliceVector message(buffers);
+  ASSERT_TRUE(
+      server_peer_->SendOrQueueMessage(message.span(), /*datagram_id=*/1));
+
+  // Wait for the client to receive the message.
+  RunTasks();
+
+  // The message is not fragmented along MemSlice boundaries.
+  EXPECT_THAT(client_session_delegate_->incoming_messages(),
+              testing::ElementsAre("Hello, world!"));
+}
+
 TEST_F(QuartcSessionTest, SendMessageFails) {
   CreateClientAndServerSessions(QuartcSessionConfig());
   AwaitHandshake();