use crate::pdu::Pdu;
use crate::{Error, Packet};
pub(crate) struct AssembledPacket<P> {
packet: P,
written: usize,
}
impl<P> core::fmt::Debug for AssembledPacket<P> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("AssembledPacket")
.field("assembled", &self.written)
.finish()
}
}
#[cfg(feature = "defmt")]
impl<P> defmt::Format for AssembledPacket<P> {
fn format(&self, f: defmt::Formatter<'_>) {
defmt::write!(f, "assembled = {}", self.written);
}
}
impl<P> AssembledPacket<P> {
pub(crate) fn new(packet: P, initial: usize) -> Self {
Self {
packet,
written: initial,
}
}
pub(crate) fn write(&mut self, data: &[u8]) -> Result<(), Error>
where
P: Packet,
{
if self.written + data.len() <= self.packet.as_ref().len() {
self.packet.as_mut()[self.written..self.written + data.len()].copy_from_slice(data);
self.written += data.len();
Ok(())
} else {
Err(Error::InsufficientSpace)
}
}
pub(crate) fn len(&self) -> usize {
self.written
}
pub(crate) fn finalize(self, length: usize) -> Result<Pdu<P>, Error> {
if length != self.written {
return Err(Error::FailedToFinalize {
expected: length,
actual: self.written,
});
}
Ok(Pdu::new(self.packet, length))
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) struct PacketReassembly<P> {
state: Option<State<P>>,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) struct State<P> {
state: AssemblyState,
packet: AssembledPacket<P>,
}
impl<P> core::fmt::Debug for PacketReassembly<P> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PacketReassembly")
.field("state", &self.state.is_some())
.finish()
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) struct AssemblyState {
pub channel: u16,
pub length: u16,
}
impl<P> PacketReassembly<P> {
pub const fn new() -> Self {
Self { state: None }
}
pub fn init(&mut self, channel: u16, length: u16, p: P) -> Result<(), Error> {
self.init_with_written(channel, length, p, 0)
}
pub fn init_with_written(&mut self, channel: u16, length: u16, p: P, written: usize) -> Result<(), Error> {
if self.state.is_some() {
return Err(Error::InvalidState);
}
self.state.replace(State {
state: AssemblyState { channel, length },
packet: AssembledPacket::new(p, written),
});
Ok(())
}
pub fn clear(&mut self) {
let _ = self.state.take();
}
pub fn in_progress(&self) -> bool {
self.state.is_some()
}
pub fn update(&mut self, data: &[u8]) -> Result<Option<(AssemblyState, Pdu<P>)>, Error>
where
P: Packet,
{
if let Some(mut state) = self.state.take() {
state.packet.write(data)?;
let target = state.state.length as usize;
if state.packet.len() == target {
Ok(Some((state.state, state.packet.finalize(target)?)))
} else {
self.state.replace(state);
Ok(None)
}
} else {
Err(Error::NotFound)
}
}
}