skydroid_protocol/
packet.rs1use crate::command::Command;
9use crate::hex::byte2hex;
10
11#[inline]
13pub fn checksum_byte(cmd: &[u8]) -> u8 {
14 cmd.iter().fold(0u8, |acc, &b| acc.wrapping_add(b))
15}
16
17#[inline]
19pub fn checksum_hex(cmd: &[u8]) -> [u8; 2] {
20 byte2hex(checksum_byte(cmd))
21}
22
23pub fn build(cmd: &[u8], out: &mut [u8]) -> Option<usize> {
35 let n = cmd.len() + 2;
36 if out.len() < n {
37 return None;
38 }
39 out[..cmd.len()].copy_from_slice(cmd);
40 let h = checksum_hex(cmd);
41 out[cmd.len()] = h[0];
42 out[cmd.len() + 1] = h[1];
43 Some(n)
44}
45
46pub fn build_command<C: Command>(cmd: &C, out: &mut [u8]) -> Option<usize> {
57 let cmd_len = cmd.len();
58 let n = cmd_len + 2;
59 if out.len() < n {
60 return None;
61 }
62 cmd.write(&mut out[..cmd_len])?;
63 let h = checksum_hex(&out[..cmd_len]);
64 out[cmd_len] = h[0];
65 out[cmd_len + 1] = h[1];
66 Some(n)
67}
68
69pub fn verify(packet: &[u8]) -> bool {
71 if packet.len() < 3 {
72 return false;
73 }
74 let (body, tail) = packet.split_at(packet.len() - 2);
75 let h = checksum_hex(body);
76 tail == &h
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use crate::command::Record;
83
84 #[test]
85 fn checksum_byte_is_sum_mod_256() {
86 assert_eq!(checksum_byte(b""), 0);
87 assert_eq!(checksum_byte(b"A"), 0x41);
88 assert_eq!(checksum_byte(b"AB"), 0x41 + 0x42);
89 assert_eq!(checksum_byte(&[0xFF, 0x01]), 0x00);
91 }
92
93 #[test]
94 fn checksum_hex_is_two_hex_chars() {
95 let h = checksum_hex(b"#TPUG2wPTZ02");
96 assert_eq!(h.len(), 2);
97 assert!(h.iter().all(|b| b.is_ascii_hexdigit()));
99 assert!(h
100 .iter()
101 .all(|b| b.is_ascii_digit() || b.is_ascii_uppercase()));
102 }
103
104 #[test]
105 fn build_appends_two_chars() {
106 let mut out = [0u8; 32];
107 let n = build(b"#TPUD2wCAP01", &mut out).unwrap();
108 assert_eq!(n, 14);
109 assert_eq!(&out[..12], b"#TPUD2wCAP01");
110 let h = checksum_hex(b"#TPUD2wCAP01");
111 assert_eq!(&out[12..14], &h);
112 }
113
114 #[test]
115 fn build_returns_none_if_small() {
116 let mut out = [0u8; 4];
117 assert_eq!(build(b"#TPUD2wCAP01", &mut out), None);
118 }
119
120 #[test]
121 fn build_command_uses_command_trait() {
122 let mut out = [0u8; 32];
123 let n = build_command(&Record::Start, &mut out).unwrap();
124 assert_eq!(n, 14);
125 assert_eq!(&out[..12], b"#TPUD2wREC01");
126 }
127
128 #[test]
129 fn verify_roundtrip() {
130 let mut out = [0u8; 32];
131 let n = build(b"#TPUM2wZMC01", &mut out).unwrap();
132 assert!(verify(&out[..n]));
133 let mut bad = out;
135 bad[n - 1] ^= 0xFF;
136 assert!(!verify(&bad[..n]));
137 }
138}