in quic/s2n-quic-platform/src/socket/io/tx.rs [204:304]
fn try_gso<M: tx::Message<Handle = T::Handle>>(
&mut self,
mut message: M,
) -> Result<Result<tx::Outcome, M>, tx::Error> {
// the message doesn't support GSO to return it
if !T::SUPPORTS_GSO {
return Ok(Err(message));
}
let max_segments = self.max_segments;
let (prev_message, gso) = if let Some(gso) = self.gso_message() {
gso
} else {
return Ok(Err(message));
};
debug_assert!(
max_segments > 1,
"gso_segment should only be set when max_gso > 1"
);
// check to make sure the message can be GSO'd and can be included in the same
// GSO payload as the previous message
let can_gso = message.can_gso(gso.size, gso.count)
&& message.path_handle().strict_eq(&gso.handle)
&& message.ecn() == gso.ecn;
// if we can't use GSO then flush the current message
if !can_gso {
self.flush_gso();
return Ok(Err(message));
}
debug_assert!(
gso.count < max_segments,
"{} cannot exceed {}",
gso.count,
max_segments
);
let payload_len = prev_message.payload_len();
let buffer = unsafe {
// Create a slice the `message` can write into. This avoids having to update the
// payload length and worrying about panic safety.
let payload = prev_message.payload_ptr_mut();
// Safety: all payloads should have enough capacity to extend max_segments *
// gso.size
let current_payload = payload.add(payload_len);
core::slice::from_raw_parts_mut(current_payload, gso.size)
};
let buffer = tx::PayloadBuffer::new(buffer);
let size = message.write_payload(buffer, gso.count)?;
// we don't want to send empty packets
if size == 0 {
return Err(tx::Error::EmptyPayload);
}
unsafe {
debug_assert!(
gso.size >= size,
"the payload tried to write more than available"
);
// Set the len to the actual amount written to the payload. In case there is a bug,
// take the min anyway so we don't have errors elsewhere.
prev_message.set_payload_len(payload_len + size.min(gso.size));
}
// increment the number of segments that we've written
gso.count += 1;
debug_assert!(
gso.count <= max_segments,
"{} cannot exceed {}",
gso.count,
max_segments
);
// the last segment can be smaller but we can't write any more if it is
let size_mismatch = gso.size != size;
// we're bounded by the max_segments amount
let at_segment_limit = gso.count >= max_segments;
// we also can't write more data than u16::MAX
let at_payload_limit = gso.size * (gso.count + 1) > u16::MAX as usize;
// if we've hit any limits, then flush the GSO information to the message
if size_mismatch || at_segment_limit || at_payload_limit {
self.flush_gso();
}
Ok(Ok(tx::Outcome {
len: size,
index: 0,
}))
}