From f240abc1dc9e59bfabfb5ea765fa9eae0aad3122 Mon Sep 17 00:00:00 2001 From: kadmin Date: Thu, 13 Aug 2020 19:09:14 +0000 Subject: [PATCH] Add array window fn Updated issue to #75027 Update to rm oob access And hopefully fix docs as well Fixed naming conflict in test Fix test which used 1-indexing Nth starts from 0, woops Fix a bunch of off by 1 errors See https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=757b311987e3fae1ca47122969acda5a Add even more off by 1 errors And also write `next` and `next_back` in terms of `nth` and `nth_back`. Run fmt Fix forgetting to change fn name in test add nth_back test & document unsafe Remove as_ref().unwrap() Documented occurrences of unsafe, noting what invariants are maintained --- library/alloc/src/lib.rs | 1 + library/alloc/src/slice.rs | 2 + library/core/src/slice/iter.rs | 100 ++++++++++++++++++ library/core/src/slice/mod.rs | 37 +++++++ library/core/tests/lib.rs | 1 + library/core/tests/slice.rs | 49 +++++++++ .../type-dependent/issue-61936.rs | 12 +-- 7 files changed, 196 insertions(+), 6 deletions(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 5774ebb9b195a..7881c101f9f60 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -76,6 +76,7 @@ #![cfg_attr(test, feature(test))] #![feature(allocator_api)] #![feature(array_chunks)] +#![feature(array_windows)] #![feature(allow_internal_unstable)] #![feature(arbitrary_self_types)] #![feature(box_patterns)] diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 677bfdd2349ec..93501ef40852a 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -95,6 +95,8 @@ use crate::vec::Vec; pub use core::slice::ArrayChunks; #[unstable(feature = "array_chunks", issue = "74985")] pub use core::slice::ArrayChunksMut; +#[unstable(feature = "array_windows", issue = "75027")] +pub use core::slice::ArrayWindows; #[stable(feature = "slice_get_slice", since = "1.28.0")] pub use core::slice::SliceIndex; #[stable(feature = "from_ref", since = "1.28.0")] diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 2e8c3cd43e999..84fa34c75e3a2 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -1687,6 +1687,106 @@ unsafe impl<'a, T> TrustedRandomAccess for ChunksExactMut<'a, T> { } } +/// 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]. +/// +/// [`array_windows`]: ../../std/primitive.slice.html#method.array_windows +/// [slices]: ../../std/primitive.slice.html +#[derive(Debug, Clone, Copy)] +#[unstable(feature = "array_windows", issue = "75027")] +pub struct ArrayWindows<'a, T: 'a, const N: usize> { + pub(crate) slice_head: *const T, + pub(crate) num: usize, + pub(crate) marker: marker::PhantomData<&'a [T; N]>, +} + +#[unstable(feature = "array_windows", issue = "75027")] +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)?) + } +} + +#[unstable(feature = "array_windows", issue = "75027")] +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) + } +} + +#[unstable(feature = "array_windows", issue = "75027")] +impl ExactSizeIterator for ArrayWindows<'_, T, N> { + fn is_empty(&self) -> bool { + self.num == 0 + } +} + /// An iterator over a slice in (non-overlapping) chunks (`N` elements at a /// time), starting at the beginning of the slice. /// diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 64a707c39f076..771540b0877be 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -56,6 +56,9 @@ pub use iter::{RChunks, RChunksExact, RChunksExactMut, RChunksMut}; #[unstable(feature = "array_chunks", issue = "74985")] pub use iter::{ArrayChunks, ArrayChunksMut}; +#[unstable(feature = "array_windows", issue = "75027")] +pub use iter::ArrayWindows; + #[unstable(feature = "split_inclusive", issue = "72360")] pub use iter::{SplitInclusive, SplitInclusiveMut}; @@ -1099,6 +1102,40 @@ impl [T] { } } + /// 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 smaller than the size of the array, 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 + /// + /// ``` + /// #![feature(array_windows)] + /// let slice = [0, 1, 2, 3]; + /// let mut iter = slice.array_windows(); + /// 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`]: #method.windows + #[unstable(feature = "array_windows", issue = "75027")] + #[inline] + pub fn array_windows(&self) -> ArrayWindows<'_, T, N> { + assert_ne!(N, 0); + + let num_windows = self.len().saturating_sub(N - 1); + ArrayWindows { slice_head: self.as_ptr(), num: num_windows, marker: marker::PhantomData } + } + /// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the end /// of the slice. /// diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index a2e294ace1860..4a204adb33674 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -2,6 +2,7 @@ #![feature(array_chunks)] #![feature(array_methods)] #![feature(array_map)] +#![feature(array_windows)] #![feature(bool_to_option)] #![feature(bound_cloned)] #![feature(box_syntax)] diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs index 9b31e532a6a9f..9556d43f9d78b 100644 --- a/library/core/tests/slice.rs +++ b/library/core/tests/slice.rs @@ -657,6 +657,55 @@ fn test_array_chunks_mut_zip() { assert_eq!(v1, [13, 14, 19, 20, 4]); } +#[test] +fn test_array_windows_infer() { + let v: &[i32] = &[0, 1, 0, 1]; + assert_eq!(v.array_windows::<2>().count(), 3); + let c = v.array_windows(); + for &[a, b] in c { + assert_eq!(a + b, 1); + } + + let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; + let total = v2.array_windows().map(|&[a, b, c]| a + b + c).sum::(); + assert_eq!(total, 3 + 6 + 9 + 12 + 15); +} + +#[test] +fn test_array_windows_count() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.array_windows::<3>(); + assert_eq!(c.count(), 4); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.array_windows::<6>(); + assert_eq!(c2.count(), 0); + + let v3: &[i32] = &[]; + let c3 = v3.array_windows::<2>(); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_array_windows_nth() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let snd = v.array_windows::<4>().nth(1); + assert_eq!(snd, Some(&[1, 2, 3, 4])); + let mut arr_windows = v.array_windows::<2>(); + assert_ne!(arr_windows.nth(0), arr_windows.nth(0)); + let last = v.array_windows::<3>().last(); + assert_eq!(last, Some(&[3, 4, 5])); +} + +#[test] +fn test_array_windows_nth_back() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let snd = v.array_windows::<4>().nth_back(1); + assert_eq!(snd, Some(&[1, 2, 3, 4])); + let mut arr_windows = v.array_windows::<2>(); + assert_ne!(arr_windows.nth_back(0), arr_windows.nth_back(0)); +} + #[test] fn test_rchunks_count() { let v: &[i32] = &[0, 1, 2, 3, 4, 5]; diff --git a/src/test/ui/const-generics/type-dependent/issue-61936.rs b/src/test/ui/const-generics/type-dependent/issue-61936.rs index 1d42afa3f8461..f3b19109a7c80 100644 --- a/src/test/ui/const-generics/type-dependent/issue-61936.rs +++ b/src/test/ui/const-generics/type-dependent/issue-61936.rs @@ -5,21 +5,21 @@ #![cfg_attr(min, feature(min_const_generics))] trait SliceExt { - fn array_windows<'a, const N: usize>(&'a self) -> ArrayWindows<'a, T, N>; + fn array_windows_example<'a, const N: usize>(&'a self) -> ArrayWindowsExample<'a, T, N>; } impl SliceExt for [T] { - fn array_windows<'a, const N: usize>(&'a self) -> ArrayWindows<'a, T, N> { - ArrayWindows{ idx: 0, slice: &self } + fn array_windows_example<'a, const N: usize>(&'a self) -> ArrayWindowsExample<'a, T, N> { + ArrayWindowsExample{ idx: 0, slice: &self } } } -struct ArrayWindows<'a, T, const N: usize> { +struct ArrayWindowsExample<'a, T, const N: usize> { slice: &'a [T], idx: usize, } -impl <'a, T: Clone, const N: usize> Iterator for ArrayWindows<'a, T, N> { +impl <'a, T: Clone, const N: usize> Iterator for ArrayWindowsExample<'a, T, N> { type Item = [T; N]; fn next(&mut self) -> Option { // Note: this is unsound for some `T` and not meant as an example @@ -45,7 +45,7 @@ const FOUR: usize = 4; fn main() { let v: Vec = vec![0; 100]; - for array in v.as_slice().array_windows::() { + for array in v.as_slice().array_windows_example::() { assert_eq!(array, [0, 0, 0, 0]) } }