use serde::Deserialize;
#[cfg_attr(feature = "use-defmt", derive(defmt::Format))]
pub struct CobsAccumulator<const N: usize> {
buf: [u8; N],
idx: usize,
}
#[cfg_attr(feature = "use-defmt", derive(defmt::Format))]
pub enum FeedResult<'a, T> {
Consumed,
OverFull(&'a [u8]),
DeserError(&'a [u8]),
Success {
data: T,
remaining: &'a [u8],
},
}
impl<const N: usize> CobsAccumulator<N> {
pub const fn new() -> Self {
CobsAccumulator {
buf: [0; N],
idx: 0,
}
}
pub fn feed<'a, T>(&mut self, input: &'a [u8]) -> FeedResult<'a, T>
where
T: for<'de> Deserialize<'de>,
{
if input.is_empty() {
return FeedResult::Consumed;
}
let zero_pos = input.iter().position(|&i| i == 0);
if let Some(n) = zero_pos {
let (take, release) = input.split_at(n + 1);
if (self.idx + n) <= N {
self.extend_unchecked(take);
let retval = match crate::from_bytes_cobs::<T>(&mut self.buf[..self.idx]) {
Ok(t) => FeedResult::Success {
data: t,
remaining: release,
},
Err(_) => FeedResult::DeserError(release),
};
self.idx = 0;
retval
} else {
self.idx = 0;
FeedResult::OverFull(release)
}
} else {
if (self.idx + input.len()) > N {
let new_start = N - self.idx;
self.idx = 0;
FeedResult::OverFull(&input[new_start..])
} else {
self.extend_unchecked(input);
FeedResult::Consumed
}
}
}
fn extend_unchecked(&mut self, input: &[u8]) {
let new_end = self.idx + input.len();
self.buf[self.idx..new_end].copy_from_slice(input);
self.idx = new_end;
}
}
#[test]
fn loop_test() {
#[derive(serde::Serialize, Deserialize, Debug, PartialEq, Eq)]
struct Demo {
a: u32,
b: u8,
}
let mut raw_buf = [0u8; 64];
let mut cobs_buf: CobsAccumulator<64> = CobsAccumulator::new();
let ser = crate::to_slice_cobs(&Demo { a: 10, b: 20 }, &mut raw_buf).unwrap();
if let FeedResult::Success { data, remaining } = cobs_buf.feed(ser) {
assert_eq!(Demo { a: 10, b: 20 }, data);
assert_eq!(remaining.len(), 0);
} else {
panic!()
}
}