use crate::command::Command;
use crate::hex::byte2hex;
#[inline]
pub fn checksum_byte(cmd: &[u8]) -> u8 {
cmd.iter().fold(0u8, |acc, &b| acc.wrapping_add(b))
}
#[inline]
pub fn checksum_hex(cmd: &[u8]) -> [u8; 2] {
byte2hex(checksum_byte(cmd))
}
pub fn build(cmd: &[u8], out: &mut [u8]) -> Option<usize> {
let n = cmd.len() + 2;
if out.len() < n {
return None;
}
out[..cmd.len()].copy_from_slice(cmd);
let h = checksum_hex(cmd);
out[cmd.len()] = h[0];
out[cmd.len() + 1] = h[1];
Some(n)
}
pub fn build_command<C: Command>(cmd: &C, out: &mut [u8]) -> Option<usize> {
let cmd_len = cmd.len();
let n = cmd_len + 2;
if out.len() < n {
return None;
}
cmd.write(&mut out[..cmd_len])?;
let h = checksum_hex(&out[..cmd_len]);
out[cmd_len] = h[0];
out[cmd_len + 1] = h[1];
Some(n)
}
pub fn verify(packet: &[u8]) -> bool {
if packet.len() < 3 {
return false;
}
let (body, tail) = packet.split_at(packet.len() - 2);
let h = checksum_hex(body);
tail == &h
}
#[cfg(test)]
mod tests {
use super::*;
use crate::command::Record;
#[test]
fn checksum_byte_is_sum_mod_256() {
assert_eq!(checksum_byte(b""), 0);
assert_eq!(checksum_byte(b"A"), 0x41);
assert_eq!(checksum_byte(b"AB"), 0x41 + 0x42);
assert_eq!(checksum_byte(&[0xFF, 0x01]), 0x00);
}
#[test]
fn checksum_hex_is_two_hex_chars() {
let h = checksum_hex(b"#TPUG2wPTZ02");
assert_eq!(h.len(), 2);
assert!(h.iter().all(|b| b.is_ascii_hexdigit()));
assert!(h
.iter()
.all(|b| b.is_ascii_digit() || b.is_ascii_uppercase()));
}
#[test]
fn build_appends_two_chars() {
let mut out = [0u8; 32];
let n = build(b"#TPUD2wCAP01", &mut out).unwrap();
assert_eq!(n, 14);
assert_eq!(&out[..12], b"#TPUD2wCAP01");
let h = checksum_hex(b"#TPUD2wCAP01");
assert_eq!(&out[12..14], &h);
}
#[test]
fn build_returns_none_if_small() {
let mut out = [0u8; 4];
assert_eq!(build(b"#TPUD2wCAP01", &mut out), None);
}
#[test]
fn build_command_uses_command_trait() {
let mut out = [0u8; 32];
let n = build_command(&Record::Start, &mut out).unwrap();
assert_eq!(n, 14);
assert_eq!(&out[..12], b"#TPUD2wREC01");
}
#[test]
fn verify_roundtrip() {
let mut out = [0u8; 32];
let n = build(b"#TPUM2wZMC01", &mut out).unwrap();
assert!(verify(&out[..n]));
let mut bad = out;
bad[n - 1] ^= 0xFF;
assert!(!verify(&bad[..n]));
}
}