1use crate::DCPError;
4
5pub const HBTP_MAGIC: u32 = 0x48425450;
7
8#[repr(C, packed)]
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub struct HbtpHeader {
12 pub magic: u32,
14 pub version: u8,
16 pub msg_type: u8,
18 pub flags: u16,
20 pub stream_id: u32,
22 pub length: u32,
24 pub checksum: u32,
26}
27
28impl HbtpHeader {
29 pub const SIZE: usize = 20;
31
32 pub fn new(version: u8, msg_type: u8, flags: u16, stream_id: u32, length: u32) -> Self {
34 let mut header = Self {
35 magic: HBTP_MAGIC,
36 version,
37 msg_type,
38 flags,
39 stream_id,
40 length,
41 checksum: 0,
42 };
43 header.checksum = header.compute_checksum();
44 header
45 }
46
47 pub fn compute_checksum(&self) -> u32 {
49 let bytes = self.as_bytes();
50 crc32fast::hash(&bytes[..16])
52 }
53
54 pub fn verify_checksum(&self) -> bool {
56 self.checksum == self.compute_checksum()
57 }
58
59 #[inline(always)]
61 pub fn from_bytes(bytes: &[u8]) -> Result<&Self, DCPError> {
62 if bytes.len() < Self::SIZE {
63 return Err(DCPError::InsufficientData);
64 }
65 let header = unsafe { &*(bytes.as_ptr() as *const Self) };
67 if header.magic != HBTP_MAGIC {
68 return Err(DCPError::InvalidMagic);
69 }
70 Ok(header)
71 }
72
73 pub fn from_bytes_verified(bytes: &[u8]) -> Result<&Self, DCPError> {
75 let header = Self::from_bytes(bytes)?;
76 if !header.verify_checksum() {
77 return Err(DCPError::ChecksumMismatch);
78 }
79 Ok(header)
80 }
81
82 #[inline(always)]
84 pub fn as_bytes(&self) -> &[u8] {
85 unsafe { std::slice::from_raw_parts(self as *const Self as *const u8, Self::SIZE) }
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn test_header_size() {
96 assert_eq!(std::mem::size_of::<HbtpHeader>(), 20);
97 }
98
99 #[test]
100 fn test_header_round_trip() {
101 let header = HbtpHeader::new(1, 2, 0x0100, 12345, 4096);
102 let bytes = header.as_bytes();
103 let parsed = HbtpHeader::from_bytes(bytes).unwrap();
104
105 let magic = parsed.magic;
107 let version = parsed.version;
108 let msg_type = parsed.msg_type;
109 let flags = parsed.flags;
110 let stream_id = parsed.stream_id;
111 let length = parsed.length;
112
113 assert_eq!(magic, HBTP_MAGIC);
114 assert_eq!(version, 1);
115 assert_eq!(msg_type, 2);
116 assert_eq!(flags, 0x0100);
117 assert_eq!(stream_id, 12345);
118 assert_eq!(length, 4096);
119 assert!(parsed.verify_checksum());
120 }
121
122 #[test]
123 fn test_checksum_verification() {
124 let header = HbtpHeader::new(1, 2, 0, 0, 100);
125 assert!(header.verify_checksum());
126
127 let mut bytes = header.as_bytes().to_vec();
129 bytes[4] ^= 0xFF; let corrupted = HbtpHeader::from_bytes(&bytes).unwrap();
131 assert!(!corrupted.verify_checksum());
132 }
133
134 #[test]
135 fn test_invalid_magic() {
136 let bytes = [0u8; 20];
137 assert_eq!(HbtpHeader::from_bytes(&bytes), Err(DCPError::InvalidMagic));
138 }
139
140 #[test]
141 fn test_insufficient_data() {
142 let bytes = [0u8; 10];
143 assert_eq!(
144 HbtpHeader::from_bytes(&bytes),
145 Err(DCPError::InsufficientData)
146 );
147 }
148}