use std::{marker::PhantomData, ptr::null};
#[derive(Copy, Clone, PartialEq)]
pub enum SliceError {
OffsetOutOfBounds {
size: usize,
offset: usize,
},
AttributeLargerThanStride {
type_name: &'static str,
attr: usize,
stride: usize,
},
AlignmentFault {
type_name: &'static str,
offset: usize,
},
}
impl std::fmt::Debug for SliceError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::OffsetOutOfBounds { size, offset } => {
write!(
f,
"Byte offset is {}, but slice has a size of {} bytes",
offset, size
)
}
Self::AttributeLargerThanStride {
type_name,
attr,
stride,
} => {
write!(
f,
"Attribute '{:?}' with size {} bytes, larger than stride with size {}",
type_name, attr, stride
)
}
Self::AlignmentFault { type_name, offset } => write!(
f,
"Attribute '{:?}' isn't aligned to the byte offset {}",
type_name, offset
),
}
}
}
#[derive(Clone, Copy)]
pub struct SliceBase<Attr: Sized + 'static> {
pub(crate) start: *const u8,
pub(crate) end: *const u8,
stride: usize,
_phantom: PhantomData<Attr>,
}
impl<Attr: Sized> SliceBase<Attr> {
pub(crate) fn new_typed<V: Pod>(
data: &[V],
offset: usize,
elt_count: usize,
) -> Result<Self, SliceError> {
let stride = std::mem::size_of::<V>() * elt_count;
let bytes = std::mem::size_of_val(data);
let ptr = data.as_ptr_range();
Self::new(
ptr.start as *const u8..ptr.end as *const u8,
offset,
stride,
bytes,
)
}
pub(crate) fn new(
ptr_range: std::ops::Range<*const u8>,
offset: usize,
stride: usize,
bytes: usize,
) -> Result<Self, SliceError> {
let ptr: *const u8 = unsafe { ptr_range.start.add(offset) };
if std::mem::size_of::<Attr>() > stride {
Err(SliceError::AttributeLargerThanStride {
type_name: std::any::type_name::<Attr>(),
attr: std::mem::size_of::<Attr>(),
stride,
})
} else if offset > 0 && offset >= bytes {
Err(SliceError::OffsetOutOfBounds {
size: bytes,
offset,
})
} else if ptr.align_offset(std::mem::align_of::<Attr>()) != 0 {
Err(SliceError::AlignmentFault {
type_name: std::any::type_name::<Attr>(),
offset,
})
} else {
Ok(Self {
start: ptr,
end: ptr_range.end,
stride,
_phantom: PhantomData,
})
}
}
pub fn get(&self, index: usize) -> Option<&Attr> {
self.get_ptr(index)
.map(|ptr| unsafe { &*ptr.cast::<Attr>() })
}
pub fn len(&self) -> usize {
(self.end as usize)
.checked_sub(self.start as usize)
.unwrap()
.div_ceil(self.stride)
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn as_ptr(&self) -> *const u8 {
self.start
}
pub(crate) fn get_ptr(&self, index: usize) -> Option<*const u8> {
if index < self.len() {
let start = self.stride * index;
Some(unsafe { self.start.add(start) })
} else {
None
}
}
pub fn stride(&self) -> usize {
self.stride
}
}
impl<Attr: Sized + 'static> Default for SliceBase<Attr> {
fn default() -> Self {
Self {
start: null(),
end: null(),
stride: 0,
_phantom: PhantomData,
}
}
}
macro_rules! impl_iterator {
($name: ident -> $elem: ty) => {
impl<'a, T: Pod> Iterator for $name<'a, T> {
type Item = $elem;
fn next(&mut self) -> Option<$elem> {
if self.start >= self.end {
return None;
}
unsafe {
let ret = Some(std::mem::transmute::<_, $elem>(self.start));
self.start = self.start.add(self.stride);
ret
}
}
fn nth(&mut self, i: usize) -> Option<$elem> {
self.start = unsafe { self.start.add(i * self.stride) };
if self.start >= self.end {
return None;
}
let ret = unsafe {
Some(std::mem::transmute::<_, $elem>(self.start))
};
ret
}
}
impl<'a, T: Pod + Debug> std::fmt::Debug for $name<'a, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list().entries(self.into_iter()).finish()
}
}
};
}
use bytemuck::Pod;
pub(super) use impl_iterator;