Skip to main content

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}