Maybe copy datagram frames in QuicUnackedPacketMap.

Protected by FLAGS_quic_reloadable_flag_quic_maybe_copy_datagram_frames.

PiperOrigin-RevId: 914318383
diff --git a/quiche/common/quiche_feature_flags_list.h b/quiche/common/quiche_feature_flags_list.h
index dfed18a..069549d 100755
--- a/quiche/common/quiche_feature_flags_list.h
+++ b/quiche/common/quiche_feature_flags_list.h
@@ -44,6 +44,7 @@
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_fix_gap_filling_ack_logic, true, true, "If true, fix the gap filling ack logic in QuicAckFrame.")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_fix_timeouts, true, true, "If true, postpone setting handshake timeout to infinite to handshake complete.")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_include_datagrams_in_willing_to_write, false, false, "If true, checks for queued datagrams when determining if a connection is willing to write.")
+QUICHE_FLAG(bool, quiche_reloadable_flag_quic_maybe_copy_datagram_frames, false, true, "If true, maybe copy datagram frames in QuicUnackedPacketMap.")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_move_clock_now, false, false, "If true, move the call to clock.Now() in QuicPacketReader::ReadAndDispatchPackets to after socket_api_.ReadMultiplePackets().")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_neuter_packets_on_migration, false, true, "If ture, QuicSentPacketManager::OnConnectionMigration will call old_send_algorithm->OnPacketNeutered on all inflight packets.")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_no_write_control_frame_upon_connection_close, false, true, "If trrue, early return before write control frame in OnCanWrite() if the connection is already closed.")
diff --git a/quiche/quic/core/frames/quic_frame.cc b/quiche/quic/core/frames/quic_frame.cc
index 8451bb9..dac978e 100644
--- a/quiche/quic/core/frames/quic_frame.cc
+++ b/quiche/quic/core/frames/quic_frame.cc
@@ -192,6 +192,15 @@
   return false;
 }
 
