use std::ops::Deref;
use std::sync::Arc;
pub type Backing = Arc<dyn AsRef<[u8]> + Send + Sync>;
pub(crate) trait Pod: bytemuck::Pod + Send + Sync {}
impl Pod for u16 {}
impl Pod for u32 {}
impl Pod for u64 {}
impl Pod for [u32; 2] {}
pub(crate) enum Table<T: Pod> {
Owned(Vec<T>),
Mapped { backing: Backing, off: usize, len: usize },
}
impl<T: Pod> Table<T> {
pub(crate) fn view(backing: &Backing, off: usize, len: usize) -> Option<Table<T>> {
let bytes = (**backing).as_ref();
let end = len.checked_mul(std::mem::size_of::<T>())?.checked_add(off)?;
bytemuck::try_cast_slice::<u8, T>(bytes.get(off..end)?).ok()?;
Some(Table::Mapped { backing: Arc::clone(backing), off, len })
}
pub(crate) fn heap_bytes(&self) -> usize {
match self {
Table::Owned(v) => v.capacity() * std::mem::size_of::<T>(),
Table::Mapped { .. } => 0,
}
}
pub(crate) fn mapped_bytes(&self) -> usize {
match self {
Table::Owned(_) => 0,
Table::Mapped { len, .. } => len * std::mem::size_of::<T>(),
}
}
pub(crate) fn as_bytes(&self) -> &[u8] {
bytemuck::cast_slice(self)
}
}
impl<T: Pod> Clone for Table<T> {
fn clone(&self) -> Self {
match self {
Table::Owned(v) => Table::Owned(v.clone()),
Table::Mapped { backing, off, len } => Table::Mapped { backing: Arc::clone(backing), off: *off, len: *len },
}
}
}
impl<T: Pod + PartialEq> PartialEq for Table<T> {
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}
impl<T: Pod + Eq> Eq for Table<T> {}
impl<T: Pod + std::fmt::Debug> std::fmt::Debug for Table<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
impl<T: Pod> Default for Table<T> {
fn default() -> Self {
Table::Owned(Vec::new())
}
}
impl<T: Pod> From<Vec<T>> for Table<T> {
fn from(v: Vec<T>) -> Self {
Table::Owned(v)
}
}
impl<T: Pod> Deref for Table<T> {
type Target = [T];
fn deref(&self) -> &[T] {
match self {
Table::Owned(v) => v,
Table::Mapped { backing, off, len } => {
bytemuck::cast_slice(&(**backing).as_ref()[*off..*off + *len * std::mem::size_of::<T>()])
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn views_check_bounds_and_alignment() {
let words: Vec<u32> = vec![1, 2, 3, 4];
let bytes: Vec<u8> = words.iter().flat_map(|w| w.to_ne_bytes()).collect();
struct Words(Vec<u32>);
impl AsRef<[u8]> for Words {
fn as_ref(&self) -> &[u8] {
bytemuck::cast_slice(&self.0)
}
}
let backing: Backing = Arc::new(Words(words.clone()));
let t = Table::<u32>::view(&backing, 4, 3).unwrap();
assert_eq!(&*t, &[2, 3, 4]);
assert_eq!(t.heap_bytes(), 0);
assert_eq!(t.as_bytes(), &bytes[4..]);
let pairs = Table::<[u32; 2]>::view(&backing, 0, 2).unwrap();
assert_eq!(&*pairs, &[[1, 2], [3, 4]]);
assert!(Table::<u32>::view(&backing, 4, 4).is_none(), "past the end");
assert!(Table::<u32>::view(&backing, 2, 1).is_none(), "misaligned");
assert!(Table::<u32>::view(&backing, usize::MAX, 1).is_none(), "overflow");
let owned: Table<u32> = vec![7u32; 3].into();
assert_eq!(owned.heap_bytes(), 12);
}
}