1use rns_core::constants::HEADER_MINSIZE;
6
7const FLAG: u8 = 0x7E;
8const ESC: u8 = 0x7D;
9const ESC_MASK: u8 = 0x20;
10
11pub(crate) fn framed_len(data: &[u8]) -> usize {
13 data.len()
14 .saturating_add(
15 data.iter()
16 .filter(|&&byte| byte == FLAG || byte == ESC)
17 .count(),
18 )
19 .saturating_add(2)
20}
21
22pub fn escape(data: &[u8]) -> Vec<u8> {
24 let mut out = Vec::with_capacity(data.len());
25 for &b in data {
26 match b {
27 ESC => {
28 out.push(ESC);
29 out.push(ESC ^ ESC_MASK);
30 }
31 FLAG => {
32 out.push(ESC);
33 out.push(FLAG ^ ESC_MASK);
34 }
35 _ => out.push(b),
36 }
37 }
38 out
39}
40
41pub fn frame(data: &[u8]) -> Vec<u8> {
43 let mut out = Vec::with_capacity(framed_len(data));
44 out.push(FLAG);
45 for &byte in data {
46 match byte {
47 ESC | FLAG => {
48 out.push(ESC);
49 out.push(byte ^ ESC_MASK);
50 }
51 _ => out.push(byte),
52 }
53 }
54 out.push(FLAG);
55 out
56}
57
58fn unescape(data: &[u8]) -> Vec<u8> {
60 let mut out = Vec::with_capacity(data.len());
61 let mut i = 0;
62 while i < data.len() {
63 if data[i] == ESC && i + 1 < data.len() {
64 match data[i + 1] {
65 byte if byte == FLAG ^ ESC_MASK => {
66 out.push(FLAG);
67 i += 2;
68 }
69 byte if byte == ESC ^ ESC_MASK => {
70 out.push(ESC);
71 i += 2;
72 }
73 _ => {
74 out.push(ESC);
75 i += 1;
76 }
77 }
78 } else {
79 out.push(data[i]);
80 i += 1;
81 }
82 }
83 out
84}
85
86pub struct Decoder {
91 buffer: Vec<u8>,
92 offset: usize,
93 min_frame_size: usize,
94 max_frame_size: Option<usize>,
95 max_buffer_size: usize,
96}
97
98#[derive(Debug, Default, Eq, PartialEq)]
100pub struct DecodeBatch {
101 pub frames: Vec<Vec<u8>>,
102 pub invalid_frame_lengths: Vec<usize>,
103}
104
105impl Decoder {
106 pub fn new() -> Self {
107 Self::with_limits(HEADER_MINSIZE, 64 * 1024)
108 }
109
110 pub fn with_min_frame_size(min_frame_size: usize) -> Self {
112 Self::with_limits(min_frame_size, 64 * 1024)
113 }
114
115 pub fn with_limits(min_frame_size: usize, max_buffer_size: usize) -> Self {
116 Decoder {
117 buffer: Vec::new(),
118 offset: 0,
119 min_frame_size,
120 max_frame_size: None,
121 max_buffer_size: max_buffer_size.max(2),
122 }
123 }
124
125 pub fn reticulum(hardware_mtu: usize, ifac_size: usize) -> Self {
131 Decoder {
132 buffer: Vec::new(),
133 offset: 0,
134 min_frame_size: HEADER_MINSIZE.saturating_add(1),
135 max_frame_size: Some(hardware_mtu.saturating_add(ifac_size)),
136 max_buffer_size: hardware_mtu.saturating_mul(2).max(2),
137 }
138 }
139
140 pub fn feed(&mut self, chunk: &[u8]) -> Vec<Vec<u8>> {
142 self.feed_with_diagnostics(chunk).frames
143 }
144
145 pub fn feed_with_diagnostics(&mut self, chunk: &[u8]) -> DecodeBatch {
148 self.buffer.extend_from_slice(chunk);
149 let mut decoded = DecodeBatch::default();
150
151 loop {
152 let start = match self.buffer[self.offset..].iter().position(|&b| b == FLAG) {
154 Some(pos) => self.offset + pos,
155 None => {
156 self.buffer.clear();
158 self.offset = 0;
159 break;
160 }
161 };
162
163 let end = match self.buffer[start + 1..].iter().position(|&b| b == FLAG) {
165 Some(pos) => start + 1 + pos,
166 None => {
167 if self.buffer.len() - self.offset > self.max_buffer_size {
168 self.buffer.clear();
169 self.offset = 0;
170 }
171 break;
172 }
173 };
174
175 let between = &self.buffer[start + 1..end];
177 let unescaped = unescape(between);
178
179 let frame_len = unescaped.len();
180 let within_maximum = self
181 .max_frame_size
182 .is_none_or(|max_frame_size| frame_len <= max_frame_size);
183 if frame_len >= self.min_frame_size && within_maximum {
184 decoded.frames.push(unescaped);
185 } else if frame_len != 0 {
186 decoded.invalid_frame_lengths.push(frame_len);
187 }
188
189 self.offset = end;
192 if self.offset > 0 && self.offset >= self.buffer.len() / 2 {
193 self.buffer.drain(..self.offset);
194 self.offset = 0;
195 }
196 }
197
198 decoded
199 }
200}
201
202impl Default for Decoder {
203 fn default() -> Self {
204 Self::new()
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 #[test]
213 fn escape_passthrough() {
214 let data = b"hello world";
215 assert_eq!(escape(data), data.to_vec());
216 }
217
218 #[test]
219 fn escape_flag() {
220 assert_eq!(escape(&[FLAG]), vec![ESC, FLAG ^ ESC_MASK]);
221 assert_eq!(escape(&[0x7E]), vec![0x7D, 0x5E]);
222 }
223
224 #[test]
225 fn escape_esc() {
226 assert_eq!(escape(&[ESC]), vec![ESC, ESC ^ ESC_MASK]);
227 assert_eq!(escape(&[0x7D]), vec![0x7D, 0x5D]);
228 }
229
230 #[test]
231 fn escape_mixed() {
232 let data = [0x01, FLAG, 0x02, ESC, 0x03];
233 let expected = vec![0x01, ESC, FLAG ^ ESC_MASK, 0x02, ESC, ESC ^ ESC_MASK, 0x03];
234 assert_eq!(escape(&data), expected);
235 }
236
237 #[test]
238 fn frame_structure() {
239 let data = b"test";
240 let framed = frame(data);
241 assert_eq!(framed[0], FLAG);
242 assert_eq!(*framed.last().unwrap(), FLAG);
243 assert_eq!(&framed[1..framed.len() - 1], &escape(data));
244 }
245
246 #[test]
247 fn canonical_frame_matches_wrapped_escape_for_every_byte() {
248 let data: Vec<u8> = (0..=u8::MAX).collect();
249 let mut expected = Vec::with_capacity(escape(&data).len() + 2);
250 expected.push(FLAG);
251 expected.extend_from_slice(&escape(&data));
252 expected.push(FLAG);
253
254 let framed = frame(&data);
255
256 assert_eq!(framed, expected);
257 assert_eq!(framed.capacity(), framed.len());
258 }
259
260 #[test]
261 fn framed_len_matches_exact_encoded_length() {
262 for data in [
263 Vec::new(),
264 vec![0x01, 0x02],
265 vec![FLAG, ESC, 0x03],
266 vec![FLAG; 1_024],
267 ] {
268 assert_eq!(framed_len(&data), frame(&data).len());
269 }
270 }
271
272 #[test]
273 fn roundtrip_all_bytes() {
274 let data: Vec<u8> = (0..=255).collect();
276 let framed = frame(&data);
277
278 let mut decoder = Decoder::new();
279 let frames = decoder.feed(&framed);
280 assert_eq!(frames.len(), 1);
281 assert_eq!(frames[0], data);
282 }
283
284 #[test]
285 fn unescape_preserves_unknown_and_trailing_escape_bytes() {
286 let mut decoder = Decoder::with_min_frame_size(0);
287
288 let frames = decoder.feed(&[FLAG, ESC, 0x00, ESC, FLAG]);
289
290 assert_eq!(frames, vec![vec![ESC, 0x00, ESC]]);
291 }
292
293 #[test]
294 fn decoder_single_frame() {
295 let data: Vec<u8> = (0..32).collect();
297 let framed = frame(&data);
298
299 let mut decoder = Decoder::new();
300 let frames = decoder.feed(&framed);
301 assert_eq!(frames.len(), 1);
302 assert_eq!(frames[0], data);
303 }
304
305 #[test]
306 fn decoder_two_frames_one_chunk() {
307 let data1: Vec<u8> = (0..24).collect();
308 let data2: Vec<u8> = (100..130).collect();
309 let mut combined = frame(&data1);
310 let framed2 = frame(&data2);
315 combined.extend_from_slice(&framed2[1..]);
317
318 let mut decoder = Decoder::new();
319 let frames = decoder.feed(&combined);
320 assert_eq!(frames.len(), 2);
321 assert_eq!(frames[0], data1);
322 assert_eq!(frames[1], data2);
323 }
324
325 #[test]
326 fn decoder_split_frame() {
327 let data: Vec<u8> = (0..32).collect();
328 let framed = frame(&data);
329
330 let mid = framed.len() / 2;
332 let mut decoder = Decoder::new();
333
334 let frames1 = decoder.feed(&framed[..mid]);
335 assert_eq!(frames1.len(), 0); let frames2 = decoder.feed(&framed[mid..]);
338 assert_eq!(frames2.len(), 1);
339 assert_eq!(frames2[0], data);
340 }
341
342 #[test]
343 fn decoder_does_not_drop_large_coalesced_batches() {
344 let payload = vec![0x42; 32];
345 let encoded = frame(&payload);
346 let count = 20_000;
347 let mut batch = Vec::with_capacity(encoded.len() * count);
348 for index in 0..count {
349 if index == 0 {
350 batch.extend_from_slice(&encoded);
351 } else {
352 batch.extend_from_slice(&encoded[1..]);
353 }
354 }
355 let mut decoder = Decoder::with_limits(HEADER_MINSIZE, 64 * 1024);
356 let frames = decoder.feed(&batch);
357 assert_eq!(frames.len(), count);
358 assert!(frames.iter().all(|decoded| decoded == &payload));
359 }
360
361 #[test]
362 fn decoder_defers_compaction_until_consumed_prefix_reaches_half() {
363 let first = vec![0x31; 32];
364 let second = vec![0x42; 200];
365 let mut stream = frame(&first);
366 stream.extend_from_slice(&escape(&second));
367 let mut decoder = Decoder::with_limits(HEADER_MINSIZE, 1024);
368
369 assert_eq!(decoder.feed(&stream), vec![first]);
370 assert!(decoder.offset > 0);
371 assert!(decoder.buffer.len() - decoder.offset > decoder.offset);
372
373 assert_eq!(decoder.feed(&[FLAG]), vec![second]);
374 assert_eq!(decoder.buffer, vec![FLAG]);
375 assert_eq!(decoder.offset, 0);
376 }
377
378 #[test]
379 fn decoder_drops_short() {
380 let data = vec![0x01, 0x02, 0x03]; let framed = frame(&data);
383
384 let mut decoder = Decoder::new();
385 let frames = decoder.feed(&framed);
386 assert_eq!(frames.len(), 0); }
388
389 #[test]
390 fn reticulum_decoder_enforces_strict_minimum_and_ifac_adjusted_maximum() {
391 let hardware_mtu = 64;
392 let ifac_size = 8;
393 let mut decoder = Decoder::reticulum(hardware_mtu, ifac_size);
394 let lengths = [
395 HEADER_MINSIZE,
396 HEADER_MINSIZE + 1,
397 hardware_mtu + ifac_size,
398 hardware_mtu + ifac_size + 1,
399 ];
400 let mut encoded = vec![FLAG];
401 for length in lengths {
402 encoded.extend_from_slice(&frame(&vec![0x42; length])[1..]);
403 }
404
405 let decoded = decoder.feed_with_diagnostics(&encoded);
406
407 assert_eq!(
408 decoded.frames.iter().map(Vec::len).collect::<Vec<_>>(),
409 vec![HEADER_MINSIZE + 1, hardware_mtu + ifac_size]
410 );
411 assert_eq!(
412 decoded.invalid_frame_lengths,
413 vec![HEADER_MINSIZE, hardware_mtu + ifac_size + 1]
414 );
415 }
416
417 #[test]
418 fn empty_frames_are_ignored_without_invalid_frame_diagnostics() {
419 let mut decoder = Decoder::reticulum(64, 0);
420
421 let decoded = decoder.feed_with_diagnostics(&[FLAG, FLAG, FLAG]);
422
423 assert!(decoded.frames.is_empty());
424 assert!(decoded.invalid_frame_lengths.is_empty());
425 }
426
427 #[test]
428 fn oversized_complete_frame_is_dropped_and_next_frame_is_recovered() {
429 let mut decoder = Decoder::reticulum(64, 0);
430 let oversized = frame(&[0x55; 65]);
431 let valid = frame(&[0x33; HEADER_MINSIZE + 1]);
432 let mut encoded = oversized;
433 encoded.extend_from_slice(&valid[1..]);
434
435 let decoded = decoder.feed_with_diagnostics(&encoded);
436
437 assert_eq!(decoded.frames, vec![vec![0x33; HEADER_MINSIZE + 1]]);
438 assert_eq!(decoded.invalid_frame_lengths, vec![65]);
439 }
440
441 #[test]
442 fn fragmented_frame_larger_than_legacy_buffer_limit_is_preserved() {
443 let hardware_mtu = 70 * 1024;
444 let payload = vec![0x42; hardware_mtu];
445 let encoded = frame(&payload);
446 let split = 65 * 1024;
447 let mut decoder = Decoder::reticulum(hardware_mtu, 0);
448
449 let first = decoder.feed_with_diagnostics(&encoded[..split]);
450 assert!(first.frames.is_empty());
451 assert!(first.invalid_frame_lengths.is_empty());
452 assert_eq!(decoder.buffer.len(), split);
453
454 let second = decoder.feed_with_diagnostics(&encoded[split..]);
455 assert_eq!(second.frames, vec![payload]);
456 assert!(second.invalid_frame_lengths.is_empty());
457 }
458
459 #[test]
460 fn unterminated_tail_is_dropped_only_after_twice_hardware_mtu() {
461 let hardware_mtu = 32;
462 let mut decoder = Decoder::reticulum(hardware_mtu, 0);
463 let mut exact_limit = vec![FLAG];
464 exact_limit.extend(vec![0x22; hardware_mtu * 2 - 1]);
465
466 assert!(decoder.feed(&exact_limit).is_empty());
467 assert_eq!(decoder.buffer.len(), hardware_mtu * 2);
468
469 assert!(decoder.feed(&[0x22]).is_empty());
470 assert!(decoder.buffer.is_empty());
471
472 let payload = vec![0x44; HEADER_MINSIZE + 1];
473 assert_eq!(decoder.feed(&frame(&payload)), vec![payload]);
474 }
475
476 #[test]
477 fn decoded_length_not_escaped_wire_length_controls_frame_limit() {
478 let hardware_mtu = 64;
479 let payload = vec![FLAG; hardware_mtu];
480 let encoded = frame(&payload);
481 assert!(encoded.len() > hardware_mtu * 2);
482 let mut decoder = Decoder::reticulum(hardware_mtu, 0);
483
484 let decoded = decoder.feed_with_diagnostics(&encoded);
485
486 assert_eq!(decoded.frames, vec![payload]);
487 assert!(decoded.invalid_frame_lengths.is_empty());
488 }
489}