use super::*;
#[derive(Clone)]
pub struct Encoder {
header_written: bool,
}
pub struct EncodeTotals {
pub read: usize,
pub written: usize,
}
impl Encoder {
pub fn new() -> Self {
Encoder {
header_written: false,
}
}
pub fn encode(&mut self, input: &[u8], output: &mut [u8]) -> Result<EncodeTotals> {
let mut out_byte = 0;
if !self.header_written {
if output.len() < 1 {
return Err(Error::NoOutputSpaceForHeader);
}
output[out_byte] = END;
out_byte = 1;
self.header_written = true;
}
let mut in_byte = 0;
while in_byte < input.len() {
match input[in_byte] {
ESC => {
if (output.len() - out_byte) < 2 {
break;
}
output[out_byte] = ESC;
output[out_byte + 1] = ESC_ESC;
out_byte += 2;
}
END => {
if (output.len() - out_byte) < 2 {
break;
}
output[out_byte] = ESC;
output[out_byte + 1] = ESC_END;
out_byte += 2;
}
_ => {
if (output.len() - out_byte) < 1 {
break;
}
output[out_byte] = input[in_byte];
out_byte += 1;
}
}
in_byte += 1;
}
Ok(EncodeTotals {
read: in_byte,
written: out_byte,
})
}
pub fn finish(self, output: &mut [u8]) -> Result<EncodeTotals> {
if output.len() < 1 {
return Err(Error::NoOutputSpaceForEndByte);
}
output[0] = END;
Ok(EncodeTotals {
read: 0,
written: 1,
})
}
}
impl core::ops::AddAssign for EncodeTotals {
fn add_assign(&mut self, other: EncodeTotals) {
*self = EncodeTotals {
read: self.read + other.read,
written: self.written + other.written,
};
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_encode() {
const EXPECTED: [u8; 2] = [0xc0, 0xc0];
let mut output: [u8; 32] = [0; 32];
let mut slip = Encoder::new();
let mut totals = slip.encode(&[0; 0], &mut output).unwrap();
assert_eq!(0, totals.read);
assert_eq!(1, totals.written);
totals += slip.finish(&mut output[totals.written..]).unwrap();
assert_eq!(0, totals.read);
assert_eq!(2, totals.written);
assert_eq!(&EXPECTED, &output[..totals.written]);
}
#[test]
fn encode_esc_esc_sequence() {
const INPUT: [u8; 3] = [0x01, ESC, 0x03];
const EXPECTED: [u8; 6] = [0xc0, 0x01, ESC, ESC_ESC, 0x03, 0xc0];
let mut output: [u8; 32] = [0; 32];
let mut slip = Encoder::new();
let mut totals = slip.encode(&INPUT, &mut output).unwrap();
assert_eq!(2 + INPUT.len(), totals.written);
totals += slip.finish(&mut output[totals.written..]).unwrap();
assert_eq!(INPUT.len(), totals.read);
assert_eq!(3 + INPUT.len(), totals.written);
assert_eq!(&EXPECTED, &output[..totals.written]);
}
#[test]
fn encode_end_esc_sequence() {
const INPUT: [u8; 3] = [0x01, END, 0x03];
const EXPECTED: [u8; 6] = [0xc0, 0x01, ESC, ESC_END, 0x03, 0xc0];
let mut output: [u8; 32] = [0; 32];
let mut slip = Encoder::new();
let mut totals = slip.encode(&INPUT, &mut output).unwrap();
assert_eq!(2 + INPUT.len(), totals.written);
totals += slip.finish(&mut output[totals.written..]).unwrap();
assert_eq!(INPUT.len(), totals.read);
assert_eq!(3 + INPUT.len(), totals.written);
assert_eq!(&EXPECTED, &output[..totals.written]);
}
#[test]
fn multi_part_encode() {
const INPUT_1: [u8; 4] = [0x01, 0x02, 0x03, ESC];
const INPUT_2: [u8; 4] = [0x05, END, 0x07, 0x08];
const INPUT_3: [u8; 4] = [0x09, 0x0a, ESC, 0x0c];
const EXPECTED: &[u8] = &[
0xc0, 0x01, 0x02, 0x03, ESC, ESC_ESC, 0x05, ESC, ESC_END, 0x07, 0x08, 0x09, 0x0a, ESC,
ESC_ESC, 0x0c, 0xc0,
];
let mut output: [u8; 32] = [0; 32];
let mut slip = Encoder::new();
let mut final_totals = EncodeTotals {
read: 0,
written: 0,
};
let totals = slip.encode(&INPUT_1, &mut output).unwrap();
assert_eq!(INPUT_1.len(), totals.read);
assert_eq!(1 + INPUT_1.len() + 1, totals.written);
final_totals += totals;
let totals = slip
.encode(&INPUT_2, &mut output[final_totals.written..])
.unwrap();
assert_eq!(INPUT_2.len(), totals.read);
assert_eq!(INPUT_2.len() + 1, totals.written);
final_totals += totals;
let totals = slip
.encode(&INPUT_3, &mut output[final_totals.written..])
.unwrap();
assert_eq!(INPUT_3.len(), totals.read);
assert_eq!(INPUT_3.len() + 1, totals.written);
final_totals += totals;
let totals = slip.finish(&mut output[final_totals.written..]).unwrap();
assert_eq!(0, totals.read);
assert_eq!(1, totals.written);
final_totals += totals;
assert_eq!(EXPECTED, &output[..final_totals.written]);
}
}