#[derive(Debug, Default)]
pub enum DecoderState {
#[default]
Idle,
Grab(u8),
GrabChain(u8),
}
fn add(to: &mut [u8], idx: usize, data: u8) -> Result<(), DecodeError> {
*to.get_mut(idx).ok_or(DecodeError::TargetBufTooSmall)? = data;
Ok(())
}
#[derive(Debug)]
pub enum DecodeResult {
NoData,
DataStart,
DataComplete,
DataContinue(u8),
}
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DecodeError {
#[error("empty input frame")]
EmptyFrame,
#[error("frame with invalid format, written {decoded_bytes:?} to decoded buffer")]
InvalidFrame {
decoded_bytes: usize,
},
#[error("target buffer too small")]
TargetBufTooSmall,
}
impl DecoderState {
pub fn feed(&mut self, data: u8) -> Result<DecodeResult, DecodeError> {
use DecodeResult::*;
use DecoderState::*;
let (ret, state) = match (&self, data) {
(Grab(i), n) => {
if *i > 0 && n > 0 {
(Ok(DataContinue(n)), Grab(*i - 1))
} else if *i == 0 {
match n {
0x00 => (Ok(DataComplete), Idle),
0xFF => (Ok(DataContinue(0)), GrabChain(0xFE)),
n => (Ok(DataContinue(0)), Grab(n - 1)),
}
} else {
(Err(DecodeError::InvalidFrame { decoded_bytes: 0 }), Idle)
}
}
(GrabChain(i), n) => {
if *i > 0 && n > 0 {
(Ok(DataContinue(n)), GrabChain(*i - 1))
} else if *i == 0 {
match n {
0x00 => (Ok(DataComplete), Idle),
0xFF => (Ok(NoData), GrabChain(0xFE)),
n => (Ok(NoData), Grab(n - 1)),
}
} else {
(Err(DecodeError::InvalidFrame { decoded_bytes: 0 }), Idle)
}
}
(Idle, 0x00) => (Ok(NoData), Idle),
(Idle, 0xFF) => (Ok(DataStart), GrabChain(0xFE)),
(Idle, n) => (Ok(DataStart), Grab(n - 1)),
};
*self = state;
ret
}
}
#[derive(Debug, Default)]
struct CobsDecoderInner {
dest_idx: usize,
state: DecoderState,
}
impl CobsDecoderInner {
const fn new() -> Self {
Self {
dest_idx: 0,
state: DecoderState::Idle,
}
}
fn feed(&mut self, dest: &mut [u8], data: u8) -> Result<Option<usize>, DecodeError> {
match self.state.feed(data) {
Err(_) => Err(DecodeError::InvalidFrame {
decoded_bytes: self.dest_idx,
}),
Ok(DecodeResult::NoData) => Ok(None),
Ok(DecodeResult::DataStart) => {
self.dest_idx = 0;
Ok(None)
}
Ok(DecodeResult::DataContinue(n)) => {
add(dest, self.dest_idx, n)?;
self.dest_idx += 1;
Ok(None)
}
Ok(DecodeResult::DataComplete) => Ok(Some(self.dest_idx)),
}
}
pub fn push(
&mut self,
dest: &mut [u8],
data: &[u8],
) -> Result<Option<DecodeReport>, DecodeError> {
for (consumed_idx, byte) in data.iter().enumerate() {
let opt_decoded_bytes = self.feed(dest, *byte)?;
if let Some(decoded_bytes_ct) = opt_decoded_bytes {
return Ok(Some(DecodeReport {
frame_size: decoded_bytes_ct,
parsed_size: consumed_idx + 1,
}));
}
}
Ok(None)
}
}
#[derive(Debug)]
pub struct CobsDecoder<'a> {
dest: &'a mut [u8],
inner: CobsDecoderInner,
}
impl<'a> CobsDecoder<'a> {
pub const fn new(dest: &'a mut [u8]) -> CobsDecoder<'a> {
CobsDecoder {
dest,
inner: CobsDecoderInner::new(),
}
}
pub fn feed(&mut self, data: u8) -> Result<Option<usize>, DecodeError> {
self.inner.feed(self.dest, data)
}
pub fn push(&mut self, data: &[u8]) -> Result<Option<DecodeReport>, DecodeError> {
self.inner.push(self.dest, data)
}
#[inline]
pub fn dest(&self) -> &[u8] {
self.dest
}
#[inline]
pub fn dest_mut(&mut self) -> &mut [u8] {
self.dest
}
}
#[derive(Default, Debug)]
pub struct CobsDecoderHeapless<const N: usize> {
dest: heapless::Vec<u8, N>,
inner: CobsDecoderInner,
}
impl<const N: usize> CobsDecoderHeapless<N> {
pub fn new() -> Self {
let vec = heapless::Vec::new();
Self::new_with_vec(vec)
}
pub fn new_with_vec(mut vec: heapless::Vec<u8, N>) -> Self {
vec.resize(vec.capacity(), 0).unwrap();
Self {
dest: vec,
inner: CobsDecoderInner::new(),
}
}
pub fn feed(&mut self, data: u8) -> Result<Option<usize>, DecodeError> {
self.inner.feed(&mut self.dest, data)
}
pub fn push(&mut self, data: &[u8]) -> Result<Option<DecodeReport>, DecodeError> {
self.inner.push(&mut self.dest, data)
}
#[inline]
pub fn dest(&self) -> &[u8] {
&self.dest
}
#[inline]
pub fn dest_mut(&mut self) -> &mut [u8] {
&mut self.dest
}
#[inline]
pub fn reset(&mut self) {
self.inner = Default::default();
}
}
#[cfg(feature = "alloc")]
#[derive(Debug)]
pub struct CobsDecoderOwned {
dest: alloc::vec::Vec<u8>,
inner: CobsDecoderInner,
}
#[cfg(feature = "alloc")]
impl CobsDecoderOwned {
pub fn new(dest_buf_size: usize) -> Self {
Self {
dest: alloc::vec![0; dest_buf_size],
inner: CobsDecoderInner::new(),
}
}
pub fn feed(&mut self, data: u8) -> Result<Option<usize>, DecodeError> {
self.inner.feed(&mut self.dest, data)
}
pub fn push(&mut self, data: &[u8]) -> Result<Option<DecodeReport>, DecodeError> {
self.inner.push(&mut self.dest, data)
}
#[inline]
pub fn dest(&self) -> &[u8] {
&self.dest
}
#[inline]
pub fn dest_mut(&mut self) -> &mut [u8] {
&mut self.dest
}
#[inline]
pub fn reset(&mut self) {
self.inner = Default::default();
}
}
pub fn decode(source: &[u8], dest: &mut [u8]) -> Result<DecodeReport, DecodeError> {
if source.is_empty() {
return Err(DecodeError::EmptyFrame);
}
let mut dec = CobsDecoder::new(dest);
if let Some(result) = dec.push(source)? {
return Ok(result);
}
if source.last() != Some(&0) {
if let Some(result) = dec.push(&[0])? {
return Ok(DecodeReport {
frame_size: result.frame_size(),
parsed_size: source.len(),
});
}
}
Err(DecodeError::InvalidFrame {
decoded_bytes: dec.inner.dest_idx,
})
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DecodeReport {
parsed_size: usize,
frame_size: usize,
}
impl DecodeReport {
#[inline]
pub fn parsed_size(&self) -> usize {
self.parsed_size
}
#[inline]
pub fn frame_size(&self) -> usize {
self.frame_size
}
}
pub fn decode_in_place_report(buf: &mut [u8]) -> Result<DecodeReport, DecodeError> {
let mut source_index = 0;
let mut dest_index = 0;
if buf.is_empty() {
return Err(DecodeError::EmptyFrame);
}
let src_end = if let Some(end) = buf.iter().position(|b| *b == 0) {
end
} else {
buf.len()
};
while source_index < src_end {
let code = buf[source_index];
if source_index + code as usize > src_end && code != 1 {
return Err(DecodeError::InvalidFrame {
decoded_bytes: dest_index,
});
}
source_index += 1;
for _ in 1..code {
*buf.get_mut(dest_index)
.ok_or(DecodeError::TargetBufTooSmall)? = buf[source_index];
source_index += 1;
dest_index += 1;
}
if 0xFF != code && source_index < src_end {
*buf.get_mut(dest_index)
.ok_or(DecodeError::TargetBufTooSmall)? = 0;
dest_index += 1;
}
}
Ok(DecodeReport {
frame_size: dest_index,
parsed_size: source_index,
})
}
pub fn decode_in_place(buff: &mut [u8]) -> Result<usize, DecodeError> {
decode_in_place_report(buff).map(|res| res.frame_size())
}
pub fn decode_with_sentinel(
source: &[u8],
dest: &mut [u8],
sentinel: u8,
) -> Result<usize, DecodeError> {
for (x, y) in source.iter().zip(dest.iter_mut()) {
*y = *x ^ sentinel;
}
decode_in_place(dest)
}
pub fn decode_in_place_with_sentinel(buff: &mut [u8], sentinel: u8) -> Result<usize, DecodeError> {
for x in buff.iter_mut() {
*x ^= sentinel;
}
decode_in_place(buff)
}
#[cfg(feature = "alloc")]
pub fn decode_vec(source: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
let mut decoded = alloc::vec![0; source.len()];
let result = decode(source, &mut decoded[..])?;
decoded.truncate(result.frame_size());
Ok(decoded)
}
#[cfg(feature = "alloc")]
pub fn decode_vec_with_sentinel(
source: &[u8],
sentinel: u8,
) -> Result<alloc::vec::Vec<u8>, DecodeError> {
let mut decoded = alloc::vec![0; source.len()];
let n = decode_with_sentinel(source, &mut decoded[..], sentinel)?;
decoded.truncate(n);
Ok(decoded)
}
#[deprecated(since = "0.5.0", note = "use DecodeReport instead")]
pub type DecodingResult = DecodeReport;
#[cfg(test)]
mod tests {
use crate::{encode, encode_vec_including_sentinels};
use super::*;
#[test]
fn decode_malformed() {
let malformed_buf: [u8; 32] = [
68, 69, 65, 68, 66, 69, 69, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
];
let mut dest_buf: [u8; 32] = [0; 32];
if let Err(DecodeError::InvalidFrame { decoded_bytes }) =
decode(&malformed_buf, &mut dest_buf)
{
assert_eq!(decoded_bytes, 7);
} else {
panic!("decoding worked when it should not have");
}
}
#[test]
fn decode_empty() {
#[cfg(feature = "alloc")]
matches!(decode_vec(&[]).unwrap_err(), DecodeError::EmptyFrame);
matches!(
decode_in_place(&mut []).unwrap_err(),
DecodeError::EmptyFrame
);
matches!(
decode(&[], &mut [0; 256]).unwrap_err(),
DecodeError::EmptyFrame
);
}
#[test]
fn decode_target_buf_too_small() {
let encoded = &[3, 10, 11, 2, 12];
let expected_decoded_len = 4;
for i in 0..expected_decoded_len - 1 {
let mut dest = alloc::vec![0; i];
let result = decode(encoded, &mut dest);
assert_eq!(result, Err(DecodeError::TargetBufTooSmall));
}
}
fn continuous_decoding(decoder: &mut CobsDecoder, expected_data: &[u8], encoded_frame: &[u8]) {
for _ in 0..10 {
for byte in encoded_frame.iter().take(encoded_frame.len() - 1) {
decoder.feed(*byte).unwrap();
}
if let Ok(Some(sz_msg)) = decoder.feed(encoded_frame[encoded_frame.len() - 1]) {
assert_eq!(sz_msg, expected_data.len());
assert_eq!(expected_data, &decoder.dest()[0..sz_msg]);
} else {
panic!("decoding call did not yield expected frame");
}
}
}
fn continuous_decoding_heapless(
decoder: &mut CobsDecoderHeapless<32>,
expected_data: &[u8],
encoded_frame: &[u8],
) {
for _ in 0..10 {
for byte in encoded_frame.iter().take(encoded_frame.len() - 1) {
decoder.feed(*byte).unwrap();
}
if let Ok(Some(sz_msg)) = decoder.feed(encoded_frame[encoded_frame.len() - 1]) {
assert_eq!(sz_msg, expected_data.len());
assert_eq!(expected_data, &decoder.dest()[0..sz_msg]);
} else {
panic!("decoding call did not yield expected frame");
}
}
}
fn continuous_decoding_owned(
decoder: &mut CobsDecoderOwned,
expected_data: &[u8],
encoded_frame: &[u8],
) {
for _ in 0..10 {
for byte in encoded_frame.iter().take(encoded_frame.len() - 1) {
decoder.feed(*byte).unwrap();
}
if let Ok(Some(sz_msg)) = decoder.feed(encoded_frame[encoded_frame.len() - 1]) {
assert_eq!(sz_msg, expected_data.len());
assert_eq!(expected_data, &decoder.dest()[0..sz_msg]);
} else {
panic!("decoding call did not yield expected frame");
}
}
}
#[test]
fn stream_continuously() {
let mut dest: [u8; 16] = [0; 16];
let data = b"hello world";
let mut encoded_data: [u8; 16] = [0; 16];
let mut encoded_len = encode(data, &mut encoded_data);
encoded_data[encoded_len] = 0x00;
encoded_len += 1;
let mut decoder = CobsDecoder::new(&mut dest);
continuous_decoding(&mut decoder, data, &encoded_data[0..encoded_len]);
}
#[test]
fn stream_continuously_owned() {
let data = b"hello world";
let mut encoded_data: [u8; 16] = [0; 16];
let mut encoded_len = encode(data, &mut encoded_data);
encoded_data[encoded_len] = 0x00;
encoded_len += 1;
let mut decoder = CobsDecoderOwned::new(32);
continuous_decoding_owned(&mut decoder, data, &encoded_data[0..encoded_len]);
}
#[test]
fn stream_continuously_heapless() {
let data = b"hello world";
let mut encoded_data: [u8; 16] = [0; 16];
let mut encoded_len = encode(data, &mut encoded_data);
encoded_data[encoded_len] = 0x00;
encoded_len += 1;
let mut decoder = CobsDecoderHeapless::new();
continuous_decoding_heapless(&mut decoder, data, &encoded_data[0..encoded_len]);
}
#[test]
fn stream_continuously_2() {
let mut dest: [u8; 16] = [0; 16];
let data = b"hello world";
let mut encoded_data: [u8; 16] = [0; 16];
let mut encoded_len = encode(data, &mut encoded_data[1..]);
encoded_data[0] = 0x00;
encoded_data[encoded_len + 1] = 0x00;
encoded_len += 2;
let mut decoder = CobsDecoder::new(&mut dest);
continuous_decoding(&mut decoder, data, &encoded_data[0..encoded_len]);
}
#[test]
fn stream_continuously_2_owned() {
let data = b"hello world";
let mut encoded_data: [u8; 16] = [0; 16];
let mut encoded_len = encode(data, &mut encoded_data[1..]);
encoded_data[0] = 0x00;
encoded_data[encoded_len + 1] = 0x00;
encoded_len += 2;
let mut decoder = CobsDecoderOwned::new(32);
continuous_decoding_owned(&mut decoder, data, &encoded_data[0..encoded_len]);
}
#[test]
fn test_owned_decoder_push_function() {
let data = b"hello world";
let encoded_data = encode_vec_including_sentinels(data);
let mut decoder = CobsDecoderOwned::new(32);
let report = decoder.push(&encoded_data).unwrap().unwrap();
assert_eq!(report.parsed_size(), encoded_data.len());
assert_eq!(report.frame_size(), data.len());
assert_eq!(&decoder.dest()[0..report.frame_size()], data);
assert_eq!(&decoder.dest_mut()[0..report.frame_size()], data);
}
#[test]
fn test_decoder_push_function() {
let mut dest_buf: [u8; 32] = [0; 32];
let data = b"hello world";
let encoded_data = encode_vec_including_sentinels(data);
let mut decoder = CobsDecoder::new(&mut dest_buf);
let report = decoder.push(&encoded_data).unwrap().unwrap();
assert_eq!(report.parsed_size(), encoded_data.len());
assert_eq!(report.frame_size(), data.len());
assert_eq!(&decoder.dest()[0..report.frame_size()], data);
assert_eq!(&decoder.dest_mut()[0..report.frame_size()], data);
}
#[test]
fn test_decoder_heapless_push_function() {
let data = b"hello world";
let encoded_data = encode_vec_including_sentinels(data);
let mut decoder = CobsDecoderHeapless::<32>::new();
let report = decoder.push(&encoded_data).unwrap().unwrap();
assert_eq!(report.parsed_size(), encoded_data.len());
assert_eq!(report.frame_size(), data.len());
assert_eq!(&decoder.dest()[0..report.frame_size()], data);
assert_eq!(&decoder.dest_mut()[0..report.frame_size()], data);
}
}