use err_rs::{ErrorLevel, ErrorLevelProvider};
use flood_rs::in_stream::InOctetStream;
use flood_rs::prelude::OutOctetStream;
use flood_rs::{Deserialize, ReadOctetStream, Serialize};
use hexify::format_hex;
use log::trace;
use std::fmt::{Debug, Display};
use std::{io, mem};
pub mod prelude;
#[derive(Debug)]
pub enum DatagramChunkerError {
ItemSizeTooBig,
IoError(io::Error),
}
impl ErrorLevelProvider for DatagramChunkerError {
fn error_level(&self) -> ErrorLevel {
match self {
Self::ItemSizeTooBig => ErrorLevel::Critical,
Self::IoError(_) => ErrorLevel::Info,
}
}
}
pub struct DatagramChunker {
datagrams: Vec<Vec<u8>>,
current: Vec<u8>,
max_size: usize,
}
impl DatagramChunker {
#[must_use]
pub fn new(max_size: usize) -> Self {
Self {
current: Vec::with_capacity(max_size),
datagrams: Vec::new(),
max_size,
}
}
pub fn push(&mut self, buf: &[u8]) -> Result<(), DatagramChunkerError> {
if buf.len() > self.max_size {
return Err(DatagramChunkerError::ItemSizeTooBig);
}
if self.current.len() + buf.len() > self.max_size {
self.datagrams.push(mem::take(&mut self.current));
self.current = buf.to_vec();
} else {
self.current.extend_from_slice(buf);
}
Ok(())
}
#[must_use]
pub fn finalize(mut self) -> Vec<Vec<u8>> {
if !self.current.is_empty() {
self.datagrams.push(self.current.clone());
}
self.datagrams
}
}
impl From<io::Error> for DatagramChunkerError {
fn from(value: io::Error) -> Self {
Self::IoError(value)
}
}
pub fn serialize_to_datagrams<I, T>(
messages: I,
max_datagram_size: usize,
) -> Result<Vec<Vec<u8>>, DatagramChunkerError>
where
T: Serialize + Debug + Display,
I: AsRef<[T]>,
{
let mut chunker = DatagramChunker::new(max_datagram_size);
for message in messages.as_ref() {
let mut temp = OutOctetStream::new();
message.serialize(&mut temp)?;
trace!("serializing {message} to {}", format_hex(temp.octets_ref()));
chunker.push(temp.octets_ref())?;
}
Ok(chunker.finalize())
}
pub fn deserialize_datagram<T>(buf: &[u8]) -> Result<Vec<T>, io::Error>
where
T: Deserialize + Debug + Display,
{
let mut messages = vec![];
let mut in_stream = InOctetStream::new(buf);
while !&in_stream.has_reached_end() {
let message = T::deserialize(&mut in_stream)?;
trace!("deserialize {message}");
messages.push(message);
}
Ok(messages)
}
pub fn deserialize_datagrams<T>(datagrams: Vec<Vec<u8>>) -> Result<Vec<T>, io::Error>
where
T: Deserialize + Debug + Display,
{
let mut all_messages = Vec::new();
for datagram in datagrams {
let messages = deserialize_datagram(&datagram)?;
all_messages.extend(messages);
}
Ok(all_messages)
}