1use core::borrow::Borrow;
4use core::fmt;
5use core::ops::{Deref, Range};
6
7use crate::checksum::Checksum;
8use crate::flags::Flags;
9use crate::payload::Payload;
10use crate::seq::{FrameNum, FrameNumSequence};
11use crate::util::Error;
12use crate::wants::Wants;
13
14const VERSION_MASK: u64 = 0x0000_0000_0000_00ff;
15const VERSION_SHIFT: u32 = 0;
16
17const FLAGS_MASK: u64 = 0x0000_0000_0000_0300;
18const FLAGS_SHIFT: u32 = 8;
19
20const FRAME_NUM_MASK: u64 = 0x0000_0000_0000_fc00;
21const FRAME_NUM_SHIFT: u32 = 10;
22
23const CHECKSUM_FIELD: Range<usize> = 2..4;
24
25const DATA_LEN_MASK: u64 = 0xffff_ffff_0000_0000;
26const DATA_LEN_SHIFT: u32 = 32;
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub struct Header {
31 pub flags: Flags,
33 pub frame_num: FrameNum,
35 pub data_len: u32,
37}
38
39impl Header {
40 const VERSION: u8 = 0x42;
41
42 pub const SIZE: usize = 8;
44
45 #[inline]
47 pub const fn builder() -> Builder {
48 Builder {
49 flags: Flags::empty(),
50 frame_num: FrameNum::new(0),
51 data_len: 0,
52 }
53 }
54
55 #[inline]
57 #[must_use]
58 #[allow(clippy::cast_lossless)]
59 pub fn to_bytes(self) -> HeaderBytes {
60 let x = ((Header::VERSION as u64) << VERSION_SHIFT) | ((0u8 as u64) << FLAGS_SHIFT) | ((self.frame_num.get() as u64) << FRAME_NUM_SHIFT) | ((self.data_len as u64) << DATA_LEN_SHIFT); let mut data = u64::to_le_bytes(x);
66
67 let checksum = Checksum::checksum(&data);
68 data[CHECKSUM_FIELD].copy_from_slice(&checksum.to_ne_bytes());
69
70 HeaderBytes { data }
71 }
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
76pub enum HeaderError {
77 InvalidChecksum,
79 VersionMismatch,
81}
82
83impl fmt::Display for HeaderError {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 match *self {
86 Self::InvalidChecksum => f.write_str("invalid checksum"),
87 Self::VersionMismatch => f.write_str("version mismatch"),
88 }
89 }
90}
91
92impl Error for HeaderError {}
93
94impl Header {
95 #[must_use = "unused parse result"]
103 #[allow(
104 clippy::missing_panics_doc,
105 clippy::cast_possible_truncation
106 )]
107 pub fn try_parse(
108 bytes: &[u8],
109 ) -> Result<Result<Self, Wants>, HeaderError> {
110 let Some(hdr_bytes) = bytes.get(..Self::SIZE) else {
111 return Ok(Err(Wants(Self::SIZE - bytes.len())));
112 };
113
114 let hdr_bytes: &[u8; Self::SIZE] = hdr_bytes
115 .try_into()
116 .expect("cast header slice to array failed");
117
118 let hdr = u64::from_le_bytes(*hdr_bytes);
119
120 let version = ((hdr & VERSION_MASK) >> VERSION_SHIFT) as u8;
121
122 let flags = Flags::from_bits(
123 ((hdr & FLAGS_MASK) >> FLAGS_SHIFT) as u8,
124 );
125
126 let frame_num = FrameNum::new(
127 ((hdr & FRAME_NUM_MASK) >> FRAME_NUM_SHIFT) as u8,
128 );
129
130 let data_len =
131 ((hdr & DATA_LEN_MASK) >> DATA_LEN_SHIFT) as u32;
132
133 if version != Self::VERSION {
134 return Err(HeaderError::VersionMismatch);
135 }
136
137 if Checksum::checksum(hdr_bytes) != 0 {
138 return Err(HeaderError::InvalidChecksum);
139 }
140
141 Ok(Ok(Self { flags, frame_num, data_len }))
142 }
143}
144
145#[allow(missing_debug_implementations)]
147#[must_use = "builders don't do anything unless you build them"]
148pub struct Builder {
149 flags: Flags,
150 frame_num: FrameNum,
151 data_len: u32,
152}
153
154impl Builder {
155 #[inline]
157 pub const fn flags(mut self, flags: Flags) -> Self {
158 self.flags = flags;
159 self
160 }
161
162 #[inline]
164 pub const fn frame_num(mut self, frame_num: FrameNum) -> Self {
165 self.frame_num = frame_num;
166 self
167 }
168
169 #[inline]
175 pub fn frame_num_from_seq(
176 self,
177 seq: &mut FrameNumSequence,
178 ) -> Self {
179 self.frame_num(seq.advance())
180 }
181
182 #[inline]
184 pub const fn data_len(mut self, data_len: u32) -> Self {
185 self.data_len = data_len;
186 self
187 }
188
189 #[inline]
191 pub fn data_len_from_payload<T: AsRef<[u8]>>(
192 self,
193 payload: &Payload<T>,
194 ) -> Self {
195 self.data_len(payload.length())
196 }
197
198 #[inline]
200 #[must_use]
201 pub const fn build(self) -> Header {
202 let Self { flags, frame_num, data_len } = self;
203 Header { flags, frame_num, data_len }
204 }
205}
206
207#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
209pub struct HeaderBytes {
210 data: [u8; Header::SIZE],
211}
212
213impl HeaderBytes {
214 const fn as_slice(&self) -> &[u8] {
215 self.data.as_slice()
216 }
217}
218
219impl Borrow<[u8]> for HeaderBytes {
220 #[inline]
221 fn borrow(&self) -> &[u8] {
222 self.as_slice()
223 }
224}
225
226impl AsRef<[u8]> for HeaderBytes {
227 #[inline]
228 fn as_ref(&self) -> &[u8] {
229 self.as_slice()
230 }
231}
232
233impl Deref for HeaderBytes {
234 type Target = [u8];
235
236 #[inline]
237 fn deref(&self) -> &Self::Target {
238 self.as_slice()
239 }
240}
241
242#[cfg(test)]
243#[allow(clippy::unusual_byte_groupings)]
244mod tests {
245 use super::*;
246
247 struct Vector {
248 header: Header,
249 bytes: [u8; Header::SIZE],
250 }
251
252 #[rustfmt::skip]
253 static TEST_VECTORS: &[Vector] = &[
254 Vector {
255 header: Header {
256 flags: Flags::empty(),
257 frame_num: FrameNum::new(32),
258 data_len: 0,
259 },
260 bytes: [0x42, 0b100000_00, 0xbd, 0x7f, 0, 0, 0, 0],
261 },
262 Vector {
263 header: Header {
264 flags: Flags::empty(),
265 frame_num: FrameNum::new(34),
266 data_len: 42,
267 },
268 bytes: [0x42, 0b100010_00, 0x93, 0x77, 42, 0, 0, 0],
269 },
270 Vector {
271 header: Header {
272 flags: Flags::empty(),
273 frame_num: FrameNum::new(23),
274 data_len: 0xffff,
275 },
276 bytes: [0x42, 0b010111_00, 0xbd, 0xa3, 0xff, 0xff, 0, 0],
277 },
278 Vector {
279 header: Header {
280 flags: Flags::empty(),
281 data_len: 0x0001_0000,
282 frame_num: FrameNum::new(5),
283 },
284 bytes: [0x42, 0b000101_00, 0xbc, 0xeb, 0x00, 0x00, 0x01, 0x00],
285 },
286 Vector {
287 header: Header {
288 flags: Flags::empty(),
289 data_len: 0xffff_ffff,
290 frame_num: FrameNum::new(14),
291 },
292 bytes: [0x42, 0b001110_00, 0xbd, 0xc7, 0xff, 0xff, 0xff, 0xff],
293 },
294 ];
295
296 macro_rules! tests {
297 ($($test_vector_idx:expr => $test_encode:ident, $test_decode:ident,)*) => {
298 $(
299 #[test]
300 fn $test_encode() {
301 let Vector { header, bytes } = TEST_VECTORS[$test_vector_idx];
302 let buf = header.to_bytes();
303 assert_eq!(buf.as_ref(), bytes);
304 }
305
306 #[test]
307 fn $test_decode() {
308 let Vector { header, bytes } = TEST_VECTORS[$test_vector_idx];
309 let x = Header::try_parse(&bytes).unwrap().unwrap();
310 assert_eq!(header, x);
311 }
312 )*
313 };
314 }
315
316 tests! {
317 0 => test_header_encode_no_payload, test_header_decode_no_payload,
318 1 => test_header_encode_small_payload, test_header_decode_small_payload,
319 2 => test_header_encode_small_medium_payload, test_header_decode_small_medium_payload,
320 3 => test_header_encode_medium_payload, test_header_decode_medium_payload,
321 4 => test_header_encode_medium_large_payload, test_header_decode_medium_large_payload,
322 }
323
324 #[test]
325 fn test_header_decode_invalid_version() {
326 let bytes: &[u8] =
327 &[0x43, 0b000000_00, 0x00, 0x00, 0, 0, 0, 0];
328 assert_eq!(
329 Header::try_parse(bytes),
330 Err(HeaderError::VersionMismatch)
331 );
332 }
333
334 #[test]
335 fn test_header_decode_invalid_checksum() {
336 let bytes: &[u8] =
337 &[0x42, 0b000100_01, 0xCC, 0xCC, 0x23, 0x00, 0x00, 0x00];
338 assert_eq!(
339 Header::try_parse(bytes),
340 Err(HeaderError::InvalidChecksum)
341 );
342 }
343
344 #[test]
345 fn test_header_decode_not_enough() {
346 const HEADERS: &[&[u8]] = &[
347 &[],
348 &[0x42],
349 &[0x42, 0b010101_01],
350 &[0x42, 0b010101_01, 0xCC],
351 &[0x42, 0b010101_01, 0xCC, 0xCC],
352 &[0x42, 0b010101_01, 0xCC, 0xCC, 0x00],
353 &[0x42, 0b010101_11, 0xCC, 0xCC, 0x00, 0x00],
354 &[0x42, 0b010101_11, 0xCC, 0xCC, 0x00, 0x00, 0x00],
355 ];
356
357 HEADERS.iter().copied().for_each(|bytes| {
358 assert!(
359 matches!(Header::try_parse(bytes), Ok(Err(_))),
360 "fail"
361 );
362 });
363 }
364}