1pub const FLAG: u8 = 0xF9;
9
10pub const SABM: u8 = 0x2F;
12pub const UA: u8 = 0x63;
13pub const DM: u8 = 0x0F;
14pub const DISC: u8 = 0x43;
15pub const UIH: u8 = 0xEF;
16pub const UI: u8 = 0x03;
17pub const PF: u8 = 0x10;
18
19pub const MAX_INFO: usize = 32_767;
22pub const DEFAULT_MAX_RX_INFO: usize = 1_509;
27pub const TX_N1: usize = 31;
31
32const fn crc_table() -> [u8; 256] {
33 let mut table = [0u8; 256];
34 let mut i = 0;
35 while i < 256 {
36 let mut crc = i as u8;
37 let mut bit = 0;
38 while bit < 8 {
39 crc = if crc & 1 != 0 {
40 (crc >> 1) ^ 0xE0
41 } else {
42 crc >> 1
43 };
44 bit += 1;
45 }
46 table[i] = crc;
47 i += 1;
48 }
49 table
50}
51
52static CRC_TABLE: [u8; 256] = crc_table();
53
54pub fn fcs(bytes: &[u8]) -> u8 {
55 let crc = bytes
56 .iter()
57 .fold(0xFFu8, |crc, &b| CRC_TABLE[(crc ^ b) as usize]);
58 0xFF - crc
59}
60
61pub fn encode_address(dlci: u8, cr: bool) -> u8 {
62 ((dlci & 0x3F) << 2) | (u8::from(cr) << 1) | 0x01
63}
64
65pub fn decode_address(addr: u8) -> (u8, bool) {
66 ((addr >> 2) & 0x3F, addr & 0x02 != 0)
67}
68
69pub fn encode_frame(dlci: u8, cr: bool, control: u8, info: &[u8]) -> Vec<u8> {
74 assert!(info.len() <= MAX_INFO, "cmux info field too long");
75 let mut header = vec![encode_address(dlci, cr), control];
76 let len = info.len();
77 if len <= 127 {
78 header.push(((len as u8) << 1) | 0x01);
79 } else {
80 header.push(((len & 0x7F) as u8) << 1);
81 header.push((len >> 7) as u8);
82 }
83 let check = if control & !PF == UI {
84 fcs(&[header.as_slice(), info].concat())
85 } else {
86 fcs(&header)
87 };
88 let mut frame = Vec::with_capacity(header.len() + len + 3);
89 frame.push(FLAG);
90 frame.extend_from_slice(&header);
91 frame.extend_from_slice(info);
92 frame.push(check);
93 frame.push(FLAG);
94 frame
95}
96
97pub fn encode_uih(dlci: u8, payload: &[u8]) -> Vec<u8> {
99 if payload.is_empty() {
100 return encode_frame(dlci, true, UIH, &[]);
101 }
102 payload
103 .chunks(TX_N1)
104 .flat_map(|chunk| encode_frame(dlci, true, UIH, chunk))
105 .collect()
106}
107
108#[derive(Debug, Clone, PartialEq, Eq)]
109pub struct DecodedFrame {
110 pub dlci: u8,
111 pub cr: bool,
113 pub kind: u8,
115 pub payload: Vec<u8>,
116}
117
118pub struct Deframer {
120 buf: Vec<u8>,
121 max_info: usize,
122}
123
124impl Default for Deframer {
125 fn default() -> Self {
126 Self::with_max_info(DEFAULT_MAX_RX_INFO)
127 }
128}
129
130impl Deframer {
131 pub fn with_max_info(max_info: usize) -> Self {
134 Self {
135 buf: Vec::new(),
136 max_info: max_info.clamp(1, MAX_INFO),
137 }
138 }
139
140 pub fn feed(&mut self, chunk: &[u8]) -> Vec<DecodedFrame> {
141 self.buf.extend_from_slice(chunk);
142 let mut out = Vec::new();
143 let mut pos = 0;
145 loop {
146 match self.buf[pos..].iter().position(|&b| b == FLAG) {
148 Some(i) => pos += i,
149 None => {
150 pos = self.buf.len();
151 break;
152 }
153 }
154 let flags = self.buf[pos..].iter().take_while(|&&b| b == FLAG).count();
156 pos += flags - 1;
157 match self.parse_one(pos) {
158 Parse::Need => break,
159 Parse::Bad => pos += 1,
161 Parse::Frame(frame, used) => {
162 pos += used - 1;
164 out.push(frame);
165 }
166 }
167 }
168 self.buf.drain(..pos);
169 out
170 }
171
172 fn parse_one(&self, pos: usize) -> Parse {
173 let b = &self.buf[pos..];
174 if b.len() < 4 {
175 return Parse::Need;
176 }
177 let (addr, control) = (b[1], b[2]);
178 if addr & 0x01 == 0 {
179 return Parse::Bad;
180 }
181 let (len, len_octets) = if b[3] & 0x01 != 0 {
182 ((b[3] >> 1) as usize, 1)
183 } else {
184 if b.len() < 5 {
185 return Parse::Need;
186 }
187 (((b[3] >> 1) as usize) | ((b[4] as usize) << 7), 2)
188 };
189 if len > self.max_info {
190 return Parse::Bad;
191 }
192 let header_end = 3 + len_octets;
193 let total = header_end + len + 2; if b.len() < total {
195 return Parse::Need;
196 }
197 let info = &b[header_end..header_end + len];
198 let kind = control & !PF;
199 let expected = if kind == UI {
200 fcs(&b[1..header_end + len])
201 } else {
202 fcs(&b[1..header_end])
203 };
204 if b[header_end + len] != expected || b[total - 1] != FLAG {
205 return Parse::Bad;
206 }
207 let (dlci, cr) = decode_address(addr);
208 Parse::Frame(
209 DecodedFrame {
210 dlci,
211 cr,
212 kind,
213 payload: info.to_vec(),
214 },
215 total,
216 )
217 }
218}
219
220enum Parse {
221 Need,
222 Bad,
223 Frame(DecodedFrame, usize),
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229
230 #[test]
232 fn sabm_dlci0_matches_spec_vector() {
233 assert_eq!(
234 encode_frame(0, true, SABM | PF, &[]),
235 vec![0xF9, 0x03, 0x3F, 0x01, 0x1C, 0xF9]
236 );
237 }
238
239 #[test]
240 fn uih_roundtrip_with_flag_bytes_split_across_reads() {
241 let payload = b"AT\r\xF9\xF9 data";
242 let frame = encode_uih(1, payload);
243 let mut def = Deframer::default();
244 let (a, b) = frame.split_at(5);
245 assert!(def.feed(a).is_empty());
246 let frames = def.feed(b);
247 assert_eq!(frames.len(), 1);
248 assert_eq!(frames[0].dlci, 1);
249 assert_eq!(frames[0].kind, UIH);
250 assert_eq!(frames[0].payload, payload);
251 }
252
253 #[test]
254 fn long_payload_is_split_into_n1_frames_and_back_to_back_frames_share_flags() {
255 let payload: Vec<u8> = (0..100u8).collect();
256 let mut stream = encode_uih(2, &payload);
257 let second = encode_uih(3, b"OK");
259 stream.extend_from_slice(&second[1..]);
260 let frames = Deframer::default().feed(&stream);
261 let data: Vec<u8> = frames
262 .iter()
263 .filter(|f| f.dlci == 2)
264 .flat_map(|f| f.payload.clone())
265 .collect();
266 assert_eq!(data, payload);
267 assert!(frames.iter().all(|f| f.payload.len() <= TX_N1));
268 assert_eq!(frames.last().unwrap().payload, b"OK");
269 }
270
271 #[test]
272 fn two_octet_length_roundtrip() {
273 let info = vec![0x55u8; 200];
274 let frame = encode_frame(5, true, UIH, &info);
275 assert_eq!(frame[3] & 0x01, 0, "EA must be 0 for a 2-octet length");
276 let frames = Deframer::default().feed(&frame);
277 assert_eq!(frames[0].payload, info);
278 }
279
280 #[test]
281 fn garbage_and_bad_fcs_are_skipped() {
282 let mut stream = vec![0x00, 0xF9, 0x12, 0xF9];
283 let mut bad = encode_uih(1, b"x");
284 let fcs_at = bad.len() - 2;
285 bad[fcs_at] ^= 0xFF;
286 stream.extend_from_slice(&bad);
287 stream.extend_from_slice(&encode_uih(1, b"good"));
288 let frames = Deframer::default().feed(&stream);
289 assert_eq!(frames.len(), 1);
290 assert_eq!(frames[0].payload, b"good");
291 }
292
293 #[test]
295 fn resync_over_a_large_noise_read_is_linear() {
296 let mut stream: Vec<u8> = [FLAG, 0x00].repeat(1 << 19);
297 stream.extend_from_slice(&encode_uih(1, b"good"));
298 let t = std::time::Instant::now();
299 let frames = Deframer::default().feed(&stream);
300 assert!(
301 t.elapsed() < std::time::Duration::from_secs(5),
302 "{:?}",
303 t.elapsed()
304 );
305 assert_eq!(frames.len(), 1);
306 assert_eq!(frames[0].payload, b"good");
307 }
308
309 #[test]
310 fn info_field_is_limited_to_15_bits() {
311 let info = vec![0x55u8; MAX_INFO];
312 let frames = Deframer::with_max_info(MAX_INFO).feed(&encode_frame(1, true, UIH, &info));
313 assert_eq!(frames[0].payload, info);
314 let too_long = std::panic::catch_unwind(|| encode_frame(1, true, UIH, &[0; MAX_INFO + 1]));
315 assert!(too_long.is_err(), "32768 bytes would encode as length 0");
316 }
317}