use crate::Constants;
use crate::FrameType;
use crate::checksum::{GOOD_FCS16, INIT_FCS16, fcs16_byte};
use crate::Error;
enum SlipmuxState {
Idle,
UnkownFrameType,
Diagnostic,
DiagnosticEscape,
Configuration,
ConfigurationEscape,
Ip,
IpEscape,
}
use SlipmuxState::{
Configuration, ConfigurationEscape, Diagnostic, DiagnosticEscape, Idle, Ip, IpEscape,
UnkownFrameType,
};
pub trait FrameHandler {
fn begin_frame(&mut self, frame_type: FrameType);
fn write_byte(&mut self, byte: u8);
fn end_frame(&mut self, error: Option<Error>);
}
#[derive(Debug)]
pub enum DecodeStatus {
Incomplete,
FrameCompleteDiagnostic,
FrameCompleteConfiguration,
FrameCompleteIp,
}
pub struct Decoder {
state: SlipmuxState,
fcs: u16,
}
impl Default for Decoder {
fn default() -> Self {
Self::new()
}
}
impl Decoder {
#[must_use]
pub const fn new() -> Self {
Self {
state: Idle,
fcs: 0,
}
}
#[allow(clippy::match_same_arms)]
#[allow(clippy::too_many_lines)]
pub fn decode<H: FrameHandler>(
&mut self,
byte: u8,
handler: &mut H,
) -> Result<DecodeStatus, Error> {
self.state = {
match (&self.state, byte) {
(Idle, Constants::END) => Idle,
(Idle, Constants::DIAGNOSTIC) => {
handler.begin_frame(FrameType::Diagnostic);
Diagnostic
}
(Idle, Constants::CONFIGURATION) => {
handler.begin_frame(FrameType::Configuration);
self.fcs = fcs16_byte(INIT_FCS16, Constants::CONFIGURATION);
Configuration
}
(
Idle,
Constants::IP4_FROM..Constants::IP4_TO | Constants::IP6_FROM..Constants::IP6_TO,
) => {
handler.begin_frame(FrameType::Ip);
handler.write_byte(byte);
Ip
}
(Idle, _) => {
self.state = UnkownFrameType;
handler.end_frame(Some(Error::BadFrameType(byte)));
return Err(Error::BadFrameType(byte));
}
(UnkownFrameType, Constants::END) => Idle,
(UnkownFrameType, _) => UnkownFrameType,
(Diagnostic, Constants::ESC) => DiagnosticEscape,
(Diagnostic, Constants::END) => {
handler.end_frame(None);
self.state = Idle;
return Ok(DecodeStatus::FrameCompleteDiagnostic);
}
(Diagnostic, _) => {
handler.write_byte(byte);
Diagnostic
}
(DiagnosticEscape, Constants::ESC_END) => {
handler.write_byte(Constants::END);
Diagnostic
}
(DiagnosticEscape, Constants::ESC_ESC) => {
handler.write_byte(Constants::ESC);
Diagnostic
}
(DiagnosticEscape, Constants::END) => {
self.state = Idle;
handler.end_frame(Some(Error::Abort));
return Err(Error::Abort);
}
(DiagnosticEscape, _) => {
handler.write_byte(byte);
Diagnostic
}
(Configuration, Constants::ESC) => ConfigurationEscape,
(Configuration, Constants::END) => {
self.state = Idle;
if self.fcs == GOOD_FCS16 {
handler.end_frame(None);
return Ok(DecodeStatus::FrameCompleteConfiguration);
}
handler.end_frame(Some(Error::BadFCS));
return Err(Error::BadFCS);
}
(Configuration, _) => {
handler.write_byte(byte);
self.fcs = fcs16_byte(self.fcs, byte);
Configuration
}
(ConfigurationEscape, Constants::ESC_END) => {
handler.write_byte(Constants::END);
self.fcs = fcs16_byte(self.fcs, Constants::END);
Configuration
}
(ConfigurationEscape, Constants::ESC_ESC) => {
handler.write_byte(Constants::ESC);
self.fcs = fcs16_byte(self.fcs, Constants::ESC);
Configuration
}
(ConfigurationEscape, Constants::END) => {
self.state = Idle;
handler.end_frame(Some(Error::Abort));
return Err(Error::Abort);
}
(ConfigurationEscape, _) => {
handler.write_byte(byte);
self.fcs = fcs16_byte(self.fcs, byte);
Configuration
}
(Ip, Constants::ESC) => IpEscape,
(Ip, Constants::END) => {
self.state = Idle;
handler.end_frame(None);
return Ok(DecodeStatus::FrameCompleteIp);
}
(Ip, _) => {
handler.write_byte(byte);
Ip
}
(IpEscape, Constants::ESC_END) => {
handler.write_byte(Constants::END);
Ip
}
(IpEscape, Constants::ESC_ESC) => {
handler.write_byte(Constants::ESC);
Ip
}
(IpEscape, Constants::END) => {
self.state = Idle;
handler.end_frame(Some(Error::Abort));
return Err(Error::Abort);
}
(IpEscape, _) => {
handler.write_byte(byte);
Ip
}
}
};
Ok(DecodeStatus::Incomplete)
}
}
#[cfg(test)]
mod tests {
use crate::{OwnedLatestFrame, Slipmux, encode_buffered};
use super::*;
#[test]
fn decode_all_frametypes() {
const HELLO_WORLD: &str = "Hello World!";
const HELLO_CONFIG: [u8; 14] = [
0x64, 0x45, 0x13, 0xFD, 0xD0, 0xE2, 0x4D, 0xAC, 0xFF, 0x48, 0x65, 0x6C, 0x6C, 0x6F,
];
const HELLO_PACKET: [u8; 6] = [0x60, 0x0d, 0xda, 0x01, 0xfe, 0x80];
let input_diagnostic = Slipmux::Diagnostic(HELLO_WORLD.to_owned());
let mut buffer = encode_buffered(input_diagnostic);
let input_configuration = Slipmux::Configuration(HELLO_CONFIG.to_vec());
buffer.append(&mut encode_buffered(input_configuration));
let input_packet = Slipmux::Packet(HELLO_PACKET.to_vec());
buffer.append(&mut encode_buffered(input_packet));
let expected: [DecodeStatus; 42] = [
DecodeStatus::Incomplete, DecodeStatus::Incomplete, DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::FrameCompleteDiagnostic,
DecodeStatus::Incomplete, DecodeStatus::Incomplete, DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete, DecodeStatus::Incomplete, DecodeStatus::FrameCompleteConfiguration,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::Incomplete,
DecodeStatus::FrameCompleteIp,
];
let mut slipmux = Decoder::new();
let mut handler = OwnedLatestFrame::new();
for (index, byte) in buffer.iter().enumerate() {
let result: DecodeStatus = slipmux.decode(*byte, &mut handler).unwrap();
match expected[index] {
DecodeStatus::Incomplete => {
assert!(matches!(result, DecodeStatus::Incomplete));
}
DecodeStatus::FrameCompleteDiagnostic => {
assert!(matches!(result, DecodeStatus::FrameCompleteDiagnostic));
}
DecodeStatus::FrameCompleteConfiguration => {
assert!(matches!(result, DecodeStatus::FrameCompleteConfiguration));
}
DecodeStatus::FrameCompleteIp => {
assert!(matches!(result, DecodeStatus::FrameCompleteIp));
}
}
}
}
}