use super::crc32::calculate_crc32;
use super::PubSubError;
use super::Result;
const MAGIC_WORD: u32 = 0x55AA55AA;
const PROTOCOL_VERSION: u8 = 1;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FrameType {
Data = 0x01,
Nack = 0x02,
Heartbeat = 0x03,
}
impl From<u8> for FrameType {
fn from(value: u8) -> Self {
match value {
0x01 => FrameType::Data,
0x02 => FrameType::Nack,
0x03 => FrameType::Heartbeat,
_ => FrameType::Data, }
}
}
#[derive(Debug, Clone)]
pub struct ProtocolFrame {
magic: u32,
version: u8,
frame_type: FrameType,
seq_num: u32,
topic_id: u16,
data_len: u16,
crc32: u32,
payload: alloc::vec::Vec<u8>,
}
impl ProtocolFrame {
pub const HEADER_SIZE: usize = 4 + 1 + 1 + 4 + 2 + 2 + 4;
pub const MAX_DATA_LEN: usize = 4096;
pub const MAX_FRAME_SIZE: usize = Self::HEADER_SIZE + Self::MAX_DATA_LEN;
pub fn new_data_frame(seq_num: u32, topic_id: u16, data: &[u8]) -> Result<Self> {
if data.len() > Self::MAX_DATA_LEN {
return Err(PubSubError::InvalidParameter);
}
let crc32 = calculate_crc32(data);
Ok(Self {
magic: MAGIC_WORD,
version: PROTOCOL_VERSION,
frame_type: FrameType::Data,
seq_num,
topic_id,
data_len: data.len() as u16,
crc32,
payload: data.to_vec(),
})
}
pub fn new_nack_frame(seq_num: u32, topic_id: u16) -> Self {
Self {
magic: MAGIC_WORD,
version: PROTOCOL_VERSION,
frame_type: FrameType::Nack,
seq_num,
topic_id,
data_len: 0,
crc32: 0, payload: alloc::vec::Vec::new(),
}
}
pub fn new_heartbeat_frame() -> Self {
Self {
magic: MAGIC_WORD,
version: PROTOCOL_VERSION,
frame_type: FrameType::Heartbeat,
seq_num: 0,
topic_id: 0,
data_len: 0,
crc32: 0, payload: alloc::vec::Vec::new(),
}
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
if bytes.len() < Self::HEADER_SIZE {
return Err(PubSubError::InvalidFrameFormat);
}
let magic = u32::from_le_bytes(
bytes[0..4]
.try_into()
.map_err(|_| PubSubError::InvalidFrameFormat)?,
);
if magic != MAGIC_WORD {
return Err(PubSubError::InvalidFrameFormat);
}
let version = bytes[4];
if version != PROTOCOL_VERSION {
return Err(PubSubError::InvalidFrameFormat);
}
let frame_type = FrameType::from(bytes[5]);
let seq_num = u32::from_le_bytes(
bytes[6..10]
.try_into()
.map_err(|_| PubSubError::InvalidFrameFormat)?,
);
let topic_id = u16::from_le_bytes(
bytes[10..12]
.try_into()
.map_err(|_| PubSubError::InvalidFrameFormat)?,
);
let data_len = u16::from_le_bytes(
bytes[12..14]
.try_into()
.map_err(|_| PubSubError::InvalidFrameFormat)?,
);
if bytes.len() < Self::HEADER_SIZE + data_len as usize {
return Err(PubSubError::InvalidFrameFormat);
}
let crc32 = u32::from_le_bytes(
bytes[14..18]
.try_into()
.map_err(|_| PubSubError::InvalidFrameFormat)?,
);
let payload = bytes[18..18 + data_len as usize].to_vec();
if frame_type == FrameType::Data {
let calculated_crc = calculate_crc32(&payload);
if calculated_crc != crc32 {
return Err(PubSubError::CrcCheckFailed);
}
}
Ok(Self {
magic,
version,
frame_type,
seq_num,
topic_id,
data_len,
crc32,
payload,
})
}
pub fn to_bytes(&self) -> alloc::vec::Vec<u8> {
let mut bytes = alloc::vec::Vec::with_capacity(Self::HEADER_SIZE + self.data_len as usize);
bytes.extend_from_slice(&self.magic.to_le_bytes());
bytes.push(self.version);
bytes.push(self.frame_type as u8);
bytes.extend_from_slice(&self.seq_num.to_le_bytes());
bytes.extend_from_slice(&self.topic_id.to_le_bytes());
bytes.extend_from_slice(&self.data_len.to_le_bytes());
bytes.extend_from_slice(&self.crc32.to_le_bytes());
bytes.extend_from_slice(&self.payload);
bytes
}
pub fn frame_type(&self) -> FrameType {
self.frame_type
}
pub fn seq_num(&self) -> u32 {
self.seq_num
}
pub fn topic_id(&self) -> u16 {
self.topic_id
}
pub fn payload(&self) -> &[u8] {
&self.payload
}
pub fn data_len(&self) -> u16 {
self.data_len
}
}