#[cfg(feature = "use_std")]
use crate::max_encoding_length;
#[derive(Debug)]
pub struct CobsEncoder<'a> {
dest: &'a mut [u8],
dest_idx: usize,
state: EncoderState,
}
#[derive(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)
}
}
impl<'a> CobsEncoder<'a> {
pub fn new(out_buf: &'a mut [u8]) -> CobsEncoder<'a> {
CobsEncoder {
dest: out_buf,
dest_idx: 1,
state: EncoderState::default(),
}
}
pub fn push(&mut self, data: &[u8]) -> Result<(), ()> {
for x in data {
use PushResult::*;
match self.state.push(*x) {
AddSingle(y) => {
*self.dest.get_mut(self.dest_idx)
.ok_or_else(|| ())? = y;
}
ModifyFromStartAndSkip((idx, mval)) => {
*self.dest.get_mut(idx)
.ok_or_else(|| ())? = mval;
}
ModifyFromStartAndPushAndSkip((idx, mval, nval1)) => {
*self.dest.get_mut(idx)
.ok_or_else(|| ())? = mval;
*self.dest.get_mut(self.dest_idx)
.ok_or_else(|| ())? = nval1;
self.dest_idx += 1;
}
}
self.dest_idx += 1;
}
Ok(())
}
pub fn finalize(self) -> Result<usize, ()> {
if self.dest_idx == 1 {
return Ok(0);
}
let (idx, mval) = self.state.finalize();
if let Some(i) = self.dest.get_mut(idx) {
*i = mval;
}
return Ok(self.dest_idx);
}
}
pub fn encode(source: &[u8], dest: &mut[u8]) -> usize {
let mut enc = CobsEncoder::new(dest);
enc.push(source).unwrap();
enc.finalize().unwrap()
}
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;
}
return encoded_size;
}
#[cfg(feature = "use_std")]
pub fn encode_vec(source: &[u8]) -> Vec<u8> {
let mut encoded = vec![0; max_encoding_length(source.len())];
let encoded_len = encode(source, &mut encoded[..]);
encoded.truncate(encoded_len);
return encoded;
}
#[cfg(feature = "use_std")]
pub fn encode_vec_with_sentinel(source: &[u8], sentinel: u8) -> Vec<u8> {
let mut encoded = vec![0; max_encoding_length(source.len())];
let encoded_len = encode_with_sentinel(source, &mut encoded[..], sentinel);
encoded.truncate(encoded_len);
return encoded;
}