use alloc::vec::Vec;
#[derive(Debug, Clone)]
pub struct PusiReassembler {
pid: u16,
buf: Vec<u8>,
started: bool,
}
impl PusiReassembler {
#[inline]
pub fn new(pid: u16) -> Self {
Self {
pid,
buf: Vec::new(),
started: false,
}
}
pub fn push(&mut self, pid: u16, pusi: bool, payload: &[u8]) -> Option<Vec<u8>> {
if pid != self.pid {
return None;
}
if pusi {
if self.started {
let completed = core::mem::take(&mut self.buf);
self.buf.extend_from_slice(payload);
return Some(completed);
}
self.started = true;
self.buf.extend_from_slice(payload);
return None;
}
self.buf.extend_from_slice(payload);
None
}
pub fn flush(&mut self) -> Option<Vec<u8>> {
if self.started && !self.buf.is_empty() {
self.started = false;
Some(core::mem::take(&mut self.buf))
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const BIG_BOX_SIZE: u32 = 400;
fn synthetic_box_bytes(size: u32) -> Vec<u8> {
let n = size as usize;
let mut bytes = Vec::with_capacity(n);
bytes.extend_from_slice(&size.to_be_bytes()); bytes.extend_from_slice(b"test"); for i in 8..n {
bytes.push((i & 0xFF) as u8);
}
debug_assert_eq!(bytes.len(), n);
bytes
}
#[test]
fn spanning_across_two_packets() {
let box_bytes = synthetic_box_bytes(BIG_BOX_SIZE);
let pid = 0x0004u16;
let chunk1 = &box_bytes[..184];
let chunk2 = &box_bytes[184..];
let mut reasm = PusiReassembler::new(pid);
assert!(reasm.push(pid, true, chunk1).is_none());
assert!(reasm.push(pid, false, chunk2).is_none());
let reassembled = reasm.flush().expect("flush should return the unit");
assert_eq!(reassembled, box_bytes);
}
#[test]
fn boundary_two_units() {
let unit_a = b"AAAA-first-unit-data";
let unit_b = b"BBBB-second-unit-data";
let pid = 0x0004u16;
let mut reasm = PusiReassembler::new(pid);
assert!(reasm.push(pid, true, unit_a).is_none());
let closed = reasm.push(pid, true, unit_b);
assert_eq!(closed.as_deref(), Some(unit_a.as_slice()));
let flushed = reasm.flush();
assert_eq!(flushed.as_deref(), Some(unit_b.as_slice()));
}
#[test]
fn different_pid_ignored() {
let pid = 0x0004u16;
let mut reasm = PusiReassembler::new(pid);
assert!(reasm.push(0x0100, true, b"ignored").is_none());
assert!(reasm.push(0x0100, false, b"more-ignored").is_none());
assert!(reasm.flush().is_none());
assert!(reasm.push(pid, true, b"real-data").is_none());
let flushed = reasm.flush();
assert_eq!(flushed.as_deref(), Some(b"real-data".as_slice()));
}
#[test]
fn flush_empty_returns_none() {
let mut reasm = PusiReassembler::new(0x0004);
assert!(reasm.flush().is_none());
}
#[test]
fn single_packet_unit() {
let pid = 0x0004u16;
let mut reasm = PusiReassembler::new(pid);
assert!(reasm.push(pid, true, b"single-packet-emsg").is_none());
let flushed = reasm.flush();
assert_eq!(flushed.as_deref(), Some(b"single-packet-emsg".as_slice()));
}
}