]> git.nega.tv - josh/narcissus/commitdiff
Add `slice::array_windows` from std library
authorJoshua Simmons <josh@nega.tv>
Sun, 25 Sep 2022 11:53:10 +0000 (13:53 +0200)
committerJoshua Simmons <josh@nega.tv>
Sun, 25 Sep 2022 11:53:10 +0000 (13:53 +0200)
narcissus-core/src/lib.rs
narcissus-core/src/slice.rs [new file with mode: 0644]

index e0fef028809f6dd0dde22dce2c33e8fd263dcd05..5373b9edc79e6f0f5d8f634d1c284c441bd7849f 100644 (file)
@@ -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 (file)
index 0000000..758e769
--- /dev/null
@@ -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<Self::Item> {
+        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<usize>) {
+        (self.num, Some(self.num))
+    }
+
+    #[inline]
+    fn count(self) -> usize {
+        self.num
+    }
+
+    #[inline]
+    fn nth(&mut self, n: usize) -> Option<Self::Item> {
+        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::Item> {
+        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<T, const N: usize>(slice: &[T]) -> ArrayWindows<'_, T, N> {
+    assert_ne!(N, 0);
+    ArrayWindows::new(slice)
+}