From 22f8dd8b684ae0cbb6a607309f0b9ff93a0e4870 Mon Sep 17 00:00:00 2001 From: Joshua Simmons Date: Sun, 25 Sep 2022 13:53:10 +0200 Subject: [PATCH] Add `slice::array_windows` from std library --- narcissus-core/src/lib.rs | 1 + narcissus-core/src/slice.rs | 145 ++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 narcissus-core/src/slice.rs diff --git a/narcissus-core/src/lib.rs b/narcissus-core/src/lib.rs index e0fef02..5373b9e 100644 --- a/narcissus-core/src/lib.rs +++ b/narcissus-core/src/lib.rs @@ -6,6 +6,7 @@ pub mod manual_arc; mod mutex; mod pool; mod ref_count; +pub mod slice; mod uuid; mod virtual_mem; mod virtual_vec; diff --git a/narcissus-core/src/slice.rs b/narcissus-core/src/slice.rs new file mode 100644 index 0000000..758e769 --- /dev/null +++ b/narcissus-core/src/slice.rs @@ -0,0 +1,145 @@ +use std::marker::PhantomData; + +// Some unstable code from rust stdlib. + +/// A windowed iterator over a slice in overlapping chunks (`N` elements at a +/// time), starting at the beginning of the slice +/// +/// This struct is created by the [`array_windows`] method on [slices]. +/// +/// # Example +/// +/// ``` +/// #![feature(array_windows)] +/// +/// let slice = [0, 1, 2, 3]; +/// let iter = slice.array_windows::<2>(); +/// ``` +/// +/// [`array_windows`]: slice::array_windows +/// [slices]: slice +#[derive(Debug, Clone, Copy)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +pub struct ArrayWindows<'a, T: 'a, const N: usize> { + slice_head: *const T, + num: usize, + marker: PhantomData<&'a [T; N]>, +} + +impl<'a, T: 'a, const N: usize> ArrayWindows<'a, T, N> { + #[inline] + pub(super) fn new(slice: &'a [T]) -> Self { + let num_windows = slice.len().saturating_sub(N - 1); + Self { + slice_head: slice.as_ptr(), + num: num_windows, + marker: PhantomData, + } + } +} + +impl<'a, T, const N: usize> Iterator for ArrayWindows<'a, T, N> { + type Item = &'a [T; N]; + + #[inline] + fn next(&mut self) -> Option { + if self.num == 0 { + return None; + } + // SAFETY: + // This is safe because it's indexing into a slice guaranteed to be length > N. + let ret = unsafe { &*self.slice_head.cast::<[T; N]>() }; + // SAFETY: Guaranteed that there are at least 1 item remaining otherwise + // earlier branch would've been hit + self.slice_head = unsafe { self.slice_head.add(1) }; + + self.num -= 1; + Some(ret) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.num, Some(self.num)) + } + + #[inline] + fn count(self) -> usize { + self.num + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + if self.num <= n { + self.num = 0; + return None; + } + // SAFETY: + // This is safe because it's indexing into a slice guaranteed to be length > N. + let ret = unsafe { &*self.slice_head.add(n).cast::<[T; N]>() }; + // SAFETY: Guaranteed that there are at least n items remaining + self.slice_head = unsafe { self.slice_head.add(n + 1) }; + + self.num -= n + 1; + Some(ret) + } + + #[inline] + fn last(mut self) -> Option { + self.nth(self.num.checked_sub(1)?) + } +} + +impl<'a, T, const N: usize> DoubleEndedIterator for ArrayWindows<'a, T, N> { + #[inline] + fn next_back(&mut self) -> Option<&'a [T; N]> { + if self.num == 0 { + return None; + } + // SAFETY: Guaranteed that there are n items remaining, n-1 for 0-indexing. + let ret = unsafe { &*self.slice_head.add(self.num - 1).cast::<[T; N]>() }; + self.num -= 1; + Some(ret) + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option<&'a [T; N]> { + if self.num <= n { + self.num = 0; + return None; + } + // SAFETY: Guaranteed that there are n items remaining, n-1 for 0-indexing. + let ret = unsafe { &*self.slice_head.add(self.num - (n + 1)).cast::<[T; N]>() }; + self.num -= n + 1; + Some(ret) + } +} + +/// Returns an iterator over overlapping windows of `N` elements of a slice, +/// starting at the beginning of the slice. +/// +/// This is the const generic equivalent of [`windows`]. +/// +/// If `N` is greater than the size of the slice, it will return no windows. +/// +/// # Panics +/// +/// Panics if `N` is 0. This check will most probably get changed to a compile time +/// error before this method gets stabilized. +/// +/// # Examples +/// +/// ``` +/// let slice = [0, 1, 2, 3]; +/// let mut iter = array_windows(slice); +/// assert_eq!(iter.next().unwrap(), &[0, 1]); +/// assert_eq!(iter.next().unwrap(), &[1, 2]); +/// assert_eq!(iter.next().unwrap(), &[2, 3]); +/// assert!(iter.next().is_none()); +/// ``` +/// +/// [`windows`]: slice::windows +#[inline] +pub fn array_windows(slice: &[T]) -> ArrayWindows<'_, T, N> { + assert_ne!(N, 0); + ArrayWindows::new(slice) +} -- 2.49.0