use core::{alloc::Layout, ptr::NonNull};
use s2n_quic_core::{inet::datagram, io::tx, path};
#[cfg(s2n_quic_platform_cmsg)]
pub mod cmsg;
#[cfg(s2n_quic_platform_socket_mmsg)]
pub mod mmsg;
#[cfg(s2n_quic_platform_socket_msg)]
pub mod msg;
pub mod simple;
pub mod default {
cfg_if::cfg_if! {
if #[cfg(s2n_quic_platform_socket_mmsg)] {
pub use super::mmsg::*;
} else if #[cfg(s2n_quic_platform_socket_msg)] {
pub use super::msg::*;
} else {
pub use super::simple::*;
}
}
}
pub struct Storage {
ptr: NonNull<u8>,
layout: Layout,
}
unsafe impl Send for Storage {}
unsafe impl Sync for Storage {}
impl Storage {
#[inline]
pub fn new(layout: Layout) -> Self {
unsafe {
let ptr = alloc::alloc::alloc_zeroed(layout);
let ptr = NonNull::new(ptr).expect("could not allocate message storage");
Self { layout, ptr }
}
}
#[inline]
pub fn as_ptr(&self) -> *mut u8 {
self.ptr.as_ptr()
}
#[inline]
pub fn check_bounds<T: Sized>(&self, ptr: *mut T) {
let start = self.as_ptr();
let end = unsafe {
start.add(self.layout.size())
};
let allocation_range = start..=end;
let actual_end_ptr = ptr as *mut u8;
debug_assert!(allocation_range.contains(&actual_end_ptr));
}
}
impl Drop for Storage {
fn drop(&mut self) {
unsafe {
alloc::alloc::dealloc(self.as_ptr(), self.layout)
}
}
}
pub trait Message: 'static + Copy {
type Handle: path::Handle;
const SUPPORTS_GSO: bool;
const SUPPORTS_ECN: bool;
const SUPPORTS_FLOW_LABELS: bool;
fn alloc(entries: u32, payload_len: u32, offset: usize) -> Storage;
fn payload_len(&self) -> usize;
unsafe fn set_payload_len(&mut self, payload_len: usize);
fn validate_replication(source: &Self, dest: &Self);
fn payload_ptr_mut(&mut self) -> *mut u8;
#[inline]
fn payload_mut(&mut self) -> &mut [u8] {
unsafe { core::slice::from_raw_parts_mut(self.payload_ptr_mut(), self.payload_len()) }
}
fn set_segment_size(&mut self, _size: usize) {
panic!("cannot use GSO on the current platform");
}
unsafe fn reset(&mut self, mtu: usize);
fn rx_read(
&mut self,
local_address: &path::LocalAddress,
) -> Option<RxMessage<'_, Self::Handle>>;
fn tx_write<M: tx::Message<Handle = Self::Handle>>(
&mut self,
message: M,
) -> Result<usize, tx::Error>;
}
pub struct RxMessage<'a, Handle: Copy> {
pub header: datagram::Header<Handle>,
pub segment_size: usize,
pub payload: &'a mut [u8],
}
impl<Handle: Copy> RxMessage<'_, Handle> {
#[inline]
pub fn for_each<F: FnMut(datagram::Header<Handle>, &mut [u8])>(self, mut on_packet: F) {
if self.segment_size == 0 {
return;
}
for segment in self.payload.chunks_mut(self.segment_size) {
on_packet(self.header, segment);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use bolero::check;
#[test]
#[cfg_attr(kani, kani::proof, kani::unwind(17), kani::solver(minisat))]
fn rx_message_test() {
let path = bolero::produce::<path::RemoteAddress>();
let ecn = bolero::produce();
let segment_size = bolero::produce();
let max_payload_len = if cfg!(kani) { 16 } else { u16::MAX as usize };
let payload_len = 0..=max_payload_len;
check!()
.with_generator((path, ecn, segment_size, payload_len))
.cloned()
.for_each(|(path, ecn, segment_size, payload_len)| {
let mut payload = vec![0u8; payload_len];
let rx_message = RxMessage {
header: datagram::Header { path, ecn },
segment_size,
payload: &mut payload,
};
rx_message.for_each(|header, segment| {
assert_eq!(header.path, path);
assert_eq!(header.ecn, ecn);
assert!(segment.len() <= payload_len);
assert!(segment.len() <= segment_size);
})
})
}
}