use core::marker::PhantomData;
pub struct TableDataRef<'a, H> {
data: &'a [u8],
_header: PhantomData<&'a H>,
}
impl<'a, H: bytemuck::AnyBitPattern> TableDataRef<'a, H> {
const HEADER_ALIGN_IS_ONE: () = assert!(
core::mem::align_of::<H>() == 1,
"a table header must be readable at any address"
);
#[inline(always)]
pub fn new(data: &'a [u8]) -> Option<Self> {
#[allow(clippy::let_unit_value)]
let _ = Self::HEADER_ALIGN_IS_ONE;
(data.len() >= core::mem::size_of::<H>()).then_some(Self {
data,
_header: PhantomData,
})
}
#[inline(always)]
pub fn header(&self) -> &'a H {
unsafe { &*self.data.as_ptr().cast::<H>() }
}
#[inline(always)]
pub fn data(&self) -> &'a [u8] {
self.data
}
}
impl<H> Clone for TableDataRef<'_, H> {
fn clone(&self) -> Self {
*self
}
}
impl<H> Copy for TableDataRef<'_, H> {}
#[cfg(test)]
mod tests {
use super::*;
use crate::BigEndian;
#[derive(Clone, Copy, bytemuck::AnyBitPattern)]
#[repr(C, packed)]
struct Header {
a: BigEndian<u16>,
b: BigEndian<u32>,
}
#[test]
fn reads_header() {
let data = [0, 1, 0, 0, 0, 2, 0xff];
let table = TableDataRef::<Header>::new(&data).unwrap();
assert_eq!(table.header().a.get(), 1);
assert_eq!(table.header().b.get(), 2);
assert_eq!(table.data().len(), 7);
}
#[test]
fn rejects_short_data() {
assert!(TableDataRef::<Header>::new(&[0, 1, 0, 0, 0]).is_none());
assert!(TableDataRef::<Header>::new(&[0, 1, 0, 0, 0, 2]).is_some());
}
#[test]
fn reads_unaligned_header() {
let data = [0xff, 0, 3, 0, 0, 0, 4];
let table = TableDataRef::<Header>::new(&data[1..]).unwrap();
assert_eq!(table.header().a.get(), 3);
assert_eq!(table.header().b.get(), 4);
}
}