lazy_net/parse.rs
1use bytes::Buf;
2
3/// A trait for parsing framed binary protocols with optional default implementations.
4///
5/// This trait defines a typical framing + checksum pattern for binary protocols.
6/// Types implementing this trait must provide `try_parse_frame`, and optionally override `parse` and helper methods.
7pub trait LazyParse: Send {
8 /// Attempts to parse one complete frame from the given buffer.
9 ///
10 /// # Arguments
11 /// * `buf` - A mutable reference to a `BytesMut` buffer containing the incoming stream data.
12 ///
13 /// # Returns
14 /// * `Ok(())` if a frame was successfully parsed and processed.
15 /// * `Err` if parsing fails or an incomplete frame is encountered.
16 fn try_parse_frame(
17 &mut self,
18 buf: &mut bytes::BytesMut,
19 ) -> Result<(), Box<dyn std::error::Error>>;
20
21 /// The default implementation for extracting and validating a frame.
22 ///
23 /// This includes:
24 /// - Searching for a valid header (`pattern`)
25 /// - Calculating BCC (checksum)
26 /// - Extracting length and validating buffer completeness
27 ///
28 /// # Arguments
29 /// * `buf` - The buffer to parse from.
30 /// * `pattern` - A byte pattern representing the frame header, e.g., `[0x55, 0xAA]`.
31 ///
32 /// # Returns
33 /// * `Ok(bcc)` if the frame is structurally valid and the buffer is large enough.
34 /// * `Err` with a detailed error message otherwise.
35 fn parse(
36 &mut self,
37 buf: &mut bytes::BytesMut,
38 pattern: &[u8],
39 ) -> Result<u8, Box<dyn std::error::Error>> {
40 if buf.len() < 10 {
41 return Err("buffer too short (less than 10 bytes)".into());
42 }
43
44 // Find the positions of the first and second matched headers
45 let (first, second) = self.find_first_two_matches(buf, pattern);
46
47 let Some(start) = first else {
48 return Err("frame header not found".into());
49 };
50
51 // Skip any leading bytes before the first valid header
52 if start != 0 {
53 buf.advance(start);
54 }
55
56 let bcc_end = second.unwrap_or(buf.len());
57 if bcc_end < 3 {
58 return Err(format!("second header index too small: bcc_end = {}", bcc_end).into());
59 }
60
61 // Ensure enough bytes are available for BCC computation
62 if buf.len() < bcc_end {
63 return Err(format!(
64 " not enough data for BCC computation, need {} bytes, got {}",
65 bcc_end,
66 buf.len()
67 )
68 .into());
69 }
70
71 // Compute BCC (checksum) from the frame body (excluding header and CRC)
72 let bcc = self.bcc(&buf[2..bcc_end.saturating_sub(1)]);
73
74 // Double check buffer length again (for header and length field)
75 if buf.len() < 10 {
76 return Err("insufficient buffer for header fields".into());
77 }
78
79 // Extract length field from buffer (little endian), mask with 0x1FFF
80 let mut len = ((buf[9] as u16) << 8) | (buf[8] as u16);
81 len &= 0x1fff;
82
83 // Ensure the full frame is present
84 if buf.len() < (len + 10) as usize {
85 return Err("insufficient buffer for full frame".into());
86 }
87
88 Ok(bcc)
89 }
90
91 /// Computes XOR-based checksum (BCC) from a slice of bytes.
92 ///
93 /// # Arguments
94 /// * `data` - Byte slice to compute checksum for.
95 ///
96 /// # Returns
97 /// * A single-byte XOR checksum result.
98 fn bcc(&self, data: &[u8]) -> u8 {
99 data.iter().fold(0u8, |acc, &b| acc ^ b)
100 }
101
102 /// Finds the positions of the first and second occurrences of a given byte pattern.
103 ///
104 /// This is useful for detecting valid frame start boundaries and possibly the start of the next frame.
105 ///
106 /// # Arguments
107 /// * `buf` - The full buffer to search.
108 /// * `pattern` - The byte pattern to match, e.g., `[0x55, 0xAA]`.
109 ///
110 /// # Returns
111 /// * A tuple of `(first_match_index, second_match_index)`; each is `Option<usize>`.
112 fn find_first_two_matches(&self, buf: &[u8], pattern: &[u8]) -> (Option<usize>, Option<usize>) {
113 let mut first = None;
114 let mut second = None;
115
116 for (i, window) in buf.windows(pattern.len()).enumerate() {
117 if window == pattern {
118 if first.is_none() {
119 first = Some(i);
120 } else {
121 second = Some(i);
122 break;
123 }
124 }
125 }
126
127 (first, second)
128 }
129}