1use zerocopy::byteorder::big_endian::{U16 as BeU16, U32 as BeU32};
8use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
9
10use crate::error::FrameError;
11
12const TYPE_DATA: u8 = 0x01;
14const TYPE_ACK: u8 = 0x02;
16const TYPE_CLOSE: u8 = 0x03;
18const TYPE_RESET: u8 = 0x04;
20const TYPE_NEGOTIATE_REQ: u8 = 0x10;
22const TYPE_NEGOTIATE_RESP: u8 = 0x11;
24
25pub const CONTROL_CHANNEL_ID: u16 = 0;
27
28pub(crate) const SYNC_WORD_LEN: usize = 2;
30pub(crate) const CHECKSUM_LEN: usize = 4;
32
33pub(crate) const SYNC_WORD: [u8; SYNC_WORD_LEN] = [0xAA, 0x55];
35
36#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Debug, Clone, Copy, PartialEq, Eq)]
38#[repr(C, packed)]
39pub(crate) struct WireHeader {
40 pub sync: [u8; SYNC_WORD_LEN],
42 pub session_id: BeU32,
44 pub channel_id: BeU16,
46 pub seq: u8,
48 pub frame_type: u8,
50 pub payload_len: BeU16,
52 pub header_checksum: u8,
54}
55
56pub(crate) const HEADER_LEN: usize = std::mem::size_of::<WireHeader>();
58
59pub const MAX_PAYLOAD_SIZE: usize = 1024;
61
62pub const MAX_FRAME_SIZE: usize = HEADER_LEN + MAX_PAYLOAD_SIZE + CHECKSUM_LEN;
64
65#[cfg(test)]
66pub(crate) const PAYLOAD_LEN_OFFSET: usize = std::mem::offset_of!(WireHeader, payload_len);
67pub(crate) const HEADER_CHECKSUM_OFFSET: usize = std::mem::offset_of!(WireHeader, header_checksum);
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
71pub enum FrameType {
72 Data,
74 Ack,
76 Close,
78 Reset,
80 NegotiateReq,
82 NegotiateResp,
84 Unknown(u8),
86}
87
88impl From<u8> for FrameType {
89 fn from(val: u8) -> Self {
90 match val {
91 TYPE_DATA => FrameType::Data,
92 TYPE_ACK => FrameType::Ack,
93 TYPE_CLOSE => FrameType::Close,
94 TYPE_RESET => FrameType::Reset,
95 TYPE_NEGOTIATE_REQ => FrameType::NegotiateReq,
96 TYPE_NEGOTIATE_RESP => FrameType::NegotiateResp,
97 other => FrameType::Unknown(other),
98 }
99 }
100}
101
102impl From<FrameType> for u8 {
103 fn from(ft: FrameType) -> Self {
104 match ft {
105 FrameType::Data => TYPE_DATA,
106 FrameType::Ack => TYPE_ACK,
107 FrameType::Close => TYPE_CLOSE,
108 FrameType::Reset => TYPE_RESET,
109 FrameType::NegotiateReq => TYPE_NEGOTIATE_REQ,
110 FrameType::NegotiateResp => TYPE_NEGOTIATE_RESP,
111 FrameType::Unknown(val) => val,
112 }
113 }
114}
115
116impl std::fmt::Display for FrameType {
117 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118 write!(f, "{self:?}")
119 }
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
124pub enum ProtocolId {
125 ResendSP,
127 Unknown(u32),
129}
130
131impl ProtocolId {
132 pub fn wire_id(self) -> u32 {
134 match self {
135 ProtocolId::ResendSP => 1,
136 ProtocolId::Unknown(val) => val,
137 }
138 }
139}
140
141impl From<u32> for ProtocolId {
142 fn from(val: u32) -> Self {
143 match val {
144 1 => ProtocolId::ResendSP,
145 other => ProtocolId::Unknown(other),
146 }
147 }
148}
149
150impl From<ProtocolId> for u32 {
151 fn from(proto: ProtocolId) -> Self {
152 proto.wire_id()
153 }
154}
155
156impl std::fmt::Display for ProtocolId {
157 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158 match self {
159 ProtocolId::ResendSP => write!(f, "ResendSP"),
160 ProtocolId::Unknown(id) => write!(f, "Unknown({id})"),
161 }
162 }
163}
164
165impl Default for ProtocolId {
166 fn default() -> Self {
167 ProtocolId::ResendSP
168 }
169}
170
171#[derive(Debug, Clone, PartialEq, Eq)]
176pub struct Frame {
177 pub session_id: u32,
179 pub channel_id: u16,
181 pub seq: u8,
183 pub frame_type: FrameType,
185 pub payload: Vec<u8>,
187}
188
189impl Frame {
190 pub fn new(
195 session_id: u32,
196 channel_id: u16,
197 seq: u8,
198 frame_type: FrameType,
199 payload: Vec<u8>,
200 ) -> Result<Self, FrameError> {
201 if payload.len() > MAX_PAYLOAD_SIZE {
202 return Err(FrameError::PayloadTooLarge(payload.len()));
203 }
204 Ok(Self { session_id, channel_id, seq, frame_type, payload })
205 }
206
207 pub fn encode(&self) -> Result<Vec<u8>, FrameError> {
212 encode_frame(self.session_id, self.channel_id, self.seq, self.frame_type, &self.payload)
213 }
214
215 pub fn wire_len(&self) -> usize {
217 HEADER_LEN + self.payload.len() + CHECKSUM_LEN
218 }
219}
220
221pub(crate) fn crc32(data: &[u8]) -> u32 {
223 crc32fast::hash(data)
224}
225
226pub(crate) fn crc8(data: &[u8]) -> u8 {
228 let mut crc: u8 = 0x00;
229 for &byte in data {
230 crc ^= byte;
231 for _ in 0..8 {
232 if (crc & 0x80) != 0 {
233 crc = (crc << 1) ^ 0x07;
234 } else {
235 crc <<= 1;
236 }
237 }
238 }
239 crc
240}
241
242pub fn encode_frame(
250 session_id: u32,
251 channel_id: u16,
252 seq: u8,
253 frame_type: FrameType,
254 payload: &[u8],
255) -> Result<Vec<u8>, FrameError> {
256 if payload.len() > MAX_PAYLOAD_SIZE {
257 return Err(FrameError::PayloadTooLarge(payload.len()));
258 }
259
260 let mut header = WireHeader {
261 sync: SYNC_WORD,
262 session_id: BeU32::new(session_id),
263 channel_id: BeU16::new(channel_id),
264 seq,
265 frame_type: frame_type.into(),
266 payload_len: BeU16::new(payload.len() as u16),
267 header_checksum: 0,
268 };
269 let header_bytes = header.as_bytes();
270 header.header_checksum = crc8(&header_bytes[SYNC_WORD_LEN..HEADER_CHECKSUM_OFFSET]);
271
272 let mut frame = Vec::with_capacity(HEADER_LEN + payload.len() + CHECKSUM_LEN);
273 frame.extend_from_slice(header.as_bytes());
274 frame.extend_from_slice(payload);
275
276 let cksum = crc32(&frame[SYNC_WORD_LEN..]);
277 frame.extend_from_slice(&cksum.to_be_bytes());
278
279 Ok(frame)
280}
281
282#[cfg(test)]
283mod tests {
284 use super::*;
285
286 #[fuchsia::test]
287 fn test_crc32() {
288 assert_eq!(crc32(b""), 0);
289 assert_eq!(crc32(b"123456789"), 0xCBF43926);
290 }
291
292 #[fuchsia::test]
293 fn test_crc8() {
294 assert_eq!(crc8(b""), 0);
295 assert_eq!(crc8(b"123456789"), 0xF4);
296 }
297
298 #[fuchsia::test]
299 fn test_encode_frame() {
300 let payload = b"hello";
301 let encoded = encode_frame(0x12345678, 1, 0, FrameType::Data, payload)
302 .expect("payload length is within MAX_PAYLOAD_SIZE");
303 assert_eq!(encoded[0], 0xAA);
304 assert_eq!(encoded[1], 0x55);
305 assert_eq!(encoded.len(), HEADER_LEN + payload.len() + CHECKSUM_LEN);
306 }
307
308 #[fuchsia::test]
309 fn test_encode_frame_payload_exceeding_max_payload_size() {
310 let oversized = vec![0u8; MAX_PAYLOAD_SIZE + 1];
311 let res = encode_frame(1, 1, 1, FrameType::Data, &oversized);
312 assert_eq!(res, Err(FrameError::PayloadTooLarge(MAX_PAYLOAD_SIZE + 1)));
313 }
314
315 #[fuchsia::test]
316 fn test_encode_frame_max_payload_boundary() {
317 let max_payload = vec![0x42u8; MAX_PAYLOAD_SIZE];
318 let encoded = encode_frame(1, 1, 1, FrameType::Data, &max_payload)
319 .expect("MAX_PAYLOAD_SIZE boundary must encode successfully");
320 assert_eq!(encoded.len(), HEADER_LEN + MAX_PAYLOAD_SIZE + CHECKSUM_LEN);
321 }
322
323 #[fuchsia::test]
324 fn test_frame_type_conversions() {
325 assert_eq!(FrameType::from(0x01), FrameType::Data);
326 assert_eq!(FrameType::from(0x02), FrameType::Ack);
327 assert_eq!(FrameType::from(0x03), FrameType::Close);
328 assert_eq!(FrameType::from(0x04), FrameType::Reset);
329 assert_eq!(FrameType::from(0x10), FrameType::NegotiateReq);
330 assert_eq!(FrameType::from(0x11), FrameType::NegotiateResp);
331 assert_eq!(FrameType::from(0x99), FrameType::Unknown(0x99));
332
333 assert_eq!(u8::from(FrameType::Data), 0x01);
334 assert_eq!(u8::from(FrameType::Ack), 0x02);
335 assert_eq!(u8::from(FrameType::Close), 0x03);
336 assert_eq!(u8::from(FrameType::Reset), 0x04);
337 assert_eq!(u8::from(FrameType::NegotiateReq), 0x10);
338 assert_eq!(u8::from(FrameType::NegotiateResp), 0x11);
339 assert_eq!(u8::from(FrameType::Unknown(0x99)), 0x99);
340
341 assert_eq!(FrameType::Data.to_string(), "Data");
342 assert_eq!(FrameType::Ack.to_string(), "Ack");
343 assert_eq!(FrameType::Close.to_string(), "Close");
344 assert_eq!(FrameType::Reset.to_string(), "Reset");
345 assert_eq!(FrameType::NegotiateReq.to_string(), "NegotiateReq");
346 assert_eq!(FrameType::NegotiateResp.to_string(), "NegotiateResp");
347 assert_eq!(FrameType::Unknown(0x99).to_string(), "Unknown(153)");
348 }
349
350 #[fuchsia::test]
351 fn test_protocol_id_conversions() {
352 assert_eq!(ProtocolId::from(1), ProtocolId::ResendSP);
353 assert_eq!(ProtocolId::from(42), ProtocolId::Unknown(42));
354
355 assert_eq!(ProtocolId::default(), ProtocolId::ResendSP);
356
357 assert_eq!(ProtocolId::ResendSP.wire_id(), 1);
358 assert_eq!(ProtocolId::Unknown(42).wire_id(), 42);
359
360 assert_eq!(ProtocolId::ResendSP.to_string(), "ResendSP");
361 assert_eq!(ProtocolId::Unknown(42).to_string(), "Unknown(42)");
362 }
363
364 #[fuchsia::test]
365 fn test_frame_inherent_methods() {
366 let payload = b"payload".to_vec();
367 let frame = Frame::new(42, CONTROL_CHANNEL_ID, 7, FrameType::Data, payload.clone())
368 .expect("valid frame creation");
369 assert_eq!(frame.session_id, 42);
370 assert_eq!(frame.channel_id, CONTROL_CHANNEL_ID);
371 assert_eq!(frame.seq, 7);
372 assert_eq!(frame.frame_type, FrameType::Data);
373 assert_eq!(frame.payload, payload);
374 assert_eq!(frame.wire_len(), HEADER_LEN + payload.len() + CHECKSUM_LEN);
375
376 let encoded = frame.encode().expect("encode succeeds");
377 assert_eq!(encoded.len(), frame.wire_len());
378
379 let oversized = vec![0u8; MAX_PAYLOAD_SIZE + 1];
380 let err = Frame::new(42, 1, 0, FrameType::Data, oversized).unwrap_err();
381 assert_eq!(err, FrameError::PayloadTooLarge(MAX_PAYLOAD_SIZE + 1));
382 }
383}