____ _ _______ __
/ __ \ | /| / / __/ |/ /
/ /_/ / |/ |/ / _// /
\____/|__/|__/___/_||_/
snolc / src/fragment.rs
use std::collections::HashMap;
use std::time::{Duration, Instant};
use crate::error::{Error, Result};
pub const FRAGMENT_HEADER_LEN: usize = 16;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Fragment {
pub message: u64,
pub index: u16,
pub count: u16,
pub total: u32,
pub payload: Vec,
}
pub fn split(
message: u64,
payload: &[u8],
mtu: usize,
outer_overhead: usize,
) -> Result> {
let capacity = mtu
.checked_sub(outer_overhead)
.and_then(|space| space.checked_sub(FRAGMENT_HEADER_LEN))
.ok_or_else(|| Error::Protocol("carrier MTU cannot hold a fragment".to_owned()))?;
if capacity == 0 {
return Err(Error::Protocol(
"fragment payload capacity is zero".to_owned(),
));
}
let count = payload.len().max(1).div_ceil(capacity);
let count = u16::try_from(count)
.map_err(|_| Error::Protocol("packet requires too many fragments".to_owned()))?;
let total = u32::try_from(payload.len())
.map_err(|_| Error::Protocol("packet is too large".to_owned()))?;
if payload.is_empty() {
return Ok(vec![Fragment {
message,
index: 0,
count,
total,
payload: Vec::new(),
}]);
}
Ok(payload
.chunks(capacity)
.enumerate()
.map(|(index, chunk)| Fragment {
message,
index: index as u16,
count,
total,
payload: chunk.to_vec(),
})
.collect())
}
impl Fragment {
pub fn encode(&self) -> Vec {
let mut output = Vec::with_capacity(FRAGMENT_HEADER_LEN + self.payload.len());
output.extend_from_slice(&self.message.to_be_bytes());
output.extend_from_slice(&self.index.to_be_bytes());
output.extend_from_slice(&self.count.to_be_bytes());
output.extend_from_slice(&self.total.to_be_bytes());
output.extend_from_slice(&self.payload);
output
}
pub fn decode(input: &[u8]) -> Result {
if input.len() < FRAGMENT_HEADER_LEN {
return Err(Error::Protocol("truncated fragment".to_owned()));
}
let fragment = Self {
message: u64::from_be_bytes(input[..8].try_into().expect("checked fragment message")),
index: u16::from_be_bytes(input[8..10].try_into().expect("checked fragment index")),
count: u16::from_be_bytes(input[10..12].try_into().expect("checked fragment count")),
total: u32::from_be_bytes(input[12..16].try_into().expect("checked fragment total")),
payload: input[16..].to_vec(),
};
fragment.validate()?;
Ok(fragment)
}
fn validate(&self) -> Result<()> {
if self.count == 0 || self.index >= self.count {
return Err(Error::Protocol("invalid fragment position".to_owned()));
}
if self.payload.len() > self.total as usize {
return Err(Error::Protocol(
"fragment exceeds total packet length".to_owned(),
));
}
Ok(())
}
}
pub struct Reassembler {
entries: HashMap,
bytes: usize,
max_entries: usize,
max_bytes: usize,
timeout: Duration,
}
struct Assembly {
created: Instant,
count: u16,
total: u32,
received: usize,
parts: Vec