use crate::{
constants::{
FRAME_HEADER_SIZE, FRAME_KIND_OFFSET, FRAME_LENGTH_FIELD_SIZE, FRAME_SEQ_ID_OFFSET,
FRAME_STREAM_ID_OFFSET, FRAME_TIMESTAMP_OFFSET,
},
frame::{DecodedFrame, Frame, FrameDecodeError, FrameKind},
};
pub struct FrameCodec;
impl FrameCodec {
pub fn encode(frame: &Frame) -> Vec<u8> {
let mut buf = Vec::with_capacity(FRAME_HEADER_SIZE + frame.payload.len());
buf.extend(&(frame.payload.len() as u32).to_le_bytes());
buf.extend(&frame.stream_id.to_le_bytes());
buf.extend(&frame.seq_id.to_le_bytes());
buf.push(frame.kind as u8);
buf.extend(&frame.timestamp_micros.to_le_bytes());
buf.extend(&frame.payload);
buf
}
pub fn decode(buf: &[u8]) -> Result<DecodedFrame, FrameDecodeError> {
if buf.len() < FRAME_HEADER_SIZE {
return Err(FrameDecodeError::IncompleteHeader); }
let len = u32::from_le_bytes(
buf[0..FRAME_LENGTH_FIELD_SIZE]
.try_into()
.map_err(|_| FrameDecodeError::CorruptFrame)?,
) as usize;
if buf.len() < FRAME_HEADER_SIZE + len {
return Err(FrameDecodeError::IncompleteHeader); }
let stream_id = u32::from_le_bytes(
buf[FRAME_STREAM_ID_OFFSET..FRAME_SEQ_ID_OFFSET]
.try_into()
.map_err(|_| FrameDecodeError::CorruptFrame)?,
);
let seq_id = u32::from_le_bytes(
buf[FRAME_SEQ_ID_OFFSET..FRAME_KIND_OFFSET]
.try_into()
.map_err(|_| FrameDecodeError::CorruptFrame)?,
);
let kind = FrameKind::try_from(buf[FRAME_KIND_OFFSET])
.map_err(|_| FrameDecodeError::CorruptFrame)?;
let timestamp = u64::from_le_bytes(
buf[FRAME_TIMESTAMP_OFFSET..FRAME_HEADER_SIZE]
.try_into()
.map_err(|_| FrameDecodeError::CorruptFrame)?,
);
let payload = match kind {
FrameKind::Cancel => vec![],
_ => buf[FRAME_HEADER_SIZE..FRAME_HEADER_SIZE + len].to_vec(),
};
let frame = Frame {
stream_id,
seq_id,
kind,
timestamp_micros: timestamp,
payload,
};
let decoded_frame = DecodedFrame {
inner: frame,
decode_error: None,
};
Ok(decoded_frame)
}
}