pub const MAX_FRAGMENT_PAYLOAD: usize = 1200;
pub const FRAGMENT_HEADER_SIZE: usize = 8;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FragmentFrame {
pub fragment_id: u32,
pub fragment_offset: u16,
pub total_length: u16,
pub payload: bytes::Bytes,
}
impl FragmentFrame {
pub fn serialize(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(FRAGMENT_HEADER_SIZE + self.payload.len());
buf.extend_from_slice(&self.fragment_id.to_be_bytes());
buf.extend_from_slice(&self.fragment_offset.to_be_bytes());
buf.extend_from_slice(&self.total_length.to_be_bytes());
buf.extend_from_slice(&self.payload);
buf
}
pub fn deserialize(buf: &[u8]) -> Option<Self> {
if buf.len() < FRAGMENT_HEADER_SIZE {
return None;
}
let fragment_id = u32::from_be_bytes(buf[0..4].try_into().ok()?);
let fragment_offset = u16::from_be_bytes(buf[4..6].try_into().ok()?);
let total_length = u16::from_be_bytes(buf[6..8].try_into().ok()?);
let payload = bytes::Bytes::copy_from_slice(&buf[8..]);
let end = (fragment_offset as usize).checked_add(payload.len())?;
if end > total_length as usize {
return None;
}
Some(Self {
fragment_id,
fragment_offset,
total_length,
payload,
})
}
pub fn is_last(&self) -> bool {
self.fragment_offset as usize + self.payload.len() == self.total_length as usize
}
}
pub fn fragment_packet(data: bytes::Bytes, fragment_id: u32) -> Vec<FragmentFrame> {
if data.is_empty() {
return Vec::new();
}
let total_length = data.len() as u16;
let mut frames = Vec::with_capacity(data.len().div_ceil(MAX_FRAGMENT_PAYLOAD));
let mut offset = 0usize;
while offset < data.len() {
let end = (offset + MAX_FRAGMENT_PAYLOAD).min(data.len());
frames.push(FragmentFrame {
fragment_id,
fragment_offset: offset as u16,
total_length,
payload: data.slice(offset..end),
});
offset = end;
}
frames
}
#[cfg(test)]
mod tests;