@@ -661,70 +661,42 @@ impl Connection {
661661
662662 builder. finish_and_track ( now, self , sent_frames. take ( ) , transmit. buf ) ;
663663
664- if transmit. num_datagrams == 1 {
665- // Set the segment size for this GSO batch to the size of the first UDP
666- // datagram in the batch. Larger data that cannot be fragmented
667- // (e.g. application datagrams) will be included in a future batch. When
668- // sending large enough volumes of data for GSO to be useful, we expect
669- // packet sizes to usually be consistent, e.g. populated by max-size STREAM
670- // frames or uniformly sized datagrams.
671- transmit. segment_size = transmit. len ( ) ;
672- // Clip the unused capacity out of the buffer so future packets don't
673- // overrun
674- transmit. buf_capacity = transmit. len ( ) ;
675-
676- // Check whether the data we planned to send will fit in the reduced segment
677- // size. If not, bail out and leave it for the next GSO batch so we don't
678- // end up trying to send an empty packet. We can't easily compute the right
679- // segment size before the original call to `space_can_send`, because at
680- // that time we haven't determined whether we're going to coalesce with the
681- // first datagram or potentially pad it to `MIN_INITIAL_SIZE`.
682- if space_id == SpaceId :: Data {
683- let frame_space_1rtt = transmit
684- . segment_size
685- . saturating_sub ( self . predict_1rtt_overhead ( Some ( pn) ) ) ;
686- if self . space_can_send ( space_id, frame_space_1rtt) . is_empty ( ) {
687- break ;
688- }
664+ if transmit. num_datagrams == 1 && space_id == SpaceId :: Data {
665+ // Now that we know the size of the first datagram, check whether
666+ // the data we planned to send will fit in the next segment. If
667+ // not, bails out and leave it for the next GSO batch. We can't
668+ // easily compute the right segment size before the original call to
669+ // `space_can_send`, because at that time we haven't determined
670+ // whether we're going to coalesce with the first datagram or
671+ // potentially pad it to `MIN_INITIAL_SIZE`.
672+
673+ transmit. clip_datagram_size ( ) ;
674+
675+ let frame_space_1rtt = transmit
676+ . segment_size
677+ . saturating_sub ( self . predict_1rtt_overhead ( Some ( pn) ) ) ;
678+ if self . space_can_send ( space_id, frame_space_1rtt) . is_empty ( ) {
679+ break ;
689680 }
690681 }
691682 }
692683
693- // Allocate space for another datagram
694- let next_datagram_size_limit = match self . spaces [ space_id] . loss_probes {
695- 0 => transmit. segment_size ,
684+ // Start the next datagram
685+ match self . spaces [ space_id] . loss_probes {
686+ 0 => transmit. start_new_datagram ( ) ,
696687 _ => {
697688 self . spaces [ space_id] . loss_probes -= 1 ;
698689 // Clamp the datagram to at most the minimum MTU to ensure that loss probes
699690 // can get through and enable recovery even if the path MTU has shrank
700691 // unexpectedly.
701- std:: cmp:: min ( transmit. segment_size , usize:: from ( INITIAL_MTU ) )
692+ transmit. start_new_datagram_with_size ( std:: cmp:: min (
693+ usize:: from ( INITIAL_MTU ) ,
694+ transmit. segment_size ,
695+ ) ) ;
702696 }
703697 } ;
704- transmit. buf_capacity += next_datagram_size_limit;
705- if transmit. buf . capacity ( ) < transmit. buf_capacity {
706- // We reserve the maximum space for sending `max_datagrams` upfront
707- // to avoid any reallocations if more datagrams have to be appended later on.
708- // Benchmarks have shown shown a 5-10% throughput improvement
709- // compared to continuously resizing the datagram buffer.
710- // While this will lead to over-allocation for small transmits
711- // (e.g. purely containing ACKs), modern memory allocators
712- // (e.g. mimalloc and jemalloc) will pool certain allocation sizes
713- // and therefore this is still rather efficient.
714- transmit
715- . buf
716- . reserve ( transmit. max_datagrams * transmit. segment_size ) ;
717- }
718- transmit. num_datagrams += 1 ;
719698 coalesce = true ;
720699 pad_datagram = false ;
721- transmit. datagram_start = transmit. len ( ) ;
722-
723- debug_assert_eq ! (
724- transmit. datagram_start % transmit. segment_size,
725- 0 ,
726- "datagrams in a GSO batch must be aligned to the segment size"
727- ) ;
728700 } else {
729701 // We can append/coalesce the next packet into the current
730702 // datagram.
@@ -926,8 +898,8 @@ impl Connection {
926898 . mtud
927899 . poll_transmit ( now, self . packet_number_filter . peek ( & self . spaces [ space_id] ) ) ?;
928900
929- transmit. buf_capacity = probe_size as usize ;
930- transmit. buf . reserve ( transmit . buf_capacity ) ;
901+ debug_assert_eq ! ( transmit. num_datagrams , 0 ) ;
902+ transmit. start_new_datagram_with_size ( probe_size as usize ) ;
931903
932904 debug_assert_eq ! ( transmit. datagram_start, 0 ) ;
933905 let mut builder = PacketBuilder :: new (
@@ -957,7 +929,6 @@ impl Connection {
957929 builder. finish_and_track ( now, self , Some ( sent_frames) , transmit. buf ) ;
958930
959931 self . stats . path . sent_plpmtud_probes += 1 ;
960- transmit. num_datagrams = 1 ;
961932
962933 trace ! ( ?probe_size, "writing MTUD probe" ) ;
963934 }
@@ -1013,9 +984,7 @@ impl Connection {
1013984 SpaceId :: Data ,
1014985 "PATH_CHALLENGE queued without 1-RTT keys"
1015986 ) ;
1016- buf. buf . reserve ( MIN_INITIAL_SIZE as usize ) ;
1017-
1018- buf. buf_capacity = buf. buf . capacity ( ) ;
987+ buf. start_new_datagram_with_size ( MIN_INITIAL_SIZE as usize ) ;
1019988
1020989 // Use the previous CID to avoid linking the new path with the previous path. We
1021990 // don't bother accounting for possible retirement of that prev_cid because this is
0 commit comments