use serial_line_ip::EncodeTotals;
use serial_line_ip::Encoder;
use serial_line_ip::Error as SlipError;
const END: u8 = 0xC0;
const DIAGNOSTIC: u8 = 0x0A;
const CONFIGURATION: u8 = 0xA9;
const IP4_FROM: u8 = 0x45;
const IP4_TO: u8 = 0x4F;
const IP6_FROM: u8 = 0x60;
const IP6_TO: u8 = 0x6F;
#[derive(Debug)]
pub enum Slipmux {
Diagnostic(String),
Configuration(Vec<u8>),
Packet(Vec<u8>),
}
#[derive(Debug)]
pub enum Error {
NotEnoughSpace,
BadFraming,
BadFrameType,
}
#[must_use]
pub fn encode_diagnostic(text: &str) -> ([u8; 256], usize) {
encode(Slipmux::Diagnostic(text.to_owned()))
}
#[must_use]
pub fn encode_configuration(packet: Vec<u8>) -> ([u8; 256], usize) {
encode(Slipmux::Configuration(packet))
}
#[must_use]
pub fn encode(input: Slipmux) -> ([u8; 256], usize) {
let mut buffer = [0; 256];
let mut slip = Encoder::new();
let mut totals = EncodeTotals {
read: 0,
written: 0,
};
match input {
Slipmux::Diagnostic(s) => {
totals += slip.encode(&[DIAGNOSTIC], &mut buffer).unwrap();
totals += slip
.encode(s.as_bytes(), &mut buffer[totals.written..])
.unwrap();
}
Slipmux::Configuration(conf) => {
totals += slip.encode(&[CONFIGURATION], &mut buffer).unwrap();
totals += slip.encode(&conf, &mut buffer[totals.written..]).unwrap();
}
Slipmux::Packet(packet) => {
totals += slip.encode(&packet, &mut buffer[totals.written..]).unwrap();
}
}
totals += slip.finish(&mut buffer[totals.written..]).unwrap();
(buffer, totals.written)
}
enum DecoderState {
Fin(Result<Slipmux, Error>, usize),
DecodeError(Error),
Skip(),
Incomplete(),
}
pub struct Decoder {
slip: serial_line_ip::Decoder,
index: usize,
buffer: [u8; 10240],
}
impl Default for Decoder {
fn default() -> Self {
Self::new()
}
}
impl Decoder {
#[must_use]
pub fn new() -> Self {
let mut decoder = serial_line_ip::Decoder::new();
let mut buffer = [0; 10240];
decoder.decode(&[END], &mut buffer).unwrap();
Self {
slip: decoder,
index: 0,
buffer,
}
}
fn reset(&mut self) {
self.slip = serial_line_ip::Decoder::new();
self.slip.decode(&[END], &mut self.buffer).unwrap();
self.index = 0;
}
pub fn decode(&mut self, input: &[u8]) -> Vec<Result<Slipmux, Error>> {
let mut result_vec = Vec::new();
let mut offset = 0;
while offset < input.len() {
let used_bytes = {
match self.decode_partial(&input[offset..]) {
DecoderState::Fin(data, bytes_consumed) => {
result_vec.push(data);
bytes_consumed
}
DecoderState::DecodeError(err) => {
result_vec.push(Err(err));
break;
}
DecoderState::Incomplete() => input.len(),
DecoderState::Skip() => 1,
}
};
offset += used_bytes;
}
result_vec
}
fn decode_partial(&mut self, input: &[u8]) -> DecoderState {
let partial_result = self.slip.decode(input, &mut self.buffer[self.index..]);
match partial_result {
Err(SlipError::NoOutputSpaceForHeader | SlipError::NoOutputSpaceForEndByte) => {
return DecoderState::DecodeError(Error::NotEnoughSpace);
}
Err(SlipError::BadHeaderDecode | SlipError::BadEscapeSequenceDecode) => {
return DecoderState::DecodeError(Error::BadFraming);
}
_ => (),
}
let (used_bytes_from_input, out, end) = partial_result.unwrap();
self.index += out.len();
if end && self.index == 0 {
return DecoderState::Skip();
}
if end {
let retval = {
match self.buffer[0] {
DIAGNOSTIC => {
let s = String::from_utf8_lossy(&self.buffer[1..self.index]).to_string();
Ok(Slipmux::Diagnostic(s))
}
CONFIGURATION => {
Ok(Slipmux::Configuration(self.buffer[1..self.index].to_vec()))
}
IP4_FROM..IP4_TO | IP6_FROM..IP6_TO => {
Ok(Slipmux::Packet(self.buffer[0..self.index].to_vec()))
}
_ => Err(Error::BadFrameType),
}
};
self.reset();
DecoderState::Fin(retval, used_bytes_from_input)
} else {
assert!(used_bytes_from_input == input.len());
DecoderState::Incomplete()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use coap_lite::Packet;
#[test]
fn encode_simple_diagnostic() {
let (result, length) = encode_diagnostic("Hello World!");
assert_eq!(result[..length], *b"\xc0\x0aHello World!\xc0");
let (result, length) = encode_diagnostic("Yes, I would like one \x0a please.");
assert_eq!(
result[..length],
*b"\xc0\x0aYes, I would like one \x0a please.\xc0"
);
}
#[test]
fn encode_empty_diagnostic() {
let (result, length) = encode_diagnostic("");
assert_eq!(result[..length], *b"\xc0\x0a\xc0");
}
#[test]
fn encode_simple_configuration() {
let (result, length) = encode_configuration(Packet::new().to_bytes().unwrap());
assert_eq!(
result[..length],
[END, CONFIGURATION, 0x40, 0x01, 0x00, 0x00, END]
);
}
#[test]
fn encode_direct() {
let input = Slipmux::Diagnostic("Hello World!".to_owned());
let (result, length) = encode(input);
assert_eq!(result[..length], *b"\xc0\x0aHello World!\xc0");
let input = Slipmux::Configuration(Packet::new().to_bytes().unwrap());
let (result, length) = encode(input);
assert_eq!(
result[..length],
[END, CONFIGURATION, 0x40, 0x01, 0x00, 0x00, END]
);
}
#[test]
fn decode_simple() {
const SLIPMUX_ENCODED: [u8; 15] = [
END, DIAGNOSTIC, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64,
0x21, END,
];
let mut slipmux = Decoder::new();
let mut results = slipmux.decode(&SLIPMUX_ENCODED);
assert_eq!(results.len(), 1);
let frame = results.pop().unwrap();
assert!(frame.is_ok());
match frame.unwrap() {
Slipmux::Diagnostic(s) => assert_eq!(s, "Hello World!"),
_ => assert!(false),
}
}
#[test]
fn decode_simple_no_leading_deliminator() {
const SLIPMUX_ENCODED: [u8; 14] = [
DIAGNOSTIC, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21, END,
];
let mut slipmux = Decoder::new();
let mut results = slipmux.decode(&SLIPMUX_ENCODED);
assert_eq!(results.len(), 1);
let frame = results.pop().unwrap();
assert!(frame.is_ok());
match frame.unwrap() {
Slipmux::Diagnostic(s) => assert_eq!(s, "Hello World!"),
_ => assert!(false),
}
}
#[test]
fn decode_ignore_empty_frames() {
const SLIPMUX_ENCODED: [u8; 19] = [
END, END, END, DIAGNOSTIC, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c,
0x64, 0x21, END, END, END,
];
let mut slipmux = Decoder::new();
let mut results = slipmux.decode(&SLIPMUX_ENCODED);
assert_eq!(results.len(), 1);
let frame = results.pop().unwrap();
assert!(frame.is_ok());
match frame.unwrap() {
Slipmux::Diagnostic(s) => assert_eq!(s, "Hello World!"),
_ => assert!(false),
}
}
#[test]
fn decode_only_one_end_byte_frames() {
const SLIPMUX_ENCODED: [u8; 43] = [
END, DIAGNOSTIC, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64,
0x21, END, DIAGNOSTIC, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c,
0x64, 0x21, END, DIAGNOSTIC, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72,
0x6c, 0x64, 0x21, END,
];
let mut slipmux = Decoder::new();
for input_slice in SLIPMUX_ENCODED.chunks(3) {
for slipframe in slipmux.decode(&input_slice) {
match slipframe {
Ok(Slipmux::Diagnostic(s)) => {
assert_eq!(s, "Hello World!")
}
Ok(Slipmux::Configuration(_conf)) => {
}
Ok(Slipmux::Packet(_packet)) => {
}
_ => assert!(false),
}
}
}
}
#[test]
fn decode_unkown_frametype() {
const SLIPMUX_ENCODED: [u8; 15] = [
END, 0x50, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21, END,
];
let mut slipmux = Decoder::new();
let mut results = slipmux.decode(&SLIPMUX_ENCODED);
assert_eq!(results.len(), 1);
let frame = results.pop().unwrap();
assert!(frame.is_err());
match frame {
Err(Error::BadFrameType) => assert!(true),
_ => assert!(false),
}
}
#[test]
fn decode_stream_with_unkown_frametype_inbetween() {
const SLIPMUX_ENCODED: [u8; 45] = [
END, DIAGNOSTIC, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64,
0x21, END, END, 0x50, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64,
0x21, END, END, DIAGNOSTIC, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c,
0x64, 0x21, END,
];
let mut slipmux = Decoder::new();
let frames = slipmux.decode(&SLIPMUX_ENCODED);
assert_eq!(frames.len(), 3);
assert!(matches!(frames[0], Ok(Slipmux::Diagnostic(_))));
assert!(matches!(frames[1], Err(Error::BadFrameType)));
assert!(matches!(frames[2], Ok(Slipmux::Diagnostic(_))));
}
#[test]
fn decode_stream() {
const SLIPMUX_ENCODED: [u8; 45] = [
END, DIAGNOSTIC, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64,
0x21, END, END, DIAGNOSTIC, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c,
0x64, 0x21, END, END, DIAGNOSTIC, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72,
0x6c, 0x64, 0x21, END,
];
let mut slipmux = Decoder::new();
for input_slice in SLIPMUX_ENCODED.chunks(4) {
for slipframe in slipmux.decode(&input_slice) {
match slipframe {
Ok(Slipmux::Diagnostic(s)) => {
assert_eq!(s, "Hello World!")
}
Ok(Slipmux::Configuration(_conf)) => {
}
Ok(Slipmux::Packet(_packet)) => {
}
_ => assert!(false),
}
}
}
}
}