Rearrange fields in QuicConnection::ReceivedPacketInfo and eliminate duplicate storage of destination_connection_id.

Changed QuicEcnCodepoint to an enum uint8_t, which fixes an earlier oversight.

Reduces size from 288B to 248B when the second field is finally deprecated; 272B for now.

PiperOrigin-RevId: 841855204
diff --git a/quiche/common/quiche_feature_flags_list.h b/quiche/common/quiche_feature_flags_list.h
index 372ed84..c94364b 100755
--- a/quiche/common/quiche_feature_flags_list.h
+++ b/quiche/common/quiche_feature_flags_list.h
@@ -51,6 +51,7 @@
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_notify_ack_listener_earlier, true, true, "If true, call QuicAckListenerInterface::OnPacketAcked() before moving the stream to closed stream list.")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_notify_stream_soon_to_destroy, true, true, "If true, notify each QUIC stream before it gets destroyed and update ACK listener before that.")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_on_packet_header_return_connected, false, true, "If true, QuicConnection::OnPacketHeader will return connected_ at the end of the function.")
+QUICHE_FLAG(bool, quiche_reloadable_flag_quic_one_dcid, false, false, "If true, stores only one copy of the destination connection ID in QuicConnection::ReceivedPacketInfo.")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_optimize_qpack_blocking_manager, false, false, "If true, optimize qpack_blocking_manager for CPU efficiency.")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_pacing_remove_non_initial_burst, false, false, "If true, remove the non-initial burst in QUIC PacingSender.")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_parse_cert_compression_algos_from_chlo, true, true, "If true, parse offered cert compression algorithms from received CHLOs.")
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc
index 2015e27..57b9181 100644
--- a/quiche/quic/core/quic_connection.cc
+++ b/quiche/quic/core/quic_connection.cc
@@ -228,7 +228,8 @@
       received_client_addresses_cache_(kMaxReceivedClientAddressSize),
       perspective_(perspective),
       owns_writer_(owns_writer),
