muxio_core/frame/frame_codec.rs
1use crate::{
2 constants::{
3 FRAME_HEADER_SIZE, FRAME_KIND_OFFSET, FRAME_LENGTH_FIELD_SIZE, FRAME_SEQ_ID_OFFSET,
4 FRAME_STREAM_ID_OFFSET, FRAME_TIMESTAMP_OFFSET,
5 },
6 frame::{DecodedFrame, Frame, FrameDecodeError, FrameKind},
7};
8
9/// Provides encoding and decoding functionality for frames.
10///
11/// The `FrameCodec` is responsible for serializing a `Frame` into a byte stream and
12/// deserializing a byte stream back into a `Frame`. It handles the creation and parsing
13/// of the frame header, ensuring the correct encoding of all frame fields such as stream ID,
14/// sequence ID, kind, timestamp, and payload.
15///
16/// This is used to package and unpack frames for transmission across a stream, ensuring
17/// that all necessary metadata is included alongside the actual payload.
18pub struct FrameCodec;
19
20impl FrameCodec {
21 /// Encodes a `Frame` into a byte vector.
22 ///
23 /// This method serializes the `Frame`'s metadata and payload into a byte stream.
24 /// The resulting vector can be transmitted over the stream or saved to a buffer.
25 ///
26 /// # Arguments
27 ///
28 /// * `frame` - The `Frame` to be encoded.
29 ///
30 /// # Returns
31 ///
32 /// Returns a vector of bytes that represents the encoded frame. The frame consists
33 /// of the frame's length, stream ID, sequence ID, kind, timestamp, and payload.
34 pub fn encode(frame: &Frame) -> Vec<u8> {
35 let mut buf = Vec::with_capacity(FRAME_HEADER_SIZE + frame.payload.len());
36
37 // Add the frame length (payload length in bytes)
38 buf.extend(&(frame.payload.len() as u32).to_le_bytes());
39
40 // Add the stream ID, sequence ID, frame kind, timestamp, and payload
41 buf.extend(&frame.stream_id.to_le_bytes());
42 buf.extend(&frame.seq_id.to_le_bytes());
43 buf.push(frame.kind as u8);
44 buf.extend(&frame.timestamp_micros.to_le_bytes());
45 buf.extend(&frame.payload);
46
47 buf
48 }
49
50 /// Decodes a byte slice into a `Frame`.
51 ///
52 /// This method takes a byte buffer representing a serialized frame and attempts to parse it
53 /// back into a `Frame` struct. It checks the integrity of the frame, ensuring that the buffer
54 /// contains enough data and that all fields can be correctly interpreted.
55 ///
56 /// # Arguments
57 ///
58 /// * `buf` - A byte slice representing the frame to decode.
59 ///
60 /// # Returns
61 ///
62 /// Returns a `Result` where:
63 /// - `Ok(Frame)` contains the decoded `Frame` object.
64 /// - `Err(FrameStreamError)` contains an error if the frame is corrupted or malformed.
65 ///
66 /// The method will return an error if the buffer is too short or if the frame data does not
67 /// conform to the expected structure.
68 pub fn decode(buf: &[u8]) -> Result<DecodedFrame, FrameDecodeError> {
69 if buf.len() < FRAME_HEADER_SIZE {
70 return Err(FrameDecodeError::IncompleteHeader); // Not enough data to form a valid frame
71 }
72
73 // Extract the length of the payload
74 let len = u32::from_le_bytes(
75 buf[0..FRAME_LENGTH_FIELD_SIZE]
76 .try_into()
77 .map_err(|_| FrameDecodeError::CorruptFrame)?,
78 ) as usize;
79
80 // Ensure the buffer contains enough data for the frame (header + payload)
81 if buf.len() < FRAME_HEADER_SIZE + len {
82 return Err(FrameDecodeError::IncompleteHeader); // Frame size mismatch
83 }
84
85 // Parse the stream ID, sequence ID, frame kind, and timestamp
86 let stream_id = u32::from_le_bytes(
87 buf[FRAME_STREAM_ID_OFFSET..FRAME_SEQ_ID_OFFSET]
88 .try_into()
89 .map_err(|_| FrameDecodeError::CorruptFrame)?,
90 );
91 let seq_id = u32::from_le_bytes(
92 buf[FRAME_SEQ_ID_OFFSET..FRAME_KIND_OFFSET]
93 .try_into()
94 .map_err(|_| FrameDecodeError::CorruptFrame)?,
95 );
96 let kind = FrameKind::try_from(buf[FRAME_KIND_OFFSET])
97 .map_err(|_| FrameDecodeError::CorruptFrame)?; // Map error to FrameStreamError
98
99 // Extract the timestamp and payload
100 let timestamp = u64::from_le_bytes(
101 buf[FRAME_TIMESTAMP_OFFSET..FRAME_HEADER_SIZE]
102 .try_into()
103 .map_err(|_| FrameDecodeError::CorruptFrame)?,
104 );
105
106 // Discard payload if canceled frame
107 let payload = match kind {
108 FrameKind::Cancel => vec![],
109 _ => buf[FRAME_HEADER_SIZE..FRAME_HEADER_SIZE + len].to_vec(),
110 };
111
112 let frame = Frame {
113 stream_id,
114 seq_id,
115 kind,
116 timestamp_micros: timestamp,
117 payload,
118 };
119
120 // Return the decoded frame
121 let decoded_frame = DecodedFrame {
122 inner: frame,
123 decode_error: None,
124 };
125
126 Ok(decoded_frame)
127 }
128}