#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("out of bounds error during encoding")]
pub struct DestBufTooSmallError;
#[derive(Clone, Debug)]
pub struct EncoderState {
code_idx: usize,
num_bt_sent: u8,
offset_idx: u8,
}
pub enum PushResult {
AddSingle(u8),
ModifyFromStartAndSkip((usize, u8)),
ModifyFromStartAndPushAndSkip((usize, u8, u8)),
}
impl Default for EncoderState {
fn default() -> Self {
Self {
code_idx: 0,
num_bt_sent: 1,
offset_idx: 1,
}
}
}
impl EncoderState {
pub fn push(&mut self, data: u8) -> PushResult {
if data == 0 {
let ret = PushResult::ModifyFromStartAndSkip((self.code_idx, self.num_bt_sent));
self.code_idx += usize::from(self.offset_idx);
self.num_bt_sent = 1;
self.offset_idx = 1;
ret
} else {
self.num_bt_sent += 1;
self.offset_idx += 1;
if 0xFF == self.num_bt_sent {
let ret = PushResult::ModifyFromStartAndPushAndSkip((
self.code_idx,
self.num_bt_sent,
data,
));
self.num_bt_sent = 1;
self.code_idx += usize::from(self.offset_idx);
self.offset_idx = 1;
ret
} else {
PushResult::AddSingle(data)
}
}
}
pub fn finalize(self) -> (usize, u8) {
(self.code_idx, self.num_bt_sent)
}
}
#[derive(Debug)]
pub struct CobsEncoder<'a> {
dest: &'a mut [u8],
dest_idx: usize,
state: EncoderState,
might_be_done: bool,
}
impl<'a> CobsEncoder<'a> {
pub fn new(out_buf: &'a mut [u8]) -> CobsEncoder<'a> {
CobsEncoder {
dest: out_buf,
dest_idx: 1,
state: EncoderState::default(),
might_be_done: false,
}
}
pub fn push(&mut self, data: &[u8]) -> Result<(), DestBufTooSmallError> {
if self.might_be_done {
self.dest_idx += 1;
self.might_be_done = false;
}
for (slice_idx, val) in data.iter().enumerate() {
use PushResult::*;
match self.state.push(*val) {
AddSingle(y) => {
*self
.dest
.get_mut(self.dest_idx)
.ok_or(DestBufTooSmallError)? = y;
}
ModifyFromStartAndSkip((idx, mval)) => {
*self.dest.get_mut(idx).ok_or(DestBufTooSmallError)? = mval;
}
ModifyFromStartAndPushAndSkip((idx, mval, nval1)) => {
*self.dest.get_mut(idx).ok_or(DestBufTooSmallError)? = mval;
*self
.dest
.get_mut(self.dest_idx)
.ok_or(DestBufTooSmallError)? = nval1;
if slice_idx == data.len() - 1 {
self.might_be_done = true;
} else {
self.dest_idx += 1;
}
}
}
self.dest_idx += 1;
}
Ok(())
}
pub fn finalize(self) -> usize {
let (idx, mval) = if self.dest_idx == 0 {
(0, 0x01)
} else {
self.state.finalize()
};
if let Some(i) = self.dest.get_mut(idx) {
*i = mval;
}
self.dest_idx
}
}
pub fn encode(source: &[u8], dest: &mut [u8]) -> usize {
let mut enc = CobsEncoder::new(dest);
enc.push(source).unwrap();
enc.finalize()
}
pub fn encode_including_sentinels(source: &[u8], dest: &mut [u8]) -> usize {
if dest.len() < 2 {
panic!("destination buffer too small");
}
dest[0] = 0;
let mut enc = CobsEncoder::new(&mut dest[1..]);
enc.push(source).unwrap();
let encoded_len = enc.finalize();
dest[encoded_len + 1] = 0;
encoded_len + 2
}
pub fn try_encode(source: &[u8], dest: &mut [u8]) -> Result<usize, DestBufTooSmallError> {
let mut enc = CobsEncoder::new(dest);
enc.push(source)?;
Ok(enc.finalize())
}
pub fn try_encode_including_sentinels(
source: &[u8],
dest: &mut [u8],
) -> Result<usize, DestBufTooSmallError> {
if dest.len() < 2 {
return Err(DestBufTooSmallError);
}
dest[0] = 0;
let mut enc = CobsEncoder::new(&mut dest[1..]);
enc.push(source)?;
let encoded_len = enc.finalize();
dest[encoded_len + 1] = 0;
Ok(encoded_len + 2)
}
pub fn encode_with_sentinel(source: &[u8], dest: &mut [u8], sentinel: u8) -> usize {
let encoded_size = encode(source, dest);
for x in &mut dest[..encoded_size] {
*x ^= sentinel;
}
encoded_size
}
#[cfg(feature = "alloc")]
pub fn encode_vec(source: &[u8]) -> alloc::vec::Vec<u8> {
let mut encoded = alloc::vec![0; crate::max_encoding_length(source.len())];
let encoded_len = encode(source, &mut encoded[..]);
encoded.truncate(encoded_len);
encoded
}
#[cfg(feature = "alloc")]
pub fn encode_vec_including_sentinels(source: &[u8]) -> alloc::vec::Vec<u8> {
let mut encoded = alloc::vec![0; crate::max_encoding_length(source.len()) + 2];
let encoded_len = encode_including_sentinels(source, &mut encoded);
encoded.truncate(encoded_len + 2);
encoded
}
#[cfg(feature = "alloc")]
pub fn encode_vec_with_sentinel(source: &[u8], sentinel: u8) -> alloc::vec::Vec<u8> {
let mut encoded = alloc::vec![0; crate::max_encoding_length(source.len())];
let encoded_len = encode_with_sentinel(source, &mut encoded[..], sentinel);
encoded.truncate(encoded_len);
encoded
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
decode_vec,
tests::{test_decode_in_place, test_encode_decode_free_functions},
};
#[test]
fn test_encode_0() {
let mut output = [0xFFu8; 16];
let used = encode(&[], &mut output);
assert_eq!(used, 1);
assert_eq!(output[0], 0x01);
}
fn test_pair(source: &[u8], encoded: &[u8]) {
test_encode_decode_free_functions(source, encoded);
test_decode_in_place(source, encoded);
}
#[test]
fn test_encode_1() {
test_pair(&[10, 11, 0, 12], &[3, 10, 11, 2, 12])
}
#[test]
fn test_encode_empty() {
test_pair(&[], &[1])
}
#[test]
fn test_encode_2() {
test_pair(&[0, 0, 1, 0], &[1, 1, 2, 1, 1])
}
#[test]
fn test_encode_3() {
test_pair(&[255, 0], &[2, 255, 1])
}
#[test]
fn test_encode_4() {
test_pair(&[1], &[2, 1])
}
#[test]
fn encode_target_buf_too_small() {
let source = &[10, 11, 0, 12];
let expected = &[3, 10, 11, 2, 12];
for len in 0..expected.len() {
let mut dest = alloc::vec![0; len];
matches!(
try_encode(source, &mut dest).unwrap_err(),
DestBufTooSmallError
);
}
}
#[test]
fn try_encode_with_sentinels() {
let source = &[10, 11, 0, 12];
let expected = &[0, 3, 10, 11, 2, 12, 0];
let mut dest = alloc::vec![0; expected.len()];
let encoded_len = try_encode_including_sentinels(source, &mut dest).unwrap();
assert_eq!(encoded_len, expected.len());
assert_eq!(dest[0], 0);
assert_eq!(dest[expected.len() - 1], 0);
assert_eq!(decode_vec(&dest).unwrap(), source);
}
#[test]
fn test_encoding_including_sentinels() {
let data = [1, 2, 3];
let encoded = encode_vec_including_sentinels(&data);
assert_eq!(*encoded.first().unwrap(), 0);
assert_eq!(*encoded.last().unwrap(), 0);
let data_decoded = decode_vec(&encoded).unwrap();
assert_eq!(data_decoded, data);
let data_decoded = decode_vec(&encoded[1..]).unwrap();
assert_eq!(data_decoded, data);
let data_decoded = decode_vec(&encoded[1..encoded.len() - 1]).unwrap();
assert_eq!(data_decoded, data);
}
#[test]
#[should_panic]
fn encode_target_buf_too_small_panicking() {
let source = &[10, 11, 0, 12];
let expected = &[3, 10, 11, 2, 12];
encode(source, &mut alloc::vec![0; expected.len() - 1]);
}
}