+bool HasOnlyDatagramFrame(const QuicFrames& frames) {
+  for (const QuicFrame& frame : frames) {
+    if (frame.type != DATAGRAM_FRAME) {
+      return false;
+    }
+  }
+  return !frames.empty();
+}
+
 bool IsControlFrame(QuicFrameType type) {
   switch (type) {
     case RST_STREAM_FRAME:
diff --git a/quiche/quic/core/frames/quic_frame.h b/quiche/quic/core/frames/quic_frame.h
index a9eb9d7..37c6bb3 100644
--- a/quiche/quic/core/frames/quic_frame.h
+++ b/quiche/quic/core/frames/quic_frame.h
@@ -149,9 +149,12 @@
 QUICHE_EXPORT void RemoveFramesForStream(QuicFrames* frames,
                                          QuicStreamId stream_id);
 
-// Returns true if |frames| contains at least one message frame.
+// Returns true if |frames| contains at least one data frame.
 QUICHE_EXPORT bool HasDatagramFrame(const QuicFrames& frames);
 
+// Returns true if |frames| contains only data frames.
+QUICHE_EXPORT bool HasOnlyDatagramFrame(const QuicFrames& frames);
+
 // Returns true if |type| is a retransmittable control frame.
 QUICHE_EXPORT bool IsControlFrame(QuicFrameType type);
 
diff --git a/quiche/quic/core/frames/quic_frames_test.cc b/quiche/quic/core/frames/quic_frames_test.cc
index 5f99eff..e85166a 100644
--- a/quiche/quic/core/frames/quic_frames_test.cc
+++ b/quiche/quic/core/frames/quic_frames_test.cc
@@ -8,8 +8,10 @@
 #include "quiche/quic/core/frames/quic_ack_frame.h"
 #include "quiche/quic/core/frames/quic_blocked_frame.h"
 #include "quiche/quic/core/frames/quic_connection_close_frame.h"
+#include "quiche/quic/core/frames/quic_datagram_frame.h"
 #include "quiche/quic/core/frames/quic_frame.h"
 #include "quiche/quic/core/frames/quic_goaway_frame.h"
+#include "quiche/quic/core/frames/quic_handshake_done_frame.h"
 #include "quiche/quic/core/frames/quic_immediate_ack_frame.h"
 #include "quiche/quic/core/frames/quic_mtu_discovery_frame.h"
 #include "quiche/quic/core/frames/quic_new_connection_id_frame.h"
@@ -917,6 +919,21 @@
   DeleteFrames(&frames);
 }
 
+TEST_F(QuicFramesTest, HasOnlyDatagramFrame) {
+  QuicFrames frames;
+
+  EXPECT_FALSE(HasOnlyDatagramFrame(frames));
+
+  frames.push_back(QuicFrame(
+      new QuicDatagramFrame(1, quiche::QuicheMemSlice::Copy("datagram"))));
+  EXPECT_TRUE(HasOnlyDatagramFrame(frames));
+
+  frames.push_back(QuicFrame(QuicHandshakeDoneFrame()));
+  EXPECT_FALSE(HasOnlyDatagramFrame(frames));
+
+  DeleteFrames(&frames);
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quiche/quic/core/quic_unacked_packet_map.cc b/quiche/quic/core/quic_unacked_packet_map.cc
index 80401dd..12c2837 100644
--- a/quiche/quic/core/quic_unacked_packet_map.cc
+++ b/quiche/quic/core/quic_unacked_packet_map.cc
@@ -507,7 +507,17 @@
   quiche::SimpleBufferAllocator allocator;
   std::optional<QuicFrames> frames_copy;
   const bool use_copied_frames =
-      !HasDatagramFrame(info->retransmittable_frames);
+      maybe_copy_datagram_frames_
+          ? !HasOnlyDatagramFrame(info->retransmittable_frames)
+          : !HasDatagramFrame(info->retransmittable_frames);
+
+  if (maybe_copy_datagram_frames_) {
+    if (use_copied_frames) {
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_maybe_copy_datagram_frames, 1, 4);
+    } else {
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_maybe_copy_datagram_frames, 2, 4);
+    }
+  }
 
   if (use_copied_frames) {
     frames = &frames_copy.emplace(
@@ -552,7 +562,16 @@
   quiche::SimpleBufferAllocator allocator;
   std::optional<QuicFrames> frames_copy;
   const bool use_copied_frames =
-      !HasDatagramFrame(info->retransmittable_frames);
+      maybe_copy_datagram_frames_
+          ? !HasOnlyDatagramFrame(info->retransmittable_frames)
+          : !HasDatagramFrame(info->retransmittable_frames);
+  if (maybe_copy_datagram_frames_) {
+    if (use_copied_frames) {
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_maybe_copy_datagram_frames, 3, 4);
+    } else {
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_maybe_copy_datagram_frames, 4, 4);
+    }
+  }
 
   if (use_copied_frames) {
     frames = &frames_copy.emplace(
diff --git a/quiche/quic/core/quic_unacked_packet_map.h b/quiche/quic/core/quic_unacked_packet_map.h
index e37ac3c..9377b6e 100644
--- a/quiche/quic/core/quic_unacked_packet_map.h
+++ b/quiche/quic/core/quic_unacked_packet_map.h
@@ -10,11 +10,13 @@
 
 #include "absl/container/inlined_vector.h"
 #include "absl/strings/str_cat.h"
+#include "quiche/quic/core/frames/quic_frame.h"
 #include "quiche/quic/core/quic_packets.h"
 #include "quiche/quic/core/quic_transmission_info.h"
 #include "quiche/quic/core/session_notifier_interface.h"
 #include "quiche/quic/platform/api/quic_export.h"
 #include "quiche/quic/platform/api/quic_flags.h"
+#include "quiche/common/platform/api/quiche_flags.h"
 #include "quiche/common/quiche_circular_deque.h"
 
 namespace quic {
@@ -344,6 +346,9 @@
 
   // Latched value of the quic_simple_inflight_time flag.
   bool simple_inflight_time_;
+
+  bool maybe_copy_datagram_frames_ =
+      GetQuicReloadableFlag(quic_maybe_copy_datagram_frames);
 };
 
 }  // namespace quic