use core::{ fmt, marker::PhantomData, num::{NonZeroU16, NonZeroUsize}, ops::Range, }; use crate::soa_index::Index; /// A slice into an array of values, based /// on an offset into the array rather than a pointer. /// /// Unlike a Rust slice, this is a u32 offset /// rather than a pointer, and the length is u16. #[derive(PartialEq, Eq, PartialOrd, Ord)] pub struct Slice { pub start: u32, pub length: u16, pub _marker: core::marker::PhantomData, } impl fmt::Debug for Slice { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "Slice<{}> {{ start: {}, length: {} }}", core::any::type_name::(), self.start, self.length ) } } // derive of copy and clone does not play well with PhantomData impl Copy for Slice {} impl Clone for Slice { fn clone(&self) -> Self { *self } } impl Default for Slice { fn default() -> Self { Self::empty() } } impl Slice { pub const fn empty() -> Self { Self { start: 0, length: 0, _marker: PhantomData, } } pub const fn start(self) -> u32 { self.start } pub fn advance(&mut self, amount: u32) { self.start += amount } pub fn get_slice<'a>(&self, elems: &'a [T]) -> &'a [T] { &elems[self.indices()] } pub fn get_slice_mut<'a>(&self, elems: &'a mut [T]) -> &'a mut [T] { &mut elems[self.indices()] } #[inline(always)] pub const fn indices(&self) -> Range { self.start as usize..(self.start as usize + self.length as usize) } pub const fn len(&self) -> usize { self.length as usize } pub const fn is_empty(&self) -> bool { self.len() == 0 } pub fn at_start(&self) -> Index { Index { index: self.start, _marker: PhantomData, } } pub fn at(&self, i: usize) -> Index { Index { index: self.start + i as u32, _marker: PhantomData, } } pub const fn new(start: u32, length: u16) -> Self { Self { start, length, _marker: PhantomData, } } pub const fn truncate(&self, length: u16) -> Self { Self { start: self.start, length, _marker: PhantomData, } } pub fn as_nonempty_slice(&self) -> Option> { NonZeroU16::new(self.length).map(|nonzero_len| NonEmptySlice::new(self.start, nonzero_len)) } } impl IntoIterator for Slice { type Item = Index; type IntoIter = SliceIterator; fn into_iter(self) -> Self::IntoIter { SliceIterator { slice: self, current: self.start, } } } pub struct SliceIterator { slice: Slice, current: u32, } impl Iterator for SliceIterator { type Item = Index; fn next(&mut self) -> Option { if self.current < self.slice.start + self.slice.length as u32 { let index = Index { index: self.current, _marker: PhantomData, }; self.current += 1; Some(index) } else { None } } fn size_hint(&self) -> (usize, Option) { let remaining = (self.slice.start + self.slice.length as u32 - self.current) as usize; (remaining, Some(remaining)) } } impl ExactSizeIterator for SliceIterator {} pub trait GetSlice { fn get_slice(&self, slice: Slice) -> &[T]; } #[derive(PartialEq, Eq, PartialOrd, Ord)] pub struct NonEmptySlice { inner: Slice, } impl fmt::Debug for NonEmptySlice { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) } } impl Copy for NonEmptySlice {} impl Clone for NonEmptySlice { fn clone(&self) -> Self { *self } } impl NonEmptySlice { pub const fn start(self) -> u32 { self.inner.start() } pub fn advance(&mut self, amount: u32) { self.inner.advance(amount); } pub fn get_slice<'a>(&self, elems: &'a [T]) -> &'a [T] { self.inner.get_slice(elems) } pub fn get_slice_mut<'a>(&self, elems: &'a mut [T]) -> &'a mut [T] { self.inner.get_slice_mut(elems) } #[inline(always)] pub const fn indices(&self) -> Range { self.inner.indices() } pub const fn len(&self) -> NonZeroUsize { // Safety: we only accept a nonzero length on construction unsafe { NonZeroUsize::new_unchecked(self.inner.len()) } } pub const fn new(start: u32, length: NonZeroU16) -> Self { Self { inner: Slice { start, length: length.get(), _marker: PhantomData, }, } } /// # Safety /// /// The caller must ensure that the length is nonzero pub const unsafe fn new_unchecked(start: u32, length: u16) -> Self { Self { inner: Slice { start, length, _marker: PhantomData, }, } } pub const fn from_slice(slice: Slice) -> Option { // Using a match here because Option::map is not const match NonZeroU16::new(slice.length) { Some(len) => Some(Self::new(slice.start, len)), None => None, } } /// # Safety /// /// The caller must ensure that the length is nonzero pub const unsafe fn from_slice_unchecked(slice: Slice) -> Self { Self::new(slice.start, NonZeroU16::new_unchecked(slice.length)) } pub const fn truncate(&self, length: NonZeroU16) -> Self { Self { inner: Slice { start: self.inner.start, length: length.get(), _marker: PhantomData, }, } } pub fn as_slice(&self) -> Slice { self.inner } } impl IntoIterator for NonEmptySlice { type Item = Index; type IntoIter = SliceIterator; fn into_iter(self) -> Self::IntoIter { self.inner.into_iter() } } /// Like `Slice`, but for pairs of `T` #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone)] pub struct PairSlice(Slice); impl PairSlice { pub const fn start(self) -> u32 { self.0.start() } pub const fn empty() -> Self { Self(Slice::empty()) } pub const fn len(&self) -> usize { self.0.len() / 2 } pub const fn is_empty(&self) -> bool { self.0.is_empty() } pub fn indices_iter(&self) -> impl Iterator { (self.0.start as usize..(self.0.start as usize + self.0.length as usize)) .step_by(2) .map(|i| (i, i + 1)) } pub const fn new(start: u32, length: u16) -> Self { Self(Slice::new(start, length * 2)) } } impl Default for PairSlice { fn default() -> Self { Self(Slice::default()) } }