#![crate_name = "wake_rs"]
#[cfg(test)]
extern crate rand;
#[cfg(test)]
use rand::Rng;
use std::fmt;
const FEND: u8 = 0xC0;
const FESC: u8 = 0xDB;
const TFEND: u8 = 0xDC;
const TFESC: u8 = 0xDD;
const ADDR_MASK: u8 = 0x80;
const CRC_INIT: u8 = 0xDE;
const PACKET_MIN_LEN: usize = 4;
pub const DATA_MAX_LEN: usize = 0xff;
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum WakeError {
TooShortPacket,
CannotFindStart,
DestuffingFailed,
WrongPacketLength,
WrongPacketCrc,
WrongAddrRange,
WrongCmdRange,
}
impl std::error::Error for WakeError {
fn description(&self) -> &str {
match *self {
WakeError::TooShortPacket => "Too short packet",
WakeError::CannotFindStart => "Can't find a start of the packet",
WakeError::DestuffingFailed => "De-stuffing failed",
WakeError::WrongPacketLength => "Wrong packet length",
WakeError::WrongPacketCrc => "Wrong packet CRC",
WakeError::WrongAddrRange => "Address is out of range [0 - 127]",
WakeError::WrongCmdRange => "Command is out of range [0 - 127]",
}
}
}
impl fmt::Display for WakeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Wake error: {:?}", self)
}
}
#[derive(Default)]
pub struct Packet {
pub address: Option<u8>,
pub command: u8,
pub data: Option<Vec<u8>>,
}
impl fmt::Display for Packet {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let addr = match self.address {
Some(a) => format!("ADDR: 0x{:02X}", a),
None => "ADDR: ----".to_string(),
};
let cmd = format!("CMD: 0x{:02X}", self.command);
let data = match &self.data {
Some(d) => {
let mut print = format!("DATA: {} bytes\n", d.len());
print.push_str(" 0 1 2 3 4 5 6 7 8 9 a b c d e f");
for (i, item) in d.iter().enumerate() {
if (i == 0) | (i % 16 == 0) {
print.push_str(&format!("\n{:02x}: ", i));
}
print.push_str(&format!("{:02x} ", item));
}
print
}
None => ("DATA: none").to_string(),
};
write!(f, "{}\n{}\n{}\n", addr, cmd, data)
}
}
trait Wake {
fn crc(&self) -> u8;
fn stuff(&self) -> Vec<u8>;
fn dry(&self) -> Result<Vec<u8>, WakeError>;
}
impl Wake for Vec<u8> {
fn crc(&self) -> u8 {
let mut crc: u8 = CRC_INIT;
let mut crc8 = |data| {
let mut b = data;
for _ in 0..8 {
crc = if (b ^ crc) & 1 == 1 {
((crc ^ 0x18) >> 1) | 0x80
} else {
(crc >> 1) & !0x80
};
b >>= 1;
}
};
for n in self {
crc8(*n);
}
crc
}
fn stuff(&self) -> Vec<u8> {
assert!(self.len() >= (PACKET_MIN_LEN - 1)); assert_eq!(self[0], FEND);
let mut stuffed: Vec<u8> = vec![self[0]];
for x in &self[1..] {
match *x {
FESC => {
stuffed.push(FESC);
stuffed.push(TFESC);
}
FEND => {
stuffed.push(FESC);
stuffed.push(TFEND);
}
_ => stuffed.push(*x),
}
}
stuffed
}
fn dry(&self) -> Result<Vec<u8>, WakeError> {
let mut output: Vec<u8> = vec![];
let mut i = 0;
while i < self.len() {
match self[i] {
FESC => {
if i > (self.len() - 2) {
return Err(WakeError::WrongPacketLength);
}
output.push(match self[i + 1] {
TFESC => FESC,
TFEND => FEND,
_ => return Err(WakeError::DestuffingFailed),
});
i += 1;
}
_ => output.push(self[i]),
}
i += 1;
}
Ok(output)
}
}
pub trait Decode {
fn decode(&self) -> Result<Packet, WakeError>;
}
impl Decode for Vec<u8> {
fn decode(&self) -> Result<Packet, WakeError> {
if self.len() < PACKET_MIN_LEN {
return Err(WakeError::TooShortPacket);
}
if self[0] != FEND {
return Err(WakeError::CannotFindStart);
}
let mut destuffed_pkt = self.dry()?;
let mut v_iter = destuffed_pkt.iter().enumerate();
v_iter.next(); let mut decoded = Packet::default();
let (_, d) = v_iter.next().ok_or(WakeError::TooShortPacket)?;
match d {
addr @ ADDR_MASK..=0xff => {
decoded.address = Some(addr & !ADDR_MASK);
let (_, cmd) = v_iter.next().ok_or(WakeError::TooShortPacket)?;
decoded.command = *cmd;
}
cmd => {
decoded.address = None;
decoded.command = *cmd;
}
};
let (i, data_len) = v_iter.next().ok_or(WakeError::TooShortPacket)?;
if (destuffed_pkt.len() - i - 2) != *data_len as usize {
return Err(WakeError::WrongPacketLength);
}
decoded.data = match data_len {
0 => None,
_ => Some(destuffed_pkt[i + 1..destuffed_pkt.len() - 1].to_vec()),
};
let received_crc = destuffed_pkt.remove(destuffed_pkt.len() - 1);
if received_crc != destuffed_pkt.to_vec().crc() {
Err(WakeError::WrongPacketCrc)
} else {
Ok(decoded)
}
}
}
pub trait Encode {
fn encode(&self) -> Result<Vec<u8>, WakeError>;
}
impl Encode for Packet {
fn encode(&self) -> Result<Vec<u8>, WakeError> {
let mut encoded_packet: Vec<u8> = vec![];
encoded_packet.push(FEND);
if let Some(addr) = self.address {
if addr > 0x7f {
return Err(WakeError::WrongAddrRange);
}
encoded_packet.push(addr | ADDR_MASK);
}
if self.command > 0x7f {
return Err(WakeError::WrongCmdRange);
}
encoded_packet.push(self.command);
match &self.data {
Some(d) => {
encoded_packet.push(d.len() as u8);
encoded_packet.extend(d.iter().cloned());
}
None => encoded_packet.push(0),
}
encoded_packet.push(encoded_packet.crc());
Ok(encoded_packet.stuff())
}
}
#[test]
fn crc_test() {
let xs = vec![1, 2, 3, 4, 5];
assert_eq!(xs.crc(), 0xd6);
let xs = vec![0xc0, 0x03, 0x00];
assert_eq!(xs.crc(), 0xeb);
let xs = vec![0xc0, 0x89, 0x03, 0x05, 1, 2, 3, 4, 5];
assert_eq!(xs.crc(), 0x69);
}
#[test]
fn stuff_test() {
let a = vec![FEND, FESC, 1, 2, 3, 4, 5, FEND]; let b = vec![FEND, FESC, TFESC, 1, 2, 3, 4, 5, FESC, TFEND]; assert_eq!(a.stuff(), b);
let a = vec![FEND, 3, 0];
assert_eq!(a.stuff(), a);
let a = vec![];
let result = std::panic::catch_unwind(|| a.stuff());
assert!(result.is_err());
let a = vec![FEND, 3];
let result = std::panic::catch_unwind(|| a.stuff());
assert!(result.is_err());
}
#[test]
fn dry_test() {
let t0 = vec![]; let t1 = vec![0x34]; let t2 = vec![1, 2, 3, 4, 5, FEND]; let t3 = vec![FEND, FESC, TFESC, 1, 2, 3, 4, 5, FESC]; let t4 = vec![FEND, FESC, 1, 2, 3, 4, 5, FESC, TFEND]; let t5 = vec![FEND, FESC, TFESC, 1, 2, 3, 4, 5, FESC, TFEND]; let a5 = vec![FEND, FESC, 1, 2, 3, 4, 5, FEND]; assert_eq!(t0.dry(), Ok(vec![]));
assert_eq!(t1.clone().dry(), Ok(t1));
assert_eq!(t2.clone().dry(), Ok(t2));
assert_eq!(t3.dry(), Err(WakeError::WrongPacketLength));
assert_eq!(t4.dry(), Err(WakeError::DestuffingFailed));
assert_eq!(t5.dry(), Ok(a5));
}
#[test]
fn encode_packet_test() {
let wp = Packet {
address: Some(128),
command: 9,
data: Some(vec![0x12, 0x34]),
};
assert_eq!(wp.encode(), Err(WakeError::WrongAddrRange));
let wp = Packet {
address: None,
command: 128,
data: Some(vec![0x12, 0x34]),
};
assert_eq!(wp.encode(), Err(WakeError::WrongCmdRange));
let wp = Packet {
address: None,
command: 9,
data: Some(vec![0x12, 0x34]),
};
assert_eq!(wp.encode(), Ok(vec![FEND, 0x09, 0x02, 0x12, 0x34, 160]));
let wp = Packet {
address: Some(0x12),
command: 3,
data: Some(vec![0x00, 0xeb]),
};
assert_eq!(
wp.encode(),
Ok(vec![FEND, 0x92, 0x03, 0x02, 0x00, 0xeb, 114])
);
let wp = Packet {
address: Some(0x13),
command: 4,
data: None,
};
assert_eq!(wp.encode(), Ok(vec![FEND, 0x93, 0x04, 0x00, 218]));
let wp = Packet {
address: Some(0x40),
command: 0x40,
data: None,
};
assert_eq!(wp.encode(), Ok(vec![FEND, FESC, TFEND, 0x40, 0x00, 229]));
}
#[test]
fn decode_wo_address_test() {
let command = 0x03u8;
let data = [1, 2, 3, 4, 5];
let n = data.len() as u8;
let crc = [0x6B];
let wrong_crc = [0x6C];
let mut good_packet = vec![FEND, command, n];
good_packet.extend_from_slice(&data);
good_packet.extend_from_slice(&crc);
let decoded = good_packet.decode().unwrap(); assert_eq!(decoded.command, command);
assert_eq!(decoded.data.unwrap(), data);
let bad_packet_too_short = vec![FEND, command, n];
let decoded = bad_packet_too_short.decode();
assert_eq!(decoded.err(), Some(WakeError::TooShortPacket));
let mut bad_packet_wo_start = vec![command, n];
bad_packet_wo_start.extend_from_slice(&data);
bad_packet_wo_start.extend_from_slice(&crc);
let decoded = bad_packet_wo_start.decode();
assert_eq!(decoded.err(), Some(WakeError::CannotFindStart));
let bad_packet_wrong_stuffing = vec![FEND, FESC, FESC, 1, 2, 3, 4, 5, FESC, TFEND]; let decoded = bad_packet_wrong_stuffing.decode();
assert_eq!(decoded.err(), Some(WakeError::DestuffingFailed));
let mut bad_packet_wrong_data_len = vec![FEND, command, n - 1];
bad_packet_wrong_data_len.extend_from_slice(&data);
bad_packet_wrong_data_len.extend_from_slice(&wrong_crc);
let decoded = bad_packet_wrong_data_len.decode();
assert_eq!(decoded.err(), Some(WakeError::WrongPacketLength));
let mut bad_packet_wrong_data_len = vec![FEND, command, n + 1];
bad_packet_wrong_data_len.extend_from_slice(&data);
bad_packet_wrong_data_len.extend_from_slice(&wrong_crc);
let decoded = bad_packet_wrong_data_len.decode();
assert_eq!(decoded.err(), Some(WakeError::WrongPacketLength));
let mut bad_packet_wrong_crc = vec![FEND, command, n];
bad_packet_wrong_crc.extend_from_slice(&data);
bad_packet_wrong_crc.extend_from_slice(&wrong_crc);
let decoded = bad_packet_wrong_crc.decode();
assert_eq!(decoded.err(), Some(WakeError::WrongPacketCrc));
}
#[test]
fn decode_w_address_test() {
let address = 0x09u8;
let command = 0x03u8;
let data = [1, 2, 3, 4, 5];
let n = data.len() as u8;
let crc = [0x69];
let mut good_packet = vec![FEND, address | 0x80u8, command, n];
good_packet.extend_from_slice(&data);
good_packet.extend_from_slice(&crc);
let decoded = good_packet.decode();
assert_eq!(decoded.is_ok(), true);
let decoded = decoded.unwrap();
assert_eq!(decoded.address.unwrap(), address);
assert_eq!(decoded.command, command);
assert_eq!(decoded.data.unwrap(), data);
let good_packet = vec![FEND, FESC, TFEND, 0x40, 0x00, 229];
let decoded = good_packet.decode();
assert_eq!(decoded.is_ok(), true);
let decoded = decoded.unwrap();
assert_eq!(decoded.address.unwrap(), 0x40);
assert_eq!(decoded.command, 0x40);
assert_eq!(decoded.data, None);
}
#[test]
fn random_encode_decode_test() {
let mut rng = rand::thread_rng();
for _ in 0..100_000 {
let address_exists = rng.gen_bool(0.5);
let n = rng.gen_range(0..0x100);
let mut d: Vec<u8> = Vec::new();
for _ in 0..n {
d.push(rng.gen_range(0..0xff));
}
let wp = Packet {
address: if address_exists {
Some(rng.gen_range(0..0x7f))
} else {
None
},
command: rng.gen_range(0..0x7f),
data: if d.len() == 0 { None } else { Some(d.clone()) },
};
let encoded = wp.encode().unwrap();
let decoded = encoded.decode().unwrap();
assert_eq!(decoded.address, wp.address);
assert_eq!(decoded.command, wp.command);
assert_eq!(decoded.data, wp.data);
}
}