1use core::marker::PhantomData;
4
5pub struct TableDataRef<'a, H> {
10 data: &'a [u8],
11 _header: PhantomData<&'a H>,
12}
13
14impl<'a, H: bytemuck::AnyBitPattern> TableDataRef<'a, H> {
15 const HEADER_ALIGN_IS_ONE: () = assert!(
16 core::mem::align_of::<H>() == 1,
17 "a table header must be readable at any address"
18 );
19
20 #[inline(always)]
23 pub fn new(data: &'a [u8]) -> Option<Self> {
24 #[allow(clippy::let_unit_value)]
25 let _ = Self::HEADER_ALIGN_IS_ONE;
26 (data.len() >= core::mem::size_of::<H>()).then_some(Self {
27 data,
28 _header: PhantomData,
29 })
30 }
31
32 #[inline(always)]
34 pub fn header(&self) -> &'a H {
35 unsafe { &*self.data.as_ptr().cast::<H>() }
39 }
40
41 #[inline(always)]
43 pub fn data(&self) -> &'a [u8] {
44 self.data
45 }
46}
47
48impl<H> Clone for TableDataRef<'_, H> {
49 fn clone(&self) -> Self {
50 *self
51 }
52}
53
54impl<H> Copy for TableDataRef<'_, H> {}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59 use crate::BigEndian;
60
61 #[derive(Clone, Copy, bytemuck::AnyBitPattern)]
62 #[repr(C, packed)]
63 struct Header {
64 a: BigEndian<u16>,
65 b: BigEndian<u32>,
66 }
67
68 #[test]
69 fn reads_header() {
70 let data = [0, 1, 0, 0, 0, 2, 0xff];
71 let table = TableDataRef::<Header>::new(&data).unwrap();
72 assert_eq!(table.header().a.get(), 1);
73 assert_eq!(table.header().b.get(), 2);
74 assert_eq!(table.data().len(), 7);
75 }
76
77 #[test]
78 fn rejects_short_data() {
79 assert!(TableDataRef::<Header>::new(&[0, 1, 0, 0, 0]).is_none());
80 assert!(TableDataRef::<Header>::new(&[0, 1, 0, 0, 0, 2]).is_some());
81 }
82
83 #[test]
84 fn reads_unaligned_header() {
85 let data = [0xff, 0, 3, 0, 0, 0, 4];
86 let table = TableDataRef::<Header>::new(&data[1..]).unwrap();
87 assert_eq!(table.header().a.get(), 3);
88 assert_eq!(table.header().b.get(), 4);
89 }
90}