use crate::DCPError;
pub const HBTP_MAGIC: u32 = 0x48425450;
#[repr(C, packed)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct HbtpHeader {
pub magic: u32,
pub version: u8,
pub msg_type: u8,
pub flags: u16,
pub stream_id: u32,
pub length: u32,
pub checksum: u32,
}
impl HbtpHeader {
pub const SIZE: usize = 20;
pub fn new(version: u8, msg_type: u8, flags: u16, stream_id: u32, length: u32) -> Self {
let mut header = Self {
magic: HBTP_MAGIC,
version,
msg_type,
flags,
stream_id,
length,
checksum: 0,
};
header.checksum = header.compute_checksum();
header
}
pub fn compute_checksum(&self) -> u32 {
let bytes = self.as_bytes();
crc32fast::hash(&bytes[..16])
}
pub fn verify_checksum(&self) -> bool {
self.checksum == self.compute_checksum()
}
#[inline(always)]
pub fn from_bytes(bytes: &[u8]) -> Result<&Self, DCPError> {
if bytes.len() < Self::SIZE {
return Err(DCPError::InsufficientData);
}
let header = unsafe { &*(bytes.as_ptr() as *const Self) };
if header.magic != HBTP_MAGIC {
return Err(DCPError::InvalidMagic);
}
Ok(header)
}
pub fn from_bytes_verified(bytes: &[u8]) -> Result<&Self, DCPError> {
let header = Self::from_bytes(bytes)?;
if !header.verify_checksum() {
return Err(DCPError::ChecksumMismatch);
}
Ok(header)
}
#[inline(always)]
pub fn as_bytes(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self as *const Self as *const u8, Self::SIZE) }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_header_size() {
assert_eq!(std::mem::size_of::<HbtpHeader>(), 20);
}
#[test]
fn test_header_round_trip() {
let header = HbtpHeader::new(1, 2, 0x0100, 12345, 4096);
let bytes = header.as_bytes();
let parsed = HbtpHeader::from_bytes(bytes).unwrap();
let magic = parsed.magic;
let version = parsed.version;
let msg_type = parsed.msg_type;
let flags = parsed.flags;
let stream_id = parsed.stream_id;
let length = parsed.length;
assert_eq!(magic, HBTP_MAGIC);
assert_eq!(version, 1);
assert_eq!(msg_type, 2);
assert_eq!(flags, 0x0100);
assert_eq!(stream_id, 12345);
assert_eq!(length, 4096);
assert!(parsed.verify_checksum());
}
#[test]
fn test_checksum_verification() {
let header = HbtpHeader::new(1, 2, 0, 0, 100);
assert!(header.verify_checksum());
let mut bytes = header.as_bytes().to_vec();
bytes[4] ^= 0xFF; let corrupted = HbtpHeader::from_bytes(&bytes).unwrap();
assert!(!corrupted.verify_checksum());
}
#[test]
fn test_invalid_magic() {
let bytes = [0u8; 20];
assert_eq!(HbtpHeader::from_bytes(&bytes), Err(DCPError::InvalidMagic));
}
#[test]
fn test_insufficient_data() {
let bytes = [0u8; 10];
assert_eq!(
HbtpHeader::from_bytes(&bytes),
Err(DCPError::InsufficientData)
);
}
}