-      can_truncate_connection_ids_(perspective == Perspective::IS_SERVER) {
+      can_truncate_connection_ids_(perspective == Perspective::IS_SERVER),
+      store_one_dcid_(GetQuicReloadableFlag(quic_one_dcid)) {
   QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT ||
                 default_path_.self_address.IsInitialized());
 
@@ -987,17 +988,33 @@
 
 bool QuicConnection::OnUnauthenticatedPublicHeader(
     const QuicPacketHeader& header) {
-  last_received_packet_info_.destination_connection_id =
-      header.destination_connection_id;
-  // If last packet destination connection ID is the original server
-  // connection ID chosen by client, replaces it with the connection ID chosen
-  // by server.
-  if (perspective_ == Perspective::IS_SERVER &&
-      original_destination_connection_id_.has_value() &&
-      last_received_packet_info_.destination_connection_id ==
-          *original_destination_connection_id_) {
+  if (store_one_dcid_) {
+    QUIC_RELOADABLE_FLAG_COUNT_N(quic_one_dcid, 2, 3);
+    last_received_packet_info_.header.destination_connection_id =
+        header.destination_connection_id;
+    // If last packet destination connection ID is the original server
+    // connection ID chosen by client, replaces it with the connection ID chosen
+    // by server.
+    if (perspective_ == Perspective::IS_SERVER &&
+        original_destination_connection_id_.has_value() &&
+        last_received_packet_info_.header.destination_connection_id ==
+            *original_destination_connection_id_) {
+      last_received_packet_info_.header.destination_connection_id =
+          original_destination_connection_id_replacement_;
+    }
+  } else {
     last_received_packet_info_.destination_connection_id =
-        original_destination_connection_id_replacement_;
+        header.destination_connection_id;
+    // If last packet destination connection ID is the original server
+    // connection ID chosen by client, replaces it with the connection ID chosen
+    // by server.
+    if (perspective_ == Perspective::IS_SERVER &&
+        original_destination_connection_id_.has_value() &&
+        last_received_packet_info_.destination_connection_id ==
+            *original_destination_connection_id_) {
+      last_received_packet_info_.destination_connection_id =
+          original_destination_connection_id_replacement_;
+    }
   }
 
   // As soon as we receive an initial we start ignoring subsequent retries.
@@ -1233,6 +1250,15 @@
     }
   }
 
+  if (store_one_dcid_) {
+    QUIC_RELOADABLE_FLAG_COUNT_N(quic_one_dcid, 3, 3);
+    // Save the stored destination connection ID, in case it was substituted.
+    QuicConnectionId destination_connection_id =
+        last_received_packet_info_.header.destination_connection_id;
+    last_received_packet_info_.header = header;
+    last_received_packet_info_.header.destination_connection_id =
+        destination_connection_id;
+  }
   if (perspective_ == Perspective::IS_CLIENT) {
     if (!GetLargestReceivedPacket().IsInitialized() ||
         header.packet_number > GetLargestReceivedPacket()) {
@@ -1276,34 +1302,33 @@
       // connection ID set on path.
       // 1) If client uses 1 unique server connection ID per path and the packet
       // is received from an existing path, then
-      // last_received_packet_info_.destination_connection_id will always be the
-      // same as the server connection ID on path. Server side will maintain the
-      // 1-to-1 mapping from server connection ID to path. 2) If client uses
-      // multiple server connection IDs on the same path, compared to the
-      // server_connection_id on path,
-      // last_received_packet_info_.destination_connection_id has the advantage
-      // that it is still present in the session map since the packet can be
-      // routed here regardless of packet reordering.
+      // last_received_packet_info_.header.destination_connection_id will always
+      // be the same as the server connection ID on path. Server side will
+      // maintain the 1-to-1 mapping from server connection ID to path. 2) If
+      // client uses multiple server connection IDs on the same path, compared
+      // to the server_connection_id on path,
+      // last_received_packet_info_.header.destination_connection_id has the
+      // advantage that it is still present in the session map since the packet
+      // can be routed here regardless of packet reordering.
       if (IsDefaultPath(last_received_packet_info_.destination_address,
                         effective_peer_address)) {
         default_path_.server_connection_id =
-            last_received_packet_info_.destination_connection_id;
+            GetDestinationConnectionId(last_received_packet_info_);
       } else if (IsAlternativePath(
                      last_received_packet_info_.destination_address,
                      effective_peer_address)) {
         alternative_path_.server_connection_id =
-            last_received_packet_info_.destination_connection_id;
+            GetDestinationConnectionId(last_received_packet_info_);
       }
     }
 
-    if (last_received_packet_info_.destination_connection_id !=
+    if (GetDestinationConnectionId(last_received_packet_info_) !=
             default_path_.server_connection_id &&
         (!original_destination_connection_id_.has_value() ||
-         last_received_packet_info_.destination_connection_id !=
+         GetDestinationConnectionId(last_received_packet_info_) !=
              *original_destination_connection_id_)) {
       QUIC_CODE_COUNT(quic_connection_id_change);
     }
-
     QUIC_DLOG_IF(INFO, current_effective_peer_migration_type_ != NO_CHANGE)
         << ENDPOINT << "Effective peer's ip:port changed from "
         << default_path_.peer_address.ToString() << " to "
@@ -1314,7 +1339,19 @@
 
   --stats_.packets_dropped;
   QUIC_DVLOG(1) << ENDPOINT << "Received packet header: " << header;
-  last_received_packet_info_.header = header;
+  if (store_one_dcid_) {
+    // last_received_packet_info_.header.destination_connection_id will often
+    // be different from header.destination_connection_id, so we can't simply
+    // compare the two headers.
+    QUIC_BUG_IF(
+        quic_bug_header_mismatch,
+        last_received_packet_info_.header.packet_number !=
+                header.packet_number ||
+            last_received_packet_info_.header.type_byte != header.type_byte)
+        << "last_received_packet_info.header not assigned";
+  } else {
+    last_received_packet_info_.header = header;
+  }
   if (!stats_.first_decrypted_packet.IsInitialized()) {
     stats_.first_decrypted_packet =
         last_received_packet_info_.header.packet_number;
@@ -5071,8 +5108,8 @@
   os << " { destination_address: " << info.destination_address.ToString()
      << ", source_address: " << info.source_address.ToString()
      << ", received_bytes_counted: " << info.received_bytes_counted
-     << ", length: " << info.length
-     << ", destination_connection_id: " << info.destination_connection_id;
+     << ", length: " << info.length << ", destination_connection_id: "
+     << info.header.destination_connection_id;
   if (!info.decrypted) {
     os << " }\n";
     return os;
@@ -5433,12 +5470,12 @@
     std::optional<StatelessResetToken> stateless_reset_token;
     FindMatchingOrNewClientConnectionIdOrToken(
         previous_default_path, alternative_path_,
-        last_received_packet_info_.destination_connection_id,
+        GetDestinationConnectionId(last_received_packet_info_),
         &client_connection_id, &stateless_reset_token);
     SetDefaultPathState(
         PathState(last_received_packet_info_.destination_address,
                   current_effective_peer_address, client_connection_id,
-                  last_received_packet_info_.destination_connection_id,
+                  GetDestinationConnectionId(last_received_packet_info_),
                   stateless_reset_token));
     // The path is considered validated if its peer IP address matches any
     // validated path's peer IP address.
@@ -5636,15 +5673,15 @@
         std::optional<StatelessResetToken> stateless_reset_token;
         FindMatchingOrNewClientConnectionIdOrToken(
             default_path_, alternative_path_,
-            last_received_packet_info_.destination_connection_id,
+            GetDestinationConnectionId(last_received_packet_info_),
             &client_connection_id, &stateless_reset_token);
-        // Only override alternative path state upon receiving a PATH_CHALLENGE
-        // from an unvalidated peer address, and the connection isn't validating
-        // a recent peer migration.
+        // Only override alternative path state upon receiving a
+        // PATH_CHALLENGE from an unvalidated peer address, and the connection
+        // isn't validating a recent peer migration.
         alternative_path_ =
             PathState(last_received_packet_info_.destination_address,
                       current_effective_peer_address, client_connection_id,
-                      last_received_packet_info_.destination_connection_id,
+                      GetDestinationConnectionId(last_received_packet_info_),
                       stateless_reset_token);
         should_proactively_validate_peer_address_on_path_challenge_ = true;
       }
diff --git a/quiche/quic/core/quic_connection.h b/quiche/quic/core/quic_connection.h
index 2539953..a4d3d15 100644
--- a/quiche/quic/core/quic_connection.h
+++ b/quiche/quic/core/quic_connection.h
@@ -1698,22 +1698,30 @@
     QuicSocketAddress destination_address;
     QuicSocketAddress source_address;
     QuicTime receipt_time = QuicTime::Zero();
-    bool received_bytes_counted = false;
     QuicByteCount length = 0;
-    QuicConnectionId destination_connection_id;
-    // Fields below are only populated if packet gets decrypted successfully.
+    // END FIRST CACHELINE
+    // Fields below are only populated if packet gets decrypted successfully,
+    // except received_bytes_counted and header.destination_connection_id.
     // TODO(fayang): consider using std::optional for following fields.
+    QuicPacketHeader header;  // Placed to fall on the cacheline boundary, as it
+                              // fills two full cachelines including padding.
+    // END THIRD CACHELINE
+    bool received_bytes_counted = false;
     bool decrypted = false;
-    EncryptionLevel decrypted_level = ENCRYPTION_INITIAL;
-    QuicPacketHeader header;
-    absl::InlinedVector<QuicFrameType, 1> frames;
     QuicEcnCodepoint ecn_codepoint = ECN_NOT_ECT;
+    EncryptionLevel decrypted_level = ENCRYPTION_INITIAL;
     uint32_t flow_label = 0;
+    absl::InlinedVector<QuicFrameType, 1> frames;
     // Stores the actual address this packet is received on when it is received
     // on the preferred address. In this case, |destination_address| will
     // be overridden to the current default self address.
     QuicSocketAddress actual_destination_address;
+    // 8B remaining in the fourth cacheline.
+    // TODO(martinduke): Remove once gfe2_reloadable_flag_quic_one_dcid is
+    // deprecated.
+    QuicConnectionId destination_connection_id;
   };
+  static_assert(offsetof(ReceivedPacketInfo, received_bytes_counted) <= 192);
 
   QUICHE_EXPORT friend std::ostream& operator<<(
       std::ostream& os, const QuicConnection::ReceivedPacketInfo& info);
@@ -2171,6 +2179,16 @@
     return QuicAlarmProxy(&alarms_, QuicAlarmSlot::kMultiPortProbing);
   }
 
+  // Extract destination connection ID from ReceivedPacketInfo.
+  inline QuicConnectionId GetDestinationConnectionId(
+      const ReceivedPacketInfo& packet_info) const {
+    if (store_one_dcid_) {
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_one_dcid, 1, 3);
+      return packet_info.header.destination_connection_id;
+    }
+    return packet_info.destination_connection_id;
+  }
+
   QuicConnectionContext context_;
 
   QuicFramer framer_;
@@ -2530,6 +2548,10 @@
   // If true then flow labels will be changed when a PTO fires, or when
   // a PTO'd packet from a peer is detected.
   bool enable_black_hole_avoidance_via_flow_label_ : 1 = false;
+  // If true, stores only one copy of the destination connection ID in
+  // ReceivedPacketInfo.
+  const bool store_one_dcid_ : 1;
+
   const bool quic_limit_new_streams_per_loop_2_ : 1 =
       GetQuicReloadableFlag(quic_limit_new_streams_per_loop_2);
   const bool quic_test_peer_addr_change_after_normalize_ : 1 =
diff --git a/quiche/quic/test_tools/quic_connection_peer.cc b/quiche/quic/test_tools/quic_connection_peer.cc
index 9e94e70..1e549f9 100644
--- a/quiche/quic/test_tools/quic_connection_peer.cc
+++ b/quiche/quic/test_tools/quic_connection_peer.cc
@@ -567,7 +567,7 @@
       << " ecn_codepoint passed: " << (info.ecn_codepoint == ECN_NOT_ECT)
       << " sizeof(ReceivedPacketInfo) passed: "
       << (sizeof(size_t) != 8 ||
-          sizeof(QuicConnection::ReceivedPacketInfo) == 288);
+          sizeof(QuicConnection::ReceivedPacketInfo) == 272);
   return info.destination_address == QuicSocketAddress() &&
          info.source_address == QuicSocketAddress() &&
          info.receipt_time == QuicTime::Zero() &&
@@ -581,7 +581,7 @@
          // have changed. Please add the relevant conditions and update the
          // length below.
          (sizeof(size_t) != 8 ||
-          sizeof(QuicConnection::ReceivedPacketInfo) == 288);
+          sizeof(QuicConnection::ReceivedPacketInfo) == 272);
 }
 
 // static