Skip to main content

ember_plus/s101/
codec.rs

1//! S101 codec for encoding and decoding streams.
2
3use bytes::{Buf, BufMut, BytesMut};
4use crate::error::{S101Error, Result};
5use super::constants::*;
6use super::frame::{S101Frame, S101Message, EmberFlags};
7use super::crc;
8
9/// S101 stream decoder.
10#[derive(Debug, Default)]
11pub struct S101Decoder {
12    buffer: BytesMut,
13    in_frame: bool,
14    escape_next: bool,
15}
16
17impl S101Decoder {
18    /// Create a new decoder.
19    pub fn new() -> Self {
20        S101Decoder {
21            buffer: BytesMut::new(),
22            in_frame: false,
23            escape_next: false,
24        }
25    }
26
27    /// Feed data to the decoder.
28    pub fn feed(&mut self, data: &[u8]) {
29        for &byte in data {
30            self.process_byte(byte);
31        }
32    }
33
34    /// Process a single byte.
35    fn process_byte(&mut self, byte: u8) {
36        if self.escape_next {
37            self.buffer.put_u8(byte ^ ESCAPE_XOR);
38            self.escape_next = false;
39        } else if byte == BOF {
40            // Start of frame
41            self.buffer.clear();
42            self.in_frame = true;
43        } else if byte == EOF {
44            if self.in_frame && !self.buffer.is_empty() {
45                // End of frame - keep the data for extraction
46                self.in_frame = false;
47            }
48        } else if byte == ESCAPE_BYTE && self.in_frame {
49            self.escape_next = true;
50        } else if self.in_frame {
51            self.buffer.put_u8(byte);
52        }
53    }
54
55    /// Try to extract a complete frame.
56    pub fn decode_frame(&mut self) -> Result<Option<S101Frame>> {
57        if self.buffer.is_empty() || self.in_frame {
58            return Ok(None);
59        }
60
61        // Verify CRC
62        if self.buffer.len() < 5 {
63            // Too short for a valid frame
64            self.buffer.clear();
65            return Err(S101Error::IncompleteFrame.into());
66        }
67
68        if !crc::verify_crc(&self.buffer) {
69            let len = self.buffer.len();
70            let received = u16::from_be_bytes([self.buffer[len - 2], self.buffer[len - 1]]);
71            let calculated = crc::calculate_crc(&self.buffer[..len - 2]);
72            self.buffer.clear();
73            return Err(S101Error::CrcMismatch {
74                expected: calculated,
75                actual: received,
76            }.into());
77        }
78
79        // Remove CRC
80        let payload_len = self.buffer.len() - 2;
81        self.buffer.truncate(payload_len);
82
83        // Parse frame header
84        let data = self.buffer.split();
85        
86        if data.len() < 3 {
87            return Err(S101Error::IncompleteFrame.into());
88        }
89
90        let slot = data[0];
91        let message_type = super::frame::MessageType::try_from(data[1])?;
92        let command = super::frame::Command::try_from(data[2])?;
93
94        // Extract payload based on command type
95        let payload = match command {
96            super::frame::Command::KeepAliveRequest | super::frame::Command::KeepAliveResponse => {
97                // Keep-alive: slot(1) + msg_type(1) + cmd(1) + version(1) = 4 bytes, no payload
98                vec![]
99            }
100            super::frame::Command::Ember => {
101                // Ember data: slot(1) + msg_type(1) + cmd(1) + version(1) + flags(1) + dtd(1) + app_count(1) + app_bytes + payload
102                if data.len() < 7 {
103                    return Err(S101Error::IncompleteFrame.into());
104                }
105                let app_bytes_count = data[6] as usize;
106                let payload_start = 7 + app_bytes_count;
107                if payload_start > data.len() {
108                    return Err(S101Error::IncompleteFrame.into());
109                }
110                data[payload_start..].to_vec()
111            }
112        };
113
114        Ok(Some(S101Frame {
115            slot,
116            message_type,
117            command,
118            payload,
119        }))
120    }
121
122    /// Decode and return a message if available.
123    pub fn decode_message(&mut self) -> Result<Option<S101Message>> {
124        if let Some(frame) = self.decode_frame()? {
125            Ok(Some(frame.to_message()))
126        } else {
127            Ok(None)
128        }
129    }
130}
131
132/// S101 stream encoder.
133#[derive(Debug, Default)]
134pub struct S101Encoder;
135
136impl S101Encoder {
137    /// Create a new encoder.
138    pub fn new() -> Self {
139        S101Encoder
140    }
141
142    /// Encode an Ember+ packet.
143    pub fn encode_ember_packet(payload: &[u8]) -> Vec<u8> {
144        let frame = S101Frame::ember_data(payload.to_vec());
145        frame.encode()
146    }
147
148    /// Encode a keep-alive request.
149    pub fn encode_keepalive_request() -> Vec<u8> {
150        S101Frame::keepalive_request().encode()
151    }
152
153    /// Encode a keep-alive response.
154    pub fn encode_keepalive_response() -> Vec<u8> {
155        S101Frame::keepalive_response().encode()
156    }
157
158    /// Encode a message.
159    pub fn encode_message(message: &S101Message) -> Vec<u8> {
160        match message {
161            S101Message::EmberData { payload, .. } => {
162                Self::encode_ember_packet(payload)
163            }
164            S101Message::KeepAliveRequest => Self::encode_keepalive_request(),
165            S101Message::KeepAliveResponse => Self::encode_keepalive_response(),
166        }
167    }
168
169    /// Encode raw bytes with S101 framing.
170    fn encode_with_framing(data: &[u8]) -> Vec<u8> {
171        let mut output = Vec::with_capacity(data.len() * 2 + 4);
172        output.push(BOF);
173        
174        for &byte in data {
175            // Escape only: BOF (0xFE), EOF (0xFF), ESCAPE (0xFD)
176            if byte == BOF || byte == EOF || byte == ESCAPE_BYTE {
177                output.push(ESCAPE_BYTE);
178                output.push(byte ^ ESCAPE_XOR);
179            } else {
180                output.push(byte);
181            }
182        }
183        
184        output.push(EOF);
185        output
186    }
187}
188
189/// Combined S101 codec for bidirectional communication.
190pub struct S101Codec {
191    /// Decoder for incoming data
192    pub decoder: S101Decoder,
193    /// Encoder for outgoing data
194    pub encoder: S101Encoder,
195}
196
197impl S101Codec {
198    /// Create a new codec.
199    pub fn new() -> Self {
200        S101Codec {
201            decoder: S101Decoder::new(),
202            encoder: S101Encoder::new(),
203        }
204    }
205
206    /// Feed data to the decoder.
207    pub fn feed(&mut self, data: &[u8]) {
208        self.decoder.feed(data);
209    }
210
211    /// Try to decode a frame.
212    pub fn decode_frame(&mut self) -> Result<Option<S101Frame>> {
213        self.decoder.decode_frame()
214    }
215
216    /// Encode an Ember packet.
217    pub fn encode_ember_packet(&self, payload: &[u8]) -> Vec<u8> {
218        S101Encoder::encode_ember_packet(payload)
219    }
220}
221
222impl Default for S101Codec {
223    fn default() -> Self {
224        Self::new()
225    }
226}
227
228#[cfg(test)]
229mod tests {
230    use super::*;
231
232    #[test]
233    fn test_decoder_basic() {
234        let mut decoder = S101Decoder::new();
235        
236        // Create a simple frame
237        let frame = S101Frame::ember_data(vec![0x01, 0x02, 0x03]);
238        let encoded = frame.encode();
239        
240        decoder.feed(&encoded);
241        let decoded = decoder.decode_frame().unwrap().unwrap();
242        
243        assert_eq!(decoded.payload, vec![0x01, 0x02, 0x03]);
244    }
245
246    #[test]
247    fn test_decoder_streaming() {
248        let mut decoder = S101Decoder::new();
249        
250        let frame = S101Frame::ember_data(vec![0xAA, 0xBB, 0xCC]);
251        let encoded = frame.encode();
252        
253        // Feed byte by byte
254        for &byte in &encoded {
255            decoder.feed(&[byte]);
256        }
257        
258        let decoded = decoder.decode_frame().unwrap().unwrap();
259        assert_eq!(decoded.payload, vec![0xAA, 0xBB, 0xCC]);
260    }
261
262    #[test]
263    fn test_keepalive_roundtrip() {
264        let mut decoder = S101Decoder::new();
265        
266        let encoded = S101Encoder::encode_keepalive_request();
267        decoder.feed(&encoded);
268        
269        let message = decoder.decode_message().unwrap().unwrap();
270        assert!(message.is_keepalive_request());
271    }
272}