use core::fmt;
use crate::decode::{check_frame, decode_extended_in_place};
use crate::frame::{DATA_OFFSET, wire_length};
use crate::{Frame, FrameError, FrameHeader, PREAMBLE};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[must_use = "the event may be a frame, which the next push drops"]
pub enum DecodeEvent<'a> {
NeedMore,
Frame {
frame: Frame<'a>,
consumed: usize,
},
Invalid {
discard: usize,
},
Malformed {
discard: usize,
error: FrameError,
destination: Option<u8>,
},
Error(FrameError),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum StreamPhase {
Sync,
Preamble,
Header,
Data,
}
pub struct StreamDecoder<'a> {
storage: &'a mut [u8],
start: usize,
length: usize,
pending: usize,
skip: usize,
reserved_cobs: bool,
}
impl fmt::Debug for StreamDecoder<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("StreamDecoder")
.field("capacity", &self.capacity())
.field("buffered", &self.length)
.field("pending", &self.pending)
.field("skip", &self.skip)
.field("phase", &self.phase())
.field("reserved_cobs", &self.reserved_cobs)
.finish()
}
}
const DESTINATION_INDEX: usize = PREAMBLE.len() + 1;
const LENGTH_INDEX: usize = PREAMBLE.len() + 3;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum InternalEvent {
NeedMore,
Frame {
header: FrameHeader,
data_length: usize,
consumed: usize,
},
Invalid {
discard: usize,
},
Malformed {
discard: usize,
error: FrameError,
destination: Option<u8>,
},
}
impl<'a> StreamDecoder<'a> {
#[must_use]
pub const fn new(storage: &'a mut [u8]) -> Self {
Self {
storage,
start: 0,
length: 0,
pending: 0,
skip: 0,
reserved_cobs: false,
}
}
#[must_use]
pub const fn with_reserved_cobs(mut self) -> Self {
self.reserved_cobs = true;
self
}
#[must_use]
pub const fn checks_reserved_cobs(&self) -> bool {
self.reserved_cobs
}
#[must_use]
pub const fn buffered_len(&self) -> usize {
self.length
}
#[must_use]
pub const fn capacity(&self) -> usize {
self.storage.len()
}
#[must_use]
pub fn phase(&self) -> StreamPhase {
if self.skip != 0 {
return StreamPhase::Data;
}
let buffered = self.buffered();
if self.pending != 0 || buffered.first() != Some(&PREAMBLE[0]) {
return StreamPhase::Sync;
}
if self.length == 1 {
return StreamPhase::Preamble;
}
if buffered[1] != PREAMBLE[1] {
return StreamPhase::Sync;
}
if self.length < DATA_OFFSET {
StreamPhase::Header
} else {
StreamPhase::Data
}
}
pub fn reset(&mut self) {
self.start = 0;
self.length = 0;
self.pending = 0;
self.skip = 0;
}
pub fn push(&mut self, byte: u8) -> DecodeEvent<'_> {
self.push_slice(core::slice::from_ref(&byte))
}
pub fn push_slice(&mut self, input: &[u8]) -> DecodeEvent<'_> {
self.commit_pending();
let skipped = self.skip.min(input.len());
let rest = &input[skipped..];
let free = self.storage.len() - self.length;
if rest.len() > free {
return DecodeEvent::Error(FrameError::BufferExhausted {
needed: rest.len(),
available: free,
});
}
self.skip -= skipped;
if self.start + self.length + rest.len() > self.storage.len() {
self.compact();
}
let end = self.start + self.length;
self.storage[end..end + rest.len()].copy_from_slice(rest);
self.length += rest.len();
match self.try_decode() {
InternalEvent::NeedMore => DecodeEvent::NeedMore,
InternalEvent::Frame {
header,
data_length,
consumed,
} => DecodeEvent::Frame {
frame: Frame {
header,
data: &self.buffered()[DATA_OFFSET..DATA_OFFSET + data_length],
},
consumed,
},
InternalEvent::Invalid { discard } => DecodeEvent::Invalid { discard },
InternalEvent::Malformed {
discard,
error,
destination,
} => DecodeEvent::Malformed {
discard,
error,
destination,
},
}
}
fn commit_pending(&mut self) {
if self.pending == 0 {
return;
}
self.discard(self.pending);
self.pending = 0;
}
fn try_decode(&mut self) -> InternalEvent {
let buffered = self.buffered();
let Some(&first) = buffered.first() else {
return InternalEvent::NeedMore;
};
let may_start_preamble =
first == PREAMBLE[0] && buffered.get(1).is_none_or(|&second| second == PREAMBLE[1]);
if !may_start_preamble {
let discard = buffered[1..]
.iter()
.position(|&octet| octet == PREAMBLE[0])
.map_or(buffered.len(), |index| index + 1);
self.discard(discard);
return InternalEvent::Invalid { discard };
}
let error = match check_frame(buffered, self.reserved_cobs) {
Ok(valid) => {
let end = self.start + self.length;
decode_extended_in_place(&mut self.storage[self.start..end], &valid);
self.pending = valid.consumed;
return InternalEvent::Frame {
header: valid.header,
data_length: valid.data_length,
consumed: valid.consumed,
};
}
Err(error) => error,
};
let destination = match error {
FrameError::TooShort { .. }
| FrameError::InvalidPreamble { .. }
| FrameError::HeaderCrcMismatch => None,
FrameError::BroadcastSource
| FrameError::InvalidCobsLength { .. }
| FrameError::Oversize { .. }
| FrameError::UnsupportedCobsFrame { .. }
| FrameError::Truncated { .. }
| FrameError::DataCrcMismatch { .. } => Some(self.buffered()[DESTINATION_INDEX]),
FrameError::ExtendedFrame { .. }
| FrameError::UnexpectedData { .. }
| FrameError::MissingVendorId { .. }
| FrameError::FrameTooLong { .. }
| FrameError::BufferExhausted { .. } => None,
};
match error {
FrameError::TooShort { needed, .. } => self.wait_for_header(needed),
FrameError::Truncated { length, .. } => {
let needed = wire_length(length);
let capacity = self.storage.len();
if needed <= capacity {
return InternalEvent::NeedMore;
}
self.skip_frame(
needed,
FrameError::FrameTooLong { needed, capacity },
destination,
)
}
FrameError::UnsupportedCobsFrame { .. } => {
let length = self.declared_length();
self.skip_frame(wire_length(length), error, destination)
}
FrameError::DataCrcMismatch { length } => {
self.skip_frame(wire_length(length), error, destination)
}
_ => {
self.discard(1);
InternalEvent::Malformed {
discard: 1,
error,
destination,
}
}
}
}
fn wait_for_header(&mut self, needed: usize) -> InternalEvent {
let capacity = self.storage.len();
if needed <= capacity {
return InternalEvent::NeedMore;
}
self.discard(1);
InternalEvent::Malformed {
discard: 1,
error: FrameError::FrameTooLong { needed, capacity },
destination: None,
}
}
fn declared_length(&self) -> usize {
let buffered = self.buffered();
usize::from(u16::from_be_bytes([
buffered[LENGTH_INDEX],
buffered[LENGTH_INDEX + 1],
]))
}
fn skip_frame(
&mut self,
total: usize,
error: FrameError,
destination: Option<u8>,
) -> InternalEvent {
if total <= self.length {
self.discard(total);
} else {
self.skip = total - self.length;
self.start = 0;
self.length = 0;
}
InternalEvent::Malformed {
discard: total,
error,
destination,
}
}
fn buffered(&self) -> &[u8] {
&self.storage[self.start..self.start + self.length]
}
fn discard(&mut self, count: usize) {
let count = count.min(self.length);
self.start += count;
self.length -= count;
if self.length == 0 {
self.start = 0;
}
}
fn compact(&mut self) {
let gap = self.start;
if gap == 0 {
return;
}
let end = self.start + self.length;
let mut from = gap;
while from < end {
let count = gap.min(end - from);
let (front, back) = self.storage.split_at_mut(from);
front[from - gap..from - gap + count].copy_from_slice(&back[..count]);
from += count;
}
self.start = 0;
}
}